[
  {
    "path": ".clang-format",
    "content": "---\nBasedOnStyle: LLVM\nColumnLimit: 88\n"
  },
  {
    "path": ".clang-tidy",
    "content": "---\nChecks: 'clang-diagnostic-*,clang-analyzer-*,cppcoreguidelines-*,modernize-*,bugprone-*,concurrency-*,performance-*,portability-*,-modernize-use-nodiscard,-modernize-use-trailing-return-type,-cppcoreguidelines-special-member-functions,-bugprone-easily-swappable-parameters,-bugprone-assignment-in-if-condition,-modernize-use-nodiscard'\nWarningsAsErrors: false\nHeaderFilterRegex: '(build/.+)|(codon/util/.+)'\nAnalyzeTemporaryDtors: false\nFormatStyle: llvm\nCheckOptions:\n  - key:             cppcoreguidelines-macro-usage.CheckCapsOnly\n    value:           '1'\n"
  },
  {
    "path": ".gitattributes",
    "content": "*.codon linguist-language=Python\n\n*.png binary\n*.jpg binary\n*.jpeg binary\n*.gif binary\n*.ico binary\n*.mov binary\n*.mp4 binary\n*.mp3 binary\n*.flv binary\n*.fla binary\n*.swf binary\n*.gz binary\n*.zip binary\n*.7z binary\n*.ttf binary\n*.eot binary\n*.woff binary\n*.pyc binary\n*.pdf binary\n\n*.gz binary\n\n*.bam binary\n*.bam.bai binary\n*.cram binary\n*.cram.crai binary\n"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"monthly\"\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: Codon CI\n\non:\n  push:\n    branches:\n      - master\n      - develop\n    tags:\n      - '*'\n  pull_request:\n    branches:\n      - develop\n\njobs:\n  create_release:\n    name: GitHub Release\n    runs-on: ubuntu-latest\n    outputs:\n      upload_url: ${{ steps.create_release.outputs.upload_url }}\n    permissions:\n      contents: write\n    steps:\n      - name: Create Release\n        if: contains(github.ref, 'tags/v')\n        id: create_release\n        uses: ncipollo/release-action@v1\n\n  build:\n    strategy:\n      matrix:\n        include:\n          - os: ubuntu-latest\n            arch: linux-x86_64\n          - os: ubuntu-latest\n            arch: manylinux2014-x86_64\n          - os: ubuntu-24.04-arm\n            arch: linux-aarch64\n          - os: ubuntu-24.04-arm\n            arch: manylinux2014-aarch64\n          - os: macos-15-intel\n            arch: darwin-x86_64\n          - os: macos-14\n            arch: darwin-arm64\n    runs-on: ${{ matrix.os }}\n    name: Build Codon\n    needs: create_release\n    permissions:\n      contents: write\n      id-token: write\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Set up Python\n        uses: actions/setup-python@v6\n        with:\n          python-version: '3.11'\n\n      - name: Build (Ubuntu)\n        if: startsWith(matrix.os, 'ubuntu')\n        run: |\n          (cd .github/build-linux && docker build -t local -f Dockerfile.${{ matrix.arch }} .)\n          docker run -v $(pwd):/github/workspace local /github/workspace ${{ matrix.arch }} yes\n\n      - name: Build (macOS)\n        if: startsWith(matrix.os, 'macos')\n        run: |\n          sudo mkdir -p /opt/llvm-codon\n          sudo chown -R $(whoami) /opt/llvm-codon\n          curl -L https://github.com/exaloop/llvm-project/releases/download/codon-20.1.7/llvm-codon-20.1.7-${{ matrix.arch }}.tar.bz2 | tar jxf - -C /opt\n          brew install gcc\n          bash .github/build-linux/entrypoint.sh ${{ github.workspace }} ${{ matrix.arch }} yes\n\n      - name: Upload Artifacts\n        uses: actions/upload-artifact@v7\n        with:\n          name: codon-${{ matrix.arch }}.tar.gz\n          path: codon-${{ matrix.arch }}.tar.gz\n\n      - name: Upload Release Asset\n        if: contains(github.ref, 'tags/v')\n        uses: actions/upload-release-asset@v1.0.2\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          upload_url: ${{ needs.create_release.outputs.upload_url }}\n          asset_path: ./codon-${{ matrix.arch }}.tar.gz\n          asset_name: codon-${{ matrix.arch }}.tar.gz\n          asset_content_type: application/gzip\n\n      - name: Publish PyPI Package\n        if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags') && matrix.arch == 'linux-x86_64'\n        uses: pypa/gh-action-pypi-publish@release/v1\n\n  build_documentation:\n    name: Build Docs\n    runs-on: ubuntu-latest\n    needs: build\n    permissions:\n      contents: write\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n\n      - name: Set up Python\n        uses: actions/setup-python@v6\n        with:\n          python-version: '3.11'\n\n      - name: Install dependencies\n        run: |\n          python -m pip install --upgrade pip\n          python -m pip install mkdocs \\\n                                mkdocs-autorefs \\\n                                mkdocs-macros-plugin \\\n                                mkdocs-material \\\n                                mkdocs-material-extensions \\\n                                mkdocs-redirects\n          sudo apt-get update\n          sudo apt-get install -y pngquant\n\n      - name: Download Artifact\n        uses: actions/download-artifact@v8\n        with:\n          name: codon-linux-x86_64.tar.gz\n          path: ./downloaded-artifact\n\n      - name: Build API reference\n        run: |\n          mv downloaded-artifact/* .\n          tar -xvzf codon-linux-x86_64.tar.gz\n          codon-deploy-linux-x86_64/bin/codon doc codon-deploy-linux-x86_64/lib/codon/stdlib > docs.json\n          python scripts/docgen.py docs.json docs/libraries/api $(pwd)/codon-deploy-linux-x86_64/lib/codon/stdlib\n\n      - name: Build MkDocs site\n        run: mkdocs build --strict\n\n      - name: Deploy to GitHub Pages\n        if: github.ref == 'refs/heads/master'\n        uses: peaceiris/actions-gh-pages@v4\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          publish_dir: ./site\n          force_orphan: true\n          cname: docs.exaloop.io\n"
  },
  {
    "path": ".gitignore",
    "content": "######################\n# Generic .gitignore #\n######################\n\n# Compiled source #\n###################\n*.com\n*.class\n*.dll\n*.exe\n*.o\n*.a\n*.obj\n*.so\n*.dylib\n*.pyc\nbuild*/\ninstall*/\nextra/python/src/jit.cpp\nextra/jupyter/build/\n/site\n\n# Packages #\n############\n# it's better to unpack these files and commit the raw source\n# git has its own built-in compression methods\n*.7z\n*.dmg\n*.iso\n*.jar\n*.rar\n*.tar\n*.zip\n**/**.egg-info\n\n# Logs and databases #\n######################\n*.log\n*.sql\n*.sqlite\n\n# OS generated files #\n######################\n.DS_Store\n.DS_Store?\n._*\n.Spotlight-V100\n.Trashes\nehthumbs.db\nThumbs.db\n\n# IDE generated files #\n#######################\n.idea\n.mypy_cache\n.vscode\n.cache\n.ipynb_checkpoints\n\n# CMake generated files #\n#########################\njupyter/share/jupyter/kernels/codon/kernel.json\njit/codon/version.py\n\n# Testing files #\n#################\ntemp/\nplayground/\nscratch*.*\n/_*\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "repos:\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v4.5.0\n    hooks:\n      - id: trailing-whitespace\n\n  - repo: https://github.com/pre-commit/mirrors-clang-format\n    rev: v17.0.2\n    hooks:\n      - id: clang-format\n        types:\n          - c++\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\nproject(\n  Codon\n  VERSION \"0.19.6\"\n  HOMEPAGE_URL \"https://github.com/exaloop/codon\"\n  DESCRIPTION \"high-performance, extensible Python compiler\")\nset(CODON_JIT_PYTHON_VERSION \"0.4.6\")\nconfigure_file(\"${PROJECT_SOURCE_DIR}/cmake/config.h.in\"\n               \"${PROJECT_SOURCE_DIR}/codon/config/config.h\")\nconfigure_file(\"${PROJECT_SOURCE_DIR}/cmake/config.py.in\"\n               \"${PROJECT_SOURCE_DIR}/jit/codon/version.py\")\n\nif (CMAKE_VERSION VERSION_GREATER_EQUAL \"3.24.0\")\n  cmake_policy(SET CMP0135 NEW)\nendif()\n\nset(CMAKE_CXX_STANDARD 20)\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS}\")\nif(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\")\n  set(CMAKE_CXX_FLAGS\n      \"${CMAKE_CXX_FLAGS} -pedantic -fvisibility-inlines-hidden -Wno-return-type-c-linkage -Wno-gnu-zero-variadic-macro-arguments -Wno-deprecated-declarations\"\n  )\nelse()\n  set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -Wno-return-type\")\nendif()\nset(CMAKE_CXX_FLAGS_DEBUG \"-g\")\nif(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\")\n  set(CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} -fno-limit-debug-info\")\nendif()\nset(CMAKE_CXX_FLAGS_RELEASE \"-O3\")\ninclude_directories(.)\n\nset(APPLE_ARM OFF)\nif (APPLE AND CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL \"arm64\")\n  set(APPLE_ARM ON)\nendif()\n\nset(THREADS_PREFER_PTHREAD_FLAG ON)\nfind_package(Threads REQUIRED)\nfind_package(LLVM REQUIRED)\n\nmessage(STATUS \"Found LLVM ${LLVM_PACKAGE_VERSION}\")\nmessage(STATUS \"Using LLVMConfig.cmake in: ${LLVM_DIR}\")\ninclude(${CMAKE_SOURCE_DIR}/cmake/deps.cmake)\ninclude(${CMAKE_SOURCE_DIR}/cmake/CMakeRC.cmake)\n\nset(CMAKE_BUILD_WITH_INSTALL_RPATH ON)\nif(APPLE)\n  set(CMAKE_INSTALL_RPATH \"@loader_path;@loader_path/../lib/codon\")\nelse()\n  set(CMAKE_INSTALL_RPATH \"$ORIGIN:$ORIGIN/../lib/codon\")\nendif()\n\nadd_executable(peg2cpp codon/util/peg2cpp.cpp)\ntarget_include_directories(peg2cpp PRIVATE ${peglib_SOURCE_DIR})\ntarget_link_libraries(peg2cpp PRIVATE Threads::Threads fmt)\nadd_custom_command(\n  OUTPUT codon_rules.cpp\n  COMMAND peg2cpp ${CMAKE_SOURCE_DIR}/codon/parser/peg/grammar.peg\n          codon_rules.cpp codon\n  DEPENDS peg2cpp codon/parser/peg/grammar.peg)\nadd_custom_command(\n  OUTPUT omp_rules.cpp\n  COMMAND peg2cpp ${CMAKE_SOURCE_DIR}/codon/parser/peg/openmp.peg omp_rules.cpp\n          omp\n  DEPENDS peg2cpp codon/parser/peg/openmp.peg)\n\n# Codon Jupyter library\nset(CODON_JUPYTER_FILES codon/util/jupyter.h codon/util/jupyter.cpp)\nadd_library(codon_jupyter SHARED ${CODON_JUPYTER_FILES})\n\n# Codon runtime library\nadd_library(codonfloat STATIC\n            codon/runtime/floatlib/extenddftf2.c\n            codon/runtime/floatlib/fp_trunc.h\n            codon/runtime/floatlib/truncdfhf2.c\n            codon/runtime/floatlib/extendhfsf2.c\n            codon/runtime/floatlib/int_endianness.h\n            codon/runtime/floatlib/truncdfsf2.c\n            codon/runtime/floatlib/extendhftf2.c\n            codon/runtime/floatlib/int_lib.h\n#            codon/runtime/floatlib/truncsfbf2.c\n            codon/runtime/floatlib/extendsfdf2.c\n            codon/runtime/floatlib/int_math.h\n            codon/runtime/floatlib/truncsfhf2.c\n            codon/runtime/floatlib/extendsftf2.c\n            codon/runtime/floatlib/int_types.h\n            codon/runtime/floatlib/trunctfdf2.c\n            codon/runtime/floatlib/fp_extend.h\n            codon/runtime/floatlib/int_util.h\n            codon/runtime/floatlib/trunctfhf2.c\n            codon/runtime/floatlib/fp_lib.h\n#            codon/runtime/floatlib/truncdfbf2.c\n            codon/runtime/floatlib/trunctfsf2.c)\ntarget_compile_options(codonfloat PRIVATE -O3)\ntarget_compile_definitions(codonfloat PRIVATE COMPILER_RT_HAS_FLOAT16)\n\nset(CODONRT_FILES codon/runtime/lib.h codon/runtime/lib.cpp\n                  codon/runtime/re.cpp codon/runtime/exc.cpp\n                  codon/runtime/numpy/sort.cpp\n                  codon/runtime/numpy/loops.cpp codon/runtime/numpy/zmath.cpp)\nadd_library(codonrt SHARED ${CODONRT_FILES})\nadd_dependencies(codonrt zlibstatic gc backtrace bz2 liblzma\n                         re2 hwy hwy_contrib fast_float codonfloat)\n\nif(DEFINED ENV{CODON_SYSTEM_LIBRARIES})\n  if(APPLE)\n    set(copied_libgfortran \"${CMAKE_BINARY_DIR}/libgfortran.5${CMAKE_SHARED_LIBRARY_SUFFIX}\")\n    set(copied_libquadmath \"${CMAKE_BINARY_DIR}/libquadmath.0${CMAKE_SHARED_LIBRARY_SUFFIX}\")\n    set(copied_libgcc      \"${CMAKE_BINARY_DIR}/libgcc_s.1.1${CMAKE_SHARED_LIBRARY_SUFFIX}\")\n  else()\n    set(copied_libgfortran \"${CMAKE_BINARY_DIR}/libgfortran${CMAKE_SHARED_LIBRARY_SUFFIX}.5\")\n    set(copied_libquadmath \"${CMAKE_BINARY_DIR}/libquadmath${CMAKE_SHARED_LIBRARY_SUFFIX}.0\")\n    set(copied_libgcc      \"${CMAKE_BINARY_DIR}/libgcc_s${CMAKE_SHARED_LIBRARY_SUFFIX}.1\")\n  endif()\n\n  add_custom_command(\n    OUTPUT ${copied_libgfortran}\n    DEPENDS \"${CMAKE_SOURCE_DIR}/scripts/get_system_libs.sh\"\n    COMMAND ${CMAKE_SOURCE_DIR}/scripts/get_system_libs.sh \"$ENV{CODON_SYSTEM_LIBRARIES}\" ${CMAKE_BINARY_DIR}\n    COMMENT \"Copying system libraries to build directory\")\n\n  add_custom_target(copy_libraries ALL DEPENDS ${copied_libgfortran})\n  add_dependencies(codonrt copy_libraries)\n\n  add_library(libgfortran SHARED IMPORTED)\n  set_target_properties(libgfortran PROPERTIES IMPORTED_LOCATION ${copied_libgfortran})\n  target_link_libraries(codonrt PRIVATE libgfortran)\nelse()\n  message(FATAL_ERROR \"Set 'CODON_SYSTEM_LIBRARIES' to the directory containing system libraries.\")\nendif()\n\ntarget_include_directories(codonrt PRIVATE ${backtrace_SOURCE_DIR}\n                                           ${re2_SOURCE_DIR}\n                                           ${highway_SOURCE_DIR}\n                                           \"${gc_SOURCE_DIR}/include\"\n                                           \"${fast_float_SOURCE_DIR}/include\" runtime)\ntarget_link_libraries(codonrt PRIVATE fmt omp backtrace LLVMSupport)\nif(APPLE)\n  target_link_libraries(\n    codonrt\n    PRIVATE -Wl,-force_load,$<TARGET_FILE:zlibstatic>\n            -Wl,-force_load,$<TARGET_FILE:gc>\n            -Wl,-force_load,$<TARGET_FILE:bz2>\n            -Wl,-force_load,$<TARGET_FILE:liblzma>\n            -Wl,-force_load,$<TARGET_FILE:re2>\n            -Wl,-force_load,$<TARGET_FILE:hwy>\n            -Wl,-force_load,$<TARGET_FILE:hwy_contrib>\n            -Wl,-force_load,$<TARGET_FILE:codonfloat>)\n  target_link_libraries(codonrt PUBLIC \"-framework Accelerate\")\nelse()\n  add_dependencies(codonrt openblas)\n  target_link_libraries(\n    codonrt\n    PRIVATE -Wl,--whole-archive $<TARGET_FILE:zlibstatic> $<TARGET_FILE:gc>\n            $<TARGET_FILE:bz2> $<TARGET_FILE:liblzma> $<TARGET_FILE:re2>\n            $<TARGET_FILE:openblas> $<TARGET_FILE:hwy> $<TARGET_FILE:hwy_contrib>\n            $<TARGET_FILE:codonfloat> -Wl,--no-whole-archive)\nendif()\nif(ASAN)\n  target_compile_options(\n    codonrt PRIVATE \"-fno-omit-frame-pointer\" \"-fsanitize=address\"\n                    \"-fsanitize-recover=address\")\n  target_link_libraries(\n    codonrt PRIVATE \"-fno-omit-frame-pointer\" \"-fsanitize=address\"\n                    \"-fsanitize-recover=address\")\nendif()\nadd_custom_command(\n  TARGET codonrt\n  POST_BUILD\n  COMMAND ${CMAKE_COMMAND} -E copy_if_different $<TARGET_FILE:omp>\n          ${CMAKE_BINARY_DIR})\n\n# Codon compiler library\ninclude_directories(${LLVM_INCLUDE_DIRS})\nadd_definitions(${LLVM_DEFINITIONS})\nset(CODON_HPPFILES\n  codon/compiler/compiler.h\n  codon/compiler/debug_listener.h\n  codon/compiler/engine.h\n  codon/compiler/error.h\n  codon/compiler/jit.h\n  codon/compiler/jit_extern.h\n  codon/compiler/memory_manager.h\n  codon/dsl/dsl.h\n  codon/dsl/plugins.h\n  codon/parser/ast.h\n  codon/parser/match.h\n  codon/parser/ast/node.h\n  codon/parser/ast/expr.h\n  codon/parser/ast/stmt.h\n  codon/parser/ast/types.h\n  codon/parser/ast/attr.h\n  codon/parser/ast/types/type.h\n  codon/parser/ast/types/link.h\n  codon/parser/ast/types/class.h\n  codon/parser/ast/types/function.h\n  codon/parser/ast/types/union.h\n  codon/parser/ast/types/static.h\n  codon/parser/ast/types/traits.h\n  codon/parser/cache.h\n  codon/parser/common.h\n  codon/parser/ctx.h\n  codon/parser/peg/peg.h\n  codon/parser/peg/rules.h\n  codon/parser/visitors/doc/doc.h\n  codon/parser/visitors/format/format.h\n  codon/parser/visitors/scoping/scoping.h\n  codon/parser/visitors/translate/translate.h\n  codon/parser/visitors/translate/translate_ctx.h\n  codon/parser/visitors/typecheck/typecheck.h\n  codon/parser/visitors/typecheck/ctx.h\n  codon/parser/visitors/visitor.h\n  codon/cir/analyze/analysis.h\n  codon/cir/analyze/dataflow/capture.h\n  codon/cir/analyze/dataflow/cfg.h\n  codon/cir/analyze/dataflow/dominator.h\n  codon/cir/analyze/dataflow/reaching.h\n  codon/cir/analyze/module/global_vars.h\n  codon/cir/analyze/module/side_effect.h\n  codon/cir/attribute.h\n  codon/cir/base.h\n  codon/cir/const.h\n  codon/cir/dsl/codegen.h\n  codon/cir/dsl/nodes.h\n  codon/cir/flow.h\n  codon/cir/func.h\n  codon/cir/instr.h\n  codon/cir/llvm/gpu.h\n  codon/cir/llvm/llvisitor.h\n  codon/cir/llvm/llvm.h\n  codon/cir/llvm/optimize.h\n  codon/cir/module.h\n  codon/cir/pyextension.h\n  codon/cir/cir.h\n  codon/cir/transform/cleanup/canonical.h\n  codon/cir/transform/cleanup/dead_code.h\n  codon/cir/transform/cleanup/global_demote.h\n  codon/cir/transform/cleanup/replacer.h\n  codon/cir/transform/folding/const_fold.h\n  codon/cir/transform/folding/const_prop.h\n  codon/cir/transform/folding/folding.h\n  codon/cir/transform/folding/rule.h\n  codon/cir/transform/lowering/async_for.h\n  codon/cir/transform/lowering/await.h\n  codon/cir/transform/lowering/imperative.h\n  codon/cir/transform/lowering/pipeline.h\n  codon/cir/transform/manager.h\n  codon/cir/transform/parallel/openmp.h\n  codon/cir/transform/parallel/schedule.h\n  codon/cir/transform/pass.h\n  codon/cir/transform/pythonic/dict.h\n  codon/cir/transform/pythonic/generator.h\n  codon/cir/transform/pythonic/io.h\n  codon/cir/transform/pythonic/list.h\n  codon/cir/transform/pythonic/str.h\n  codon/cir/transform/rewrite.h\n  codon/cir/types/types.h\n  codon/cir/util/cloning.h\n  codon/cir/util/context.h\n  codon/cir/util/format.h\n  codon/cir/util/inlining.h\n  codon/cir/util/irtools.h\n  codon/cir/util/iterators.h\n  codon/cir/util/matching.h\n  codon/cir/util/operator.h\n  codon/cir/util/outlining.h\n  codon/cir/util/packs.h\n  codon/cir/util/side_effect.h\n  codon/cir/util/visitor.h\n  codon/cir/value.h\n  codon/cir/llvm/native/native.h\n  codon/cir/llvm/native/targets/aarch64.h\n  codon/cir/llvm/native/targets/arm.h\n  codon/cir/llvm/native/targets/target.h\n  codon/cir/llvm/native/targets/x86.h\n  codon/cir/transform/numpy/numpy.h\n  codon/cir/transform/numpy/indexing.h\n  codon/cir/var.h\n  codon/util/common.h\n  codon/util/serialize.h\n  codon/util/tser.h)\nset(CODON_CPPFILES\n  codon/compiler/compiler.cpp\n  codon/compiler/debug_listener.cpp\n  codon/compiler/engine.cpp\n  codon/compiler/error.cpp\n  codon/compiler/jit.cpp\n  codon/compiler/memory_manager.cpp\n  codon/dsl/plugins.cpp\n  codon/parser/ast/expr.cpp\n  codon/parser/ast/attr.cpp\n  codon/parser/ast/stmt.cpp\n  codon/parser/ast/types/type.cpp\n  codon/parser/ast/types/link.cpp\n  codon/parser/ast/types/class.cpp\n  codon/parser/ast/types/function.cpp\n  codon/parser/ast/types/union.cpp\n  codon/parser/ast/types/static.cpp\n  codon/parser/ast/types/traits.cpp\n  codon/parser/cache.cpp\n  codon/parser/match.cpp\n  codon/parser/common.cpp\n  codon/parser/peg/peg.cpp\n  codon/parser/visitors/doc/doc.cpp\n  codon/parser/visitors/format/format.cpp\n  codon/parser/visitors/scoping/scoping.cpp\n  codon/parser/visitors/translate/translate.cpp\n  codon/parser/visitors/translate/translate_ctx.cpp\n  codon/parser/visitors/typecheck/typecheck.cpp\n  codon/parser/visitors/typecheck/infer.cpp\n  codon/parser/visitors/typecheck/ctx.cpp\n  codon/parser/visitors/typecheck/assign.cpp\n  codon/parser/visitors/typecheck/basic.cpp\n  codon/parser/visitors/typecheck/call.cpp\n  codon/parser/visitors/typecheck/class.cpp\n  codon/parser/visitors/typecheck/collections.cpp\n  codon/parser/visitors/typecheck/cond.cpp\n  codon/parser/visitors/typecheck/function.cpp\n  codon/parser/visitors/typecheck/access.cpp\n  codon/parser/visitors/typecheck/import.cpp\n  codon/parser/visitors/typecheck/loops.cpp\n  codon/parser/visitors/typecheck/op.cpp\n  codon/parser/visitors/typecheck/error.cpp\n  codon/parser/visitors/typecheck/special.cpp\n  codon/parser/visitors/visitor.cpp\n  codon/cir/attribute.cpp\n  codon/cir/analyze/analysis.cpp\n  codon/cir/analyze/dataflow/capture.cpp\n  codon/cir/analyze/dataflow/cfg.cpp\n  codon/cir/analyze/dataflow/dominator.cpp\n  codon/cir/analyze/dataflow/reaching.cpp\n  codon/cir/analyze/module/global_vars.cpp\n  codon/cir/analyze/module/side_effect.cpp\n  codon/cir/base.cpp\n  codon/cir/const.cpp\n  codon/cir/dsl/nodes.cpp\n  codon/cir/flow.cpp\n  codon/cir/func.cpp\n  codon/cir/instr.cpp\n  codon/cir/llvm/gpu.cpp\n  codon/cir/llvm/llvisitor.cpp\n  codon/cir/llvm/optimize.cpp\n  codon/cir/module.cpp\n  codon/cir/transform/cleanup/canonical.cpp\n  codon/cir/transform/cleanup/dead_code.cpp\n  codon/cir/transform/cleanup/global_demote.cpp\n  codon/cir/transform/cleanup/replacer.cpp\n  codon/cir/transform/folding/const_fold.cpp\n  codon/cir/transform/folding/const_prop.cpp\n  codon/cir/transform/folding/folding.cpp\n  codon/cir/transform/lowering/async_for.cpp\n  codon/cir/transform/lowering/await.cpp\n  codon/cir/transform/lowering/imperative.cpp\n  codon/cir/transform/lowering/pipeline.cpp\n  codon/cir/transform/manager.cpp\n  codon/cir/transform/parallel/openmp.cpp\n  codon/cir/transform/parallel/schedule.cpp\n  codon/cir/transform/pass.cpp\n  codon/cir/transform/pythonic/dict.cpp\n  codon/cir/transform/pythonic/generator.cpp\n  codon/cir/transform/pythonic/io.cpp\n  codon/cir/transform/pythonic/list.cpp\n  codon/cir/transform/pythonic/str.cpp\n  codon/cir/types/types.cpp\n  codon/cir/util/cloning.cpp\n  codon/cir/util/format.cpp\n  codon/cir/util/inlining.cpp\n  codon/cir/util/irtools.cpp\n  codon/cir/util/matching.cpp\n  codon/cir/util/outlining.cpp\n  codon/cir/util/side_effect.cpp\n  codon/cir/util/visitor.cpp\n  codon/cir/value.cpp\n  codon/cir/var.cpp\n  codon/cir/llvm/native/native.cpp\n  codon/cir/llvm/native/targets/aarch64.cpp\n  codon/cir/llvm/native/targets/arm.cpp\n  codon/cir/llvm/native/targets/x86.cpp\n  codon/cir/transform/numpy/expr.cpp\n  codon/cir/transform/numpy/forward.cpp\n  codon/cir/transform/numpy/indexing.cpp\n  codon/cir/transform/numpy/numpy.cpp\n  codon/util/common.cpp)\nadd_library(codonc SHARED ${CODON_HPPFILES})\ntarget_include_directories(codonc PRIVATE ${peglib_SOURCE_DIR}\n                                          ${toml_SOURCE_DIR}/include\n                                          ${semver_SOURCE_DIR}/include\n                                          ${fast_float_SOURCE_DIR}/include)\ntarget_sources(codonc PRIVATE ${CODON_CPPFILES} codon_rules.cpp omp_rules.cpp)\nif(ASAN)\n  target_compile_options(\n    codonc PRIVATE \"-fno-omit-frame-pointer\" \"-fsanitize=address\"\n                   \"-fsanitize-recover=address\")\n  target_link_libraries(\n    codonc PRIVATE \"-fno-omit-frame-pointer\" \"-fsanitize=address\"\n                   \"-fsanitize-recover=address\")\nendif()\nif(CMAKE_BUILD_TYPE MATCHES Debug)\n  set_source_files_properties(codon_rules.cpp codon/parser/peg/peg.cpp\n                              PROPERTIES COMPILE_FLAGS \"-O2\")\nendif()\nllvm_map_components_to_libnames(\n  LLVM_LIBS\n  AllTargetsAsmParsers\n  AllTargetsCodeGens\n  AllTargetsDescs\n  AllTargetsInfos\n  AggressiveInstCombine\n  Analysis\n  AsmParser\n  BitWriter\n  CodeGen\n  Core\n  Extensions\n  IPO\n  IRReader\n  InstCombine\n  Instrumentation\n  MC\n  MCJIT\n  ObjCARCOpts\n  OrcJIT\n  Remarks\n  ScalarOpts\n  Support\n  Symbolize\n  Target\n  TransformUtils\n  Vectorize\n  Passes)\n\nfile(GLOB_RECURSE CODON_STDLIB_RESOURCES\n  CONFIGURE_DEPENDS\n  \"${CMAKE_CURRENT_SOURCE_DIR}/stdlib/*.codon\"\n)\ncmrc_add_resource_library(\n  codon-stdlib\n  NAMESPACE codon\n  ${CODON_STDLIB_RESOURCES}\n)\nset_property(TARGET codon-stdlib PROPERTY POSITION_INDEPENDENT_CODE ON)\n\ntarget_link_libraries(codonc PRIVATE ${LLVM_LIBS} fmt dl codonrt codon-stdlib)\n\n# Gather headers\nadd_custom_target(\n  headers ALL\n  COMMENT \"Collecting headers\"\n  BYPRODUCTS \"${CMAKE_BINARY_DIR}/include\"\n  VERBATIM\n  COMMAND ${CMAKE_COMMAND} -E make_directory \"${CMAKE_BINARY_DIR}/include/codon\"\n  COMMAND ${CMAKE_COMMAND} -E copy_directory \"${CMAKE_SOURCE_DIR}/codon\"\n          \"${CMAKE_BINARY_DIR}/include/codon\"\n  COMMAND find \"${CMAKE_BINARY_DIR}/include\" -type f ! -name \"*.h\" -exec rm {}\n          \\\\;)\nadd_dependencies(headers codonrt codonc)\n\n# Prepare lib directory for plugin compilation\nadd_custom_target(\n  libs ALL\n  COMMENT \"Collecting libraries\"\n  BYPRODUCTS \"${CMAKE_BINARY_DIR}/lib\"\n  VERBATIM\n  COMMAND ${CMAKE_COMMAND} -E make_directory \"${CMAKE_BINARY_DIR}/lib/codon\"\n  COMMAND\n    ${CMAKE_COMMAND} -E copy\n    \"${CMAKE_BINARY_DIR}/libcodonc${CMAKE_SHARED_LIBRARY_SUFFIX}\"\n    \"${CMAKE_BINARY_DIR}/lib/codon\"\n  COMMAND\n    ${CMAKE_COMMAND} -E copy\n    \"${CMAKE_BINARY_DIR}/libcodonrt${CMAKE_SHARED_LIBRARY_SUFFIX}\"\n    \"${CMAKE_BINARY_DIR}/lib/codon\"\n  COMMAND\n    ${CMAKE_COMMAND} -E copy\n    \"${CMAKE_BINARY_DIR}/libomp${CMAKE_SHARED_LIBRARY_SUFFIX}\"\n    \"${CMAKE_BINARY_DIR}/lib/codon\"\n  COMMAND\n    ${CMAKE_COMMAND} -E copy ${copied_libgfortran} \"${CMAKE_BINARY_DIR}/lib/codon\"\n  COMMAND\n    /bin/sh -c \"test -f '${copied_libquadmath}' && ${CMAKE_COMMAND} -E copy '${copied_libquadmath}' '${CMAKE_BINARY_DIR}/lib/codon' || true\"\n  COMMAND\n    ${CMAKE_COMMAND} -E copy ${copied_libgcc} \"${CMAKE_BINARY_DIR}/lib/codon\")\nadd_dependencies(libs codonrt codonc)\n\n# Codon command-line tool\nadd_executable(codon codon/app/main.cpp)\ntarget_link_libraries(codon PUBLIC fmt codonc codon_jupyter Threads::Threads)\n\n# Codon test Download and unpack googletest at configure time\ninclude(FetchContent)\nFetchContent_Declare(\n  googletest\n  URL https://github.com/google/googletest/archive/03597a01ee50ed33e9dfd640b249b4be3799d395.zip\n)\n# For Windows: Prevent overriding the parent project's compiler/linker settings\nset(gtest_force_shared_crt ON CACHE BOOL \"\" FORCE)\noption(INSTALL_GTEST \"Enable installation of googletest.\" OFF)\nFetchContent_MakeAvailable(googletest)\nenable_testing()\nset(CODON_TEST_CPPFILES\n    test/main.cpp\n    test/cir/analyze/dominator.cpp\n    test/cir/analyze/reaching.cpp\n    test/cir/base.cpp\n    test/cir/constant.cpp\n    test/cir/flow.cpp\n    test/cir/func.cpp\n    test/cir/instr.cpp\n    test/cir/module.cpp\n    test/cir/transform/manager.cpp\n    test/cir/types/types.cpp\n    test/cir/util/matching.cpp\n    test/cir/value.cpp\n    test/cir/var.cpp\n    test/types.cpp)\nadd_executable(codon_test ${CODON_TEST_CPPFILES})\ntarget_include_directories(codon_test PRIVATE test/cir \"${gc_SOURCE_DIR}/include\")\ntarget_link_libraries(codon_test fmt codonc codonrt gtest_main)\ntarget_compile_definitions(codon_test\n                           PRIVATE TEST_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}/test\")\n\ninstall(TARGETS codonrt codonc codon_jupyter DESTINATION lib/codon)\ninstall(FILES ${CMAKE_BINARY_DIR}/libomp${CMAKE_SHARED_LIBRARY_SUFFIX} DESTINATION lib/codon)\ninstall(FILES ${copied_libgfortran} DESTINATION lib/codon)\n# only install libquadmath if it exists at build time\ninstall(CODE \"\n  file(GLOB _quadmath \\\"${copied_libquadmath}\\\")\n  if(EXISTS \\\"\\${_quadmath}\\\")\n    file(INSTALL DESTINATION \\\"\\${CMAKE_INSTALL_PREFIX}/lib/codon\\\" TYPE FILE FILES \\\"\\${_quadmath}\\\")\n  endif()\n\")\ninstall(FILES ${copied_libgcc} DESTINATION lib/codon)\ninstall(TARGETS codon DESTINATION bin)\ninstall(DIRECTORY ${CMAKE_BINARY_DIR}/include/codon DESTINATION include)\ninstall(DIRECTORY ${LLVM_INCLUDE_DIRS}/llvm DESTINATION include)\ninstall(DIRECTORY ${LLVM_INCLUDE_DIRS}/llvm-c DESTINATION include)\ninstall(DIRECTORY ${CMAKE_SOURCE_DIR}/stdlib DESTINATION lib/codon)\ninstall(DIRECTORY ${CMAKE_SOURCE_DIR}/jit/ DESTINATION python)\ninstall(DIRECTORY DESTINATION lib/codon/plugins)\n\ninstall(CODE [[\n  if(APPLE)\n    # Compute the real install root (supports DESTDIR)\n    set(_root \"$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}\")\n    message(STATUS \"fix_loader_paths.sh on: ${_root}\")\n    execute_process(\n      COMMAND /bin/bash \"${CMAKE_SOURCE_DIR}/scripts/fix_loader_paths.sh\" \"${_root}\"\n      RESULT_VARIABLE rc\n    )\n    if(NOT rc EQUAL 0)\n      message(FATAL_ERROR \"fix_loader_paths.sh failed with code ${rc}\")\n    endif()\n  endif()\n]])\n"
  },
  {
    "path": "CODEOWNERS",
    "content": "*                    @arshajii @inumanag\n/codon/              @arshajii\n/codon/parser/       @inumanag\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# Contributing to Codon\n\nThank you for considering contributing to Codon! This document contains some helpful information for getting started.\nThe best place to ask questions or get feedback is [our Discord](https://discord.gg/HeWRhagCmP).\n\n## Development workflow\n\nAll development is done on the [`develop`](https://github.com/exaloop/codon/tree/develop) branch. Just before release,\nwe bump the version number, merge into [`master`](https://github.com/exaloop/codon/tree/master) and tag the build with\na tag of the form `vX.Y.Z` where `X`, `Y` and `Z` are the [SemVer](https://semver.org) major, minor and patch numbers,\nrespectively. Our CI build process automatically builds and deploys tagged commits as a new GitHub release.\n\n## Coding standards\n\nAll C++ code should be formatted with [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) using the `.clang-format`\nfile in the root of the repository.\n\n## Writing tests\n\nTests are written as Codon programs. The [`test/core/`](https://github.com/exaloop/codon/tree/master/test/core) directory\ncontains some examples. If you add a new test file, be sure to add it to\n[`test/main.cpp`](https://github.com/exaloop/codon/blob/master/test/main.cpp) so that it will be executed as part of the test\nsuite. There are two ways to write tests for Codon:\n\n#### New style\n\nExample:\n\n```python\n@test\ndef my_test():\n    assert 2 + 2 == 4\nmy_test()\n```\n\n**Semantics:** `assert` statements in functions marked `@test` are not compiled to standard assertions: they don't terminate\nthe program when the condition fails, but instead print source information, fail the test, and move on.\n\n#### Old style\n\nExample:\n\n```python\nprint(2 + 2)  # EXPECT: 4\n```\n\n**Semantics:** The source file is scanned for `EXPECT`s, executed, then the output is compared to the \"expected\" output. Note\nthat if you have, for example, an `EXPECT` in a loop, you will need to duplicate it however many times the loop is executed.\nUsing `EXPECT` is helpful mainly in cases where you need to test control flow, **otherwise prefer the new style**.\n\n## Pull requests\n\nPull requests should generally be based on the `develop` branch. Before submitting a pull request, please make sure...\n\n- ... to provide a clear description of the purpose of the pull request.\n- ... to include tests for any new or changed code.\n- ... that all code is formatted as per the guidelines above.\n\nPlease be patient with pull request reviews, as our throughput is limited.\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\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "<h1 align=\"center\">\n <img src=\"docs/img/codon-banner.svg\" alt=\"Codon banner\"/>\n</h1>\n\n<h3 align=\"center\">\n  <a href=\"https://docs.exaloop.io/codon\" target=\"_blank\"><b>Docs</b></a>\n  &nbsp;&#183;&nbsp;\n  <a href=\"https://docs.exaloop.io/codon/general/faq\" target=\"_blank\"><b>FAQ</b></a>\n  &nbsp;&#183;&nbsp;\n  <a href=\"https://exaloop.io/blog\" target=\"_blank\"><b>Blog</b></a>\n  &nbsp;&#183;&nbsp;\n  <a href=\"https://discord.gg/HeWRhagCmP\" target=\"_blank\">Discord</a>\n  &nbsp;&#183;&nbsp;\n  <a href=\"https://docs.exaloop.io/codon/general/roadmap\" target=\"_blank\">Roadmap</a>\n  &nbsp;&#183;&nbsp;\n  <a href=\"https://exaloop.io/#benchmarks\" target=\"_blank\">Benchmarks</a>\n</h3>\n\n<a href=\"https://github.com/exaloop/codon/actions/workflows/ci.yml\">\n  <img src=\"https://github.com/exaloop/codon/actions/workflows/ci.yml/badge.svg\"\n       alt=\"Build Status\">\n</a>\n\n# What is Codon?\n\nCodon is a high-performance Python implementation that compiles to native machine code without\nany runtime overhead. Typical speedups over vanilla Python are on the order of 10-100x or more, on\na single thread. Codon's performance is typically on par with (and sometimes better than) that of\nC/C++. Unlike Python, Codon supports native multithreading, which can lead to speedups many times\nhigher still.\n\n*Think of Codon as Python reimagined for static, ahead-of-time compilation, built from the ground\nup with best possible performance in mind.*\n\n## Goals\n\n- :bulb: **No learning curve:** Be as close to CPython as possible in terms of syntax, semantics and libraries\n- :rocket: **Top-notch performance:** At *least* on par with low-level languages like C, C++ or Rust\n- :computer: **Hardware support:** Full, seamless support for multicore programming, multithreading (no GIL!), GPU and more\n- :chart_with_upwards_trend: **Optimizations:** Comprehensive optimization framework that can target high-level Python constructs\n  and libraries\n- :battery: **Interoperability:** Full interoperability with Python's ecosystem of packages and libraries\n\n## Non-goals\n\n- :x: *Drop-in replacement for CPython:* Codon is not a drop-in replacement for CPython. There are some\n  aspects of Python that are not suitable for static compilation — we don't support these in Codon.\n  There are ways to use Codon in larger Python codebases via its [JIT decorator](https://docs.exaloop.io/codon/interoperability/decorator)\n  or [Python extension backend](https://docs.exaloop.io/codon/interoperability/pyext). Codon also supports\n  calling any Python module via its [Python interoperability](https://docs.exaloop.io/codon/interoperability/python).\n  See also [*\"Differences with Python\"*](https://docs.exaloop.io/codon/general/differences) in the docs.\n\n- :x: *New syntax and language constructs:* We try to avoid adding new syntax, keywords or other language\n  features as much as possible. While Codon does add some new syntax in a couple places (e.g. to express\n  parallelism), we try to make it as familiar and intuitive as possible.\n\n## How it works\n\n<p align=\"center\">\n <img src=\"docs/img/codon-pipeline.svg\" width=\"90%\" alt=\"Codon figure\"/>\n</p>\n\n# Quick start\n\nDownload and install Codon with this command:\n\n```bash\n/bin/bash -c \"$(curl -fsSL https://exaloop.io/install.sh)\"\n```\n\nAfter following the prompts, the `codon` command will be available to use. For example:\n\n- To run a program: `codon run file.py`\n- To run a program with optimizations enabled: `codon run -release file.py`\n- To compile to an executable: `codon build -release file.py`\n- To generate LLVM IR: `codon build -release -llvm file.py`\n\nMany more options are available and described in [the docs](https://docs.exaloop.io/codon/general/intro).\n\nAlternatively, you can [build from source](https://docs.exaloop.io/codon/advanced/build).\n\n# Examples\n\n## Basics\n\nCodon supports much of Python, and many Python programs will work with few if any modifications.\nHere's a simple script `fib.py` that computes the 40th Fibonacci number...\n\n``` python\nfrom time import time\n\ndef fib(n):\n    return n if n < 2 else fib(n - 1) + fib(n - 2)\n\nt0 = time()\nans = fib(40)\nt1 = time()\nprint(f'Computed fib(40) = {ans} in {t1 - t0} seconds.')\n```\n\n... run through Python and Codon:\n\n```\n$ python3 fib.py\nComputed fib(40) = 102334155 in 17.979357957839966 seconds.\n$ codon run -release fib.py\nComputed fib(40) = 102334155 in 0.275645 seconds.\n```\n\n## Using Python libraries\n\nYou can import and use any Python package from Codon via `from python import`. For example:\n\n```python\nfrom python import matplotlib.pyplot as plt\ndata = [x**2 for x in range(10)]\nplt.plot(data)\nplt.show()\n```\n\n(Just remember to set the `CODON_PYTHON` environment variable to the CPython shared library,\nas explained in the [the Python interoperability docs](https://docs.exaloop.io/codon/interoperability/python).)\n\n## Parallelism\n\nCodon supports native multithreading via [OpenMP](https://www.openmp.org/). The `@par` annotation\nin the code below tells the compiler to parallelize the following `for`-loop, in this case using\na dynamic schedule, chunk size of 100, and 16 threads.\n\n```python\nfrom sys import argv\n\ndef is_prime(n):\n    factors = 0\n    for i in range(2, n):\n        if n % i == 0:\n            factors += 1\n    return factors == 0\n\nlimit = int(argv[1])\ntotal = 0\n\n@par(schedule='dynamic', chunk_size=100, num_threads=16)\nfor i in range(2, limit):\n    if is_prime(i):\n        total += 1\n\nprint(total)\n```\n\nNote that Codon automatically turns the `total += 1` statement in the loop body into an atomic\nreduction to avoid race conditions. Learn more in the [multithreading docs](https://docs.exaloop.io/codon/advanced/parallel).\n\nCodon also supports writing and executing GPU kernels. Here's an example that computes the\n[Mandelbrot set](https://en.wikipedia.org/wiki/Mandelbrot_set):\n\n```python\nimport gpu\n\nMAX    = 1000  # maximum Mandelbrot iterations\nN      = 4096  # width and height of image\npixels = [0 for _ in range(N * N)]\n\ndef scale(x, a, b):\n    return a + (x/N)*(b - a)\n\n@gpu.kernel\ndef mandelbrot(pixels):\n    idx = (gpu.block.x * gpu.block.dim.x) + gpu.thread.x\n    i, j = divmod(idx, N)\n    c = complex(scale(j, -2.00, 0.47), scale(i, -1.12, 1.12))\n    z = 0j\n    iteration = 0\n\n    while abs(z) <= 2 and iteration < MAX:\n        z = z**2 + c\n        iteration += 1\n\n    pixels[idx] = int(255 * iteration/MAX)\n\nmandelbrot(pixels, grid=(N*N)//1024, block=1024)\n```\n\nGPU programming can also be done using the `@par` syntax with `@par(gpu=True)`. See the\n[GPU programming docs](https://docs.exaloop.io/codon/advanced/gpu) for more details.\n\n## NumPy support\n\nCodon includes a feature-complete, fully-compiled native NumPy implementation. It uses the same\nAPI as NumPy, but re-implements everything in Codon itself, allowing for a range of optimizations\nand performance improvements.\n\nHere's an example NumPy program that approximates $\\pi$ using random numbers...\n\n``` python\nimport time\nimport numpy as np\n\nrng = np.random.default_rng(seed=0)\nx = rng.random(500_000_000)\ny = rng.random(500_000_000)\n\nt0 = time.time()\n# pi ~= 4 x (fraction of points in circle)\npi = ((x-1)**2 + (y-1)**2 < 1).sum() * (4 / len(x))\nt1 = time.time()\n\nprint(f'Computed pi~={pi:.4f} in {t1 - t0:.2f} sec')\n```\n\n... run through Python and Codon:\n\n```\n$ python3 pi.py\nComputed pi~=3.1417 in 2.25 sec\n$ codon run -release pi.py\nComputed pi~=3.1417 in 0.43 sec\n```\n\nCodon can speed up NumPy code through general-purpose and NumPy-specific compiler optimizations,\nincluding inlining, fusion, memory allocation elision and more. Furthermore, Codon's NumPy\nimplementation works with its multithreading and GPU capabilities, and can even integrate with\n[PyTorch](https://pytorch.org). Learn more in the [Codon-NumPy docs](https://docs.exaloop.io/codon/interoperability/numpy).\n\n# Documentation\n\nPlease see [docs.exaloop.io](https://docs.exaloop.io) for in-depth documentation.\n\n# Acknowledgements\n\nThis project would not be possible without:\n\n- **Funding**:\n  - National Science Foundation (NSF) 🇺🇸\n  - National Institutes of Health (NIH) 🇺🇸\n  - MIT 🇺🇸\n  - MIT E14 Fund 🇺🇸\n  - Natural Sciences and Engineering Research Council (NSERC) 🇨🇦\n  - Canada Research Chairs 🇨🇦\n  - Canada Foundation for Innovation 🇨🇦\n  - B.C. Knowledge Development Fund 🇨🇦\n  - University of Victoria 🇨🇦\n- **Libraries**:\n  [LLVM Compiler Infrastructure](https://llvm.org/),\n  [yhirose's peglib](https://github.com/yhirose/cpp-peglib),\n  [Boehm-Demers-Weiser Garbage Collector](https://github.com/ivmai/bdwgc),\n  [KonanM's tser](https://github.com/KonanM/tser),\n  [{fmt}](https://github.com/fmtlib/fmt),\n  [toml++](https://marzer.github.io/tomlplusplus/),\n  [semver](https://github.com/Neargye/semver),\n  [zlib-ng](https://github.com/zlib-ng/zlib-ng),\n  [xz](https://github.com/tukaani-project/xz),\n  [bz2](https://sourceware.org/bzip2/),\n  [Google RE2](https://github.com/google/re2),\n  [libbacktrace](https://github.com/ianlancetaylor/libbacktrace),\n  [fast_float](https://github.com/fastfloat/fast_float),\n  [Google Highway](https://github.com/google/highway),\n  [NumPy](https://numpy.org/)\n"
  },
  {
    "path": "bench/README.md",
    "content": "# Codon benchmark suite\n\nThis folder contains a number of Codon benchmarks. Some are taken\nfrom the [pyperformance suite](https://github.com/python/pyperformance)\nwhile others are adaptations of applications we've encountered in the\nwild. Further, some of the benchmarks are identical in both Python and\nCodon, some are changed slightly to work with Codon's type system, and\nsome use Codon-specific features like parallelism or GPU.\n\nSome of the pyperformance benchmarks can be made (much) faster in Codon\nby using various Codon-specific features, but their adaptations here are\nvirtually identical to the original implementations (mainly just the use\nof the `pyperf` module is removed).\n\n## Benchmarks\n\n- `chaos`: [Pyperformance's `chaos` benchmark](https://github.com/python/pyperformance/blob/main/pyperformance/data-files/benchmarks/bm_chaos/run_benchmark.py).\n- `float`: [Pyperformance's `float` benchmark](https://github.com/python/pyperformance/blob/main/pyperformance/data-files/benchmarks/bm_float/run_benchmark.py).\n- `go`: [Pyperformance's `go` benchmark](https://github.com/python/pyperformance/blob/main/pyperformance/data-files/benchmarks/bm_go/run_benchmark.py).\n- `nbody`: [Pyperformance's `nbody` benchmark](https://github.com/python/pyperformance/blob/main/pyperformance/data-files/benchmarks/bm_nbody/run_benchmark.py).\n- `spectral_norm`: [Pyperformance's `spectral_norm` benchmark](https://github.com/python/pyperformance/blob/main/pyperformance/data-files/benchmarks/bm_spectral_norm/run_benchmark.py).\n- `mandelbrot`: Generates an image of the Mandelbrot set. Codon version uses GPU via one additional `@par(gpu=True, collapse=2)` line.\n- `set_partition`: Calculates set partitions. Code taken from [this Stack Overflow answer](https://stackoverflow.com/a/73549333).\n- `sum`: Computes sum of integers from 1 to 50000000 with a loop. Code taken from [this article](https://towardsdatascience.com/getting-started-with-pypy-ef4ba5cb431c).\n- `taq`: Performs volume peak detection on an NYSE TAQ file. Sample TAQ files can be downloaded and uncompressed from [here](https://ftp.nyse.com/Historical%20Data%20Samples/DAILY%20TAQ/)\n         (e.g. `EQY_US_ALL_NBBO_20220705.gz`). We recommend using the first 10M lines for benchmarking purposes. The TAQ file path should be passed to the benchmark script\n         through the `DATA_TAQ` environment variable.\n- `binary_trees`: [Boehm's binary trees benchmark](https://hboehm.info/gc/gc_bench.html).\n- `fannkuch`: See [*Performing Lisp analysis of the FANNKUCH benchmark*](https://dl.acm.org/doi/10.1145/382109.382124) by Kenneth R. Anderson and Duane Rettig. Benchmark\n              involves generating permutations and repeatedly reversing elements of a list. Codon version is multithreaded with a dynamic schedule via one additional\n              `@par(schedule='dynamic')` line.\n- `word_count`: Counts occurrences of words in a file using a dictionary. The file should be passed to the benchmark script through the `DATA_WORD_COUNT` environment variable.\n- `primes`: Counts the number of prime numbers below a threshold. Codon version is multithreaded with a dynamic schedule via one additional `@par(schedule='dynamic')` line.\n"
  },
  {
    "path": "bench/codon/binary_trees.codon",
    "content": "# The Computer Language Benchmarks Game\n# http://benchmarksgame.alioth.debian.org/\n#\n# contributed by Antoine Pitrou\n# modified by Dominique Wahli and Daniel Nanz\n# modified by Joerg Baumann\n# modified by @arshajii for Codon\n\nimport sys\nimport time\n\nclass Node:\n    left: Optional[Node] = None\n    right: Optional[Node] = None\n\ndef make_tree(d):\n    return Node(make_tree(d - 1), make_tree(d - 1)) if d > 0 else Node()\n\ndef check_tree(node):\n    l, r = node.left, node.right\n    if l is None:\n        return 1\n    else:\n        return 1 + check_tree(l) + check_tree(r)\n\ndef make_check(itde, make=make_tree, check=check_tree):\n    i, d = itde\n    return check(make(d))\n\ndef get_argchunks(i, d, chunksize=5000):\n    assert chunksize % 2 == 0\n    chunk = []\n    for k in range(1, i + 1):\n        chunk.append((k, d))\n        if len(chunk) == chunksize:\n            yield chunk\n            chunk = []\n    if len(chunk) > 0:\n        yield chunk\n\ndef main(n, min_depth=4):\n    max_depth = max(min_depth + 2, n)\n    stretch_depth = max_depth + 1\n    print(f'stretch tree of depth {stretch_depth}\\t check: {make_check((0, stretch_depth))}')\n\n    long_lived_tree = make_tree(max_depth)\n\n    mmd = max_depth + min_depth\n    for d in range(min_depth, stretch_depth, 2):\n        i = 2 ** (mmd - d)\n        cs = 0\n        for argchunk in get_argchunks(i, d):\n            cs += sum(map(make_check, argchunk))\n        print(f'{i}\\t trees of depth {d}\\t check: {cs}')\n\n    print(f'long lived tree of depth {max_depth}\\t check: {check_tree(long_lived_tree)}')\n\nt0 = time.time()\nmain(int(sys.argv[1]))\nt1 = time.time()\nprint(t1 - t0)\n"
  },
  {
    "path": "bench/codon/binary_trees.cpp",
    "content": "#include <cassert>\n#include <chrono>\n#include <iostream>\n#include <memory>\n#include <utility>\n#include <vector>\n\nstruct Node {\n  std::unique_ptr<Node> left{};\n  std::unique_ptr<Node> right{};\n};\n\ninline std::unique_ptr<Node> make_tree(int d) {\n  if (d > 0) {\n    return std::make_unique<Node>(Node{make_tree(d - 1), make_tree(d - 1)});\n  } else {\n    return std::make_unique<Node>();\n  }\n}\n\ninline int check_tree(const std::unique_ptr<Node> &node) {\n  if (!node->left)\n    return 1;\n  else\n    return 1 + check_tree(node->left) + check_tree(node->right);\n}\n\ninline int make_check(const std::pair<int, int> &itde) {\n  int i = itde.first, d = itde.second;\n  auto tree = make_tree(d);\n  return check_tree(tree);\n}\n\nstruct ArgChunks {\n  int i, k, d, chunksize;\n  std::vector<std::pair<int, int>> chunk;\n\n  ArgChunks(int i, int d, int chunksize = 5000)\n      : i(i), k(1), d(d), chunksize(chunksize), chunk() {\n    assert(chunksize % 2 == 0);\n  }\n\n  bool next() {\n    chunk.clear();\n    while (k <= i) {\n      chunk.emplace_back(k++, d);\n      if (chunk.size() == chunksize)\n        return true;\n    }\n    return !chunk.empty();\n  }\n};\n\nint main(int argc, char *argv[]) {\n  using clock = std::chrono::high_resolution_clock;\n  using std::chrono::duration_cast;\n  using std::chrono::milliseconds;\n\n  auto t = clock::now();\n  int min_depth = 4;\n  int n = std::stoi(argv[1]);\n  int max_depth = std::max(min_depth + 2, n);\n  int stretch_depth = max_depth + 1;\n\n  std::cout << \"stretch tree of depth \" << stretch_depth\n            << \"\\t check: \" << make_check({0, stretch_depth}) << '\\n';\n\n  auto long_lived_tree = make_tree(max_depth);\n  int mmd = max_depth + min_depth;\n  for (int d = min_depth; d < stretch_depth; d += 2) {\n    int i = (1 << (mmd - d));\n    int cs = 0;\n    ArgChunks iter(i, d);\n    while (iter.next()) {\n      for (auto &argchunk : iter.chunk) {\n        cs += make_check(argchunk);\n      }\n    }\n    std::cout << i << \"\\t trees of depth \" << d << \"\\t check: \" << cs << '\\n';\n  }\n  std::cout << \"long lived tree of depth \" << max_depth\n            << \"\\t check: \" << check_tree(long_lived_tree) << '\\n';\n  std::cout << (duration_cast<milliseconds>(clock::now() - t).count() / 1e3)\n            << std::endl;\n}\n"
  },
  {
    "path": "bench/codon/binary_trees.py",
    "content": "# The Computer Language Benchmarks Game\n# http://benchmarksgame.alioth.debian.org/\n#\n# contributed by Antoine Pitrou\n# modified by Dominique Wahli and Daniel Nanz\n# modified by Joerg Baumann\n# modified by @arshajii for Codon\n\nimport sys\nimport time\n\nclass Node:\n    def __init__(self, left = None, right = None):\n        self.left = left\n        self.right = right\n\ndef make_tree(d):\n    return Node(make_tree(d - 1), make_tree(d - 1)) if d > 0 else Node()\n\ndef check_tree(node):\n    l, r = node.left, node.right\n    if l is None:\n        return 1\n    else:\n        return 1 + check_tree(l) + check_tree(r)\n\ndef make_check(itde, make=make_tree, check=check_tree):\n    i, d = itde\n    return check(make(d))\n\ndef get_argchunks(i, d, chunksize=5000):\n    assert chunksize % 2 == 0\n    chunk = []\n    for k in range(1, i + 1):\n        chunk.append((k, d))\n        if len(chunk) == chunksize:\n            yield chunk\n            chunk = []\n    if len(chunk) > 0:\n        yield chunk\n\ndef main(n, min_depth=4):\n    max_depth = max(min_depth + 2, n)\n    stretch_depth = max_depth + 1\n    print(f'stretch tree of depth {stretch_depth}\\t check: {make_check((0, stretch_depth))}')\n\n    long_lived_tree = make_tree(max_depth)\n\n    mmd = max_depth + min_depth\n    for d in range(min_depth, stretch_depth, 2):\n        i = 2 ** (mmd - d)\n        cs = 0\n        for argchunk in get_argchunks(i, d):\n            cs += sum(map(make_check, argchunk))\n        print(f'{i}\\t trees of depth {d}\\t check: {cs}')\n\n    print(f'long lived tree of depth {max_depth}\\t check: {check_tree(long_lived_tree)}')\n\nt0 = time.time()\nmain(int(sys.argv[1]))\nt1 = time.time()\nprint(t1 - t0)\n"
  },
  {
    "path": "bench/codon/chaos.codon",
    "content": "\"\"\"create chaosgame-like fractals\nCopyright (C) 2005 Carl Friedrich Bolz\n\nadapted by @arshajii for Codon\n\"\"\"\n\nimport math\nimport random\nimport sys\nimport time\n\nDEFAULT_THICKNESS = 1.0\nDEFAULT_WIDTH = 2048 #256\nDEFAULT_HEIGHT = 2048 #256\nDEFAULT_ITERATIONS = 1000000 #5000\nDEFAULT_RNG_SEED = 1234\n\n\nclass GVector(object):\n    x: float\n    y: float\n    z: float\n\n    def __init__(self, x=0, y=0, z=0):\n        self.x = x\n        self.y = y\n        self.z = z\n\n    def Mag(self):\n        return math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2)\n\n    def dist(self, other):\n        return math.sqrt((self.x - other.x) ** 2\n                         + (self.y - other.y) ** 2\n                         + (self.z - other.z) ** 2)\n\n    def __add__(self, other):\n        if not isinstance(other, GVector):\n            raise ValueError(\"Can't add GVector to \" + str(type(other)))\n        v = GVector(self.x + other.x, self.y + other.y, self.z + other.z)\n        return v\n\n    def __sub__(self, other):\n        return self + other * -1\n\n    def __mul__(self, other):\n        v = GVector(self.x * other, self.y * other, self.z * other)\n        return v\n    #__rmul__ = __mul__\n\n    def linear_combination(self, other, l1, l2=None):\n        if l2 is None:\n            l2 = 1 - l1\n        v = GVector(self.x * l1 + other.x * l2,\n                    self.y * l1 + other.y * l2,\n                    self.z * l1 + other.z * l2)\n        return v\n\n    #def __str__(self):\n    #    return \"<%f, %f, %f>\" % (self.x, self.y, self.z)\n\n    #def __repr__(self):\n    #    return \"GVector(%f, %f, %f)\" % (self.x, self.y, self.z)\n\n\nclass Spline(object):\n    \"\"\"Class for representing B-Splines and NURBS of arbitrary degree\"\"\"\n    knots: List[int]\n    degree: int\n    points: List[GVector]\n\n    def __init__(self, points, degree, knots):\n        \"\"\"Creates a Spline.\n        points is a list of GVector, degree is the degree of the Spline.\n        \"\"\"\n        if len(points) > len(knots) - degree + 1:\n            raise ValueError(\"too many control points\")\n        elif len(points) < len(knots) - degree + 1:\n            raise ValueError(\"not enough control points\")\n        last = knots[0]\n        for cur in knots[1:]:\n            if cur < last:\n                raise ValueError(\"knots not strictly increasing\")\n            last = cur\n        self.knots = knots\n        self.points = points\n        self.degree = degree\n\n    def GetDomain(self):\n        \"\"\"Returns the domain of the B-Spline\"\"\"\n        return (self.knots[self.degree - 1],\n                self.knots[len(self.knots) - self.degree])\n\n    def __call__(self, u):\n        \"\"\"Calculates a point of the B-Spline using de Boors Algorithm\"\"\"\n        dom = self.GetDomain()\n        if u < dom[0] or u > dom[1]:\n            raise ValueError(\"Function value not in domain\")\n        if u == dom[0]:\n            return self.points[0]\n        if u == dom[1]:\n            return self.points[-1]\n        I = self.GetIndex(u)\n        d = [self.points[I - self.degree + 1 + ii]\n             for ii in range(self.degree + 1)]\n        U = self.knots\n        for ik in range(1, self.degree + 1):\n            for ii in range(I - self.degree + ik + 1, I + 2):\n                ua = U[ii + self.degree - ik]\n                ub = U[ii - 1]\n                co1 = (ua - u) / (ua - ub)\n                co2 = (u - ub) / (ua - ub)\n                index = ii - I + self.degree - ik - 1\n                d[index] = d[index].linear_combination(d[index + 1], co1, co2)\n        return d[0]\n\n    def GetIndex(self, u):\n        dom = self.GetDomain()\n        for ii in range(self.degree - 1, len(self.knots) - self.degree):\n            if u >= self.knots[ii] and u < self.knots[ii + 1]:\n                I = ii\n                break\n        else:\n            I = dom[1] - 1\n        return I\n\n    def __len__(self):\n        return len(self.points)\n\n    #def __repr__(self):\n    #    return \"Spline(%r, %r, %r)\" % (self.points, self.degree, self.knots)\n\n\ndef write_ppm(im, filename):\n    magic = 'P6\\n'\n    maxval = 255\n    w = len(im)\n    h = len(im[0])\n\n    #with open(filename, \"w\", encoding=\"latin1\", newline='') as fp:\n    with open(filename, \"w\") as fp:\n        fp.write(magic)\n        #fp.write('%i %i\\n%i\\n' % (w, h, maxval))\n        fp.write(f'{w} {h}\\n{maxval}\\n')\n        for j in range(h):\n            for i in range(w):\n                val = im[i][j]\n                c = val * 255\n                #fp.write('%c%c%c' % (c, c, c))\n                c = chr(c)\n                fp.write(f'{c}{c}{c}')\n\n\nclass Chaosgame(object):\n    splines: List[Spline]\n    thickness: float\n    minx: float\n    miny: float\n    maxx: float\n    maxy: float\n    height: float\n    width: float\n    num_trafos: List[int]\n    num_total: int\n\n    def __init__(self, splines, thickness=0.1):\n        self.splines = splines\n        self.thickness = thickness\n        self.minx = min([p.x for spl in splines for p in spl.points])\n        self.miny = min([p.y for spl in splines for p in spl.points])\n        self.maxx = max([p.x for spl in splines for p in spl.points])\n        self.maxy = max([p.y for spl in splines for p in spl.points])\n        self.height = self.maxy - self.miny\n        self.width = self.maxx - self.minx\n        self.num_trafos = []\n        maxlength = thickness * self.width / self.height\n        for spl in splines:\n            length = 0.\n            curr = spl(0)\n            for i in range(1, 1000):\n                last = curr\n                t = 1 / 999 * i\n                curr = spl(t)\n                length += curr.dist(last)\n            self.num_trafos.append(max(1, int(length / maxlength * 1.5)))\n        self.num_total = sum(self.num_trafos)\n\n    def get_random_trafo(self):\n        r = random.randrange(int(self.num_total) + 1)\n        l = 0\n        for i in range(len(self.num_trafos)):\n            if r >= l and r < l + self.num_trafos[i]:\n                return i, random.randrange(self.num_trafos[i])\n            l += self.num_trafos[i]\n        return len(self.num_trafos) - 1, random.randrange(self.num_trafos[-1])\n\n    def transform_point(self, point):\n        x = (point.x - self.minx) / self.width\n        y = (point.y - self.miny) / self.height\n        #if trafo is None:\n        trafo = self.get_random_trafo()\n        start, end = self.splines[trafo[0]].GetDomain()\n        length = end - start\n        seg_length = length / self.num_trafos[trafo[0]]\n        t = start + seg_length * trafo[1] + seg_length * x\n        basepoint = self.splines[trafo[0]](t)\n        if t + 1 / 50000 > end:\n            neighbour = self.splines[trafo[0]](t - 1 / 50000)\n            derivative = neighbour - basepoint\n        else:\n            neighbour = self.splines[trafo[0]](t + 1 / 50000)\n            derivative = basepoint - neighbour\n        if derivative.Mag() != 0:\n            basepoint.x += derivative.y / derivative.Mag() * (y - 0.5) * \\\n                self.thickness\n            basepoint.y += -derivative.x / derivative.Mag() * (y - 0.5) * \\\n                self.thickness\n        else:\n            print(\"r\", end='')\n        self.truncate(basepoint)\n        return basepoint\n\n    def truncate(self, point):\n        if point.x >= self.maxx:\n            point.x = self.maxx\n        if point.y >= self.maxy:\n            point.y = self.maxy\n        if point.x < self.minx:\n            point.x = self.minx\n        if point.y < self.miny:\n            point.y = self.miny\n\n    def create_image_chaos(self, w, h, iterations, filename, rng_seed):\n        # Always use the same sequence of random numbers\n        # to get reproducible benchmark\n        random.seed(rng_seed)\n\n        im = [[1] * h for i in range(w)]\n        point = GVector((self.maxx + self.minx) / 2,\n                        (self.maxy + self.miny) / 2, 0)\n        for _ in range(iterations):\n            point = self.transform_point(point)\n            x = (point.x - self.minx) / self.width * w\n            y = (point.y - self.miny) / self.height * h\n            x = int(x)\n            y = int(y)\n            if x == w:\n                x -= 1\n            if y == h:\n                y -= 1\n            im[x][h - y - 1] = 0\n\n        if filename:\n            write_ppm(im, filename)\n\n\ndef main():\n    splines = [\n        Spline([\n            GVector(1.597350, 3.304460, 0.000000),\n            GVector(1.575810, 4.123260, 0.000000),\n            GVector(1.313210, 5.288350, 0.000000),\n            GVector(1.618900, 5.329910, 0.000000),\n            GVector(2.889940, 5.502700, 0.000000),\n            GVector(2.373060, 4.381830, 0.000000),\n            GVector(1.662000, 4.360280, 0.000000)],\n            3, [0, 0, 0, 1, 1, 1, 2, 2, 2]),\n        Spline([\n            GVector(2.804500, 4.017350, 0.000000),\n            GVector(2.550500, 3.525230, 0.000000),\n            GVector(1.979010, 2.620360, 0.000000),\n            GVector(1.979010, 2.620360, 0.000000)],\n            3, [0, 0, 0, 1, 1, 1]),\n        Spline([\n            GVector(2.001670, 4.011320, 0.000000),\n            GVector(2.335040, 3.312830, 0.000000),\n            GVector(2.366800, 3.233460, 0.000000),\n            GVector(2.366800, 3.233460, 0.000000)],\n            3, [0, 0, 0, 1, 1, 1])\n    ]\n\n    chaos = Chaosgame(splines, DEFAULT_THICKNESS)\n    chaos.create_image_chaos(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_ITERATIONS, sys.argv[1], DEFAULT_RNG_SEED)\n\nt0 = time.time()\nmain()\nt1 = time.time()\nprint(t1 - t0)\n"
  },
  {
    "path": "bench/codon/chaos.py",
    "content": "\"\"\"create chaosgame-like fractals\nCopyright (C) 2005 Carl Friedrich Bolz\n\nadapted by @arshajii for Codon\n\"\"\"\n\nimport math\nimport random\nimport sys\nimport time\n\nDEFAULT_THICKNESS = 1.0\nDEFAULT_WIDTH = 2048 #256\nDEFAULT_HEIGHT = 2048 #256\nDEFAULT_ITERATIONS = 1000000 #5000\nDEFAULT_RNG_SEED = 1234\n\n\nclass GVector(object):\n\n    def __init__(self, x=0, y=0, z=0):\n        self.x = x\n        self.y = y\n        self.z = z\n\n    def Mag(self):\n        return math.sqrt(self.x ** 2 + self.y ** 2 + self.z ** 2)\n\n    def dist(self, other):\n        return math.sqrt((self.x - other.x) ** 2\n                         + (self.y - other.y) ** 2\n                         + (self.z - other.z) ** 2)\n\n    def __add__(self, other):\n        if not isinstance(other, GVector):\n            raise ValueError(\"Can't add GVector to \" + str(type(other)))\n        v = GVector(self.x + other.x, self.y + other.y, self.z + other.z)\n        return v\n\n    def __sub__(self, other):\n        return self + other * -1\n\n    def __mul__(self, other):\n        v = GVector(self.x * other, self.y * other, self.z * other)\n        return v\n    __rmul__ = __mul__\n\n    def linear_combination(self, other, l1, l2=None):\n        if l2 is None:\n            l2 = 1 - l1\n        v = GVector(self.x * l1 + other.x * l2,\n                    self.y * l1 + other.y * l2,\n                    self.z * l1 + other.z * l2)\n        return v\n\n    def __str__(self):\n        return \"<%f, %f, %f>\" % (self.x, self.y, self.z)\n\n    def __repr__(self):\n        return \"GVector(%f, %f, %f)\" % (self.x, self.y, self.z)\n\n\nclass Spline(object):\n    \"\"\"Class for representing B-Splines and NURBS of arbitrary degree\"\"\"\n\n    def __init__(self, points, degree, knots):\n        \"\"\"Creates a Spline.\n        points is a list of GVector, degree is the degree of the Spline.\n        \"\"\"\n        if len(points) > len(knots) - degree + 1:\n            raise ValueError(\"too many control points\")\n        elif len(points) < len(knots) - degree + 1:\n            raise ValueError(\"not enough control points\")\n        last = knots[0]\n        for cur in knots[1:]:\n            if cur < last:\n                raise ValueError(\"knots not strictly increasing\")\n            last = cur\n        self.knots = knots\n        self.points = points\n        self.degree = degree\n\n    def GetDomain(self):\n        \"\"\"Returns the domain of the B-Spline\"\"\"\n        return (self.knots[self.degree - 1],\n                self.knots[len(self.knots) - self.degree])\n\n    def __call__(self, u):\n        \"\"\"Calculates a point of the B-Spline using de Boors Algorithm\"\"\"\n        dom = self.GetDomain()\n        if u < dom[0] or u > dom[1]:\n            raise ValueError(\"Function value not in domain\")\n        if u == dom[0]:\n            return self.points[0]\n        if u == dom[1]:\n            return self.points[-1]\n        I = self.GetIndex(u)\n        d = [self.points[I - self.degree + 1 + ii]\n             for ii in range(self.degree + 1)]\n        U = self.knots\n        for ik in range(1, self.degree + 1):\n            for ii in range(I - self.degree + ik + 1, I + 2):\n                ua = U[ii + self.degree - ik]\n                ub = U[ii - 1]\n                co1 = (ua - u) / (ua - ub)\n                co2 = (u - ub) / (ua - ub)\n                index = ii - I + self.degree - ik - 1\n                d[index] = d[index].linear_combination(d[index + 1], co1, co2)\n        return d[0]\n\n    def GetIndex(self, u):\n        dom = self.GetDomain()\n        for ii in range(self.degree - 1, len(self.knots) - self.degree):\n            if u >= self.knots[ii] and u < self.knots[ii + 1]:\n                I = ii\n                break\n        else:\n            I = dom[1] - 1\n        return I\n\n    def __len__(self):\n        return len(self.points)\n\n    def __repr__(self):\n        return \"Spline(%r, %r, %r)\" % (self.points, self.degree, self.knots)\n\n\ndef write_ppm(im, filename):\n    magic = 'P6\\n'\n    maxval = 255\n    w = len(im)\n    h = len(im[0])\n\n    with open(filename, \"w\", encoding=\"latin1\", newline='') as fp:\n        fp.write(magic)\n        fp.write('%i %i\\n%i\\n' % (w, h, maxval))\n        for j in range(h):\n            for i in range(w):\n                val = im[i][j]\n                c = val * 255\n                fp.write('%c%c%c' % (c, c, c))\n\n\nclass Chaosgame(object):\n\n    def __init__(self, splines, thickness=0.1):\n        self.splines = splines\n        self.thickness = thickness\n        self.minx = min([p.x for spl in splines for p in spl.points])\n        self.miny = min([p.y for spl in splines for p in spl.points])\n        self.maxx = max([p.x for spl in splines for p in spl.points])\n        self.maxy = max([p.y for spl in splines for p in spl.points])\n        self.height = self.maxy - self.miny\n        self.width = self.maxx - self.minx\n        self.num_trafos = []\n        maxlength = thickness * self.width / self.height\n        for spl in splines:\n            length = 0\n            curr = spl(0)\n            for i in range(1, 1000):\n                last = curr\n                t = 1 / 999 * i\n                curr = spl(t)\n                length += curr.dist(last)\n            self.num_trafos.append(max(1, int(length / maxlength * 1.5)))\n        self.num_total = sum(self.num_trafos)\n\n    def get_random_trafo(self):\n        r = random.randrange(int(self.num_total) + 1)\n        l = 0\n        for i in range(len(self.num_trafos)):\n            if r >= l and r < l + self.num_trafos[i]:\n                return i, random.randrange(self.num_trafos[i])\n            l += self.num_trafos[i]\n        return len(self.num_trafos) - 1, random.randrange(self.num_trafos[-1])\n\n    def transform_point(self, point, trafo=None):\n        x = (point.x - self.minx) / self.width\n        y = (point.y - self.miny) / self.height\n        if trafo is None:\n            trafo = self.get_random_trafo()\n        start, end = self.splines[trafo[0]].GetDomain()\n        length = end - start\n        seg_length = length / self.num_trafos[trafo[0]]\n        t = start + seg_length * trafo[1] + seg_length * x\n        basepoint = self.splines[trafo[0]](t)\n        if t + 1 / 50000 > end:\n            neighbour = self.splines[trafo[0]](t - 1 / 50000)\n            derivative = neighbour - basepoint\n        else:\n            neighbour = self.splines[trafo[0]](t + 1 / 50000)\n            derivative = basepoint - neighbour\n        if derivative.Mag() != 0:\n            basepoint.x += derivative.y / derivative.Mag() * (y - 0.5) * \\\n                self.thickness\n            basepoint.y += -derivative.x / derivative.Mag() * (y - 0.5) * \\\n                self.thickness\n        else:\n            print(\"r\", end='')\n        self.truncate(basepoint)\n        return basepoint\n\n    def truncate(self, point):\n        if point.x >= self.maxx:\n            point.x = self.maxx\n        if point.y >= self.maxy:\n            point.y = self.maxy\n        if point.x < self.minx:\n            point.x = self.minx\n        if point.y < self.miny:\n            point.y = self.miny\n\n    def create_image_chaos(self, w, h, iterations, filename, rng_seed):\n        # Always use the same sequence of random numbers\n        # to get reproducible benchmark\n        random.seed(rng_seed)\n\n        im = [[1] * h for i in range(w)]\n        point = GVector((self.maxx + self.minx) / 2,\n                        (self.maxy + self.miny) / 2, 0)\n        for _ in range(iterations):\n            point = self.transform_point(point)\n            x = (point.x - self.minx) / self.width * w\n            y = (point.y - self.miny) / self.height * h\n            x = int(x)\n            y = int(y)\n            if x == w:\n                x -= 1\n            if y == h:\n                y -= 1\n            im[x][h - y - 1] = 0\n\n        if filename:\n            write_ppm(im, filename)\n\n\ndef main():\n    splines = [\n        Spline([\n            GVector(1.597350, 3.304460, 0.000000),\n            GVector(1.575810, 4.123260, 0.000000),\n            GVector(1.313210, 5.288350, 0.000000),\n            GVector(1.618900, 5.329910, 0.000000),\n            GVector(2.889940, 5.502700, 0.000000),\n            GVector(2.373060, 4.381830, 0.000000),\n            GVector(1.662000, 4.360280, 0.000000)],\n            3, [0, 0, 0, 1, 1, 1, 2, 2, 2]),\n        Spline([\n            GVector(2.804500, 4.017350, 0.000000),\n            GVector(2.550500, 3.525230, 0.000000),\n            GVector(1.979010, 2.620360, 0.000000),\n            GVector(1.979010, 2.620360, 0.000000)],\n            3, [0, 0, 0, 1, 1, 1]),\n        Spline([\n            GVector(2.001670, 4.011320, 0.000000),\n            GVector(2.335040, 3.312830, 0.000000),\n            GVector(2.366800, 3.233460, 0.000000),\n            GVector(2.366800, 3.233460, 0.000000)],\n            3, [0, 0, 0, 1, 1, 1])\n    ]\n\n    chaos = Chaosgame(splines, DEFAULT_THICKNESS)\n    chaos.create_image_chaos(DEFAULT_WIDTH, DEFAULT_HEIGHT, DEFAULT_ITERATIONS, sys.argv[1], DEFAULT_RNG_SEED)\n\nt0 = time.time()\nmain()\nt1 = time.time()\nprint(t1 - t0)\n"
  },
  {
    "path": "bench/codon/fannkuch.codon",
    "content": "# FANNKUCH benchmark\nfrom math import factorial as fact\nfrom sys import argv\nfrom time import time\n\ndef perm(n, i):\n    p = [0] * n\n\n    for k in range(n):\n        f = fact(n - 1 - k)\n        p[k] = i // f\n        i = i % f\n\n    for k in range(n - 1, -1, -1):\n        for j in range(k - 1, -1, -1):\n            if p[j] <= p[k]:\n                p[k] += 1\n\n    return p\n\nn = int(argv[1])\nmax_flips = 0\n\nt0 = time()\n@par(schedule='dynamic', num_threads=4)\nfor idx in range(fact(n)):\n    p = perm(n, idx)\n    flips = 0\n    k = p[0]\n\n    while k:\n        i = 0\n        j = k\n        while i < j:\n            p[i], p[j] = p[j], p[i]\n            i += 1\n            j -= 1\n\n        k = p[0]\n        flips += 1\n\n    max_flips = max(flips, max_flips)\n\nprint(f'Pfannkuchen({n}) = {max_flips}')\nt1 = time()\nprint(t1 - t0)\n"
  },
  {
    "path": "bench/codon/fannkuch.py",
    "content": "# FANNKUCH benchmark\nfrom math import factorial as fact\nfrom sys import argv\nfrom time import time\n\ndef perm(n, i):\n    p = [0] * n\n\n    for k in range(n):\n        f = fact(n - 1 - k)\n        p[k] = i // f\n        i = i % f\n\n    for k in range(n - 1, -1, -1):\n        for j in range(k - 1, -1, -1):\n            if p[j] <= p[k]:\n                p[k] += 1\n\n    return p\n\nn = int(argv[1])\nmax_flips = 0\n\nt0 = time()\nfor idx in range(fact(n)):\n    p = perm(n, idx)\n    flips = 0\n    k = p[0]\n\n    while k:\n        i = 0\n        j = k\n        while i < j:\n            p[i], p[j] = p[j], p[i]\n            i += 1\n            j -= 1\n\n        k = p[0]\n        flips += 1\n\n    max_flips = max(flips, max_flips)\n\nprint(f'Pfannkuchen({n}) = {max_flips}')\nt1 = time()\nprint(t1 - t0)\n"
  },
  {
    "path": "bench/codon/float.py",
    "content": "from math import sin, cos, sqrt\nfrom time import time\n\nPOINTS = 10000000\n\n\nclass Point:\n    x: float\n    y: float\n    z: float\n\n    def __init__(self, i):\n        self.x = x = sin(i)\n        self.y = cos(i) * 3\n        self.z = (x * x) / 2\n\n    def __repr__(self):\n        return f\"<Point: x={self.x}, y={self.y}, z={self.z}>\"\n\n    def normalize(self):\n        x = self.x\n        y = self.y\n        z = self.z\n        norm = sqrt(x * x + y * y + z * z)\n        self.x /= norm\n        self.y /= norm\n        self.z /= norm\n\n    def maximize(self, other):\n        self.x = self.x if self.x > other.x else other.x\n        self.y = self.y if self.y > other.y else other.y\n        self.z = self.z if self.z > other.z else other.z\n        return self\n\n\ndef maximize(points):\n    next = points[0]\n    for p in points[1:]:\n        next = next.maximize(p)\n    return next\n\n\ndef benchmark(n):\n    points = [None] * n\n    for i in range(n):\n        points[i] = Point(i)\n    for p in points:\n        p.normalize()\n    return maximize(points)\n\n\nt0 = time()\nprint(benchmark(POINTS))\nt1 = time()\nprint(t1 - t0)\n"
  },
  {
    "path": "bench/codon/go.codon",
    "content": "\"\"\"\nGo board game\n\"\"\"\nimport math\nimport random\nfrom time import time\n\nSIZE = 9\nGAMES = 200\nKOMI = 7.5\nEMPTY, WHITE, BLACK = 0, 1, 2\nSHOW = {EMPTY: '.', WHITE: 'o', BLACK: 'x'}\nPASS = -1\nMAXMOVES = SIZE * SIZE * 3\nTIMESTAMP = 0\nMOVES = 0\n\n\ndef to_pos(x, y):\n    return y * SIZE + x\n\n\ndef to_xy(pos):\n    y, x = divmod(pos, SIZE)\n    return x, y\n\n\n@dataclass(init=False)\nclass Square[Board]:\n    board: Board\n    pos: int\n    timestamp: int\n    removestamp: int\n    zobrist_strings: List[int]\n    neighbours: Optional[List[Square[Board]]]\n    color: int\n    used: bool\n    reference: Optional[Square[Board]]\n    ledges: int\n    temp_ledges: int\n\n    def __init__(self, board, pos):\n        self.board = board\n        self.pos = pos\n        self.timestamp = TIMESTAMP\n        self.removestamp = TIMESTAMP\n        self.zobrist_strings = [random.randrange(9223372036854775807)\n                                for i in range(3)]\n        self.neighbours = None\n        self.color = EMPTY\n        self.used = False\n        self.reference = None\n        self.ledges = 0\n        self.temp_ledges = 0\n\n    def set_neighbours(self):\n        x, y = self.pos % SIZE, self.pos // SIZE\n        self.neighbours = []\n        for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:\n            newx, newy = x + dx, y + dy\n            if 0 <= newx < SIZE and 0 <= newy < SIZE:\n                self.neighbours.append(self.board.squares[to_pos(newx, newy)])\n\n    def move(self, color):\n        global TIMESTAMP, MOVES\n        TIMESTAMP += 1\n        MOVES += 1\n        self.board.zobrist.update(self, color)\n        self.color = color\n        self.reference = self\n        self.ledges = 0\n        self.used = True\n        for neighbour in self.neighbours:\n            neighcolor = neighbour.color\n            if neighcolor == EMPTY:\n                self.ledges += 1\n            else:\n                neighbour_ref = neighbour.find(update=True)\n                if neighcolor == color:\n                    if neighbour_ref.reference.pos != self.pos:\n                        self.ledges += neighbour_ref.ledges\n                        neighbour_ref.reference = self\n                    self.ledges -= 1\n                else:\n                    neighbour_ref.ledges -= 1\n                    if neighbour_ref.ledges == 0:\n                        neighbour.remove(neighbour_ref)\n        self.board.zobrist.add()\n\n    def remove(self, reference, update=True):\n        self.board.zobrist.update(self, EMPTY)\n        self.removestamp = TIMESTAMP\n        if update:\n            self.color = EMPTY\n            self.board.emptyset.add(self.pos)\n#            if color == BLACK:\n#                self.board.black_dead += 1\n#            else:\n#                self.board.white_dead += 1\n        for neighbour in self.neighbours:\n            if neighbour.color != EMPTY and neighbour.removestamp != TIMESTAMP:\n                neighbour_ref = neighbour.find(update)\n                if neighbour_ref.pos == reference.pos:\n                    neighbour.remove(reference, update)\n                else:\n                    if update:\n                        neighbour_ref.ledges += 1\n\n    def find(self, update=False):\n        reference = self.reference\n        if reference.pos != self.pos:\n            reference = reference.find(update)\n            if update:\n                self.reference = reference\n        return reference\n\n    def __repr__(self):\n        return repr(to_xy(self.pos))\n\n\nclass EmptySet[Board]:\n    board: Board\n    empties: List[int]\n    empty_pos: List[int]\n\n    def __init__(self, board):\n        self.board = board\n        self.empties = list(range(SIZE * SIZE))\n        self.empty_pos = list(range(SIZE * SIZE))\n\n    def random_choice(self):\n        choices = len(self.empties)\n        while choices:\n            i = int(random.random() * choices)\n            pos = self.empties[i]\n            if self.board.useful(pos):\n                return pos\n            choices -= 1\n            self.set(i, self.empties[choices])\n            self.set(choices, pos)\n        return PASS\n\n    def add(self, pos):\n        self.empty_pos[pos] = len(self.empties)\n        self.empties.append(pos)\n\n    def remove(self, pos):\n        self.set(self.empty_pos[pos], self.empties[len(self.empties) - 1])\n        self.empties.pop()\n\n    def set(self, i, pos):\n        self.empties[i] = pos\n        self.empty_pos[pos] = i\n\n\nclass ZobristHash[Board]:\n    board: Board\n    hash_set: Set[int]\n    hash: int\n\n    def __init__(self, board):\n        self.board = board\n        self.hash_set = set()\n        self.hash = 0\n        for square in self.board.squares:\n            self.hash ^= square.zobrist_strings[EMPTY]\n        self.hash_set.clear()\n        self.hash_set.add(self.hash)\n\n    def update(self, square, color):\n        self.hash ^= square.zobrist_strings[square.color]\n        self.hash ^= square.zobrist_strings[color]\n\n    def add(self):\n        self.hash_set.add(self.hash)\n\n    def dupe(self):\n        return self.hash in self.hash_set\n\n\nclass Board:\n    squares: List[Square[Board]]\n    emptyset: EmptySet[Board]\n    zobrist: ZobristHash[Board]\n    color: int\n    finished: bool\n    lastmove: int\n    history: List[int]\n    white_dead: int\n    black_dead: int\n\n    def __init__(self):\n        self.squares = [Square(self, pos) for pos in range(SIZE * SIZE)]\n        for square in self.squares:\n            square.set_neighbours()\n        self.reset()\n\n    def reset(self):\n        for square in self.squares:\n            square.color = EMPTY\n            square.used = False\n        self.emptyset = EmptySet(self)\n        self.zobrist = ZobristHash(self)\n        self.color = BLACK\n        self.finished = False\n        self.lastmove = -2\n        self.history = []\n        self.white_dead = 0\n        self.black_dead = 0\n\n    def move(self, pos):\n        square = self.squares[pos]\n        if pos != PASS:\n            square.move(self.color)\n            self.emptyset.remove(square.pos)\n        elif self.lastmove == PASS:\n            self.finished = True\n        if self.color == BLACK:\n            self.color = WHITE\n        else:\n            self.color = BLACK\n        self.lastmove = pos\n        self.history.append(pos)\n\n    def random_move(self):\n        return self.emptyset.random_choice()\n\n    def useful_fast(self, square):\n        if not square.used:\n            for neighbour in square.neighbours:\n                if neighbour.color == EMPTY:\n                    return True\n        return False\n\n    def useful(self, pos):\n        global TIMESTAMP\n        TIMESTAMP += 1\n        square = self.squares[pos]\n        if self.useful_fast(square):\n            return True\n        old_hash = self.zobrist.hash\n        self.zobrist.update(square, self.color)\n        empties = opps = weak_opps = neighs = weak_neighs = 0\n        for neighbour in square.neighbours:\n            neighcolor = neighbour.color\n            if neighcolor == EMPTY:\n                empties += 1\n                continue\n            neighbour_ref = neighbour.find()\n            if neighbour_ref.timestamp != TIMESTAMP:\n                if neighcolor == self.color:\n                    neighs += 1\n                else:\n                    opps += 1\n                neighbour_ref.timestamp = TIMESTAMP\n                neighbour_ref.temp_ledges = neighbour_ref.ledges\n            neighbour_ref.temp_ledges -= 1\n            if neighbour_ref.temp_ledges == 0:\n                if neighcolor == self.color:\n                    weak_neighs += 1\n                else:\n                    weak_opps += 1\n                    neighbour_ref.remove(neighbour_ref, update=False)\n        dupe = self.zobrist.dupe()\n        self.zobrist.hash = old_hash\n        strong_neighs = neighs - weak_neighs\n        strong_opps = opps - weak_opps\n        return not dupe and \\\n            (empties or weak_opps or (strong_neighs and (strong_opps or weak_neighs)))\n\n    def useful_moves(self):\n        return [pos for pos in self.emptyset.empties if self.useful(pos)]\n\n    def replay(self, history):\n        for pos in history:\n            self.move(pos)\n\n    def score(self, color):\n        if color == WHITE:\n            count = KOMI + self.black_dead\n        else:\n            count = float(self.white_dead)\n        for square in self.squares:\n            squarecolor = square.color\n            if squarecolor == color:\n                count += 1\n            elif squarecolor == EMPTY:\n                surround = 0\n                for neighbour in square.neighbours:\n                    if neighbour.color == color:\n                        surround += 1\n                if surround == len(square.neighbours):\n                    count += 1\n        return count\n\n    def check(self):\n        for square in self.squares:\n            if square.color == EMPTY:\n                continue\n\n            members1 = set([square])\n            changed = True\n            while changed:\n                changed = False\n                for member in members1.copy():\n                    for neighbour in member.neighbours:\n                        if neighbour.color == square.color and neighbour not in members1:\n                            changed = True\n                            members1.add(neighbour)\n            ledges1 = 0\n            for member in members1:\n                for neighbour in member.neighbours:\n                    if neighbour.color == EMPTY:\n                        ledges1 += 1\n\n            root = square.find()\n\n            # print 'members1', square, root, members1\n            # print 'ledges1', square, ledges1\n\n            members2 = set()\n            for square2 in self.squares:\n                if square2.color != EMPTY and square2.find() == root:\n                    members2.add(square2)\n\n            ledges2 = root.ledges\n            # print 'members2', square, root, members1\n            # print 'ledges2', square, ledges2\n\n            assert members1 == members2\n            assert ledges1 == ledges2\n\n            set(self.emptyset.empties)\n\n            empties2 = set()\n            for square in self.squares:\n                if square.color == EMPTY:\n                    empties2.add(square.pos)\n\n    def __repr__(self):\n        result = []\n        for y in range(SIZE):\n            start = to_pos(0, y)\n            result.append(''.join(\n                [SHOW[square.color] + ' ' for square in self.squares[start:start + SIZE]]))\n        return '\\n'.join(result)\n\n\nclass UCTNode:\n    bestchild: Optional[UCTNode]\n    pos: int\n    wins: int\n    losses: int\n    pos_child: List[Optional[UCTNode]]\n    parent: Optional[UCTNode]\n    unexplored: List[int]\n\n    def __init__(self):\n        self.bestchild = None\n        self.pos = -1\n        self.wins = 0\n        self.losses = 0\n        self.pos_child = [None for x in range(SIZE * SIZE)]\n        self.parent = None\n        self.unexplored = []\n\n    def play(self, board):\n        \"\"\" uct tree search \"\"\"\n        color = board.color\n        node = self\n        path = [node]\n        while True:\n            pos = node.select(board)\n            if pos == PASS:\n                break\n            board.move(pos)\n            child = node.pos_child[pos]\n            if not child:\n                child = node.pos_child[pos] = UCTNode()\n                child.unexplored = board.useful_moves()\n                child.pos = pos\n                child.parent = node\n                path.append(child)\n                break\n            path.append(child)\n            node = child\n        self.random_playout(board)\n        self.update_path(board, color, path)\n\n    def select(self, board):\n        \"\"\" select move; unexplored children first, then according to uct value \"\"\"\n        if self.unexplored:\n            i = random.randrange(len(self.unexplored))\n            pos = self.unexplored[i]\n            self.unexplored[i] = self.unexplored[len(self.unexplored) - 1]\n            self.unexplored.pop()\n            return pos\n        elif self.bestchild:\n            return self.bestchild.pos\n        else:\n            return PASS\n\n    def random_playout(self, board):\n        \"\"\" random play until both players pass \"\"\"\n        for x in range(MAXMOVES):  # XXX while not self.finished?\n            if board.finished:\n                break\n            board.move(board.random_move())\n\n    def update_path(self, board, color, path):\n        \"\"\" update win/loss count along path \"\"\"\n        wins = board.score(BLACK) >= board.score(WHITE)\n        for node in path:\n            if color == BLACK:\n                color = WHITE\n            else:\n                color = BLACK\n            if wins == (color == BLACK):\n                node.wins += 1\n            else:\n                node.losses += 1\n            if node.parent:\n                node.parent.bestchild = node.parent.best_child()\n\n    def score(self):\n        winrate = self.wins / float(self.wins + self.losses)\n        parentvisits = self.parent.wins + self.parent.losses\n        if not parentvisits:\n            return winrate\n        nodevisits = self.wins + self.losses\n        return winrate + math.sqrt((math.log(parentvisits)) / (5 * nodevisits))\n\n    def best_child(self):\n        maxscore = -1.\n        maxchild = None\n        for child in self.pos_child:\n            if child and child.score() > maxscore:\n                maxchild = child\n                maxscore = child.score()\n        return maxchild\n\n    def best_visited(self):\n        maxvisits = -1\n        maxchild = None\n        for child in self.pos_child:\n            #            if child:\n            # print to_xy(child.pos), child.wins, child.losses, child.score()\n            if child and (child.wins + child.losses) > maxvisits:\n                maxvisits, maxchild = (child.wins + child.losses), child\n        return maxchild\n\n\n# def user_move(board):\n#     while True:\n#         text = input('?').strip()\n#         if text == 'p':\n#             return PASS\n#         if text == 'q':\n#             raise EOFError\n#         try:\n#             x, y = [int(i) for i in text.split()]\n#         except ValueError:\n#             continue\n#         if not (0 <= x < SIZE and 0 <= y < SIZE):\n#             continue\n#         pos = to_pos(x, y)\n#         if board.useful(pos):\n#             return pos\n\n\ndef computer_move(board):\n    pos = board.random_move()\n    if pos == PASS:\n        return PASS\n    tree = UCTNode()\n    tree.unexplored = board.useful_moves()\n    nboard = Board()\n    for game in range(GAMES):\n        node = tree\n        nboard.reset()\n        nboard.replay(board.history)\n        node.play(nboard)\n    return tree.best_visited().pos\n\n\ndef versus_cpu():\n    for i in range(100):\n        random.seed(i)\n        board = Board()\n        computer_move(board)\n\n\nif __name__ == \"__main__\":\n    t0 = time()\n    versus_cpu()\n    t1 = time()\n    print(t1 - t0)\n"
  },
  {
    "path": "bench/codon/go.py",
    "content": "\"\"\"\nGo board game\n\"\"\"\nimport math\nimport random\nfrom time import time\n\nSIZE = 9\nGAMES = 200\nKOMI = 7.5\nEMPTY, WHITE, BLACK = 0, 1, 2\nSHOW = {EMPTY: '.', WHITE: 'o', BLACK: 'x'}\nPASS = -1\nMAXMOVES = SIZE * SIZE * 3\nTIMESTAMP = 0\nMOVES = 0\n\n\ndef to_pos(x, y):\n    return y * SIZE + x\n\n\ndef to_xy(pos):\n    y, x = divmod(pos, SIZE)\n    return x, y\n\n\nclass Square:\n\n    def __init__(self, board, pos):\n        self.board = board\n        self.pos = pos\n        self.timestamp = TIMESTAMP\n        self.removestamp = TIMESTAMP\n        self.zobrist_strings = [random.randrange(9223372036854775807)\n                                for i in range(3)]\n\n    def set_neighbours(self):\n        x, y = self.pos % SIZE, self.pos // SIZE\n        self.neighbours = []\n        for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:\n            newx, newy = x + dx, y + dy\n            if 0 <= newx < SIZE and 0 <= newy < SIZE:\n                self.neighbours.append(self.board.squares[to_pos(newx, newy)])\n\n    def move(self, color):\n        global TIMESTAMP, MOVES\n        TIMESTAMP += 1\n        MOVES += 1\n        self.board.zobrist.update(self, color)\n        self.color = color\n        self.reference = self\n        self.ledges = 0\n        self.used = True\n        for neighbour in self.neighbours:\n            neighcolor = neighbour.color\n            if neighcolor == EMPTY:\n                self.ledges += 1\n            else:\n                neighbour_ref = neighbour.find(update=True)\n                if neighcolor == color:\n                    if neighbour_ref.reference.pos != self.pos:\n                        self.ledges += neighbour_ref.ledges\n                        neighbour_ref.reference = self\n                    self.ledges -= 1\n                else:\n                    neighbour_ref.ledges -= 1\n                    if neighbour_ref.ledges == 0:\n                        neighbour.remove(neighbour_ref)\n        self.board.zobrist.add()\n\n    def remove(self, reference, update=True):\n        self.board.zobrist.update(self, EMPTY)\n        self.removestamp = TIMESTAMP\n        if update:\n            self.color = EMPTY\n            self.board.emptyset.add(self.pos)\n#            if color == BLACK:\n#                self.board.black_dead += 1\n#            else:\n#                self.board.white_dead += 1\n        for neighbour in self.neighbours:\n            if neighbour.color != EMPTY and neighbour.removestamp != TIMESTAMP:\n                neighbour_ref = neighbour.find(update)\n                if neighbour_ref.pos == reference.pos:\n                    neighbour.remove(reference, update)\n                else:\n                    if update:\n                        neighbour_ref.ledges += 1\n\n    def find(self, update=False):\n        reference = self.reference\n        if reference.pos != self.pos:\n            reference = reference.find(update)\n            if update:\n                self.reference = reference\n        return reference\n\n    def __repr__(self):\n        return repr(to_xy(self.pos))\n\n\nclass EmptySet:\n\n    def __init__(self, board):\n        self.board = board\n        self.empties = list(range(SIZE * SIZE))\n        self.empty_pos = list(range(SIZE * SIZE))\n\n    def random_choice(self):\n        choices = len(self.empties)\n        while choices:\n            i = int(random.random() * choices)\n            pos = self.empties[i]\n            if self.board.useful(pos):\n                return pos\n            choices -= 1\n            self.set(i, self.empties[choices])\n            self.set(choices, pos)\n        return PASS\n\n    def add(self, pos):\n        self.empty_pos[pos] = len(self.empties)\n        self.empties.append(pos)\n\n    def remove(self, pos):\n        self.set(self.empty_pos[pos], self.empties[len(self.empties) - 1])\n        self.empties.pop()\n\n    def set(self, i, pos):\n        self.empties[i] = pos\n        self.empty_pos[pos] = i\n\n\nclass ZobristHash:\n\n    def __init__(self, board):\n        self.board = board\n        self.hash_set = set()\n        self.hash = 0\n        for square in self.board.squares:\n            self.hash ^= square.zobrist_strings[EMPTY]\n        self.hash_set.clear()\n        self.hash_set.add(self.hash)\n\n    def update(self, square, color):\n        self.hash ^= square.zobrist_strings[square.color]\n        self.hash ^= square.zobrist_strings[color]\n\n    def add(self):\n        self.hash_set.add(self.hash)\n\n    def dupe(self):\n        return self.hash in self.hash_set\n\n\nclass Board:\n\n    def __init__(self):\n        self.squares = [Square(self, pos) for pos in range(SIZE * SIZE)]\n        for square in self.squares:\n            square.set_neighbours()\n        self.reset()\n\n    def reset(self):\n        for square in self.squares:\n            square.color = EMPTY\n            square.used = False\n        self.emptyset = EmptySet(self)\n        self.zobrist = ZobristHash(self)\n        self.color = BLACK\n        self.finished = False\n        self.lastmove = -2\n        self.history = []\n        self.white_dead = 0\n        self.black_dead = 0\n\n    def move(self, pos):\n        square = self.squares[pos]\n        if pos != PASS:\n            square.move(self.color)\n            self.emptyset.remove(square.pos)\n        elif self.lastmove == PASS:\n            self.finished = True\n        if self.color == BLACK:\n            self.color = WHITE\n        else:\n            self.color = BLACK\n        self.lastmove = pos\n        self.history.append(pos)\n\n    def random_move(self):\n        return self.emptyset.random_choice()\n\n    def useful_fast(self, square):\n        if not square.used:\n            for neighbour in square.neighbours:\n                if neighbour.color == EMPTY:\n                    return True\n        return False\n\n    def useful(self, pos):\n        global TIMESTAMP\n        TIMESTAMP += 1\n        square = self.squares[pos]\n        if self.useful_fast(square):\n            return True\n        old_hash = self.zobrist.hash\n        self.zobrist.update(square, self.color)\n        empties = opps = weak_opps = neighs = weak_neighs = 0\n        for neighbour in square.neighbours:\n            neighcolor = neighbour.color\n            if neighcolor == EMPTY:\n                empties += 1\n                continue\n            neighbour_ref = neighbour.find()\n            if neighbour_ref.timestamp != TIMESTAMP:\n                if neighcolor == self.color:\n                    neighs += 1\n                else:\n                    opps += 1\n                neighbour_ref.timestamp = TIMESTAMP\n                neighbour_ref.temp_ledges = neighbour_ref.ledges\n            neighbour_ref.temp_ledges -= 1\n            if neighbour_ref.temp_ledges == 0:\n                if neighcolor == self.color:\n                    weak_neighs += 1\n                else:\n                    weak_opps += 1\n                    neighbour_ref.remove(neighbour_ref, update=False)\n        dupe = self.zobrist.dupe()\n        self.zobrist.hash = old_hash\n        strong_neighs = neighs - weak_neighs\n        strong_opps = opps - weak_opps\n        return not dupe and \\\n            (empties or weak_opps or (strong_neighs and (strong_opps or weak_neighs)))\n\n    def useful_moves(self):\n        return [pos for pos in self.emptyset.empties if self.useful(pos)]\n\n    def replay(self, history):\n        for pos in history:\n            self.move(pos)\n\n    def score(self, color):\n        if color == WHITE:\n            count = KOMI + self.black_dead\n        else:\n            count = self.white_dead\n        for square in self.squares:\n            squarecolor = square.color\n            if squarecolor == color:\n                count += 1\n            elif squarecolor == EMPTY:\n                surround = 0\n                for neighbour in square.neighbours:\n                    if neighbour.color == color:\n                        surround += 1\n                if surround == len(square.neighbours):\n                    count += 1\n        return count\n\n    def check(self):\n        for square in self.squares:\n            if square.color == EMPTY:\n                continue\n\n            members1 = set([square])\n            changed = True\n            while changed:\n                changed = False\n                for member in members1.copy():\n                    for neighbour in member.neighbours:\n                        if neighbour.color == square.color and neighbour not in members1:\n                            changed = True\n                            members1.add(neighbour)\n            ledges1 = 0\n            for member in members1:\n                for neighbour in member.neighbours:\n                    if neighbour.color == EMPTY:\n                        ledges1 += 1\n\n            root = square.find()\n\n            # print 'members1', square, root, members1\n            # print 'ledges1', square, ledges1\n\n            members2 = set()\n            for square2 in self.squares:\n                if square2.color != EMPTY and square2.find() == root:\n                    members2.add(square2)\n\n            ledges2 = root.ledges\n            # print 'members2', square, root, members1\n            # print 'ledges2', square, ledges2\n\n            assert members1 == members2\n            assert ledges1 == ledges2, ('ledges differ at %r: %d %d' % (\n                square, ledges1, ledges2))\n\n            set(self.emptyset.empties)\n\n            empties2 = set()\n            for square in self.squares:\n                if square.color == EMPTY:\n                    empties2.add(square.pos)\n\n    def __repr__(self):\n        result = []\n        for y in range(SIZE):\n            start = to_pos(0, y)\n            result.append(''.join(\n                [SHOW[square.color] + ' ' for square in self.squares[start:start + SIZE]]))\n        return '\\n'.join(result)\n\n\nclass UCTNode:\n\n    def __init__(self):\n        self.bestchild = None\n        self.pos = -1\n        self.wins = 0\n        self.losses = 0\n        self.pos_child = [None for x in range(SIZE * SIZE)]\n        self.parent = None\n\n    def play(self, board):\n        \"\"\" uct tree search \"\"\"\n        color = board.color\n        node = self\n        path = [node]\n        while True:\n            pos = node.select(board)\n            if pos == PASS:\n                break\n            board.move(pos)\n            child = node.pos_child[pos]\n            if not child:\n                child = node.pos_child[pos] = UCTNode()\n                child.unexplored = board.useful_moves()\n                child.pos = pos\n                child.parent = node\n                path.append(child)\n                break\n            path.append(child)\n            node = child\n        self.random_playout(board)\n        self.update_path(board, color, path)\n\n    def select(self, board):\n        \"\"\" select move; unexplored children first, then according to uct value \"\"\"\n        if self.unexplored:\n            i = random.randrange(len(self.unexplored))\n            pos = self.unexplored[i]\n            self.unexplored[i] = self.unexplored[len(self.unexplored) - 1]\n            self.unexplored.pop()\n            return pos\n        elif self.bestchild:\n            return self.bestchild.pos\n        else:\n            return PASS\n\n    def random_playout(self, board):\n        \"\"\" random play until both players pass \"\"\"\n        for x in range(MAXMOVES):  # XXX while not self.finished?\n            if board.finished:\n                break\n            board.move(board.random_move())\n\n    def update_path(self, board, color, path):\n        \"\"\" update win/loss count along path \"\"\"\n        wins = board.score(BLACK) >= board.score(WHITE)\n        for node in path:\n            if color == BLACK:\n                color = WHITE\n            else:\n                color = BLACK\n            if wins == (color == BLACK):\n                node.wins += 1\n            else:\n                node.losses += 1\n            if node.parent:\n                node.parent.bestchild = node.parent.best_child()\n\n    def score(self):\n        winrate = self.wins / float(self.wins + self.losses)\n        parentvisits = self.parent.wins + self.parent.losses\n        if not parentvisits:\n            return winrate\n        nodevisits = self.wins + self.losses\n        return winrate + math.sqrt((math.log(parentvisits)) / (5 * nodevisits))\n\n    def best_child(self):\n        maxscore = -1\n        maxchild = None\n        for child in self.pos_child:\n            if child and child.score() > maxscore:\n                maxchild = child\n                maxscore = child.score()\n        return maxchild\n\n    def best_visited(self):\n        maxvisits = -1\n        maxchild = None\n        for child in self.pos_child:\n            #            if child:\n            # print to_xy(child.pos), child.wins, child.losses, child.score()\n            if child and (child.wins + child.losses) > maxvisits:\n                maxvisits, maxchild = (child.wins + child.losses), child\n        return maxchild\n\n\n# def user_move(board):\n#     while True:\n#         text = input('?').strip()\n#         if text == 'p':\n#             return PASS\n#         if text == 'q':\n#             raise EOFError\n#         try:\n#             x, y = [int(i) for i in text.split()]\n#         except ValueError:\n#             continue\n#         if not (0 <= x < SIZE and 0 <= y < SIZE):\n#             continue\n#         pos = to_pos(x, y)\n#         if board.useful(pos):\n#             return pos\n\n\ndef computer_move(board):\n    pos = board.random_move()\n    if pos == PASS:\n        return PASS\n    tree = UCTNode()\n    tree.unexplored = board.useful_moves()\n    nboard = Board()\n    for game in range(GAMES):\n        node = tree\n        nboard.reset()\n        nboard.replay(board.history)\n        node.play(nboard)\n    return tree.best_visited().pos\n\n\ndef versus_cpu():\n    for i in range(100):\n        random.seed(i)\n        board = Board()\n        computer_move(board)\n\n\nif __name__ == \"__main__\":\n    t0 = time()\n    versus_cpu()\n    t1 = time()\n    print(t1 - t0)\n"
  },
  {
    "path": "bench/codon/mandelbrot.codon",
    "content": "import time\nMAX    = 1000  # maximum Mandelbrot iterations\nN      = 4096  # width and height of image\npixels = [0 for _ in range(N * N)]\n\ndef scale(x, a, b):\n    return a + (x/N)*(b - a)\n\nt0 = time.time()\n@par(gpu=True, collapse=2)\nfor i in range(N):\n    for j in range(N):\n        c = complex(scale(j, -2.00, 0.47), scale(i, -1.12, 1.12))\n        z = 0j\n        iteration = 0\n\n        while abs(z) <= 2 and iteration < MAX:\n            z = z**2 + c\n            iteration += 1\n\n        pixels[i*N + j] = int(255 * iteration/MAX)\nprint(sum(pixels))\nprint(time.time() - t0)\n"
  },
  {
    "path": "bench/codon/mandelbrot.py",
    "content": "import time\nMAX    = 1000  # maximum Mandelbrot iterations\nN      = 4096  # width and height of image\npixels = [0 for _ in range(N * N)]\n\ndef scale(x, a, b):\n    return a + (x/N)*(b - a)\n\nt0 = time.time()\nfor i in range(N):\n    for j in range(N):\n        c = complex(scale(j, -2.00, 0.47), scale(i, -1.12, 1.12))\n        z = 0j\n        iteration = 0\n\n        while abs(z) <= 2 and iteration < MAX:\n            z = z**2 + c\n            iteration += 1\n\n        pixels[i*N + j] = int(255 * iteration/MAX)\nprint(sum(pixels))\nprint(time.time() - t0)\n"
  },
  {
    "path": "bench/codon/nbody.cpp",
    "content": "#include <chrono>\n#include <cmath>\n#include <iostream>\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\nnamespace {\n\nconst double PI = 3.14159265358979323;\nconst double SOLAR_MASS = 4 * PI * PI;\nconst double DAYS_PER_YEAR = 365.24;\n\nstruct Body {\n  std::vector<double> r, v;\n  double m;\n};\n\nstd::unordered_map<std::string, Body> BODIES = {\n    {\"sun\", {{0.0, 0.0, 0.0}, {0.0, 0.0, 0.0}, SOLAR_MASS}},\n    {\"jupiter\",\n     {{4.84143144246472090e+00, -1.16032004402742839e+00, -1.03622044471123109e-01},\n      {1.66007664274403694e-03 * DAYS_PER_YEAR, 7.69901118419740425e-03 * DAYS_PER_YEAR,\n       -6.90460016972063023e-05 * DAYS_PER_YEAR},\n      9.54791938424326609e-04 * SOLAR_MASS}},\n    {\"saturn\",\n     {{8.34336671824457987e+00, 4.12479856412430479e+00, -4.03523417114321381e-01},\n      {-2.76742510726862411e-03 * DAYS_PER_YEAR,\n       4.99852801234917238e-03 * DAYS_PER_YEAR,\n       2.30417297573763929e-05 * DAYS_PER_YEAR},\n      2.85885980666130812e-04 * SOLAR_MASS}},\n    {\"uranus\",\n     {{1.28943695621391310e+01, -1.51111514016986312e+01, -2.23307578892655734e-01},\n      {2.96460137564761618e-03 * DAYS_PER_YEAR, 2.37847173959480950e-03 * DAYS_PER_YEAR,\n       -2.96589568540237556e-05 * DAYS_PER_YEAR},\n      4.36624404335156298e-05 * SOLAR_MASS}},\n    {\"neptune\",\n     {{1.53796971148509165e+01, -2.59193146099879641e+01, 1.79258772950371181e-01},\n      {2.68067772490389322e-03 * DAYS_PER_YEAR, 1.62824170038242295e-03 * DAYS_PER_YEAR,\n       -9.51592254519715870e-05 * DAYS_PER_YEAR},\n      5.15138902046611451e-05 * SOLAR_MASS}},\n};\n\ntemplate <typename K, typename V> auto values(std::unordered_map<K, V> &m) {\n  std::vector<V *> v;\n  v.reserve(m.size());\n  for (auto &e : m)\n    v.push_back(&e.second);\n  return v;\n}\n\ntemplate <typename T> auto combinations(const std::vector<T> &v) {\n  std::vector<std::pair<T, T>> p;\n  auto n = v.size();\n  p.reserve(n);\n  for (auto i = 0; i < n - 1; i++)\n    for (auto j = i + 1; j < n; j++)\n      p.push_back({v[i], v[j]});\n  return p;\n}\n\nstd::vector<Body *> SYSTEM = values(BODIES);\nauto PAIRS = combinations(SYSTEM);\n\nvoid advance(double dt, int n, std::vector<Body *> &bodies = SYSTEM,\n             std::vector<std::pair<Body *, Body *>> &pairs = PAIRS) {\n  for (int i = 0; i < n; i++) {\n    for (auto &pair : pairs) {\n      double x1 = pair.first->r[0], y1 = pair.first->r[1], z1 = pair.first->r[2];\n      auto &v1 = pair.first->v;\n      double m1 = pair.first->m;\n      double x2 = pair.second->r[0], y2 = pair.second->r[1], z2 = pair.second->r[2];\n      auto &v2 = pair.second->v;\n      double m2 = pair.second->m;\n      double dx = x1 - x2, dy = y1 - y2, dz = z1 - z2;\n      double mag = dt * std::pow((dx * dx + dy * dy + dz * dz), -1.5);\n      double b1m = m1 * mag;\n      double b2m = m2 * mag;\n      v1[0] -= dx * b2m;\n      v1[1] -= dy * b2m;\n      v1[2] -= dz * b2m;\n      v2[0] += dx * b1m;\n      v2[1] += dy * b1m;\n      v2[2] += dz * b1m;\n    }\n\n    for (auto *body : bodies) {\n      auto &r = body->r;\n      double vx = body->v[0], vy = body->v[1], vz = body->v[2];\n      r[0] += dt * vx;\n      r[1] += dt * vy;\n      r[2] += dt * vz;\n    }\n  }\n}\n\nvoid report_energy(std::vector<Body *> &bodies = SYSTEM,\n                   std::vector<std::pair<Body *, Body *>> &pairs = PAIRS,\n                   double e = 0.0) {\n  for (auto &pair : pairs) {\n    double x1 = pair.first->r[0], y1 = pair.first->r[1], z1 = pair.first->r[2];\n    auto &v1 = pair.first->v;\n    double m1 = pair.first->m;\n    double x2 = pair.second->r[0], y2 = pair.second->r[1], z2 = pair.second->r[2];\n    auto &v2 = pair.second->v;\n    double m2 = pair.second->m;\n    double dx = x1 - x2, dy = y1 - y2, dz = z1 - z2;\n    e -= (m1 * m2) / std::pow((dx * dx + dy * dy + dz * dz), 0.5);\n  }\n\n  for (auto *body : bodies) {\n    double vx = body->v[0], vy = body->v[1], vz = body->v[2];\n    double m = body->m;\n    e += m * (vx * vx + vy * vy + vz * vz) / 2.;\n  }\n\n  std::cout << e << std::endl;\n}\n\nvoid offset_momentum(Body &ref, std::vector<Body *> &bodies = SYSTEM, double px = 0.0,\n                     double py = 0.0, double pz = 0.0) {\n  for (auto *body : bodies) {\n    double vx = body->v[0], vy = body->v[1], vz = body->v[2];\n    double m = body->m;\n    px -= vx * m;\n    py -= vy * m;\n    pz -= vz * m;\n  }\n\n  auto &v = ref.v;\n  double m = ref.m;\n  v[0] = px / m;\n  v[1] = py / m;\n  v[2] = pz / m;\n}\n\n} // namespace\n\nint main(int argc, char *argv[]) {\n  using clock = std::chrono::high_resolution_clock;\n  using std::chrono::duration_cast;\n  using std::chrono::milliseconds;\n\n  auto t = clock::now();\n  std::string ref = \"sun\";\n  offset_momentum(BODIES[ref]);\n  report_energy();\n  advance(0.01, std::atoi(argv[1]));\n  report_energy();\n  std::cout << (duration_cast<milliseconds>(clock::now() - t).count() / 1e3)\n            << std::endl;\n}\n"
  },
  {
    "path": "bench/codon/nbody.py",
    "content": "# The Computer Language Benchmarks Game\n# http://benchmarksgame.alioth.debian.org/\n#\n# originally by Kevin Carson\n# modified by Tupteq, Fredrik Johansson, and Daniel Nanz\n# modified by Maciej Fijalkowski\n# modified by @arshajii\n# 2to3\n\nfrom time import time\nimport sys\n\ndef combinations(l):\n    result = []\n    for x in range(len(l) - 1):\n        ls = l[x+1:]\n        for y in ls:\n            result.append((l[x],y))\n    return result\n\nPI = 3.14159265358979323\nSOLAR_MASS = 4 * PI * PI\nDAYS_PER_YEAR = 365.24\n\nBODIES = {\n    'sun': ([0.0, 0.0, 0.0], [0.0, 0.0, 0.0], SOLAR_MASS),\n\n    'jupiter': ([4.84143144246472090e+00,\n                 -1.16032004402742839e+00,\n                 -1.03622044471123109e-01],\n                [1.66007664274403694e-03 * DAYS_PER_YEAR,\n                 7.69901118419740425e-03 * DAYS_PER_YEAR,\n                 -6.90460016972063023e-05 * DAYS_PER_YEAR],\n                9.54791938424326609e-04 * SOLAR_MASS),\n\n    'saturn': ([8.34336671824457987e+00,\n                4.12479856412430479e+00,\n                -4.03523417114321381e-01],\n               [-2.76742510726862411e-03 * DAYS_PER_YEAR,\n                4.99852801234917238e-03 * DAYS_PER_YEAR,\n                2.30417297573763929e-05 * DAYS_PER_YEAR],\n               2.85885980666130812e-04 * SOLAR_MASS),\n\n    'uranus': ([1.28943695621391310e+01,\n                -1.51111514016986312e+01,\n                -2.23307578892655734e-01],\n               [2.96460137564761618e-03 * DAYS_PER_YEAR,\n                2.37847173959480950e-03 * DAYS_PER_YEAR,\n                -2.96589568540237556e-05 * DAYS_PER_YEAR],\n               4.36624404335156298e-05 * SOLAR_MASS),\n\n    'neptune': ([1.53796971148509165e+01,\n                 -2.59193146099879641e+01,\n                 1.79258772950371181e-01],\n                [2.68067772490389322e-03 * DAYS_PER_YEAR,\n                 1.62824170038242295e-03 * DAYS_PER_YEAR,\n                 -9.51592254519715870e-05 * DAYS_PER_YEAR],\n                5.15138902046611451e-05 * SOLAR_MASS) }\n\n\nSYSTEM = list(BODIES.values())\nPAIRS = combinations(SYSTEM)\n\n\ndef advance(dt, n, bodies=SYSTEM, pairs=PAIRS):\n\n    for i in range(n):\n        for (([x1, y1, z1], v1, m1),\n             ([x2, y2, z2], v2, m2)) in pairs:\n            dx = x1 - x2\n            dy = y1 - y2\n            dz = z1 - z2\n            mag = dt * ((dx * dx + dy * dy + dz * dz) ** (-1.5))\n            b1m = m1 * mag\n            b2m = m2 * mag\n            v1[0] -= dx * b2m\n            v1[1] -= dy * b2m\n            v1[2] -= dz * b2m\n            v2[0] += dx * b1m\n            v2[1] += dy * b1m\n            v2[2] += dz * b1m\n        for (r, [vx, vy, vz], m) in bodies:\n            r[0] += dt * vx\n            r[1] += dt * vy\n            r[2] += dt * vz\n\n\ndef report_energy(bodies=SYSTEM, pairs=PAIRS, e=0.0):\n\n    for (((x1, y1, z1), v1, m1),\n         ((x2, y2, z2), v2, m2)) in pairs:\n        dx = x1 - x2\n        dy = y1 - y2\n        dz = z1 - z2\n        e -= (m1 * m2) / ((dx * dx + dy * dy + dz * dz) ** 0.5)\n    for (r, [vx, vy, vz], m) in bodies:\n        e += m * (vx * vx + vy * vy + vz * vz) / 2.\n    print(e)\n\ndef offset_momentum(ref, bodies=SYSTEM, px=0.0, py=0.0, pz=0.0):\n\n    for (r, [vx, vy, vz], m) in bodies:\n        px -= vx * m\n        py -= vy * m\n        pz -= vz * m\n    (r, v, m) = ref\n    v[0] = px / m\n    v[1] = py / m\n    v[2] = pz / m\n\ndef main(n, ref='sun'):\n    offset_momentum(BODIES[ref])\n    report_energy()\n    advance(0.01, n)\n    report_energy()\n\nt0 = time()\nmain(int(sys.argv[1]))\nt1 = time()\nprint(t1 - t0)\n"
  },
  {
    "path": "bench/codon/npbench.codon",
    "content": "import numpy as np\nfrom numpy.random import default_rng\nimport npbench_lib as bench\nimport time\n\n\ndef run(b, prep, **kwargs):\n    n = prep.__class__.__name__.split(\"(\")[0]\n    with time.timing(f\"{n}.prep\"):\n        data = prep(**kwargs)\n    with time.timing(f\"{n}.run\"):\n        if isinstance(data, Tuple):\n            return b(*data)\n        else:\n            return b(data)\n\n\ndef rng_complex(shape, rng):\n    return (rng.random(shape) + rng.random(shape) * 1j)\n\n\ndef adi(N, TSTEPS, datatype=np.float64):\n    u = np.fromfunction(lambda i, j: (i + N - j) / N, (N, N), dtype=datatype)\n    return TSTEPS, N, u\nrun(bench.adi, adi, N=500, TSTEPS=50)\n\n\ndef arc_distance(N):\n    rng = default_rng(42)\n    t0, p0, t1, p1 = rng.random((N, )), rng.random((N, )), rng.random(\n        (N, )), rng.random((N, ))\n    return t0, p0, t1, p1\nrun(bench.arc_distance, arc_distance, N=10000000)\n\n\ndef azimint_naive(N, npt):\n    rng = default_rng(42)\n    data, radius = rng.random((N, )), rng.random((N, ))\n    return data, radius, npt\nrun(bench.azimint_naive, azimint_naive, N=4000000, npt=1000)\n\n\ndef azimint_hist(N, npt):\n    rng = default_rng(42)\n    data, radius = rng.random((N, )), rng.random((N, ))\n    return data, radius, npt\nrun(bench.azimint_hist, azimint_hist, N=40000000, npt=1000)\n\n\ndef atax(M, N, datatype=np.float64):\n    fn = datatype(N)\n    x = np.fromfunction(lambda i: 1 + (i / fn), (N, ), dtype=datatype)\n    A = np.fromfunction(lambda i, j: ((i + j) % N) / (5 * M), (M, N),\n                        dtype=datatype)\n    return A, x\nrun(bench.atax, atax, M=20000, N=25000)\n\n\ndef bicg(M, N, datatype=np.float64):\n    A = np.fromfunction(lambda i, j: (i * (j + 1) % N) / N, (N, M),\n                        dtype=datatype)\n    p = np.fromfunction(lambda i: (i % M) / M, (M, ), dtype=datatype)\n    r = np.fromfunction(lambda i: (i % N) / N, (N, ), dtype=datatype)\n    return A, p, r\nrun(bench.bicg, bicg, M=20000, N=25000)\n\n\ndef cavity_flow(ny, nx, nt, nit, rho, nu):\n    u = np.zeros((ny, nx), dtype=np.float64)\n    v = np.zeros((ny, nx), dtype=np.float64)\n    p = np.zeros((ny, nx), dtype=np.float64)\n    dx = 2 / (nx - 1)\n    dy = 2 / (ny - 1)\n    dt = .1 / ((nx - 1) * (ny - 1))\n\n    return nx, ny, nt, nit, u, v, dt, dx, dy, p, rho, nu\nrun(bench.cavity_flow, cavity_flow, ny=101, nx=101, nt=700, nit=50, rho=1.0, nu=0.1)\n\n\ndef channel_flow(ny, nx, nit, rho, nu, F):\n    u = np.zeros((ny, nx), dtype=np.float64)\n    v = np.zeros((ny, nx), dtype=np.float64)\n    p = np.ones((ny, nx), dtype=np.float64)\n    dx = 2 / (nx - 1)\n    dy = 2 / (ny - 1)\n    dt = .1 / ((nx - 1) * (ny - 1))\n    return nit, u, v, dt, dx, dy, p, rho, nu, F\nrun(bench.channel_flow, channel_flow, ny=101, nx=101, nit=50, rho=1.0, nu=0.1, F=1.0)\n\n\ndef cholesky(N, datatype=np.float64):\n    A = np.empty((N, N), dtype=datatype)\n    for i in range(N):\n        A[i, :i + 1] = np.fromfunction(lambda j: (-j % N) / N + 1, (i + 1, ),\n                                       dtype=datatype)\n        A[i, i + 1:] = 0.0\n        A[i, i] = 1.0\n    A[:] = A @ np.transpose(A)\n    return A\nrun(bench.cholesky, cholesky, N=2000)\n\n\ndef cholesky2(N, datatype=np.float64):\n    A = np.empty((N, N), dtype=datatype)\n    for i in range(N):\n        A[i, :i + 1] = np.fromfunction(lambda j: (-j % N) / N + 1, (i + 1, ),\n                                       dtype=datatype)\n        A[i, i + 1:] = 0.0\n        A[i, i] = 1.0\n    A[:] = A @ np.transpose(A)\n    return A\nrun(bench.cholesky2, cholesky2, N=8000)\n\n\ndef compute(M, N):\n    rng = default_rng(42)\n    array_1 = rng.uniform(0, 1000, size=(M, N)).astype(np.int64)\n    array_2 = rng.uniform(0, 1000, size=(M, N)).astype(np.int64)\n    a = np.int64(4)\n    b = np.int64(3)\n    c = np.int64(9)\n    return array_1, array_2, a, b, c\nrun(bench.compute, compute, M=16000, N=16000)\n\n\ndef contour_integral(NR, NM, slab_per_bc, num_int_pts):\n    rng = default_rng(42)\n    Ham = rng_complex((slab_per_bc + 1, NR, NR), rng)\n    int_pts = rng_complex((num_int_pts, ), rng)\n    Y = rng_complex((NR, NM), rng)\n    return NR, NM, slab_per_bc, Ham, int_pts, Y\nrun(bench.contour_integral, contour_integral, NR=600, NM=1000, slab_per_bc=2, num_int_pts=32)\n\n\ndef conv2d_bias(C_in, C_out, H, K, N, W):\n    rng = default_rng(42)\n    # NHWC data layout\n    input = rng.random((N, H, W, C_in), dtype=np.float32)\n    # Weights\n    weights = rng.random((K, K, C_in, C_out), dtype=np.float32)\n    bias = rng.random((C_out, ), dtype=np.float32)\n    return input, weights, bias\nrun(bench.conv2d_bias, conv2d_bias, N=8, C_in=3, C_out=16, K=20, H=256, W=256)\n\n\ndef correlation(M, N, datatype=np.float64):\n    float_n = datatype(N)\n    data = np.fromfunction(lambda i, j: (i * j) / M + i, (N, M),\n                           dtype=datatype)\n    return M, float_n, data\nrun(bench.correlation, correlation, M=3200, N=4000)\n\n\ndef covariance(M, N, datatype=np.float64):\n    float_n = datatype(N)\n    data = np.fromfunction(lambda i, j: (i * j) / M, (N, M), dtype=datatype)\n    return M, float_n, data\nrun(bench.covariance, covariance, M=3200, N=4000)\n\n\ndef crc16(N):\n    rng = default_rng(42)\n    data = rng.integers(0, 256, size=(N, ), dtype=np.uint8)\n    return data\nrun(bench.crc16, crc16, N=1000000)\n\n\ndef deriche(W, H, datatype=np.float64):\n    alpha = datatype(0.25)\n    imgIn = np.fromfunction(lambda i, j:\n                            ((313 * i + 991 * j) % 65536) / 65535.0, (W, H),\n                            dtype=datatype)\n    return alpha, imgIn\nrun(bench.deriche, deriche, W=7680, H=4320)\n\n\ndef doitgen(NR, NQ, NP, datatype=np.float64):\n    A = np.fromfunction(lambda i, j, k: ((i * j + k) % NP) / NP, (NR, NQ, NP),\n                        dtype=datatype)\n    C4 = np.fromfunction(lambda i, j: (i * j % NP) / NP, (NP, NP),\n                         dtype=datatype)\n    return NR, NQ, NP, A, C4\nrun(bench.doitgen, doitgen, NR=220, NQ=250, NP=512)\n\n\ndef durbin(N, datatype=np.float64):\n    r = np.fromfunction(lambda i: N + 1 - i, (N, ), dtype=datatype)\n    return r\nrun(bench.durbin, durbin, N=20000)\n\n\ndef fdtd_2d(TMAX, NX, NY, datatype=np.float64):\n    ex = np.fromfunction(lambda i, j: (i * (j + 1)) / NX, (NX, NY),\n                         dtype=datatype)\n    ey = np.fromfunction(lambda i, j: (i * (j + 2)) / NY, (NX, NY),\n                         dtype=datatype)\n    hz = np.fromfunction(lambda i, j: (i * (j + 3)) / NX, (NX, NY),\n                         dtype=datatype)\n    _fict_ = np.fromfunction(lambda i: i, (TMAX, ), dtype=datatype)\n    return TMAX, ex, ey, hz, _fict_\nrun(bench.fdtd_2d, fdtd_2d, TMAX=500, NX=1000, NY=1200)\n\n\ndef floyd_warshall(N, datatype=np.int32):\n    path = np.fromfunction(lambda i, j: i * j % 7i32 + 1i32, (N, N), dtype=datatype)\n    for i in range(N):\n        for j in range(N):\n            if (i + j) % 13 == 0 or (i + j) % 7 == 0 or (i + j) % 11 == 0:\n                path[i, j] = 999\n    return path\nrun(bench.floyd_warshall, floyd_warshall, N=850)\n\n\ndef gemm(NI, NJ, NK, datatype=np.float64):\n    alpha = datatype(1.5)\n    beta = datatype(1.2)\n    C = np.fromfunction(lambda i, j: ((i * j + 1) % NI) / NI, (NI, NJ),\n                        dtype=datatype)\n    A = np.fromfunction(lambda i, k: (i * (k + 1) % NK) / NK, (NI, NK),\n                        dtype=datatype)\n    B = np.fromfunction(lambda k, j: (k * (j + 2) % NJ) / NJ, (NK, NJ),\n                        dtype=datatype)\n    return alpha, beta, C, A, B\nrun(bench.gemm, gemm, NI=7000, NJ=7500, NK=8000)\n\n\ndef gemver(N, datatype=np.float64):\n    alpha = datatype(1.5)\n    beta = datatype(1.2)\n    fn = datatype(N)\n    A = np.fromfunction(lambda i, j: (i * j % N) / N, (N, N), dtype=datatype)\n    u1 = np.fromfunction(lambda i: i, (N, ), dtype=datatype)\n    u2 = np.fromfunction(lambda i: ((i + 1) / fn) / 2.0, (N, ), dtype=datatype)\n    v1 = np.fromfunction(lambda i: ((i + 1) / fn) / 4.0, (N, ), dtype=datatype)\n    v2 = np.fromfunction(lambda i: ((i + 1) / fn) / 6.0, (N, ), dtype=datatype)\n    w = np.zeros((N, ), dtype=datatype)\n    x = np.zeros((N, ), dtype=datatype)\n    y = np.fromfunction(lambda i: ((i + 1) / fn) / 8.0, (N, ), dtype=datatype)\n    z = np.fromfunction(lambda i: ((i + 1) / fn) / 9.0, (N, ), dtype=datatype)\n    return alpha, beta, A, u1, v1, u2, v2, w, x, y, z\nrun(bench.gemver, gemver, N=10000)\n\n\ndef gesummv(N, datatype=np.float64):\n    alpha = datatype(1.5)\n    beta = datatype(1.2)\n    A = np.fromfunction(lambda i, j: ((i * j + 1) % N) / N, (N, N),\n                        dtype=datatype)\n    B = np.fromfunction(lambda i, j: ((i * j + 2) % N) / N, (N, N),\n                        dtype=datatype)\n    x = np.fromfunction(lambda i: (i % N) / N, (N, ), dtype=datatype)\n    return alpha, beta, A, B, x\nrun(bench.gesummv, gesummv, N=14000)\n\n\ndef go_fast(N):\n    rng = default_rng(42)\n    a = rng.random((N, N), dtype=np.float64)\n    return a\nrun(bench.go_fast, go_fast, N=20000)\n\n\ndef gramschmidt(M, N, datatype=np.float64):\n    rng = default_rng(42)\n\n    A = rng.random((M, N), dtype=datatype)\n    while np.linalg.matrix_rank(A) < N:\n        A = rng.random((M, N), dtype=datatype)\n    return A\nrun(bench.gramschmidt, gramschmidt, M=600, N=500)\n\n\ndef hdiff(I, J, K):\n    rng = default_rng(42)\n\n    # Define arrays\n    in_field = rng.random((I + 4, J + 4, K))\n    out_field = rng.random((I, J, K))\n    coeff = rng.random((I, J, K))\n    return in_field, out_field, coeff\nrun(bench.hdiff, hdiff, I=384, J=384, K=160)\n\n\ndef heat_3d(N, TSTEPS, datatype=np.float64):\n    A = np.fromfunction(lambda i, j, k: (i + j + (N - k)) * 10 / N, (N, N, N),\n                        dtype=datatype)\n    B = A.copy()  # TODO: np.copy(A)\n    return TSTEPS, A, B\nrun(bench.heat_3d, heat_3d, N=70, TSTEPS=100)\n\n\ndef jacobi_1d(N, TSTEPS, datatype=np.float64):\n    A = np.fromfunction(lambda i: (i + 2) / N, (N, ), dtype=datatype)\n    B = np.fromfunction(lambda i: (i + 3) / N, (N, ), dtype=datatype)\n    return TSTEPS, A, B\nrun(bench.jacobi_1d, jacobi_1d, N=34000, TSTEPS=8500)\n\n\ndef jacobi_2d(N, TSTEPS, datatype=np.float64):\n    A = np.fromfunction(lambda i, j: i * (j + 2) / N, (N, N), dtype=datatype)\n    B = np.fromfunction(lambda i, j: i * (j + 3) / N, (N, N), dtype=datatype)\n    return TSTEPS, A, B\nrun(bench.jacobi_2d, jacobi_2d, N=700, TSTEPS=200)\n\n\ndef k2mm(NI, NJ, NK, NL, datatype=np.float64):\n    alpha = datatype(1.5)\n    beta = datatype(1.2)\n    A = np.fromfunction(lambda i, j: ((i * j + 1) % NI) / NI, (NI, NK),\n                        dtype=datatype)\n    B = np.fromfunction(lambda i, j: (i * (j + 1) % NJ) / NJ, (NK, NJ),\n                        dtype=datatype)\n    C = np.fromfunction(lambda i, j: ((i * (j + 3) + 1) % NL) / NL, (NJ, NL),\n                        dtype=datatype)\n    D = np.fromfunction(lambda i, j: (i * (j + 2) % NK) / NK, (NI, NL),\n                        dtype=datatype)\n    return alpha, beta, A, B, C, D\nrun(bench.k2mm, k2mm, NI=6000, NJ=6500, NK=7000, NL=7500)\n\n\ndef k3mm(NI, NJ, NK, NL, NM, datatype=np.float64):\n    A = np.fromfunction(lambda i, j: ((i * j + 1) % NI) / (5 * NI), (NI, NK),\n                        dtype=datatype)\n    B = np.fromfunction(lambda i, j: ((i * (j + 1) + 2) % NJ) / (5 * NJ),\n                        (NK, NJ),\n                        dtype=datatype)\n    C = np.fromfunction(lambda i, j: (i * (j + 3) % NL) / (5 * NL), (NJ, NM),\n                        dtype=datatype)\n    D = np.fromfunction(lambda i, j: ((i * (j + 2) + 2) % NK) / (5 * NK),\n                        (NM, NL),\n                        dtype=datatype)\n    return A, B, C, D\nrun(bench.k3mm, k3mm, NI=5500, NJ=6000, NK=6500, NL=7000, NM=7500)\n\n\ndef lenet(N, H, W):\n    rng = default_rng(42)\n\n    H_conv1 = H - 4\n    W_conv1 = W - 4\n    H_pool1 = H_conv1 // 2\n    W_pool1 = W_conv1 // 2\n    H_conv2 = H_pool1 - 4\n    W_conv2 = W_pool1 - 4\n    H_pool2 = H_conv2 // 2\n    W_pool2 = W_conv2 // 2\n    C_before_fc1 = 16 * H_pool2 * W_pool2\n\n    # NHWC data layout\n    input = rng.random((N, H, W, 1), dtype=np.float32)\n    # Weights\n    conv1 = rng.random((5, 5, 1, 6), dtype=np.float32)\n    conv1bias = rng.random((6, ), dtype=np.float32)\n    conv2 = rng.random((5, 5, 6, 16), dtype=np.float32)\n    conv2bias = rng.random((16, ), dtype=np.float32)\n    fc1w = rng.random((C_before_fc1, 120), dtype=np.float32)\n    fc1b = rng.random((120, ), dtype=np.float32)\n    fc2w = rng.random((120, 84), dtype=np.float32)\n    fc2b = rng.random((84, ), dtype=np.float32)\n    fc3w = rng.random((84, 10), dtype=np.float32)\n    fc3b = rng.random((10, ), dtype=np.float32)\n\n    return (\n        input, conv1, conv1bias, conv2, conv2bias, fc1w, fc1b, fc2w, fc2b,\n        fc3w, fc3b, N, C_before_fc1\n    )\nrun(bench.lenet, lenet, N=16, H=256, W=256)\n\n\ndef lu(N, datatype=np.float64):\n    A = np.empty((N, N), dtype=datatype)\n    for i in range(N):\n        A[i, :i + 1] = np.fromfunction(lambda j: (-j % N) / N + 1, (i + 1, ),\n                                       dtype=datatype)\n        A[i, i + 1:] = 0.0\n        A[i, i] = 1.0\n    A[:] = A @ np.transpose(A)\n    return A\nrun(bench.lu, lu, N=2000)\n\n\ndef ludcmp(N, datatype=np.float64):\n    A = np.empty((N, N), dtype=datatype)\n    for i in range(N):\n        A[i, :i + 1] = np.fromfunction(lambda j: (-j % N) / N + 1, (i + 1, ),\n                                       dtype=datatype)\n        A[i, i + 1:] = 0.0\n        A[i, i] = 1.0\n    A[:] = A @ np.transpose(A)\n    fn = datatype(N)\n    b = np.fromfunction(lambda i: (i + 1) / fn / 2.0 + 4.0, (N, ),\n                        dtype=datatype)\n    return A, b\nrun(bench.ludcmp, ludcmp, N=2000)\n\n\ndef mandelbrot1(xmin, xmax, ymin, ymax, xn, yn, maxiter, horizon):\n    return xmin, xmax, ymin, ymax, xn, yn, maxiter, horizon\nrun(bench.mandelbrot1, mandelbrot1, xmin=-2.25, xmax=0.75, xn=1000, ymin=-1.25, ymax=1.25, yn=1000, maxiter=200, horizon=2.0)\n\n\ndef mandelbrot2(xmin, xmax, ymin, ymax, xn, yn, maxiter, horizon):\n    return xmin, xmax, ymin, ymax, xn, yn, maxiter, horizon\nrun(bench.mandelbrot2, mandelbrot2, xmin=-2.25, xmax=0.75, xn=1000, ymin=-1.25, ymax=1.25, yn=1000, maxiter=200, horizon=2.0)\n\n\ndef mlp(C_in, N, S0, S1, S2):\n    rng = default_rng(42)\n\n    mlp_sizes = [S0, S1, S2]  # [300, 100, 10]\n    # Inputs\n    input = np.random.rand(N, C_in).astype(np.float32)\n    # Weights\n    w1 = rng.random((C_in, mlp_sizes[0]), dtype=np.float32)\n    b1 = rng.random((mlp_sizes[0], ), dtype=np.float32)\n    w2 = rng.random((mlp_sizes[0], mlp_sizes[1]), dtype=np.float32)\n    b2 = rng.random((mlp_sizes[1], ), dtype=np.float32)\n    w3 = rng.random((mlp_sizes[1], mlp_sizes[2]), dtype=np.float32)\n    b3 = rng.random((mlp_sizes[2], ), dtype=np.float32)\n    return input, w1, b1, w2, b2, w3, b3\nrun(bench.mlp, mlp, C_in=3, N=8, S0=30000, S1=30000, S2=30000)\n\n\ndef mvt(N, datatype=np.float64):\n    x1 = np.fromfunction(lambda i: (i % N) / N, (N, ), dtype=datatype)\n    x2 = np.fromfunction(lambda i: ((i + 1) % N) / N, (N, ), dtype=datatype)\n    y_1 = np.fromfunction(lambda i: ((i + 3) % N) / N, (N, ), dtype=datatype)\n    y_2 = np.fromfunction(lambda i: ((i + 4) % N) / N, (N, ), dtype=datatype)\n    A = np.fromfunction(lambda i, j: (i * j % N) / N, (N, N), dtype=datatype)\n    return x1, x2, y_1, y_2, A\nrun(bench.mvt, mvt, N=22000)\n\n\ndef nbody(N, tEnd, dt, softening, G):\n    rng = default_rng(42)\n    mass = 20.0 * np.ones((N, 1)) / N  # total mass of particles is 20\n    pos = rng.random((N, 3))  # randomly selected positions and velocities\n    vel = rng.random((N, 3))\n    Nt = int(np.ceil(tEnd / dt))\n    return mass, pos, vel, N, Nt, dt, G, softening\nrun(bench.nbody, nbody, N=100, tEnd=10.0, dt=0.01, softening=0.1, G=1.0)\n\n\ndef nussinov(N, datatype=np.int32):\n    seq = np.fromfunction(lambda i: (i + 1i32) % 4i32, (N, ), dtype=datatype)\n    return N, seq\nrun(bench.nussinov, nussinov, N=500)\n\n\ndef resnet(N, W, H, C1, C2):\n    rng = default_rng(42)\n\n    # Input\n    input = rng.random((N, H, W, C1), dtype=np.float32)\n    # Weights\n    conv1 = rng.random((1, 1, C1, C2), dtype=np.float32)\n    conv2 = rng.random((3, 3, C2, C2), dtype=np.float32)\n    conv3 = rng.random((1, 1, C2, C1), dtype=np.float32)\n    return input, conv1, conv2, conv3\nrun(bench.resnet, resnet, N=8, W=56, H=56, C1=256, C2=64)\n\n\ndef scattering_self_energies(Nkz, NE, Nqz, Nw, N3D, NA, NB, Norb):\n    rng = default_rng(42)\n\n    neigh_idx = np.empty((NA, NB), dtype=np.int32)\n    for i in range(NA):\n        neigh_idx[i] = np.positive(np.arange(i - NB / 2, i + NB / 2) % NA)\n    dH = rng_complex((NA, NB, N3D, Norb, Norb), rng)\n    G = rng_complex((Nkz, NE, NA, Norb, Norb), rng)\n    D = rng_complex((Nqz, Nw, NA, NB, N3D, N3D), rng)\n    Sigma = np.zeros((Nkz, NE, NA, Norb, Norb), dtype=np.complex128)\n    return neigh_idx, dH, G, D, Sigma\nrun(bench.scattering_self_energies, scattering_self_energies, Nkz=4, NE=10, Nqz=4, Nw=3, N3D=3, NA=20, NB=4, Norb=4)\n\n\ndef seidel_2d(N, TSTEPS, datatype=np.float64):\n    A = np.fromfunction(lambda i, j: (i * (j + 2) + 2) / N, (N, N),\n                        dtype=datatype)\n    return TSTEPS, N, A\nrun(bench.seidel_2d, seidel_2d, N=400, TSTEPS=100)\n\n\ndef softmax(N, H, SM):\n    rng = default_rng(42)\n    x = rng.random((N, H, SM, SM), dtype=np.float32)\n    return x\nrun(bench.softmax, softmax, N=64, H=16, SM=512)\n\n\ndef spmv(M, N, nnz):\n    from python import numpy as NP\n    from python import numpy.random as NR\n    from python import scipy.sparse as SS\n\n    rng = NR.default_rng(42)\n    x: np.ndarray[float,1] = rng.random((N, ))\n    matrix = SS.random(\n        M, N, density=nnz / (M * N), format='csr', dtype=NP.float64, random_state=rng\n    )\n    rows: np.ndarray[u32,1] = NP.uint32(matrix.indptr)\n    cols: np.ndarray[u32,1] = NP.uint32(matrix.indices)\n    vals: np.ndarray[float,1] = matrix.data\n    return rows, cols, vals, x\nrun(bench.spmv, spmv, M=131072, N=131072, nnz=262144)\n\n\ndef stockham_fft(R, K):\n    rng = default_rng(42)\n\n    N = R**K\n    X = rng_complex((N, ), rng)\n    Y = np.zeros_like(X, dtype=np.complex128)\n    return N, R, K, X, Y\nrun(bench.stockham_fft, stockham_fft, R=2, K=21)\n\n\ndef symm(M, N, datatype=np.float64):\n    alpha = datatype(1.5)\n    beta = datatype(1.2)\n    C = np.fromfunction(lambda i, j: ((i + j) % 100) / M, (M, N),\n                        dtype=datatype)\n    B = np.fromfunction(lambda i, j: ((N + i - j) % 100) / M, (M, N),\n                        dtype=datatype)\n    A = np.empty((M, M), dtype=datatype)\n    for i in range(M):\n        A[i, :i + 1] = np.fromfunction(lambda j: ((i + j) % 100) / M,\n                                       (i + 1, ),\n                                       dtype=datatype)\n        A[i, i + 1:] = -999\n    return alpha, beta, C, A, B\nrun(bench.symm, symm, M=1000, N=1200)\n\n\ndef syr2k(M, N, datatype=np.float64):\n    alpha = datatype(1.5)\n    beta = datatype(1.2)\n    C = np.fromfunction(lambda i, j: ((i * j + 3) % N) / M, (N, N),\n                        dtype=datatype)\n    A = np.fromfunction(lambda i, j: ((i * j + 1) % N) / N, (N, M),\n                        dtype=datatype)\n    B = np.fromfunction(lambda i, j: ((i * j + 2) % M) / M, (N, M),\n                        dtype=datatype)\n    return alpha, beta, C, A, B\nrun(bench.syr2k, syr2k, M=350, N=400)\n\n\ndef syrk(M, N, datatype=np.float64):\n    alpha = datatype(1.5)\n    beta = datatype(1.2)\n    C = np.fromfunction(lambda i, j: ((i * j + 2) % N) / M, (N, N),\n                        dtype=datatype)\n    A = np.fromfunction(lambda i, j: ((i * j + 1) % N) / N, (N, M),\n                        dtype=datatype)\n    return alpha, beta, C, A\nrun(bench.syr2k, syr2k, M=1000, N=1200)\n\n\ndef trisolv(N, datatype=np.float64):\n    L = np.fromfunction(lambda i, j: (i + N - j + 1) * 2 / N, (N, N),\n                        dtype=datatype)\n    x = np.full((N, ), -999, dtype=datatype)\n    b = np.fromfunction(lambda i: i, (N, ), dtype=datatype)\n    return L, x, b\nrun(bench.trisolv, trisolv, N=16000)\n\n\ndef trmm(M, N, datatype=np.float64):\n    alpha = datatype(1.5)\n    A = np.fromfunction(lambda i, j: ((i * j) % M) / M, (M, M), dtype=datatype)\n    for i in range(M):\n        A[i, i] = 1.0\n    B = np.fromfunction(lambda i, j: ((N + i - j) % N) / N, (M, N),\n                        dtype=datatype)\n    return alpha, A, B\nrun(bench.trmm, trmm, M=1000, N=1200)\n\n\ndef vadv(I, J, K):\n    rng = default_rng(42)\n    dtr_stage = 3. / 20.\n\n    # Define arrays\n    utens_stage = rng.random((I, J, K))\n    u_stage = rng.random((I, J, K))\n    wcon = rng.random((I + 1, J, K))\n    u_pos = rng.random((I, J, K))\n    utens = rng.random((I, J, K))\n    return utens_stage, u_stage, wcon, u_pos, utens, dtr_stage\nrun(bench.vadv, vadv, I=256, J=256, K=160)\n"
  },
  {
    "path": "bench/codon/npbench_lib.codon",
    "content": "# Copyright 2021 ETH Zurich and the NPBench authors. All rights reserved.\n\n__CODON_RET__: Literal[bool] = True\n\nimport numpy as np\nimport numpy.pybridge\n\ndef adi(TSTEPS: int, N: int, u: np.ndarray[float, 2]):\n    v = np.empty(u.shape, dtype=u.dtype)\n    p = np.empty(u.shape, dtype=u.dtype)\n    q = np.empty(u.shape, dtype=u.dtype)\n\n    DX = 1.0 / N\n    DY = 1.0 / N\n    DT = 1.0 / TSTEPS\n    B1 = 2.0\n    B2 = 1.0\n    mul1 = B1 * DT / (DX * DX)\n    mul2 = B2 * DT / (DY * DY)\n\n    a = -mul1 / 2.0\n    b = 1.0 + mul2\n    c = a\n    d = -mul2 / 2.0\n    e = 1.0 + mul2\n    f = d\n\n    for t in range(1, TSTEPS + 1):\n        v[0, 1:N - 1] = 1.0\n        p[1:N - 1, 0] = 0.0\n        q[1:N - 1, 0] = v[0, 1:N - 1]\n        for j in range(1, N - 1):\n            p[1:N - 1, j] = -c / (a * p[1:N - 1, j - 1] + b)\n            q[1:N - 1,\n              j] = (-d *u[j, 0:N - 2] +\n                    (1.0 + 2.0 * d) *u[j, 1:N - 1] - f *u[j, 2:N] -\n                    a * q[1:N - 1, j - 1]) / (a * p[1:N - 1, j - 1] + b)\n        v[N - 1, 1:N - 1] = 1.0\n        for j in range(N - 2, 0, -1):\n            v[j, 1:N - 1] = p[1:N - 1, j] * v[j + 1, 1:N - 1] + q[1:N - 1, j]\n\n        u[1:N - 1, 0] = 1.0\n        p[1:N - 1, 0] = 0.0\n        q[1:N - 1, 0] = u[1:N - 1, 0]\n        for j in range(1, N - 1):\n            p[1:N - 1, j] = -f / (d * p[1:N - 1, j - 1] + e)\n            q[1:N - 1,\n              j] = (-a * v[0:N - 2, j] +\n                    (1.0 + 2.0 * a) * v[1:N - 1, j] - c * v[2:N, j] -\n                    d * q[1:N - 1, j - 1]) / (d * p[1:N - 1, j - 1] + e)\n        u[1:N - 1, N - 1] = 1.0\n        for j in range(N - 2, 0, -1):\n            u[1:N - 1, j] = p[1:N - 1, j] *u[1:N - 1, j + 1] + q[1:N - 1, j]\n\ndef arc_distance(theta_1: np.ndarray[float, 1], phi_1: np.ndarray[float, 1], theta_2: np.ndarray[float, 1], phi_2: np.ndarray[float, 1]):\n    \"\"\"\n    Calculates the pairwise arc distance between all points in vector a and b.\n    \"\"\"\n    temp = np.sin((theta_2 - theta_1) /\n                  2)**2 + np.cos(theta_1) * np.cos(theta_2) * np.sin(\n                      (phi_2 - phi_1) / 2)**2\n    distance_matrix = 2 * (np.arctan2(np.sqrt(temp), np.sqrt(1 - temp)))\n    if __CODON_RET__: return distance_matrix\n\ndef azimint_naive(data: np.ndarray[float, 1], radius: np.ndarray[float, 1], npt: int):\n    rmax = radius.max()\n    res = np.zeros(npt, dtype=np.float64)\n    for i in range(npt):\n        r1 = rmax * i / npt\n        r2 = rmax * (i + 1) / npt\n        mask_r12 = np.logical_and((r1 <= radius), (radius < r2))\n        values_r12 = data[mask_r12]\n        res[i] = values_r12.mean()\n    if __CODON_RET__: return res\n\ndef azimint_hist(data: np.ndarray[float,1], radius: np.ndarray[float,1], npt: int):\n    histu = np.histogram(radius, npt)[0]\n    histw = np.histogram(radius, npt, weights=data)[0]\n    res = histw / histu\n    if __CODON_RET__: return res\n\ndef atax(A: np.ndarray[float,2], x: np.ndarray[float,1]):\n    res = (A @ x) @ A\n    if __CODON_RET__: return res\n\ndef bicg(A: np.ndarray[float,2], p: np.ndarray[float,1], r: np.ndarray[float,1]):\n    res = r @ A, A @ p\n    if __CODON_RET__: return res\n\ndef cavity_flow(nx: int, ny: int, nt: int, nit: int, u: np.ndarray[float,2], v: np.ndarray[float,2], dt: float, dx: float, dy: float, p: np.ndarray[float,2], rho: float, nu: float):\n    def build_up_b(b, rho, dt, u, v, dx, dy):\n        b[1:-1,\n        1:-1] = (rho * (1 / dt * ((u[1:-1, 2:] - u[1:-1, 0:-2]) / (2 * dx) +\n                                    (v[2:, 1:-1] - v[0:-2, 1:-1]) / (2 * dy)) -\n                        ((u[1:-1, 2:] - u[1:-1, 0:-2]) / (2 * dx))**2 - 2 *\n                        ((u[2:, 1:-1] - u[0:-2, 1:-1]) / (2 * dy) *\n                        (v[1:-1, 2:] - v[1:-1, 0:-2]) / (2 * dx)) -\n                        ((v[2:, 1:-1] - v[0:-2, 1:-1]) / (2 * dy))**2))\n\n    def pressure_poisson(nit, p, dx, dy, b):\n        pn = np.empty_like(p)\n        pn = p.copy()\n\n        for q in range(nit):\n            pn = p.copy()\n            p[1:-1, 1:-1] = (((pn[1:-1, 2:] + pn[1:-1, 0:-2]) * dy**2 +\n                            (pn[2:, 1:-1] + pn[0:-2, 1:-1]) * dx**2) /\n                            (2 * (dx**2 + dy**2)) - dx**2 * dy**2 /\n                            (2 * (dx**2 + dy**2)) * b[1:-1, 1:-1])\n\n            p[:, -1] = p[:, -2]  # dp/dx = 0 at x = 2\n            p[0, :] = p[1, :]  # dp/dy = 0 at y = 0\n            p[:, 0] = p[:, 1]  # dp/dx = 0 at x = 0\n            p[-1, :] = 0  # p = 0 at y = 2\n\n    un = np.empty_like(u)\n    vn = np.empty_like(v)\n    b = np.zeros((ny, nx))\n\n    for n in range(nt):\n        un = u.copy()\n        vn = v.copy()\n\n        build_up_b(b, rho, dt, u, v, dx, dy)\n        pressure_poisson(nit, p, dx, dy, b)\n\n        u[1:-1,\n          1:-1] = (un[1:-1, 1:-1] - un[1:-1, 1:-1] * dt / dx *\n                   (un[1:-1, 1:-1] - un[1:-1, 0:-2]) -\n                   vn[1:-1, 1:-1] * dt / dy *\n                   (un[1:-1, 1:-1] - un[0:-2, 1:-1]) - dt / (2 * rho * dx) *\n                   (p[1:-1, 2:] - p[1:-1, 0:-2]) + nu *\n                   (dt / dx**2 *\n                    (un[1:-1, 2:] - 2 * un[1:-1, 1:-1] + un[1:-1, 0:-2]) +\n                    dt / dy**2 *\n                    (un[2:, 1:-1] - 2 * un[1:-1, 1:-1] + un[0:-2, 1:-1])))\n\n        v[1:-1,\n          1:-1] = (vn[1:-1, 1:-1] - un[1:-1, 1:-1] * dt / dx *\n                   (vn[1:-1, 1:-1] - vn[1:-1, 0:-2]) -\n                   vn[1:-1, 1:-1] * dt / dy *\n                   (vn[1:-1, 1:-1] - vn[0:-2, 1:-1]) - dt / (2 * rho * dy) *\n                   (p[2:, 1:-1] - p[0:-2, 1:-1]) + nu *\n                   (dt / dx**2 *\n                    (vn[1:-1, 2:] - 2 * vn[1:-1, 1:-1] + vn[1:-1, 0:-2]) +\n                    dt / dy**2 *\n                    (vn[2:, 1:-1] - 2 * vn[1:-1, 1:-1] + vn[0:-2, 1:-1])))\n\n        u[0, :] = 0\n        u[:, 0] = 0\n        u[:, -1] = 0\n        u[-1, :] = 1  # set velocity on cavity lid equal to 1\n        v[0, :] = 0\n        v[-1, :] = 0\n        v[:, 0] = 0\n        v[:, -1] = 0\n\ndef channel_flow(nit: int, u: np.ndarray[float, 2], v: np.ndarray[float, 2],\n                 dt: float, dx:float, dy: float, p: np.ndarray[float, 2],\n                 rho: float, nu: float, F: float):\n    def build_up_b(rho, dt, dx, dy, u, v):\n        b = np.zeros_like(u)\n        b[1:-1,\n        1:-1] = (rho * (1 / dt * ((u[1:-1, 2:] - u[1:-1, 0:-2]) / (2 * dx) +\n                                    (v[2:, 1:-1] - v[0:-2, 1:-1]) / (2 * dy)) -\n                        ((u[1:-1, 2:] - u[1:-1, 0:-2]) / (2 * dx))**2 - 2 *\n                        ((u[2:, 1:-1] - u[0:-2, 1:-1]) / (2 * dy) *\n                        (v[1:-1, 2:] - v[1:-1, 0:-2]) / (2 * dx)) -\n                        ((v[2:, 1:-1] - v[0:-2, 1:-1]) / (2 * dy))**2))\n\n        # Periodic BC Pressure @ x = 2\n        b[1:-1, -1] = (rho * (1 / dt * ((u[1:-1, 0] - u[1:-1, -2]) / (2 * dx) +\n                                        (v[2:, -1] - v[0:-2, -1]) / (2 * dy)) -\n                            ((u[1:-1, 0] - u[1:-1, -2]) / (2 * dx))**2 - 2 *\n                            ((u[2:, -1] - u[0:-2, -1]) / (2 * dy) *\n                            (v[1:-1, 0] - v[1:-1, -2]) / (2 * dx)) -\n                            ((v[2:, -1] - v[0:-2, -1]) / (2 * dy))**2))\n\n        # Periodic BC Pressure @ x = 0\n        b[1:-1, 0] = (rho * (1 / dt * ((u[1:-1, 1] - u[1:-1, -1]) / (2 * dx) +\n                                    (v[2:, 0] - v[0:-2, 0]) / (2 * dy)) -\n                            ((u[1:-1, 1] - u[1:-1, -1]) / (2 * dx))**2 - 2 *\n                            ((u[2:, 0] - u[0:-2, 0]) / (2 * dy) *\n                            (v[1:-1, 1] - v[1:-1, -1]) /\n                            (2 * dx)) - ((v[2:, 0] - v[0:-2, 0]) / (2 * dy))**2))\n\n        return b\n\n    def pressure_poisson_periodic(nit, p, dx, dy, b):\n        pn = np.empty_like(p)\n\n        for q in range(nit):\n            pn = p.copy()\n            p[1:-1, 1:-1] = (((pn[1:-1, 2:] + pn[1:-1, 0:-2]) * dy**2 +\n                            (pn[2:, 1:-1] + pn[0:-2, 1:-1]) * dx**2) /\n                            (2 * (dx**2 + dy**2)) - dx**2 * dy**2 /\n                            (2 * (dx**2 + dy**2)) * b[1:-1, 1:-1])\n\n            # Periodic BC Pressure @ x = 2\n            p[1:-1, -1] = (((pn[1:-1, 0] + pn[1:-1, -2]) * dy**2 +\n                            (pn[2:, -1] + pn[0:-2, -1]) * dx**2) /\n                        (2 * (dx**2 + dy**2)) - dx**2 * dy**2 /\n                        (2 * (dx**2 + dy**2)) * b[1:-1, -1])\n\n            # Periodic BC Pressure @ x = 0\n            p[1:-1,\n            0] = (((pn[1:-1, 1] + pn[1:-1, -1]) * dy**2 +\n                    (pn[2:, 0] + pn[0:-2, 0]) * dx**2) / (2 * (dx**2 + dy**2)) -\n                    dx**2 * dy**2 / (2 * (dx**2 + dy**2)) * b[1:-1, 0])\n\n            # Wall boundary conditions, pressure\n            p[-1, :] = p[-2, :]  # dp/dy = 0 at y = 2\n            p[0, :] = p[1, :]  # dp/dy = 0 at y = 0\n\n\n    udiff = 1.0\n    stepcount = 0\n\n    while udiff > .001:\n        un = u.copy()\n        vn = v.copy()\n\n        b = build_up_b(rho, dt, dx, dy, u, v)\n        pressure_poisson_periodic(nit, p, dx, dy, b)\n\n        u[1:-1,\n          1:-1] = (un[1:-1, 1:-1] - un[1:-1, 1:-1] * dt / dx *\n                   (un[1:-1, 1:-1] - un[1:-1, 0:-2]) -\n                   vn[1:-1, 1:-1] * dt / dy *\n                   (un[1:-1, 1:-1] - un[0:-2, 1:-1]) - dt / (2 * rho * dx) *\n                   (p[1:-1, 2:] - p[1:-1, 0:-2]) + nu *\n                   (dt / dx**2 *\n                    (un[1:-1, 2:] - 2 * un[1:-1, 1:-1] + un[1:-1, 0:-2]) +\n                    dt / dy**2 *\n                    (un[2:, 1:-1] - 2 * un[1:-1, 1:-1] + un[0:-2, 1:-1])) +\n                   F * dt)\n\n        v[1:-1,\n          1:-1] = (vn[1:-1, 1:-1] - un[1:-1, 1:-1] * dt / dx *\n                   (vn[1:-1, 1:-1] - vn[1:-1, 0:-2]) -\n                   vn[1:-1, 1:-1] * dt / dy *\n                   (vn[1:-1, 1:-1] - vn[0:-2, 1:-1]) - dt / (2 * rho * dy) *\n                   (p[2:, 1:-1] - p[0:-2, 1:-1]) + nu *\n                   (dt / dx**2 *\n                    (vn[1:-1, 2:] - 2 * vn[1:-1, 1:-1] + vn[1:-1, 0:-2]) +\n                    dt / dy**2 *\n                    (vn[2:, 1:-1] - 2 * vn[1:-1, 1:-1] + vn[0:-2, 1:-1])))\n\n        # Periodic BC u @ x = 2\n        u[1:-1, -1] = (\n            un[1:-1, -1] - un[1:-1, -1] * dt / dx *\n            (un[1:-1, -1] - un[1:-1, -2]) - vn[1:-1, -1] * dt / dy *\n            (un[1:-1, -1] - un[0:-2, -1]) - dt / (2 * rho * dx) *\n            (p[1:-1, 0] - p[1:-1, -2]) + nu *\n            (dt / dx**2 *\n             (un[1:-1, 0] - 2 * un[1:-1, -1] + un[1:-1, -2]) + dt / dy**2 *\n             (un[2:, -1] - 2 * un[1:-1, -1] + un[0:-2, -1])) + F * dt)\n\n        # Periodic BC u @ x = 0\n        u[1:-1,\n          0] = (un[1:-1, 0] - un[1:-1, 0] * dt / dx *\n                (un[1:-1, 0] - un[1:-1, -1]) - vn[1:-1, 0] * dt / dy *\n                (un[1:-1, 0] - un[0:-2, 0]) - dt / (2 * rho * dx) *\n                (p[1:-1, 1] - p[1:-1, -1]) + nu *\n                (dt / dx**2 *\n                 (un[1:-1, 1] - 2 * un[1:-1, 0] + un[1:-1, -1]) + dt / dy**2 *\n                 (un[2:, 0] - 2 * un[1:-1, 0] + un[0:-2, 0])) + F * dt)\n\n        # Periodic BC v @ x = 2\n        v[1:-1, -1] = (\n            vn[1:-1, -1] - un[1:-1, -1] * dt / dx *\n            (vn[1:-1, -1] - vn[1:-1, -2]) - vn[1:-1, -1] * dt / dy *\n            (vn[1:-1, -1] - vn[0:-2, -1]) - dt / (2 * rho * dy) *\n            (p[2:, -1] - p[0:-2, -1]) + nu *\n            (dt / dx**2 *\n             (vn[1:-1, 0] - 2 * vn[1:-1, -1] + vn[1:-1, -2]) + dt / dy**2 *\n             (vn[2:, -1] - 2 * vn[1:-1, -1] + vn[0:-2, -1])))\n\n        # Periodic BC v @ x = 0\n        v[1:-1,\n          0] = (vn[1:-1, 0] - un[1:-1, 0] * dt / dx *\n                (vn[1:-1, 0] - vn[1:-1, -1]) - vn[1:-1, 0] * dt / dy *\n                (vn[1:-1, 0] - vn[0:-2, 0]) - dt / (2 * rho * dy) *\n                (p[2:, 0] - p[0:-2, 0]) + nu *\n                (dt / dx**2 *\n                 (vn[1:-1, 1] - 2 * vn[1:-1, 0] + vn[1:-1, -1]) + dt / dy**2 *\n                 (vn[2:, 0] - 2 * vn[1:-1, 0] + vn[0:-2, 0])))\n\n        # Wall BC: u,v = 0 @ y = 0,2\n        u[0, :] = 0\n        u[-1, :] = 0\n        v[0, :] = 0\n        v[-1, :] = 0\n\n        udiff = (np.sum(u) - np.sum(un)) / np.sum(u)\n        stepcount += 1\n\n    if __CODON_RET__: return stepcount\n\ndef cholesky2(A: np.ndarray[float,2]):\n    A[:] = np.linalg.cholesky(A) + np.triu(A, k=1)\n\ndef cholesky(A: np.ndarray[float,2]):\n    A[0, 0] = np.sqrt(A[0, 0])\n    for i in range(1, A.shape[0]):\n        for j in range(i):\n            A[i, j] -= np.dot(A[i, :j], A[j, :j])\n            A[i, j] /= A[j, j]\n        A[i, i] -= np.dot(A[i, :i], A[i, :i])\n        A[i, i] = np.sqrt(A[i, i])\n\ndef compute(array_1: np.ndarray[int,2], array_2: np.ndarray[int,2], a: int, b: int, c: int):\n    res = np.clip(array_1, 2, 10) * a + array_2 * b + c\n    if __CODON_RET__: return res\n\ndef contour_integral(NR: int, NM: int, slab_per_bc: int, Ham: np.ndarray[complex, 3], int_pts: np.ndarray[complex, 1], Y: np.ndarray[complex, 2]):\n    P0 = np.zeros((NR, NM), dtype=np.complex128)\n    P1 = np.zeros((NR, NM), dtype=np.complex128)\n    for z in int_pts:\n        Tz = np.zeros((NR, NR), dtype=np.complex128)\n        for n in range(slab_per_bc + 1):\n            zz = np.power(z, slab_per_bc / 2 - n)\n            Tz += zz * Ham[n]\n        if NR == NM:\n            X = np.linalg.inv(Tz)\n        else:\n            X = np.linalg.solve(Tz, Y)\n        if abs(z) < 1.0:\n            X = -X\n        P0 += X\n        P1 += z * X\n\n    res = P0, P1\n    if __CODON_RET__: return res\n\ndef conv2d_bias(input: np.ndarray[float32,4], weights: np.ndarray[float32,4], bias: np.ndarray[float32,1]):\n    def conv2d(input, weights):\n        K = weights.shape[0]  # Assuming square kernel\n        N = input.shape[0]\n        H_out = input.shape[1] - K + 1\n        W_out = input.shape[2] - K + 1\n        C_out = weights.shape[3]\n        output = np.empty((N, H_out, W_out, C_out), dtype=np.float32)\n\n        # Loop structure adapted from https://github.com/SkalskiP/ILearnDeepLearning.py/blob/ba0b5ba589d4e656141995e8d1a06d44db6ce58d/01_mysteries_of_neural_networks/06_numpy_convolutional_neural_net/src/layers/convolutional.py#L88\n        for i in range(H_out):\n            for j in range(W_out):\n                output[:, i, j, :] = np.sum(\n                    input[:, i:i + K, j:j + K, :, np.newaxis] *\n                    weights[np.newaxis, :, :, :],\n                    axis=(1, 2, 3),\n                )\n\n        return output\n    res = conv2d(input, weights) + bias\n    if __CODON_RET__: return res\n\ndef correlation(M: int, float_n: float, data: np.ndarray[float,2]):\n\n    mean = np.mean(data, axis=0)\n    stddev = np.std(data, axis=0)\n    stddev[stddev <= 0.1] = 1.0\n    data -= mean\n    data /= np.sqrt(float_n) * stddev\n    corr = np.eye(M, dtype=data.dtype)\n    for i in range(M - 1):\n        corr[i + 1:M, i] = corr[i, i + 1:M] = data[:, i] @ data[:, i + 1:M]\n\n    if __CODON_RET__: return corr\n\ndef covariance(M: int, float_n: float, data: np.ndarray[float, 2]):\n\n    mean = np.mean(data, axis=0)\n    data -= mean\n    cov = np.zeros((M, M), dtype=data.dtype)\n    for i in range(M):\n        cov[i:M, i] = cov[i, i:M] = data[:, i] @ data[:, i:M] / (float_n - 1.0)\n\n    if __CODON_RET__: return cov\n\ndef crc16(data: np.ndarray[np.uint8, 1]):\n    '''\n    CRC-16-CCITT Algorithm\n    '''\n    poly=0x8408\n    crc = 0xFFFF\n    for b in data:\n        cur_byte = 0xFF & int(b)\n        for _ in range(0, 8):\n            if (crc & 0x0001) ^ (cur_byte & 0x0001):\n                crc = (crc >> 1) ^ poly\n            else:\n                crc >>= 1\n            cur_byte >>= 1\n    crc = (~crc & 0xFFFF)\n    crc = (crc << 8) | ((crc >> 8) & 0xFF)\n\n    res = crc & 0xFFFF\n    if __CODON_RET__: return res\n\ndef deriche(alpha: float, imgIn: np.ndarray[float,2]):\n\n    k = (1.0 - np.exp(-alpha)) * (1.0 - np.exp(-alpha)) / (\n        1.0 + alpha * np.exp(-alpha) - np.exp(2.0 * alpha))\n    a1 = a5 = k\n    a2 = a6 = k * np.exp(-alpha) * (alpha - 1.0)\n    a3 = a7 = k * np.exp(-alpha) * (alpha + 1.0)\n    a4 = a8 = -k * np.exp(-2.0 * alpha)\n    b1 = 2.0**(-alpha)\n    b2 = -np.exp(-2.0 * alpha)\n    c1 = c2 = 1\n\n    y1 = np.empty_like(imgIn)\n    y1[:, 0] = a1 * imgIn[:, 0]\n    y1[:, 1] = a1 * imgIn[:, 1] + a2 * imgIn[:, 0] + b1 * y1[:, 0]\n    for j in range(2, imgIn.shape[1]):\n        y1[:, j] = (a1 * imgIn[:, j] + a2 * imgIn[:, j - 1] +\n                    b1 * y1[:, j - 1] + b2 * y1[:, j - 2])\n\n    y2 = np.empty_like(imgIn)\n    y2[:, -1] = 0.0\n    y2[:, -2] = a3 * imgIn[:, -1]\n    for j in range(imgIn.shape[1] - 3, -1, -1):\n        y2[:, j] = (a3 * imgIn[:, j + 1] + a4 * imgIn[:, j + 2] +\n                    b1 * y2[:, j + 1] + b2 * y2[:, j + 2])\n\n    imgOut = c1 * (y1 + y2)\n\n    y1[0, :] = a5 * imgOut[0, :]\n    y1[1, :] = a5 * imgOut[1, :] + a6 * imgOut[0, :] + b1 * y1[0, :]\n    for i in range(2, imgIn.shape[0]):\n        y1[i, :] = (a5 * imgOut[i, :] + a6 * imgOut[i - 1, :] +\n                    b1 * y1[i - 1, :] + b2 * y1[i - 2, :])\n\n    y2[-1, :] = 0.0\n    y2[-2, :] = a7 * imgOut[-1, :]\n    for i in range(imgIn.shape[0] - 3, -1, -1):\n        y2[i, :] = (a7 * imgOut[i + 1, :] + a8 * imgOut[i + 2, :] +\n                    b1 * y2[i + 1, :] + b2 * y2[i + 2, :])\n\n    imgOut[:] = c2 * (y1 + y2)\n\n    if __CODON_RET__: return imgOut\n\ndef doitgen(NR: int, NQ: int, NP: int, A: np.ndarray[float,3], C4: np.ndarray[float,2]):\n\n    A[:] = np.reshape(np.reshape(A, (NR, NQ, 1, NP)) @ C4, (NR, NQ, NP))\n\ndef durbin(r: np.ndarray[float,1]):\n\n    y = np.empty_like(r)\n    alpha = -r[0]\n    beta = 1.0\n    y[0] = -r[0]\n\n    for k in range(1, r.shape[0]):\n        beta *= 1.0 - alpha * alpha\n        alpha = -(r[k] + np.dot(np.flip(r[:k]), y[:k])) / beta\n        y[:k] += alpha * np.flip(y[:k])\n        y[k] = alpha\n\n    if __CODON_RET__: return y\n\ndef fdtd_2d(TMAX: int, ex: np.ndarray[float,2], ey: np.ndarray[float,2], hz: np.ndarray[float,2], _fict_: np.ndarray[float,1]):\n\n    for t in range(TMAX):\n        ey[0, :] = _fict_[t]\n        ey[1:, :] -= 0.5 * (hz[1:, :] - hz[:-1, :])\n        ex[:, 1:] -= 0.5 * (hz[:, 1:] - hz[:, :-1])\n        hz[:-1, :-1] -= 0.7 * (ex[:-1, 1:] - ex[:-1, :-1] + ey[1:, :-1] - ey[:-1, :-1])\n\ndef floyd_warshall(path: np.ndarray[Int[32],2]):\n\n    for k in range(path.shape[0]):\n        path[:] = np.minimum(path[:], np.add.outer(path[:, k], path[k, :]))\n\ndef gemm(alpha: float, beta: float, C: np.ndarray[float,2], A: np.ndarray[float,2], B: np.ndarray[float,2]):\n\n    C[:] = alpha * A @ B + beta * C\n\ndef gemver(alpha: float, beta: float, A: np.ndarray[float,2], u1: np.ndarray[float,1], v1: np.ndarray[float,1], u2: np.ndarray[float,1], v2: np.ndarray[float,1], w: np.ndarray[float,1], x: np.ndarray[float,1], y: np.ndarray[float,1], z: np.ndarray[float,1]):\n\n    A += np.outer(u1, v1) + np.outer(u2, v2)\n    x += beta * y @ A + z\n    w += alpha * A @ x\n\ndef gesummv(alpha: float, beta: float, A: np.ndarray[float,2], B: np.ndarray[float,2], x: np.ndarray[float,1]):\n\n    res = alpha * A @ x + beta * B @ x\n    if __CODON_RET__: return res\n\ndef go_fast(a: np.ndarray[float,2]):\n    trace = 0.0\n    for i in range(a.shape[0]):\n        trace += np.tanh(a[i, i])\n    res = a + trace\n    if __CODON_RET__: return res\n\ndef gramschmidt(A: np.ndarray[float,2]):\n\n    Q = np.zeros_like(A)\n    R = np.zeros((A.shape[1], A.shape[1]), dtype=A.dtype)\n\n    for k in range(A.shape[1]):\n        nrm = np.dot(A[:, k], A[:, k])\n        R[k, k] = np.sqrt(nrm)\n        Q[:, k] = A[:, k] / R[k, k]\n        for j in range(k + 1, A.shape[1]):\n            R[k, j] = np.dot(Q[:, k], A[:, j])\n            A[:, j] -= Q[:, k] * R[k, j]\n\n    res = Q, R\n    if __CODON_RET__: return res\n\ndef hdiff(in_field: np.ndarray[float,3], out_field: np.ndarray[float,3], coeff: np.ndarray[float,3]):\n    I, J, K = out_field.shape[0], out_field.shape[1], out_field.shape[2]\n    lap_field = 4.0 * in_field[1:I + 3, 1:J + 3, :] - (\n        in_field[2:I + 4, 1:J + 3, :] + in_field[0:I + 2, 1:J + 3, :] +\n        in_field[1:I + 3, 2:J + 4, :] + in_field[1:I + 3, 0:J + 2, :])\n\n    res = lap_field[1:, 1:J + 1, :] - lap_field[:-1, 1:J + 1, :]\n    flx_field = np.where(\n        (res *\n         (in_field[2:I + 3, 2:J + 2, :] - in_field[1:I + 2, 2:J + 2, :])) > 0,\n        0,\n        res,\n    )\n\n    res = lap_field[1:I + 1, 1:, :] - lap_field[1:I + 1, :-1, :]\n    fly_field = np.where(\n        (res *\n         (in_field[2:I + 2, 2:J + 3, :] - in_field[2:I + 2, 1:J + 2, :])) > 0,\n        0,\n        res,\n    )\n\n    out_field[:, :, :] = in_field[2:I + 2, 2:J + 2, :] - coeff[:, :, :] * (\n        flx_field[1:, :, :] - flx_field[:-1, :, :] + fly_field[:, 1:, :] -\n        fly_field[:, :-1, :])\n\ndef heat_3d(TSTEPS: int, A: np.ndarray[float, 3], B: np.ndarray[float, 3]):\n    for t in range(1, TSTEPS):\n        B[1:-1, 1:-1,\n          1:-1] = (0.125 * (A[2:, 1:-1, 1:-1] - 2.0 * A[1:-1, 1:-1, 1:-1] +\n                            A[:-2, 1:-1, 1:-1]) + 0.125 *\n                   (A[1:-1, 2:, 1:-1] - 2.0 * A[1:-1, 1:-1, 1:-1] +\n                    A[1:-1, :-2, 1:-1]) + 0.125 *\n                   (A[1:-1, 1:-1, 2:] - 2.0 * A[1:-1, 1:-1, 1:-1] +\n                    A[1:-1, 1:-1, 0:-2]) + A[1:-1, 1:-1, 1:-1])\n        A[1:-1, 1:-1,\n          1:-1] = (0.125 * (B[2:, 1:-1, 1:-1] - 2.0 * B[1:-1, 1:-1, 1:-1] +\n                            B[:-2, 1:-1, 1:-1]) + 0.125 *\n                   (B[1:-1, 2:, 1:-1] - 2.0 * B[1:-1, 1:-1, 1:-1] +\n                    B[1:-1, :-2, 1:-1]) + 0.125 *\n                   (B[1:-1, 1:-1, 2:] - 2.0 * B[1:-1, 1:-1, 1:-1] +\n                    B[1:-1, 1:-1, 0:-2]) + B[1:-1, 1:-1, 1:-1])\n\ndef jacobi_1d(TSTEPS: int, A: np.ndarray[float, 1], B: np.ndarray[float, 1]):\n\n    for t in range(1, TSTEPS):\n        B[1:-1] = 0.33333 * (A[:-2] + A[1:-1] + A[2:])\n        A[1:-1] = 0.33333 * (B[:-2] + B[1:-1] + B[2:])\n\ndef jacobi_2d(TSTEPS: int, A: np.ndarray[float, 2], B: np.ndarray[float, 2]):\n\n    for t in range(1, TSTEPS):\n        B[1:-1, 1:-1] = 0.2 * (A[1:-1, 1:-1] + A[1:-1, :-2] + A[1:-1, 2:] +\n                               A[2:, 1:-1] + A[:-2, 1:-1])\n        A[1:-1, 1:-1] = 0.2 * (B[1:-1, 1:-1] + B[1:-1, :-2] + B[1:-1, 2:] +\n                               B[2:, 1:-1] + B[:-2, 1:-1])\n\ndef k2mm(alpha: float, beta: float, A: np.ndarray[float,2], B: np.ndarray[float,2], C: np.ndarray[float,2], D: np.ndarray[float,2]):\n\n    D[:] = alpha * A @ B @ C + beta * D\n\ndef k3mm(A: np.ndarray[float,2], B: np.ndarray[float,2], C: np.ndarray[float,2], D: np.ndarray[float,2]):\n\n    res = A @ B @ C @ D\n    if __CODON_RET__: return res\n\ndef lenet(input: np.ndarray[float32,4], conv1: np.ndarray[float32,4], conv1bias: np.ndarray[float32,1],\n          conv2: np.ndarray[float32,4], conv2bias: np.ndarray[float32,1], fc1w: np.ndarray[float32,2], fc1b: np.ndarray[float32,1], fc2w: np.ndarray[float32,2], fc2b: np.ndarray[float32,1],\n          fc3w: np.ndarray[float32,2], fc3b: np.ndarray[float32,1], N:int, C_before_fc1:int):\n    def relu(x):\n        return np.maximum(x, 0)\n\n    def conv2d(input, weights):\n        K = weights.shape[0]  # Assuming square kernel\n        N = input.shape[0]\n        H_out = input.shape[1] - K + 1\n        W_out = input.shape[2] - K + 1\n        C_out = weights.shape[3]\n        output = np.empty((N, H_out, W_out, C_out), dtype=np.float32)\n\n        # Loop structure adapted from https://github.com/SkalskiP/ILearnDeepLearning.py/blob/ba0b5ba589d4e656141995e8d1a06d44db6ce58d/01_mysteries_of_neural_networks/06_numpy_convolutional_neural_net/src/layers/convolutional.py#L88\n        for i in range(H_out):\n            for j in range(W_out):\n                output[:, i, j, :] = np.sum(\n                    input[:, i:i + K, j:j + K, :, np.newaxis] *\n                    weights[np.newaxis, :, :, :],\n                    axis=(1, 2, 3),\n                )\n\n        return output\n\n    def maxpool2d(x):\n        output = np.empty(\n            (x.shape[0], x.shape[1] // 2, x.shape[2] // 2, x.shape[3]),\n            dtype=x.dtype)\n        for i in range(x.shape[1] // 2):\n            for j in range(x.shape[2] // 2):\n                output[:, i, j, :] = np.max(x[:, 2 * i:2 * i + 2,\n                                            2 * j:2 * j + 2, :],\n                                            axis=(1, 2))\n        return output\n\n    x = relu(conv2d(input, conv1) + conv1bias)\n    x = maxpool2d(x)\n    x = relu(conv2d(x, conv2) + conv2bias)\n    x = maxpool2d(x)\n    x = np.reshape(x, (N, C_before_fc1))\n    x = relu(x @ fc1w + fc1b)\n    x = relu(x @ fc2w + fc2b)\n    res = x @ fc3w + fc3b\n    if __CODON_RET__: return res\n\ndef lu(A: np.ndarray[float, 2]):\n\n    for i in range(A.shape[0]):\n        for j in range(i):\n            A[i, j] -= A[i, :j] @ A[:j, j]\n            A[i, j] /= A[j, j]\n        for j in range(i, A.shape[0]):\n            A[i, j] -= A[i, :i] @ A[:i, j]\n\ndef ludcmp(A: np.ndarray[float, 2], b: np.ndarray[float,1]):\n\n    x = np.zeros_like(b)\n    y = np.zeros_like(b)\n\n    for i in range(A.shape[0]):\n        for j in range(i):\n            A[i, j] -= A[i, :j] @ A[:j, j]\n            A[i, j] /= A[j, j]\n        for j in range(i, A.shape[0]):\n            A[i, j] -= A[i, :i] @ A[:i, j]\n    for i in range(A.shape[0]):\n        y[i] = b[i] - A[i, :i] @ y[:i]\n    for i in range(A.shape[0] - 1, -1, -1):\n        x[i] = (y[i] - A[i, i + 1:] @ x[i + 1:]) / A[i, i]\n\n    res = x, y\n    if __CODON_RET__: return res\n\ndef mandelbrot1(xmin: float, xmax: float, ymin: float, ymax: float, xn: int, yn: int, maxiter: int, horizon: float):\n    horizon = 2.0\n    X = np.linspace(xmin, xmax, xn, dtype=np.float64)\n    Y = np.linspace(ymin, ymax, yn, dtype=np.float64)\n    C = X + Y[:, None] * 1j\n    N = np.zeros(C.shape, dtype=np.int64)\n    Z = np.zeros(C.shape, dtype=np.complex128)\n    for n in range(maxiter):\n        I = np.less(abs(Z), horizon)\n        N[I] = n\n        Z[I] = Z[I]**2 + C[I]\n    N[N == maxiter - 1] = 0\n    res = Z, N\n    if __CODON_RET__: return res\n\ndef mandelbrot2(xmin: float, xmax: float, ymin: float, ymax: float, xn: int, yn: int, itermax: int, horizon: float):\n    def mgrid(xn, yn):\n        Xi = np.empty((xn, yn), dtype=np.int64)\n        Yi = np.empty((xn, yn), dtype=np.int64)\n        for i in range(xn):\n            Xi[i, :] = i\n        for j in range(yn):\n            Yi[:, j] = j\n        return Xi, Yi\n\n    Xi, Yi = mgrid(xn, yn)\n    X = np.linspace(xmin, xmax, xn, dtype=np.float64)[Xi]\n    Y = np.linspace(ymin, ymax, yn, dtype=np.float64)[Yi]\n    C = X + Y * 1j\n    N_ = np.zeros(C.shape, dtype=np.int64)\n    Z_ = np.zeros(C.shape, dtype=np.complex128)\n    # Xi.shape = Yi.shape = C.shape = xn * yn\n    Xi = Xi.reshape(xn * yn)\n    Yi = Yi.reshape(xn * yn)\n    C = C.reshape(xn * yn)\n\n    Z = np.zeros(C.shape, np.complex128)\n    for i in range(itermax):\n        if not len(Z):\n            break\n\n        # Compute for relevant points only\n        np.multiply(Z, Z, Z)\n        np.add(Z, C, Z)\n\n        # Failed convergence\n        I = abs(Z) > horizon\n        N_[Xi[I], Yi[I]] = i + 1\n        Z_[Xi[I], Yi[I]] = Z[I]\n\n        # Keep going with those who have not diverged yet\n        np.logical_not(I, I)  # np.negative(I, I) not working any longer\n        Z = Z[I]\n        Xi, Yi = Xi[I], Yi[I]\n        C = C[I]\n    res = Z_.T, N_.T\n    if __CODON_RET__: return res\n\ndef mlp(input: np.ndarray[float32, 2], w1: np.ndarray[float32,2], b1: np.ndarray[float32,1], w2: np.ndarray[float32,2], b2: np.ndarray[float32,1], w3: np.ndarray[float32, 2], b3: np.ndarray[float32, 1]):\n    def relu(x):\n        return np.maximum(x, 0)\n\n    def softmax(x):\n        tmp_max = np.max(x, axis=-1, keepdims=True)\n        tmp_out = np.exp(x - tmp_max)\n        tmp_sum = np.sum(tmp_out, axis=-1, keepdims=True)\n        return tmp_out / tmp_sum\n\n    x = relu(input @ w1 + b1)\n    # x = np.array(x, dtype=np.float32)\n    x = relu(x @ w2 + b2)\n    # x = np.array(x, dtype=np.float32)\n    x = softmax(x @ w3 + b3)  # Softmax call can be omitted if necessary\n    if __CODON_RET__: return x\n\ndef mvt(x1: np.ndarray[float, 1], x2: np.ndarray[float, 1], y_1: np.ndarray[float,1], y_2: np.ndarray[float,1], A: np.ndarray[float, 2]):\n    x1 += A @ y_1\n    x2 += y_2 @ A\n\ndef nbody(mass: np.ndarray[float, 2], pos: np.ndarray[float, 2], vel: np.ndarray[float, 2],\n          N: int, Nt: int, dt: float, G: float, softening: float):\n    def getAcc(pos, mass, G, softening):\n        \"\"\"\n        Calculate the acceleration on each particle due to Newton's Law\n        pos  is an N x 3 matrix of positions\n        mass is an N x 1 vector of masses\n        G is Newton's Gravitational constant\n        softening is the softening length\n        a is N x 3 matrix of accelerations\n        \"\"\"\n        # positions r = [x,y,z] for all particles\n        x = pos[:, 0:1]\n        y = pos[:, 1:2]\n        z = pos[:, 2:3]\n\n        # matrix that stores all pairwise particle separations: r_j - r_i\n        dx = x.T - x\n        dy = y.T - y\n        dz = z.T - z\n\n        # matrix that stores 1/r^3 for all particle pairwise particle separations\n        inv_r3 = (dx**2 + dy**2 + dz**2 + softening**2)\n        inv_r3[inv_r3 > 0] = inv_r3[inv_r3 > 0]**(-1.5)\n\n        ax = G * (dx * inv_r3) @ mass\n        ay = G * (dy * inv_r3) @ mass\n        az = G * (dz * inv_r3) @ mass\n\n        # pack together the acceleration components\n        a = np.hstack((ax, ay, az))\n\n        return a\n\n    def getEnergy(pos, vel, mass, G):\n        \"\"\"\n        Get kinetic energy (KE) and potential energy (PE) of simulation\n        pos is N x 3 matrix of positions\n        vel is N x 3 matrix of velocities\n        mass is an N x 1 vector of masses\n        G is Newton's Gravitational constant\n        KE is the kinetic energy of the system\n        PE is the potential energy of the system\n        \"\"\"\n        # Kinetic Energy:\n        # KE = 0.5 * np.sum(np.sum( mass * vel**2 ))\n        KE = 0.5 * np.sum(mass * vel**2)\n\n        # Potential Energy:\n\n        # positions r = [x,y,z] for all particles\n        x = pos[:, 0:1]\n        y = pos[:, 1:2]\n        z = pos[:, 2:3]\n\n        # matrix that stores all pairwise particle separations: r_j - r_i\n        dx = x.T - x\n        dy = y.T - y\n        dz = z.T - z\n\n        # matrix that stores 1/r for all particle pairwise particle separations\n        inv_r = np.sqrt(dx**2 + dy**2 + dz**2)\n        inv_r[inv_r > 0] = 1.0 / inv_r[inv_r > 0]\n\n        # sum over upper triangle, to count each interaction only once\n        # PE = G * np.sum(np.sum(np.triu(-(mass*mass.T)*inv_r,1)))\n        PE = G * np.sum(np.triu(-(mass * mass.T) * inv_r, 1))\n\n        return KE, PE\n\n    # Convert to Center-of-Mass frame\n    vel -= np.mean(mass * vel, axis=0) / np.mean(mass)\n\n    # calculate initial gravitational accelerations\n    acc = getAcc(pos, mass, G, softening)\n\n    # calculate initial energy of system\n    KE = np.empty(Nt + 1, dtype=np.float64)\n    PE = np.empty(Nt + 1, dtype=np.float64)\n    KE[0], PE[0] = getEnergy(pos, vel, mass, G)\n\n    t = 0.0\n\n    # Simulation Main Loop\n    for i in range(Nt):\n        # (1/2) kick\n        vel += acc * dt / 2.0\n\n        # drift\n        pos += vel * dt\n\n        # update accelerations\n        acc = getAcc(pos, mass, G, softening)\n\n        # (1/2) kick\n        vel += acc * dt / 2.0\n\n        # update time\n        t += dt\n\n        # get energy of system\n        KE[i + 1], PE[i + 1] = getEnergy(pos, vel, mass, G)\n\n    res = KE, PE\n    if __CODON_RET__: return res\n\ndef nussinov(N: int, seq: np.ndarray[Int[32], 1]):\n    def match(b1, b2):\n        if b1 + b2 == 3:\n            return 1\n        else:\n            return 0\n\n    table = np.zeros((N, N), np.int32)\n\n    for i in range(N - 1, -1, -1):\n        for j in range(i + 1, N):\n            if j - 1 >= 0:\n                table[i, j] = max(table[i, j], table[i, j - 1])\n            if i + 1 < N:\n                table[i, j] = max(table[i, j], table[i + 1, j])\n            if j - 1 >= 0 and i + 1 < N:\n                if i < j - 1:\n                    table[i, j] = max(table[i, j], table[i + 1, j - 1] + Int[32](match(int(seq[i]), int(seq[j]))))\n                else:\n                    table[i, j] = max(table[i, j], table[i + 1, j - 1])\n            for k in range(i + 1, j):\n                table[i, j] = max(table[i, j], table[i, k] + table[k + 1, j])\n\n    if __CODON_RET__: return table\n\ndef resnet(input: np.ndarray[float32, 4], conv1: np.ndarray[float32, 4], conv2: np.ndarray[float32, 4], conv3: np.ndarray[float32, 4]):\n    def relu(x):\n        return np.maximum(x, 0)\n\n    def conv2d(input, weights):\n        K = weights.shape[0]  # Assuming square kernel\n        N = input.shape[0]\n        H_out = input.shape[1] - K + 1\n        W_out = input.shape[2] - K + 1\n        C_out = weights.shape[3]\n        output = np.empty((N, H_out, W_out, C_out), dtype=np.float32)\n\n        # Loop structure adapted from https://github.com/SkalskiP/ILearnDeepLearning.py/blob/ba0b5ba589d4e656141995e8d1a06d44db6ce58d/01_mysteries_of_neural_networks/06_numpy_convolutional_neural_net/src/layers/convolutional.py#L88\n        for i in range(H_out):\n            for j in range(W_out):\n                output[:, i, j, :] = np.sum(np.sum(\n                    np.sum(input[:, i:i + K, j:j + K, :, np.newaxis] *\n                        weights[np.newaxis, :, :, :],\n                        axis=1),\n                    axis=1),\n                                            axis=1)\n        return output\n\n    def batchnorm2d(x):\n        # mean = np.mean(x, axis=0, keepdims=True)\n        eps=1e-5\n        mean = np.mean(x, axis=0)[np.newaxis, :, :, :]\n        # std = np.std(x, axis=0, keepdims=True)\n        std = np.std(x, axis=0)[np.newaxis, :, :, :]\n        return (x - mean) / np.sqrt(std + eps)\n\n\n    # Pad output of first convolution for second convolution\n    padded = np.zeros((input.shape[0], input.shape[1] + 2, input.shape[2] + 2,\n                       conv1.shape[3]))\n\n    padded[:, 1:-1, 1:-1, :] = conv2d(input, conv1)\n    x = batchnorm2d(padded)\n    x = relu(x)\n    x = conv2d(x, conv2)\n    x = batchnorm2d(x)\n    x = relu(x)\n    x = conv2d(x, conv3)\n    x = batchnorm2d(x)\n    res = relu(x + input)\n    if __CODON_RET__: return res\n\ndef scattering_self_energies(neigh_idx: np.ndarray[Int[32], 2], dH: np.ndarray[complex, 5], G: np.ndarray[complex, 5], D: np.ndarray[complex, 6], Sigma: np.ndarray[complex, 5]):\n\n    for k in range(G.shape[0]):\n        for E in range(G.shape[1]):\n            for q in range(D.shape[0]):\n                for w in range(D.shape[1]):\n                    for i in range(D.shape[-2]):\n                        for j in range(D.shape[-1]):\n                            for a in range(neigh_idx.shape[0]):\n                                for b in range(neigh_idx.shape[1]):\n                                    if E - w >= 0:\n                                        dHG = G[k, E - w, int(neigh_idx[a, b])] @ dH[a, b, i]\n                                        dHD = dH[a, b, j] * D[q, w, a, b, i, j]\n                                        Sigma[k, E, a] += dHG @ dHD\n\ndef seidel_2d(TSTEPS: int, N: int, A: np.ndarray[float,2]):\n\n    for t in range(0, TSTEPS - 1):\n        for i in range(1, N - 1):\n            A[i, 1:-1] += (A[i - 1, :-2] + A[i - 1, 1:-1] + A[i - 1, 2:] +\n                           A[i, 2:] + A[i + 1, :-2] + A[i + 1, 1:-1] +\n                           A[i + 1, 2:])\n            for j in range(1, N - 1):\n                A[i, j] += A[i, j - 1]\n                A[i, j] /= 9.0\n\ndef softmax(x: np.ndarray[float32, 4]):\n    tmp_max = np.max(x, axis=-1, keepdims=True)\n    tmp_out = np.exp(x - tmp_max)\n    tmp_sum = np.sum(tmp_out, axis=-1, keepdims=True)\n    res = tmp_out / tmp_sum\n    if __CODON_RET__: return res\n\ndef spmv(A_row: np.ndarray[u32,1], A_col: np.ndarray[u32,1], A_val: np.ndarray[float,1], x: np.ndarray[float,1]):\n    y = np.empty(A_row.size - 1, A_val.dtype)\n\n    for i in range(A_row.size - 1):\n        cols = A_col[int(A_row[i]):int(A_row[i + 1])].astype(np.int64)\n        vals = A_val[int(A_row[i]):int(A_row[i + 1])]\n        y[i] = vals @ x[cols]\n\n    if __CODON_RET__: return y\n\ndef stockham_fft(N: int, R: int, K: int, x: np.ndarray[complex, 1], y: np.ndarray[complex, 1]):\n    def mgrid_stockham(xn, yn):\n        Xi = np.empty((xn, yn), dtype=np.int32)\n        Yi = np.empty((xn, yn), dtype=np.int32)\n        for i in range(xn):\n            Xi[i, :] = i\n        for j in range(yn):\n            Yi[:, j] = j\n        return Xi, Yi\n\n    # Generate DFT matrix for radix R.\n    # Define transient variable for matrix.\n    # i_coord, j_coord = np.mgrid[0:R, 0:R]\n    i_coord, j_coord = mgrid_stockham(R, R)\n    dft_mat = np.empty((R, R), dtype=np.complex128)\n    dft_mat = np.exp(-2.0j * np.pi * i_coord * j_coord / R)\n    # Move input x to output y\n    # to avoid overwriting the input.\n    y[:] = x[:]\n\n    # ii_coord, jj_coord = np.mgrid[0:R, 0:R**K]\n    ii_coord, jj_coord = mgrid_stockham(R, R**K)\n\n    # Main Stockham loop\n    for i in range(K):\n        # Stride permutation\n        yv = np.reshape(y, (R**i, R, R**(K-i-1)))\n        tmp_perm = np.transpose(yv, axes=(1, 0, 2))\n        # Twiddle Factor multiplication\n        D = np.empty((R, R**i, R**(K - i - 1)), dtype=np.complex128)\n        tmp = np.exp(-2.0j * np.pi * ii_coord[:, :R**i] * jj_coord[:, :R**i] /\n                     R**(i + 1))\n        D[:] = np.repeat(np.reshape(tmp, (R, R**i, 1)), R ** (K-i-1), axis=2)\n        tmp_twid = np.reshape(tmp_perm, (N, )) * np.reshape(D, (N, ))\n        # Product with Butterfly\n        y[:] = np.reshape(dft_mat @ np.reshape(tmp_twid, (R, R**(K-1))), (N, ))\n\ndef symm(alpha: float, beta: float, C: np.ndarray[float, 2], A: np.ndarray[float, 2], B: np.ndarray[float, 2]):\n\n    temp2 = np.empty((C.shape[1], ), dtype=C.dtype)\n    C *= beta\n    for i in range(C.shape[0]):\n        for j in range(C.shape[1]):\n            C[:i, j] += alpha * B[i, j] * A[i, :i]\n            temp2[j] = B[:i, j] @ A[i, :i]\n        C[i, :] += alpha * B[i, :] * A[i, i] + alpha * temp2\n\ndef syr2k(alpha: float, beta: float, C: np.ndarray[float,2], A: np.ndarray[float,2], B: np.ndarray[float,2]):\n\n    for i in range(A.shape[0]):\n        C[i, :i + 1] *= beta\n        for k in range(A.shape[1]):\n            C[i, :i + 1] += (A[:i + 1, k] * alpha * B[i, k] +\n                             B[:i + 1, k] * alpha * A[i, k])\n\ndef syrk(alpha: float, beta: float, C: np.ndarray[float, 2], A: np.ndarray[float, 2]):\n\n    for i in range(A.shape[0]):\n        C[i, :i + 1] *= beta\n        for k in range(A.shape[1]):\n            C[i, :i + 1] += alpha * A[i, k] * A[:i + 1, k]\n\ndef trisolv(L: np.ndarray[float, 2], x: np.ndarray[float, 1], b: np.ndarray[float,1]):\n    for i in range(x.shape[0]):\n        x[i] = (b[i] - L[i, :i] @ x[:i]) / L[i, i]\n\ndef trmm(alpha: float, A: np.ndarray[float,2], B: np.ndarray[float,2]):\n\n    for i in range(B.shape[0]):\n        for j in range(B.shape[1]):\n            B[i, j] += np.dot(A[i + 1:, i], B[i + 1:, j])\n    B *= alpha\n\ndef vadv(utens_stage: np.ndarray[float, 3], u_stage: np.ndarray[float,3], wcon: np.ndarray[float,3], u_pos: np.ndarray[float,3], utens: np.ndarray[float,3], dtr_stage: float):\n    BET_M = 0.5\n    BET_P = 0.5\n\n    I, J, K = utens_stage.shape[0], utens_stage.shape[1], utens_stage.shape[2]\n    # ccol = np.ndarray((I, J, K), dtype=utens_stage.dtype)\n    # dcol = np.ndarray((I, J, K), dtype=utens_stage.dtype)\n    # data_col = np.ndarray((I, J), dtype=utens_stage.dtype)\n    ccol = np.empty((I, J, K), dtype=utens_stage.dtype)\n    dcol = np.empty((I, J, K), dtype=utens_stage.dtype)\n    data_col = np.empty((I, J), dtype=utens_stage.dtype)\n\n    for k in range(1):\n        gcv = 0.25 * (wcon[1:, :, k + 1] + wcon[:-1, :, k + 1])\n        cs = gcv * BET_M\n\n        ccol[:, :, k] = gcv * BET_P\n        bcol = dtr_stage - ccol[:, :, k]\n\n        # update the d column\n        correction_term = -cs * (u_stage[:, :, k + 1] - u_stage[:, :, k])\n        dcol[:, :, k] = (dtr_stage * u_pos[:, :, k] + utens[:, :, k] +\n                         utens_stage[:, :, k] + correction_term)\n\n        # Thomas forward\n        divided = 1.0 / bcol\n        ccol[:, :, k] = ccol[:, :, k] * divided\n        dcol[:, :, k] = dcol[:, :, k] * divided\n\n    for k in range(1, K - 1):\n        gav = -0.25 * (wcon[1:, :, k] + wcon[:-1, :, k])\n        gcv = 0.25 * (wcon[1:, :, k + 1] + wcon[:-1, :, k + 1])\n\n        as_ = gav * BET_M\n        cs = gcv * BET_M\n\n        acol = gav * BET_P\n        ccol[:, :, k] = gcv * BET_P\n        bcol = dtr_stage - acol - ccol[:, :, k]\n\n        # update the d column\n        correction_term = -as_ * (u_stage[:, :, k - 1] -\n                                  u_stage[:, :, k]) - cs * (\n                                      u_stage[:, :, k + 1] - u_stage[:, :, k])\n        dcol[:, :, k] = (dtr_stage * u_pos[:, :, k] + utens[:, :, k] +\n                         utens_stage[:, :, k] + correction_term)\n\n        # Thomas forward\n        divided = 1.0 / (bcol - ccol[:, :, k - 1] * acol)\n        ccol[:, :, k] = ccol[:, :, k] * divided\n        dcol[:, :, k] = (dcol[:, :, k] - (dcol[:, :, k - 1]) * acol) * divided\n\n    for k in range(K - 1, K):\n        gav = -0.25 * (wcon[1:, :, k] + wcon[:-1, :, k])\n        as_ = gav * BET_M\n        acol = gav * BET_P\n        bcol = dtr_stage - acol\n\n        # update the d column\n        correction_term = -as_ * (u_stage[:, :, k - 1] - u_stage[:, :, k])\n        dcol[:, :, k] = (dtr_stage * u_pos[:, :, k] + utens[:, :, k] +\n                         utens_stage[:, :, k] + correction_term)\n\n        # Thomas forward\n        divided = 1.0 / (bcol - ccol[:, :, k - 1] * acol)\n        dcol[:, :, k] = (dcol[:, :, k] - (dcol[:, :, k - 1]) * acol) * divided\n\n    for k in range(K - 1, K - 2, -1):\n        datacol = dcol[:, :, k]\n        data_col[:] = datacol\n        utens_stage[:, :, k] = dtr_stage * (datacol - u_pos[:, :, k])\n\n    for k in range(K - 2, -1, -1):\n        datacol = dcol[:, :, k] - ccol[:, :, k] * data_col[:, :]\n        data_col[:] = datacol\n        utens_stage[:, :, k] = dtr_stage * (datacol - u_pos[:, :, k])\n"
  },
  {
    "path": "bench/codon/primes.codon",
    "content": "from sys import argv\nfrom time import time\n\ndef is_prime(n):\n    factors = 0\n    for i in range(2, n):\n        if n % i == 0:\n            factors += 1\n    return factors == 0\n\nlimit = int(argv[1])\ntotal = 0\n\nt0 = time()\n@par(schedule='dynamic')\nfor i in range(2, limit):\n    if is_prime(i):\n        total += 1\nt1 = time()\n\nprint(total)\nprint(t1 - t0)\n"
  },
  {
    "path": "bench/codon/primes.py",
    "content": "from sys import argv\nfrom time import time\n\ndef is_prime(n):\n    factors = 0\n    for i in range(2, n):\n        if n % i == 0:\n            factors += 1\n    return factors == 0\n\nlimit = int(argv[1])\ntotal = 0\n\nt0 = time()\nfor i in range(2, limit):\n    if is_prime(i):\n        total += 1\nt1 = time()\n\nprint(total)\nprint(t1 - t0)\n"
  },
  {
    "path": "bench/codon/set_partition.cpp",
    "content": "#include <algorithm>\n#include <chrono>\n#include <functional>\n#include <iostream>\n#include <vector>\n\ntemplate <class T> using vec = std::vector<T>;\n\ninline vec<int> range(int start, int stop) {\n  vec<int> v(stop - start);\n  uint j = 0;\n  for (int i = start; i < stop; i++)\n    v[j++] = i;\n  return v;\n}\n\ninline bool conforms(const vec<vec<int>> &candidate, int minsize, int forgive) {\n  int deficit = 0;\n  for (const auto &p : candidate) {\n    int need = minsize - static_cast<int>(p.size());\n    if (need > 0)\n      deficit += need;\n  }\n  return deficit <= forgive;\n}\n\ninline void partition_filtered(const vec<int> &collection,\n                               std::function<void(const vec<vec<int>> &)> callback,\n                               int minsize = 1, int forgive = 0) {\n  if (collection.size() == 1) {\n    callback({collection});\n    return;\n  }\n\n  auto first = collection[0];\n  auto loop = [&](const vec<vec<int>> &smaller) {\n    int n = 0;\n\n    vec<vec<int>> candidate;\n    candidate.reserve(smaller.size() + 1);\n    vec<int> rep;\n\n    for (const auto &subset : smaller) {\n      candidate.resize(n);\n      for (int i = 0; i < n; i++)\n        candidate[i] = smaller[i];\n\n      rep.clear();\n      rep.reserve(subset.size() + 1);\n      rep.push_back(first);\n      rep.insert(rep.end(), subset.begin(), subset.end());\n      candidate.push_back({rep});\n\n      for (int i = n + 1; i < smaller.size(); i++)\n        candidate.push_back(smaller[i]);\n\n      if (conforms(candidate, minsize, forgive))\n        callback(candidate);\n      ++n;\n    }\n\n    candidate.clear();\n    candidate.push_back({first});\n    candidate.insert(candidate.end(), smaller.begin(), smaller.end());\n\n    if (conforms(candidate, minsize, forgive))\n      callback(candidate);\n  };\n\n  vec<int> new_collection(collection.begin() + 1, collection.end());\n  partition_filtered(new_collection, loop, minsize, forgive + 1);\n}\n\nint main(int argc, char *argv[]) {\n  using clock = std::chrono::high_resolution_clock;\n  using std::chrono::duration_cast;\n  using std::chrono::milliseconds;\n\n  auto t = clock::now();\n  int n = 1;\n  int x = 0;\n\n  auto callback = [&](const vec<vec<int>> &p) {\n    auto copy = p;\n    std::sort(copy.begin(), copy.end());\n    x += copy[copy.size() / 3][0];\n  };\n\n  auto something = range(1, std::atoi(argv[1]));\n  partition_filtered(something, callback, 2);\n  std::cout << x << std::endl;\n  std::cout << (duration_cast<milliseconds>(clock::now() - t).count() / 1e3)\n            << std::endl;\n}\n"
  },
  {
    "path": "bench/codon/set_partition.py",
    "content": "# https://stackoverflow.com/questions/73473074/speed-up-set-partition-generation-by-skipping-ones-with-subsets-smaller-or-large\nimport sys\nimport time\n\ndef conforms(candidate, minsize, forgive):\n    \"\"\"\n    Check if partition `candidate` is at most `forgive` additions from making\n    all its elements conform to having minimum size `minsize`\n    \"\"\"\n    deficit = 0\n    for p in candidate:\n        need = minsize - len(p)\n        if need > 0:\n            deficit += need\n\n    # Is the deficit small enough?\n    return (deficit <= forgive)\n\ndef partition_filtered(collection, minsize=1, forgive=0):\n    \"\"\"\n    Generate partitions that contain at least `minsize` elements per set;\n    allow `forgive` missing elements, which can get added in subsequent steps\n    \"\"\"\n    if len(collection) == 1:\n        yield [ collection ]\n        return\n\n    first = collection[0]\n    for smaller in partition_filtered(collection[1:], minsize, forgive=forgive+1):\n        # insert `first` in each of the subpartition's subsets\n        for n, subset in enumerate(smaller):\n            candidate = smaller[:n] + [[ first ] + subset]  + smaller[n+1:]\n            if conforms(candidate, minsize, forgive):\n                yield candidate\n\n        # put `first` in its own subset\n        candidate = [ [ first ] ] + smaller\n        if conforms(candidate, minsize, forgive):\n            yield candidate\n\n\nimport time\n\nt = time.time()\nsomething = list(range(1, int(sys.argv[1])))\nv = partition_filtered(something, minsize=2)\nx = 0\nfor p in v:\n    p.sort()\n    x += p[len(p) // 3][0]\nprint(x)\nprint(time.time() - t)\n"
  },
  {
    "path": "bench/codon/spectral_norm.py",
    "content": "\"\"\"\nMathWorld: \"Hundred-Dollar, Hundred-Digit Challenge Problems\", Challenge #3.\nhttp://mathworld.wolfram.com/Hundred-DollarHundred-DigitChallengeProblems.html\n\nThe Computer Language Benchmarks Game\nhttp://benchmarksgame.alioth.debian.org/u64q/spectralnorm-description.html#spectralnorm\n\nContributed by Sebastien Loisel\nFixed by Isaac Gouy\nSped up by Josh Goldfoot\nDirtily sped up by Simon Descarpentries\nConcurrency by Jason Stitt\nAdapted for Codon by @arshajii\n\"\"\"\n\nfrom time import time\n\nDEFAULT_N = 260\n\ndef eval_A(i, j):\n    return 1.0 / ((i + j) * (i + j + 1) // 2 + i + 1)\n\ndef eval_times_u(func, u):\n    return [func((i, u)) for i in range(len(list(u)))]\n\ndef part_At_times_u(i_u):\n    i, u = i_u\n    partial_sum = 0.\n    for j, u_j in enumerate(u):\n        partial_sum += eval_A(j, i) * u_j\n    return partial_sum\n\ndef part_A_times_u(i_u):\n    i, u = i_u\n    partial_sum = 0.\n    for j, u_j in enumerate(u):\n        partial_sum += eval_A(i, j) * u_j\n    return partial_sum\n\ndef eval_AtA_times_u(u):\n    return eval_times_u(part_At_times_u, eval_times_u(part_A_times_u, u))\n\ndef bench_spectral_norm(loops):\n    range_it = range(loops)\n    total = 0.\n    for _ in range_it:\n        u = [1.] * DEFAULT_N\n        v = None\n        for dummy in range(10):\n            v = eval_AtA_times_u(u)\n            u = eval_AtA_times_u(v)\n        vBv = vv = 0.\n        for ue, ve in zip(u, v):\n            vBv += ue * ve\n            vv += ve * ve\n        total += vBv + vv\n    return total\n\nt0 = time()\nprint(bench_spectral_norm(100))\nt1 = time()\nprint(t1 - t0)\n"
  },
  {
    "path": "bench/codon/sum.py",
    "content": "# https://towardsdatascience.com/getting-started-with-pypy-ef4ba5cb431c\nimport time\nt1 = time.time()\nnums = range(50000000)\nsum = 0\nfor k in nums:\n    sum = sum + k\nprint(\"Sum of 50000000 numbers is : \", sum)\nt2 = time.time()\nt = t2 - t1\nprint(t)\n"
  },
  {
    "path": "bench/codon/taq.cpp",
    "content": "#include <algorithm>\n#include <chrono>\n#include <cmath>\n#include <fstream>\n#include <iostream>\n#include <numeric>\n#include <sstream>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\nnamespace {\ntemplate <typename It> double mean(It begin, It end) {\n  double sum = std::accumulate(begin, end, 0.0);\n  double mean = sum / std::distance(begin, end);\n  return mean;\n}\n\ntemplate <typename It> double stdev(It begin, It end) {\n  auto n = std::distance(begin, end);\n  double sum = std::accumulate(begin, end, 0.0);\n  double mean = sum / n;\n  double sq_sum = std::inner_product(begin, end, begin, 0.0);\n  double stdev = std::sqrt(sq_sum / n - mean * mean);\n  return stdev;\n}\n\nstd::vector<int> find_peaks(const std::vector<double> &y) {\n  int lag = 100;\n  double threshold = 10.0;\n  double influence = 0.5;\n  int t = y.size();\n  std::vector<int> signals(t);\n\n  if (t <= lag)\n    return signals;\n\n  std::vector<double> filtered_y;\n  filtered_y.reserve(t);\n  for (int i = 0; i < t; i++)\n    filtered_y.push_back(i < lag ? y[i] : 0.0);\n\n  std::vector<double> avg_filter(t);\n  std::vector<double> std_filter(t);\n  avg_filter[lag] = mean(y.begin(), y.begin() + lag);\n  avg_filter[lag] = stdev(y.begin(), y.begin() + lag);\n\n  for (int i = lag; i < t; i++) {\n    if (std::abs(y[i] - avg_filter[i - 1]) > threshold * std_filter[i - 1]) {\n      signals[i] = y[i] > avg_filter[i - 1] ? +1 : -1;\n      filtered_y[i] = influence * y[i] + (1 - influence) * filtered_y[i - 1];\n    } else {\n      signals[i] = 0;\n      filtered_y[i] = y[i];\n    }\n\n    avg_filter[i] = mean(filtered_y.begin() + (i - lag), filtered_y.begin() + i);\n    std_filter[i] = stdev(filtered_y.begin() + (i - lag), filtered_y.begin() + i);\n  }\n\n  return signals;\n}\n\nstd::pair<std::vector<double>, std::vector<int>>\nprocess_data(const std::vector<std::pair<uint64_t, long>> &series) {\n  std::unordered_map<uint64_t, long> grouped;\n  for (const auto &p : series) {\n    auto bucket = p.first;\n    auto volume = p.second;\n    grouped[bucket] += volume;\n  }\n\n  std::vector<std::pair<uint64_t, long>> temp;\n  temp.reserve(grouped.size());\n  for (const auto &p : grouped)\n    temp.emplace_back(p.first, p.second);\n  std::sort(temp.begin(), temp.end());\n\n  std::vector<double> y;\n  y.reserve(grouped.size());\n  for (const auto &p : temp)\n    y.push_back(p.second);\n\n  return {y, find_peaks(y)};\n}\n\nconst uint64_t BUCKET_SIZE = 1000000000;\n} // namespace\n\nint main(int argc, char *argv[]) {\n  using clock = std::chrono::high_resolution_clock;\n  using std::chrono::duration_cast;\n  using std::chrono::milliseconds;\n\n  auto t = clock::now();\n  std::unordered_map<std::string, std::vector<std::pair<uint64_t, long>>> data;\n  std::ifstream file(argv[1]);\n  bool header = true;\n\n  for (std::string line; std::getline(file, line);) {\n    if (header) {\n      header = false;\n      continue;\n    }\n\n    std::stringstream ss(line);\n    std::vector<std::string> x;\n    for (std::string field; std::getline(ss, field, '|');)\n      x.push_back(field);\n\n    if (x[0] == \"END\" || x[4] == \"ENDP\")\n      continue;\n\n    uint64_t timestamp = std::stoull(x[0]);\n    std::string symbol = x[2];\n    long volume = std::stol(x[4]);\n    data[symbol].emplace_back(timestamp / BUCKET_SIZE, volume);\n  }\n\n  for (auto &e : data) {\n    auto symbol = e.first;\n    auto &series = e.second;\n    auto p = process_data(series);\n    auto &signals = p.second;\n    std::cout << symbol << \" \" << std::reduce(signals.begin(), signals.end())\n              << std::endl;\n  }\n\n  std::cout << (duration_cast<milliseconds>(clock::now() - t).count() / 1e3)\n            << std::endl;\n}\n"
  },
  {
    "path": "bench/codon/taq.py",
    "content": "# Parses TAQ file and performs volume peak detection\nfrom sys import argv\nfrom time import time\nfrom statistics import mean, stdev\n\n# https://stackoverflow.com/questions/22583391/peak-signal-detection-in-realtime-timeseries-data\ndef find_peaks(y):\n    lag = 100\n    threshold = 10.0\n    influence = 0.5\n\n    t = len(y)\n    signals = [0. for _ in range(t)]\n\n    if t <= lag:\n        return signals\n\n    filtered_y = [y[i] if i < lag else 0. for i in range(t)]\n    avg_filter = [0. for _ in range(t)]\n    std_filter = [0. for _ in range(t)]\n    avg_filter[lag] = mean(y[:lag])\n    std_filter[lag] = stdev(y[:lag])\n\n    for i in range(lag, t):\n        if abs(y[i] - avg_filter[i-1]) > threshold * std_filter[i-1]:\n            signals[i] = +1 if y[i] > avg_filter[i-1] else -1\n            filtered_y[i] = influence*y[i] + (1 - influence)*filtered_y[i-1]\n        else:\n            signals[i] = 0\n            filtered_y[i] = y[i]\n\n        avg_filter[i] = mean(filtered_y[i-lag:i])\n        std_filter[i] = stdev(filtered_y[i-lag:i])\n\n    return signals\n\ndef process_data(series):\n    grouped = {}\n    for bucket, volume in series:\n        grouped[bucket] = grouped.get(bucket, 0) + volume\n\n    y = [float(t[1]) for t in sorted(grouped.items())]\n    return y, find_peaks(y)\n\nBUCKET_SIZE = 1_000_000_000\nt0 = time()\n\ndata = {}\nwith open(argv[1]) as f:\n    header = True\n\n    for line in f:\n        if header:\n            header = False\n            continue\n\n        x = line.split('|')\n        if x[0] == 'END' or x[4] == 'ENDP':\n            continue\n\n        timestamp = int(x[0])\n        symbol = x[2]\n        volume = int(x[4])\n\n        series = data.setdefault(symbol, [])\n        series.append((timestamp // BUCKET_SIZE, volume))\n\nfor symbol, series in data.items():\n    y, signals = process_data(series)\n    print(symbol, sum(signals))\n\nt1 = time()\nprint(t1 - t0)\n"
  },
  {
    "path": "bench/codon/word_count.cpp",
    "content": "#include <chrono>\n#include <fstream>\n#include <iostream>\n#include <sstream>\n#include <string>\n#include <unordered_map>\n\nusing namespace std;\n\nint main(int argc, char *argv[]) {\n  using clock = chrono::high_resolution_clock;\n  using chrono::duration_cast;\n  using chrono::milliseconds;\n  auto t = clock::now();\n\n  cin.tie(nullptr);\n  cout.sync_with_stdio(false);\n\n  if (argc != 2) {\n    cerr << \"Expected one argument.\" << endl;\n    return -1;\n  }\n\n  ifstream file(argv[1]);\n  if (!file.is_open()) {\n    cerr << \"Could not open file: \" << argv[1] << endl;\n    return -1;\n  }\n\n  unordered_map<string, int> map;\n  for (string line; getline(file, line);) {\n    istringstream sin(line);\n    for (string word; sin >> word;)\n      map[word] += 1;\n  }\n\n  cout << map.size() << endl;\n  cout << (duration_cast<milliseconds>(clock::now() - t).count() / 1e3) << endl;\n}\n"
  },
  {
    "path": "bench/codon/word_count.py",
    "content": "from sys import argv\nfrom time import time\n\nt0 = time()\nwc = {}\nfilename = argv[-1]\n\nwith open(filename) as f:\n    for l in f:\n        for w in l.split():\n            wc[w] = wc.get(w, 0) + 1\n\nprint(len(wc))\nt1 = time()\nprint(t1 - t0)\n"
  },
  {
    "path": "bench/run.sh",
    "content": "# set -x\n\ntrap 'echo Exiting...; exit' INT\n\nget_data() {\n    git clone https://github.com/exaloop/seq\n    mkdir -p test\n    cp -r seq/test/* test/\n    cp -r ../test/* test/\n    mkdir -p build\n    mkdir -p data\n    curl -L https://ftp.nyse.com/Historical%20Data%20Samples/DAILY%20TAQ/EQY_US_ALL_NBBO_20250102.gz | gzip -d -c | head -n10000000 > data/taq.txt\n    curl -L https://hgdownload.soe.ucsc.edu/goldenPath/hg38/chromosomes/chr22.fa.gz | gzip -d -c > data/chr22.fa\n    samtools faidx data/chr22.fa\n    curl -L https://github.com/lh3/biofast/releases/download/biofast-data-v1/biofast-data-v1.tar.gz | tar zxvf - -C data\n    curl -L http://cb.csail.mit.edu/cb/seq/nbt/sw-data.tar.bz2 | tar jxvf - -C data\n    curl -L http://cb.csail.mit.edu/cb/seq/nbt/umi-data.bz2 | bzip2 -c -d > data/hgmm_100_R1.fastq\n    run/exe/bench_fasta 25000000 > data/three_.fa\n    samtools faidx data/three_.fa\n    samtools faidx data/three_.fa THREE > data/three.fa\n    rm -f data/three_.fa\n    samtools view https://hgdownload.cse.ucsc.edu/goldenPath/hg19/encodeDCC/wgEncodeSydhRnaSeq/wgEncodeSydhRnaSeqK562Ifna6hPolyaAln.bam chr22 -b -o data/rnaseq.bam\n    curl -L https://hgdownload.soe.ucsc.edu/goldenPath/hg19/chromosomes/chr22.fa.gz | gzip -d -c > data/chr22_hg19.fa\n    samtools faidx data/chr22_hg19.fa\n    samtools index data/rnaseq.bam\n}\n\ncompile() {\n    name=$1\n    path=$2\n    extra=$3\n    echo -n \"====> C: ${name} ${path} \"\n    start=$(date +%s.%N)\n    CODON_DEBUG=lt /usr/bin/time -f 'time=%e mem=%M exit=%x' \\\n        codon build -release $extra $path -o run/exe/${name}.exe \\\n        >run/log/${name}.compile.txt 2>&1\n    duration=$(echo \"$(date +%s.%N) $start\" | awk '{printf \"%.1f\", $1-$2}')\n    echo \"[$? run/log/${name}.compile.txt ${duration}]\"\n}\n\nrun() {\n    name=$1\n    args=\"${@:2}\"\n    echo -n \"      R: $name $args \"\n    start=$(date +%s.%N)\n    eval \"/usr/bin/time -o run/log/${name}.time.txt -f 'time=%e mem=%M exit=%x' run/exe/${name}.exe $args >run/log/${name}.run.txt 2>&1\"\n    duration=$(echo \"$(date +%s.%N) $start\" | awk '{printf \"%.1f\", $1-$2}')\n    echo \"[$? run/log/${name}.run.txt ${duration}]\"\n}\n\n# get_data\nmkdir -p run/exe\nmkdir -p run/log\nmkdir -p build\n\nbench() {\n    path=$1\n    args=$2\n    dirname=$(basename $(dirname $path))\n    filename=$(basename $path)\n    filename=${filename%.*}\n    extra=\"\"\n    if [[ $path == *\"seq/\"* ]]\n    then\n        dirname=\"seq_${dirname}\"\n        extra=\"-plugin seq\"\n    fi\n    name=\"${dirname}_${filename}\"\n\n    istart=$(date +%s.%N)\n    compile $name $path \"$extra\"\n    run $name $args\n    duration=$(echo \"$(date +%s.%N) $istart\" | awk '{printf \"%.1f\", $1-$2}')\n    echo \"      T: ${duration}\"\n}\n\nbench ../test/stdlib/bisect_test.codon\nbench ../test/stdlib/cmath_test.codon\nbench ../test/stdlib/datetime_test.codon\nbench ../test/stdlib/heapq_test.codon\nbench ../test/stdlib/itertools_test.codon\nbench ../test/stdlib/math_test.codon\nbench ../test/stdlib/operator_test.codon\nbench ../test/stdlib/random_test.codon\nbench ../test/stdlib/re_test.codon\nbench ../test/stdlib/sort_test.codon\nbench ../test/stdlib/statistics_test.codon\nbench ../test/stdlib/str_test.codon\n\nbench ../test/numpy/test_dtype.codon\nbench ../test/numpy/test_fft.codon\nbench ../test/numpy/test_functional.codon\nbench ../test/numpy/test_fusion.codon\nbench ../test/numpy/test_indexing.codon\nbench ../test/numpy/test_io.codon\nbench ../test/numpy/test_lib.codon\nbench ../test/numpy/test_linalg.codon\nbench ../test/numpy/test_loops.codon\nbench ../test/numpy/test_misc.codon\nbench ../test/numpy/test_ndmath.codon\nbench ../test/numpy/test_npdatetime.codon\nbench ../test/numpy/test_pybridge.codon\nbench ../test/numpy/test_reductions.codon\nbench ../test/numpy/test_routines.codon\nbench ../test/numpy/test_sorting.codon\nbench ../test/numpy/test_statistics.codon\nbench ../test/numpy/test_window.codon\nbench ../test/numpy/random_tests/test_mt19937.codon\nbench ../test/numpy/random_tests/test_pcg64.codon\nbench ../test/numpy/random_tests/test_philox.codon\nbench ../test/numpy/random_tests/test_sfc64.codon\n\nbench codon/binary_trees.codon 20 # 6s\nbench codon/chaos.codon /dev/null # 1s\nbench codon/fannkuch.codon 11 # 6s\nbench codon/float.py\nbench codon/go.codon\n# TODO: bench codon/mandelbrot.codon\nbench codon/nbody.py 10000000 # 6s\nbench codon/npbench.codon # ...s\nbench codon/set_partition.py 15 # 15s\nbench codon/spectral_norm.py\nbench codon/primes.codon 100000 # 3s\nbench codon/sum.py\nbench codon/taq.py data/taq.txt # 10s\nbench codon/word_count.py data/taq.txt # 10s\n\nbench ../seq/test/core/align.codon # 10s\nbench ../seq/test/core/big.codon\nbench ../seq/test/core/bltin.codon\nbench ../seq/test/core/bwtsa.codon # 10s\nbench ../seq/test/core/containers.codon\nbench ../seq/test/core/formats.codon\nbench ../seq/test/core/kmers.codon\nbench ../seq/test/core/match.codon\nbench ../seq/test/core/proteins.codon\nbench ../seq/test/core/serialization.codon\n\nbench ../seq/test/pipeline/canonical_opt.codon\nbench ../seq/test/pipeline/interalign.codon # 25s\nbench ../seq/test/pipeline/prefetch.codon\nbench ../seq/test/pipeline/revcomp_opt.codon # 1s\n\nbench ../seq/test/bench/16mer.codon data/chr22.fa # 10s\nbench ../seq/test/bench/bedcov.codon \"data/biofast-data-v1/ex-anno.bed data/biofast-data-v1/ex-rna.bed\" # 25s\nbench ../seq/test/bench/cpg.codon data/chr22.fa # 1s\nbench ../seq/test/bench/fasta.codon 25000000 # 10s\nbench ../seq/test/bench/fastx.codon \"data/chr22.fa data/biofast-data-v1/M_abscessus_HiSeq.fq\" # 15s\n# TODO: ../seq/test/bench/fmindex.codon\nbench ../seq/test/bench/fqcnt.codon data/biofast-data-v1/M_abscessus_HiSeq.fq # 1s\nbench ../seq/test/bench/hamming.codon data/chr22.fa # 20s\nbench ../seq/test/bench/hash.codon data/chr22.fa # 15s\nbench ../seq/test/bench/kmercnt.codon data/biofast-data-v1/M_abscessus_HiSeq.fq # 25s\nbench ../seq/test/bench/knucleotide.codon \"<data/three.fa\" # 15s\nbench ../seq/test/bench/match.codon data/chr22.fa # 1m\nbench ../seq/test/bench/rc.codon data/chr22.fa  # 20s\nbench ../seq/test/bench/revcomp.codon \"<data/three.fa\" # 1s\n# TODO: ../seq/test/bench/sw.codon\n\nbench ../seq/test/apps/avid/avid.codon data/wgac100.txt # 40s\n# TODO: ../seq/test/apps/bwa/fastmap_build.codon\nbench ../seq/test/apps/cora/hom_exact.codon \"data/chr22.fa _cora\" # 10s\nbench ../seq/test/apps/cora/hom_inexact.codon \"data/chr22.fa 1 _cora\" # 3m\nbench ../seq/test/apps/gatk/splitncigar.codon \"data/rnaseq.bam data/chr22_hg19.fa _gatk\" # 2s; TODO: OUTPUT EMPTY\n# TODO: ../seq/test/apps/minimap2/sw_simple.codon data/queries.small data/targets.small # compiles but got stuck on running!\n# TODO: ../seq/test/apps/minimap2/sw.codon data/queries.small data/targets.small # compiles but got stuck on running!\nbench ../seq/test/apps/mrsfast/mrsfast.codon \"index data/chr22.fa\" # 15s\nbench ../seq/test/apps/mrsfast/mrsfast.codon \"search data/chr22.fa data/biofast-data-v1/M_abscessus_HiSeq.fq _mrsfast\" # 20s\nbench ../seq/test/apps/umi/whitelist.codon data/hgmm_100_R1.fastq # 5s\n\n# CODON_DIR=../../install python setup_codon.py build_ext --inplace\n\ncleanup () {\n    rm -f _cora* _gatk _mrsfast testjar.bin _dump_* fmi.bin\n}\n\ncleanup\n"
  },
  {
    "path": "cmake/CMakeRC.cmake",
    "content": "# This block is executed when generating an intermediate resource file, not when\n# running in CMake configure mode\nif(_CMRC_GENERATE_MODE)\n    # Read in the digits\n    file(READ \"${INPUT_FILE}\" bytes HEX)\n    # Format each pair into a character literal. Heuristics seem to favor doing\n    # the conversion in groups of five for fastest conversion\n    string(REGEX REPLACE \"(..)(..)(..)(..)(..)\" \"'\\\\\\\\x\\\\1','\\\\\\\\x\\\\2','\\\\\\\\x\\\\3','\\\\\\\\x\\\\4','\\\\\\\\x\\\\5',\" chars \"${bytes}\")\n    # Since we did this in groups, we have some leftovers to clean up\n    string(LENGTH \"${bytes}\" n_bytes2)\n    math(EXPR n_bytes \"${n_bytes2} / 2\")\n    math(EXPR remainder \"${n_bytes} % 5\") # <-- '5' is the grouping count from above\n    set(cleanup_re \"$\")\n    set(cleanup_sub )\n    while(remainder)\n        set(cleanup_re \"(..)${cleanup_re}\")\n        set(cleanup_sub \"'\\\\\\\\x\\\\${remainder}',${cleanup_sub}\")\n        math(EXPR remainder \"${remainder} - 1\")\n    endwhile()\n    if(NOT cleanup_re STREQUAL \"$\")\n        string(REGEX REPLACE \"${cleanup_re}\" \"${cleanup_sub}\" chars \"${chars}\")\n    endif()\n    string(CONFIGURE [[\n        namespace { const char file_array[] = { @chars@ 0 }; }\n        namespace cmrc { namespace @NAMESPACE@ { namespace res_chars {\n        extern const char* const @SYMBOL@_begin = file_array;\n        extern const char* const @SYMBOL@_end = file_array + @n_bytes@;\n        }}}\n    ]] code)\n    file(WRITE \"${OUTPUT_FILE}\" \"${code}\")\n    # Exit from the script. Nothing else needs to be processed\n    return()\nendif()\n\nset(_version 2.0.0)\n\ncmake_minimum_required(VERSION 3.12)\ninclude(CMakeParseArguments)\n\nif(COMMAND cmrc_add_resource_library)\n    if(NOT DEFINED _CMRC_VERSION OR NOT (_version STREQUAL _CMRC_VERSION))\n        message(WARNING \"More than one CMakeRC version has been included in this project.\")\n    endif()\n    # CMakeRC has already been included! Don't do anything\n    return()\nendif()\n\nset(_CMRC_VERSION \"${_version}\" CACHE INTERNAL \"CMakeRC version. Used for checking for conflicts\")\n\nset(_CMRC_SCRIPT \"${CMAKE_CURRENT_LIST_FILE}\" CACHE INTERNAL \"Path to CMakeRC script\")\n\nfunction(_cmrc_normalize_path var)\n    set(path \"${${var}}\")\n    file(TO_CMAKE_PATH \"${path}\" path)\n    while(path MATCHES \"//\")\n        string(REPLACE \"//\" \"/\" path \"${path}\")\n    endwhile()\n    string(REGEX REPLACE \"/+$\" \"\" path \"${path}\")\n    set(\"${var}\" \"${path}\" PARENT_SCOPE)\nendfunction()\n\nget_filename_component(_inc_dir \"${CMAKE_BINARY_DIR}/_cmrc/include\" ABSOLUTE)\nset(CMRC_INCLUDE_DIR \"${_inc_dir}\" CACHE INTERNAL \"Directory for CMakeRC include files\")\n# Let's generate the primary include file\nfile(MAKE_DIRECTORY \"${CMRC_INCLUDE_DIR}/cmrc\")\nset(hpp_content [==[\n#ifndef CMRC_CMRC_HPP_INCLUDED\n#define CMRC_CMRC_HPP_INCLUDED\n\n#include <cassert>\n#include <functional>\n#include <iterator>\n#include <list>\n#include <map>\n#include <mutex>\n#include <string>\n#include <system_error>\n#include <type_traits>\n\n#if !(defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) || defined(CMRC_NO_EXCEPTIONS))\n#define CMRC_NO_EXCEPTIONS 1\n#endif\n\nnamespace cmrc { namespace detail { struct dummy; } }\n\n#define CMRC_DECLARE(libid) \\\n    namespace cmrc { namespace detail { \\\n    struct dummy; \\\n    static_assert(std::is_same<dummy, ::cmrc::detail::dummy>::value, \"CMRC_DECLARE() must only appear at the global namespace\"); \\\n    } } \\\n    namespace cmrc { namespace libid { \\\n    cmrc::embedded_filesystem get_filesystem(); \\\n    } } static_assert(true, \"\")\n\nnamespace cmrc {\n\nclass file {\n    const char* _begin = nullptr;\n    const char* _end = nullptr;\n\npublic:\n    using iterator = const char*;\n    using const_iterator = iterator;\n    iterator begin() const noexcept { return _begin; }\n    iterator cbegin() const noexcept { return _begin; }\n    iterator end() const noexcept { return _end; }\n    iterator cend() const noexcept { return _end; }\n    std::size_t size() const { return static_cast<std::size_t>(std::distance(begin(), end())); }\n\n    file() = default;\n    file(iterator beg, iterator end) noexcept : _begin(beg), _end(end) {}\n};\n\nclass directory_entry;\n\nnamespace detail {\n\nclass directory;\nclass file_data;\n\nclass file_or_directory {\n    union _data_t {\n        class file_data* file_data;\n        class directory* directory;\n    } _data;\n    bool _is_file = true;\n\npublic:\n    explicit file_or_directory(file_data& f) {\n        _data.file_data = &f;\n    }\n    explicit file_or_directory(directory& d) {\n        _data.directory = &d;\n        _is_file = false;\n    }\n    bool is_file() const noexcept {\n        return _is_file;\n    }\n    bool is_directory() const noexcept {\n        return !is_file();\n    }\n    const directory& as_directory() const noexcept {\n        assert(!is_file());\n        return *_data.directory;\n    }\n    const file_data& as_file() const noexcept {\n        assert(is_file());\n        return *_data.file_data;\n    }\n};\n\nclass file_data {\npublic:\n    const char* begin_ptr;\n    const char* end_ptr;\n    file_data(const file_data&) = delete;\n    file_data(const char* b, const char* e) : begin_ptr(b), end_ptr(e) {}\n};\n\ninline std::pair<std::string, std::string> split_path(const std::string& path) {\n    auto first_sep = path.find(\"/\");\n    if (first_sep == path.npos) {\n        return std::make_pair(path, \"\");\n    } else {\n        return std::make_pair(path.substr(0, first_sep), path.substr(first_sep + 1));\n    }\n}\n\nstruct created_subdirectory {\n    class directory& directory;\n    class file_or_directory& index_entry;\n};\n\nclass directory {\n    std::list<file_data> _files;\n    std::list<directory> _dirs;\n    std::map<std::string, file_or_directory> _index;\n\n    using base_iterator = std::map<std::string, file_or_directory>::const_iterator;\n\npublic:\n\n    directory() = default;\n    directory(const directory&) = delete;\n\n    created_subdirectory add_subdir(std::string name) & {\n        _dirs.emplace_back();\n        auto& back = _dirs.back();\n        auto& fod = _index.emplace(name, file_or_directory{back}).first->second;\n        return created_subdirectory{back, fod};\n    }\n\n    file_or_directory* add_file(std::string name, const char* begin, const char* end) & {\n        assert(_index.find(name) == _index.end());\n        _files.emplace_back(begin, end);\n        return &_index.emplace(name, file_or_directory{_files.back()}).first->second;\n    }\n\n    const file_or_directory* get(const std::string& path) const {\n        auto pair = split_path(path);\n        auto child = _index.find(pair.first);\n        if (child == _index.end()) {\n            return nullptr;\n        }\n        auto& entry  = child->second;\n        if (pair.second.empty()) {\n            // We're at the end of the path\n            return &entry;\n        }\n\n        if (entry.is_file()) {\n            // We can't traverse into a file. Stop.\n            return nullptr;\n        }\n        // Keep going down\n        return entry.as_directory().get(pair.second);\n    }\n\n    class iterator {\n        base_iterator _base_iter;\n        base_iterator _end_iter;\n    public:\n        using value_type = directory_entry;\n        using difference_type = std::ptrdiff_t;\n        using pointer = const value_type*;\n        using reference = const value_type&;\n        using iterator_category = std::input_iterator_tag;\n\n        iterator() = default;\n        explicit iterator(base_iterator iter, base_iterator end) : _base_iter(iter), _end_iter(end) {}\n\n        iterator begin() const noexcept {\n            return *this;\n        }\n\n        iterator end() const noexcept {\n            return iterator(_end_iter, _end_iter);\n        }\n\n        inline value_type operator*() const noexcept;\n\n        bool operator==(const iterator& rhs) const noexcept {\n            return _base_iter == rhs._base_iter;\n        }\n\n        bool operator!=(const iterator& rhs) const noexcept {\n            return !(*this == rhs);\n        }\n\n        iterator& operator++() noexcept {\n            ++_base_iter;\n            return *this;\n        }\n\n        iterator operator++(int) noexcept {\n            auto cp = *this;\n            ++_base_iter;\n            return cp;\n        }\n    };\n\n    using const_iterator = iterator;\n\n    iterator begin() const noexcept {\n        return iterator(_index.begin(), _index.end());\n    }\n\n    iterator end() const noexcept {\n        return iterator();\n    }\n};\n\ninline std::string normalize_path(std::string path) {\n    while (path.find(\"/\") == 0) {\n        path.erase(path.begin());\n    }\n    while (!path.empty() && (path.rfind(\"/\") == path.size() - 1)) {\n        path.pop_back();\n    }\n    auto off = path.npos;\n    while ((off = path.find(\"//\")) != path.npos) {\n        path.erase(path.begin() + static_cast<std::string::difference_type>(off));\n    }\n    return path;\n}\n\nusing index_type = std::map<std::string, const cmrc::detail::file_or_directory*>;\n\n} // detail\n\nclass directory_entry {\n    std::string _fname;\n    const detail::file_or_directory* _item;\n\npublic:\n    directory_entry() = delete;\n    explicit directory_entry(std::string filename, const detail::file_or_directory& item)\n        : _fname(filename)\n        , _item(&item)\n    {}\n\n    const std::string& filename() const & {\n        return _fname;\n    }\n    std::string filename() const && {\n        return std::move(_fname);\n    }\n\n    bool is_file() const {\n        return _item->is_file();\n    }\n\n    bool is_directory() const {\n        return _item->is_directory();\n    }\n};\n\ndirectory_entry detail::directory::iterator::operator*() const noexcept {\n    assert(begin() != end());\n    return directory_entry(_base_iter->first, _base_iter->second);\n}\n\nusing directory_iterator = detail::directory::iterator;\n\nclass embedded_filesystem {\n    // Never-null:\n    const cmrc::detail::index_type* _index;\n    const detail::file_or_directory* _get(std::string path) const {\n        path = detail::normalize_path(path);\n        auto found = _index->find(path);\n        if (found == _index->end()) {\n            return nullptr;\n        } else {\n            return found->second;\n        }\n    }\n\npublic:\n    explicit embedded_filesystem(const detail::index_type& index)\n        : _index(&index)\n    {}\n\n    file open(const std::string& path) const {\n        auto entry_ptr = _get(path);\n        if (!entry_ptr || !entry_ptr->is_file()) {\n#ifdef CMRC_NO_EXCEPTIONS\n            fprintf(stderr, \"Error no such file or directory: %s\\n\", path.c_str());\n            abort();\n#else\n            throw std::system_error(make_error_code(std::errc::no_such_file_or_directory), path);\n#endif\n        }\n        auto& dat = entry_ptr->as_file();\n        return file{dat.begin_ptr, dat.end_ptr};\n    }\n\n    bool is_file(const std::string& path) const noexcept {\n        auto entry_ptr = _get(path);\n        return entry_ptr && entry_ptr->is_file();\n    }\n\n    bool is_directory(const std::string& path) const noexcept {\n        auto entry_ptr = _get(path);\n        return entry_ptr && entry_ptr->is_directory();\n    }\n\n    bool exists(const std::string& path) const noexcept {\n        return !!_get(path);\n    }\n\n    directory_iterator iterate_directory(const std::string& path) const {\n        auto entry_ptr = _get(path);\n        if (!entry_ptr) {\n#ifdef CMRC_NO_EXCEPTIONS\n            fprintf(stderr, \"Error no such file or directory: %s\\n\", path.c_str());\n            abort();\n#else\n            throw std::system_error(make_error_code(std::errc::no_such_file_or_directory), path);\n#endif\n        }\n        if (!entry_ptr->is_directory()) {\n#ifdef CMRC_NO_EXCEPTIONS\n            fprintf(stderr, \"Error not a directory: %s\\n\", path.c_str());\n            abort();\n#else\n            throw std::system_error(make_error_code(std::errc::not_a_directory), path);\n#endif\n        }\n        return entry_ptr->as_directory().begin();\n    }\n};\n\n}\n\n#endif // CMRC_CMRC_HPP_INCLUDED\n]==])\n\nset(cmrc_hpp \"${CMRC_INCLUDE_DIR}/cmrc/cmrc.hpp\" CACHE INTERNAL \"\")\nset(_generate 1)\nif(EXISTS \"${cmrc_hpp}\")\n    file(READ \"${cmrc_hpp}\" _current)\n    if(_current STREQUAL hpp_content)\n        set(_generate 0)\n    endif()\nendif()\nfile(GENERATE OUTPUT \"${cmrc_hpp}\" CONTENT \"${hpp_content}\" CONDITION ${_generate})\n\nadd_library(cmrc-base INTERFACE)\ntarget_include_directories(cmrc-base INTERFACE $<BUILD_INTERFACE:${CMRC_INCLUDE_DIR}>)\n# Signal a basic C++11 feature to require C++11.\ntarget_compile_features(cmrc-base INTERFACE cxx_nullptr)\nset_property(TARGET cmrc-base PROPERTY INTERFACE_CXX_EXTENSIONS OFF)\nadd_library(cmrc::base ALIAS cmrc-base)\n\nfunction(cmrc_add_resource_library name)\n    set(args ALIAS NAMESPACE TYPE)\n    cmake_parse_arguments(ARG \"\" \"${args}\" \"\" \"${ARGN}\")\n    # Generate the identifier for the resource library's namespace\n    set(ns_re \"[a-zA-Z_][a-zA-Z0-9_]*\")\n    if(NOT DEFINED ARG_NAMESPACE)\n        # Check that the library name is also a valid namespace\n        if(NOT name MATCHES \"${ns_re}\")\n            message(SEND_ERROR \"Library name is not a valid namespace. Specify the NAMESPACE argument\")\n        endif()\n        set(ARG_NAMESPACE \"${name}\")\n    else()\n        if(NOT ARG_NAMESPACE MATCHES \"${ns_re}\")\n            message(SEND_ERROR \"NAMESPACE for ${name} is not a valid C++ namespace identifier (${ARG_NAMESPACE})\")\n        endif()\n    endif()\n    set(libname \"${name}\")\n    # Check that type is either \"STATIC\" or \"OBJECT\", or default to \"STATIC\" if\n    # not set\n    if(NOT DEFINED ARG_TYPE)\n        set(ARG_TYPE STATIC)\n    elseif(NOT \"${ARG_TYPE}\" MATCHES \"^(STATIC|OBJECT)$\")\n        message(SEND_ERROR \"${ARG_TYPE} is not a valid TYPE (STATIC and OBJECT are acceptable)\")\n        set(ARG_TYPE STATIC)\n    endif()\n    # Generate a library with the compiled in character arrays.\n    string(CONFIGURE [=[\n        #include <cmrc/cmrc.hpp>\n        #include <map>\n        #include <utility>\n\n        namespace cmrc {\n        namespace @ARG_NAMESPACE@ {\n\n        namespace res_chars {\n        // These are the files which are available in this resource library\n        $<JOIN:$<TARGET_PROPERTY:@libname@,CMRC_EXTERN_DECLS>,\n        >\n        }\n\n        namespace {\n\n        const cmrc::detail::index_type&\n        get_root_index() {\n            static cmrc::detail::directory root_directory_;\n            static cmrc::detail::file_or_directory root_directory_fod{root_directory_};\n            static cmrc::detail::index_type root_index;\n            root_index.emplace(\"\", &root_directory_fod);\n            struct dir_inl {\n                class cmrc::detail::directory& directory;\n            };\n            dir_inl root_directory_dir{root_directory_};\n            (void)root_directory_dir;\n            $<JOIN:$<TARGET_PROPERTY:@libname@,CMRC_MAKE_DIRS>,\n            >\n            $<JOIN:$<TARGET_PROPERTY:@libname@,CMRC_MAKE_FILES>,\n            >\n            return root_index;\n        }\n\n        }\n\n        cmrc::embedded_filesystem get_filesystem() {\n            static auto& index = get_root_index();\n            return cmrc::embedded_filesystem{index};\n        }\n\n        } // @ARG_NAMESPACE@\n        } // cmrc\n    ]=] cpp_content @ONLY)\n    get_filename_component(libdir \"${CMAKE_CURRENT_BINARY_DIR}/__cmrc_${name}\" ABSOLUTE)\n    get_filename_component(lib_tmp_cpp \"${libdir}/lib_.cpp\" ABSOLUTE)\n    string(REPLACE \"\\n        \" \"\\n\" cpp_content \"${cpp_content}\")\n    file(GENERATE OUTPUT \"${lib_tmp_cpp}\" CONTENT \"${cpp_content}\")\n    get_filename_component(libcpp \"${libdir}/lib.cpp\" ABSOLUTE)\n    add_custom_command(OUTPUT \"${libcpp}\"\n        DEPENDS \"${lib_tmp_cpp}\" \"${cmrc_hpp}\"\n        COMMAND ${CMAKE_COMMAND} -E copy_if_different \"${lib_tmp_cpp}\" \"${libcpp}\"\n        COMMENT \"Generating ${name} resource loader\"\n        )\n    # Generate the actual static library. Each source file is just a single file\n    # with a character array compiled in containing the contents of the\n    # corresponding resource file.\n    add_library(${name} ${ARG_TYPE} ${libcpp})\n    set_property(TARGET ${name} PROPERTY CMRC_LIBDIR \"${libdir}\")\n    set_property(TARGET ${name} PROPERTY CMRC_NAMESPACE \"${ARG_NAMESPACE}\")\n    target_link_libraries(${name} PUBLIC cmrc::base)\n    set_property(TARGET ${name} PROPERTY CMRC_IS_RESOURCE_LIBRARY TRUE)\n    if(ARG_ALIAS)\n        add_library(\"${ARG_ALIAS}\" ALIAS ${name})\n    endif()\n    cmrc_add_resources(${name} ${ARG_UNPARSED_ARGUMENTS})\nendfunction()\n\nfunction(_cmrc_register_dirs name dirpath)\n    if(dirpath STREQUAL \"\")\n        return()\n    endif()\n    # Skip this dir if we have already registered it\n    get_target_property(registered \"${name}\" _CMRC_REGISTERED_DIRS)\n    if(dirpath IN_LIST registered)\n        return()\n    endif()\n    # Register the parent directory first\n    get_filename_component(parent \"${dirpath}\" DIRECTORY)\n    if(NOT parent STREQUAL \"\")\n        _cmrc_register_dirs(\"${name}\" \"${parent}\")\n    endif()\n    # Now generate the registration\n    set_property(TARGET \"${name}\" APPEND PROPERTY _CMRC_REGISTERED_DIRS \"${dirpath}\")\n    _cm_encode_fpath(sym \"${dirpath}\")\n    if(parent STREQUAL \"\")\n        set(parent_sym root_directory)\n    else()\n        _cm_encode_fpath(parent_sym \"${parent}\")\n    endif()\n    get_filename_component(leaf \"${dirpath}\" NAME)\n    set_property(\n        TARGET \"${name}\"\n        APPEND PROPERTY CMRC_MAKE_DIRS\n        \"static auto ${sym}_dir = ${parent_sym}_dir.directory.add_subdir(\\\"${leaf}\\\")\\;\"\n        \"root_index.emplace(\\\"${dirpath}\\\", &${sym}_dir.index_entry)\\;\"\n        )\nendfunction()\n\nfunction(cmrc_add_resources name)\n    get_target_property(is_reslib ${name} CMRC_IS_RESOURCE_LIBRARY)\n    if(NOT TARGET ${name} OR NOT is_reslib)\n        message(SEND_ERROR \"cmrc_add_resources called on target '${name}' which is not an existing resource library\")\n        return()\n    endif()\n\n    set(options)\n    set(args WHENCE PREFIX)\n    set(list_args)\n    cmake_parse_arguments(ARG \"${options}\" \"${args}\" \"${list_args}\" \"${ARGN}\")\n\n    if(NOT ARG_WHENCE)\n        set(ARG_WHENCE ${CMAKE_CURRENT_SOURCE_DIR})\n    endif()\n    _cmrc_normalize_path(ARG_WHENCE)\n    get_filename_component(ARG_WHENCE \"${ARG_WHENCE}\" ABSOLUTE)\n\n    # Generate the identifier for the resource library's namespace\n    get_target_property(lib_ns \"${name}\" CMRC_NAMESPACE)\n\n    get_target_property(libdir ${name} CMRC_LIBDIR)\n    get_target_property(target_dir ${name} SOURCE_DIR)\n    file(RELATIVE_PATH reldir \"${target_dir}\" \"${CMAKE_CURRENT_SOURCE_DIR}\")\n    if(reldir MATCHES \"^\\\\.\\\\.\")\n        message(SEND_ERROR \"Cannot call cmrc_add_resources in a parent directory from the resource library target\")\n        return()\n    endif()\n\n    foreach(input IN LISTS ARG_UNPARSED_ARGUMENTS)\n        _cmrc_normalize_path(input)\n        get_filename_component(abs_in \"${input}\" ABSOLUTE)\n        # Generate a filename based on the input filename that we can put in\n        # the intermediate directory.\n        file(RELATIVE_PATH relpath \"${ARG_WHENCE}\" \"${abs_in}\")\n        if(relpath MATCHES \"^\\\\.\\\\.\")\n            # For now we just error on files that exist outside of the soure dir.\n            message(SEND_ERROR \"Cannot add file '${input}': File must be in a subdirectory of ${ARG_WHENCE}\")\n            continue()\n        endif()\n        if(DEFINED ARG_PREFIX)\n            _cmrc_normalize_path(ARG_PREFIX)\n        endif()\n        if(ARG_PREFIX AND NOT ARG_PREFIX MATCHES \"/$\")\n            set(ARG_PREFIX \"${ARG_PREFIX}/\")\n        endif()\n        get_filename_component(dirpath \"${ARG_PREFIX}${relpath}\" DIRECTORY)\n        _cmrc_register_dirs(\"${name}\" \"${dirpath}\")\n        get_filename_component(abs_out \"${libdir}/intermediate/${ARG_PREFIX}${relpath}.cpp\" ABSOLUTE)\n        # Generate a symbol name relpath the file's character array\n        _cm_encode_fpath(sym \"${relpath}\")\n        # Get the symbol name for the parent directory\n        if(dirpath STREQUAL \"\")\n            set(parent_sym root_directory)\n        else()\n            _cm_encode_fpath(parent_sym \"${dirpath}\")\n        endif()\n        # Generate the rule for the intermediate source file\n        _cmrc_generate_intermediate_cpp(${lib_ns} ${sym} \"${abs_out}\" \"${abs_in}\")\n        target_sources(${name} PRIVATE \"${abs_out}\")\n        set_property(TARGET ${name} APPEND PROPERTY CMRC_EXTERN_DECLS\n            \"// Pointers to ${input}\"\n            \"extern const char* const ${sym}_begin\\;\"\n            \"extern const char* const ${sym}_end\\;\"\n            )\n        get_filename_component(leaf \"${relpath}\" NAME)\n        set_property(\n            TARGET ${name}\n            APPEND PROPERTY CMRC_MAKE_FILES\n            \"root_index.emplace(\"\n            \"    \\\"${ARG_PREFIX}${relpath}\\\",\"\n            \"    ${parent_sym}_dir.directory.add_file(\"\n            \"        \\\"${leaf}\\\",\"\n            \"        res_chars::${sym}_begin,\"\n            \"        res_chars::${sym}_end\"\n            \"    )\"\n            \")\\;\"\n            )\n    endforeach()\nendfunction()\n\nfunction(_cmrc_generate_intermediate_cpp lib_ns symbol outfile infile)\n    add_custom_command(\n        # This is the file we will generate\n        OUTPUT \"${outfile}\"\n        # These are the primary files that affect the output\n        DEPENDS \"${infile}\" \"${_CMRC_SCRIPT}\"\n        COMMAND\n            \"${CMAKE_COMMAND}\"\n                -D_CMRC_GENERATE_MODE=TRUE\n                -DNAMESPACE=${lib_ns}\n                -DSYMBOL=${symbol}\n                \"-DINPUT_FILE=${infile}\"\n                \"-DOUTPUT_FILE=${outfile}\"\n                -P \"${_CMRC_SCRIPT}\"\n        COMMENT \"Generating intermediate file for ${infile}\"\n    )\nendfunction()\n\nfunction(_cm_encode_fpath var fpath)\n    string(MAKE_C_IDENTIFIER \"${fpath}\" ident)\n    string(MD5 hash \"${fpath}\")\n    string(SUBSTRING \"${hash}\" 0 4 hash)\n    set(${var} f_${hash}_${ident} PARENT_SCOPE)\nendfunction()\n"
  },
  {
    "path": "cmake/backtrace-config.h.in",
    "content": "/* config.h.cmake */\n\n/* ELF size: 32 or 64 */\n#define BACKTRACE_ELF_SIZE ${BACKTRACE_ELF_SIZE}\n\n/* XCOFF size: 32 or 64 */\n#cmakedefine BACKTRACE_XCOFF_SIZE\n\n/* Define to 1 if you have the __atomic functions */\n#cmakedefine HAVE_ATOMIC_FUNCTIONS 1\n\n/* Define to 1 if you have the `clock_gettime' function. */\n#cmakedefine HAVE_CLOCK_GETTIME 1\n\n/* Define to 1 if you have the declaration of `strnlen', and to 0 if you\n   don't. */\n#cmakedefine HAVE_DECL_STRNLEN 1\n\n/* Define to 1 if you have the <dlfcn.h> header file. */\n#cmakedefine HAVE_DLFCN_H 1\n\n/* Define if dl_iterate_phdr is available. */\n#cmakedefine HAVE_DL_ITERATE_PHDR 1\n\n/* Define to 1 if you have the fcntl function */\n#cmakedefine HAVE_FCNTL 1\n\n/* Define if getexecname is available. */\n#cmakedefine HAVE_GETEXECNAME 1\n\n/* Define if _Unwind_GetIPInfo is available. */\n#cmakedefine HAVE_GETIPINFO 1\n\n/* Define to 1 if you have the <inttypes.h> header file. */\n#cmakedefine HAVE_INTTYPES_H 1\n\n/* Define to 1 if you have the `z' library (-lz). */\n#cmakedefine HAVE_LIBZ 1\n\n/* Define to 1 if you have the <link.h> header file. */\n#cmakedefine HAVE_LINK_H 1\n\n/* Define if AIX loadquery is available. */\n#cmakedefine HAVE_LOADQUERY 1\n\n/* Define to 1 if you have the `lstat' function. */\n#cmakedefine HAVE_LSTAT 1\n\n/* Define to 1 if you have the <mach-o/dyld.h> header file. */\n#cmakedefine HAVE_MACH_O_DYLD_H 1\n\n/* Define to 1 if you have the <memory.h> header file. */\n#cmakedefine HAVE_MEMORY_H 1\n\n/* Define to 1 if you have the `readlink' function. */\n#cmakedefine HAVE_READLINK 1\n\n/* Define to 1 if you have the <stdint.h> header file. */\n#cmakedefine HAVE_STDINT_H 1\n\n/* Define to 1 if you have the <stdlib.h> header file. */\n#cmakedefine HAVE_STDLIB_H 1\n\n/* Define to 1 if you have the <strings.h> header file. */\n#cmakedefine HAVE_STRINGS_H 1\n\n/* Define to 1 if you have the <string.h> header file. */\n#cmakedefine HAVE_STRING_H 1\n\n/* Define to 1 if you have the __sync functions */\n#cmakedefine HAVE_SYNC_FUNCTIONS 1\n\n/* Define to 1 if you have the <sys/ldr.h> header file. */\n#cmakedefine HAVE_SYS_LDR_H 1\n\n/* Define to 1 if you have the <sys/mman.h> header file. */\n#cmakedefine HAVE_SYS_MMAN_H 1\n\n/* Define to 1 if you have the <sys/stat.h> header file. */\n#cmakedefine HAVE_SYS_STAT_H 1\n\n/* Define to 1 if you have the <sys/types.h> header file. */\n#cmakedefine HAVE_SYS_TYPES_H 1\n\n/* Define to 1 if you have the <unistd.h> header file. */\n#cmakedefine HAVE_UNISTD_H 1\n\n/* Define if -lz is available. */\n#cmakedefine HAVE_ZLIB 1\n"
  },
  {
    "path": "cmake/backtrace-supported.h.in",
    "content": "/* backtrace-supported.h.cmake -- Whether stack backtrace is supported.\n   Copyright (C) 2012-2016 Free Software Foundation, Inc.\n   Based backtrace-supported.h.in, written by Ian Lance Taylor, Google.\n\nRedistribution and use in source and binary forms, with or without\nmodification, are permitted provided that the following conditions are\nmet:\n\n    (1) Redistributions of source code must retain the above copyright\n    notice, this list of conditions and the following disclaimer.\n\n    (2) Redistributions in binary form must reproduce the above copyright\n    notice, this list of conditions and the following disclaimer in\n    the documentation and/or other materials provided with the\n    distribution.\n\n    (3) The name of the author may not be used to\n    endorse or promote products derived from this software without\n    specific prior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR\nIMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE\nDISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,\nINDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES\n(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR\nSERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)\nHOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\nSTRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING\nIN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE\nPOSSIBILITY OF SUCH DAMAGE.  */\n\n/* The file backtrace-supported.h.in is used by configure to generate\n   the file backtrace-supported.h.  The file backtrace-supported.h may\n   be #include'd to see whether the backtrace library will be able to\n   get a backtrace and produce symbolic information.  */\n\n/* BACKTRACE_SUPPORTED will be #define'd as 1 if the backtrace library\n   should work, 0 if it will not.  Libraries may #include this to make\n   other arrangements.  */\n\n#cmakedefine01 BACKTRACE_SUPPORTED\n\n/* BACKTRACE_USES_MALLOC will be #define'd as 1 if the backtrace\n   library will call malloc as it works, 0 if it will call mmap\n   instead.  This may be used to determine whether it is safe to call\n   the backtrace functions from a signal handler.  In general this\n   only applies to calls like backtrace and backtrace_pcinfo.  It does\n   not apply to backtrace_simple, which never calls malloc.  It does\n   not apply to backtrace_print, which always calls fprintf and\n   therefore malloc.  */\n\n#cmakedefine01 BACKTRACE_USES_MALLOC\n\n/* BACKTRACE_SUPPORTS_THREADS will be #define'd as 1 if the backtrace\n   library is configured with threading support, 0 if not.  If this is\n   0, the threaded parameter to backtrace_create_state must be passed\n   as 0.  */\n\n#cmakedefine01 BACKTRACE_SUPPORTS_THREADS\n\n/* BACKTRACE_SUPPORTS_DATA will be #defined'd as 1 if the backtrace_syminfo\n   will work for variables.  It will always work for functions.  */\n\n#cmakedefine01 BACKTRACE_SUPPORTS_DATA\n"
  },
  {
    "path": "cmake/config.h.in",
    "content": "#pragma once\n\n#define CODON_VERSION       \"@PROJECT_VERSION@\"\n#define CODON_VERSION_MAJOR @PROJECT_VERSION_MAJOR@\n#define CODON_VERSION_MINOR @PROJECT_VERSION_MINOR@\n#define CODON_VERSION_PATCH @PROJECT_VERSION_PATCH@\n"
  },
  {
    "path": "cmake/config.py.in",
    "content": "__version__         = \"@CODON_JIT_PYTHON_VERSION@\"\nCODON_VERSION       = \"@PROJECT_VERSION@\"\nCODON_VERSION_MAJOR = @PROJECT_VERSION_MAJOR@\nCODON_VERSION_MINOR = @PROJECT_VERSION_MINOR@\nCODON_VERSION_PATCH = @PROJECT_VERSION_PATCH@\n"
  },
  {
    "path": "cmake/deps.cmake",
    "content": "set(CPM_DOWNLOAD_VERSION 0.40.8)\nset(CPM_DOWNLOAD_LOCATION \"${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake\")\nif(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION}))\n    message(STATUS \"Downloading CPM.cmake...\")\n    file(DOWNLOAD https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake ${CPM_DOWNLOAD_LOCATION})\nendif()\ninclude(${CPM_DOWNLOAD_LOCATION})\n\nCPMAddPackage(\n    NAME peglib\n    GITHUB_REPOSITORY \"exaloop/cpp-peglib\"\n    GIT_TAG codon\n    OPTIONS \"BUILD_TESTS OFF\")\n\nCPMAddPackage(\n    NAME fmt\n    GITHUB_REPOSITORY \"fmtlib/fmt\"\n    GIT_TAG 11.1.0\n    OPTIONS \"CMAKE_POSITION_INDEPENDENT_CODE ON\"\n            \"FMT_INSTALL ON\")\n\nCPMAddPackage(\n    NAME toml\n    GITHUB_REPOSITORY \"marzer/tomlplusplus\"\n    GIT_TAG v3.2.0)\n\nCPMAddPackage(\n    NAME semver\n    GITHUB_REPOSITORY \"Neargye/semver\"\n    GIT_TAG v0.3.0)\n\nCPMAddPackage(\n    NAME zlibng\n    GITHUB_REPOSITORY \"zlib-ng/zlib-ng\"\n    VERSION 2.0.5\n    GIT_TAG 2.0.5\n    EXCLUDE_FROM_ALL YES\n    OPTIONS \"HAVE_OFF64_T ON\"\n            \"ZLIB_COMPAT ON\"\n            \"ZLIB_ENABLE_TESTS OFF\"\n            \"CMAKE_POSITION_INDEPENDENT_CODE ON\")\nif(zlibng_ADDED)\n    set_target_properties(zlib PROPERTIES EXCLUDE_FROM_ALL ON)\nendif()\n\nCPMAddPackage(\n    NAME xz\n    GITHUB_REPOSITORY \"xz-mirror/xz\"\n    VERSION 5.2.5\n    GIT_TAG e7da44d5151e21f153925781ad29334ae0786101\n    EXCLUDE_FROM_ALL YES\n    OPTIONS \"BUILD_SHARED_LIBS OFF\"\n            \"CMAKE_POSITION_INDEPENDENT_CODE ON\")\nif(xz_ADDED)\n    set_target_properties(xz PROPERTIES EXCLUDE_FROM_ALL ON)\n    set_target_properties(xzdec PROPERTIES EXCLUDE_FROM_ALL ON)\nendif()\n\nCPMAddPackage(\n    NAME bz2\n    URL \"https://www.sourceware.org/pub/bzip2/bzip2-1.0.8.tar.gz\"\n    DOWNLOAD_ONLY YES)\nif(bz2_ADDED)\n    add_library(bz2 STATIC\n        \"${bz2_SOURCE_DIR}/blocksort.c\"\n        \"${bz2_SOURCE_DIR}/huffman.c\"\n        \"${bz2_SOURCE_DIR}/crctable.c\"\n        \"${bz2_SOURCE_DIR}/randtable.c\"\n        \"${bz2_SOURCE_DIR}/compress.c\"\n        \"${bz2_SOURCE_DIR}/decompress.c\"\n        \"${bz2_SOURCE_DIR}/bzlib.c\"\n        \"${bz2_SOURCE_DIR}/libbz2.def\")\n    set_target_properties(bz2 PROPERTIES\n        COMPILE_FLAGS \"-D_FILE_OFFSET_BITS=64\"\n        POSITION_INDEPENDENT_CODE ON)\nendif()\n\nCPMAddPackage(\n    NAME bdwgc\n    GITHUB_REPOSITORY \"exaloop/bdwgc\"\n    VERSION 8.0.5\n    GIT_TAG e16c67244aff26802203060422545d38305e0160\n    EXCLUDE_FROM_ALL YES\n    OPTIONS \"CMAKE_POSITION_INDEPENDENT_CODE ON\"\n            \"BUILD_SHARED_LIBS OFF\"\n            \"enable_threads ON\"\n            \"enable_large_config ON\"\n            \"enable_thread_local_alloc ON\"\n            \"enable_handle_fork ON\")\nif(bdwgc_ADDED)\n    set_target_properties(cord PROPERTIES EXCLUDE_FROM_ALL ON)\nendif()\n\nCPMAddPackage(\n    NAME backtrace\n    GITHUB_REPOSITORY \"ianlancetaylor/libbacktrace\"\n    GIT_TAG d0f5e95a87a4d3e0a1ed6c069b5dae7cbab3ed2a\n    DOWNLOAD_ONLY YES)\nif(backtrace_ADDED)\n    set(backtrace_SOURCES\n        \"${backtrace_SOURCE_DIR}/atomic.c\"\n        \"${backtrace_SOURCE_DIR}/backtrace.c\"\n        \"${backtrace_SOURCE_DIR}/dwarf.c\"\n        \"${backtrace_SOURCE_DIR}/fileline.c\"\n        \"${backtrace_SOURCE_DIR}/mmapio.c\"\n        \"${backtrace_SOURCE_DIR}/mmap.c\"\n        \"${backtrace_SOURCE_DIR}/posix.c\"\n        \"${backtrace_SOURCE_DIR}/print.c\"\n        \"${backtrace_SOURCE_DIR}/simple.c\"\n        \"${backtrace_SOURCE_DIR}/sort.c\"\n        \"${backtrace_SOURCE_DIR}/state.c\")\n\n    # https://go.googlesource.com/gollvm/+/refs/heads/master/cmake/modules/LibbacktraceUtils.cmake\n    set(BACKTRACE_SUPPORTED 1)\n    set(BACKTRACE_ELF_SIZE 64)\n    set(HAVE_GETIPINFO 1)\n    set(BACKTRACE_USES_MALLOC 1)\n    set(BACKTRACE_SUPPORTS_THREADS 1)\n    set(BACKTRACE_SUPPORTS_DATA 1)\n    set(HAVE_SYNC_FUNCTIONS 1)\n    if(APPLE)\n        set(HAVE_MACH_O_DYLD_H 1)\n        list(APPEND backtrace_SOURCES \"${backtrace_SOURCE_DIR}/macho.c\")\n    else()\n        set(HAVE_MACH_O_DYLD_H 0)\n        list(APPEND backtrace_SOURCES \"${backtrace_SOURCE_DIR}/elf.c\")\n    endif()\n    # Generate backtrace-supported.h based on the above.\n    configure_file(\n        ${CMAKE_SOURCE_DIR}/cmake/backtrace-supported.h.in\n        ${backtrace_SOURCE_DIR}/backtrace-supported.h)\n    configure_file(\n        ${CMAKE_SOURCE_DIR}/cmake/backtrace-config.h.in\n        ${backtrace_SOURCE_DIR}/config.h)\n    add_library(backtrace STATIC ${backtrace_SOURCES})\n    target_include_directories(backtrace BEFORE PRIVATE \"${backtrace_SOURCE_DIR}\")\n    set_target_properties(backtrace PROPERTIES\n        COMPILE_FLAGS \"-funwind-tables -D_GNU_SOURCE\"\n        POSITION_INDEPENDENT_CODE ON)\nendif()\n\nCPMAddPackage(\n    NAME re2\n    GITHUB_REPOSITORY \"google/re2\"\n    VERSION 2022-06-01\n    GIT_TAG 5723bb8950318135ed9cf4fc76bed988a087f536\n    EXCLUDE_FROM_ALL YES\n    OPTIONS \"CMAKE_POSITION_INDEPENDENT_CODE ON\"\n            \"BUILD_SHARED_LIBS OFF\"\n            \"RE2_BUILD_TESTING OFF\")\n\nCPMAddPackage(\n    NAME fast_float\n    GITHUB_REPOSITORY \"fastfloat/fast_float\"\n    GIT_TAG v6.1.1\n    EXCLUDE_FROM_ALL YES)\n\nif(NOT APPLE)\n    enable_language(Fortran)\n    CPMAddPackage(\n        NAME openblas\n        GITHUB_REPOSITORY \"OpenMathLib/OpenBLAS\"\n        GIT_TAG v0.3.29\n        EXCLUDE_FROM_ALL YES\n        OPTIONS \"DYNAMIC_ARCH ON\"\n                \"BUILD_TESTING OFF\"\n                \"BUILD_BENCHMARKS OFF\"\n                \"NUM_THREADS 64\"\n                \"CCOMMON_OPT -O3\")\nendif()\n\nCPMAddPackage(\n    NAME highway\n    GITHUB_REPOSITORY \"google/highway\"\n    GIT_TAG 1.3.0\n    EXCLUDE_FROM_ALL YES\n    OPTIONS \"HWY_ENABLE_CONTRIB ON\"\n            \"HWY_ENABLE_EXAMPLES OFF\"\n            \"HWY_ENABLE_INSTALL OFF\"\n            \"HWY_ENABLE_TESTS OFF\"\n            \"BUILD_TESTING OFF\")\n"
  },
  {
    "path": "codon/app/main.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <algorithm>\n#include <cstdio>\n#include <cstdlib>\n#include <fstream>\n#include <functional>\n#include <iostream>\n#include <sstream>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"codon/cir/util/format.h\"\n#include \"codon/compiler/compiler.h\"\n#include \"codon/compiler/error.h\"\n#include \"codon/compiler/jit.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/util/common.h\"\n#include \"codon/util/jupyter.h\"\n#include \"llvm/Support/CommandLine.h\"\n#include \"llvm/Support/FileSystem.h\"\n\nnamespace {\nvoid versMsg(llvm::raw_ostream &out) {\n  out << CODON_VERSION_MAJOR << \".\" << CODON_VERSION_MINOR << \".\" << CODON_VERSION_PATCH\n      << \"\\n\";\n}\n\nbool isMacOS() {\n#ifdef __APPLE__\n  return true;\n#else\n  return false;\n#endif\n}\n\nconst std::vector<std::string> &supportedExtensions() {\n  static const std::vector<std::string> extensions = {\".codon\", \".py\", \".seq\"};\n  return extensions;\n}\n\nbool hasExtension(const std::string &filename, const std::string &extension) {\n  return filename.size() >= extension.size() &&\n         filename.compare(filename.size() - extension.size(), extension.size(),\n                          extension) == 0;\n}\n\nstd::string trimExtension(const std::string &filename, const std::string &extension) {\n  if (hasExtension(filename, extension)) {\n    return filename.substr(0, filename.size() - extension.size());\n  } else {\n    return filename;\n  }\n}\n\nstd::string makeOutputFilename(const std::string &filename,\n                               const std::string &extension) {\n  for (const auto &ext : supportedExtensions()) {\n    if (hasExtension(filename, ext))\n      return trimExtension(filename, ext) + extension;\n  }\n  return filename + extension;\n}\n\nvoid display(const codon::error::ParserErrorInfo &e) {\n  using codon::MessageGroupPos;\n  std::unordered_set<std::string> seen;\n  for (auto &group : e.getErrors()) {\n    int i = 0;\n    for (auto &msg : group) {\n      auto t = msg.toString();\n      if (seen.find(t) != seen.end()) {\n        continue;\n      }\n      seen.insert(t);\n      MessageGroupPos pos = MessageGroupPos::NONE;\n      if (i == 0) {\n        pos = MessageGroupPos::HEAD;\n      } else if (i == group.size() - 1) {\n        pos = MessageGroupPos::LAST;\n      } else {\n        pos = MessageGroupPos::MID;\n      }\n      i++;\n      codon::compilationError(msg.getMessage(), msg.getFile(), msg.getLine(),\n                              msg.getColumn(), msg.getLength(), msg.getErrorCode(),\n                              /*terminate=*/false, pos);\n    }\n  }\n}\n\nvoid initLogFlags(const llvm::cl::opt<std::string> &log) {\n  codon::getLogger().parse(log);\n  if (auto *d = getenv(\"CODON_DEBUG\"))\n    codon::getLogger().parse(std::string(d));\n}\n\nenum BuildKind {\n  LLVM,\n  Bitcode,\n  Object,\n  Assembly,\n  Executable,\n  Library,\n  PyExtension,\n  Detect,\n  CIR\n};\nenum OptMode { Debug, Release };\nenum Numerics { C, Python };\n} // namespace\n\nint docMode(const std::vector<const char *> &args, const std::string &argv0) {\n  llvm::cl::opt<std::string> input(llvm::cl::Positional,\n                                   llvm::cl::desc(\"<input directory or file>\"),\n                                   llvm::cl::init(\"-\"));\n  llvm::cl::ParseCommandLineOptions(static_cast<int>(args.size()), args.data());\n  std::vector<std::string> files;\n  auto collectPaths = [&files](const std::string &path) {\n    llvm::sys::fs::file_status status;\n    llvm::sys::fs::status(path, status);\n    if (!llvm::sys::fs::exists(status)) {\n      codon::compilationError(fmt::format(\"'{}' does not exist\", path), \"\", 0, 0, 0, -1,\n                              false);\n    }\n    if (llvm::sys::fs::is_regular_file(status)) {\n      files.emplace_back(path);\n    } else if (llvm::sys::fs::is_directory(status)) {\n      std::error_code ec;\n      for (llvm::sys::fs::recursive_directory_iterator it(path, ec), e; it != e;\n           it.increment(ec)) {\n        auto status = it->status();\n        if (!status)\n          continue;\n        if (status->type() == llvm::sys::fs::file_type::regular_file)\n          if (!codon::ast::endswith(it->path(), \"__init_test__.codon\"))\n            files.emplace_back(it->path());\n      }\n    }\n  };\n\n  if (args.size() > 1)\n    collectPaths(args[1]);\n  auto compiler = std::make_unique<codon::Compiler>(args[0]);\n  bool failed = false;\n  std::sort(files.begin(), files.end());\n  auto result = compiler->docgen(files);\n  llvm::handleAllErrors(result.takeError(),\n                        [&failed](const codon::error::ParserErrorInfo &e) {\n                          display(e);\n                          failed = true;\n                        });\n  if (failed)\n    return EXIT_FAILURE;\n\n  fmt::print(\"{}\\n\", *result);\n  return EXIT_SUCCESS;\n}\n\nstd::unique_ptr<codon::Compiler> processSource(\n    const std::vector<const char *> &args, bool standalone,\n    std::function<bool()> pyExtension = [] { return false; }) {\n  llvm::cl::opt<std::string> input(llvm::cl::Positional, llvm::cl::desc(\"<input file>\"),\n                                   llvm::cl::init(\"-\"));\n  auto regs = llvm::cl::getRegisteredOptions();\n  llvm::cl::opt<OptMode> optMode(\n      llvm::cl::desc(\"optimization mode\"),\n      llvm::cl::values(\n          clEnumValN(Debug, regs.find(\"debug\") != regs.end() ? \"default\" : \"debug\",\n                     \"Turn off compiler optimizations and show backtraces\"),\n          clEnumValN(Release, \"release\",\n                     \"Turn on compiler optimizations and disable debug info\")),\n      llvm::cl::init(Debug));\n  llvm::cl::list<std::string> defines(\n      \"D\", llvm::cl::Prefix,\n      llvm::cl::desc(\"Add static variable definitions. The syntax is <name>=<value>\"));\n  llvm::cl::list<std::string> disabledOpts(\n      \"disable-opt\", llvm::cl::desc(\"Disable the specified IR optimization\"));\n  llvm::cl::list<std::string> plugins(\"plugin\",\n                                      llvm::cl::desc(\"Load specified plugin\"));\n  llvm::cl::opt<std::string> log(\"log\", llvm::cl::desc(\"Enable given log streams\"));\n  llvm::cl::opt<Numerics> numerics(\n      \"numerics\", llvm::cl::desc(\"numerical semantics\"),\n      llvm::cl::values(\n          clEnumValN(C, \"c\", \"C semantics: best performance but deviates from Python\"),\n          clEnumValN(Python, \"py\",\n                     \"Python semantics: mirrors Python but might disable optimizations \"\n                     \"like vectorization\")),\n      llvm::cl::init(C));\n\n  llvm::cl::ParseCommandLineOptions(args.size(), args.data());\n  initLogFlags(log);\n\n  std::unordered_map<std::string, std::string> defmap;\n  for (const auto &define : defines) {\n    auto eq = define.find('=');\n    if (eq == std::string::npos || !eq) {\n      codon::compilationWarning(\"ignoring malformed definition: \" + define);\n      continue;\n    }\n\n    auto name = define.substr(0, eq);\n    auto value = define.substr(eq + 1);\n\n    if (defmap.find(name) != defmap.end()) {\n      codon::compilationWarning(\"ignoring duplicate definition: \" + define);\n      continue;\n    }\n\n    defmap.emplace(name, value);\n  }\n\n  const bool isDebug = (optMode == OptMode::Debug);\n  std::vector<std::string> disabledOptsVec(disabledOpts);\n  auto compiler = std::make_unique<codon::Compiler>(\n      args[0], isDebug, disabledOptsVec,\n      /*isTest=*/false, (numerics == Numerics::Python), pyExtension());\n  compiler->getLLVMVisitor()->setStandalone(standalone);\n\n  // load plugins\n  for (const auto &plugin : plugins) {\n    bool failed = false;\n    llvm::handleAllErrors(\n        compiler->load(plugin), [&failed](const codon::error::PluginErrorInfo &e) {\n          codon::compilationError(e.getMessage(), /*file=*/\"\",\n                                  /*line=*/0, /*col=*/0, /*len=*/0, /*errorCode=*/-1,\n                                  /*terminate=*/false);\n          failed = true;\n        });\n    if (failed)\n      return {};\n  }\n\n  bool failed = false;\n  int testFlags = 0;\n  if (auto *tf = getenv(\"CODON_TEST_FLAGS\"))\n    testFlags = std::atoi(tf);\n  llvm::handleAllErrors(compiler->parseFile(input, /*testFlags=*/testFlags, defmap),\n                        [&failed](const codon::error::ParserErrorInfo &e) {\n                          display(e);\n                          failed = true;\n                        });\n  if (failed)\n    return {};\n\n  {\n    TIME(\"compile\");\n    llvm::cantFail(compiler->compile());\n  }\n  return compiler;\n}\n\nint runMode(const std::vector<const char *> &args) {\n  llvm::cl::list<std::string> libs(\n      \"l\", llvm::cl::desc(\"Load and link the specified library\"));\n  llvm::cl::list<std::string> progArgs(llvm::cl::ConsumeAfter,\n                                       llvm::cl::desc(\"<program arguments>...\"));\n  auto compiler = processSource(args, /*standalone=*/false);\n  if (!compiler)\n    return EXIT_FAILURE;\n  std::vector<std::string> libsVec(libs);\n  std::vector<std::string> argsVec(progArgs);\n  argsVec.insert(argsVec.begin(), compiler->getInput());\n  compiler->getLLVMVisitor()->run(argsVec, libsVec);\n  return EXIT_SUCCESS;\n}\n\nnamespace {\nstd::string jitExec(codon::jit::JIT *jit, const std::string &code) {\n  auto result = jit->execute(code);\n  if (auto err = result.takeError()) {\n    std::string output;\n    llvm::handleAllErrors(\n        std::move(err), [](const codon::error::ParserErrorInfo &e) { display(e); },\n        [&output](const codon::error::RuntimeErrorInfo &e) {\n          std::stringstream buf;\n          buf << e.getOutput();\n          buf << \"\\n\\033[1mBacktrace:\\033[0m\\n\";\n          for (const auto &line : e.getBacktrace()) {\n            buf << \"  \" << line << \"\\n\";\n          }\n          output = buf.str();\n        });\n    return output;\n  }\n  return *result;\n}\n\nvoid jitLoop(codon::jit::JIT *jit, std::istream &fp) {\n  std::string code;\n  for (std::string line; std::getline(fp, line);) {\n    if (line != \"#%%\") {\n      code += line + \"\\n\";\n    } else {\n      fmt::print(\"{}[done]\\n\", jitExec(jit, code));\n      code = \"\";\n      fflush(stdout);\n    }\n  }\n  if (!code.empty())\n    fmt::print(\"{}[done]\\n\", jitExec(jit, code));\n}\n} // namespace\n\nint jitMode(const std::vector<const char *> &args) {\n  llvm::cl::opt<std::string> input(llvm::cl::Positional, llvm::cl::desc(\"<input file>\"),\n                                   llvm::cl::init(\"-\"));\n  llvm::cl::list<std::string> plugins(\"plugin\",\n                                      llvm::cl::desc(\"Load specified plugin\"));\n  llvm::cl::opt<std::string> log(\"log\", llvm::cl::desc(\"Enable given log streams\"));\n  llvm::cl::ParseCommandLineOptions(args.size(), args.data());\n  initLogFlags(log);\n  codon::jit::JIT jit(args[0]);\n\n  // load plugins\n  for (const auto &plugin : plugins) {\n    bool failed = false;\n    llvm::handleAllErrors(jit.getCompiler()->load(plugin),\n                          [&failed](const codon::error::PluginErrorInfo &e) {\n                            codon::compilationError(e.getMessage(), /*file=*/\"\",\n                                                    /*line=*/0, /*col=*/0, /*len=*/0,\n                                                    /*errorCode=*/-1,\n                                                    /*terminate=*/false);\n                            failed = true;\n                          });\n    if (failed)\n      return EXIT_FAILURE;\n  }\n\n  llvm::cantFail(jit.init());\n  fmt::print(\">>> Codon JIT v{} <<<\\n\", CODON_VERSION);\n  if (input == \"-\") {\n    jitLoop(&jit, std::cin);\n  } else {\n    std::ifstream fileInput(input);\n    jitLoop(&jit, fileInput);\n  }\n  return EXIT_SUCCESS;\n}\n\nint buildMode(const std::vector<const char *> &args, const std::string &argv0) {\n  llvm::cl::list<std::string> libs(\n      \"l\", llvm::cl::desc(\"Link the specified library (only for executables)\"));\n  llvm::cl::opt<std::string> lflags(\"linker-flags\",\n                                    llvm::cl::desc(\"Pass given flags to linker\"));\n  llvm::cl::opt<BuildKind> buildKind(\n      llvm::cl::desc(\"output type\"),\n      llvm::cl::values(\n          clEnumValN(LLVM, \"llvm\", \"Generate LLVM IR\"),\n          clEnumValN(Bitcode, \"bc\", \"Generate LLVM bitcode\"),\n          clEnumValN(Object, \"obj\", \"Generate native object file\"),\n          clEnumValN(Assembly, \"asm\", \"Generate assembly code\"),\n          clEnumValN(Executable, \"exe\", \"Generate executable\"),\n          clEnumValN(Library, \"lib\", \"Generate shared library\"),\n          clEnumValN(PyExtension, \"pyext\", \"Generate Python extension module\"),\n          clEnumValN(CIR, \"cir\", \"Generate Codon Intermediate Representation\"),\n          clEnumValN(Detect, \"detect\",\n                     \"Detect output type based on output file extension\")),\n      llvm::cl::init(Detect));\n  llvm::cl::opt<std::string> output(\n      \"o\",\n      llvm::cl::desc(\n          \"Write compiled output to specified file. Supported extensions: \"\n          \"none (executable), .o (object file), .ll (LLVM IR), .bc (LLVM bitcode)\"));\n  llvm::cl::opt<std::string> pyModule(\n      \"module\", llvm::cl::desc(\"Python extension module name (only applicable when \"\n                               \"building Python extension module)\"));\n\n  auto compiler = processSource(args, /*standalone=*/true,\n                                [&] { return buildKind == BuildKind::PyExtension; });\n  if (!compiler)\n    return EXIT_FAILURE;\n  std::vector<std::string> libsVec(libs);\n\n  if (output.empty() && compiler->getInput() == \"-\")\n    codon::compilationError(\"output file must be specified when reading from stdin\");\n  std::string extension;\n  switch (buildKind) {\n  case BuildKind::LLVM:\n    extension = \".ll\";\n    break;\n  case BuildKind::Bitcode:\n    extension = \".bc\";\n    break;\n  case BuildKind::Object:\n  case BuildKind::PyExtension:\n    extension = \".o\";\n    break;\n  case BuildKind::Assembly:\n    extension = \".s\";\n    break;\n  case BuildKind::Library:\n    extension = isMacOS() ? \".dylib\" : \".so\";\n    break;\n  case BuildKind::Executable:\n  case BuildKind::Detect:\n    extension = \"\";\n    break;\n  case BuildKind::CIR:\n    extension = \".cir\";\n    break;\n  default:\n    seqassertn(0, \"unknown build kind\");\n  }\n  const std::string filename =\n      output.empty() ? makeOutputFilename(compiler->getInput(), extension) : output;\n  switch (buildKind) {\n  case BuildKind::LLVM:\n    compiler->getLLVMVisitor()->writeToLLFile(filename);\n    break;\n  case BuildKind::Bitcode:\n    compiler->getLLVMVisitor()->writeToBitcodeFile(filename);\n    break;\n  case BuildKind::Object:\n    compiler->getLLVMVisitor()->writeToObjectFile(filename);\n    break;\n  case BuildKind::Assembly:\n    compiler->getLLVMVisitor()->writeToObjectFile(filename, /*pic=*/false,\n                                                  /*assembly=*/true);\n    break;\n  case BuildKind::Executable:\n    compiler->getLLVMVisitor()->writeToExecutable(filename, argv0, false, libsVec,\n                                                  lflags);\n    break;\n  case BuildKind::Library:\n    compiler->getLLVMVisitor()->writeToExecutable(filename, argv0, true, libsVec,\n                                                  lflags);\n    break;\n  case BuildKind::PyExtension:\n    compiler->getCache()->pyModule->name =\n        pyModule.empty() ? llvm::sys::path::stem(compiler->getInput()).str() : pyModule;\n    compiler->getLLVMVisitor()->writeToPythonExtension(*compiler->getCache()->pyModule,\n                                                       filename);\n    break;\n  case BuildKind::CIR: {\n    std::ofstream out(filename);\n    codon::ir::util::format(out, compiler->getModule());\n    break;\n  }\n  case BuildKind::Detect:\n    compiler->getLLVMVisitor()->compile(filename, argv0, libsVec, lflags);\n    break;\n  default:\n    seqassertn(0, \"unknown build kind\");\n  }\n\n  return EXIT_SUCCESS;\n}\n\nint jupyterMode(const std::vector<const char *> &args) {\n  llvm::cl::list<std::string> plugins(\"plugin\",\n                                      llvm::cl::desc(\"Load specified plugin\"));\n  llvm::cl::opt<std::string> input(llvm::cl::Positional,\n                                   llvm::cl::desc(\"<connection file>\"),\n                                   llvm::cl::init(\"connection.json\"));\n  llvm::cl::ParseCommandLineOptions(args.size(), args.data());\n  int code = codon::startJupyterKernel(args[0], plugins, input);\n  return code;\n}\n\nvoid showCommandsAndExit() {\n  codon::compilationError(\"Available commands: codon <run|build|doc>\");\n}\n\nint otherMode(const std::vector<const char *> &args) {\n  llvm::cl::opt<std::string> input(llvm::cl::Positional, llvm::cl::desc(\"<mode>\"));\n  llvm::cl::extrahelp(\"\\nMODES:\\n\\n\"\n                      \"  run   - run a program interactively\\n\"\n                      \"  build - build a program\\n\"\n                      \"  doc   - generate program documentation\\n\");\n  llvm::cl::ParseCommandLineOptions(args.size(), args.data());\n\n  if (!input.empty())\n    showCommandsAndExit();\n  return EXIT_SUCCESS;\n}\n\nint main(int argc, const char **argv) {\n  if (argc < 2)\n    showCommandsAndExit();\n\n  llvm::cl::SetVersionPrinter(versMsg);\n  std::vector<const char *> args{argv[0]};\n  for (int i = 2; i < argc; i++)\n    args.push_back(argv[i]);\n\n  std::string mode(argv[1]);\n  std::string argv0 = std::string(args[0]) + \" \" + mode;\n  if (mode == \"run\") {\n    args[0] = argv0.data();\n    return runMode(args);\n  }\n  if (mode == \"build\") {\n    const char *oldArgv0 = args[0];\n    args[0] = argv0.data();\n    return buildMode(args, oldArgv0);\n  }\n  if (mode == \"doc\") {\n    const char *oldArgv0 = args[0];\n    args[0] = argv0.data();\n    return docMode(args, oldArgv0);\n  }\n  if (mode == \"jit\") {\n    args[0] = argv0.data();\n    return jitMode(args);\n  }\n  if (mode == \"jupyter\") {\n    args[0] = argv0.data();\n    return jupyterMode(args);\n  }\n  return otherMode({argv, argv + argc});\n}\n"
  },
  {
    "path": "codon/cir/analyze/analysis.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"analysis.h\"\n\n#include \"codon/cir/transform/manager.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace analyze {\n\nResult *Analysis::doGetAnalysis(const std::string &key) {\n  return manager ? manager->getAnalysisResult(key) : nullptr;\n}\n\n} // namespace analyze\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/analyze/analysis.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n\n#include \"codon/cir/module.h\"\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace analyze {\n\n/// Analysis result base struct.\nstruct Result {\n  virtual ~Result() noexcept = default;\n};\n\n/// Base class for IR analyses.\nclass Analysis {\nprivate:\n  transform::PassManager *manager = nullptr;\n\npublic:\n  virtual ~Analysis() noexcept = default;\n\n  /// @return a unique key for this pass\n  virtual std::string getKey() const = 0;\n\n  /// Execute the analysis.\n  /// @param module the module\n  virtual std::unique_ptr<Result> run(const Module *module) = 0;\n\n  /// Sets the manager.\n  /// @param mng the new manager\n  void setManager(transform::PassManager *mng) { manager = mng; }\n  /// Returns the result of a given analysis.\n  /// @param key the analysis key\n  template <typename AnalysisType>\n  AnalysisType *getAnalysisResult(const std::string &key) {\n    return static_cast<AnalysisType *>(doGetAnalysis(key));\n  }\n\nprivate:\n  analyze::Result *doGetAnalysis(const std::string &key);\n};\n\n} // namespace analyze\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/analyze/dataflow/capture.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"capture.h\"\n\n#include <algorithm>\n#include <iterator>\n#include <utility>\n\n#include \"codon/cir/analyze/dataflow/reaching.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/cir/util/side_effect.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace analyze {\nnamespace dataflow {\nnamespace {\n\ntemplate <typename S, typename T> bool contains(const S &x, T i) {\n  for (auto a : x) {\n    if (a == i)\n      return true;\n  }\n  return false;\n}\n\ntemplate <typename S, typename T> bool containsId(const S &x, T i) {\n  for (auto a : x) {\n    if (a->getId() == i->getId())\n      return true;\n  }\n  return false;\n}\n\ntemplate <typename T> bool shouldTrack(const T *x) {\n  // We only care about things with pointers,\n  // since you can't capture primitive types\n  // like int, float, etc.\n  return x && !x->getType()->isAtomic();\n}\n\ntemplate <> bool shouldTrack(const types::Type *x) { return x && !x->isAtomic(); }\n\nstruct CaptureContext;\n\nbool extractVars(CaptureContext &cc, const Value *v, std::vector<const Var *> &result);\n\nbool reachable(CFBlock *start, CFBlock *end, std::unordered_set<CFBlock *> &seen) {\n  if (start == end)\n    return true;\n\n  if (seen.count(start))\n    return false;\n\n  seen.insert(start);\n  for (auto it = start->successors_begin(); it != start->successors_end(); ++it) {\n    if (reachable(*it, end, seen))\n      return true;\n  }\n\n  return false;\n}\n\n// Check if one value must always be encountered before another, if\n// it is to be encountered at all. This is NOT the same as domination\n// since we can have \"(if _: B) ; A\", where B does not dominate A yet\n// must always occur before A is it does occur.\nbool happensBefore(const Value *before, const Value *after, CFGraph *cfg,\n                   DominatorInspector *dom) {\n  auto *beforeBlock = cfg->getBlock(before);\n  auto *afterBlock = cfg->getBlock(after);\n\n  if (!beforeBlock || !afterBlock)\n    return false;\n\n  // If values are in the same block we just need to see\n  // which one shows up first.\n  if (beforeBlock == afterBlock) {\n    for (auto *val : *beforeBlock) {\n      if (val->getId() == before->getId())\n        return true;\n      else if (val->getId() == after->getId())\n        return false;\n    }\n    seqassertn(false, \"could not find values in CFG block\");\n    return false;\n  }\n\n  // If we have different blocks, then either 'before' dominates\n  // 'after', in which case the answer is true, or there must be\n  // no paths from 'afterBlock' to 'beforeBlock'.\n  std::unordered_set<CFBlock *> seen;\n  return dom->isDominated(after, before) || !reachable(afterBlock, beforeBlock, seen);\n}\n\nstruct RDManager {\n  struct IDPairHash {\n    template <class T1, class T2>\n    std::size_t operator()(const std::pair<T1, T2> &pair) const {\n      return (std::hash<T1>()(pair.first) << 32) ^ std::hash<T2>()(pair.second);\n    }\n  };\n\n  RDInspector *rd;\n  std::unordered_map<std::pair<id_t, id_t>, std::unordered_set<id_t>, IDPairHash> cache;\n\n  explicit RDManager(RDInspector *rd) : rd(rd), cache() {}\n\n  std::unordered_set<id_t> getReachingDefinitions(const Var *var, const Value *loc) {\n    auto key = std::make_pair(var->getId(), loc->getId());\n    auto it = cache.find(key);\n    if (it == cache.end()) {\n      auto defs = rd->getReachingDefinitions(var, loc);\n      std::unordered_set<id_t> dset;\n      for (auto &def : defs) {\n        dset.insert(def.assignment->getId());\n      }\n      cache.emplace(key, dset);\n      return dset;\n    } else {\n      return it->second;\n    }\n  }\n\n  bool isInvalid(const Var *v) { return rd->isInvalid(v); }\n};\n\nstruct DerivedSet {\n  const Func *func;\n  const Var *root;\n  std::vector<id_t> args;\n  std::unordered_set<id_t> derivedVals;\n  std::unordered_map<id_t, std::vector<const Value *>> derivedVars;\n  CaptureInfo result;\n\n  void setReturnCaptured() {\n    if (shouldTrack(util::getReturnType(func)))\n      result.returnCaptures = true;\n  }\n\n  void setExternCaptured() {\n    setReturnCaptured();\n    result.externCaptures = true;\n  }\n\n  bool isDerived(const Var *v, const Value *loc, RDManager &rd) const {\n    auto it = derivedVars.find(v->getId());\n    if (it == derivedVars.end())\n      return false;\n\n    // We assume global references are always derived\n    // if the var is derived, since they can change\n    // at any point as far as we know. Same goes for\n    // vars untracked by the reaching-def analysis.\n    if (v->isGlobal() || rd.isInvalid(v))\n      return true;\n\n    // Make sure the var at this point is reached by\n    // at least one definition that has led to a\n    // derived value.\n    auto mySet = rd.getReachingDefinitions(v, loc);\n    for (auto *cause : it->second) {\n      auto otherSet = rd.getReachingDefinitions(v, cause);\n      for (auto &elem : mySet) {\n        if (otherSet.count(elem))\n          return true;\n      }\n    }\n\n    return false;\n  }\n\n  bool isDerived(const Value *v) const {\n    return derivedVals.find(v->getId()) != derivedVals.end();\n  }\n\n  void setDerived(const Var *v, const Value *cause, bool shouldArgCapture = true) {\n    if (!shouldTrack(v))\n      return;\n\n    if (v->isGlobal())\n      setExternCaptured();\n\n    auto id = v->getId();\n    if (shouldArgCapture && root && id != root->getId()) {\n      for (unsigned i = 0; i < args.size(); i++) {\n        if (args[i] == id && !contains(result.argCaptures, i))\n          result.argCaptures.push_back(i);\n      }\n    }\n\n    auto it = derivedVars.find(id);\n    if (it == derivedVars.end()) {\n      std::vector<const Value *> info = {cause};\n      derivedVars.emplace(id, info);\n    } else {\n      if (!containsId(it->second, cause))\n        it->second.push_back(cause);\n    }\n  }\n\n  void setDerived(const Value *v) {\n    if (!shouldTrack(v))\n      return;\n\n    derivedVals.insert(v->getId());\n  }\n\n  unsigned size() const {\n    unsigned total = derivedVals.size();\n    for (auto &e : derivedVars) {\n      total += e.second.size();\n    }\n    return total;\n  }\n\n  explicit DerivedSet(const Func *func, const Var *root = nullptr)\n      : func(func), root(root), args(), derivedVals(), derivedVars(), result() {}\n\n  // Set for function argument\n  DerivedSet(const Func *func, const Var *root, const Value *cause)\n      : DerivedSet(func, root) {\n    // extract arguments\n    for (auto it = func->arg_begin(); it != func->arg_end(); ++it) {\n      args.push_back((*it)->getId());\n    }\n\n    setDerived(root, cause);\n  }\n\n  // Set for function argument\n  DerivedSet(const Func *func, const Value *value, CaptureContext &cc)\n      : DerivedSet(func) {\n    std::vector<const Var *> vars;\n    bool escapes = extractVars(cc, value, vars);\n    if (escapes)\n      setExternCaptured();\n\n    setDerived(value);\n    for (auto *var : vars) {\n      setDerived(var, value);\n    }\n  }\n};\n\nbool noCaptureByAnnotation(const Func *func) {\n  return util::hasAttribute(func, util::PURE_ATTR) ||\n         util::hasAttribute(func, util::NO_SIDE_EFFECT_ATTR) ||\n         util::hasAttribute(func, util::NO_CAPTURE_ATTR);\n}\n\nstd::vector<CaptureInfo> makeAllCaptureInfo(const Func *func) {\n  std::vector<CaptureInfo> result;\n  for (auto it = func->arg_begin(); it != func->arg_end(); ++it) {\n    result.push_back(CaptureInfo::unknown(func, (*it)->getType()));\n  }\n  return result;\n}\n\nstd::vector<CaptureInfo> makeNoCaptureInfo(const Func *func, bool derives) {\n  std::vector<CaptureInfo> result;\n  for (auto it = func->arg_begin(); it != func->arg_end(); ++it) {\n    auto info = CaptureInfo::nothing();\n    if (derives && shouldTrack(*it))\n      info.returnCaptures = true;\n    result.push_back(info);\n  }\n  return result;\n}\n\nstruct CaptureContext {\n  RDResult *reaching;\n  DominatorResult *dominating;\n  std::unordered_map<id_t, std::vector<CaptureInfo>> results;\n\n  CaptureContext(RDResult *reaching, DominatorResult *dominating)\n      : reaching(reaching), dominating(dominating), results() {}\n\n  std::vector<CaptureInfo> get(const Func *func);\n  void set(const Func *func, const std::vector<CaptureInfo> &result);\n\n  CFGraph *getCFGraph(const Func *func) {\n    auto it = reaching->cfgResult->graphs.find(func->getId());\n    seqassertn(it != reaching->cfgResult->graphs.end(),\n               \"could not find function in CFG results\");\n    return it->second.get();\n  }\n\n  RDInspector *getRDInspector(const Func *func) {\n    auto it = reaching->results.find(func->getId());\n    seqassertn(it != reaching->results.end(),\n               \"could not find function in reaching-definitions results\");\n    return it->second.get();\n  }\n\n  DominatorInspector *getDomInspector(const Func *func) {\n    auto it = dominating->results.find(func->getId());\n    seqassertn(it != dominating->results.end(),\n               \"could not find function in dominator results\");\n    return it->second.get();\n  }\n};\n\n// This visitor answers the questions of what vars are\n// relevant to track in a capturing expression. For\n// example, in \"a[i] = x\", the expression \"a[i]\" captures\n// \"x\"; in this case we need to track \"a\" but the variable\n// \"i\" (typically) we would not care about.\nstruct ExtractVars : public util::ConstVisitor {\n  CaptureContext &cc;\n  std::unordered_set<id_t> vars;\n  bool escapes;\n\n  explicit ExtractVars(CaptureContext &cc)\n      : util::ConstVisitor(), cc(cc), vars(), escapes(false) {}\n\n  template <typename Node> void process(const Node *v) { v->accept(*this); }\n\n  void add(const Var *v) {\n    if (shouldTrack(v))\n      vars.insert(v->getId());\n  }\n\n  void defaultVisit(const Node *) override {}\n\n  void visit(const VarValue *v) override { add(v->getVar()); }\n\n  void visit(const PointerValue *v) override { add(v->getVar()); }\n\n  void visit(const CallInstr *v) override {\n    if (auto *func = util::getFunc(v->getCallee())) {\n      auto capInfo = cc.get(util::getFunc(v->getCallee()));\n      unsigned i = 0;\n      for (auto *arg : *v) {\n        // note possibly capInfo.size() != v->numArgs() if calling vararg C function\n        auto info = (i < capInfo.size()) ? capInfo[i]\n                                         : CaptureInfo::unknown(func, arg->getType());\n        if (shouldTrack(arg) && capInfo[i].returnCaptures)\n          process(arg);\n        ++i;\n      }\n    } else {\n      for (auto *arg : *v) {\n        if (shouldTrack(arg))\n          process(arg);\n      }\n    }\n  }\n\n  void visit(const YieldInInstr *v) override {\n    // We have no idea what the yield-in\n    // value could be, so just assume we\n    // escape in this case.\n    escapes = true;\n  }\n\n  void visit(const TernaryInstr *v) override {\n    process(v->getTrueValue());\n    process(v->getFalseValue());\n  }\n\n  void visit(const ExtractInstr *v) override { process(v->getVal()); }\n\n  void visit(const FlowInstr *v) override { process(v->getValue()); }\n\n  void visit(const dsl::CustomInstr *v) override {\n    // TODO\n  }\n};\n\nbool extractVars(CaptureContext &cc, const Value *v, std::vector<const Var *> &result) {\n  auto *M = v->getModule();\n  ExtractVars ev(cc);\n  v->accept(ev);\n  for (auto id : ev.vars) {\n    result.push_back(M->getVar(id));\n  }\n  return ev.escapes;\n}\n\nstruct CaptureTracker : public util::Operator {\n  CaptureContext &cc;\n  CFGraph *cfg;\n  RDManager rd;\n  DominatorInspector *dom;\n  std::vector<DerivedSet> dsets;\n\n  CaptureTracker(CaptureContext &cc, const Func *func, bool isArg)\n      : Operator(), cc(cc), cfg(cc.getCFGraph(func)), rd(cc.getRDInspector(func)),\n        dom(cc.getDomInspector(func)), dsets() {}\n\n  CaptureTracker(CaptureContext &cc, const BodiedFunc *func)\n      : CaptureTracker(cc, func, /*isArg=*/true) {\n    // find synthetic assignments in CFG for argument vars\n    auto *entry = cfg->getEntryBlock();\n    std::unordered_map<id_t, const SyntheticAssignInstr *> synthAssigns;\n\n    for (auto *v : *entry) {\n      if (auto *synth = cast<SyntheticAssignInstr>(v)) {\n        if (shouldTrack(synth->getLhs()))\n          synthAssigns[synth->getLhs()->getId()] = synth;\n      }\n    }\n\n    // extract arguments\n    std::vector<id_t> args;\n    for (auto it = func->arg_begin(); it != func->arg_end(); ++it) {\n      args.push_back((*it)->getId());\n    }\n\n    // make a derived set for each function argument\n    for (auto it = func->arg_begin(); it != func->arg_end(); ++it) {\n      if (!shouldTrack(*it))\n        continue;\n\n      auto it2 = synthAssigns.find((*it)->getId());\n      seqassertn(it2 != synthAssigns.end(),\n                 \"could not find synthetic assignment for arg var\");\n      dsets.push_back(DerivedSet(func, *it, it2->second));\n    }\n  }\n\n  CaptureTracker(CaptureContext &cc, const BodiedFunc *func, const Value *value)\n      : CaptureTracker(cc, func, /*isArg=*/false) {\n    dsets.push_back(DerivedSet(func, value, cc));\n  }\n\n  unsigned size() const {\n    unsigned total = 0;\n    for (auto &dset : dsets) {\n      total += dset.size();\n    }\n    return total;\n  }\n\n  void forEachDSetOf(Value *v, std::function<void(DerivedSet &)> func) {\n    if (!v)\n      return;\n\n    for (auto &dset : dsets) {\n      if (dset.isDerived(v))\n        func(dset);\n    }\n  }\n\n  void forEachDSetOf(Var *v, Value *loc, std::function<void(DerivedSet &)> func) {\n    if (!v)\n      return;\n\n    for (auto &dset : dsets) {\n      if (dset.isDerived(v, loc, rd))\n        func(dset);\n    }\n  }\n\n  void forwardLink(Value *from, Value *cause, const std::vector<const Var *> &toVars,\n                   bool toEscapes, bool shouldArgCapture) {\n    forEachDSetOf(from, [&](DerivedSet &dset) {\n      if (toEscapes)\n        dset.setExternCaptured();\n\n      for (auto *toVar : toVars) {\n        dset.setDerived(toVar, cause, shouldArgCapture);\n      }\n    });\n  }\n\n  void backwardLinkFunc(DerivedSet &dset, Value *cause,\n                        const std::vector<const Var *> &toVars,\n                        const std::vector<const Var *> &fromVars, bool fromEscapes) {\n    if (fromEscapes)\n      dset.setExternCaptured();\n\n    for (auto *toVar : toVars) {\n      auto it = dset.derivedVars.find(toVar->getId());\n      if (it == dset.derivedVars.end())\n        continue;\n      auto &toCauses = it->second;\n\n      for (auto *toCause : toCauses) {\n        if (isA<AssignInstr>(toCause) || isA<SyntheticAssignInstr>(toCause) ||\n            happensBefore(toCause, cause, cfg, dom))\n          continue;\n\n        bool derived = false;\n        if (toVar->isGlobal() || rd.isInvalid(toVar)) {\n          derived = true;\n        } else {\n          auto mySet = rd.getReachingDefinitions(toVar, cause);\n          auto otherSet = rd.getReachingDefinitions(toVar, toCause);\n          for (auto &elem : mySet) {\n            if (otherSet.count(elem)) {\n              derived = true;\n              break;\n            }\n          }\n        }\n\n        if (derived) {\n          for (auto *fromVar : fromVars) {\n            dset.setDerived(fromVar, toCause);\n          }\n        }\n      }\n    }\n  }\n\n  void link(Value *from, Value *to, Value *cause) {\n    std::vector<const Var *> fromVars, toVars;\n    bool fromEscapes = extractVars(cc, from, fromVars);\n    bool toEscapes = extractVars(cc, to, toVars);\n\n    forwardLink(from, cause, toVars, toEscapes, /*shouldArgCapture=*/true);\n    forEachDSetOf(to, [&](DerivedSet &dset) {\n      backwardLinkFunc(dset, cause, toVars, fromVars, fromEscapes);\n    });\n  }\n\n  void link(Value *from, Var *to, Value *cause) {\n    std::vector<const Var *> fromVars, toVars = {to};\n    bool fromEscapes = extractVars(cc, from, fromVars);\n    bool toEscapes = false;\n\n    forwardLink(from, cause, toVars, toEscapes, /*shouldArgCapture=*/false);\n    forEachDSetOf(to, cause, [&](DerivedSet &dset) {\n      backwardLinkFunc(dset, cause, toVars, fromVars, fromEscapes);\n    });\n  }\n\n  void handle(VarValue *v) override {\n    forEachDSetOf(v->getVar(), v, [&](DerivedSet &dset) { dset.setDerived(v); });\n  }\n\n  void handle(PointerValue *v) override {\n    forEachDSetOf(v->getVar(), v, [&](DerivedSet &dset) { dset.setDerived(v); });\n  }\n\n  void handle(AssignInstr *v) override { link(v->getRhs(), v->getLhs(), v); }\n\n  void handle(ExtractInstr *v) override {\n    if (!shouldTrack(v))\n      return;\n\n    forEachDSetOf(v->getVal(), [&](DerivedSet &dset) { dset.setDerived(v); });\n  }\n\n  void handle(InsertInstr *v) override {\n    link(v->getRhs(), v->getLhs(), v);\n    forEachDSetOf(v->getLhs(), [&](DerivedSet &dset) { dset.result.modified = true; });\n  }\n\n  void handle(CallInstr *v) override {\n    std::vector<Value *> args(v->begin(), v->end());\n    std::vector<CaptureInfo> capInfo;\n    auto *func = util::getFunc(v->getCallee());\n\n    if (func) {\n      capInfo = cc.get(func);\n    } else {\n      std::vector<unsigned> argCaptures;\n      unsigned i = 0;\n      for (auto *arg : args) {\n        if (shouldTrack(arg))\n          argCaptures.push_back(i);\n        ++i;\n      }\n\n      const bool returnCaptures = shouldTrack(v);\n      for (auto *arg : args) {\n        CaptureInfo info = CaptureInfo::nothing();\n        if (shouldTrack(arg)) {\n          info.argCaptures = argCaptures;\n          info.returnCaptures = returnCaptures;\n          info.externCaptures = true;\n          info.modified = true;\n        }\n        capInfo.push_back(info);\n      }\n    }\n\n    unsigned i = 0;\n    for (auto *arg : args) {\n      // note possibly capInfo.size() != v->numArgs() if calling vararg C function\n      auto info = (i < capInfo.size()) ? capInfo[i]\n                                       : CaptureInfo::unknown(func, arg->getType());\n      for (auto argno : info.argCaptures) {\n        Value *other = args[argno];\n        link(arg, other, v);\n      }\n\n      forEachDSetOf(arg, [&](DerivedSet &dset) {\n        // Check if the return value captures.\n        if (info.returnCaptures)\n          dset.setDerived(v);\n\n        // Check if we're externally captured.\n        if (info.externCaptures)\n          dset.setExternCaptured();\n\n        if (info.modified)\n          dset.result.modified = true;\n      });\n      ++i;\n    }\n  }\n\n  void handle(ForFlow *v) override {\n    auto *var = v->getVar();\n    if (!shouldTrack(var))\n      return;\n\n    forEachDSetOf(v->getIter(), [&](DerivedSet &dset) {\n      bool found = false;\n      for (auto it = cfg->synth_begin(); it != cfg->synth_end(); ++it) {\n        if (auto *synth = cast<SyntheticAssignInstr>(*it)) {\n          if (synth->getKind() == SyntheticAssignInstr::Kind::NEXT_VALUE &&\n              synth->getLhs()->getId() == var->getId()) {\n            seqassertn(!found, \"found multiple synthetic assignments for loop var\");\n            dset.setDerived(var, synth);\n            found = true;\n          }\n        }\n      }\n    });\n  }\n\n  void handle(TernaryInstr *v) override {\n    forEachDSetOf(v->getTrueValue(), [&](DerivedSet &dset) { dset.setDerived(v); });\n    forEachDSetOf(v->getFalseValue(), [&](DerivedSet &dset) { dset.setDerived(v); });\n  }\n\n  void handle(FlowInstr *v) override {\n    forEachDSetOf(v->getValue(), [&](DerivedSet &dset) { dset.setDerived(v); });\n  }\n\n  void handle(dsl::CustomInstr *v) override {\n    // TODO\n  }\n\n  // Actual capture points:\n\n  void handle(ReturnInstr *v) override {\n    forEachDSetOf(v->getValue(),\n                  [&](DerivedSet &dset) { dset.result.returnCaptures = true; });\n  }\n\n  void handle(YieldInstr *v) override {\n    forEachDSetOf(v->getValue(),\n                  [&](DerivedSet &dset) { dset.result.returnCaptures = true; });\n  }\n\n  void handle(AwaitInstr *v) override {\n    forEachDSetOf(v->getValue(),\n                  [&](DerivedSet &dset) { dset.result.returnCaptures = true; });\n  }\n\n  void handle(ThrowInstr *v) override {\n    forEachDSetOf(v->getValue(), [&](DerivedSet &dset) { dset.setExternCaptured(); });\n  }\n\n  // Helper to run to completion\n\n  void runToCompletion(const Func *func) {\n    unsigned oldSize = 0;\n    do {\n      oldSize = size();\n      const_cast<Func *>(func)->accept(*this);\n      reset();\n    } while (size() != oldSize);\n  }\n};\n\nstd::vector<CaptureInfo> CaptureContext::get(const Func *func) {\n  // Don't know anything about external/LLVM funcs so use annotations.\n  if (isA<ExternalFunc>(func) || isA<LLVMFunc>(func)) {\n    bool derives = util::hasAttribute(func, util::DERIVES_ATTR);\n\n    if (util::hasAttribute(func, util::SELF_CAPTURES_ATTR)) {\n      auto ans = makeNoCaptureInfo(func, derives);\n      if (!ans.empty())\n        ans[0].modified = true;\n\n      std::vector<Var *> argVars(func->arg_begin(), func->arg_end());\n      for (unsigned i = 1; i < ans.size(); i++) {\n        if (shouldTrack(argVars[i]))\n          ans[i].argCaptures.push_back(0);\n      }\n      return ans;\n    }\n\n    return noCaptureByAnnotation(func) ? makeNoCaptureInfo(func, derives)\n                                       : makeAllCaptureInfo(func);\n  }\n\n  // Only Tuple.__new__(...) and Generator.__promise__(self) capture.\n  if (isA<InternalFunc>(func)) {\n    bool isTupleNew = func->getUnmangledName() == \"__new__\" &&\n                      isA<types::RecordType>(util::getReturnType(func));\n    bool isPromise = func->getUnmangledName() == \"__promise__\" &&\n                     std::distance(func->arg_begin(), func->arg_end()) == 1 &&\n                     isA<types::GeneratorType>(func->arg_front()->getType());\n\n    bool derives = (isTupleNew || isPromise);\n    return makeNoCaptureInfo(func, derives);\n  }\n\n  // Bodied function\n  if (isA<BodiedFunc>(func)) {\n    auto it = results.find(func->getId());\n    if (it != results.end())\n      return it->second;\n\n    set(func, makeAllCaptureInfo(func));\n\n    CaptureTracker ct(*this, cast<BodiedFunc>(func));\n    ct.runToCompletion(func);\n\n    std::vector<CaptureInfo> answer;\n    unsigned i = 0;\n    for (auto it = func->arg_begin(); it != func->arg_end(); ++it) {\n      if (shouldTrack(*it)) {\n        answer.push_back(ct.dsets[i++].result);\n      } else {\n        answer.push_back(CaptureInfo::nothing());\n      }\n    }\n\n    set(func, answer);\n    return answer;\n  }\n\n  seqassertn(false, \"unknown function type\");\n  return {};\n}\n\nvoid CaptureContext::set(const Func *func, const std::vector<CaptureInfo> &result) {\n  results[func->getId()] = result;\n}\n\n} // namespace\n\nCaptureInfo CaptureInfo::unknown(const Func *func, types::Type *type) {\n  if (!shouldTrack(type))\n    return CaptureInfo::nothing();\n\n  CaptureInfo c;\n  unsigned i = 0;\n  for (auto it = func->arg_begin(); it != func->arg_end(); ++it) {\n    if (shouldTrack(*it))\n      c.argCaptures.push_back(i);\n    ++i;\n  }\n  c.returnCaptures = shouldTrack(util::getReturnType(func));\n  c.externCaptures = true;\n  c.modified = true;\n  return c;\n}\n\nconst std::string CaptureAnalysis::KEY = \"core-analyses-capture\";\n\nstd::unique_ptr<Result> CaptureAnalysis::run(const Module *m) {\n  auto res = std::make_unique<CaptureResult>();\n  auto *rdResult = getAnalysisResult<RDResult>(rdAnalysisKey);\n  auto *domResult = getAnalysisResult<DominatorResult>(domAnalysisKey);\n  res->rdResult = rdResult;\n  res->domResult = domResult;\n  CaptureContext cc(rdResult, domResult);\n\n  if (const auto *main = cast<BodiedFunc>(m->getMainFunc())) {\n    auto ans = cc.get(main);\n    res->results.emplace(main->getId(), ans);\n  }\n\n  for (const auto *var : *m) {\n    if (const auto *f = cast<Func>(var)) {\n      auto ans = cc.get(f);\n      res->results.emplace(f->getId(), ans);\n    }\n  }\n\n  return res;\n}\n\nCaptureInfo escapes(const BodiedFunc *func, const Value *value, CaptureResult *cr) {\n  if (!shouldTrack(value))\n    return CaptureInfo::nothing();\n\n  CaptureContext cc(cr->rdResult, cr->domResult);\n  cc.results = cr->results;\n  CaptureTracker ct(cc, cast<BodiedFunc>(func), value);\n  ct.runToCompletion(func);\n  seqassertn(ct.dsets.size() == 1, \"unexpected dsets size\");\n  return ct.dsets[0].result;\n}\n\n} // namespace dataflow\n} // namespace analyze\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/analyze/dataflow/capture.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <unordered_map>\n#include <vector>\n\n#include \"codon/cir/analyze/analysis.h\"\n#include \"codon/cir/analyze/dataflow/dominator.h\"\n#include \"codon/cir/analyze/dataflow/reaching.h\"\n#include \"codon/cir/cir.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace analyze {\nnamespace dataflow {\n\n/// Information about how a function argument is captured.\nstruct CaptureInfo {\n  /// vector of other argument indices capturing this one\n  std::vector<unsigned> argCaptures;\n  /// true if the return value of the function captures this argument\n  bool returnCaptures = false;\n  /// true if this argument is externally captured e.g. by assignment to global\n  bool externCaptures = false;\n  /// true if this argument is modified\n  bool modified = false;\n\n  /// @return true if anything captures\n  operator bool() const {\n    return !argCaptures.empty() || returnCaptures || externCaptures;\n  }\n\n  /// Returns an instance denoting no captures.\n  /// @return an instance denoting no captures\n  static CaptureInfo nothing() { return {}; }\n\n  /// Returns an instance denoting unknown capture status.\n  /// @param func the function containing this argument\n  /// @param type the argument's type\n  /// @return an instance denoting unknown capture status\n  static CaptureInfo unknown(const Func *func, types::Type *type);\n};\n\n/// Capture analysis result.\nstruct CaptureResult : public Result {\n  /// the corresponding reaching definitions result\n  RDResult *rdResult = nullptr;\n\n  /// the corresponding dominator result\n  DominatorResult *domResult = nullptr;\n\n  /// map from function id to capture information, where\n  /// each element of the value vector corresponds to an\n  /// argument of the function\n  std::unordered_map<id_t, std::vector<CaptureInfo>> results;\n};\n\n/// Capture analysis that runs on all functions.\nclass CaptureAnalysis : public Analysis {\nprivate:\n  /// the reaching definitions analysis key\n  std::string rdAnalysisKey;\n  /// the dominator analysis key\n  std::string domAnalysisKey;\n\npublic:\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n\n  /// Initializes a capture analysis.\n  /// @param rdAnalysisKey the reaching definitions analysis key\n  /// @param domAnalysisKey the dominator analysis key\n  explicit CaptureAnalysis(std::string rdAnalysisKey, std::string domAnalysisKey)\n      : rdAnalysisKey(std::move(rdAnalysisKey)),\n        domAnalysisKey(std::move(domAnalysisKey)) {}\n\n  std::unique_ptr<Result> run(const Module *m) override;\n};\n\nCaptureInfo escapes(const BodiedFunc *func, const Value *value, CaptureResult *cr);\n\n} // namespace dataflow\n} // namespace analyze\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/analyze/dataflow/cfg.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"cfg.h\"\n\n#include <vector>\n\n#include \"codon/cir/dsl/codegen.h\"\n#include \"codon/cir/dsl/nodes.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace analyze {\nnamespace dataflow {\nnamespace {\n// TODO: this logic is very similar to lowering/pipeline -- unify somehow?\nValue *callStage(analyze::dataflow::CFGraph *cfg, PipelineFlow::Stage *stage,\n                 Value *last) {\n  std::vector<Value *> args;\n  for (auto *arg : *stage) {\n    args.push_back(arg ? arg : last);\n  }\n  return cfg->N<CallInstr>(stage->getCallee(), args);\n}\n\nValue *convertPipelineToForLoopsHelper(analyze::dataflow::CFGraph *cfg,\n                                       std::vector<PipelineFlow::Stage *> &stages,\n                                       unsigned idx = 0, Value *last = nullptr) {\n  if (idx >= stages.size())\n    return last;\n\n  auto *stage = stages[idx];\n  if (idx == 0)\n    return convertPipelineToForLoopsHelper(cfg, stages, idx + 1, stage->getCallee());\n\n  auto *prev = stages[idx - 1];\n  if (prev->isGenerator()) {\n    auto *var = cfg->N<Var>(prev->getOutputElementType());\n    auto *body = convertPipelineToForLoopsHelper(\n        cfg, stages, idx + 1, callStage(cfg, stage, cfg->N<VarValue>(var)));\n    auto *series = cfg->N<SeriesFlow>();\n    series->push_back(body);\n    return cfg->N<ForFlow>(last, series, var);\n  } else {\n    return convertPipelineToForLoopsHelper(cfg, stages, idx + 1,\n                                           callStage(cfg, stage, last));\n  }\n}\n} // namespace\n\nconst Value *convertPipelineToForLoops(analyze::dataflow::CFGraph *cfg,\n                                       const PipelineFlow *p) {\n  std::vector<PipelineFlow::Stage *> stages;\n  for (const auto &stage : *p) {\n    stages.push_back(const_cast<PipelineFlow::Stage *>(&stage));\n  }\n  return convertPipelineToForLoopsHelper(cfg, stages);\n}\n\nvoid CFBlock::reg(const Value *v) { graph->valueLocations[v->getId()] = this; }\n\nconst char SyntheticAssignInstr::NodeId = 0;\n\nint SyntheticAssignInstr::doReplaceUsedValue(id_t id, Value *newValue) {\n  if (arg && arg->getId() == id) {\n    arg = newValue;\n    return 1;\n  }\n  return 0;\n}\n\nint SyntheticAssignInstr::doReplaceUsedVariable(id_t id, Var *newVar) {\n  if (lhs->getId() == id) {\n    lhs = newVar;\n    return 1;\n  }\n  return 0;\n}\n\nconst char SyntheticPhiInstr::NodeId = 0;\n\nstd::vector<Value *> SyntheticPhiInstr::doGetUsedValues() const {\n  std::vector<Value *> ret;\n  for (auto &p : *this) {\n    ret.push_back(const_cast<Value *>(p.getResult()));\n  }\n  return ret;\n}\n\nint SyntheticPhiInstr::doReplaceUsedValue(id_t id, Value *newValue) {\n  auto res = 0;\n  for (auto &p : *this) {\n    if (p.getResult()->getId() == id) {\n      p.setResult(newValue);\n      ++res;\n    }\n  }\n  return res;\n}\n\nCFGraph::CFGraph(const BodiedFunc *f) : func(f) { newBlock(\"entry\", true); }\n\nstd::ostream &operator<<(std::ostream &os, const CFGraph &cfg) {\n  os << \"digraph \\\"\" << cfg.func->getName() << \"\\\" {\\n\";\n  for (auto *block : cfg) {\n    os << \"  \";\n    os << block->getName() << \"_\" << reinterpret_cast<uintptr_t>(block);\n    os << \" [ label=\\\"\" << block->getName() << \"\\\"\";\n    if (block == cfg.getEntryBlock()) {\n      os << \" shape=square\";\n    }\n    os << \" ];\\n\";\n  }\n  for (auto *block : cfg) {\n    for (auto next = block->successors_begin(); next != block->successors_end();\n         ++next) {\n      CFBlock *succ = *next;\n      os << \"  \";\n      os << block->getName() << \"_\" << reinterpret_cast<uintptr_t>(block);\n      os << \" -> \";\n      os << succ->getName() << \"_\" << reinterpret_cast<uintptr_t>(succ);\n      os << \";\\n\";\n    }\n  }\n  os << \"}\";\n  return os;\n}\n\nstd::unique_ptr<CFGraph> buildCFGraph(const BodiedFunc *f) {\n  auto ret = std::make_unique<CFGraph>(f);\n  CFVisitor v(ret.get());\n  v.process(f);\n  return ret;\n}\n\nconst std::string CFAnalysis::KEY = \"core-analyses-cfg\";\n\nstd::unique_ptr<Result> CFAnalysis::run(const Module *m) {\n  auto res = std::make_unique<CFResult>();\n  if (const auto *main = cast<BodiedFunc>(m->getMainFunc())) {\n    res->graphs.insert(std::make_pair(main->getId(), buildCFGraph(main)));\n  }\n\n  for (const auto *var : *m) {\n    if (const auto *f = cast<BodiedFunc>(var)) {\n      res->graphs.insert(std::make_pair(f->getId(), buildCFGraph(f)));\n    }\n  }\n  return res;\n}\n\nvoid CFVisitor::visit(const BodiedFunc *f) {\n  auto *blk = graph->getCurrentBlock();\n  for (auto it = f->arg_begin(); it != f->arg_end(); it++) {\n    blk->push_back(graph->N<analyze::dataflow::SyntheticAssignInstr>(\n        f, const_cast<Var *>(*it), const_cast<VarValue *>(graph->N<VarValue>(*it))));\n  }\n  process(f->getBody());\n}\n\nvoid CFVisitor::visit(const SeriesFlow *v) {\n  for (auto *c : *v) {\n    process(c);\n  }\n}\n\nvoid CFVisitor::visit(const IfFlow *v) {\n  process(v->getCond());\n  auto *original = graph->getCurrentBlock();\n  auto *end = graph->newBlock(\"endIf\");\n\n  auto *tBranch = graph->newBlock(\"trueBranch\", true);\n  process(v->getTrueBranch());\n  graph->getCurrentBlock()->successors_insert(end);\n\n  analyze::dataflow::CFBlock *fBranch = nullptr;\n  if (v->getFalseBranch()) {\n    fBranch = graph->newBlock(\"falseBranch\", true);\n    process(v->getFalseBranch());\n    graph->getCurrentBlock()->successors_insert(end);\n  }\n\n  original->successors_insert(tBranch);\n  if (fBranch)\n    original->successors_insert(fBranch);\n  else\n    original->successors_insert(end);\n\n  graph->setCurrentBlock(end);\n}\n\nvoid CFVisitor::visit(const WhileFlow *v) {\n  auto *original = graph->getCurrentBlock();\n  auto *end = graph->newBlock(\"endWhile\");\n\n  auto *loopBegin = graph->newBlock(\"whileBegin\", true);\n  original->successors_insert(loopBegin);\n  process(v->getCond());\n  graph->getCurrentBlock()->successors_insert(end);\n\n  loopStack.emplace_back(loopBegin, end, v->getId(), tryCatchStack.size() - 1);\n  auto *body = graph->newBlock(\"whileBody\", true);\n  loopBegin->successors_insert(body);\n  process(v->getBody());\n  loopStack.pop_back();\n  graph->getCurrentBlock()->successors_insert(loopBegin);\n\n  graph->setCurrentBlock(end);\n}\n\nvoid CFVisitor::visit(const ForFlow *v) {\n  if (v->isParallel()) {\n    for (auto *v : v->getSchedule()->getUsedValues()) {\n      process(v);\n    }\n  }\n  auto *original = graph->getCurrentBlock();\n  auto *end = graph->newBlock(\"endFor\");\n\n  auto *loopBegin = graph->newBlock(\"forBegin\", true);\n  original->successors_insert(loopBegin);\n  process(v->getIter());\n\n  auto *loopCheck = graph->newBlock(\"forCheck\");\n  graph->getCurrentBlock()->successors_insert(loopCheck);\n  loopCheck->successors_insert(end);\n\n  auto *loopNext = graph->newBlock(\"forNext\");\n  loopCheck->successors_insert(loopNext);\n  loopNext->push_back(graph->N<analyze::dataflow::SyntheticAssignInstr>(\n      v, const_cast<Var *>(v->getVar()), const_cast<Value *>(v->getIter()),\n      analyze::dataflow::SyntheticAssignInstr::NEXT_VALUE));\n\n  loopStack.emplace_back(loopCheck, end, v->getId(), tryCatchStack.size() - 1);\n  auto *loopBody = graph->newBlock(\"forBody\", true);\n  loopNext->successors_insert(loopBody);\n  process(v->getBody());\n  graph->getCurrentBlock()->successors_insert(loopCheck);\n  loopStack.pop_back();\n\n  graph->setCurrentBlock(end);\n}\n\nvoid CFVisitor::visit(const ImperativeForFlow *v) {\n  if (v->isParallel()) {\n    for (auto *v : v->getSchedule()->getUsedValues()) {\n      process(v);\n    }\n  }\n  auto *original = graph->getCurrentBlock();\n  auto *end = graph->newBlock(\"endFor\");\n\n  auto *loopBegin = graph->newBlock(\"forBegin\", true);\n  original->successors_insert(loopBegin);\n  loopBegin->push_back(graph->N<analyze::dataflow::SyntheticAssignInstr>(\n      v, const_cast<Var *>(v->getVar()), const_cast<Value *>(v->getStart()),\n      analyze::dataflow::SyntheticAssignInstr::KNOWN));\n  process(v->getStart());\n  process(v->getEnd());\n\n  auto *loopCheck = graph->newBlock(\"forCheck\");\n  graph->getCurrentBlock()->successors_insert(loopCheck);\n  loopCheck->successors_insert(end);\n\n  auto *loopNext = graph->newBlock(\"forUpdate\");\n  loopNext->push_back(graph->N<analyze::dataflow::SyntheticAssignInstr>(\n      v, const_cast<Var *>(v->getVar()), v->getStep()));\n  loopNext->successors_insert(loopCheck);\n\n  loopStack.emplace_back(loopCheck, end, v->getId(), tryCatchStack.size() - 1);\n  auto *loopBody = graph->newBlock(\"forBody\", true);\n  loopCheck->successors_insert(loopBody);\n  process(v->getBody());\n  graph->getCurrentBlock()->successors_insert(loopCheck);\n  loopStack.pop_back();\n\n  graph->setCurrentBlock(end);\n}\n\nvoid CFVisitor::visit(const TryCatchFlow *v) {\n  auto *routeBlock = graph->newBlock(\"tcRoute\");\n  auto *end = graph->newBlock(\"tcEnd\");\n  analyze::dataflow::CFBlock *else_ = nullptr;\n  analyze::dataflow::CFBlock *finally = nullptr;\n  if (v->getElse())\n    else_ = graph->newBlock(\"tcElse\");\n  if (v->getFinally())\n    finally = graph->newBlock(\"tcFinally\");\n\n  auto *dst = finally ? finally : end;\n\n  tryCatchStack.emplace_back(routeBlock, finally);\n  process(v->getBody());\n  graph->getCurrentBlock()->successors_insert(else_ ? else_ : dst);\n\n  for (auto &c : *v) {\n    auto *cBlock = graph->newBlock(\"catch\", true);\n    if (c.getVar())\n      cBlock->push_back(graph->N<analyze::dataflow::SyntheticAssignInstr>(\n          v, const_cast<Var *>(c.getVar())));\n    process(c.getHandler());\n    routeBlock->successors_insert(cBlock);\n    graph->getCurrentBlock()->successors_insert(dst);\n  }\n\n  if (v->getElse()) {\n    graph->setCurrentBlock(else_);\n    process(v->getElse());\n    graph->getCurrentBlock()->successors_insert(dst);\n  }\n\n  tryCatchStack.pop_back();\n\n  if (v->getFinally()) {\n    graph->setCurrentBlock(finally);\n    process(v->getFinally());\n    graph->getCurrentBlock()->successors_insert(end);\n    routeBlock->successors_insert(finally);\n  }\n\n  if (!tryCatchStack.empty()) {\n    if (finally)\n      finally->successors_insert(tryCatchStack.back().first);\n    else\n      routeBlock->successors_insert(tryCatchStack.back().first);\n  }\n\n  graph->setCurrentBlock(end);\n}\n\nvoid CFVisitor::visit(const PipelineFlow *v) {\n  if (auto *loops = convertPipelineToForLoops(graph, v)) {\n    process(loops);\n  } else {\n    // pipeline is empty\n  }\n}\n\nvoid CFVisitor::visit(const dsl::CustomFlow *v) {\n  v->getCFBuilder()->buildCFNodes(this);\n}\n\nvoid CFVisitor::visit(const AssignInstr *v) {\n  process(v->getRhs());\n  defaultInsert(v);\n}\n\nvoid CFVisitor::visit(const ExtractInstr *v) {\n  process(v->getVal());\n  defaultInsert(v);\n}\n\nvoid CFVisitor::visit(const InsertInstr *v) {\n  process(v->getLhs());\n  process(v->getRhs());\n  defaultInsert(v);\n}\n\nvoid CFVisitor::visit(const CallInstr *v) {\n  process(v->getCallee());\n  for (auto *a : *v)\n    process(a);\n  defaultInsert(v);\n}\n\nvoid CFVisitor::visit(const TernaryInstr *v) {\n  auto *end = graph->newBlock(\"ternaryDone\");\n  auto *tBranch = graph->newBlock(\"ternaryTrue\");\n  auto *fBranch = graph->newBlock(\"ternaryFalse\");\n\n  process(v->getCond());\n  graph->getCurrentBlock()->successors_insert(tBranch);\n  graph->getCurrentBlock()->successors_insert(fBranch);\n\n  graph->setCurrentBlock(tBranch);\n  process(v->getTrueValue());\n  graph->getCurrentBlock()->successors_insert(end);\n\n  graph->setCurrentBlock(fBranch);\n  process(v->getFalseValue());\n  graph->getCurrentBlock()->successors_insert(end);\n\n  auto *phi = graph->N<analyze::dataflow::SyntheticPhiInstr>(v);\n  phi->emplace_back(tBranch, const_cast<Value *>(v->getTrueValue()));\n  phi->emplace_back(fBranch, const_cast<Value *>(v->getFalseValue()));\n\n  end->push_back(phi);\n  graph->remapValue(v, phi);\n  graph->setCurrentBlock(end);\n}\n\nvoid CFVisitor::visit(const BreakInstr *v) {\n  auto &loop = v->getLoop() ? findLoop(v->getLoop()->getId()) : loopStack.back();\n  defaultJump(loop.end, loop.tcIndex);\n  defaultInsert(v);\n}\n\nvoid CFVisitor::visit(const ContinueInstr *v) {\n  auto &loop = v->getLoop() ? findLoop(v->getLoop()->getId()) : loopStack.back();\n  defaultJump(loop.nextIt, loop.tcIndex);\n  defaultInsert(v);\n}\n\nvoid CFVisitor::visit(const ReturnInstr *v) {\n  if (v->getValue())\n    process(v->getValue());\n  defaultJump(nullptr, -1);\n  defaultInsert(v);\n}\n\nvoid CFVisitor::visit(const YieldInstr *v) {\n  if (v->getValue())\n    process(v->getValue());\n  defaultInsert(v);\n}\n\nvoid CFVisitor::visit(const AwaitInstr *v) {\n  process(v->getValue());\n  defaultInsert(v);\n}\n\nvoid CFVisitor::visit(const ThrowInstr *v) {\n  if (v->getValue())\n    process(v->getValue());\n  defaultInsert(v);\n}\n\nvoid CFVisitor::visit(const FlowInstr *v) {\n  process(v->getFlow());\n  if (v->getValue())\n    process(v->getValue());\n  defaultInsert(v);\n}\n\nvoid CFVisitor::visit(const dsl::CustomInstr *v) {\n  v->getCFBuilder()->buildCFNodes(this);\n}\n\nvoid CFVisitor::defaultInsert(const Value *v) {\n  if (tryCatchStack.empty()) {\n    graph->getCurrentBlock()->push_back(v);\n  } else {\n    auto *original = graph->getCurrentBlock();\n    auto *newBlock = graph->newBlock(\"default\", true);\n    original->successors_insert(newBlock);\n    newBlock->successors_insert(tryCatchStack.back().first);\n    graph->getCurrentBlock()->push_back(v);\n  }\n  seenIds.insert(v->getId());\n}\n\nvoid CFVisitor::defaultJump(const CFBlock *cf, int newTcLevel) {\n  int curTc = tryCatchStack.size() - 1;\n\n  if (curTc == -1 || curTc <= newTcLevel) {\n    if (cf)\n      graph->getCurrentBlock()->successors_insert(const_cast<CFBlock *>(cf));\n  } else {\n    CFBlock *nearestFinally = nullptr;\n    for (auto i = newTcLevel + 1; i <= curTc; ++i) {\n      if (auto *n = tryCatchStack[i].second) {\n        nearestFinally = n;\n        break;\n      }\n    }\n    if (nearestFinally) {\n      graph->getCurrentBlock()->successors_insert(tryCatchStack.back().first);\n      if (cf)\n        nearestFinally->successors_insert(const_cast<CFBlock *>(cf));\n    } else {\n      if (cf)\n        graph->getCurrentBlock()->successors_insert(const_cast<CFBlock *>(cf));\n    }\n  }\n}\n\nCFVisitor::Loop &CFVisitor::findLoop(id_t id) {\n  return *std::find_if(loopStack.begin(), loopStack.end(),\n                       [=](auto &it) { return it.loopId == id; });\n}\n\n} // namespace dataflow\n} // namespace analyze\n} // namespace ir\n} // namespace codon\n\n#undef DEFAULT_VISIT\n"
  },
  {
    "path": "codon/cir/analyze/dataflow/cfg.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <iostream>\n#include <list>\n#include <memory>\n#include <unordered_map>\n#include <unordered_set>\n\n#include \"codon/cir/analyze/analysis.h\"\n#include \"codon/cir/cir.h\"\n#include \"codon/cir/util/iterators.h\"\n\n#define DEFAULT_VISIT(x)                                                               \\\n  void visit(const x *v) override { defaultInsert(v); }\n\nnamespace codon {\nnamespace ir {\nnamespace analyze {\nnamespace dataflow {\n\nclass CFGraph;\n\nclass CFBlock : public IdMixin {\nprivate:\n  /// the in-order list of values in this block\n  std::list<const Value *> values;\n  /// an un-ordered list of successor blocks\n  std::unordered_set<CFBlock *> successors;\n  /// an un-ordered list of successor blocks\n  std::unordered_set<CFBlock *> predecessors;\n  /// the block's name\n  std::string name;\n  /// the graph\n  CFGraph *graph;\n\npublic:\n  /// Constructs a control-flow block.\n  /// @param graph the parent graph\n  /// @param name the block's name\n  explicit CFBlock(CFGraph *graph, std::string name = \"\")\n      : name(std::move(name)), graph(graph) {}\n\n  virtual ~CFBlock() noexcept = default;\n\n  /// @return this block's name\n  std::string getName() const { return name; }\n\n  /// @return an iterator to the first value\n  auto begin() { return values.begin(); }\n  /// @return an iterator beyond the last value\n  auto end() { return values.end(); }\n  /// @return an iterator to the first value\n  auto begin() const { return values.begin(); }\n  /// @return an iterator beyond the last value\n  auto end() const { return values.end(); }\n  /// @return a pointer to the first value\n  const Value *front() const { return values.front(); }\n  /// @return a pointer to the last value\n  const Value *back() const { return values.back(); }\n\n  /// Inserts a value at a given position.\n  /// @param it the position\n  /// @param v the new value\n  /// @param an iterator to the new value\n  template <typename It> auto insert(It it, const Value *v) {\n    values.insert(it, v);\n    reg(v);\n  }\n  /// Inserts a value at the back.\n  /// @param v the new value\n  void push_back(const Value *v) {\n    values.push_back(v);\n    reg(v);\n  }\n  /// Erases a value at the given position.\n  /// @param it the position\n  /// @return an iterator following the removed value\n  template <typename It> auto erase(It it) { values.erase(it); }\n\n  /// @return an iterator to the first successor\n  auto successors_begin() { return successors.begin(); }\n  /// @return an iterator beyond the last successor\n  auto successors_end() { return successors.end(); }\n  /// @return an iterator to the first successor\n  auto successors_begin() const { return successors.begin(); }\n  /// @return an iterator beyond the last successor\n  auto successors_end() const { return successors.end(); }\n  /// Inserts a successor at some position.\n  /// @param v the new successor\n  /// @return an iterator to the new successor\n  auto successors_insert(CFBlock *v) {\n    successors.insert(v);\n    v->predecessors.insert(this);\n  }\n  /// Removes a given successor.\n  /// @param v the successor to remove\n  auto successors_erase(CFBlock *v) {\n    successors.erase(v);\n    v->predecessors.erase(this);\n  }\n\n  /// @return an iterator to the first predecessor\n  auto predecessors_begin() { return predecessors.begin(); }\n  /// @return an iterator beyond the last predecessor\n  auto predecessors_end() { return predecessors.end(); }\n  /// @return an iterator to the first predecessor\n  auto predecessors_begin() const { return predecessors.begin(); }\n  /// @return an iterator beyond the last predecessor\n  auto predecessors_end() const { return predecessors.end(); }\n\n  /// @return the graph\n  CFGraph *getGraph() { return graph; }\n  /// @return the graph\n  const CFGraph *getGraph() const { return graph; }\n  /// Sets the graph.\n  /// @param g the new graph\n  void setGraph(CFGraph *g) { graph = g; }\n\nprivate:\n  void reg(const Value *v);\n};\n\nclass SyntheticInstr : public AcceptorExtend<SyntheticInstr, Instr> {\nprivate:\n  const Node *source;\n\npublic:\n  /// Constructs a synthetic instruction.\n  /// @param source the node that gave rise to this instruction\n  /// @param name the name of the instruction\n  SyntheticInstr(const Node *source, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), source(source) {}\n\n  /// Gets the source of this synthetic instruction, i.e. the\n  /// node that gave rise to it when constructing the CFG.\n  /// @return the node that gave rise to this synthetic instruction\n  const Node *getSource() const { return source; }\n  /// Sets the source of this synthetic instruction\n  /// @param s the new source node\n  void setSource(const Node *s) { source = s; }\n};\n\nclass SyntheticAssignInstr\n    : public AcceptorExtend<SyntheticAssignInstr, SyntheticInstr> {\npublic:\n  enum Kind { UNKNOWN, KNOWN, NEXT_VALUE, ADD };\n\nprivate:\n  /// the left-hand side\n  Var *lhs;\n  /// the kind of synthetic assignment\n  Kind kind;\n  /// any argument to the synthetic assignment\n  Value *arg = nullptr;\n  /// the difference\n  int64_t diff = 0;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a synthetic assignment.\n  /// @param source the node that gave rise to this instruction\n  /// @param lhs the variable being assigned\n  /// @param arg the argument\n  /// @param k the kind of assignment\n  /// @param name the name of the instruction\n  SyntheticAssignInstr(const Node *source, Var *lhs, Value *arg, Kind k = KNOWN,\n                       std::string name = \"\")\n      : AcceptorExtend(source, std::move(name)), lhs(lhs), kind(k), arg(arg) {}\n  /// Constructs an unknown synthetic assignment.\n  /// @param source the node that gave rise to this instruction\n  /// @param lhs the variable being assigned\n  /// @param name the name of the instruction\n  explicit SyntheticAssignInstr(const Node *source, Var *lhs, std::string name = \"\")\n      : SyntheticAssignInstr(source, lhs, nullptr, UNKNOWN, std::move(name)) {}\n  /// Constructs an addition synthetic assignment.\n  /// @param source the node that gave rise to this instruction\n  /// @param lhs the variable being assigned\n  /// @param diff the difference\n  /// @param name the name of the instruction\n  SyntheticAssignInstr(const Node *source, Var *lhs, int64_t diff,\n                       std::string name = \"\")\n      : AcceptorExtend(source, std::move(name)), lhs(lhs), kind(ADD), diff(diff) {}\n\n  /// @return the variable being assigned\n  Var *getLhs() { return lhs; }\n  /// @return the variable being assigned\n  const Var *getLhs() const { return lhs; }\n  /// Sets the variable being assigned.\n  /// @param v the variable\n  void setLhs(Var *v) { lhs = v; }\n\n  /// @return the argument\n  Value *getArg() { return arg; }\n  /// @return the argument\n  const Value *getArg() const { return arg; }\n  /// Sets the argument.\n  /// @param v the new value\n  void setArg(Value *v) { arg = v; }\n\n  /// @return the diff\n  int64_t getDiff() const { return diff; }\n  /// Sets the diff.\n  /// @param v the new value\n  void setDiff(int64_t v) { diff = v; }\n\n  /// @return the kind of synthetic assignment\n  Kind getKind() const { return kind; }\n  /// Sets the kind.\n  /// @param k the new value\n  void setKind(Kind k) { kind = k; }\n\nprotected:\n  std::vector<Value *> doGetUsedValues() const override { return {arg}; }\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n\n  std::vector<Var *> doGetUsedVariables() const override { return {lhs}; }\n  int doReplaceUsedVariable(id_t id, Var *newVar) override;\n};\n\nclass SyntheticPhiInstr : public AcceptorExtend<SyntheticPhiInstr, SyntheticInstr> {\npublic:\n  class Predecessor {\n  private:\n    /// the predecessor block\n    CFBlock *pred;\n    /// the value\n    Value *result;\n\n  public:\n    /// Constructs a predecessor.\n    /// @param pred the predecessor block\n    /// @param result the result of this predecessor.\n    Predecessor(CFBlock *pred, Value *result) : pred(pred), result(result) {}\n\n    /// @return the predecessor block\n    CFBlock *getPred() { return pred; }\n    /// @return the predecessor block\n    const CFBlock *getPred() const { return pred; }\n    /// Sets the predecessor.\n    /// @param v the new value\n    void setPred(CFBlock *v) { pred = v; }\n\n    /// @return the result\n    Value *getResult() { return result; }\n    /// @return the result\n    const Value *getResult() const { return result; }\n    /// Sets the result\n    /// @param v the new value\n    void setResult(Value *v) { result = v; }\n  };\n\nprivate:\n  std::list<Predecessor> preds;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a synthetic phi instruction.\n  /// @param source the node that gave rise to this instruction\n  /// @param name the name of the instruction\n  explicit SyntheticPhiInstr(const Node *source, std::string name = \"\")\n      : AcceptorExtend(source, std::move(name)) {}\n\n  /// @return an iterator to the first instruction/flow\n  auto begin() { return preds.begin(); }\n  /// @return an iterator beyond the last instruction/flow\n  auto end() { return preds.end(); }\n  /// @return an iterator to the first instruction/flow\n  auto begin() const { return preds.begin(); }\n  /// @return an iterator beyond the last instruction/flow\n  auto end() const { return preds.end(); }\n\n  /// @return a pointer to the first instruction/flow\n  Predecessor &front() { return preds.front(); }\n  /// @return a pointer to the last instruction/flow\n  Predecessor &back() { return preds.back(); }\n  /// @return a pointer to the first instruction/flow\n  const Predecessor &front() const { return preds.front(); }\n  /// @return a pointer to the last instruction/flow\n  const Predecessor &back() const { return preds.back(); }\n\n  /// Inserts a predecessor.\n  /// @param pos the position\n  /// @param v the predecessor\n  /// @return an iterator to the newly added predecessor\n  template <typename It> auto insert(It pos, Predecessor v) {\n    return preds.insert(pos, v);\n  }\n  /// Appends an predecessor.\n  /// @param v the predecessor\n  void push_back(Predecessor v) { preds.push_back(v); }\n\n  /// Erases the item at the supplied position.\n  /// @param pos the position\n  /// @return the iterator beyond the removed predecessor\n  template <typename It> auto erase(It pos) { return preds.erase(pos); }\n\n  /// Emplaces a predecessor.\n  /// @param args the args\n  template <typename... Args> void emplace_back(Args &&...args) {\n    preds.emplace_back(std::forward<Args>(args)...);\n  }\n\nprotected:\n  std::vector<Value *> doGetUsedValues() const override;\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n};\n\nclass CFGraph {\nprivate:\n  /// owned list of blocks\n  std::list<std::unique_ptr<CFBlock>> blocks;\n  /// the current block\n  CFBlock *cur = nullptr;\n  /// the function being analyzed\n  const BodiedFunc *func;\n  /// a list of synthetic values\n  std::list<std::unique_ptr<Value>> syntheticValues;\n  /// a map of synthetic values\n  std::unordered_map<id_t, Value *> valueMapping;\n  /// a list of synthetic variables\n  std::list<std::unique_ptr<Var>> syntheticVars;\n  /// a mapping from value id to block\n  std::unordered_map<id_t, CFBlock *> valueLocations;\n\npublic:\n  /// Constructs a control-flow graph.\n  explicit CFGraph(const BodiedFunc *f);\n\n  /// @return number of blocks in this CFG\n  auto size() const { return blocks.size(); }\n\n  /// @return an iterator to the first block\n  auto begin() { return util::raw_ptr_adaptor(blocks.begin()); }\n  /// @return an iterator beyond the last block\n  auto end() { return util::raw_ptr_adaptor(blocks.end()); }\n  /// @return an iterator to the first block\n  auto begin() const { return util::raw_ptr_adaptor(blocks.begin()); }\n  /// @return an iterator beyond the last block\n  auto end() const { return util::raw_ptr_adaptor(blocks.end()); }\n\n  /// @return an iterator to the synthetic value\n  auto synth_begin() { return util::raw_ptr_adaptor(syntheticValues.begin()); }\n  /// @return an iterator beyond the last synthetic value\n  auto synth_end() { return util::raw_ptr_adaptor(syntheticValues.end()); }\n  /// @return an iterator to the first synthetic value\n  auto synth_begin() const { return util::raw_ptr_adaptor(syntheticValues.begin()); }\n  /// @return an iterator beyond the last synthetic value\n  auto synth_end() const { return util::raw_ptr_adaptor(syntheticValues.end()); }\n\n  /// @return the entry block\n  CFBlock *getEntryBlock() { return blocks.front().get(); }\n  /// @return the entry block\n  const CFBlock *getEntryBlock() const { return blocks.front().get(); }\n\n  /// @return the entry block\n  CFBlock *getCurrentBlock() { return cur; }\n  /// @return the entry block\n  const CFBlock *getCurrentBlock() const { return cur; }\n  /// Sets the current block.\n  /// @param v the new value\n  void setCurrentBlock(CFBlock *v) { cur = v; }\n\n  /// @return the function\n  const BodiedFunc *getFunc() const { return func; }\n  /// Sets the function.\n  /// @param f the new value\n  void setFunc(BodiedFunc *f) { func = f; }\n\n  /// Gets the block containing a value.\n  /// @param val the value\n  /// @return the block\n  CFBlock *getBlock(const Value *v) {\n    auto vmIt = valueMapping.find(v->getId());\n    if (vmIt != valueMapping.end())\n      v = vmIt->second;\n\n    auto it = valueLocations.find(v->getId());\n    return it != valueLocations.end() ? it->second : nullptr;\n  }\n  /// Gets the block containing a value.\n  /// @param val the value\n  /// @return the block\n  const CFBlock *getBlock(const Value *v) const {\n    auto vmIt = valueMapping.find(v->getId());\n    if (vmIt != valueMapping.end())\n      v = vmIt->second;\n\n    auto it = valueLocations.find(v->getId());\n    return it != valueLocations.end() ? it->second : nullptr;\n  }\n\n  /// Creates and inserts a new block\n  /// @param name the name\n  /// @param setCur true if the block should be made the current one\n  /// @return a newly inserted block\n  CFBlock *newBlock(std::string name = \"\", bool setCur = false) {\n    auto *ret = new CFBlock(this, std::move(name));\n    blocks.emplace_back(ret);\n    if (setCur)\n      setCurrentBlock(ret);\n    return ret;\n  }\n\n  template <typename NodeType, typename... Args> NodeType *N(Args &&...args) {\n    auto *ret = new NodeType(std::forward<Args>(args)...);\n    reg(ret);\n    ret->setModule(func->getModule());\n    return ret;\n  }\n\n  /// Remaps a value.\n  /// @param id original id\n  /// @param newValue the new value\n  void remapValue(id_t id, Value *newValue) { valueMapping[id] = newValue; }\n  /// Remaps a value.\n  /// @param original the original value\n  /// @param newValue the new value\n  void remapValue(const Value *original, Value *newValue) {\n    remapValue(original->getId(), newValue);\n  }\n\n  /// Gets a value by id.\n  /// @param id the id\n  /// @return the value or nullptr\n  Value *getValue(id_t id) {\n    auto it = valueMapping.find(id);\n    return it != valueMapping.end() ? it->second : func->getModule()->getValue(id);\n  }\n\n  friend std::ostream &operator<<(std::ostream &os, const CFGraph &cfg);\n  friend class CFBlock;\n\nprivate:\n  void reg(Var *v) { syntheticVars.emplace_back(v); }\n\n  void reg(Value *v) {\n    syntheticValues.emplace_back(v);\n    valueMapping[v->getId()] = v;\n  }\n};\n\n/// Builds a control-flow graph from a given function.\n/// @param f the function\n/// @return the control-flow graph\nstd::unique_ptr<CFGraph> buildCFGraph(const BodiedFunc *f);\n\n/// Control-flow analysis result.\nstruct CFResult : public Result {\n  /// map from function id to control-flow graph\n  std::unordered_map<id_t, std::unique_ptr<CFGraph>> graphs;\n};\n\n/// Control-flow analysis that runs on all functions.\nclass CFAnalysis : public Analysis {\npublic:\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n\n  std::unique_ptr<Result> run(const Module *m) override;\n};\n\nclass CFVisitor : public util::ConstVisitor {\nprivate:\n  struct Loop {\n    analyze::dataflow::CFBlock *nextIt;\n    analyze::dataflow::CFBlock *end;\n    id_t loopId;\n    int tcIndex;\n\n    Loop(analyze::dataflow::CFBlock *nextIt, analyze::dataflow::CFBlock *end,\n         id_t loopId, int tcIndex = -1)\n        : nextIt(nextIt), end(end), loopId(loopId), tcIndex(tcIndex) {}\n  };\n\n  analyze::dataflow::CFGraph *graph;\n  std::vector<std::pair<analyze::dataflow::CFBlock *, analyze::dataflow::CFBlock *>>\n      tryCatchStack;\n  std::unordered_set<id_t> seenIds;\n  std::vector<Loop> loopStack;\n\npublic:\n  explicit CFVisitor(analyze::dataflow::CFGraph *graph) : graph(graph) {}\n\n  void visit(const BodiedFunc *f) override;\n\n  DEFAULT_VISIT(VarValue)\n  DEFAULT_VISIT(PointerValue)\n\n  void visit(const SeriesFlow *v) override;\n  void visit(const IfFlow *v) override;\n  void visit(const WhileFlow *v) override;\n  void visit(const ForFlow *v) override;\n  void visit(const ImperativeForFlow *v) override;\n\n  void visit(const TryCatchFlow *v) override;\n  void visit(const PipelineFlow *v) override;\n  void visit(const dsl::CustomFlow *v) override;\n\n  DEFAULT_VISIT(TemplatedConst<int64_t>);\n  DEFAULT_VISIT(TemplatedConst<double>);\n  DEFAULT_VISIT(TemplatedConst<bool>);\n  DEFAULT_VISIT(TemplatedConst<std::string>);\n  DEFAULT_VISIT(dsl::CustomConst);\n\n  void visit(const AssignInstr *v) override;\n  void visit(const ExtractInstr *v) override;\n  void visit(const InsertInstr *v) override;\n  void visit(const CallInstr *v) override;\n  DEFAULT_VISIT(StackAllocInstr);\n  DEFAULT_VISIT(TypePropertyInstr);\n  DEFAULT_VISIT(YieldInInstr);\n\n  void visit(const TernaryInstr *v) override;\n\n  void visit(const BreakInstr *v) override;\n  void visit(const ContinueInstr *v) override;\n  void visit(const ReturnInstr *v) override;\n  void visit(const YieldInstr *v) override;\n  void visit(const AwaitInstr *v) override;\n  void visit(const ThrowInstr *v) override;\n  void visit(const FlowInstr *v) override;\n  void visit(const dsl::CustomInstr *v) override;\n\n  template <typename NodeType> void process(const NodeType *v) {\n    if (!v)\n      return;\n    if (seenIds.find(v->getId()) != seenIds.end())\n      return;\n    seenIds.insert(v->getId());\n    v->accept(*this);\n  }\n\n  void defaultInsert(const Value *v);\n  void defaultJump(const CFBlock *cf, int newTcLevel = -1);\n\nprivate:\n  Loop &findLoop(id_t id);\n};\n\n} // namespace dataflow\n} // namespace analyze\n} // namespace ir\n} // namespace codon\n\ntemplate <>\nstruct fmt::formatter<codon::ir::analyze::dataflow::CFGraph> : fmt::ostream_formatter {\n};\n\n#undef DEFAULT_VISIT\n"
  },
  {
    "path": "codon/cir/analyze/dataflow/dominator.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"dominator.h\"\n\n#include \"codon/cir/llvm/llvm.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace analyze {\nnamespace dataflow {\n\nvoid DominatorInspector::analyze() {\n  const auto numBlocks = cfg->size();\n  std::unordered_map<id_t, size_t> mapping;        // id -> sequential id\n  std::vector<id_t> mappingInv(numBlocks);         // sequential id -> id\n  std::vector<llvm::BitVector> bitvecs(numBlocks); // sequential id -> bitvector\n\n  mapping.reserve(numBlocks);\n  size_t next = 0;\n  for (auto *blk : *cfg) {\n    auto id = blk->getId();\n    mapping[id] = next;\n    mappingInv[next] = id;\n    ++next;\n  }\n\n  // Initialize: all blocks dominate themselves; others start with universal set\n  for (auto *blk : *cfg) {\n    auto id = mapping[blk->getId()];\n    llvm::BitVector &bv = bitvecs[id];\n    bv.resize(numBlocks, true);\n    if (blk == cfg->getEntryBlock()) { // entry block only dominated by itself\n      bv.reset();\n      bv.set(id);\n    }\n  }\n\n  // Run simple domination algorithm\n  bool changed = true;\n  while (changed) {\n    changed = false;\n    for (auto *blk : *cfg) {\n      auto id = mapping[blk->getId()];\n      llvm::BitVector old = bitvecs[id];\n      llvm::BitVector newSet;\n\n      if (blk->predecessors_begin() == blk->predecessors_end())\n        newSet.resize(numBlocks);\n\n      bool first = true;\n      for (auto it = blk->predecessors_begin(); it != blk->predecessors_end(); ++it) {\n        auto predId = mapping[(*it)->getId()];\n        const auto &predSet = bitvecs[predId];\n        if (first) {\n          newSet = predSet;\n          first = false;\n        } else {\n          newSet &= predSet;\n        }\n      }\n\n      newSet.set(id); // a block always dominates itself\n\n      if (newSet != old) {\n        bitvecs[id] = newSet;\n        changed = true;\n      }\n    }\n  }\n\n  // Map back to canonical id\n  sets.reserve(numBlocks);\n  for (unsigned id = 0; id < numBlocks; id++) {\n    auto &bv = bitvecs[id];\n    auto &set = sets[mappingInv[id]];\n    for (auto n = bv.find_first(); n != -1; n = bv.find_next(n)) {\n      set.insert(mappingInv[n]);\n    }\n  }\n}\n\nbool DominatorInspector::isDominated(const Value *v, const Value *dominator) {\n  auto *vBlock = cfg->getBlock(v);\n  auto *dBlock = cfg->getBlock(dominator);\n\n  if (vBlock->getId() == dBlock->getId()) {\n    auto vDist =\n        std::distance(vBlock->begin(), std::find(vBlock->begin(), vBlock->end(), v));\n    auto dDist = std::distance(vBlock->begin(),\n                               std::find(vBlock->begin(), vBlock->end(), dominator));\n    return dDist <= vDist;\n  }\n\n  auto &set = sets[vBlock->getId()];\n  return set.find(dBlock->getId()) != set.end();\n}\n\nconst std::string DominatorAnalysis::KEY = \"core-analyses-dominator\";\n\nstd::unique_ptr<Result> DominatorAnalysis::run(const Module *m) {\n  auto *cfgResult = getAnalysisResult<CFResult>(cfAnalysisKey);\n  auto ret = std::make_unique<DominatorResult>(cfgResult);\n  for (const auto &graph : cfgResult->graphs) {\n    auto inspector = std::make_unique<DominatorInspector>(graph.second.get());\n    inspector->analyze();\n    ret->results[graph.first] = std::move(inspector);\n  }\n  return ret;\n}\n\n} // namespace dataflow\n} // namespace analyze\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/analyze/dataflow/dominator.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n\n#include \"codon/cir/analyze/analysis.h\"\n#include \"codon/cir/analyze/dataflow/cfg.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace analyze {\nnamespace dataflow {\n\n/// Helper to query the dominators of a particular function.\nclass DominatorInspector {\nprivate:\n  std::unordered_map<id_t, std::unordered_set<id_t>> sets;\n  CFGraph *cfg;\n\npublic:\n  explicit DominatorInspector(CFGraph *cfg) : cfg(cfg) {}\n\n  /// Do the analysis.\n  void analyze();\n\n  /// Checks if one value dominates another.\n  /// @param v the value\n  /// @param dominator the dominator value\n  bool isDominated(const Value *v, const Value *dominator);\n};\n\n/// Result of a dominator analysis.\nstruct DominatorResult : public Result {\n  /// the corresponding control flow result\n  const CFResult *cfgResult;\n  /// the dominator inspectors\n  std::unordered_map<id_t, std::unique_ptr<DominatorInspector>> results;\n\n  explicit DominatorResult(const CFResult *cfgResult) : cfgResult(cfgResult) {}\n};\n\n/// Dominator analysis. Must have control flow-graph available.\nclass DominatorAnalysis : public Analysis {\nprivate:\n  /// the control-flow analysis key\n  std::string cfAnalysisKey;\n\npublic:\n  static const std::string KEY;\n\n  /// Initializes a dominator analysis.\n  /// @param cfAnalysisKey the control-flow analysis key\n  explicit DominatorAnalysis(std::string cfAnalysisKey)\n      : cfAnalysisKey(std::move(cfAnalysisKey)) {}\n\n  std::string getKey() const override { return KEY; }\n\n  std::unique_ptr<Result> run(const Module *m) override;\n};\n\n} // namespace dataflow\n} // namespace analyze\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/analyze/dataflow/reaching.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"reaching.h\"\n\n#include <deque>\n#include <tuple>\n\nnamespace codon {\nnamespace ir {\nnamespace analyze {\nnamespace dataflow {\nnamespace {\n\nid_t getKilled(const Value *val) {\n  if (auto *assign = cast<AssignInstr>(val)) {\n    return assign->getLhs()->getId();\n  } else if (auto *synthAssign = cast<analyze::dataflow::SyntheticAssignInstr>(val)) {\n    return synthAssign->getLhs()->getId();\n  }\n  return -1;\n}\n\nstd::pair<id_t, ReachingDef> getGenerated(const Value *val) {\n  if (auto *assign = cast<AssignInstr>(val)) {\n    return std::make_pair(assign->getLhs()->getId(),\n                          ReachingDef(assign, assign->getRhs()));\n  } else if (auto *synthAssign = cast<analyze::dataflow::SyntheticAssignInstr>(val)) {\n    if (synthAssign->getKind() == analyze::dataflow::SyntheticAssignInstr::KNOWN)\n      return std::make_pair(synthAssign->getLhs()->getId(),\n                            ReachingDef(synthAssign, synthAssign->getArg()));\n    else\n      return std::make_pair(synthAssign->getLhs()->getId(), ReachingDef(synthAssign));\n  }\n  return std::make_pair(-1, ReachingDef(nullptr));\n}\n\ntemplate <typename T> struct WorkList {\n  std::unordered_set<id_t> have;\n  std::deque<T *> queue;\n\n  void push(T *a) {\n    auto id = a->getId();\n    if (have.count(id))\n      return;\n    have.insert(id);\n    queue.push_back(a);\n  }\n\n  T *pop() {\n    if (queue.empty())\n      return nullptr;\n    auto *a = queue.front();\n    queue.pop_front();\n    have.erase(a->getId());\n    return a;\n  }\n\n  template <typename S> WorkList(S *x) : have(), queue() {\n    for (T *a : *x) {\n      push(a);\n    }\n  }\n};\n\nstruct BitSet {\n  static constexpr unsigned B = 64;\n  static unsigned allocSize(unsigned size) { return (size + B - 1) / B; }\n\n  std::vector<uint64_t> words;\n\n  explicit BitSet(unsigned size) : words(allocSize(size), 0) {}\n\n  BitSet copy(unsigned size) const {\n    auto res = BitSet(size);\n    std::memcpy(res.words.data(), words.data(), allocSize(size) * (B / 8));\n    return res;\n  }\n\n  void set(unsigned bit) { words.data()[bit / B] |= (1UL << (bit % B)); }\n\n  bool get(unsigned bit) const {\n    return (words.data()[bit / B] & (1UL << (bit % B))) != 0;\n  }\n\n  bool equals(const BitSet &other, unsigned size) {\n    return std::memcmp(words.data(), other.words.data(), allocSize(size) * (B / 8)) ==\n           0;\n  }\n\n  void clear(unsigned size) { std::memset(words.data(), 0, allocSize(size) * (B / 8)); }\n\n  void setAll(unsigned size) {\n    std::memset(words.data(), 0xff, allocSize(size) * (B / 8));\n  }\n\n  void overwrite(const BitSet &other, unsigned size) {\n    std::memcpy(words.data(), other.words.data(), allocSize(size) * (B / 8));\n  }\n\n  void update(const BitSet &other, unsigned size) {\n    auto *p = words.data();\n    auto *q = other.words.data();\n    auto n = allocSize(size);\n    for (unsigned i = 0; i < n; i++) {\n      p[i] |= q[i];\n    }\n  }\n\n  void subtract(const BitSet &other, unsigned size) {\n    auto *p = words.data();\n    auto *q = other.words.data();\n    auto n = allocSize(size);\n    for (unsigned i = 0; i < n; i++) {\n      p[i] &= ~q[i];\n    }\n  }\n};\n\ntemplate <typename T> struct BlockBitSets {\n  T *blk;\n  BitSet gen;\n  BitSet kill;\n  BitSet in;\n  BitSet out;\n\n  BlockBitSets(T *blk, BitSet gen, BitSet kill, BitSet in, BitSet out)\n      : blk(blk), gen(std::move(gen)), kill(std::move(kill)), in(std::move(in)),\n        out(std::move(out)) {}\n};\n} // namespace\n\nvoid RDInspector::analyze() {\n  std::vector<const Value *> ordering;\n  std::unordered_map<id_t, unsigned> lookup;\n  std::unordered_map<id_t, std::vector<const Value *>> varToAssignments;\n\n  for (auto *blk : *cfg) {\n    for (auto *val : *blk) {\n      auto k = getKilled(val);\n      if (k != -1) {\n        lookup.emplace(val->getId(), ordering.size());\n        ordering.push_back(val);\n        varToAssignments[k].push_back(val);\n      }\n    }\n  }\n\n  unsigned n = ordering.size();\n  std::unordered_map<id_t, BlockBitSets<CFBlock>> bitsets;\n\n  // construct initial gen and kill sets\n  for (auto *blk : *cfg) {\n    auto gen = BitSet(n);\n    auto kill = BitSet(n);\n\n    std::unordered_map<id_t, id_t> generated;\n    for (auto *val : *blk) {\n      // vars that are used by pointer may change at any time, so don't track them\n      if (auto *ptr = cast<PointerValue>(val)) {\n        invalid.insert(ptr->getVar()->getId());\n        continue;\n      }\n\n      auto g = getGenerated(val);\n      if (g.first != -1) {\n        // generated map will store latest generated assignment, as desired\n        generated[g.first] = val->getId();\n      }\n\n      auto k = getKilled(val);\n      if (k != -1) {\n        // all other assignments that use the var are killed\n        for (auto *assign : varToAssignments[k]) {\n          if (assign->getId() != val->getId())\n            kill.set(lookup[assign->getId()]);\n        }\n      }\n    }\n\n    // set gen for the last assignment of each var in the block\n    for (auto &entry : generated) {\n      gen.set(lookup[entry.second]);\n    }\n\n    auto in = BitSet(n);\n    auto out = gen.copy(n); // out = gen is an optimization over out = {}\n    bitsets.emplace(std::piecewise_construct, std::forward_as_tuple(blk->getId()),\n                    std::forward_as_tuple(blk, std::move(gen), std::move(kill),\n                                          std::move(in), std::move(out)));\n  }\n\n  WorkList<CFBlock> worklist(cfg);\n  while (auto *blk = worklist.pop()) {\n    auto &data = bitsets.find(blk->getId())->second;\n\n    // IN[blk] = U OUT[pred], for all predecessors pred\n    data.in.clear(n);\n    for (auto it = blk->predecessors_begin(); it != blk->predecessors_end(); ++it) {\n      data.in.update(bitsets.find((*it)->getId())->second.out, n);\n    }\n\n    // OUT[blk] = GEN[blk] U (IN[blk] - KILL[blk])\n    auto oldout = data.out.copy(n);\n    auto tmp = data.in.copy(n);\n    tmp.subtract(data.kill, n);\n    tmp.update(data.gen, n);\n    data.out.overwrite(tmp, n);\n\n    // if OUT changed, add all successors to worklist\n    if (!data.out.equals(oldout, n)) {\n      for (auto it = blk->successors_begin(); it != blk->successors_end(); ++it) {\n        worklist.push(*it);\n      }\n    }\n  }\n\n  // reconstruct final sets in more convenient format\n  for (auto &elem : bitsets) {\n    auto &data = elem.second;\n    auto &entry = sets[data.blk->getId()];\n\n    for (unsigned i = 0; i < n; i++) {\n      if (data.in.get(i)) {\n        auto g = getGenerated(ordering[i]);\n        entry.in[g.first].insert(g.second);\n      }\n    }\n  }\n}\n\nstd::vector<ReachingDef> RDInspector::getReachingDefinitions(const Var *var,\n                                                             const Value *loc) {\n  if (invalid.find(var->getId()) != invalid.end() || var->isGlobal())\n    return {};\n\n  auto *blk = cfg->getBlock(loc);\n  if (!blk)\n    return {};\n  auto &entry = sets[blk->getId()];\n  auto defs = entry.in[var->getId()];\n\n  bool needClear = (blk->getId() == cfg->getEntryBlock()->getId());\n  bool didClear = false;\n\n  auto done = false;\n  for (auto *val : *blk) {\n    if (done)\n      break;\n    if (val->getId() == loc->getId())\n      done = true;\n\n    auto killed = getKilled(val);\n    if (killed == var->getId()) {\n      defs.clear();\n      didClear = true;\n    }\n    auto gen = getGenerated(val);\n    if (gen.first == var->getId())\n      defs.insert(gen.second);\n  }\n\n  if (needClear && !didClear)\n    return {};\n\n  return std::vector<ReachingDef>(defs.begin(), defs.end());\n}\n\nconst std::string RDAnalysis::KEY = \"core-analyses-rd\";\n\nstd::unique_ptr<Result> RDAnalysis::run(const Module *m) {\n  auto *cfgResult = getAnalysisResult<CFResult>(cfAnalysisKey);\n  auto ret = std::make_unique<RDResult>(cfgResult);\n  for (const auto &graph : cfgResult->graphs) {\n    auto inspector = std::make_unique<RDInspector>(graph.second.get());\n    inspector->analyze();\n    ret->results[graph.first] = std::move(inspector);\n  }\n  return ret;\n}\n\n} // namespace dataflow\n} // namespace analyze\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/analyze/dataflow/reaching.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <utility>\n\n#include \"codon/cir/analyze/analysis.h\"\n#include \"codon/cir/analyze/dataflow/cfg.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace analyze {\nnamespace dataflow {\n\n/// Single answer to a reaching definition query\nstruct ReachingDef {\n  /// Assignment instruction, which can be a `AssignInstr` or\n  /// e.g. a `SyntheticAssignInstr` to represent loop variable\n  /// assignment etc.\n  const Instr *assignment;\n  /// The value being assigned, or null if unknown. The assigned\n  /// value is unknown when, for example, assigning the next value\n  /// of a loop variable.\n  const Value *assignee;\n\n  explicit ReachingDef(const Instr *assignment, const Value *assignee = nullptr)\n      : assignment(assignment), assignee(assignee) {}\n\n  bool known() const { return assignee != nullptr; }\n\n  id_t getId() const { return known() ? assignee->getId() : assignment->getId(); }\n\n  bool operator==(const ReachingDef &other) const {\n    if (known() != other.known())\n      return false;\n\n    return known() ? (assignee->getId() == other.assignee->getId())\n                   : (assignment->getId() == other.assignment->getId());\n  }\n};\n\n} // namespace dataflow\n} // namespace analyze\n} // namespace ir\n} // namespace codon\n\nnamespace std {\ntemplate <> struct hash<codon::ir::analyze::dataflow::ReachingDef> {\n  size_t operator()(const codon::ir::analyze::dataflow::ReachingDef &d) const {\n    return d.known() ? hash<id_t>{}(d.assignee->getId())\n                     : hash<id_t>{}(d.assignment->getId());\n  }\n};\n} // namespace std\n\nnamespace codon {\nnamespace ir {\nnamespace analyze {\nnamespace dataflow {\n\n/// Helper to query the reaching definitions of a particular function.\nclass RDInspector {\nprivate:\n  struct BlockData {\n    std::unordered_map<id_t, std::unordered_set<ReachingDef>> in;\n    BlockData() = default;\n  };\n  std::unordered_set<id_t> invalid;\n  std::unordered_map<id_t, BlockData> sets;\n  CFGraph *cfg;\n\npublic:\n  explicit RDInspector(CFGraph *cfg) : cfg(cfg) {}\n\n  /// Do the analysis.\n  void analyze();\n\n  /// Gets the reaching definitions at a particular location.\n  /// @param var the variable being inspected\n  /// @param loc the location\n  /// @return a vector of reaching definitions\n  std::vector<ReachingDef> getReachingDefinitions(const Var *var, const Value *loc);\n\n  bool isInvalid(const Var *var) const { return invalid.count(var->getId()) != 0; }\n};\n\n/// Result of a reaching definition analysis.\nstruct RDResult : public Result {\n  /// the corresponding control flow result\n  const CFResult *cfgResult;\n  /// the reaching definition inspectors\n  std::unordered_map<id_t, std::unique_ptr<RDInspector>> results;\n\n  explicit RDResult(const CFResult *cfgResult) : cfgResult(cfgResult) {}\n};\n\n/// Reaching definition analysis. Must have control flow-graph available.\nclass RDAnalysis : public Analysis {\nprivate:\n  /// the control-flow analysis key\n  std::string cfAnalysisKey;\n\npublic:\n  static const std::string KEY;\n\n  /// Initializes a reaching definition analysis.\n  /// @param cfAnalysisKey the control-flow analysis key\n  explicit RDAnalysis(std::string cfAnalysisKey)\n      : cfAnalysisKey(std::move(cfAnalysisKey)) {}\n\n  std::string getKey() const override { return KEY; }\n\n  std::unique_ptr<Result> run(const Module *m) override;\n};\n\n} // namespace dataflow\n} // namespace analyze\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/analyze/module/global_vars.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"global_vars.h\"\n\n#include \"codon/cir/util/operator.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace analyze {\nnamespace module {\nnamespace {\nstruct GlobalVarAnalyzer : public util::Operator {\n  std::unordered_map<id_t, id_t> assignments;\n\n  void handle(PointerValue *v) override {\n    if (v->getVar()->isGlobal())\n      assignments[v->getVar()->getId()] = -1;\n  }\n\n  void handle(AssignInstr *v) override {\n    auto *lhs = v->getLhs();\n    auto id = lhs->getId();\n    if (lhs->isGlobal()) {\n      if (assignments.find(id) != assignments.end()) {\n        assignments[id] = -1;\n      } else {\n        assignments[id] = v->getRhs()->getId();\n      }\n    }\n  }\n};\n} // namespace\n\nconst std::string GlobalVarsAnalyses::KEY = \"core-analyses-global-vars\";\n\nstd::unique_ptr<Result> GlobalVarsAnalyses::run(const Module *m) {\n  GlobalVarAnalyzer gva;\n  gva.visit(const_cast<Module *>(m)); // TODO: any way around this cast?\n  return std::make_unique<GlobalVarsResult>(std::move(gva.assignments));\n}\n\n} // namespace module\n} // namespace analyze\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/analyze/module/global_vars.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <unordered_map>\n\n#include \"codon/cir/analyze/analysis.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace analyze {\nnamespace module {\n\nstruct GlobalVarsResult : public Result {\n  std::unordered_map<id_t, id_t> assignments;\n  explicit GlobalVarsResult(std::unordered_map<id_t, id_t> assignments)\n      : assignments(std::move(assignments)) {}\n};\n\nclass GlobalVarsAnalyses : public Analysis {\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n\n  std::unique_ptr<Result> run(const Module *m) override;\n};\n\n} // namespace module\n} // namespace analyze\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/analyze/module/side_effect.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"side_effect.h\"\n\n#include <type_traits>\n#include <utility>\n\n#include \"codon/cir/analyze/dataflow/capture.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/cir/util/operator.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace analyze {\nnamespace module {\nnamespace {\ntemplate <typename T> T max(T &&t) { return std::forward<T>(t); }\n\ntemplate <typename T0, typename T1, typename... Ts>\ntypename std::common_type<T0, T1, Ts...>::type max(T0 &&val1, T1 &&val2, Ts &&...vs) {\n  return (val1 > val2) ? max(val1, std::forward<Ts>(vs)...)\n                       : max(val2, std::forward<Ts>(vs)...);\n}\n\nstruct VarUseAnalyzer : public util::Operator {\n  std::unordered_map<id_t, long> varCounts;\n  std::unordered_map<id_t, long> varAssignCounts;\n\n  void preHook(Node *v) override {\n    for (auto *var : v->getUsedVariables()) {\n      ++varCounts[var->getId()];\n    }\n  }\n\n  void handle(AssignInstr *v) override { ++varAssignCounts[v->getLhs()->getId()]; }\n};\n\nstruct SideEfectAnalyzer : public util::ConstVisitor {\n  using Status = util::SideEffectStatus;\n\n  static Status getFunctionStatusFromAttributes(const Func *v, bool *force = nullptr) {\n    auto attr = [v](const auto &s) { return util::hasAttribute(v, s); };\n\n    if (attr(util::PURE_ATTR)) {\n      if (force)\n        *force = true;\n      return Status::PURE;\n    }\n\n    if (attr(util::NO_SIDE_EFFECT_ATTR)) {\n      if (force)\n        *force = true;\n      return Status::NO_SIDE_EFFECT;\n    }\n\n    if (attr(util::NO_CAPTURE_ATTR)) {\n      if (force)\n        *force = true;\n      return Status::NO_CAPTURE;\n    }\n\n    if (attr(util::NON_PURE_ATTR)) {\n      if (force)\n        *force = true;\n      return Status::UNKNOWN;\n    }\n\n    if (force)\n      *force = false;\n    return Status::UNKNOWN;\n  }\n\n  VarUseAnalyzer &vua;\n  dataflow::CaptureResult *cr;\n  bool globalAssignmentHasSideEffects;\n  std::unordered_map<id_t, Status> result;\n  std::vector<const BodiedFunc *> funcStack;\n  Status exprStatus;\n  Status funcStatus;\n\n  // We have to sometimes be careful with globals since future\n  // IR passes might introduce globals that we've eliminated\n  // or demoted earlier. Hence the distinction with whether\n  // global assignments are considered to have side effects.\n  SideEfectAnalyzer(VarUseAnalyzer &vua, dataflow::CaptureResult *cr,\n                    bool globalAssignmentHasSideEffects)\n      : util::ConstVisitor(), vua(vua), cr(cr),\n        globalAssignmentHasSideEffects(globalAssignmentHasSideEffects), result(),\n        funcStack(), exprStatus(Status::PURE), funcStatus(Status::PURE) {}\n\n  template <typename T> bool has(const T *v) {\n    return result.find(v->getId()) != result.end();\n  }\n\n  template <typename T> void set(const T *v, Status expr, Status func = Status::PURE) {\n    result[v->getId()] = exprStatus = expr;\n    funcStatus = max(funcStatus, func);\n  }\n\n  template <typename T> Status process(const T *v) {\n    if (!v)\n      return Status::PURE;\n    if (has(v))\n      return result[v->getId()];\n    v->accept(*this);\n    seqassertn(has(v), \"node not added to results\");\n    return result[v->getId()];\n  }\n\n  std::pair<Status, Status> getVarAssignStatus(const Var *var) {\n    if (!var)\n      return {Status::PURE, Status::PURE};\n\n    auto id = var->getId();\n    auto it1 = vua.varCounts.find(id);\n    auto it2 = vua.varAssignCounts.find(id);\n    auto count1 = (it1 != vua.varCounts.end()) ? it1->second : 0;\n    auto count2 = (it2 != vua.varAssignCounts.end()) ? it2->second : 0;\n\n    bool global = var->isGlobal();\n    bool used = (count1 != count2);\n    Status defaultStatus = global ? Status::UNKNOWN : Status::NO_CAPTURE;\n    auto se2stat = [&](bool b) { return b ? defaultStatus : Status::PURE; };\n\n    if (globalAssignmentHasSideEffects || var->isExternal()) {\n      return {se2stat(used || global), se2stat(global)};\n    } else {\n      return {se2stat(used), se2stat(used && global)};\n    }\n  }\n\n  void handleVarAssign(const Value *v, const Var *var, Status base) {\n    auto pair = getVarAssignStatus(var);\n    set(v, max(pair.first, base), pair.second);\n  }\n\n  void visit(const Module *v) override {\n    process(v->getMainFunc());\n    for (auto *x : *v) {\n      process(x);\n    }\n  }\n\n  void visit(const Var *v) override { set(v, Status::PURE); }\n\n  void visit(const BodiedFunc *v) override {\n    bool force;\n    auto s = getFunctionStatusFromAttributes(v, &force);\n    set(v, s, s); // avoid infinite recursion\n    auto oldFuncStatus = funcStatus;\n    funcStatus = Status::PURE;\n    funcStack.push_back(v);\n    process(v->getBody());\n    funcStack.pop_back();\n    if (force)\n      funcStatus = s;\n    set(v, funcStatus);\n    funcStatus = oldFuncStatus;\n  }\n\n  void visit(const ExternalFunc *v) override {\n    set(v, getFunctionStatusFromAttributes(v));\n  }\n\n  void visit(const InternalFunc *v) override { set(v, Status::PURE); }\n\n  void visit(const LLVMFunc *v) override { set(v, getFunctionStatusFromAttributes(v)); }\n\n  void visit(const VarValue *v) override { set(v, Status::PURE); }\n\n  void visit(const PointerValue *v) override { set(v, Status::PURE); }\n\n  void visit(const SeriesFlow *v) override {\n    Status s = Status::PURE;\n    for (auto *x : *v) {\n      s = max(s, process(x));\n    }\n    set(v, s);\n  }\n\n  void visit(const IfFlow *v) override {\n    set(v, max(process(v->getCond()), process(v->getTrueBranch()),\n               process(v->getFalseBranch())));\n  }\n\n  void visit(const WhileFlow *v) override {\n    set(v, max(process(v->getCond()), process(v->getBody())));\n  }\n\n  void visit(const ForFlow *v) override {\n    auto s = max(process(v->getIter()), process(v->getBody()));\n    if (auto *sched = v->getSchedule()) {\n      for (auto *x : sched->getUsedValues()) {\n        s = max(s, process(x));\n      }\n    }\n    handleVarAssign(v, v->getVar(), s);\n  }\n\n  void visit(const ImperativeForFlow *v) override {\n    auto s = max(process(v->getStart()), process(v->getEnd()), process(v->getBody()));\n    if (auto *sched = v->getSchedule()) {\n      for (auto *x : sched->getUsedValues()) {\n        s = max(s, process(x));\n      }\n    }\n    handleVarAssign(v, v->getVar(), s);\n  }\n\n  void visit(const TryCatchFlow *v) override {\n    auto s =\n        max(process(v->getBody()), process(v->getElse()), process(v->getFinally()));\n    auto callStatus = Status::PURE;\n\n    for (auto &x : *v) {\n      auto pair = getVarAssignStatus(x.getVar());\n      s = max(s, pair.first, process(x.getHandler()));\n      callStatus = max(callStatus, pair.second);\n    }\n\n    set(v, s, callStatus);\n  }\n\n  void visit(const PipelineFlow *v) override {\n    auto s = Status::PURE;\n    auto callStatus = Status::PURE;\n    for (auto &stage : *v) {\n      // make sure we're treating this as a call\n      if (auto *f = util::getFunc(stage.getCallee())) {\n        auto stageCallStatus = process(f);\n        callStatus = max(callStatus, stageCallStatus);\n        s = max(s, stageCallStatus);\n      } else {\n        // unknown function\n        process(stage.getCallee());\n        callStatus = Status::UNKNOWN;\n        s = Status::UNKNOWN;\n      }\n\n      for (auto *arg : stage) {\n        s = max(s, process(arg));\n      }\n    }\n    set(v, s, callStatus);\n  }\n\n  void visit(const dsl::CustomFlow *v) override {\n    set(v, v->getSideEffectStatus(/*local=*/true),\n        v->getSideEffectStatus(/*local=*/false));\n  }\n\n  void visit(const IntConst *v) override { set(v, Status::PURE); }\n\n  void visit(const FloatConst *v) override { set(v, Status::PURE); }\n\n  void visit(const BoolConst *v) override { set(v, Status::PURE); }\n\n  void visit(const StringConst *v) override { set(v, Status::PURE); }\n\n  void visit(const dsl::CustomConst *v) override { set(v, Status::PURE); }\n\n  void visit(const AssignInstr *v) override {\n    handleVarAssign(v, v->getLhs(), process(v->getRhs()));\n  }\n\n  void visit(const ExtractInstr *v) override { set(v, process(v->getVal())); }\n\n  void visit(const InsertInstr *v) override {\n    process(v->getLhs());\n    process(v->getRhs());\n\n    auto *func = funcStack.back();\n    auto it = cr->results.find(func->getId());\n    seqassertn(it != cr->results.end(), \"function not found in capture results\");\n    auto captureInfo = it->second;\n\n    bool pure = true;\n\n    for (auto &info : captureInfo) {\n      if (info.externCaptures || info.modified || !info.argCaptures.empty()) {\n        pure = false;\n        break;\n      }\n    }\n\n    if (pure) {\n      // make sure the lhs does not escape\n      auto escapeInfo = escapes(func, v->getLhs(), cr);\n      pure = (!escapeInfo || (escapeInfo.returnCaptures && !escapeInfo.externCaptures &&\n                              escapeInfo.argCaptures.empty()));\n    }\n\n    set(v, Status::UNKNOWN, pure ? Status::PURE : Status::UNKNOWN);\n  }\n\n  void visit(const CallInstr *v) override {\n    auto s = process(v->getCallee());\n    auto callStatus = Status::UNKNOWN;\n    for (auto *x : *v) {\n      s = max(s, process(x));\n    }\n    if (auto *f = util::getFunc(v->getCallee())) {\n      callStatus = process(f);\n      s = max(s, callStatus);\n    } else {\n      // unknown function\n      s = Status::UNKNOWN;\n    }\n    set(v, s, callStatus);\n  }\n\n  void visit(const StackAllocInstr *v) override { set(v, Status::PURE); }\n\n  void visit(const TypePropertyInstr *v) override { set(v, Status::PURE); }\n\n  void visit(const YieldInInstr *v) override { set(v, Status::NO_CAPTURE); }\n\n  void visit(const TernaryInstr *v) override {\n    set(v, max(process(v->getCond()), process(v->getTrueValue()),\n               process(v->getFalseValue())));\n  }\n\n  void visit(const BreakInstr *v) override { set(v, Status::NO_CAPTURE); }\n\n  void visit(const ContinueInstr *v) override { set(v, Status::NO_CAPTURE); }\n\n  void visit(const ReturnInstr *v) override {\n    set(v, max(Status::NO_CAPTURE, process(v->getValue())));\n  }\n\n  void visit(const YieldInstr *v) override {\n    set(v, max(Status::NO_CAPTURE, process(v->getValue())));\n  }\n\n  void visit(const AwaitInstr *v) override {\n    set(v, max(Status::NO_CAPTURE, process(v->getValue())));\n  }\n\n  void visit(const ThrowInstr *v) override {\n    process(v->getValue());\n    set(v, Status::UNKNOWN, Status::NO_CAPTURE);\n  }\n\n  void visit(const FlowInstr *v) override {\n    set(v, max(process(v->getFlow()), process(v->getValue())));\n  }\n\n  void visit(const dsl::CustomInstr *v) override {\n    set(v, v->getSideEffectStatus(/*local=*/true),\n        v->getSideEffectStatus(/*local=*/false));\n  }\n};\n} // namespace\n\nconst std::string SideEffectAnalysis::KEY = \"core-analyses-side-effect\";\n\nbool SideEffectResult::hasSideEffect(const Value *v) const {\n  auto it = result.find(v->getId());\n  return it == result.end() || it->second != util::SideEffectStatus::PURE;\n}\n\nstd::unique_ptr<Result> SideEffectAnalysis::run(const Module *m) {\n  auto *capResult = getAnalysisResult<dataflow::CaptureResult>(capAnalysisKey);\n  VarUseAnalyzer vua;\n  const_cast<Module *>(m)->accept(vua);\n  SideEfectAnalyzer sea(vua, capResult, globalAssignmentHasSideEffects);\n  m->accept(sea);\n  return std::make_unique<SideEffectResult>(sea.result);\n}\n\n} // namespace module\n} // namespace analyze\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/analyze/module/side_effect.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <unordered_map>\n\n#include \"codon/cir/analyze/analysis.h\"\n#include \"codon/cir/util/side_effect.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace analyze {\nnamespace module {\n\nstruct SideEffectResult : public Result {\n  /// mapping of ID to corresponding node's side effect status\n  std::unordered_map<id_t, util::SideEffectStatus> result;\n\n  SideEffectResult(std::unordered_map<id_t, util::SideEffectStatus> result)\n      : result(std::move(result)) {}\n\n  /// @param v the value to check\n  /// @return true if the node has side effects (false positives allowed)\n  bool hasSideEffect(const Value *v) const;\n};\n\nclass SideEffectAnalysis : public Analysis {\nprivate:\n  /// the capture analysis key\n  std::string capAnalysisKey;\n  /// true if assigning to a global variable automatically has side effects\n  bool globalAssignmentHasSideEffects;\n\npublic:\n  static const std::string KEY;\n\n  /// Constructs a side effect analysis.\n  /// @param globalAssignmentHasSideEffects true if global variable assignment\n  /// automatically has side effects\n  explicit SideEffectAnalysis(const std::string &capAnalysisKey,\n                              bool globalAssignmentHasSideEffects = true)\n      : Analysis(), capAnalysisKey(capAnalysisKey),\n        globalAssignmentHasSideEffects(globalAssignmentHasSideEffects) {}\n\n  std::string getKey() const override { return KEY; }\n\n  std::unique_ptr<Result> run(const Module *m) override;\n};\n\n} // namespace module\n} // namespace analyze\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/attribute.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"attribute.h\"\n\n#include \"codon/cir/func.h\"\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/value.h\"\n#include <fmt/ostream.h>\n\nnamespace codon {\nnamespace ir {\n\nconst int StringValueAttribute::AttributeID = 101;\nconst int IntValueAttribute::AttributeID = 102;\nconst int StringListAttribute::AttributeID = 103;\nconst int KeyValueAttribute::AttributeID = 104;\nconst int MemberAttribute::AttributeID = 105;\nconst int PythonWrapperAttribute::AttributeID = 106;\nconst int SrcInfoAttribute::AttributeID = 107;\nconst int DocstringAttribute::AttributeID = 108;\nconst int TupleLiteralAttribute::AttributeID = 109;\nconst int ListLiteralAttribute::AttributeID = 111;\nconst int SetLiteralAttribute::AttributeID = 111;\nconst int DictLiteralAttribute::AttributeID = 112;\nconst int PartialFunctionAttribute::AttributeID = 113;\n\nstd::ostream &StringListAttribute::doFormat(std::ostream &os) const {\n  fmt::print(os, FMT_STRING(\"{}\"), fmt::join(values.begin(), values.end(), \",\"));\n  return os;\n}\n\nbool KeyValueAttribute::has(const std::string &key) const {\n  return attributes.find(key) != attributes.end();\n}\n\nstd::string KeyValueAttribute::get(const std::string &key) const {\n  auto it = attributes.find(key);\n  return it != attributes.end() ? it->second : \"\";\n}\n\nstd::ostream &KeyValueAttribute::doFormat(std::ostream &os) const {\n  std::vector<std::string> keys;\n  for (auto &val : attributes)\n    keys.push_back(val.second);\n  fmt::print(os, FMT_STRING(\"{}\"), fmt::join(keys.begin(), keys.end(), \",\"));\n  return os;\n}\n\nstd::ostream &MemberAttribute::doFormat(std::ostream &os) const {\n  std::vector<std::string> strings;\n  for (auto &val : memberSrcInfo)\n    strings.push_back(fmt::format(FMT_STRING(\"{}={}\"), val.first, val.second));\n  fmt::print(os, FMT_STRING(\"({})\"), fmt::join(strings.begin(), strings.end(), \",\"));\n  return os;\n}\n\nstd::unique_ptr<Attribute> PythonWrapperAttribute::clone(util::CloneVisitor &cv) const {\n  return std::make_unique<PythonWrapperAttribute>(cast<Func>(cv.clone(original)));\n}\n\nstd::unique_ptr<Attribute>\nPythonWrapperAttribute::forceClone(util::CloneVisitor &cv) const {\n  return std::make_unique<PythonWrapperAttribute>(cv.forceClone(original));\n}\n\nstd::ostream &PythonWrapperAttribute::doFormat(std::ostream &os) const {\n  fmt::print(os, FMT_STRING(\"(pywrap {})\"), original->referenceString());\n  return os;\n}\n\nstd::unique_ptr<Attribute> TupleLiteralAttribute::clone(util::CloneVisitor &cv) const {\n  std::vector<Value *> elementsCloned;\n  for (auto *val : elements)\n    elementsCloned.push_back(cv.clone(val));\n  return std::make_unique<TupleLiteralAttribute>(elementsCloned);\n}\n\nstd::unique_ptr<Attribute>\nTupleLiteralAttribute::forceClone(util::CloneVisitor &cv) const {\n  std::vector<Value *> elementsCloned;\n  for (auto *val : elements)\n    elementsCloned.push_back(cv.forceClone(val));\n  return std::make_unique<TupleLiteralAttribute>(elementsCloned);\n}\n\nstd::ostream &TupleLiteralAttribute::doFormat(std::ostream &os) const {\n  std::vector<std::string> strings;\n  for (auto *val : elements)\n    strings.push_back(fmt::format(FMT_STRING(\"{}\"), *val));\n  fmt::print(os, FMT_STRING(\"({})\"), fmt::join(strings.begin(), strings.end(), \",\"));\n  return os;\n}\n\nstd::unique_ptr<Attribute> ListLiteralAttribute::clone(util::CloneVisitor &cv) const {\n  std::vector<LiteralElement> elementsCloned;\n  for (auto &e : elements)\n    elementsCloned.push_back({cv.clone(e.value), e.star});\n  return std::make_unique<ListLiteralAttribute>(elementsCloned);\n}\n\nstd::unique_ptr<Attribute>\nListLiteralAttribute::forceClone(util::CloneVisitor &cv) const {\n  std::vector<LiteralElement> elementsCloned;\n  for (auto &e : elements)\n    elementsCloned.push_back({cv.forceClone(e.value), e.star});\n  return std::make_unique<ListLiteralAttribute>(elementsCloned);\n}\n\nstd::ostream &ListLiteralAttribute::doFormat(std::ostream &os) const {\n  std::vector<std::string> strings;\n  for (auto &e : elements)\n    strings.push_back(fmt::format(FMT_STRING(\"{}{}\"), e.star ? \"*\" : \"\", *e.value));\n  fmt::print(os, FMT_STRING(\"[{}]\"), fmt::join(strings.begin(), strings.end(), \",\"));\n  return os;\n}\n\nstd::unique_ptr<Attribute> SetLiteralAttribute::clone(util::CloneVisitor &cv) const {\n  std::vector<LiteralElement> elementsCloned;\n  for (auto &e : elements)\n    elementsCloned.push_back({cv.clone(e.value), e.star});\n  return std::make_unique<SetLiteralAttribute>(elementsCloned);\n}\n\nstd::unique_ptr<Attribute>\nSetLiteralAttribute::forceClone(util::CloneVisitor &cv) const {\n  std::vector<LiteralElement> elementsCloned;\n  for (auto &e : elements)\n    elementsCloned.push_back({cv.forceClone(e.value), e.star});\n  return std::make_unique<SetLiteralAttribute>(elementsCloned);\n}\n\nstd::ostream &SetLiteralAttribute::doFormat(std::ostream &os) const {\n  std::vector<std::string> strings;\n  for (auto &e : elements)\n    strings.push_back(fmt::format(FMT_STRING(\"{}{}\"), e.star ? \"*\" : \"\", *e.value));\n  fmt::print(os, FMT_STRING(\"set([{}])\"),\n             fmt::join(strings.begin(), strings.end(), \",\"));\n  return os;\n}\n\nstd::unique_ptr<Attribute> DictLiteralAttribute::clone(util::CloneVisitor &cv) const {\n  std::vector<DictLiteralAttribute::KeyValuePair> elementsCloned;\n  for (auto &val : elements)\n    elementsCloned.push_back(\n        {cv.clone(val.key), val.value ? cv.clone(val.value) : nullptr});\n  return std::make_unique<DictLiteralAttribute>(elementsCloned);\n}\n\nstd::unique_ptr<Attribute>\nDictLiteralAttribute::forceClone(util::CloneVisitor &cv) const {\n  std::vector<DictLiteralAttribute::KeyValuePair> elementsCloned;\n  for (auto &val : elements)\n    elementsCloned.push_back(\n        {cv.forceClone(val.key), val.value ? cv.forceClone(val.value) : nullptr});\n  return std::make_unique<DictLiteralAttribute>(elementsCloned);\n}\n\nstd::ostream &DictLiteralAttribute::doFormat(std::ostream &os) const {\n  std::vector<std::string> strings;\n  for (auto &val : elements) {\n    if (val.value) {\n      strings.push_back(fmt::format(FMT_STRING(\"{}:{}\"), *val.key, *val.value));\n    } else {\n      strings.push_back(fmt::format(FMT_STRING(\"**{}\"), *val.key));\n    }\n  }\n  fmt::print(os, FMT_STRING(\"dict([{}])\"),\n             fmt::join(strings.begin(), strings.end(), \",\"));\n  return os;\n}\n\nstd::unique_ptr<Attribute>\nPartialFunctionAttribute::clone(util::CloneVisitor &cv) const {\n  std::vector<Value *> argsCloned;\n  for (auto *val : args)\n    argsCloned.push_back(cv.clone(val));\n  return std::make_unique<PartialFunctionAttribute>(name, argsCloned);\n}\n\nstd::unique_ptr<Attribute>\nPartialFunctionAttribute::forceClone(util::CloneVisitor &cv) const {\n  std::vector<Value *> argsCloned;\n  for (auto *val : args)\n    argsCloned.push_back(cv.forceClone(val));\n  return std::make_unique<PartialFunctionAttribute>(name, argsCloned);\n}\n\nstd::ostream &PartialFunctionAttribute::doFormat(std::ostream &os) const {\n  std::vector<std::string> strings;\n  for (auto *val : args)\n    strings.push_back(val ? fmt::format(FMT_STRING(\"{}\"), *val) : \"...\");\n  fmt::print(os, FMT_STRING(\"{}({})\"), name,\n             fmt::join(strings.begin(), strings.end(), \",\"));\n  return os;\n}\n\n} // namespace ir\n\nstd::unordered_map<int, std::unique_ptr<ir::Attribute>>\nclone(const std::unordered_map<int, std::unique_ptr<ir::Attribute>> &t) {\n  std::unordered_map<int, std::unique_ptr<ir::Attribute>> r;\n  for (auto &[k, v] : t)\n    r[k] = v->clone();\n  return r;\n}\n\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/attribute.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <iostream>\n#include <map>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"codon/util/common.h\"\n\nnamespace codon {\nnamespace ir {\n\nclass Func;\nclass Value;\n\nnamespace util {\nclass CloneVisitor;\n}\n\n/// Base for CIR attributes.\nstruct Attribute {\n  virtual ~Attribute() noexcept = default;\n\n  /// @return true if the attribute should be propagated across clones\n  virtual bool needsClone() const { return true; }\n\n  friend std::ostream &operator<<(std::ostream &os, const Attribute &a) {\n    return a.doFormat(os);\n  }\n\n  /// @return a clone of the attribute\n  virtual std::unique_ptr<Attribute> clone() const {\n    return std::make_unique<Attribute>();\n  }\n\n  /// @return a clone of the attribute\n  virtual std::unique_ptr<Attribute> clone(util::CloneVisitor &cv) const {\n    return clone();\n  }\n\n  /// @return a clone of the attribute\n  virtual std::unique_ptr<Attribute> forceClone(util::CloneVisitor &cv) const {\n    return clone(cv);\n  }\n\nprivate:\n  virtual std::ostream &doFormat(std::ostream &os) const { return os; }\n};\n\n/// Attribute containing SrcInfo\nstruct SrcInfoAttribute : public Attribute {\n  static const int AttributeID;\n\n  /// source info\n  codon::SrcInfo info;\n\n  SrcInfoAttribute() = default;\n  /// Constructs a SrcInfoAttribute.\n  /// @param info the source info\n  explicit SrcInfoAttribute(codon::SrcInfo info) : info(std::move(info)) {}\n\n  std::unique_ptr<Attribute> clone() const override {\n    return std::make_unique<SrcInfoAttribute>(*this);\n  }\n\nprivate:\n  std::ostream &doFormat(std::ostream &os) const override { return os << info; }\n};\n\n/// Attribute containing docstring from source\nstruct StringValueAttribute : public Attribute {\n  static const int AttributeID;\n\n  std::string value;\n\n  StringValueAttribute() = default;\n  /// Constructs a StringValueAttribute.\n  explicit StringValueAttribute(const std::string &value) : value(value) {}\n\n  std::unique_ptr<Attribute> clone() const override {\n    return std::make_unique<StringValueAttribute>(*this);\n  }\n\nprivate:\n  std::ostream &doFormat(std::ostream &os) const override { return os << value; }\n};\n\n/// Attribute containing docstring from source\nstruct DocstringAttribute : public Attribute {\n  static const int AttributeID;\n\n  /// the docstring\n  std::string docstring;\n\n  DocstringAttribute() = default;\n  /// Constructs a DocstringAttribute.\n  /// @param docstring the docstring\n  explicit DocstringAttribute(const std::string &docstring) : docstring(docstring) {}\n\n  std::unique_ptr<Attribute> clone() const override {\n    return std::make_unique<DocstringAttribute>(*this);\n  }\n\nprivate:\n  std::ostream &doFormat(std::ostream &os) const override { return os << docstring; }\n};\n\n/// Attribute containing function information\nstruct KeyValueAttribute : public Attribute {\n  static const int AttributeID;\n\n  /// attributes map\n  std::unordered_map<std::string, std::string> attributes;\n\n  KeyValueAttribute() = default;\n  /// Constructs a KeyValueAttribute.\n  /// @param attributes the map of attributes\n  explicit KeyValueAttribute(std::unordered_map<std::string, std::string> attributes)\n      : attributes(std::move(attributes)) {}\n\n  /// @param key the key\n  /// @return true if the map contains key, false otherwise\n  bool has(const std::string &key) const;\n\n  /// @param key the key\n  /// @return the value associated with the given key, or empty\n  ///         string if none\n  std::string get(const std::string &key) const;\n\n  std::unique_ptr<Attribute> clone() const override {\n    return std::make_unique<KeyValueAttribute>(*this);\n  }\n\nprivate:\n  std::ostream &doFormat(std::ostream &os) const override;\n};\n\n/// Attribute containing function information\nstruct StringListAttribute : public Attribute {\n  static const int AttributeID;\n\n  /// attributes map\n  std::vector<std::string> values;\n\n  StringListAttribute() = default;\n  /// Constructs a StringListAttribute.\n  /// @param attributes the map of attributes\n  explicit StringListAttribute(std::vector<std::string> values)\n      : values(std::move(values)) {}\n\n  std::unique_ptr<Attribute> clone() const override {\n    return std::make_unique<StringListAttribute>(*this);\n  }\n\nprivate:\n  std::ostream &doFormat(std::ostream &os) const override;\n};\n\n/// Attribute containing type member information\nstruct MemberAttribute : public Attribute {\n  static const int AttributeID;\n\n  /// member source info map\n  std::map<std::string, SrcInfo> memberSrcInfo;\n\n  MemberAttribute() = default;\n  /// Constructs a KeyValueAttribute.\n  /// @param attributes the map of attributes\n  explicit MemberAttribute(std::map<std::string, SrcInfo> memberSrcInfo)\n      : memberSrcInfo(std::move(memberSrcInfo)) {}\n\n  std::unique_ptr<Attribute> clone() const override {\n    return std::make_unique<MemberAttribute>(*this);\n  }\n\nprivate:\n  std::ostream &doFormat(std::ostream &os) const override;\n};\n\n/// Attribute used to mark Python wrappers of Codon functions\nstruct PythonWrapperAttribute : public Attribute {\n  static const int AttributeID;\n\n  /// the function being wrapped\n  Func *original;\n\n  /// Constructs a PythonWrapperAttribute.\n  /// @param original the function being wrapped\n  explicit PythonWrapperAttribute(Func *original) : original(original) {}\n\n  bool needsClone() const override { return false; }\n\n  std::unique_ptr<Attribute> clone() const override {\n    seqassertn(false, \"cannot operate without CloneVisitor\");\n    return nullptr;\n  }\n  std::unique_ptr<Attribute> clone(util::CloneVisitor &cv) const override;\n  std::unique_ptr<Attribute> forceClone(util::CloneVisitor &cv) const override;\n\nprivate:\n  std::ostream &doFormat(std::ostream &os) const override;\n};\n\n/// Attribute attached to IR structures corresponding to tuple literals\nstruct TupleLiteralAttribute : public Attribute {\n  static const int AttributeID;\n\n  /// values contained in tuple literal\n  std::vector<Value *> elements;\n\n  explicit TupleLiteralAttribute(std::vector<Value *> elements)\n      : elements(std::move(elements)) {}\n\n  std::unique_ptr<Attribute> clone() const override {\n    seqassertn(false, \"cannot operate without CloneVisitor\");\n    return nullptr;\n  }\n  std::unique_ptr<Attribute> clone(util::CloneVisitor &cv) const override;\n  std::unique_ptr<Attribute> forceClone(util::CloneVisitor &cv) const override;\n\nprivate:\n  std::ostream &doFormat(std::ostream &os) const override;\n};\n\n/// Information about an element in a collection literal\nstruct LiteralElement {\n  /// the element value\n  Value *value;\n  /// true if preceded by \"*\", as in \"[*x]\"\n  bool star;\n};\n\n/// Attribute attached to IR structures corresponding to list literals\nstruct ListLiteralAttribute : public Attribute {\n  static const int AttributeID;\n\n  /// elements contained in list literal\n  std::vector<LiteralElement> elements;\n\n  explicit ListLiteralAttribute(std::vector<LiteralElement> elements)\n      : elements(std::move(elements)) {}\n\n  std::unique_ptr<Attribute> clone() const override {\n    seqassertn(false, \"cannot operate without CloneVisitor\");\n    return nullptr;\n  }\n  std::unique_ptr<Attribute> clone(util::CloneVisitor &cv) const override;\n  std::unique_ptr<Attribute> forceClone(util::CloneVisitor &cv) const override;\n\nprivate:\n  std::ostream &doFormat(std::ostream &os) const override;\n};\n\n/// Attribute attached to IR structures corresponding to set literals\nstruct SetLiteralAttribute : public Attribute {\n  static const int AttributeID;\n\n  /// elements contained in set literal\n  std::vector<LiteralElement> elements;\n\n  explicit SetLiteralAttribute(std::vector<LiteralElement> elements)\n      : elements(std::move(elements)) {}\n\n  std::unique_ptr<Attribute> clone() const override {\n    seqassertn(false, \"cannot operate without CloneVisitor\");\n    return nullptr;\n  }\n  std::unique_ptr<Attribute> clone(util::CloneVisitor &cv) const override;\n  std::unique_ptr<Attribute> forceClone(util::CloneVisitor &cv) const override;\n\nprivate:\n  std::ostream &doFormat(std::ostream &os) const override;\n};\n\n/// Attribute attached to IR structures corresponding to dict literals\nstruct DictLiteralAttribute : public Attribute {\n  struct KeyValuePair {\n    /// the key in the literal\n    Value *key;\n    /// the value in the literal, or null if key is being star-unpacked\n    Value *value;\n  };\n\n  static const int AttributeID;\n\n  /// keys and values contained in dict literal\n  std::vector<KeyValuePair> elements;\n\n  explicit DictLiteralAttribute(std::vector<KeyValuePair> elements)\n      : elements(std::move(elements)) {}\n\n  std::unique_ptr<Attribute> clone() const override {\n    seqassertn(false, \"cannot operate without CloneVisitor\");\n    return nullptr;\n  }\n  std::unique_ptr<Attribute> clone(util::CloneVisitor &cv) const override;\n  std::unique_ptr<Attribute> forceClone(util::CloneVisitor &cv) const override;\n\nprivate:\n  std::ostream &doFormat(std::ostream &os) const override;\n};\n\n/// Attribute attached to IR structures corresponding to partial functions\nstruct PartialFunctionAttribute : public Attribute {\n  static const int AttributeID;\n\n  /// base name of the function being used in the partial\n  std::string name;\n\n  /// partial arguments, or null if none\n  /// e.g. \"f(a, ..., b)\" has elements [a, null, b]\n  std::vector<Value *> args;\n\n  PartialFunctionAttribute(const std::string &name, std::vector<Value *> args)\n      : name(name), args(std::move(args)) {}\n\n  std::unique_ptr<Attribute> clone() const override {\n    seqassertn(false, \"cannot operate without CloneVisitor\");\n    return nullptr;\n  }\n  std::unique_ptr<Attribute> clone(util::CloneVisitor &cv) const override;\n  std::unique_ptr<Attribute> forceClone(util::CloneVisitor &cv) const override;\n\nprivate:\n  std::ostream &doFormat(std::ostream &os) const override;\n};\n\nstruct IntValueAttribute : public Attribute {\n  static const int AttributeID;\n\n  int64_t value;\n\n  IntValueAttribute() = default;\n  /// Constructs a IntValueAttribute.\n  explicit IntValueAttribute(int64_t value) : value(value) {}\n\n  std::unique_ptr<Attribute> clone() const override {\n    return std::make_unique<IntValueAttribute>(*this);\n  }\n\nprivate:\n  std::ostream &doFormat(std::ostream &os) const override { return os << value; }\n};\n\n} // namespace ir\n\nstd::unordered_map<int, std::unique_ptr<ir::Attribute>>\nclone(const std::unordered_map<int, std::unique_ptr<ir::Attribute>> &t);\n\n} // namespace codon\n\ntemplate <> struct fmt::formatter<codon::ir::Attribute> : fmt::ostream_formatter {};\n"
  },
  {
    "path": "codon/cir/base.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"base.h\"\n\n#include \"codon/cir/types/types.h\"\n#include \"codon/cir/util/format.h\"\n#include \"codon/cir/value.h\"\n#include \"codon/cir/var.h\"\n\nnamespace codon {\nnamespace ir {\n\nid_t IdMixin::currentId = 0;\n\nvoid IdMixin::resetId() { currentId = 0; }\n\nconst char Node::NodeId = 0;\n\nstd::ostream &operator<<(std::ostream &os, const Node &other) {\n  return util::format(os, &other);\n}\n\nNode::Node(const Node &n)\n    : name(n.name), module(n.module), replacement(n.replacement),\n      attributes(codon::clone(n.attributes)) {}\n\nint Node::replaceUsedValue(Value *old, Value *newValue) {\n  return replaceUsedValue(old->getId(), newValue);\n}\n\nint Node::replaceUsedType(types::Type *old, types::Type *newType) {\n  return replaceUsedType(old->getName(), newType);\n}\n\nint Node::replaceUsedVariable(Var *old, Var *newVar) {\n  return replaceUsedVariable(old->getId(), newVar);\n}\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/base.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <cstdint>\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <utility>\n\n#include \"codon/cir/attribute.h\"\n#include \"codon/cir/util/iterators.h\"\n#include \"codon/cir/util/visitor.h\"\n#include \"codon/util/common.h\"\n#include <fmt/format.h>\n#include <fmt/ostream.h>\n\nnamespace codon {\nnamespace ir {\n\nusing id_t = std::int64_t;\n\nclass Func;\nclass Module;\n\n/// Mixin class for IR nodes that need ids.\nclass IdMixin {\nprivate:\n  /// the global id counter\n  static id_t currentId;\n\nprotected:\n  /// the instance's id\n  id_t id;\n\npublic:\n  /// Resets the global id counter.\n  static void resetId();\n\n  IdMixin() : id(currentId++) {}\n\n  /// @return the node's id.\n  virtual id_t getId() const { return id; }\n};\n\n/// Base for named IR nodes.\nclass Node {\nprivate:\n  /// the node's name\n  std::string name;\n  /// the module\n  Module *module = nullptr;\n  /// a replacement, if set\n  Node *replacement = nullptr;\n\nprotected:\n  /// key-value attribute store\n  std::unordered_map<int, std::unique_ptr<Attribute>> attributes;\n\npublic:\n  // RTTI is implemented using a port of LLVM's Extensible RTTI\n  // For more details, see\n  // https://llvm.org/docs/HowToSetUpLLVMStyleRTTI.html#rtti-for-open-class-hierarchies\n  static const char NodeId;\n\n  /// Constructs a node.\n  /// @param name the node's name\n  explicit Node(std::string name = \"\") : name(std::move(name)) {}\n  /// Constructs a node.\n  /// @param name the node's name\n  explicit Node(const Node &n);\n\n  /// See LLVM documentation.\n  static const void *nodeId() { return &NodeId; }\n  /// See LLVM documentation.\n  virtual const void *dynamicNodeId() const = 0;\n  /// See LLVM documentation.\n  virtual bool isConvertible(const void *other) const {\n    if (hasReplacement())\n      return getActual()->isConvertible(other);\n    return other == nodeId();\n  }\n  /// See LLVM documentation.\n  template <typename Target> bool is() const { return isConvertible(Target::nodeId()); }\n  /// See LLVM documentation.\n  template <typename Target> Target *as() {\n    return isConvertible(Target::nodeId()) ? static_cast<Target *>(getActual())\n                                           : nullptr;\n  }\n  /// See LLVM documentation.\n  template <typename Target> const Target *as() const {\n    return isConvertible(Target::nodeId()) ? static_cast<const Target *>(getActual())\n                                           : nullptr;\n  }\n\n  /// @return the node's name\n  const std::string &getName() const { return getActual()->name; }\n  /// Sets the node's name\n  /// @param n the new name\n  void setName(std::string n) { getActual()->name = std::move(n); }\n\n  /// Accepts visitors.\n  /// @param v the visitor\n  virtual void accept(util::Visitor &v) {}\n  /// Accepts visitors.\n  /// @param v the visitor\n  virtual void accept(util::ConstVisitor &v) const {}\n\n  /// Sets an attribute\n  /// @param the attribute key\n  /// @param value the attribute\n  void setAttribute(std::unique_ptr<Attribute> value, int key) {\n    getActual()->attributes[key] = std::move(value);\n  }\n  /// Sets an attribute\n  /// @param value the attribute\n  template <typename AttributeType>\n  void setAttribute(std::unique_ptr<AttributeType> value) {\n    setAttribute(std::move(value), AttributeType::AttributeID);\n  }\n\n  /// @param n attribute ID\n  /// @return true if the attribute is in the store\n  bool hasAttribute(int n) const {\n    auto *actual = getActual();\n    return actual->attributes.find(n) != actual->attributes.end();\n  }\n  /// @return true if the attribute is in the store\n  template <typename AttributeType> bool hasAttribute() const {\n    return hasAttribute(AttributeType::AttributeID);\n  }\n\n  /// Gets the appropriate attribute.\n  /// @param key the attribute key\n  Attribute *getAttribute(int key) {\n    auto *actual = getActual();\n\n    auto it = actual->attributes.find(key);\n    return it != actual->attributes.end() ? it->second.get() : nullptr;\n  }\n  /// Gets the appropriate attribute.\n  /// @param key the attribute key\n  const Attribute *getAttribute(int key) const {\n    auto *actual = getActual();\n\n    auto it = actual->attributes.find(key);\n    return it != actual->attributes.end() ? it->second.get() : nullptr;\n  }\n  /// Gets the appropriate attribute.\n  /// @tparam AttributeType the return type\n  template <typename AttributeType> AttributeType *getAttribute() {\n    return static_cast<AttributeType *>(getAttribute(AttributeType::AttributeID));\n  }\n  /// Gets the appropriate attribute.\n  /// @tparam AttributeType the return type\n  template <typename AttributeType> const AttributeType *getAttribute() const {\n    return static_cast<const AttributeType *>(getAttribute(AttributeType::AttributeID));\n  }\n  template <typename AttributeType> AttributeType *getAttribute(int key) {\n    return static_cast<AttributeType *>(getAttribute(key));\n  }\n  template <typename AttributeType> const AttributeType *getAttribute(int key) const {\n    return static_cast<const AttributeType *>(getAttribute(key));\n  }\n  void eraseAttribute(int key) { attributes.erase(key); }\n  void cloneAttributesFrom(Node *n) { attributes = codon::clone(n->attributes); }\n\n  /// @return iterator to the first attribute\n  auto attributes_begin() {\n    return util::map_key_adaptor(getActual()->attributes.begin());\n  }\n  /// @return iterator beyond the last attribute\n  auto attributes_end() { return util::map_key_adaptor(getActual()->attributes.end()); }\n  /// @return iterator to the first attribute\n  auto attributes_begin() const {\n    return util::const_map_key_adaptor(getActual()->attributes.begin());\n  }\n  /// @return iterator beyond the last attribute\n  auto attributes_end() const {\n    return util::const_map_key_adaptor(getActual()->attributes.end());\n  }\n\n  /// Helper to add source information.\n  /// @param the source information\n  void setSrcInfo(codon::SrcInfo s) {\n    setAttribute(std::make_unique<SrcInfoAttribute>(std::move(s)));\n  }\n  /// @return the src info\n  codon::SrcInfo getSrcInfo() const {\n    auto a = getAttribute<SrcInfoAttribute>();\n    return a ? a->info : codon::SrcInfo();\n  }\n\n  /// @return a text representation of a reference to the object\n  virtual std::string referenceString() const { return getActual()->name; }\n\n  /// @return the IR module\n  Module *getModule() const { return getActual()->module; }\n  /// Sets the module.\n  /// @param m the new module\n  void setModule(Module *m) { getActual()->module = m; }\n\n  friend std::ostream &operator<<(std::ostream &os, const Node &a);\n\n  bool hasReplacement() const { return replacement != nullptr; }\n\n  /// @return a vector of all the node's children\n  virtual std::vector<Value *> getUsedValues() { return {}; }\n  /// @return a vector of all the node's children\n  virtual std::vector<const Value *> getUsedValues() const { return {}; }\n  /// Physically replaces all instances of a child value.\n  /// @param id the id of the value to be replaced\n  /// @param newValue the new value\n  /// @return number of replacements\n  virtual int replaceUsedValue(id_t id, Value *newValue) { return 0; }\n  /// Physically replaces all instances of a child value.\n  /// @param oldValue the old value\n  /// @param newValue the new value\n  /// @return number of replacements\n  int replaceUsedValue(Value *old, Value *newValue);\n\n  /// @return a vector of all the node's used types\n  virtual std::vector<types::Type *> getUsedTypes() const { return {}; }\n  /// Physically replaces all instances of a used type.\n  /// @param name the name of the type being replaced\n  /// @param newType the new type\n  /// @return number of replacements\n  virtual int replaceUsedType(const std::string &name, types::Type *newType) {\n    return 0;\n  }\n  /// Physically replaces all instances of a used type.\n  /// @param old the old type\n  /// @param newType the new type\n  /// @return number of replacements\n  int replaceUsedType(types::Type *old, types::Type *newType);\n\n  /// @return a vector of all the node's used variables\n  virtual std::vector<Var *> getUsedVariables() { return {}; }\n  /// @return a vector of all the node's used variables\n  virtual std::vector<const Var *> getUsedVariables() const { return {}; }\n  /// Physically replaces all instances of a used variable.\n  /// @param id the id of the variable\n  /// @param newType the new type\n  /// @return number of replacements\n  virtual int replaceUsedVariable(id_t id, Var *newVar) { return 0; }\n  /// Physically replaces all instances of a used variable.\n  /// @param old the old variable\n  /// @param newVar the new variable\n  /// @return number of replacements\n  int replaceUsedVariable(Var *old, Var *newVar);\n\n  template <typename, typename> friend class AcceptorExtend;\n  template <typename> friend class ReplaceableNodeBase;\n\nprivate:\n  Node *getActual() { return replacement ? replacement->getActual() : this; }\n  const Node *getActual() const {\n    return replacement ? replacement->getActual() : this;\n  }\n};\n\ntemplate <typename Derived, typename Parent> class AcceptorExtend : public Parent {\npublic:\n  using Parent::Parent;\n\n  /// See LLVM documentation.\n  static const void *nodeId() { return &Derived::NodeId; }\n  /// See LLVM documentation.\n  const void *dynamicNodeId() const override { return &Derived::NodeId; }\n  /// See LLVM documentation.\n  virtual bool isConvertible(const void *other) const override {\n    if (Node::hasReplacement())\n      return Node::getActual()->isConvertible(other);\n\n    return other == nodeId() || Parent::isConvertible(other);\n  }\n\n  void accept(util::Visitor &v) override {\n    if (Node::hasReplacement())\n      Node::getActual()->accept(v);\n    else\n      v.visit(static_cast<Derived *>(this));\n  }\n\n  void accept(util::ConstVisitor &v) const override {\n    if (Node::hasReplacement())\n      Node::getActual()->accept(v);\n    else\n      v.visit(static_cast<const Derived *>(this));\n  }\n};\n\ntemplate <typename Derived>\nclass ReplaceableNodeBase : public AcceptorExtend<Derived, Node> {\nprivate:\n  /// true if the node can be lazily replaced\n  bool replaceable = true;\n\npublic:\n  using AcceptorExtend<Derived, Node>::AcceptorExtend;\n\n  static const char NodeId;\n\n  /// @return the logical value of the node\n  Derived *getActual() {\n    return Node::replacement ? static_cast<Derived *>(Node::replacement)->getActual()\n                             : static_cast<Derived *>(this);\n  }\n\n  /// @return the logical value of the node\n  const Derived *getActual() const {\n    return Node::replacement\n               ? static_cast<const Derived *>(Node::replacement)->getActual()\n               : static_cast<const Derived *>(this);\n  }\n\n  /// Lazily replaces all instances of the node.\n  /// @param v the new value\n  void replaceAll(Derived *v) {\n    seqassertn(replaceable, \"node {} not replaceable\", *v);\n    Node::replacement = v;\n  }\n\n  /// @return true if the object can be replaced\n  bool isReplaceable() const { return replaceable; }\n  /// Sets the object's replaceable flag.\n  /// @param v the new value\n  void setReplaceable(bool v = true) { replaceable = v; }\n};\n\ntemplate <typename Derived> const char ReplaceableNodeBase<Derived>::NodeId = 0;\n\ntemplate <typename Desired> Desired *cast(Node *other) {\n  return other != nullptr ? other->as<Desired>() : nullptr;\n}\n\ntemplate <typename Desired> const Desired *cast(const Node *other) {\n  return other != nullptr ? other->as<Desired>() : nullptr;\n}\n\ntemplate <typename Desired> bool isA(Node *other) {\n  return other && other->is<Desired>();\n}\n\ntemplate <typename Desired> bool isA(const Node *other) {\n  return other && other->is<Desired>();\n}\n\n} // namespace ir\n} // namespace codon\n\ntemplate <> struct fmt::formatter<codon::ir::Node> : fmt::ostream_formatter {};\n"
  },
  {
    "path": "codon/cir/cir.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/const.h\"\n#include \"codon/cir/dsl/nodes.h\"\n#include \"codon/cir/flow.h\"\n#include \"codon/cir/func.h\"\n#include \"codon/cir/instr.h\"\n#include \"codon/cir/module.h\"\n#include \"codon/cir/types/types.h\"\n#include \"codon/cir/value.h\"\n#include \"codon/cir/var.h\"\n"
  },
  {
    "path": "codon/cir/const.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"const.h\"\n\nnamespace codon {\nnamespace ir {\n\nconst char Const::NodeId = 0;\n\nint Const::doReplaceUsedType(const std::string &name, types::Type *newType) {\n  if (type->getName() == name) {\n    type = newType;\n    return 1;\n  }\n  return 0;\n}\n\nconst char TemplatedConst<std::string>::NodeId = 0;\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/const.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/module.h\"\n#include \"codon/cir/value.h\"\n\nnamespace codon {\nnamespace ir {\n\n/// CIR constant base. Once created, constants are immutable.\nclass Const : public AcceptorExtend<Const, Value> {\nprivate:\n  /// the type\n  types::Type *type;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a constant.\n  /// @param type the type\n  /// @param name the name\n  explicit Const(types::Type *type, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), type(type) {}\n\nprivate:\n  types::Type *doGetType() const override { return type; }\n\n  std::vector<types::Type *> doGetUsedTypes() const override { return {type}; }\n  int doReplaceUsedType(const std::string &name, types::Type *newType) override;\n};\n\ntemplate <typename ValueType>\nclass TemplatedConst : public AcceptorExtend<TemplatedConst<ValueType>, Const> {\nprivate:\n  ValueType val;\n\npublic:\n  static const char NodeId;\n\n  using AcceptorExtend<TemplatedConst<ValueType>, Const>::getModule;\n  using AcceptorExtend<TemplatedConst<ValueType>, Const>::getSrcInfo;\n  using AcceptorExtend<TemplatedConst<ValueType>, Const>::getType;\n\n  TemplatedConst(ValueType v, types::Type *type, std::string name = \"\")\n      : AcceptorExtend<TemplatedConst<ValueType>, Const>(type, std::move(name)),\n        val(v) {}\n\n  /// @return the internal value.\n  ValueType getVal() const { return val; }\n  /// Sets the value.\n  /// @param v the value\n  void setVal(ValueType v) { val = v; }\n};\n\nusing IntConst = TemplatedConst<int64_t>;\nusing FloatConst = TemplatedConst<double>;\nusing BoolConst = TemplatedConst<bool>;\nusing StringConst = TemplatedConst<std::string>;\n\ntemplate <typename T> const char TemplatedConst<T>::NodeId = 0;\n\ntemplate <>\nclass TemplatedConst<std::string>\n    : public AcceptorExtend<TemplatedConst<std::string>, Const> {\nprivate:\n  std::string val;\n\npublic:\n  static const char NodeId;\n\n  TemplatedConst(std::string v, types::Type *type, std::string name = \"\")\n      : AcceptorExtend(type, std::move(name)), val(std::move(v)) {}\n\n  /// @return the internal value.\n  std::string getVal() const { return val; }\n  /// Sets the value.\n  /// @param v the value\n  void setVal(std::string v) { val = std::move(v); }\n};\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/dsl/codegen.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <unordered_map>\n\n#include \"codon/cir/llvm/llvm.h\"\n#include \"codon/cir/types/types.h\"\n\nnamespace codon {\nnamespace ir {\n\nnamespace analyze {\nnamespace dataflow {\nclass CFVisitor;\n} // namespace dataflow\n} // namespace analyze\n\nclass LLVMVisitor;\n\nnamespace dsl {\nnamespace codegen {\n\n/// Builder for LLVM types.\nstruct TypeBuilder {\n  virtual ~TypeBuilder() noexcept = default;\n\n  /// Construct the LLVM type.\n  /// @param the LLVM visitor\n  /// @return the LLVM type\n  virtual llvm::Type *buildType(LLVMVisitor *visitor) = 0;\n  /// Construct the LLVM debug type.\n  /// @param the LLVM visitor\n  /// @return the LLVM debug type\n  virtual llvm::DIType *buildDebugType(LLVMVisitor *visitor) = 0;\n};\n\n/// Builder for LLVM values.\nstruct ValueBuilder {\n  virtual ~ValueBuilder() noexcept = default;\n\n  /// Construct the LLVM value.\n  /// @param the LLVM visitor\n  /// @return the LLVM value\n  virtual llvm::Value *buildValue(LLVMVisitor *visitor) = 0;\n};\n\n/// Builder for control flow graphs.\nstruct CFBuilder {\n  virtual ~CFBuilder() noexcept = default;\n\n  /// Construct the control-flow nodes.\n  /// @param graph the graph\n  virtual void buildCFNodes(analyze::dataflow::CFVisitor *visitor) = 0;\n};\n\n} // namespace codegen\n} // namespace dsl\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/dsl/nodes.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"nodes.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace dsl {\n\nnamespace types {\nconst char CustomType::NodeId = 0;\n}\n\nconst char CustomConst::NodeId = 0;\n\nconst char CustomFlow::NodeId = 0;\n\nconst char CustomInstr::NodeId = 0;\n\n} // namespace dsl\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/dsl/nodes.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n\n#include \"codon/cir/base.h\"\n#include \"codon/cir/const.h\"\n#include \"codon/cir/instr.h\"\n#include \"codon/cir/util/side_effect.h\"\n\nnamespace codon {\nnamespace ir {\n\nnamespace util {\nclass CloneVisitor;\n} // namespace util\n\nnamespace dsl {\n\nnamespace codegen {\nstruct CFBuilder;\nstruct TypeBuilder;\nstruct ValueBuilder;\n} // namespace codegen\n\nnamespace types {\n\n/// DSL type.\nclass CustomType : public AcceptorExtend<CustomType, ir::types::Type> {\npublic:\n  static const char NodeId;\n\n  using AcceptorExtend::AcceptorExtend;\n\n  /// @return the type builder\n  virtual std::unique_ptr<codegen::TypeBuilder> getBuilder() const = 0;\n\n  /// Compares DSL nodes.\n  /// @param v the other node\n  /// @return true if they match\n  virtual bool match(const Type *v) const = 0;\n\n  /// Format the DSL node.\n  /// @param os the output stream\n  virtual std::ostream &doFormat(std::ostream &os) const = 0;\n};\n\n} // namespace types\n\n/// DSL constant.\nclass CustomConst : public AcceptorExtend<CustomConst, Const> {\npublic:\n  static const char NodeId;\n\n  using AcceptorExtend::AcceptorExtend;\n\n  /// @return the value builder\n  virtual std::unique_ptr<codegen::ValueBuilder> getBuilder() const = 0;\n  /// Compares DSL nodes.\n  /// @param v the other node\n  /// @return true if they match\n  virtual bool match(const Value *v) const = 0;\n  /// Clones the value.\n  /// @param cv the clone visitor\n  /// @return a clone of the object\n  virtual Value *doClone(util::CloneVisitor &cv) const = 0;\n\n  /// Format the DSL node.\n  /// @param os the output stream\n  virtual std::ostream &doFormat(std::ostream &os) const = 0;\n};\n\n/// DSL flow.\nclass CustomFlow : public AcceptorExtend<CustomFlow, Flow> {\npublic:\n  static const char NodeId;\n\n  using AcceptorExtend::AcceptorExtend;\n\n  /// @return the value builder\n  virtual std::unique_ptr<codegen::ValueBuilder> getBuilder() const = 0;\n  /// Compares DSL nodes.\n  /// @param v the other node\n  /// @return true if they match\n  virtual bool match(const Value *v) const = 0;\n  /// Clones the value.\n  /// @param cv the clone visitor\n  /// @return a clone of the object\n  virtual Value *doClone(util::CloneVisitor &cv) const = 0;\n  /// @return the control-flow builder\n  virtual std::unique_ptr<codegen::CFBuilder> getCFBuilder() const = 0;\n  /// Query this custom node for its side effect properties. If \"local\"\n  /// is true, then the return value should reflect this node and this\n  /// node alone, otherwise the value should reflect functions containing\n  /// this node in their bodies. For example, a \"break\" instruction has\n  /// side effects locally, but functions containing \"break\" might still\n  /// be side effect free, hence the distinction.\n  /// @param local true if result should reflect only this node\n  /// @return this node's side effect status\n  virtual util::SideEffectStatus getSideEffectStatus(bool local = true) const {\n    return util::SideEffectStatus::UNKNOWN;\n  }\n\n  /// Format the DSL node.\n  /// @param os the output stream\n  virtual std::ostream &doFormat(std::ostream &os) const = 0;\n};\n\n/// DSL instruction.\nclass CustomInstr : public AcceptorExtend<CustomInstr, Instr> {\npublic:\n  static const char NodeId;\n\n  using AcceptorExtend::AcceptorExtend;\n\n  /// @return the value builder\n  virtual std::unique_ptr<codegen::ValueBuilder> getBuilder() const = 0;\n  /// Compares DSL nodes.\n  /// @param v the other node\n  /// @return true if they match\n  virtual bool match(const Value *v) const = 0;\n  /// Clones the value.\n  /// @param cv the clone visitor\n  /// @return a clone of the object\n  virtual Value *doClone(util::CloneVisitor &cv) const = 0;\n  /// @return the control-flow builder\n  virtual std::unique_ptr<codegen::CFBuilder> getCFBuilder() const = 0;\n  /// Query this custom node for its side effect properties. If \"local\"\n  /// is true, then the return value should reflect this node and this\n  /// node alone, otherwise the value should reflect functions containing\n  /// this node in their bodies. For example, a \"break\" instruction has\n  /// side effects locally, but functions containing \"break\" might still\n  /// be side effect free, hence the distinction.\n  /// @param local true if result should reflect only this node\n  /// @return this node's side effect status\n  virtual util::SideEffectStatus getSideEffectStatus(bool local = true) const {\n    return util::SideEffectStatus::UNKNOWN;\n  }\n\n  /// Format the DSL node.\n  /// @param os the output stream\n  virtual std::ostream &doFormat(std::ostream &os) const = 0;\n};\n\n} // namespace dsl\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/flow.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"flow.h\"\n\n#include \"codon/cir/module.h\"\n#include \"codon/cir/util/iterators.h\"\n#include <fmt/ostream.h>\n\nnamespace codon {\nnamespace ir {\nnamespace {\nint findAndReplace(id_t id, codon::ir::Value *newVal,\n                   std::list<codon::ir::Value *> &values) {\n  auto replacements = 0;\n  for (auto &value : values) {\n    if (value->getId() == id) {\n      value = newVal;\n      ++replacements;\n    }\n  }\n  return replacements;\n}\n} // namespace\n\nconst char Flow::NodeId = 0;\n\ntypes::Type *Flow::doGetType() const { return getModule()->getNoneType(); }\n\nconst char SeriesFlow::NodeId = 0;\n\nint SeriesFlow::doReplaceUsedValue(id_t id, Value *newValue) {\n  return findAndReplace(id, newValue, series);\n}\n\nconst char WhileFlow::NodeId = 0;\n\nint WhileFlow::doReplaceUsedValue(id_t id, Value *newValue) {\n  auto replacements = 0;\n\n  if (cond->getId() == id) {\n    cond = newValue;\n    ++replacements;\n  }\n  if (body->getId() == id) {\n    auto *f = cast<Flow>(newValue);\n    seqassert(f, \"{} is not a flow\", *newValue);\n    body = f;\n    ++replacements;\n  }\n  return replacements;\n}\n\nconst char ForFlow::NodeId = 0;\n\nstd::vector<Value *> ForFlow::doGetUsedValues() const {\n  std::vector<Value *> ret;\n  if (isParallel())\n    ret = getSchedule()->getUsedValues();\n  ret.push_back(iter);\n  ret.push_back(body);\n  return ret;\n}\n\nint ForFlow::doReplaceUsedValue(id_t id, Value *newValue) {\n  auto count = 0;\n  if (isParallel())\n    count += getSchedule()->replaceUsedValue(id, newValue);\n  if (iter->getId() == id) {\n    iter = newValue;\n    ++count;\n  }\n  if (body->getId() == id) {\n    auto *f = cast<Flow>(newValue);\n    seqassert(f, \"{} is not a flow\", *newValue);\n    body = f;\n    ++count;\n  }\n  return count;\n}\n\nint ForFlow::doReplaceUsedVariable(id_t id, Var *newVar) {\n  if (var->getId() == id) {\n    var = newVar;\n    return 1;\n  }\n  return 0;\n}\n\nconst char ImperativeForFlow::NodeId = 0;\n\nstd::vector<Value *> ImperativeForFlow::doGetUsedValues() const {\n  std::vector<Value *> ret;\n  if (isParallel())\n    ret = getSchedule()->getUsedValues();\n  ret.push_back(start);\n  ret.push_back(end);\n  ret.push_back(body);\n  return ret;\n}\n\nint ImperativeForFlow::doReplaceUsedValue(id_t id, Value *newValue) {\n  auto count = 0;\n  if (isParallel())\n    count += getSchedule()->replaceUsedValue(id, newValue);\n  if (body->getId() == id) {\n    auto *f = cast<Flow>(newValue);\n    seqassert(f, \"{} is not a flow\", *newValue);\n    body = f;\n    ++count;\n  }\n  if (start->getId() == id) {\n    start = newValue;\n    ++count;\n  }\n  if (end->getId() == id) {\n    end = newValue;\n    ++count;\n  }\n  return count;\n}\n\nint ImperativeForFlow::doReplaceUsedVariable(id_t id, Var *newVar) {\n  if (var->getId() == id) {\n    var = newVar;\n    return 1;\n  }\n  return 0;\n}\n\nconst char IfFlow::NodeId = 0;\n\nstd::vector<Value *> IfFlow::doGetUsedValues() const {\n  std::vector<Value *> ret = {cond, trueBranch};\n  if (falseBranch)\n    ret.push_back(falseBranch);\n  return ret;\n}\n\nint IfFlow::doReplaceUsedValue(id_t id, Value *newValue) {\n  auto replacements = 0;\n\n  if (cond->getId() == id) {\n    cond = newValue;\n    ++replacements;\n  }\n  if (trueBranch->getId() == id) {\n    auto *f = cast<Flow>(newValue);\n    seqassert(f, \"{} is not a flow\", *newValue);\n    trueBranch = f;\n    ++replacements;\n  }\n  if (falseBranch && falseBranch->getId() == id) {\n    auto *f = cast<Flow>(newValue);\n    seqassert(f, \"{} is not a flow\", *newValue);\n    falseBranch = f;\n    ++replacements;\n  }\n\n  return replacements;\n}\n\nconst char TryCatchFlow::NodeId = 0;\n\nstd::vector<Value *> TryCatchFlow::doGetUsedValues() const {\n  std::vector<Value *> ret = {body};\n  if (else_)\n    ret.push_back(else_);\n  if (finally)\n    ret.push_back(finally);\n\n  for (auto &c : catches)\n    ret.push_back(const_cast<Value *>(static_cast<const Value *>(c.getHandler())));\n  return ret;\n}\n\nint TryCatchFlow::doReplaceUsedValue(id_t id, Value *newValue) {\n  auto replacements = 0;\n\n  if (body->getId() == id) {\n    auto *f = cast<Flow>(newValue);\n    seqassert(f, \"{} is not a flow\", *newValue);\n    body = f;\n    ++replacements;\n  }\n  if (else_ && else_->getId() == id) {\n    auto *f = cast<Flow>(newValue);\n    seqassert(f, \"{} is not a flow\", *newValue);\n    else_ = f;\n    ++replacements;\n  }\n  if (finally && finally->getId() == id) {\n    auto *f = cast<Flow>(newValue);\n    seqassert(f, \"{} is not a flow\", *newValue);\n    finally = f;\n    ++replacements;\n  }\n\n  for (auto &c : catches) {\n    if (c.getHandler()->getId() == id) {\n      auto *f = cast<Flow>(newValue);\n      seqassert(f, \"{} is not a flow\", *newValue);\n      c.setHandler(f);\n      ++replacements;\n    }\n  }\n\n  return replacements;\n}\n\nstd::vector<types::Type *> TryCatchFlow::doGetUsedTypes() const {\n  std::vector<types::Type *> ret;\n  for (auto &c : catches) {\n    if (auto *t = c.getType())\n      ret.push_back(const_cast<types::Type *>(t));\n  }\n  return ret;\n}\n\nint TryCatchFlow::doReplaceUsedType(const std::string &name, types::Type *newType) {\n  auto count = 0;\n  for (auto &c : catches) {\n    if (c.getType()->getName() == name) {\n      c.setType(newType);\n      ++count;\n    }\n  }\n  return count;\n}\n\nstd::vector<Var *> TryCatchFlow::doGetUsedVariables() const {\n  std::vector<Var *> ret;\n  for (auto &c : catches) {\n    if (auto *t = c.getVar())\n      ret.push_back(const_cast<Var *>(t));\n  }\n  return ret;\n}\n\nint TryCatchFlow::doReplaceUsedVariable(id_t id, Var *newVar) {\n  auto count = 0;\n  for (auto &c : catches) {\n    if (c.getVar()->getId() == id) {\n      c.setVar(newVar);\n      ++count;\n    }\n  }\n  return count;\n}\n\nconst char PipelineFlow::NodeId = 0;\n\ntypes::Type *PipelineFlow::Stage::getOutputType() const {\n  if (args.empty()) {\n    return callee->getType();\n  } else {\n    auto *funcType = cast<types::FuncType>(callee->getType());\n    seqassertn(funcType, \"{} is not a function type\", *callee->getType());\n    return funcType->getReturnType();\n  }\n}\n\ntypes::Type *PipelineFlow::Stage::getOutputElementType() const {\n  if (isGenerator()) {\n    types::GeneratorType *genType = nullptr;\n    if (args.empty()) {\n      genType = cast<types::GeneratorType>(callee->getType());\n      return genType->getBase();\n    } else {\n      auto *funcType = cast<types::FuncType>(callee->getType());\n      seqassertn(funcType, \"{} is not a function type\", *callee->getType());\n      genType = cast<types::GeneratorType>(funcType->getReturnType());\n    }\n    seqassertn(genType, \"generator type not found\");\n    return genType->getBase();\n  } else if (args.empty()) {\n    return callee->getType();\n  } else {\n    auto *funcType = cast<types::FuncType>(callee->getType());\n    seqassertn(funcType, \"{} is not a function type\", *callee->getType());\n    return funcType->getReturnType();\n  }\n}\n\nstd::vector<Value *> PipelineFlow::doGetUsedValues() const {\n  std::vector<Value *> ret;\n  for (auto &s : stages) {\n    ret.push_back(const_cast<Value *>(s.getCallee()));\n    for (auto *arg : s.args)\n      if (arg)\n        ret.push_back(arg);\n  }\n  return ret;\n}\n\nint PipelineFlow::doReplaceUsedValue(id_t id, Value *newValue) {\n  auto replacements = 0;\n\n  for (auto &c : stages) {\n    if (c.getCallee()->getId() == id) {\n      c.setCallee(newValue);\n      ++replacements;\n    }\n    for (auto &s : c.args)\n      if (s && s->getId() == id) {\n        s = newValue;\n        ++replacements;\n      }\n  }\n\n  return replacements;\n}\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/flow.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <list>\n#include <vector>\n\n#include \"codon/cir/base.h\"\n#include \"codon/cir/transform/parallel/schedule.h\"\n#include \"codon/cir/value.h\"\n#include \"codon/cir/var.h\"\n\nnamespace codon {\nnamespace ir {\n\n/// Base for flows, which represent control flow.\nclass Flow : public AcceptorExtend<Flow, Value> {\npublic:\n  static const char NodeId;\n\n  using AcceptorExtend::AcceptorExtend;\n\nprotected:\n  types::Type *doGetType() const final;\n};\n\n/// Flow that contains a series of flows or instructions.\nclass SeriesFlow : public AcceptorExtend<SeriesFlow, Flow> {\nprivate:\n  std::list<Value *> series;\n\npublic:\n  static const char NodeId;\n\n  using AcceptorExtend::AcceptorExtend;\n\n  /// @return an iterator to the first instruction/flow\n  auto begin() { return series.begin(); }\n  /// @return an iterator beyond the last instruction/flow\n  auto end() { return series.end(); }\n  /// @return an iterator to the first instruction/flow\n  auto begin() const { return series.begin(); }\n  /// @return an iterator beyond the last instruction/flow\n  auto end() const { return series.end(); }\n\n  /// @return a pointer to the first instruction/flow\n  Value *front() { return series.front(); }\n  /// @return a pointer to the last instruction/flow\n  Value *back() { return series.back(); }\n  /// @return a pointer to the first instruction/flow\n  const Value *front() const { return series.front(); }\n  /// @return a pointer to the last instruction/flow\n  const Value *back() const { return series.back(); }\n\n  /// Inserts an instruction/flow at the given position.\n  /// @param pos the position\n  /// @param v the flow or instruction\n  /// @return an iterator to the newly added instruction/flow\n  template <typename It> auto insert(It pos, Value *v) { return series.insert(pos, v); }\n  /// Appends an instruction/flow.\n  /// @param f the flow or instruction\n  void push_back(Value *f) { series.push_back(f); }\n\n  /// Erases the item at the supplied position.\n  /// @param pos the position\n  /// @return the iterator beyond the removed flow or instruction\n  template <typename It> auto erase(It pos) { return series.erase(pos); }\n\nprotected:\n  std::vector<Value *> doGetUsedValues() const override {\n    return std::vector<Value *>(series.begin(), series.end());\n  }\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n};\n\n/// Flow representing a while loop.\nclass WhileFlow : public AcceptorExtend<WhileFlow, Flow> {\nprivate:\n  /// the condition\n  Value *cond;\n  /// the body\n  Value *body;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a while loop.\n  /// @param cond the condition\n  /// @param body the body\n  /// @param name the flow's name\n  WhileFlow(Value *cond, Flow *body, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), cond(cond), body(body) {}\n\n  /// @return the condition\n  Value *getCond() { return cond; }\n  /// @return the condition\n  const Value *getCond() const { return cond; }\n  /// Sets the condition.\n  /// @param c the new condition\n  void setCond(Value *c) { cond = c; }\n\n  /// @return the body\n  Flow *getBody() { return cast<Flow>(body); }\n  /// @return the body\n  const Flow *getBody() const { return cast<Flow>(body); }\n  /// Sets the body.\n  /// @param f the new value\n  void setBody(Flow *f) { body = f; }\n\nprotected:\n  std::vector<Value *> doGetUsedValues() const override { return {cond, body}; }\n\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n};\n\n/// Flow representing a for loop.\nclass ForFlow : public AcceptorExtend<ForFlow, Flow> {\nprivate:\n  /// the iterator\n  Value *iter;\n  /// the body\n  Value *body;\n  /// the variable\n  Var *var;\n  /// parallel loop schedule, or null if none\n  std::unique_ptr<transform::parallel::OMPSched> schedule;\n  /// true if loop is async\n  bool async;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a for loop.\n  /// @param iter the iterator\n  /// @param body the body\n  /// @param var the variable\n  /// @param schedule the parallel schedule\n  /// @param async true if loop is async\n  /// @param name the flow's name\n  ForFlow(Value *iter, Flow *body, Var *var,\n          std::unique_ptr<transform::parallel::OMPSched> schedule = {},\n          bool async = false, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), iter(iter), body(body), var(var),\n        schedule(std::move(schedule)), async(async) {}\n\n  /// @return the iter\n  Value *getIter() { return iter; }\n  /// @return the iter\n  const Value *getIter() const { return iter; }\n  /// Sets the iter.\n  /// @param f the new iter\n  void setIter(Value *f) { iter = f; }\n\n  /// @return the body\n  Flow *getBody() { return cast<Flow>(body); }\n  /// @return the body\n  const Flow *getBody() const { return cast<Flow>(body); }\n  /// Sets the body.\n  /// @param f the new body\n  void setBody(Flow *f) { body = f; }\n\n  /// @return the var\n  Var *getVar() { return var; }\n  /// @return the var\n  const Var *getVar() const { return var; }\n  /// Sets the var.\n  /// @param c the new var\n  void setVar(Var *c) { var = c; }\n\n  /// @return true if parallel\n  bool isParallel() const { return bool(schedule); }\n  /// Sets parallel status.\n  /// @param a true if parallel\n  void setParallel(bool a = true) {\n    if (a)\n      schedule = std::make_unique<transform::parallel::OMPSched>();\n    else\n      schedule = std::unique_ptr<transform::parallel::OMPSched>();\n  }\n\n  /// @return the parallel loop schedule, or null if none\n  transform::parallel::OMPSched *getSchedule() { return schedule.get(); }\n  /// @return the parallel loop schedule, or null if none\n  const transform::parallel::OMPSched *getSchedule() const { return schedule.get(); }\n  /// Sets the parallel loop schedule\n  /// @param s the schedule string (e.g. OpenMP pragma)\n  void setSchedule(std::unique_ptr<transform::parallel::OMPSched> s) {\n    schedule = std::move(s);\n  }\n\n  /// @return true if async\n  bool isAsync() const { return async; }\n  /// Sets async status.\n  /// @param a true if async\n  void setAsync(bool a = true) { async = a; }\n\nprotected:\n  std::vector<Value *> doGetUsedValues() const override;\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n\n  std::vector<Var *> doGetUsedVariables() const override { return {var}; }\n  int doReplaceUsedVariable(id_t id, Var *newVar) override;\n};\n\n/// Flow representing an imperative for loop.\nclass ImperativeForFlow : public AcceptorExtend<ImperativeForFlow, Flow> {\nprivate:\n  /// the initial value\n  Value *start;\n  /// the step value\n  int64_t step;\n  /// the end value\n  Value *end;\n  /// the body\n  Value *body;\n  /// the variable, must be integer type\n  Var *var;\n  /// parallel loop schedule, or null if none\n  std::unique_ptr<transform::parallel::OMPSched> schedule;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs an imperative for loop.\n  /// @param body the body\n  /// @param start the start value\n  /// @param step the step value\n  /// @param end the end value\n  /// @param var the end variable, must be integer\n  /// @param name the flow's name\n  ImperativeForFlow(Value *start, int64_t step, Value *end, Flow *body, Var *var,\n                    std::unique_ptr<transform::parallel::OMPSched> schedule = {},\n                    std::string name = \"\")\n      : AcceptorExtend(std::move(name)), start(start), step(step), end(end), body(body),\n        var(var), schedule(std::move(schedule)) {}\n\n  /// @return the start value\n  Value *getStart() const { return start; }\n  /// Sets the start value.\n  /// @param v the new value\n  void setStart(Value *val) { start = val; }\n\n  /// @return the step value\n  int64_t getStep() const { return step; }\n  /// Sets the step value.\n  /// @param v the new value\n  void setStep(int64_t val) { step = val; }\n\n  /// @return the end value\n  Value *getEnd() const { return end; }\n  /// Sets the end value.\n  /// @param v the new value\n  void setEnd(Value *val) { end = val; }\n\n  /// @return the body\n  Flow *getBody() { return cast<Flow>(body); }\n  /// @return the body\n  const Flow *getBody() const { return cast<Flow>(body); }\n  /// Sets the body.\n  /// @param f the new body\n  void setBody(Flow *f) { body = f; }\n\n  /// @return the var\n  Var *getVar() { return var; }\n  /// @return the var\n  const Var *getVar() const { return var; }\n  /// Sets the var.\n  /// @param c the new var\n  void setVar(Var *c) { var = c; }\n\n  /// @return true if parallel\n  bool isParallel() const { return bool(schedule); }\n  /// Sets parallel status.\n  /// @param a true if parallel\n  void setParallel(bool a = true) {\n    if (a)\n      schedule = std::make_unique<transform::parallel::OMPSched>();\n    else\n      schedule = std::unique_ptr<transform::parallel::OMPSched>();\n  }\n\n  /// @return the parallel loop schedule, or null if none\n  transform::parallel::OMPSched *getSchedule() { return schedule.get(); }\n  /// @return the parallel loop schedule, or null if none\n  const transform::parallel::OMPSched *getSchedule() const { return schedule.get(); }\n  /// Sets the parallel loop schedule\n  /// @param s the schedule string (e.g. OpenMP pragma)\n  void setSchedule(std::unique_ptr<transform::parallel::OMPSched> s) {\n    schedule = std::move(s);\n  }\n\nprotected:\n  std::vector<Value *> doGetUsedValues() const override;\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n\n  std::vector<Var *> doGetUsedVariables() const override { return {var}; }\n  int doReplaceUsedVariable(id_t id, Var *newVar) override;\n};\n\n/// Flow representing an if statement.\nclass IfFlow : public AcceptorExtend<IfFlow, Flow> {\nprivate:\n  /// the condition\n  Value *cond;\n  /// the true branch\n  Value *trueBranch;\n  /// the false branch\n  Value *falseBranch;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs an if.\n  /// @param cond the condition\n  /// @param trueBranch the true branch\n  /// @param falseBranch the false branch\n  /// @param name the flow's name\n  IfFlow(Value *cond, Flow *trueBranch, Flow *falseBranch = nullptr,\n         std::string name = \"\")\n      : AcceptorExtend(std::move(name)), cond(cond), trueBranch(trueBranch),\n        falseBranch(falseBranch) {}\n\n  /// @return the true branch\n  Flow *getTrueBranch() { return cast<Flow>(trueBranch); }\n  /// @return the true branch\n  const Flow *getTrueBranch() const { return cast<Flow>(trueBranch); }\n  /// Sets the true branch.\n  /// @param f the new true branch\n  void setTrueBranch(Flow *f) { trueBranch = f; }\n\n  /// @return the false branch\n  Flow *getFalseBranch() { return cast<Flow>(falseBranch); }\n  /// @return the false branch\n  const Flow *getFalseBranch() const { return cast<Flow>(falseBranch); }\n  /// Sets the false.\n  /// @param f the new false\n  void setFalseBranch(Flow *f) { falseBranch = f; }\n\n  /// @return the condition\n  Value *getCond() { return cond; }\n  /// @return the condition\n  const Value *getCond() const { return cond; }\n  /// Sets the condition.\n  /// @param c the new condition\n  void setCond(Value *c) { cond = c; }\n\nprotected:\n  std::vector<Value *> doGetUsedValues() const override;\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n};\n\n/// Flow representing a try-catch statement.\nclass TryCatchFlow : public AcceptorExtend<TryCatchFlow, Flow> {\npublic:\n  /// Class representing a catch clause.\n  class Catch {\n  private:\n    /// the handler\n    Value *handler;\n    /// the catch type, may be nullptr\n    types::Type *type;\n    /// the catch variable, may be nullptr\n    Var *catchVar;\n\n  public:\n    explicit Catch(Flow *handler, types::Type *type = nullptr, Var *catchVar = nullptr)\n        : handler(handler), type(type), catchVar(catchVar) {}\n\n    /// @return the handler\n    Flow *getHandler() { return cast<Flow>(handler); }\n    /// @return the handler\n    const Flow *getHandler() const { return cast<Flow>(handler); }\n    /// Sets the handler.\n    /// @param h the new value\n    void setHandler(Flow *h) { handler = h; }\n\n    /// @return the catch type, may be nullptr\n    types::Type *getType() const { return type; }\n    /// Sets the catch type.\n    /// @param t the new type, nullptr for catch all\n    void setType(types::Type *t) { type = t; }\n\n    /// @return the variable, may be nullptr\n    Var *getVar() { return catchVar; }\n    /// @return the variable, may be nullptr\n    const Var *getVar() const { return catchVar; }\n    /// Sets the variable.\n    /// @param v the new value, may be nullptr\n    void setVar(Var *v) { catchVar = v; }\n  };\n\nprivate:\n  /// the catch clauses\n  std::list<Catch> catches;\n\n  /// the body\n  Value *body;\n  /// the else block, may be nullptr\n  Value *else_;\n  /// the finally, may be nullptr\n  Value *finally;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs an try-catch.\n  /// @param name the's name\n  /// @param body the body\n  /// @param finally the finally\n  explicit TryCatchFlow(Flow *body, Flow *finally = nullptr, Flow *else_ = nullptr,\n                        std::string name = \"\")\n      : AcceptorExtend(std::move(name)), body(body), else_(else_), finally(finally) {}\n\n  /// @return the body\n  Flow *getBody() { return cast<Flow>(body); }\n  /// @return the body\n  const Flow *getBody() const { return cast<Flow>(body); }\n  /// Sets the body.\n  /// @param f the new body\n  void setBody(Flow *f) { body = f; }\n\n  /// @return the else block\n  Flow *getElse() { return cast<Flow>(else_); }\n  /// @return the else block\n  const Flow *getElse() const { return cast<Flow>(else_); }\n  /// Sets the else block.\n  /// @param f the new else block\n  void setElse(Flow *f) { else_ = f; }\n\n  /// @return the finally\n  Flow *getFinally() { return cast<Flow>(finally); }\n  /// @return the finally\n  const Flow *getFinally() const { return cast<Flow>(finally); }\n  /// Sets the finally.\n  /// @param f the new finally\n  void setFinally(Flow *f) { finally = f; }\n\n  /// @return an iterator to the first catch\n  auto begin() { return catches.begin(); }\n  /// @return an iterator beyond the last catch\n  auto end() { return catches.end(); }\n  /// @return an iterator to the first catch\n  auto begin() const { return catches.begin(); }\n  /// @return an iterator beyond the last catch\n  auto end() const { return catches.end(); }\n\n  /// @return a reference to the first catch\n  auto &front() { return catches.front(); }\n  /// @return a reference to the last catch\n  auto &back() { return catches.back(); }\n  /// @return a reference to the first catch\n  auto &front() const { return catches.front(); }\n  /// @return a reference to the last catch\n  auto &back() const { return catches.back(); }\n\n  /// Inserts a catch at the given position.\n  /// @param pos the position\n  /// @param v the catch\n  /// @return an iterator to the newly added catch\n  template <typename It> auto insert(It pos, Catch v) { return catches.insert(pos, v); }\n\n  /// Appends a catch.\n  /// @param v the catch\n  void push_back(Catch v) { catches.push_back(v); }\n\n  /// Emplaces a catch.\n  /// @tparam Args the catch constructor args\n  template <typename... Args> void emplace_back(Args &&...args) {\n    catches.emplace_back(std::forward<Args>(args)...);\n  }\n\n  /// Erases a catch at the given position.\n  /// @param pos the position\n  /// @return the iterator beyond the erased catch\n  template <typename It> auto erase(It pos) { return catches.erase(pos); }\n\nprotected:\n  std::vector<Value *> doGetUsedValues() const override;\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n\n  std::vector<types::Type *> doGetUsedTypes() const override;\n  int doReplaceUsedType(const std::string &name, types::Type *newType) override;\n\n  std::vector<Var *> doGetUsedVariables() const override;\n  int doReplaceUsedVariable(id_t id, Var *newVar) override;\n};\n\n/// Flow that represents a pipeline. Pipelines with only function\n/// stages are expressions and have a concrete type. Pipelines with\n/// generator stages are not expressions and have no type. This\n/// representation allows for stages that output generators but do\n/// not get explicitly iterated in the pipeline, since generator\n/// stages are denoted by a separate flag.\nclass PipelineFlow : public AcceptorExtend<PipelineFlow, Flow> {\npublic:\n  /// Represents a single stage in a pipeline.\n  class Stage {\n  private:\n    /// the function being (partially) called in this stage\n    Value *callee;\n    /// the function arguments, where null represents where\n    /// previous pipeline output should go\n    std::vector<Value *> args;\n    /// true if this stage is a generator\n    bool generator;\n    /// true if this stage is marked parallel\n    bool parallel;\n\n  public:\n    /// Constructs a pipeline stage.\n    /// @param callee the function being called\n    /// @param args call arguments, with exactly one null entry\n    /// @param generator whether this stage is a generator stage\n    /// @param parallel whether this stage is parallel\n    Stage(Value *callee, std::vector<Value *> args, bool generator, bool parallel)\n        : callee(callee), args(std::move(args)), generator(generator),\n          parallel(parallel) {}\n\n    /// @return an iterator to the first argument\n    auto begin() { return args.begin(); }\n    /// @return an iterator beyond the last argument\n    auto end() { return args.end(); }\n    /// @return an iterator to the first argument\n    auto begin() const { return args.begin(); }\n    /// @return an iterator beyond the last argument\n    auto end() const { return args.end(); }\n\n    /// @return a pointer to the first argument\n    Value *front() { return args.front(); }\n    /// @return a pointer to the last argument\n    Value *back() { return args.back(); }\n    /// @return a pointer to the first argument\n    const Value *front() const { return args.front(); }\n    /// @return a pointer to the last argument\n    const Value *back() const { return args.back(); }\n\n    /// Inserts an argument.\n    /// @param pos the position\n    /// @param v the argument\n    /// @return an iterator to the newly added argument\n    template <typename It> auto insert(It pos, Value *v) { return args.insert(pos, v); }\n    /// Appends an argument.\n    /// @param v the argument\n    void push_back(Value *v) { args.push_back(v); }\n\n    /// Erases the item at the supplied position.\n    /// @param pos the position\n    /// @return the iterator beyond the removed argument\n    template <typename It> auto erase(It pos) { return args.erase(pos); }\n\n    /// Sets the called function.\n    /// @param c the callee\n    void setCallee(Value *c) { callee = c; }\n    /// @return the called function\n    Value *getCallee() { return callee; }\n    /// @return the called function\n    const Value *getCallee() const { return callee; }\n\n    /// Sets the stage's generator flag.\n    /// @param v the new value\n    void setGenerator(bool v = true) { generator = v; }\n    /// @return whether this stage is a generator stage\n    bool isGenerator() const { return generator; }\n    /// Sets the stage's parallel flag.\n    /// @param v the new value\n    void setParallel(bool v = true) { parallel = v; }\n    /// @return whether this stage is parallel\n    bool isParallel() const { return parallel; }\n    /// @return the output type of this stage\n    types::Type *getOutputType() const;\n    /// @return the output element type of this stage\n    types::Type *getOutputElementType() const;\n\n    friend class PipelineFlow;\n  };\n\nprivate:\n  /// pipeline stages\n  std::list<Stage> stages;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a pipeline flow.\n  /// @param stages vector of pipeline stages\n  /// @param name the name\n  explicit PipelineFlow(std::vector<Stage> stages = {}, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), stages(stages.begin(), stages.end()) {}\n\n  /// @return an iterator to the first stage\n  auto begin() { return stages.begin(); }\n  /// @return an iterator beyond the last stage\n  auto end() { return stages.end(); }\n  /// @return an iterator to the first stage\n  auto begin() const { return stages.begin(); }\n  /// @return an iterator beyond the last stage\n  auto end() const { return stages.end(); }\n\n  /// @return a pointer to the first stage\n  Stage &front() { return stages.front(); }\n  /// @return a pointer to the last stage\n  Stage &back() { return stages.back(); }\n  /// @return a pointer to the first stage\n  const Stage &front() const { return stages.front(); }\n  /// @return a pointer to the last stage\n  const Stage &back() const { return stages.back(); }\n\n  /// Inserts a stage\n  /// @param pos the position\n  /// @param v the stage\n  /// @return an iterator to the newly added stage\n  template <typename It> auto insert(It pos, Stage v) { return stages.insert(pos, v); }\n  /// Appends an stage.\n  /// @param v the stage\n  void push_back(Stage v) { stages.push_back(std::move(v)); }\n\n  /// Erases the item at the supplied position.\n  /// @param pos the position\n  /// @return the iterator beyond the removed stage\n  template <typename It> auto erase(It pos) { return stages.erase(pos); }\n\n  /// Emplaces a stage.\n  /// @param args the args\n  template <typename... Args> void emplace_back(Args &&...args) {\n    stages.emplace_back(std::forward<Args>(args)...);\n  }\n\nprotected:\n  std::vector<Value *> doGetUsedValues() const override;\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n};\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/func.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"func.h\"\n\n#include <algorithm>\n\n#include \"codon/cir/module.h\"\n#include \"codon/cir/util/iterators.h\"\n#include \"codon/cir/util/operator.h\"\n#include \"codon/cir/util/visitor.h\"\n#include \"codon/cir/var.h\"\n#include \"codon/parser/common.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace {\nint findAndReplace(id_t id, codon::ir::Var *newVal,\n                   std::list<codon::ir::Var *> &values) {\n  auto replacements = 0;\n  for (auto &value : values) {\n    if (value->getId() == id) {\n      value = newVal;\n      ++replacements;\n    }\n  }\n  return replacements;\n}\n} // namespace\n\nconst char Func::NodeId = 0;\n\nvoid Func::realize(types::Type *newType, const std::vector<std::string> &names) {\n  auto *funcType = cast<types::FuncType>(newType);\n  seqassert(funcType, \"{} is not a function type\", *newType);\n\n  setType(funcType);\n  args.clear();\n\n  auto i = 0;\n  for (auto *t : *funcType) {\n    args.push_back(getModule()->Nr<Var>(t, false, false, false, names[i]));\n    ++i;\n  }\n}\n\nVar *Func::getArgVar(const std::string &n) {\n  auto it = std::find_if(args.begin(), args.end(),\n                         [n](auto *other) { return other->getName() == n; });\n  return (it != args.end()) ? *it : nullptr;\n}\n\nstd::vector<Var *> Func::doGetUsedVariables() const {\n  std::vector<Var *> ret(args.begin(), args.end());\n  return ret;\n}\n\nint Func::doReplaceUsedVariable(id_t id, Var *newVar) {\n  return findAndReplace(id, newVar, args);\n}\n\nstd::vector<types::Type *> Func::doGetUsedTypes() const {\n  std::vector<types::Type *> ret;\n\n  for (auto *t : Var::getUsedTypes())\n    ret.push_back(const_cast<types::Type *>(t));\n\n  if (parentType)\n    ret.push_back(parentType);\n\n  return ret;\n}\n\nint Func::doReplaceUsedType(const std::string &name, types::Type *newType) {\n  auto count = Var::replaceUsedType(name, newType);\n  if (parentType && parentType->getName() == name) {\n    parentType = newType;\n    ++count;\n  }\n  return count;\n}\n\nconst char BodiedFunc::NodeId = 0;\n\nint BodiedFunc::doReplaceUsedValue(id_t id, Value *newValue) {\n  if (body && body->getId() == id) {\n    auto *flow = cast<Flow>(newValue);\n    seqassert(flow, \"{} is not a flow\", *newValue);\n    body = flow;\n    return 1;\n  }\n  return 0;\n}\n\nstd::vector<Var *> BodiedFunc::doGetUsedVariables() const {\n  auto ret = Func::doGetUsedVariables();\n  ret.insert(ret.end(), symbols.begin(), symbols.end());\n  return ret;\n}\n\nint BodiedFunc::doReplaceUsedVariable(id_t id, Var *newVar) {\n  return Func::doReplaceUsedVariable(id, newVar) + findAndReplace(id, newVar, symbols);\n}\n\nconst char ExternalFunc::NodeId = 0;\n\nconst char InternalFunc::NodeId = 0;\n\nconst char LLVMFunc::NodeId = 0;\n\nstd::vector<types::Type *> LLVMFunc::doGetUsedTypes() const {\n  std::vector<types::Type *> ret;\n\n  for (auto *t : Func::getUsedTypes())\n    ret.push_back(const_cast<types::Type *>(t));\n\n  for (auto &l : llvmLiterals)\n    if (l.isType())\n      ret.push_back(const_cast<types::Type *>(l.getTypeValue()));\n\n  return ret;\n}\n\nint LLVMFunc::doReplaceUsedType(const std::string &name, types::Type *newType) {\n  auto count = Var::doReplaceUsedType(name, newType);\n  for (auto &l : llvmLiterals)\n    if (l.isType() && l.getTypeValue()->getName() == name) {\n      l = newType;\n      ++count;\n    }\n  return count;\n}\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/func.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/flow.h\"\n#include \"codon/cir/util/iterators.h\"\n#include \"codon/cir/var.h\"\n\nnamespace codon {\nnamespace ir {\n\n/// CIR function\nclass Func : public AcceptorExtend<Func, Var> {\nprivate:\n  /// unmangled (source code) name of the function\n  std::string unmangledName;\n  /// whether the function is a generator\n  bool generator;\n  /// whether the function is an async function\n  bool async;\n  /// Parent type if func is a method, or null if not\n  types::Type *parentType;\n\nprotected:\n  /// list of arguments\n  std::list<Var *> args;\n\n  std::vector<Var *> doGetUsedVariables() const override;\n  int doReplaceUsedVariable(id_t id, Var *newVar) override;\n\n  std::vector<types::Type *> doGetUsedTypes() const override;\n  int doReplaceUsedType(const std::string &name, types::Type *newType) override;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs an unrealized CIR function.\n  /// @param name the function's name\n  explicit Func(std::string name = \"\")\n      : AcceptorExtend(nullptr, true, false, false, std::move(name)), generator(false),\n        async(false), parentType(nullptr) {}\n\n  /// Re-initializes the function with a new type and names.\n  /// @param newType the function's new type\n  /// @param names the function's new argument names\n  void realize(types::Type *newType, const std::vector<std::string> &names);\n\n  /// @return iterator to the first arg\n  auto arg_begin() { return args.begin(); }\n  /// @return iterator beyond the last arg\n  auto arg_end() { return args.end(); }\n  /// @return iterator to the first arg\n  auto arg_begin() const { return args.begin(); }\n  /// @return iterator beyond the last arg\n  auto arg_end() const { return args.end(); }\n\n  /// @return a pointer to the last arg\n  Var *arg_front() { return args.front(); }\n  /// @return a pointer to the last arg\n  Var *arg_back() { return args.back(); }\n  /// @return a pointer to the last arg\n  const Var *arg_back() const { return args.back(); }\n  /// @return a pointer to the first arg\n  const Var *arg_front() const { return args.front(); }\n\n  /// @return the function's unmangled (source code) name\n  std::string getUnmangledName() const { return unmangledName; }\n  /// Sets the unmangled name.\n  /// @param v the new value\n  void setUnmangledName(std::string v) { unmangledName = std::move(v); }\n\n  /// @return true if the function is a generator\n  bool isGenerator() const { return generator; }\n  /// Sets the function's generator flag.\n  /// @param v the new value\n  void setGenerator(bool v = true) { generator = v; }\n\n  /// @return true if the function is an async function\n  bool isAsync() const { return async; }\n  /// Sets the function's async flag.\n  /// @param v the new value\n  void setAsync(bool v = true) { async = v; }\n\n  /// @return the variable corresponding to the given argument name\n  /// @param n the argument name\n  Var *getArgVar(const std::string &n);\n\n  /// @return the parent type\n  types::Type *getParentType() const { return parentType; }\n  /// Sets the parent type.\n  /// @param p the new parent\n  void setParentType(types::Type *p) { parentType = p; }\n};\n\nclass BodiedFunc : public AcceptorExtend<BodiedFunc, Func> {\nprivate:\n  /// list of variables defined and used within the function\n  std::list<Var *> symbols;\n  /// the function body\n  Value *body = nullptr;\n  /// whether the function is a JIT input\n  bool jit = false;\n\npublic:\n  static const char NodeId;\n\n  using AcceptorExtend::AcceptorExtend;\n\n  /// @return iterator to the first symbol\n  auto begin() { return symbols.begin(); }\n  /// @return iterator beyond the last symbol\n  auto end() { return symbols.end(); }\n  /// @return iterator to the first symbol\n  auto begin() const { return symbols.begin(); }\n  /// @return iterator beyond the last symbol\n  auto end() const { return symbols.end(); }\n\n  /// @return a pointer to the first symbol\n  Var *front() { return symbols.front(); }\n  /// @return a pointer to the last symbol\n  Var *back() { return symbols.back(); }\n  /// @return a pointer to the first symbol\n  const Var *front() const { return symbols.front(); }\n  /// @return a pointer to the last symbol\n  const Var *back() const { return symbols.back(); }\n\n  /// Inserts an symbol at the given position.\n  /// @param pos the position\n  /// @param v the symbol\n  /// @return an iterator to the newly added symbol\n  template <typename It> auto insert(It pos, Var *v) { return symbols.insert(pos, v); }\n  /// Appends an symbol.\n  /// @param v the new symbol\n  void push_back(Var *v) { symbols.push_back(v); }\n\n  /// Erases the symbol at the given position.\n  /// @param pos the position\n  /// @return symbol_iterator following the removed symbol.\n  template <typename It> auto erase(It pos) { return symbols.erase(pos); }\n\n  /// @return the function body\n  Flow *getBody() { return cast<Flow>(body); }\n  /// @return the function body\n  const Flow *getBody() const { return cast<Flow>(body); }\n  /// Sets the function's body.\n  /// @param b the new body\n  void setBody(Flow *b) { body = b; }\n\n  /// @return true if the function is a JIT input\n  bool isJIT() const { return jit; }\n  /// Changes the function's JIT input status.\n  /// @param v true if JIT input, false otherwise\n  void setJIT(bool v = true) { jit = v; }\n\nprotected:\n  std::vector<Value *> doGetUsedValues() const override {\n    return body ? std::vector<Value *>{body} : std::vector<Value *>{};\n  }\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n\n  std::vector<Var *> doGetUsedVariables() const override;\n  int doReplaceUsedVariable(id_t id, Var *newVar) override;\n};\n\nclass ExternalFunc : public AcceptorExtend<ExternalFunc, Func> {\npublic:\n  static const char NodeId;\n\n  using AcceptorExtend::AcceptorExtend;\n\n  /// @return true if the function is variadic\n  bool isVariadic() const { return cast<types::FuncType>(getType())->isVariadic(); }\n};\n\n/// Internal, LLVM-only function.\nclass InternalFunc : public AcceptorExtend<InternalFunc, Func> {\npublic:\n  static const char NodeId;\n\n  using AcceptorExtend::AcceptorExtend;\n};\n\n/// LLVM function defined in Seq source.\nclass LLVMFunc : public AcceptorExtend<LLVMFunc, Func> {\nprivate:\n  /// literals that must be formatted into the body\n  std::vector<types::Generic> llvmLiterals;\n  /// declares for llvm-only function\n  std::string llvmDeclares;\n  /// body of llvm-only function\n  std::string llvmBody;\n\npublic:\n  static const char NodeId;\n\n  using AcceptorExtend::AcceptorExtend;\n\n  /// Sets the LLVM literals.\n  /// @param v the new values.\n  void setLLVMLiterals(std::vector<types::Generic> v) { llvmLiterals = std::move(v); }\n\n  /// @return iterator to the first literal\n  auto literal_begin() { return llvmLiterals.begin(); }\n  /// @return iterator beyond the last literal\n  auto literal_end() { return llvmLiterals.end(); }\n  /// @return iterator to the first literal\n  auto literal_begin() const { return llvmLiterals.begin(); }\n  /// @return iterator beyond the last literal\n  auto literal_end() const { return llvmLiterals.end(); }\n\n  /// @return a reference to the first literal\n  auto &literal_front() { return llvmLiterals.front(); }\n  /// @return a reference to the last literal\n  auto &literal_back() { return llvmLiterals.back(); }\n  /// @return a reference to the first literal\n  auto &literal_front() const { return llvmLiterals.front(); }\n  /// @return a reference to the last literal\n  auto &literal_back() const { return llvmLiterals.back(); }\n\n  /// @return the LLVM declarations\n  const std::string &getLLVMDeclarations() const { return llvmDeclares; }\n  /// Sets the LLVM declarations.\n  /// @param v the new value\n  void setLLVMDeclarations(std::string v) { llvmDeclares = std::move(v); }\n  /// @return the LLVM body\n  const std::string &getLLVMBody() const { return llvmBody; }\n  /// Sets the LLVM body.\n  /// @param v the new value\n  void setLLVMBody(std::string v) { llvmBody = std::move(v); }\n\nprotected:\n  std::vector<types::Type *> doGetUsedTypes() const override;\n  int doReplaceUsedType(const std::string &name, types::Type *newType) override;\n};\n\n} // namespace ir\n} // namespace codon\n\ntemplate <> struct fmt::formatter<codon::ir::Func> : fmt::ostream_formatter {};\n"
  },
  {
    "path": "codon/cir/instr.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"instr.h\"\n\n#include \"codon/cir/module.h\"\n#include \"codon/cir/util/iterators.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace {\nint findAndReplace(id_t id, codon::ir::Value *newVal,\n                   std::vector<codon::ir::Value *> &values) {\n  auto replacements = 0;\n  for (auto &value : values) {\n    if (value->getId() == id) {\n      value = newVal;\n      ++replacements;\n    }\n  }\n  return replacements;\n}\n} // namespace\n\nconst char Instr::NodeId = 0;\n\ntypes::Type *Instr::doGetType() const { return getModule()->getNoneType(); }\n\nconst char AssignInstr::NodeId = 0;\n\nint AssignInstr::doReplaceUsedValue(id_t id, Value *newValue) {\n  if (rhs->getId() == id) {\n    rhs = newValue;\n    return 1;\n  }\n  return 0;\n}\n\nint AssignInstr::doReplaceUsedVariable(id_t id, Var *newVar) {\n  if (lhs->getId() == id) {\n    lhs = newVar;\n    return 1;\n  }\n  return 0;\n}\n\nconst char ExtractInstr::NodeId = 0;\n\ntypes::Type *ExtractInstr::doGetType() const {\n  auto *memberedType = cast<types::MemberedType>(val->getType());\n  seqassert(memberedType, \"{} is not a membered type\", *val->getType());\n  return memberedType->getMemberType(field);\n}\n\nint ExtractInstr::doReplaceUsedValue(id_t id, Value *newValue) {\n  if (val->getId() == id) {\n    val = newValue;\n    return 1;\n  }\n  return 0;\n}\n\nconst char InsertInstr::NodeId = 0;\n\nint InsertInstr::doReplaceUsedValue(id_t id, Value *newValue) {\n  auto replacements = 0;\n  if (lhs->getId() == id) {\n    lhs = newValue;\n    ++replacements;\n  }\n  if (rhs->getId() == id) {\n    rhs = newValue;\n    ++replacements;\n  }\n  return replacements;\n}\n\nconst char CallInstr::NodeId = 0;\n\ntypes::Type *CallInstr::doGetType() const {\n  auto *funcType = cast<types::FuncType>(callee->getType());\n  seqassert(funcType, \"{} is not a function type\", *callee->getType());\n  return funcType->getReturnType();\n}\n\nstd::vector<Value *> CallInstr::doGetUsedValues() const {\n  std::vector<Value *> ret(args.begin(), args.end());\n  ret.push_back(callee);\n  return ret;\n}\n\nint CallInstr::doReplaceUsedValue(id_t id, Value *newValue) {\n  auto replacements = 0;\n  if (callee->getId() == id) {\n    callee = newValue;\n    ++replacements;\n  }\n  replacements += findAndReplace(id, newValue, args);\n  return replacements;\n}\n\nconst char StackAllocInstr::NodeId = 0;\n\nint StackAllocInstr::doReplaceUsedType(const std::string &name, types::Type *newType) {\n  if (arrayType->getName() == name) {\n    arrayType = newType;\n    return 1;\n  }\n  return 0;\n}\n\nconst char TypePropertyInstr::NodeId = 0;\n\ntypes::Type *TypePropertyInstr::doGetType() const {\n  switch (property) {\n  case Property::IS_ATOMIC:\n    return getModule()->getBoolType();\n  case Property::IS_CONTENT_ATOMIC:\n    return getModule()->getBoolType();\n  case Property::SIZEOF:\n    return getModule()->getIntType();\n  default:\n    return getModule()->getNoneType();\n  }\n}\n\nint TypePropertyInstr::doReplaceUsedType(const std::string &name,\n                                         types::Type *newType) {\n  if (inspectType->getName() == name) {\n    inspectType = newType;\n    return 1;\n  }\n  return 0;\n}\n\nconst char YieldInInstr::NodeId = 0;\n\nint YieldInInstr::doReplaceUsedType(const std::string &name, types::Type *newType) {\n  if (type->getName() == name) {\n    type = newType;\n    return 1;\n  }\n  return 0;\n}\n\nconst char AwaitInstr::NodeId = 0;\n\nint AwaitInstr::doReplaceUsedValue(id_t id, Value *newValue) {\n  if (value->getId() == id) {\n    value = newValue;\n    return 1;\n  }\n  return 0;\n}\n\nint AwaitInstr::doReplaceUsedType(const std::string &name, types::Type *newType) {\n  if (type->getName() == name) {\n    type = newType;\n    return 1;\n  }\n  return 0;\n}\n\nconst char TernaryInstr::NodeId = 0;\n\nint TernaryInstr::doReplaceUsedValue(id_t id, Value *newValue) {\n  auto replacements = 0;\n  if (cond->getId() == id) {\n    cond = newValue;\n    ++replacements;\n  }\n  if (trueValue->getId() == id) {\n    trueValue = newValue;\n    ++replacements;\n  }\n  if (falseValue->getId() == id) {\n    falseValue = newValue;\n    ++replacements;\n  }\n  return replacements;\n}\n\nconst char ControlFlowInstr::NodeId = 0;\n\nconst char BreakInstr::NodeId = 0;\n\nstd::vector<Value *> BreakInstr::doGetUsedValues() const {\n  if (loop)\n    return {loop};\n  return {};\n}\n\nint BreakInstr::doReplaceUsedValue(id_t id, Value *newValue) {\n  if (loop && loop->getId() == id) {\n    auto *f = cast<Flow>(newValue);\n    seqassert(f, \"{} is not a flow\", *newValue);\n    loop = f;\n    return 1;\n  }\n  return 0;\n}\n\nconst char ContinueInstr::NodeId = 0;\n\nstd::vector<Value *> ContinueInstr::doGetUsedValues() const {\n  if (loop)\n    return {loop};\n  return {};\n}\n\nint ContinueInstr::doReplaceUsedValue(id_t id, Value *newValue) {\n  if (loop && loop->getId() == id) {\n    auto *f = cast<Flow>(newValue);\n    seqassert(f, \"{} is not a flow\", *newValue);\n    loop = f;\n    return 1;\n  }\n  return 0;\n}\n\nconst char ReturnInstr::NodeId = 0;\n\nstd::vector<Value *> ReturnInstr::doGetUsedValues() const {\n  if (value)\n    return {value};\n  return {};\n}\n\nint ReturnInstr::doReplaceUsedValue(id_t id, Value *newValue) {\n  auto replacements = 0;\n  if (value && value->getId() == id) {\n    setValue(newValue);\n    ++replacements;\n  }\n  return replacements;\n}\n\nconst char YieldInstr::NodeId = 0;\n\nstd::vector<Value *> YieldInstr::doGetUsedValues() const {\n  if (value)\n    return {value};\n  return {};\n}\n\nint YieldInstr::doReplaceUsedValue(id_t id, Value *newValue) {\n  if (value && value->getId() == id) {\n    setValue(newValue);\n    return 1;\n  }\n  return 0;\n}\n\nconst char ThrowInstr::NodeId = 0;\n\nstd::vector<Value *> ThrowInstr::doGetUsedValues() const {\n  if (value)\n    return {value};\n  return {};\n}\n\nint ThrowInstr::doReplaceUsedValue(id_t id, Value *newValue) {\n  if (value && value->getId() == id) {\n    setValue(newValue);\n    return 1;\n  }\n  return 0;\n}\n\nconst char FlowInstr::NodeId = 0;\n\nint FlowInstr::doReplaceUsedValue(id_t id, Value *newValue) {\n  auto replacements = 0;\n  if (flow->getId() == id) {\n    auto *f = cast<Flow>(newValue);\n    seqassert(f, \"{} is not a flow\", *newValue);\n    setFlow(f);\n    ++replacements;\n  }\n  if (val->getId() == id) {\n    setValue(newValue);\n    ++replacements;\n  }\n  return replacements;\n}\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/instr.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <string>\n\n#include \"codon/cir/flow.h\"\n#include \"codon/cir/types/types.h\"\n#include \"codon/cir/util/iterators.h\"\n#include \"codon/cir/value.h\"\n#include \"codon/cir/var.h\"\n\nnamespace codon {\nnamespace ir {\n\n/// CIR object representing an \"instruction,\" or discrete operation in the context of a\n/// block.\nclass Instr : public AcceptorExtend<Instr, Value> {\npublic:\n  static const char NodeId;\n\n  using AcceptorExtend::AcceptorExtend;\n\nprivate:\n  types::Type *doGetType() const override;\n};\n\n/// Instr representing setting a memory location.\nclass AssignInstr : public AcceptorExtend<AssignInstr, Instr> {\nprivate:\n  /// the left-hand side\n  Var *lhs;\n  /// the right-hand side\n  Value *rhs;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs an assign instruction.\n  /// @param lhs the left-hand side\n  /// @param rhs the right-hand side\n  /// @param field the field being set, may be empty\n  /// @param name the instruction's name\n  AssignInstr(Var *lhs, Value *rhs, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), lhs(lhs), rhs(rhs) {}\n\n  /// @return the left-hand side\n  Var *getLhs() { return lhs; }\n  /// @return the left-hand side\n  const Var *getLhs() const { return lhs; }\n  /// Sets the left-hand side\n  /// @param l the new value\n  void setLhs(Var *v) { lhs = v; }\n\n  /// @return the right-hand side\n  Value *getRhs() { return rhs; }\n  /// @return the right-hand side\n  const Value *getRhs() const { return rhs; }\n  /// Sets the right-hand side\n  /// @param l the new value\n  void setRhs(Value *v) { rhs = v; }\n\nprotected:\n  std::vector<Value *> doGetUsedValues() const override { return {rhs}; }\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n\n  std::vector<Var *> doGetUsedVariables() const override { return {lhs}; }\n  int doReplaceUsedVariable(id_t id, Var *newVar) override;\n};\n\n/// Instr representing loading the field of a value.\nclass ExtractInstr : public AcceptorExtend<ExtractInstr, Instr> {\nprivate:\n  /// the value being manipulated\n  Value *val;\n  /// the field\n  std::string field;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a load instruction.\n  /// @param val the value being manipulated\n  /// @param field the field\n  /// @param name the instruction's name\n  explicit ExtractInstr(Value *val, std::string field, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), val(val), field(std::move(field)) {}\n\n  /// @return the location\n  Value *getVal() { return val; }\n  /// @return the location\n  const Value *getVal() const { return val; }\n  /// Sets the location.\n  /// @param p the new value\n  void setVal(Value *p) { val = p; }\n\n  /// @return the field\n  const std::string &getField() const { return field; }\n  /// Sets the field.\n  /// @param f the new field\n  void setField(std::string f) { field = std::move(f); }\n\nprotected:\n  types::Type *doGetType() const override;\n  std::vector<Value *> doGetUsedValues() const override { return {val}; }\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n};\n\n/// Instr representing setting the field of a value.\nclass InsertInstr : public AcceptorExtend<InsertInstr, Instr> {\nprivate:\n  /// the value being manipulated\n  Value *lhs;\n  /// the field\n  std::string field;\n  /// the value being inserted\n  Value *rhs;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a load instruction.\n  /// @param lhs the value being manipulated\n  /// @param field the field\n  /// @param rhs the new value\n  /// @param name the instruction's name\n  explicit InsertInstr(Value *lhs, std::string field, Value *rhs, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), lhs(lhs), field(std::move(field)), rhs(rhs) {}\n\n  /// @return the left-hand side\n  Value *getLhs() { return lhs; }\n  /// @return the left-hand side\n  const Value *getLhs() const { return lhs; }\n  /// Sets the left-hand side.\n  /// @param p the new value\n  void setLhs(Value *p) { lhs = p; }\n\n  /// @return the right-hand side\n  Value *getRhs() { return rhs; }\n  /// @return the right-hand side\n  const Value *getRhs() const { return rhs; }\n  /// Sets the right-hand side.\n  /// @param p the new value\n  void setRhs(Value *p) { rhs = p; }\n\n  /// @return the field\n  const std::string &getField() const { return field; }\n  /// Sets the field.\n  /// @param f the new field\n  void setField(std::string f) { field = std::move(f); }\n\nprotected:\n  types::Type *doGetType() const override { return lhs->getType(); }\n  std::vector<Value *> doGetUsedValues() const override { return {lhs, rhs}; }\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n};\n\n/// Instr representing calling a function.\nclass CallInstr : public AcceptorExtend<CallInstr, Instr> {\nprivate:\n  /// the function\n  Value *callee;\n  /// the arguments\n  std::vector<Value *> args;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a call instruction.\n  /// @param callee the function\n  /// @param args the arguments\n  /// @param name the instruction's name\n  CallInstr(Value *callee, std::vector<Value *> args, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), callee(callee), args(std::move(args)) {}\n\n  /// Constructs a call instruction with no arguments.\n  /// @param callee the function\n  /// @param name the instruction's name\n  explicit CallInstr(Value *callee, std::string name = \"\")\n      : CallInstr(callee, {}, std::move(name)) {}\n\n  /// @return the callee\n  Value *getCallee() { return callee; }\n  /// @return the callee\n  const Value *getCallee() const { return callee; }\n  /// Sets the callee.\n  /// @param c the new value\n  void setCallee(Value *c) { callee = c; }\n\n  /// @return an iterator to the first argument\n  auto begin() { return args.begin(); }\n  /// @return an iterator beyond the last argument\n  auto end() { return args.end(); }\n  /// @return an iterator to the first argument\n  auto begin() const { return args.begin(); }\n  /// @return an iterator beyond the last argument\n  auto end() const { return args.end(); }\n\n  /// @return a pointer to the first argument\n  Value *front() { return args.front(); }\n  /// @return a pointer to the last argument\n  Value *back() { return args.back(); }\n  /// @return a pointer to the first argument\n  const Value *front() const { return args.front(); }\n  /// @return a pointer to the last argument\n  const Value *back() const { return args.back(); }\n\n  /// Inserts an argument at the given position.\n  /// @param pos the position\n  /// @param v the argument\n  /// @return an iterator to the newly added argument\n  template <typename It> auto insert(It pos, Value *v) { return args.insert(pos, v); }\n  /// Appends an argument.\n  /// @param v the argument\n  void push_back(Value *v) { args.push_back(v); }\n\n  /// Sets the args.\n  /// @param v the new args vector\n  void setArgs(std::vector<Value *> v) { args = std::move(v); }\n\n  /// @return the number of arguments\n  int numArgs() const { return args.size(); }\n\nprotected:\n  types::Type *doGetType() const override;\n  std::vector<Value *> doGetUsedValues() const override;\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n};\n\n/// Instr representing allocating an array on the stack.\nclass StackAllocInstr : public AcceptorExtend<StackAllocInstr, Instr> {\nprivate:\n  /// the array type\n  types::Type *arrayType;\n  /// number of elements to allocate\n  int64_t count;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a stack allocation instruction.\n  /// @param arrayType the type of the array\n  /// @param count the number of elements\n  /// @param name the name\n  StackAllocInstr(types::Type *arrayType, int64_t count, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), arrayType(arrayType), count(count) {}\n\n  /// @return the count\n  int64_t getCount() const { return count; }\n  /// Sets the count.\n  /// @param c the new value\n  void setCount(int64_t c) { count = c; }\n\n  /// @return the array type\n  types::Type *getArrayType() { return arrayType; }\n  /// @return the array type\n  types::Type *getArrayType() const { return arrayType; }\n  /// Sets the array type.\n  /// @param t the new type\n  void setArrayType(types::Type *t) { arrayType = t; }\n\nprotected:\n  types::Type *doGetType() const override { return arrayType; }\n  std::vector<types::Type *> doGetUsedTypes() const override { return {arrayType}; }\n  int doReplaceUsedType(const std::string &name, types::Type *newType) override;\n};\n\n/// Instr representing getting information about a type.\nclass TypePropertyInstr : public AcceptorExtend<TypePropertyInstr, Instr> {\npublic:\n  enum Property { IS_ATOMIC, IS_CONTENT_ATOMIC, SIZEOF };\n\nprivate:\n  /// the type being inspected\n  types::Type *inspectType;\n  /// the property being checked\n  Property property;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a type property instruction.\n  /// @param type the type being inspected\n  /// @param name the name\n  explicit TypePropertyInstr(types::Type *type, Property property,\n                             std::string name = \"\")\n      : AcceptorExtend(std::move(name)), inspectType(type), property(property) {}\n\n  /// @return the type being inspected\n  types::Type *getInspectType() { return inspectType; }\n  /// @return the type being inspected\n  types::Type *getInspectType() const { return inspectType; }\n  /// Sets the type being inspected\n  /// @param t the new type\n  void setInspectType(types::Type *t) { inspectType = t; }\n\n  /// @return the property being inspected\n  Property getProperty() const { return property; }\n  /// Sets the property.\n  /// @param p the new value\n  void setProperty(Property p) { property = p; }\n\nprotected:\n  types::Type *doGetType() const override;\n  std::vector<types::Type *> doGetUsedTypes() const override { return {inspectType}; }\n  int doReplaceUsedType(const std::string &name, types::Type *newType) override;\n};\n\n/// Instr representing a Python yield expression.\nclass YieldInInstr : public AcceptorExtend<YieldInInstr, Instr> {\nprivate:\n  /// the type of the value being yielded in.\n  types::Type *type;\n  /// whether or not to suspend\n  bool suspend;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a yield in instruction.\n  /// @param type the type of the value being yielded in\n  /// @param suspend whether to suspend\n  /// @param name the instruction's name\n  explicit YieldInInstr(types::Type *type, bool suspend = true, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), type(type), suspend(suspend) {}\n\n  /// @return true if the instruction suspends\n  bool isSuspending() const { return suspend; }\n  /// Sets the instruction suspending flag.\n  /// @param v the new value\n  void setSuspending(bool v = true) { suspend = v; }\n\n  /// Sets the type.\n  /// @param t the new type\n  void setType(types::Type *t) { type = t; }\n\nprotected:\n  types::Type *doGetType() const override { return type; }\n  std::vector<types::Type *> doGetUsedTypes() const override { return {type}; }\n  int doReplaceUsedType(const std::string &name, types::Type *newType) override;\n};\n\n/// Instr representing a ternary operator.\nclass TernaryInstr : public AcceptorExtend<TernaryInstr, Instr> {\nprivate:\n  /// the condition\n  Value *cond;\n  /// the true value\n  Value *trueValue;\n  /// the false value\n  Value *falseValue;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a ternary instruction.\n  /// @param cond the condition\n  /// @param trueValue the true value\n  /// @param falseValue the false value\n  /// @param name the instruction's name\n  TernaryInstr(Value *cond, Value *trueValue, Value *falseValue, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), cond(cond), trueValue(trueValue),\n        falseValue(falseValue) {}\n\n  /// @return the condition\n  Value *getCond() { return cond; }\n  /// @return the condition\n  const Value *getCond() const { return cond; }\n  /// Sets the condition.\n  /// @param v the new value\n  void setCond(Value *v) { cond = v; }\n\n  /// @return the condition\n  Value *getTrueValue() { return trueValue; }\n  /// @return the condition\n  const Value *getTrueValue() const { return trueValue; }\n  /// Sets the true value.\n  /// @param v the new value\n  void setTrueValue(Value *v) { trueValue = v; }\n\n  /// @return the false value\n  Value *getFalseValue() { return falseValue; }\n  /// @return the false value\n  const Value *getFalseValue() const { return falseValue; }\n  /// Sets the value.\n  /// @param v the new value\n  void setFalseValue(Value *v) { falseValue = v; }\n\nprotected:\n  types::Type *doGetType() const override { return trueValue->getType(); }\n  std::vector<Value *> doGetUsedValues() const override {\n    return {cond, trueValue, falseValue};\n  }\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n};\n\n/// Base for control flow instructions\nclass ControlFlowInstr : public AcceptorExtend<ControlFlowInstr, Instr> {\npublic:\n  static const char NodeId;\n  using AcceptorExtend::AcceptorExtend;\n};\n\n/// Instr representing a break statement.\nclass BreakInstr : public AcceptorExtend<BreakInstr, ControlFlowInstr> {\nprivate:\n  /// the loop being broken, nullptr if the immediate ancestor\n  Value *loop;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a break instruction.\n  /// @param loop the loop being broken, nullptr if immediate ancestor\n  /// @param name the instruction's name\n  explicit BreakInstr(Value *loop = nullptr, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), loop(loop) {}\n\n  /// @return the loop, nullptr if immediate ancestor\n  Value *getLoop() const { return loop; }\n  /// Sets the loop id.\n  /// @param v the new loop, nullptr if immediate ancestor\n  void setLoop(Value *v) { loop = v; }\n\n  std::vector<Value *> doGetUsedValues() const override;\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n};\n\n/// Instr representing a continue statement.\nclass ContinueInstr : public AcceptorExtend<ContinueInstr, ControlFlowInstr> {\nprivate:\n  /// the loop being continued, nullptr if the immediate ancestor\n  Value *loop;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a continue instruction.\n  /// @param loop the loop being continued, nullptr if immediate ancestor\n  /// @param name the instruction's name\n  explicit ContinueInstr(Value *loop = nullptr, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), loop(loop) {}\n\n  /// @return the loop, nullptr if immediate ancestor\n  Value *getLoop() const { return loop; }\n  /// Sets the loop id.\n  /// @param v the new loop, -1 if immediate ancestor\n  void setLoop(Value *v) { loop = v; }\n\n  std::vector<Value *> doGetUsedValues() const override;\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n};\n\n/// Instr representing a return statement.\nclass ReturnInstr : public AcceptorExtend<ReturnInstr, ControlFlowInstr> {\nprivate:\n  /// the value\n  Value *value;\n\npublic:\n  static const char NodeId;\n\n  explicit ReturnInstr(Value *value = nullptr, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), value(value) {}\n\n  /// @return the value\n  Value *getValue() { return value; }\n  /// @return the value\n  const Value *getValue() const { return value; }\n  /// Sets the value.\n  /// @param v the new value\n  void setValue(Value *v) { value = v; }\n\nprotected:\n  std::vector<Value *> doGetUsedValues() const override;\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n};\n\n/// Instr representing a yield statement.\nclass YieldInstr : public AcceptorExtend<YieldInstr, Instr> {\nprivate:\n  /// the value\n  Value *value;\n  /// whether this yield is final\n  bool final;\n\npublic:\n  static const char NodeId;\n\n  explicit YieldInstr(Value *value = nullptr, bool final = false, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), value(value), final(final) {}\n\n  /// @return the value\n  Value *getValue() { return value; }\n  /// @return the value\n  const Value *getValue() const { return value; }\n  /// Sets the value.\n  /// @param v the new value\n  void setValue(Value *v) { value = v; }\n\n  /// @return if this yield is final\n  bool isFinal() const { return final; }\n  /// Sets whether this yield is final.\n  /// @param f true if final\n  void setFinal(bool f = true) { final = f; }\n\nprotected:\n  std::vector<Value *> doGetUsedValues() const override;\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n};\n\n/// Instr representing an await statement.\nclass AwaitInstr : public AcceptorExtend<AwaitInstr, Instr> {\nprivate:\n  /// the value\n  Value *value;\n  /// the type of the result\n  types::Type *type;\n  /// true if argument is a generator (e.g. custom __await__)\n  bool generator;\n\npublic:\n  static const char NodeId;\n\n  explicit AwaitInstr(Value *value, types::Type *type, bool generator = false,\n                      std::string name = \"\")\n      : AcceptorExtend(std::move(name)), value(value), type(type),\n        generator(generator) {}\n\n  /// @return the value\n  Value *getValue() { return value; }\n  /// @return the value\n  const Value *getValue() const { return value; }\n  /// Sets the value.\n  /// @param v the new value\n  void setValue(Value *v) { value = v; }\n\n  /// Sets the type.\n  /// @param t the new type\n  void setType(types::Type *t) { type = t; }\n\n  /// @return whether argument is a generator\n  bool isGenerator() const { return generator; }\n  /// Sets generator status\n  /// @param g the new value\n  void setGenerator(bool g = true) { generator = g; }\n\nprotected:\n  types::Type *doGetType() const override { return type; }\n  std::vector<Value *> doGetUsedValues() const override { return {value}; }\n  std::vector<types::Type *> doGetUsedTypes() const override { return {type}; }\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n  int doReplaceUsedType(const std::string &name, types::Type *newType) override;\n};\n\nclass ThrowInstr : public AcceptorExtend<ThrowInstr, Instr> {\nprivate:\n  /// the value\n  Value *value;\n\npublic:\n  static const char NodeId;\n\n  explicit ThrowInstr(Value *value = nullptr, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), value(value) {}\n\n  /// @return the value\n  Value *getValue() { return value; }\n  /// @return the value\n  const Value *getValue() const { return value; }\n  /// Sets the value.\n  /// @param v the new value\n  void setValue(Value *v) { value = v; }\n\nprotected:\n  std::vector<Value *> doGetUsedValues() const override;\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n};\n\n/// Instr that contains a flow and value.\nclass FlowInstr : public AcceptorExtend<FlowInstr, Instr> {\nprivate:\n  /// the flow\n  Value *flow;\n  /// the output value\n  Value *val;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a flow value.\n  /// @param flow the flow\n  /// @param val the output value\n  /// @param name the name\n  explicit FlowInstr(Flow *flow, Value *val, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), flow(flow), val(val) {}\n\n  /// @return the flow\n  Flow *getFlow() { return cast<Flow>(flow); }\n  /// @return the flow\n  const Flow *getFlow() const { return cast<Flow>(flow); }\n  /// Sets the flow.\n  /// @param f the new flow\n  void setFlow(Flow *f) { flow = f; }\n\n  /// @return the value\n  Value *getValue() { return val; }\n  /// @return the value\n  const Value *getValue() const { return val; }\n  /// Sets the value.\n  /// @param v the new value\n  void setValue(Value *v) { val = v; }\n\nprotected:\n  types::Type *doGetType() const override { return val->getType(); }\n  std::vector<Value *> doGetUsedValues() const override { return {flow, val}; }\n  int doReplaceUsedValue(id_t id, Value *newValue) override;\n};\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/llvm/gpu.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"gpu.h\"\n\n#include <algorithm>\n#include <memory>\n#include <string>\n\n#include \"codon/cir/llvm/optimize.h\"\n#include \"codon/util/common.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace {\nconst std::string GPU_TRIPLE = \"nvptx64-nvidia-cuda\";\nconst std::string GPU_DL =\n    \"e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-\"\n    \"f64:64:64-v16:16:16-v32:32:32-v64:64:64-v128:128:128-n16:32:64\";\nllvm::cl::opt<std::string>\n    libdevice(\"libdevice\", llvm::cl::desc(\"libdevice path for GPU kernels\"),\n              llvm::cl::init(\"/usr/local/cuda/nvvm/libdevice/libdevice.10.bc\"));\nllvm::cl::opt<std::string> ptxOutput(\"ptx\",\n                                     llvm::cl::desc(\"Output PTX to specified file\"));\nllvm::cl::opt<std::string> gpuName(\n    \"gpu-name\",\n    llvm::cl::desc(\n        \"Target GPU architecture or compute capability (e.g. sm_70, sm_80, etc.)\"),\n    llvm::cl::init(\"sm_30\"));\nllvm::cl::opt<std::string> gpuFeatures(\n    \"gpu-features\",\n    llvm::cl::desc(\"GPU feature flags passed (e.g. +ptx42 to enable PTX 4.2 features)\"),\n    llvm::cl::init(\"+ptx42\"));\n\n// Adapted from LLVM's GVExtractorPass, which is not externally available\n// as a pass for the new pass manager.\nclass GVExtractor : public llvm::PassInfoMixin<GVExtractor> {\n  llvm::SetVector<llvm::GlobalValue *> named;\n  bool deleteStuff;\n  bool keepConstInit;\n\npublic:\n  // If deleteS is true, this pass deletes the specified global values.\n  // Otherwise, it deletes as much of the module as possible, except for the\n  // global values specified.\n  explicit GVExtractor(std::vector<llvm::GlobalValue *> &GVs, bool deleteS = true,\n                       bool keepConstInit = false)\n      : named(GVs.begin(), GVs.end()), deleteStuff(deleteS),\n        keepConstInit(keepConstInit) {}\n\n  // Make sure GV is visible from both modules. Delete is true if it is\n  // being deleted from this module.\n  // This also makes sure GV cannot be dropped so that references from\n  // the split module remain valid.\n  static void makeVisible(llvm::GlobalValue &GV, bool del) {\n    bool local = GV.hasLocalLinkage();\n    if (local || del) {\n      GV.setLinkage(llvm::GlobalValue::ExternalLinkage);\n      if (local)\n        GV.setVisibility(llvm::GlobalValue::HiddenVisibility);\n      return;\n    }\n\n    if (!GV.hasLinkOnceLinkage()) {\n      seqassertn(!GV.isDiscardableIfUnused(), \"bad global in extractor\");\n      return;\n    }\n\n    // Map linkonce* to weak* so that llvm doesn't drop this GV.\n    switch (GV.getLinkage()) {\n    default:\n      seqassertn(false, \"unexpected linkage\");\n    case llvm::GlobalValue::LinkOnceAnyLinkage:\n      GV.setLinkage(llvm::GlobalValue::WeakAnyLinkage);\n      return;\n    case llvm::GlobalValue::LinkOnceODRLinkage:\n      GV.setLinkage(llvm::GlobalValue::WeakODRLinkage);\n      return;\n    }\n  }\n\n  llvm::PreservedAnalyses run(llvm::Module &M, llvm::ModuleAnalysisManager &) {\n    // Visit the global inline asm.\n    if (!deleteStuff)\n      M.setModuleInlineAsm(\"\");\n\n    // For simplicity, just give all GlobalValues ExternalLinkage. A trickier\n    // implementation could figure out which GlobalValues are actually\n    // referenced by the 'named' set, and which GlobalValues in the rest of\n    // the module are referenced by the NamedSet, and get away with leaving\n    // more internal and private things internal and private. But for now,\n    // be conservative and simple.\n\n    // Visit the GlobalVariables.\n    for (auto &GV : M.globals()) {\n      bool del = deleteStuff == (bool)named.count(&GV) && !GV.isDeclaration() &&\n                 (!GV.isConstant() || !keepConstInit);\n      if (!del) {\n        if (GV.hasAvailableExternallyLinkage())\n          continue;\n        if (GV.getName() == \"llvm.global_ctors\")\n          continue;\n      }\n\n      makeVisible(GV, del);\n\n      if (del) {\n        // Make this a declaration and drop it's comdat.\n        GV.setInitializer(nullptr);\n        GV.setComdat(nullptr);\n      }\n    }\n\n    // Visit the Functions.\n    for (auto &F : M) {\n      bool del = deleteStuff == (bool)named.count(&F) && !F.isDeclaration();\n      if (!del) {\n        if (F.hasAvailableExternallyLinkage())\n          continue;\n      }\n\n      makeVisible(F, del);\n\n      if (del) {\n        // Make this a declaration and drop it's comdat.\n        F.deleteBody();\n        F.setComdat(nullptr);\n      }\n    }\n\n    // Visit the Aliases.\n    for (auto &GA : llvm::make_early_inc_range(M.aliases())) {\n      bool del = deleteStuff == (bool)named.count(&GA);\n      makeVisible(GA, del);\n\n      if (del) {\n        auto *ty = GA.getValueType();\n        GA.removeFromParent();\n        llvm::Value *decl;\n        if (auto *funcTy = llvm::dyn_cast<llvm::FunctionType>(ty)) {\n          decl = llvm::Function::Create(funcTy, llvm::GlobalValue::ExternalLinkage,\n                                        GA.getAddressSpace(), GA.getName(), &M);\n\n        } else {\n          decl = new llvm::GlobalVariable(\n              M, ty, false, llvm::GlobalValue::ExternalLinkage, nullptr, GA.getName());\n        }\n        GA.replaceAllUsesWith(decl);\n        delete &GA;\n      }\n    }\n\n    return llvm::PreservedAnalyses::none();\n  }\n};\n\nstd::string cleanUpName(llvm::StringRef name) {\n  std::string validName;\n  llvm::raw_string_ostream validNameStream(validName);\n\n  auto valid = [](char c, bool first) {\n    bool ok = ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || (c == '_');\n    if (!first)\n      ok = ok || ('0' <= c && c <= '9');\n    return ok;\n  };\n\n  bool first = true;\n  for (char c : name) {\n    validNameStream << (valid(c, first) ? c : '_');\n    first = false;\n  }\n\n  return validNameStream.str();\n}\n\nvoid linkLibdevice(llvm::Module *M, const std::string &path) {\n  llvm::SMDiagnostic err;\n  auto libdevice = llvm::parseIRFile(path, err, M->getContext());\n  if (!libdevice)\n    compilationError(err.getMessage().str(), err.getFilename().str(), err.getLineNo(),\n                     err.getColumnNo());\n  libdevice->setDataLayout(M->getDataLayout());\n  libdevice->setTargetTriple(M->getTargetTriple());\n\n  llvm::Linker L(*M);\n  const bool fail = L.linkInModule(std::move(libdevice));\n  seqassertn(!fail, \"linking libdevice failed\");\n}\n\nllvm::Function *copyPrototype(llvm::Function *F, const std::string &name,\n                              bool external = false) {\n  auto *M = F->getParent();\n  return llvm::Function::Create(F->getFunctionType(),\n                                external ? llvm::GlobalValue::ExternalLinkage\n                                         : llvm::GlobalValue::PrivateLinkage,\n                                name.empty() ? F->getName() : name, *M);\n}\n\nllvm::Function *makeNoOp(llvm::Function *F) {\n  auto *M = F->getParent();\n  auto &context = M->getContext();\n  auto dummyName = (\".codon.gpu.dummy.\" + F->getName()).str();\n  auto *dummy = M->getFunction(dummyName);\n  if (!dummy) {\n    dummy = copyPrototype(F, dummyName);\n    auto *entry = llvm::BasicBlock::Create(context, \"entry\", dummy);\n    llvm::IRBuilder<> B(entry);\n\n    auto *retType = F->getReturnType();\n    if (retType->isVoidTy()) {\n      B.CreateRetVoid();\n    } else {\n      B.CreateRet(llvm::UndefValue::get(retType));\n    }\n  }\n  return dummy;\n}\n\nusing Codegen =\n    std::function<void(llvm::IRBuilder<> &, const std::vector<llvm::Value *> &)>;\n\nvoid codegenVectorizedUnaryLoop(llvm::IRBuilder<> &B,\n                                const std::vector<llvm::Value *> &args,\n                                llvm::Function *func) {\n  // Create IR to represent:\n  //   p_in = in\n  //   p_out = out\n  //   for i in range(n):\n  //       *p_out = func(*p_in)\n  //       p_in += is\n  //       p_out += os\n  auto &context = B.getContext();\n  auto *parent = B.GetInsertBlock()->getParent();\n  auto *ty = func->getReturnType();\n  auto *in = args[0];\n  auto *is = args[1];\n  auto *out = args[2];\n  auto *os = args[3];\n  auto *n = args[4];\n\n  auto *loop = llvm::BasicBlock::Create(context, \"loop\", parent);\n  auto *exit = llvm::BasicBlock::Create(context, \"exit\", parent);\n\n  auto *pinStore = B.CreateAlloca(B.getPtrTy());\n  auto *poutStore = B.CreateAlloca(B.getPtrTy());\n  auto *idxStore = B.CreateAlloca(B.getInt64Ty());\n\n  // p_in = in\n  B.CreateStore(in, pinStore);\n  // p_out = out\n  B.CreateStore(out, poutStore);\n  // i = 0\n  B.CreateStore(B.getInt64(0), idxStore);\n  // if n > 0: goto loop; else: goto exit\n  B.CreateCondBr(B.CreateICmpSGT(n, B.getInt64(0)), loop, exit);\n\n  // load pointers\n  B.SetInsertPoint(loop);\n  auto *pin = B.CreateLoad(B.getPtrTy(), pinStore);\n  auto *pout = B.CreateLoad(B.getPtrTy(), poutStore);\n\n  // y = func(x)\n  auto *x = B.CreateLoad(ty, pin);\n  auto *y = B.CreateCall(func, x);\n  B.CreateStore(y, pout);\n\n  auto *idx = B.CreateLoad(B.getInt64Ty(), idxStore);\n  // i += 1\n  B.CreateStore(B.CreateAdd(idx, B.getInt64(1)), idxStore);\n  // p_in += is\n  B.CreateStore(B.CreateGEP(B.getInt8Ty(), pin, is), pinStore);\n  // p_out += os\n  B.CreateStore(B.CreateGEP(B.getInt8Ty(), pout, os), poutStore);\n\n  idx = B.CreateLoad(B.getInt64Ty(), idxStore);\n  // if i < n: goto loop; else: goto exit\n  B.CreateCondBr(B.CreateICmpSLT(idx, n), loop, exit);\n\n  B.SetInsertPoint(exit);\n  B.CreateRet(llvm::UndefValue::get(parent->getReturnType()));\n}\n\nvoid codegenVectorizedBinaryLoop(llvm::IRBuilder<> &B,\n                                 const std::vector<llvm::Value *> &args,\n                                 llvm::Function *func) {\n  // Create IR to represent:\n  //   p_in1 = in1\n  //   p_in2 = in2\n  //   p_out = out\n  //   for i in range(n):\n  //       *p_out = func(*p_in1, *p_in2)\n  //       p_in1 += is1\n  //       p_in2 += is2\n  //       p_out += os\n  auto &context = B.getContext();\n  auto *parent = B.GetInsertBlock()->getParent();\n  auto *ty = func->getReturnType();\n  auto *in1 = args[0];\n  auto *is1 = args[1];\n  auto *in2 = args[2];\n  auto *is2 = args[3];\n  auto *out = args[4];\n  auto *os = args[5];\n  auto *n = args[6];\n\n  auto *loop = llvm::BasicBlock::Create(context, \"loop\", parent);\n  auto *exit = llvm::BasicBlock::Create(context, \"exit\", parent);\n\n  auto *pin1Store = B.CreateAlloca(B.getPtrTy());\n  auto *pin2Store = B.CreateAlloca(B.getPtrTy());\n  auto *poutStore = B.CreateAlloca(B.getPtrTy());\n  auto *idxStore = B.CreateAlloca(B.getInt64Ty());\n\n  // p_in1 = in1\n  B.CreateStore(in1, pin1Store);\n  // p_in2 = in2\n  B.CreateStore(in2, pin2Store);\n  // p_out = out\n  B.CreateStore(out, poutStore);\n  // i = 0\n  B.CreateStore(B.getInt64(0), idxStore);\n  // if n > 0: goto loop; else: goto exit\n  B.CreateCondBr(B.CreateICmpSGT(n, B.getInt64(0)), loop, exit);\n\n  // load pointers\n  B.SetInsertPoint(loop);\n  auto *pin1 = B.CreateLoad(B.getPtrTy(), pin1Store);\n  auto *pin2 = B.CreateLoad(B.getPtrTy(), pin2Store);\n  auto *pout = B.CreateLoad(B.getPtrTy(), poutStore);\n\n  // y = func(x1, x2)\n  auto *x1 = B.CreateLoad(ty, pin1);\n  auto *x2 = B.CreateLoad(ty, pin2);\n  auto *y = B.CreateCall(func, {x1, x2});\n  B.CreateStore(y, pout);\n\n  auto *idx = B.CreateLoad(B.getInt64Ty(), idxStore);\n  // i += 1\n  B.CreateStore(B.CreateAdd(idx, B.getInt64(1)), idxStore);\n  // p_in1 += is1\n  B.CreateStore(B.CreateGEP(B.getInt8Ty(), pin1, is1), pin1Store);\n  // p_in2 += is2\n  B.CreateStore(B.CreateGEP(B.getInt8Ty(), pin2, is2), pin2Store);\n  // p_out += os\n  B.CreateStore(B.CreateGEP(B.getInt8Ty(), pout, os), poutStore);\n\n  idx = B.CreateLoad(B.getInt64Ty(), idxStore);\n  // if i < n: goto loop; else: goto exit\n  B.CreateCondBr(B.CreateICmpSLT(idx, n), loop, exit);\n\n  B.SetInsertPoint(exit);\n  B.CreateRet(llvm::UndefValue::get(parent->getReturnType()));\n}\n\nllvm::Function *makeFillIn(llvm::Function *F, Codegen codegen) {\n  auto *M = F->getParent();\n  auto &context = M->getContext();\n  auto fillInName = (\".codon.gpu.fillin.\" + F->getName()).str();\n  auto *fillIn = M->getFunction(fillInName);\n  if (!fillIn) {\n    fillIn = copyPrototype(F, fillInName);\n    std::vector<llvm::Value *> args;\n    for (auto it = fillIn->arg_begin(); it != fillIn->arg_end(); ++it) {\n      args.push_back(it);\n    }\n    auto *entry = llvm::BasicBlock::Create(context, \"entry\", fillIn);\n    llvm::IRBuilder<> B(entry);\n    codegen(B, args);\n  }\n  return fillIn;\n}\n\nllvm::Function *makeMalloc(llvm::Module *M) {\n  auto &context = M->getContext();\n  llvm::IRBuilder<> B(context);\n  auto F = M->getOrInsertFunction(\"malloc\", B.getPtrTy(), B.getInt64Ty());\n  auto *G = llvm::cast<llvm::Function>(F.getCallee());\n  G->setLinkage(llvm::GlobalValue::ExternalLinkage);\n  G->setDoesNotThrow();\n  G->setReturnDoesNotAlias();\n  G->setOnlyAccessesInaccessibleMemory();\n  G->setWillReturn();\n  return G;\n}\n\nvoid remapFunctions(llvm::Module *M) {\n  // simple name-to-name remappings\n  static const std::vector<std::pair<std::string, std::string>> remapping = {\n      // 64-bit float intrinsics\n      {\"llvm.ceil.f64\", \"__nv_ceil\"},\n      {\"llvm.floor.f64\", \"__nv_floor\"},\n      {\"llvm.fabs.f64\", \"__nv_fabs\"},\n      {\"llvm.exp.f64\", \"__nv_exp\"},\n      {\"llvm.log.f64\", \"__nv_log\"},\n      {\"llvm.log2.f64\", \"__nv_log2\"},\n      {\"llvm.log10.f64\", \"__nv_log10\"},\n      {\"llvm.sqrt.f64\", \"__nv_sqrt\"},\n      {\"llvm.pow.f64\", \"__nv_pow\"},\n      {\"llvm.sin.f64\", \"__nv_sin\"},\n      {\"llvm.cos.f64\", \"__nv_cos\"},\n      {\"llvm.copysign.f64\", \"__nv_copysign\"},\n      {\"llvm.trunc.f64\", \"__nv_trunc\"},\n      {\"llvm.rint.f64\", \"__nv_rint\"},\n      {\"llvm.nearbyint.f64\", \"__nv_nearbyint\"},\n      {\"llvm.round.f64\", \"__nv_round\"},\n      {\"llvm.minnum.f64\", \"__nv_fmin\"},\n      {\"llvm.maxnum.f64\", \"__nv_fmax\"},\n      {\"llvm.copysign.f64\", \"__nv_copysign\"},\n      {\"llvm.fma.f64\", \"__nv_fma\"},\n\n      // 64-bit float math functions\n      {\"expm1\", \"__nv_expm1\"},\n      {\"ldexp\", \"__nv_ldexp\"},\n      {\"acos\", \"__nv_acos\"},\n      {\"asin\", \"__nv_asin\"},\n      {\"atan\", \"__nv_atan\"},\n      {\"atan2\", \"__nv_atan2\"},\n      {\"hypot\", \"__nv_hypot\"},\n      {\"tan\", \"__nv_tan\"},\n      {\"cosh\", \"__nv_cosh\"},\n      {\"sinh\", \"__nv_sinh\"},\n      {\"tanh\", \"__nv_tanh\"},\n      {\"acosh\", \"__nv_acosh\"},\n      {\"asinh\", \"__nv_asinh\"},\n      {\"atanh\", \"__nv_atanh\"},\n      {\"erf\", \"__nv_erf\"},\n      {\"erfc\", \"__nv_erfc\"},\n      {\"tgamma\", \"__nv_tgamma\"},\n      {\"lgamma\", \"__nv_lgamma\"},\n      {\"remainder\", \"__nv_remainder\"},\n      {\"frexp\", \"__nv_frexp\"},\n      {\"modf\", \"__nv_modf\"},\n\n      // 32-bit float intrinsics\n      {\"llvm.ceil.f32\", \"__nv_ceilf\"},\n      {\"llvm.floor.f32\", \"__nv_floorf\"},\n      {\"llvm.fabs.f32\", \"__nv_fabsf\"},\n      {\"llvm.exp.f32\", \"__nv_expf\"},\n      {\"llvm.log.f32\", \"__nv_logf\"},\n      {\"llvm.log2.f32\", \"__nv_log2f\"},\n      {\"llvm.log10.f32\", \"__nv_log10f\"},\n      {\"llvm.sqrt.f32\", \"__nv_sqrtf\"},\n      {\"llvm.pow.f32\", \"__nv_powf\"},\n      {\"llvm.sin.f32\", \"__nv_sinf\"},\n      {\"llvm.cos.f32\", \"__nv_cosf\"},\n      {\"llvm.copysign.f32\", \"__nv_copysignf\"},\n      {\"llvm.trunc.f32\", \"__nv_truncf\"},\n      {\"llvm.rint.f32\", \"__nv_rintf\"},\n      {\"llvm.nearbyint.f32\", \"__nv_nearbyintf\"},\n      {\"llvm.round.f32\", \"__nv_roundf\"},\n      {\"llvm.minnum.f32\", \"__nv_fminf\"},\n      {\"llvm.maxnum.f32\", \"__nv_fmaxf\"},\n      {\"llvm.copysign.f32\", \"__nv_copysignf\"},\n      {\"llvm.fma.f32\", \"__nv_fmaf\"},\n\n      // 32-bit float math functions\n      {\"expm1f\", \"__nv_expm1f\"},\n      {\"ldexpf\", \"__nv_ldexpf\"},\n      {\"acosf\", \"__nv_acosf\"},\n      {\"asinf\", \"__nv_asinf\"},\n      {\"atanf\", \"__nv_atanf\"},\n      {\"atan2f\", \"__nv_atan2f\"},\n      {\"hypotf\", \"__nv_hypotf\"},\n      {\"tanf\", \"__nv_tanf\"},\n      {\"coshf\", \"__nv_coshf\"},\n      {\"sinhf\", \"__nv_sinhf\"},\n      {\"tanhf\", \"__nv_tanhf\"},\n      {\"acoshf\", \"__nv_acoshf\"},\n      {\"asinhf\", \"__nv_asinhf\"},\n      {\"atanhf\", \"__nv_atanhf\"},\n      {\"erff\", \"__nv_erff\"},\n      {\"erfcf\", \"__nv_erfcf\"},\n      {\"tgammaf\", \"__nv_tgammaf\"},\n      {\"lgammaf\", \"__nv_lgammaf\"},\n      {\"remainderf\", \"__nv_remainderf\"},\n      {\"frexpf\", \"__nv_frexpf\"},\n      {\"modff\", \"__nv_modff\"},\n\n      // runtime library functions\n      {\"seq_free\", \"free\"},\n      {\"seq_register_finalizer\", \"\"},\n      {\"seq_gc_add_roots\", \"\"},\n      {\"seq_gc_remove_roots\", \"\"},\n      {\"seq_gc_clear_roots\", \"\"},\n      {\"seq_gc_exclude_static_roots\", \"\"},\n  };\n\n  // functions that need to be generated as they're not available on GPU\n  static const std::vector<std::pair<std::string, Codegen>> fillins = {\n      {\"seq_alloc\",\n       [](llvm::IRBuilder<> &B, const std::vector<llvm::Value *> &args) {\n         auto *M = B.GetInsertBlock()->getModule();\n         llvm::Value *mem = B.CreateCall(makeMalloc(M), args[0]);\n         B.CreateRet(mem);\n       }},\n\n      {\"seq_alloc_uncollectable\",\n       [](llvm::IRBuilder<> &B, const std::vector<llvm::Value *> &args) {\n         auto *M = B.GetInsertBlock()->getModule();\n         llvm::Value *mem = B.CreateCall(makeMalloc(M), args[0]);\n         B.CreateRet(mem);\n       }},\n\n      {\"seq_alloc_atomic\",\n       [](llvm::IRBuilder<> &B, const std::vector<llvm::Value *> &args) {\n         auto *M = B.GetInsertBlock()->getModule();\n         llvm::Value *mem = B.CreateCall(makeMalloc(M), args[0]);\n         B.CreateRet(mem);\n       }},\n\n      {\"seq_alloc_atomic_uncollectable\",\n       [](llvm::IRBuilder<> &B, const std::vector<llvm::Value *> &args) {\n         auto *M = B.GetInsertBlock()->getModule();\n         llvm::Value *mem = B.CreateCall(makeMalloc(M), args[0]);\n         B.CreateRet(mem);\n       }},\n\n      {\"seq_realloc\",\n       [](llvm::IRBuilder<> &B, const std::vector<llvm::Value *> &args) {\n         auto *M = B.GetInsertBlock()->getModule();\n         llvm::Value *mem = B.CreateCall(makeMalloc(M), args[1]);\n         auto F = llvm::Intrinsic::getDeclaration(\n             M, llvm::Intrinsic::memcpy, {B.getPtrTy(), B.getPtrTy(), B.getInt64Ty()});\n         B.CreateCall(F, {mem, args[0], args[2], B.getFalse()});\n         B.CreateRet(mem);\n       }},\n\n      {\"seq_calloc\",\n       [](llvm::IRBuilder<> &B, const std::vector<llvm::Value *> &args) {\n         auto *M = B.GetInsertBlock()->getModule();\n         llvm::Value *size = B.CreateMul(args[0], args[1]);\n         llvm::Value *mem = B.CreateCall(makeMalloc(M), size);\n         auto F = llvm::Intrinsic::getDeclaration(M, llvm::Intrinsic::memset,\n                                                  {B.getPtrTy(), B.getInt64Ty()});\n         B.CreateCall(F, {mem, B.getInt8(0), size, B.getFalse()});\n         B.CreateRet(mem);\n       }},\n\n      {\"seq_calloc_atomic\",\n       [](llvm::IRBuilder<> &B, const std::vector<llvm::Value *> &args) {\n         auto *M = B.GetInsertBlock()->getModule();\n         llvm::Value *size = B.CreateMul(args[0], args[1]);\n         llvm::Value *mem = B.CreateCall(makeMalloc(M), size);\n         auto F = llvm::Intrinsic::getDeclaration(M, llvm::Intrinsic::memset,\n                                                  {B.getPtrTy(), B.getInt64Ty()});\n         B.CreateCall(F, {mem, B.getInt8(0), size, B.getFalse()});\n         B.CreateRet(mem);\n       }},\n\n      {\"seq_alloc_exc\",\n       [](llvm::IRBuilder<> &B, const std::vector<llvm::Value *> &args) {\n         // TODO: print error message and abort if in debug mode\n         B.CreateUnreachable();\n       }},\n\n      {\"seq_throw\",\n       [](llvm::IRBuilder<> &B, const std::vector<llvm::Value *> &args) {\n         B.CreateUnreachable();\n       }},\n\n#define FILLIN_VECLOOP_UNARY32(loop, func)                                             \\\n  {                                                                                    \\\n    loop, [](llvm::IRBuilder<> &B, const std::vector<llvm::Value *> &args) {           \\\n      auto *M = B.GetInsertBlock()->getModule();                                       \\\n      auto f = llvm::cast<llvm::Function>(                                             \\\n          M->getOrInsertFunction(func, B.getFloatTy(), B.getFloatTy()).getCallee());   \\\n      f->setWillReturn();                                                              \\\n      codegenVectorizedUnaryLoop(B, args, f);                                          \\\n    }                                                                                  \\\n  }\n\n#define FILLIN_VECLOOP_UNARY64(loop, func)                                             \\\n  {                                                                                    \\\n    loop, [](llvm::IRBuilder<> &B, const std::vector<llvm::Value *> &args) {           \\\n      auto *M = B.GetInsertBlock()->getModule();                                       \\\n      auto f = llvm::cast<llvm::Function>(                                             \\\n          M->getOrInsertFunction(func, B.getDoubleTy(), B.getDoubleTy()).getCallee()); \\\n      f->setWillReturn();                                                              \\\n      codegenVectorizedUnaryLoop(B, args, f);                                          \\\n    }                                                                                  \\\n  }\n\n#define FILLIN_VECLOOP_BINARY32(loop, func)                                            \\\n  {                                                                                    \\\n    loop, [](llvm::IRBuilder<> &B, const std::vector<llvm::Value *> &args) {           \\\n      auto *M = B.GetInsertBlock()->getModule();                                       \\\n      auto f = llvm::cast<llvm::Function>(                                             \\\n          M->getOrInsertFunction(func, B.getFloatTy(), B.getFloatTy(), B.getFloatTy()) \\\n              .getCallee());                                                           \\\n      f->setWillReturn();                                                              \\\n      codegenVectorizedBinaryLoop(B, args, f);                                         \\\n    }                                                                                  \\\n  }\n\n#define FILLIN_VECLOOP_BINARY64(loop, func)                                            \\\n  {                                                                                    \\\n    loop, [](llvm::IRBuilder<> &B, const std::vector<llvm::Value *> &args) {           \\\n      auto *M = B.GetInsertBlock()->getModule();                                       \\\n      auto f = llvm::cast<llvm::Function>(                                             \\\n          M->getOrInsertFunction(func, B.getDoubleTy(), B.getDoubleTy(),               \\\n                                 B.getDoubleTy())                                      \\\n              .getCallee());                                                           \\\n      f->setWillReturn();                                                              \\\n      codegenVectorizedBinaryLoop(B, args, f);                                         \\\n    }                                                                                  \\\n  }\n\n      FILLIN_VECLOOP_UNARY64(\"cnp_acos_float64\", \"__nv_acos\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_acosh_float64\", \"__nv_acosh\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_asin_float64\", \"__nv_asin\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_asinh_float64\", \"__nv_asinh\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_atan_float64\", \"__nv_atan\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_atanh_float64\", \"__nv_atanh\"),\n      FILLIN_VECLOOP_BINARY64(\"cnp_atan2_float64\", \"__nv_atan2\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_exp_float64\", \"__nv_exp\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_exp2_float64\", \"__nv_exp2\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_expm1_float64\", \"__nv_expm1\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_log_float64\", \"__nv_log\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_log10_float64\", \"__nv_log10\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_log1p_float64\", \"__nv_log1p\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_log2_float64\", \"__nv_log2\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_sin_float64\", \"__nv_sin\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_sinh_float64\", \"__nv_sinh\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_tan_float64\", \"__nv_tan\"),\n      FILLIN_VECLOOP_UNARY64(\"cnp_tanh_float64\", \"__nv_tanh\"),\n      FILLIN_VECLOOP_BINARY64(\"cnp_hypot_float64\", \"__nv_hypot\"),\n\n      FILLIN_VECLOOP_UNARY32(\"cnp_acos_float32\", \"__nv_acosf\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_acosh_float32\", \"__nv_acoshf\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_asin_float32\", \"__nv_asinf\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_asinh_float32\", \"__nv_asinhf\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_atan_float32\", \"__nv_atanf\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_atanh_float32\", \"__nv_atanhf\"),\n      FILLIN_VECLOOP_BINARY32(\"cnp_atan2_float32\", \"__nv_atan2f\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_exp_float32\", \"__nv_expf\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_exp2_float32\", \"__nv_exp2f\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_expm1_float32\", \"__nv_expm1f\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_log_float32\", \"__nv_logf\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_log10_float32\", \"__nv_log10f\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_log1p_float32\", \"__nv_log1pf\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_log2_float32\", \"__nv_log2f\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_sin_float32\", \"__nv_sinf\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_sinh_float32\", \"__nv_sinhf\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_tan_float32\", \"__nv_tanf\"),\n      FILLIN_VECLOOP_UNARY32(\"cnp_tanh_float32\", \"__nv_tanhf\"),\n      FILLIN_VECLOOP_BINARY32(\"cnp_hypot_float32\", \"__nv_hypotf\"),\n  };\n\n  for (auto &pair : remapping) {\n    if (auto *F = M->getFunction(pair.first)) {\n      llvm::Function *G = nullptr;\n      if (pair.second.empty()) {\n        G = makeNoOp(F);\n      } else {\n        G = M->getFunction(pair.second);\n        if (!G)\n          G = copyPrototype(F, pair.second, /*external=*/true);\n      }\n\n      G->setWillReturn();\n      F->replaceAllUsesWith(G);\n      F->dropAllReferences();\n      F->eraseFromParent();\n    }\n  }\n\n  for (auto &pair : fillins) {\n    if (auto *F = M->getFunction(pair.first)) {\n      llvm::Function *G = makeFillIn(F, pair.second);\n      F->replaceAllUsesWith(G);\n      F->dropAllReferences();\n      F->eraseFromParent();\n    }\n  }\n}\n\nvoid exploreGV(llvm::GlobalValue *G, llvm::SmallPtrSetImpl<llvm::GlobalValue *> &keep) {\n  if (keep.contains(G))\n    return;\n\n  keep.insert(G);\n  if (auto *F = llvm::dyn_cast<llvm::Function>(G)) {\n    for (auto I = llvm::inst_begin(F), E = inst_end(F); I != E; ++I) {\n      for (auto &U : I->operands()) {\n        if (auto *G2 = llvm::dyn_cast<llvm::GlobalValue>(U.get()))\n          exploreGV(G2, keep);\n      }\n    }\n  }\n}\n\nstd::vector<llvm::GlobalValue *>\ngetRequiredGVs(const std::vector<llvm::GlobalValue *> &kernels) {\n  llvm::SmallPtrSet<llvm::GlobalValue *, 32> keep;\n  for (auto *G : kernels) {\n    exploreGV(G, keep);\n  }\n  return std::vector<llvm::GlobalValue *>(keep.begin(), keep.end());\n}\n\nstd::string moduleToPTX(llvm::Module *M, std::vector<llvm::GlobalValue *> &kernels) {\n  llvm::Triple triple(llvm::Triple::normalize(GPU_TRIPLE));\n  llvm::TargetLibraryInfoImpl tlii(triple);\n\n  std::string err;\n  const llvm::Target *target =\n      llvm::TargetRegistry::lookupTarget(\"nvptx64\", triple, err);\n  seqassertn(target, \"couldn't lookup target: {}\", err);\n\n  const llvm::TargetOptions options =\n      llvm::codegen::InitTargetOptionsFromCodeGenFlags(triple);\n\n  std::unique_ptr<llvm::TargetMachine> machine(target->createTargetMachine(\n      triple.getTriple(), gpuName, gpuFeatures, options,\n      llvm::codegen::getExplicitRelocModel(), llvm::codegen::getExplicitCodeModel(),\n      llvm::CodeGenOptLevel::Aggressive));\n\n  // Remove personality functions\n  for (auto &F : *M) {\n    F.setDoesNotThrow();\n    F.setPersonalityFn(nullptr);\n  }\n\n  M->setDataLayout(machine->createDataLayout());\n  auto keep = getRequiredGVs(kernels);\n\n  auto prune = [&](std::vector<llvm::GlobalValue *> keep) {\n    llvm::LoopAnalysisManager lam;\n    llvm::FunctionAnalysisManager fam;\n    llvm::CGSCCAnalysisManager cgam;\n    llvm::ModuleAnalysisManager mam;\n    llvm::ModulePassManager mpm;\n    llvm::PassBuilder pb;\n\n    pb.registerModuleAnalyses(mam);\n    pb.registerCGSCCAnalyses(cgam);\n    pb.registerFunctionAnalyses(fam);\n    pb.registerLoopAnalyses(lam);\n    pb.crossRegisterProxies(lam, fam, cgam, mam);\n\n    mpm.addPass(GVExtractor(keep, false));\n    mpm.addPass(llvm::GlobalDCEPass());\n    mpm.addPass(llvm::StripDeadDebugInfoPass());\n    mpm.addPass(llvm::StripDeadPrototypesPass());\n    mpm.run(*M, mam);\n  };\n\n  // Remove non-kernel functions.\n  prune(keep);\n\n  // Link libdevice and other cleanup.\n  linkLibdevice(M, libdevice);\n  remapFunctions(M);\n\n  // Strip debug info and remove noinline from functions (added in debug mode).\n  // Also, tell LLVM that all functions will return.\n  for (auto &F : *M) {\n    F.removeFnAttr(llvm::Attribute::AttrKind::NoInline);\n    F.setWillReturn();\n  }\n  llvm::StripDebugInfo(*M);\n\n  // Run NVPTX passes and general opt pipeline.\n  {\n    llvm::LoopAnalysisManager lam;\n    llvm::FunctionAnalysisManager fam;\n    llvm::CGSCCAnalysisManager cgam;\n    llvm::ModuleAnalysisManager mam;\n    llvm::PassBuilder pb(machine.get());\n\n    llvm::TargetLibraryInfoImpl tlii(triple);\n    fam.registerPass([&] { return llvm::TargetLibraryAnalysis(tlii); });\n\n    pb.registerModuleAnalyses(mam);\n    pb.registerCGSCCAnalyses(cgam);\n    pb.registerFunctionAnalyses(fam);\n    pb.registerLoopAnalyses(lam);\n    pb.crossRegisterProxies(lam, fam, cgam, mam);\n\n    pb.registerPipelineStartEPCallback(\n        [&](llvm::ModulePassManager &pm, llvm::OptimizationLevel opt) {\n          pm.addPass(llvm::InternalizePass([&](const llvm::GlobalValue &gv) {\n            return std::find(keep.begin(), keep.end(), &gv) != keep.end();\n          }));\n        });\n\n    llvm::ModulePassManager mpm =\n        pb.buildPerModuleDefaultPipeline(llvm::OptimizationLevel::O3);\n    mpm.run(*M, mam);\n  }\n\n  // Prune again after optimizations.\n  keep = getRequiredGVs(kernels);\n  prune(keep);\n\n  // Clean up names.\n  {\n    for (auto &G : M->globals()) {\n      G.setName(cleanUpName(G.getName()));\n    }\n\n    for (auto &F : M->functions()) {\n      if (F.getInstructionCount() > 0)\n        F.setName(cleanUpName(F.getName()));\n    }\n\n    for (auto *S : M->getIdentifiedStructTypes()) {\n      S->setName(cleanUpName(S->getName()));\n    }\n  }\n\n  // Generate PTX code.\n  {\n    llvm::SmallVector<char, 1024> ptx;\n    llvm::raw_svector_ostream os(ptx);\n\n    auto *mmiwp = new llvm::MachineModuleInfoWrapperPass(machine.get());\n    llvm::legacy::PassManager pm;\n\n    pm.add(new llvm::TargetLibraryInfoWrapperPass(tlii));\n    bool fail = machine->addPassesToEmitFile(pm, os, nullptr,\n                                             llvm::CodeGenFileType::AssemblyFile,\n                                             /*DisableVerify=*/false, mmiwp);\n    seqassertn(!fail, \"could not add passes\");\n\n    const_cast<llvm::TargetLoweringObjectFile *>(machine->getObjFileLowering())\n        ->Initialize(mmiwp->getMMI().getContext(), *machine);\n\n    pm.run(*M);\n    return std::string(ptx.data(), ptx.size());\n  }\n}\n\nvoid cleanUpIntrinsics(llvm::Module *M) {\n  llvm::LLVMContext &context = M->getContext();\n  llvm::SmallVector<llvm::Function *, 16> remove;\n  for (auto &F : *M) {\n    if (F.getIntrinsicID() != llvm::Intrinsic::not_intrinsic &&\n        F.getName().starts_with(\"llvm.nvvm\"))\n      remove.push_back(&F);\n  }\n\n  for (auto *F : remove) {\n    F->replaceAllUsesWith(makeNoOp(F));\n    F->dropAllReferences();\n    F->eraseFromParent();\n  }\n}\n\nvoid patchPTXVar(llvm::Module *M, llvm::GlobalValue *ptxVar,\n                 const std::string &ptxTarget = \"__codon_ptx__\") {\n  // Find and patch direct calls to cuModuleLoadData()\n  llvm::SmallVector<llvm::Instruction *, 1> callsToReplace;\n  for (auto &F : *M) {\n    for (auto &BB : F) {\n      for (auto &I : BB) {\n        auto *call = llvm::dyn_cast<llvm::CallBase>(&I);\n        if (!call)\n          continue;\n\n        auto *callee = call->getCalledFunction();\n        if (!callee)\n          continue;\n\n        if (callee->getName() == ptxTarget && call->arg_size() == 0)\n          callsToReplace.push_back(call);\n      }\n    }\n  }\n\n  for (auto *call : callsToReplace) {\n    if (ptxVar) {\n      call->replaceAllUsesWith(ptxVar);\n    } else {\n      call->replaceAllUsesWith(\n          llvm::ConstantPointerNull::get(llvm::PointerType::get(M->getContext(), 0)));\n    }\n    call->dropAllReferences();\n    call->eraseFromParent();\n  }\n\n  // Delete __codon_ptx__() stub\n  if (auto *F = M->getFunction(ptxTarget)) {\n    seqassertn(F->use_empty(), \"some __codon_ptx__() calls not replaced in module\");\n    F->eraseFromParent();\n  }\n}\n} // namespace\n\nvoid applyGPUTransformations(llvm::Module *M, const std::string &ptxFilename) {\n  llvm::LLVMContext &context = M->getContext();\n  std::unique_ptr<llvm::Module> clone = llvm::CloneModule(*M);\n  clone->setTargetTriple(llvm::Triple::normalize(GPU_TRIPLE));\n  clone->setDataLayout(GPU_DL);\n\n  if (isFastMathOn()) {\n    clone->addModuleFlag(llvm::Module::ModFlagBehavior::Override, \"nvvm-reflect-ftz\",\n                         1);\n  }\n\n  llvm::NamedMDNode *nvvmAnno = clone->getOrInsertNamedMetadata(\"nvvm.annotations\");\n  std::vector<llvm::GlobalValue *> kernels;\n\n  for (auto &F : *clone) {\n    if (!F.hasFnAttribute(\"kernel\"))\n      continue;\n\n    llvm::Metadata *nvvmElem[] = {\n        llvm::ConstantAsMetadata::get(&F),\n        llvm::MDString::get(context, \"kernel\"),\n        llvm::ConstantAsMetadata::get(\n            llvm::ConstantInt::get(llvm::Type::getInt32Ty(context), 1)),\n    };\n\n    nvvmAnno->addOperand(llvm::MDNode::get(context, nvvmElem));\n    kernels.push_back(&F);\n  }\n\n  if (kernels.empty()) {\n    patchPTXVar(M, nullptr);\n    return;\n  }\n\n  auto ptx = moduleToPTX(clone.get(), kernels);\n  cleanUpIntrinsics(M);\n\n  if (ptxOutput.getNumOccurrences() > 0) {\n    std::error_code err;\n    llvm::ToolOutputFile out(ptxOutput, err, llvm::sys::fs::OF_Text);\n    seqassertn(!err, \"Could not open file: {}\", err.message());\n    llvm::raw_ostream &os = out.os();\n    os << ptx;\n    os.flush();\n    out.keep();\n  }\n\n  // Add ptx code as a global var\n  auto *ptxVar = new llvm::GlobalVariable(\n      *M, llvm::ArrayType::get(llvm::Type::getInt8Ty(context), ptx.length() + 1),\n      /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage,\n      llvm::ConstantDataArray::getString(context, ptx), \".ptx\");\n\n  ptxVar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);\n  patchPTXVar(M, ptxVar);\n}\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/llvm/gpu.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <string>\n\n#include \"codon/cir/llvm/llvm.h\"\n\nnamespace codon {\nnamespace ir {\n\n/// Applies GPU-specific transformations and generates PTX\n/// code from kernel functions in the given LLVM module.\n/// @param module LLVM module containing GPU kernel functions (marked with \"kernel\"\n/// annotation)\n/// @param ptxFilename Filename for output PTX code; empty to use filename based on\n/// module\nvoid applyGPUTransformations(llvm::Module *module, const std::string &ptxFilename = \"\");\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/llvm/llvisitor.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"llvisitor.h\"\n\n#include <algorithm>\n#include <cctype>\n#include <cstdlib>\n#include <fmt/args.h>\n#include <sys/wait.h>\n#include <unistd.h>\n#include <utility>\n\n#include \"codon/cir/dsl/codegen.h\"\n#include \"codon/cir/llvm/optimize.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/compiler/debug_listener.h\"\n#include \"codon/compiler/memory_manager.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/runtime/lib.h\"\n#include \"codon/util/common.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace {\nconst std::string EXPORT_ATTR =\n    ast::getMangledFunc(\"std.internal.attributes\", \"export\");\nconst std::string INLINE_ATTR =\n    ast::getMangledFunc(\"std.internal.attributes\", \"inline\");\nconst std::string NOINLINE_ATTR =\n    ast::getMangledFunc(\"std.internal.attributes\", \"noinline\");\nconst std::string GPU_KERNEL_ATTR = ast::getMangledFunc(\"std.internal.gpu\", \"kernel\");\n\nconst std::string MAIN_UNCLASH = \".main.unclash\";\nconst std::string MAIN_CTOR = \".main.ctor\";\n\nenum GlobalCTORMode { No, Yes, Auto };\nllvm::cl::opt<GlobalCTORMode> GlobalCTOR(\n    \"global-ctor\", llvm::cl::desc(\"generate global constructor with main code\"),\n    llvm::cl::values(clEnumValN(No, \"no\", \"Keep main code in main() function\"),\n                     clEnumValN(Yes, \"yes\", \"Put main code in global constructor\"),\n                     clEnumValN(Auto, \"auto\",\n                                \"'yes' if shared library output, 'no' otherwise\")),\n    llvm::cl::init(Auto));\nllvm::cl::opt<bool> DisableExceptions(\"disable-exceptions\",\n                                      llvm::cl::desc(\"Disable exception handling\"),\n                                      llvm::cl::init(false));\n} // namespace\n\nllvm::DIFile *LLVMVisitor::DebugInfo::getFile(const std::string &path) {\n  std::string filename;\n  std::string directory;\n  auto pos = path.find_last_of(\"/\");\n  if (pos != std::string::npos) {\n    filename = path.substr(pos + 1);\n    directory = path.substr(0, pos);\n  } else {\n    filename = path;\n    directory = \".\";\n  }\n  return builder->createFile(filename, directory);\n}\n\nstd::string LLVMVisitor::getGlobalCtorName() { return MAIN_CTOR; }\n\nstd::string LLVMVisitor::getNameForFunction(const Func *x) {\n  if (isA<ExternalFunc>(x) || util::hasAttribute(x, EXPORT_ATTR)) {\n    return x->getUnmangledName();\n  } else if (util::hasAttribute(x, GPU_KERNEL_ATTR)) {\n    return x->getName();\n  } else {\n    return x->referenceString();\n  }\n}\n\nstd::string LLVMVisitor::getNameForVar(const Var *x) {\n  if (auto *f = cast<Func>(x))\n    return getNameForFunction(f);\n\n  auto name = x->getName();\n  if (x->isExternal()) {\n    return name;\n  } else {\n    // \".Lxxx\" is a linker-local name, so add an underscore if needed\n    return ((!name.empty() && name[0] == 'L') ? \"._\" : \".\") + name;\n  }\n}\n\nLLVMVisitor::LLVMVisitor()\n    : util::ConstVisitor(), context(std::make_unique<llvm::LLVMContext>()), M(),\n      B(std::make_unique<llvm::IRBuilder<>>(*context)), func(nullptr), block(nullptr),\n      value(nullptr), vars(), funcs(), coro(), loops(), trycatch(), finally(),\n      catches(), db(), plugins(nullptr) {\n  llvm::InitializeAllTargets();\n  llvm::InitializeAllTargetMCs();\n  llvm::InitializeAllAsmPrinters();\n  llvm::InitializeAllAsmParsers();\n\n  // Initialize passes\n  auto &registry = *llvm::PassRegistry::getPassRegistry();\n  llvm::initializeCore(registry);\n  llvm::initializeScalarOpts(registry);\n  llvm::initializeVectorization(registry);\n  llvm::initializeIPO(registry);\n  llvm::initializeAnalysis(registry);\n  llvm::initializeTransformUtils(registry);\n  llvm::initializeInstCombine(registry);\n  llvm::initializeTarget(registry);\n\n  llvm::initializeExpandLargeDivRemLegacyPassPass(registry);\n  llvm::initializeExpandLargeFpConvertLegacyPassPass(registry);\n  llvm::initializeExpandMemCmpLegacyPassPass(registry);\n  llvm::initializeScalarizeMaskedMemIntrinLegacyPassPass(registry);\n  llvm::initializeSelectOptimizePass(registry);\n  llvm::initializeCallBrPreparePass(registry);\n  llvm::initializeCodeGenPrepareLegacyPassPass(registry);\n  llvm::initializeAtomicExpandLegacyPass(registry);\n  llvm::initializeWinEHPreparePass(registry);\n  llvm::initializeDwarfEHPrepareLegacyPassPass(registry);\n  llvm::initializeSafeStackLegacyPassPass(registry);\n  llvm::initializeSjLjEHPreparePass(registry);\n  llvm::initializePreISelIntrinsicLoweringLegacyPassPass(registry);\n  llvm::initializeGlobalMergePass(registry);\n  llvm::initializeIndirectBrExpandLegacyPassPass(registry);\n  llvm::initializeInterleavedLoadCombinePass(registry);\n  llvm::initializeInterleavedAccessPass(registry);\n  llvm::initializePostInlineEntryExitInstrumenterPass(registry);\n  llvm::initializeUnreachableBlockElimLegacyPassPass(registry);\n  llvm::initializeExpandReductionsPass(registry);\n  llvm::initializeWasmEHPreparePass(registry);\n  llvm::initializeWriteBitcodePassPass(registry);\n  llvm::initializeReplaceWithVeclibLegacyPass(registry);\n  llvm::initializeJMCInstrumenterPass(registry);\n}\n\nvoid LLVMVisitor::registerGlobal(const Var *var) {\n  if (!var->isGlobal())\n    return;\n\n  if (auto *f = cast<Func>(var)) {\n    insertFunc(f, makeLLVMFunction(f));\n  } else {\n    auto *llvmType = getLLVMType(var->getType());\n    if (llvmType->isVoidTy()) {\n      insertVar(var, getDummyVoidValue());\n    } else {\n      bool external = var->isExternal();\n      bool tls = var->isThreadLocal();\n      auto linkage = (db.jit || external) ? llvm::GlobalValue::ExternalLinkage\n                                          : llvm::GlobalValue::PrivateLinkage;\n      auto *storage = new llvm::GlobalVariable(\n          *M, llvmType, /*isConstant=*/false, linkage,\n          external ? nullptr : llvm::Constant::getNullValue(llvmType),\n          getNameForVar(var));\n      insertVar(var, storage);\n\n      if (external) {\n        if (db.jit) {\n          storage->setDSOLocal(true);\n        } else {\n          storage->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Local);\n        }\n      } else {\n        // debug info\n        auto *srcInfo = getSrcInfo(var);\n        auto *file = db.getFile(srcInfo->file);\n        auto *scope = db.unit;\n        auto *debugVar = db.builder->createGlobalVariableExpression(\n            scope, getDebugNameForVariable(var), var->getName(), file, srcInfo->line,\n            getDIType(var->getType()), !var->isExternal());\n        storage->addDebugInfo(debugVar);\n      }\n\n      if (tls)\n        storage->setThreadLocal(true);\n    }\n  }\n}\n\nllvm::Value *LLVMVisitor::getVar(const Var *var) {\n  auto it = vars.find(var->getId());\n  if (db.jit && var->isGlobal()) {\n    if (it != vars.end()) {\n      if (!it->second) { // if value is null, it's from another module\n        // see if it's in the module already\n        auto name = var->getName();\n        auto privName = getNameForVar(var);\n        if (auto *global = M->getNamedValue(privName))\n          return global;\n\n        auto *llvmType = getLLVMType(var->getType());\n        auto *storage = new llvm::GlobalVariable(*M, llvmType, /*isConstant=*/false,\n                                                 llvm::GlobalValue::ExternalLinkage,\n                                                 /*Initializer=*/nullptr, privName);\n        storage->setExternallyInitialized(true);\n\n        // debug info\n        auto *srcInfo = getSrcInfo(var);\n        auto *file = db.getFile(srcInfo->file);\n        auto *scope = db.unit;\n        auto *debugVar = db.builder->createGlobalVariableExpression(\n            scope, getDebugNameForVariable(var), name, file, srcInfo->line,\n            getDIType(var->getType()),\n            /*IsLocalToUnit=*/true);\n        storage->addDebugInfo(debugVar);\n        insertVar(var, storage);\n        return storage;\n      }\n    } else {\n      registerGlobal(var);\n      it = vars.find(var->getId());\n      return it->second;\n    }\n  }\n  return (it != vars.end()) ? it->second : nullptr;\n}\n\nllvm::Function *LLVMVisitor::getFunc(const Func *func) {\n  auto it = funcs.find(func->getId());\n  if (db.jit) {\n    if (it != funcs.end()) {\n      if (!it->second) { // if value is null, it's from another module\n        // see if it's in the module already\n        const std::string name = getNameForFunction(func);\n        if (auto *g = M->getFunction(name))\n          return g;\n\n        auto *funcType = cast<types::FuncType>(func->getType());\n        auto *returnType = getLLVMType(funcType->getReturnType());\n        std::vector<llvm::Type *> argTypes;\n        for (const auto &argType : *funcType) {\n          argTypes.push_back(getLLVMType(argType));\n        }\n\n        auto *llvmFuncType =\n            llvm::FunctionType::get(returnType, argTypes, funcType->isVariadic());\n        auto *g = llvm::Function::Create(llvmFuncType, llvm::Function::ExternalLinkage,\n                                         name, M.get());\n        insertFunc(func, g);\n        return g;\n      }\n    } else {\n      registerGlobal(func);\n      it = funcs.find(func->getId());\n      return it->second;\n    }\n  }\n  return (it != funcs.end()) ? it->second : nullptr;\n}\n\nstd::unique_ptr<llvm::Module> LLVMVisitor::makeModule(llvm::LLVMContext &context,\n                                                      const SrcInfo *src) {\n  auto builder = llvm::EngineBuilder();\n  builder.setMArch(llvm::codegen::getMArch());\n  builder.setMCPU(llvm::codegen::getCPUStr());\n  builder.setMAttrs(llvm::codegen::getFeatureList());\n\n  auto target = builder.selectTarget();\n  auto M = std::make_unique<llvm::Module>(\"codon\", context);\n  M->setTargetTriple(target->getTargetTriple().str());\n  M->setDataLayout(target->createDataLayout());\n  B = std::make_unique<llvm::IRBuilder<>>(context);\n\n  auto *srcInfo = src ? src : getDefaultSrcInfo();\n  M->setSourceFileName(srcInfo->file);\n  // debug info setup\n  db.builder = std::make_unique<llvm::DIBuilder>(*M);\n  auto *file = db.getFile(srcInfo->file);\n  db.unit = db.builder->createCompileUnit(llvm::dwarf::DW_LANG_C, file,\n                                          (\"codon version \" CODON_VERSION), !db.debug,\n                                          db.flags,\n                                          /*RV=*/0);\n  M->addModuleFlag(llvm::Module::Warning, \"Debug Info Version\",\n                   llvm::DEBUG_METADATA_VERSION);\n  // darwin only supports dwarf2\n  if (llvm::Triple(M->getTargetTriple()).isOSDarwin()) {\n    M->addModuleFlag(llvm::Module::Warning, \"Dwarf Version\", 2);\n  }\n\n  return M;\n}\n\nvoid LLVMVisitor::clearLLVMData() {\n  B = {};\n  func = nullptr;\n  block = nullptr;\n  value = nullptr;\n\n  for (auto it = funcs.begin(); it != funcs.end();) {\n    if (it->second && it->second->hasPrivateLinkage()) {\n      it = funcs.erase(it);\n    } else {\n      it->second = nullptr;\n      ++it;\n    }\n  }\n\n  for (auto it = vars.begin(); it != vars.end();) {\n    if (it->second && !llvm::isa<llvm::GlobalValue>(it->second)) {\n      it = vars.erase(it);\n    } else {\n      it->second = nullptr;\n      ++it;\n    }\n  }\n\n  coro.reset();\n  loops.clear();\n  trycatch.clear();\n  finally.clear();\n  catches.clear();\n  db.reset();\n  context = {};\n  M = {};\n}\n\nstd::pair<std::unique_ptr<llvm::Module>, std::unique_ptr<llvm::LLVMContext>>\nLLVMVisitor::takeModule(Module *module, const SrcInfo *src) {\n  // process any new functions or globals\n  if (module) {\n    std::unordered_set<id_t> funcsToProcess;\n    for (auto *var : *module) {\n      auto id = var->getId();\n      if (auto *func = cast<Func>(var)) {\n        if (funcs.find(id) != funcs.end())\n          continue;\n        else\n          funcsToProcess.insert(id);\n      } else {\n        if (vars.find(id) != vars.end())\n          continue;\n      }\n\n      registerGlobal(var);\n    }\n\n    for (auto *var : *module) {\n      if (auto *func = cast<Func>(var)) {\n        if (funcsToProcess.find(func->getId()) != funcsToProcess.end()) {\n          process(func);\n        }\n      }\n    }\n  }\n\n  db.builder->finalize();\n  auto currentContext = std::move(context);\n  auto currentModule = std::move(M);\n\n  // reset all LLVM fields/data -- they are owned by the context\n  clearLLVMData();\n  context = std::make_unique<llvm::LLVMContext>();\n  M = makeModule(*context, src);\n  return {std::move(currentModule), std::move(currentContext)};\n}\n\nvoid LLVMVisitor::setDebugInfoForNode(const Node *x) {\n  if (x && func) {\n    auto *srcInfo = getSrcInfo(x);\n    B->SetCurrentDebugLocation(llvm::DILocation::get(\n        *context, srcInfo->line, srcInfo->col, func->getSubprogram()));\n  } else {\n    B->SetCurrentDebugLocation(llvm::DebugLoc());\n  }\n}\n\nvoid LLVMVisitor::process(const Node *x) {\n  setDebugInfoForNode(x);\n  x->accept(*this);\n}\n\nvoid LLVMVisitor::dump(const std::string &filename) { writeToLLFile(filename, false); }\n\nvoid LLVMVisitor::runLLVMPipeline() {\n  db.builder->finalize();\n  optimize(M.get(), db.debug, db.jit, plugins);\n}\n\nvoid LLVMVisitor::writeToObjectFile(const std::string &filename, bool pic,\n                                    bool assembly) {\n  if (GlobalCTOR == GlobalCTORMode::Yes)\n    setupGlobalCtor();\n\n  runLLVMPipeline();\n\n  std::error_code err;\n  auto out =\n      std::make_unique<llvm::ToolOutputFile>(filename, err, llvm::sys::fs::OF_None);\n  if (err)\n    compilationError(err.message());\n  auto *os = &out->os();\n\n  auto machine = getTargetMachine(M.get(), /*setFunctionAttributes=*/false, pic);\n  auto *mmiwp = new llvm::MachineModuleInfoWrapperPass(machine.get());\n  llvm::legacy::PassManager pm;\n\n  llvm::TargetLibraryInfoImpl tlii(llvm::Triple(M->getTargetTriple()));\n  pm.add(new llvm::TargetLibraryInfoWrapperPass(tlii));\n  if (machine->addPassesToEmitFile(pm, *os, nullptr,\n                                   assembly ? llvm::CodeGenFileType::AssemblyFile\n                                            : llvm::CodeGenFileType::ObjectFile,\n                                   /*DisableVerify=*/true, mmiwp))\n    seqassertn(false, \"could not add passes\");\n  const_cast<llvm::TargetLoweringObjectFile *>(machine->getObjFileLowering())\n      ->Initialize(mmiwp->getMMI().getContext(), *machine);\n  pm.run(*M);\n  out->keep();\n}\n\nvoid LLVMVisitor::writeToBitcodeFile(const std::string &filename) {\n  runLLVMPipeline();\n  std::error_code err;\n  llvm::raw_fd_ostream stream(filename, err, llvm::sys::fs::OF_None);\n  llvm::WriteBitcodeToFile(*M, stream);\n  if (err) {\n    compilationError(err.message());\n  }\n}\n\nvoid LLVMVisitor::writeToLLFile(const std::string &filename, bool optimize) {\n  if (GlobalCTOR == GlobalCTORMode::Yes)\n    setupGlobalCtor();\n  if (optimize)\n    runLLVMPipeline();\n  auto fo = fopen(filename.c_str(), \"w\");\n  llvm::raw_fd_ostream fout(fileno(fo), true);\n  fout << *M;\n  fout.close();\n}\n\nnamespace {\nvoid executeCommand(const std::vector<std::string> &args) {\n  std::vector<const char *> cArgs;\n  for (auto &arg : args) {\n    cArgs.push_back(arg.c_str());\n  }\n  LOG_USER(\"Executing '{}'\", fmt::join(cArgs, \" \"));\n  cArgs.push_back(nullptr);\n\n  if (fork() == 0) {\n    int status = execvp(cArgs[0], (char *const *)&cArgs[0]);\n    exit(status);\n  } else {\n    int status;\n    if (wait(&status) < 0) {\n      compilationError(\"process for '\" + args[0] + \"' encountered an error in wait\");\n    }\n\n    if (WEXITSTATUS(status) != 0) {\n      compilationError(\"process for '\" + args[0] + \"' exited with status \" +\n                       std::to_string(WEXITSTATUS(status)));\n    }\n  }\n}\n} // namespace\n\nvoid LLVMVisitor::setupGlobalCtor() {\n  const std::string llvmCtor = \"llvm.global_ctors\";\n  if (M->getNamedValue(llvmCtor))\n    return;\n\n  auto *main = M->getFunction(MAIN_UNCLASH);\n  if (!main) {\n    main = M->getFunction(\"main\");\n    if (!main)\n      return;\n    main->setName(MAIN_UNCLASH); // avoid clash with other main\n    main->setLinkage(llvm::GlobalValue::PrivateLinkage);\n  }\n\n  auto *ctorFuncTy = llvm::FunctionType::get(B->getVoidTy(), {}, /*isVarArg=*/false);\n  auto *ctorEntryTy =\n      llvm::StructType::get(B->getInt32Ty(), ctorFuncTy->getPointerTo(), B->getPtrTy());\n  auto *ctorArrayTy = llvm::ArrayType::get(ctorEntryTy, 1);\n\n  auto *ctor =\n      cast<llvm::Function>(M->getOrInsertFunction(MAIN_CTOR, ctorFuncTy).getCallee());\n  ctor->setLinkage(llvm::GlobalValue::PrivateLinkage);\n  auto *entry = llvm::BasicBlock::Create(*context, \"entry\", ctor);\n  B->SetInsertPoint(entry);\n  B->CreateCall(\n      {main->getFunctionType(), main},\n      {B->getInt32(0), llvm::ConstantPointerNull::get(B->getPtrTy()->getPointerTo())});\n  B->CreateRetVoid();\n\n  const int priority = 65535; // default\n  auto *ctorEntry = llvm::ConstantStruct::get(\n      ctorEntryTy,\n      {B->getInt32(priority), ctor, llvm::ConstantPointerNull::get(B->getPtrTy())});\n  new llvm::GlobalVariable(*M, ctorArrayTy,\n                           /*isConstant=*/true, llvm::GlobalValue::AppendingLinkage,\n                           llvm::ConstantArray::get(ctorArrayTy, {ctorEntry}),\n                           llvmCtor);\n}\n\nvoid LLVMVisitor::writeToExecutable(const std::string &filename,\n                                    const std::string &argv0, bool library,\n                                    const std::vector<std::string> &libs,\n                                    const std::string &lflags) {\n  if (library && GlobalCTOR != GlobalCTORMode::No)\n    setupGlobalCtor();\n\n  const std::string objFile = filename + \".o\";\n  writeToObjectFile(objFile, /*pic=*/library);\n\n  const std::string base = ast::Filesystem::executable_path(argv0.c_str());\n  auto path = llvm::SmallString<128>(llvm::sys::path::parent_path(base));\n\n  std::vector<std::string> relatives = {\"../lib\", \"../lib/codon\"};\n  std::vector<std::string> rpaths;\n  for (const auto &rel : relatives) {\n    auto newPath = path;\n    llvm::sys::path::append(newPath, rel);\n    llvm::sys::path::remove_dots(newPath, /*remove_dot_dot=*/true);\n    if (llvm::sys::fs::exists(newPath)) {\n      rpaths.push_back(std::string(newPath));\n    }\n  }\n\n  if (rpaths.empty()) {\n    rpaths.push_back(std::string(path));\n  }\n\n  std::vector<std::string> command = {\"g++\"};\n  // Avoid \"argument unused during compilation\" warning\n  command.push_back(\"-Wno-unused-command-line-argument\");\n  // MUST go before -llib to compile on Linux\n  command.push_back(objFile);\n\n  if (library)\n    command.push_back(\"-shared\");\n\n  for (const auto &rpath : rpaths) {\n    if (!rpath.empty()) {\n      command.push_back(\"-L\" + rpath);\n      command.push_back(\"-Wl,-rpath,\" + rpath);\n    }\n  }\n\n  if (plugins) {\n    for (auto *plugin : *plugins) {\n      auto dylibPath = plugin->info.dylibPath;\n      if (dylibPath.empty())\n        continue;\n\n      llvm::SmallString<128> rpath0 = llvm::sys::path::parent_path(dylibPath);\n      llvm::sys::fs::make_absolute(rpath0);\n      llvm::StringRef rpath = rpath0.str();\n      if (!rpath.empty()) {\n        command.push_back(\"-L\" + rpath.str());\n        command.push_back(\"-Wl,-rpath,\" + rpath.str());\n      }\n    }\n  }\n\n  for (const auto &lib : libs) {\n    command.push_back(\"-l\" + lib);\n  }\n\n  if (plugins) {\n    for (auto *plugin : *plugins) {\n      if (plugin->info.linkArgs.empty()) {\n        auto dylibPath = plugin->info.dylibPath;\n        if (dylibPath.empty())\n          continue;\n\n        auto stem = llvm::sys::path::stem(dylibPath);\n        if (stem.starts_with(\"lib\"))\n          stem = stem.substr(3);\n\n        command.push_back(\"-l\" + stem.str());\n      } else {\n        for (auto &l : plugin->info.linkArgs)\n          command.push_back(l);\n      }\n    }\n  }\n\n  std::vector<std::string> extraArgs = {\n      \"-lcodonrt\", \"-lomp\", \"-lpthread\", \"-ldl\", \"-lz\", \"-lm\", \"-lc\", \"-o\", filename};\n\n  for (const auto &arg : extraArgs) {\n    command.push_back(arg);\n  }\n\n  llvm::SmallVector<llvm::StringRef> userFlags(16);\n  llvm::StringRef(lflags).split(userFlags, \" \", /*MaxSplit=*/-1, /*KeepEmpty=*/false);\n\n  for (const auto &uflag : userFlags) {\n    if (!uflag.empty())\n      command.push_back(uflag.str());\n  }\n\n  // Avoid \"relocation R_X86_64_32 against `.bss' can not be used when making a PIE\n  // object\" complaints by gcc when it is built with --enable-default-pie\n  if (!library)\n    command.push_back(\"-no-pie\");\n\n  executeCommand(command);\n\n#if __APPLE__\n  if (db.debug)\n    executeCommand({\"dsymutil\", filename});\n#endif\n\n  llvm::sys::fs::remove(objFile);\n}\n\nnamespace {\n// https://github.com/python/cpython/blob/main/Include/methodobject.h\nconstexpr int PYEXT_METH_VARARGS = 0x0001;\nconstexpr int PYEXT_METH_KEYWORDS = 0x0002;\nconstexpr int PYEXT_METH_NOARGS = 0x0004;\nconstexpr int PYEXT_METH_O = 0x0008;\nconstexpr int PYEXT_METH_CLASS = 0x0010;\nconstexpr int PYEXT_METH_STATIC = 0x0020;\nconstexpr int PYEXT_METH_COEXIST = 0x0040;\nconstexpr int PYEXT_METH_FASTCALL = 0x0080;\nconstexpr int PYEXT_METH_METHOD = 0x0200;\n// https://github.com/python/cpython/blob/main/Include/modsupport.h\nconstexpr int PYEXT_PYTHON_ABI_VERSION = 1013;\n// https://github.com/python/cpython/blob/main/Include/descrobject.h\nconstexpr int PYEXT_READONLY = 1;\n} // namespace\n\nllvm::Function *LLVMVisitor::createPyTryCatchWrapper(llvm::Function *func) {\n  auto *wrap =\n      cast<llvm::Function>(M->getOrInsertFunction((func->getName() + \".tc_wrap\").str(),\n                                                  func->getFunctionType())\n                               .getCallee());\n  wrap->setPersonalityFn(llvm::cast<llvm::Constant>(makePersonalityFunc().getCallee()));\n  auto *entry = llvm::BasicBlock::Create(*context, \"entry\", wrap);\n  auto *normal = llvm::BasicBlock::Create(*context, \"normal\", wrap);\n  auto *unwind = llvm::BasicBlock::Create(*context, \"unwind\", wrap);\n\n  B->SetInsertPoint(entry);\n  std::vector<llvm::Value *> args;\n  for (auto &arg : wrap->args()) {\n    args.push_back(&arg);\n  }\n  auto *result = B->CreateInvoke(func, normal, unwind, args);\n\n  B->SetInsertPoint(normal);\n  B->CreateRet(result);\n\n  B->SetInsertPoint(unwind);\n  auto *caughtResult = B->CreateLandingPad(getPadType(), 1);\n  caughtResult->setCleanup(true);\n  caughtResult->addClause(getTypeIdxVar(nullptr));\n  auto *unwindType = llvm::StructType::get(B->getInt64Ty()); // header only\n  auto *unwindException = B->CreateExtractValue(caughtResult, 0);\n  auto *unwindExceptionClass = B->CreateLoad(\n      B->getInt64Ty(), B->CreateStructGEP(unwindType, unwindException, 0));\n  unwindException = B->CreateExtractValue(caughtResult, 0);\n  auto *excVal = B->CreateConstGEP1_64(B->getInt8Ty(), unwindException,\n                                       (uint64_t)seq_exc_offset());\n  auto *loadedExc = B->CreateLoad(B->getPtrTy(), excVal);\n\n  auto *strType = llvm::StructType::get(B->getInt64Ty(), B->getPtrTy());\n  auto *excHeader =\n      llvm::StructType::get(strType, strType, strType, B->getInt64Ty(), B->getInt64Ty(),\n                            B->getPtrTy(), B->getPtrTy());\n  auto *header = B->CreateLoad(excHeader, B->CreateLoad(B->getPtrTy(), loadedExc));\n  auto *msg = B->CreateExtractValue(header, 0);\n  auto *msgLen = B->CreateExtractValue(msg, 0);\n  auto *msgPtr = B->CreateExtractValue(msg, 1);\n  auto *pyType = B->CreateExtractValue(header, 5);\n\n  // copy msg into new null-terminated buffer\n  auto alloc = makeAllocFunc(/*atomic=*/true);\n  auto *buf = B->CreateCall(alloc, B->CreateAdd(msgLen, B->getInt64(1)));\n  B->CreateMemCpy(buf, {}, msgPtr, {}, msgLen);\n  auto *last = B->CreateInBoundsGEP(B->getInt8Ty(), buf, msgLen);\n  B->CreateStore(B->getInt8(0), last);\n\n  auto *pyErrSetString = llvm::cast<llvm::Function>(\n      M->getOrInsertFunction(\"PyErr_SetString\", B->getVoidTy(), B->getPtrTy(),\n                             B->getPtrTy())\n          .getCallee());\n\n  const std::string pyExcRuntimeErrorName = \"PyExc_RuntimeError\";\n  llvm::Value *pyExcRuntimeError = M->getNamedValue(pyExcRuntimeErrorName);\n  if (!pyExcRuntimeError) {\n    auto *pyExcRuntimeErrorVar = new llvm::GlobalVariable(\n        *M, B->getPtrTy(), /*isConstant=*/false, llvm::GlobalValue::ExternalLinkage,\n        /*Initializer=*/nullptr, pyExcRuntimeErrorName);\n    pyExcRuntimeErrorVar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);\n    pyExcRuntimeError = pyExcRuntimeErrorVar;\n  }\n  pyExcRuntimeError = B->CreateLoad(B->getPtrTy(), pyExcRuntimeError);\n\n  auto *havePyType =\n      B->CreateICmpNE(pyType, llvm::ConstantPointerNull::get(B->getPtrTy()));\n  B->CreateCall(pyErrSetString,\n                {B->CreateSelect(havePyType, pyType, pyExcRuntimeError), buf});\n\n  auto *retType = wrap->getReturnType();\n  if (retType == B->getInt32Ty()) {\n    B->CreateRet(B->getInt32(-1));\n  } else {\n    B->CreateRet(llvm::Constant::getNullValue(retType));\n  }\n\n  return wrap;\n}\n\nvoid LLVMVisitor::writeToPythonExtension(const PyModule &pymod,\n                                         const std::string &filename) {\n  // Setup LLVM types & constants\n  auto *i64 = B->getInt64Ty();\n  auto *i32 = B->getInt32Ty();\n  auto *i8 = B->getInt8Ty();\n  auto *ptr = B->getPtrTy();\n  auto *pyMethodDefType = llvm::StructType::create(\"PyMethodDef\", ptr, ptr, i32, ptr);\n  auto *pyObjectType = llvm::StructType::create(\"PyObject\", i64, ptr);\n  auto *pyVarObjectType = llvm::StructType::create(\"PyVarObject\", pyObjectType, i64);\n  auto *pyModuleDefBaseType =\n      llvm::StructType::create(\"PyMethodDefBase\", pyObjectType, ptr, i64, ptr);\n  auto *pyModuleDefType =\n      llvm::StructType::create(\"PyModuleDef\", pyModuleDefBaseType, ptr, ptr, i64,\n                               pyMethodDefType->getPointerTo(), ptr, ptr, ptr, ptr);\n  auto *pyMemberDefType =\n      llvm::StructType::create(\"PyMemberDef\", ptr, i32, i64, i32, ptr);\n  auto *pyGetSetDefType =\n      llvm::StructType::create(\"PyGetSetDef\", ptr, ptr, ptr, ptr, ptr);\n  std::vector<llvm::Type *> pyNumberMethodsFields(36, ptr);\n  auto *pyNumberMethodsType =\n      llvm::StructType::create(*context, pyNumberMethodsFields, \"PyNumberMethods\");\n  std::vector<llvm::Type *> pySequenceMethodsFields(10, ptr);\n  auto *pySequenceMethodsType =\n      llvm::StructType::create(*context, pySequenceMethodsFields, \"PySequenceMethods\");\n  std::vector<llvm::Type *> pyMappingMethodsFields(3, ptr);\n  auto *pyMappingMethodsType =\n      llvm::StructType::create(*context, pyMappingMethodsFields, \"PyMappingMethods\");\n  std::vector<llvm::Type *> pyAsyncMethodsFields(4, ptr);\n  auto *pyAsyncMethodsType =\n      llvm::StructType::create(*context, pyAsyncMethodsFields, \"PyAsyncMethods\");\n  auto *pyBufferProcsType = llvm::StructType::create(\"PyBufferProcs\", ptr, ptr);\n  auto *pyTypeObjectType = llvm::StructType::create(\n      \"PyTypeObject\", pyVarObjectType, ptr, i64, i64, ptr, i64, ptr, ptr, ptr, ptr, ptr,\n      ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, i64, ptr, ptr, ptr, ptr, i64, ptr, ptr,\n      ptr, ptr, ptr, ptr, ptr, ptr, ptr, i64, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr,\n      ptr, ptr, ptr, i32, ptr, ptr, i8);\n  auto *zero64 = B->getInt64(0);\n  auto *zero32 = B->getInt32(0);\n  auto *zero8 = B->getInt8(0);\n  auto *null = llvm::Constant::getNullValue(ptr);\n  auto *pyTypeType = new llvm::GlobalVariable(*M, ptr, /*isConstant=*/false,\n                                              llvm::GlobalValue::ExternalLinkage,\n                                              /*Initializer=*/nullptr, \"PyType_Type\");\n\n  auto allocUncollectable = llvm::cast<llvm::Function>(\n      makeAllocFunc(/*atomic=*/false, /*uncollectable=*/true).getCallee());\n  auto free = llvm::cast<llvm::Function>(makeFreeFunc().getCallee());\n\n  // Helpers\n  auto pyFuncWrap = [&](Func *func, bool wrap) -> llvm::Constant * {\n    if (!func)\n      return null;\n    auto llvmName = getNameForFunction(func);\n    auto *llvmFunc = M->getFunction(llvmName);\n    seqassertn(llvmFunc, \"function {} not found in LLVM module\", llvmName);\n    if (wrap)\n      llvmFunc = createPyTryCatchWrapper(llvmFunc);\n    return llvmFunc;\n  };\n\n  auto pyFunc = [&](Func *func) -> llvm::Constant * { return pyFuncWrap(func, true); };\n\n  auto pyString = [&](const std::string &str) -> llvm::Constant * {\n    if (str.empty())\n      return null;\n    auto *var = new llvm::GlobalVariable(\n        *M, llvm::ArrayType::get(i8, str.length() + 1),\n        /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage,\n        llvm::ConstantDataArray::getString(*context, str), \".pyext_str\");\n    var->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);\n    return var;\n  };\n\n  auto pyFunctions = [&](const std::vector<PyFunction> &functions) -> llvm::Constant * {\n    if (functions.empty())\n      return null;\n\n    std::vector<llvm::Constant *> pyMethods;\n    for (auto &pyfunc : functions) {\n      int flag = 0;\n      if (pyfunc.keywords) {\n        flag = PYEXT_METH_FASTCALL | PYEXT_METH_KEYWORDS;\n      } else {\n        switch (pyfunc.nargs) {\n        case 0:\n          flag = PYEXT_METH_NOARGS;\n          break;\n        case 1:\n          flag = PYEXT_METH_O;\n          break;\n        default:\n          flag = PYEXT_METH_FASTCALL;\n          break;\n        }\n      }\n\n      switch (pyfunc.type) {\n      case PyFunction::CLASS:\n        flag |= PYEXT_METH_CLASS;\n        break;\n      case PyFunction::STATIC:\n        flag |= PYEXT_METH_STATIC;\n        break;\n      default:\n        break;\n      }\n\n      if (pyfunc.coexist)\n        flag |= PYEXT_METH_COEXIST;\n\n      pyMethods.push_back(llvm::ConstantStruct::get(\n          pyMethodDefType, pyString(pyfunc.name), pyFunc(pyfunc.func),\n          B->getInt32(flag), pyString(pyfunc.doc)));\n    }\n    pyMethods.push_back(\n        llvm::ConstantStruct::get(pyMethodDefType, null, null, zero32, null));\n\n    auto *pyMethodDefArrayType =\n        llvm::ArrayType::get(pyMethodDefType, pyMethods.size());\n    auto *pyMethodDefArray = new llvm::GlobalVariable(\n        *M, pyMethodDefArrayType,\n        /*isConstant=*/false, llvm::GlobalValue::PrivateLinkage,\n        llvm::ConstantArray::get(pyMethodDefArrayType, pyMethods), \".pyext_methods\");\n    return pyMethodDefArray;\n  };\n\n  auto pyMembers = [&](const std::vector<PyMember> &members,\n                       llvm::StructType *type) -> llvm::Constant * {\n    if (members.empty())\n      return null;\n\n    std::vector<llvm::Constant *> pyMemb;\n    for (auto &memb : members) {\n      // Calculate offset by creating const GEP into null ptr\n      std::vector<llvm::Constant *> indexes = {zero64, B->getInt32(1)};\n      for (auto idx : memb.indexes) {\n        indexes.push_back(B->getInt32(idx));\n      }\n      auto offset = llvm::ConstantExpr::getPtrToInt(\n          llvm::ConstantExpr::getGetElementPtr(type, null, indexes), i64);\n\n      pyMemb.push_back(llvm::ConstantStruct::get(\n          pyMemberDefType, pyString(memb.name), B->getInt32(memb.type), offset,\n          B->getInt32(memb.readonly ? PYEXT_READONLY : 0), pyString(memb.doc)));\n    }\n    pyMemb.push_back(\n        llvm::ConstantStruct::get(pyMemberDefType, null, zero32, zero64, zero32, null));\n\n    auto *pyMemberDefArrayType = llvm::ArrayType::get(pyMemberDefType, pyMemb.size());\n    auto *pyMemberDefArray = new llvm::GlobalVariable(\n        *M, pyMemberDefArrayType,\n        /*isConstant=*/false, llvm::GlobalValue::PrivateLinkage,\n        llvm::ConstantArray::get(pyMemberDefArrayType, pyMemb), \".pyext_members\");\n    return pyMemberDefArray;\n  };\n\n  auto pyGetSet = [&](const std::vector<PyGetSet> &getset) -> llvm::Constant * {\n    if (getset.empty())\n      return null;\n\n    std::vector<llvm::Constant *> pyGS;\n    for (auto &gs : getset) {\n      pyGS.push_back(llvm::ConstantStruct::get(pyGetSetDefType, pyString(gs.name),\n                                               pyFunc(gs.get), pyFunc(gs.set),\n                                               pyString(gs.doc), null));\n    }\n    pyGS.push_back(\n        llvm::ConstantStruct::get(pyGetSetDefType, null, null, null, null, null));\n\n    auto *pyGetSetDefArrayType = llvm::ArrayType::get(pyGetSetDefType, pyGS.size());\n    auto *pyGetSetDefArray = new llvm::GlobalVariable(\n        *M, pyGetSetDefArrayType,\n        /*isConstant=*/false, llvm::GlobalValue::PrivateLinkage,\n        llvm::ConstantArray::get(pyGetSetDefArrayType, pyGS), \".pyext_getset\");\n    return pyGetSetDefArray;\n  };\n\n  // Construct PyModuleDef array\n  auto *pyObjectConst = llvm::ConstantStruct::get(pyObjectType, B->getInt64(1), null);\n  auto *pyModuleDefBaseConst =\n      llvm::ConstantStruct::get(pyModuleDefBaseType, pyObjectConst, null, zero64, null);\n\n  auto *pyModuleDef = llvm::ConstantStruct::get(\n      pyModuleDefType, pyModuleDefBaseConst, pyString(pymod.name), pyString(pymod.doc),\n      B->getInt64(-1), pyFunctions(pymod.functions), null, null, null, null);\n  auto *pyModuleVar =\n      new llvm::GlobalVariable(*M, pyModuleDef->getType(),\n                               /*isConstant=*/false, llvm::GlobalValue::PrivateLinkage,\n                               pyModuleDef, \".pyext_module\");\n\n  std::unordered_map<types::Type *, llvm::GlobalVariable *> typeVars;\n  for (auto &pytype : pymod.types) {\n    std::vector<llvm::Constant *> numberSlots = {\n        pyFunc(pytype.add),       // nb_add\n        pyFunc(pytype.sub),       // nb_subtract\n        pyFunc(pytype.mul),       // nb_multiply\n        pyFunc(pytype.mod),       // nb_remainder\n        pyFunc(pytype.divmod),    // nb_divmod\n        pyFunc(pytype.pow),       // nb_power\n        pyFunc(pytype.neg),       // nb_negative\n        pyFunc(pytype.pos),       // nb_positive\n        pyFunc(pytype.abs),       // nb_absolute\n        pyFunc(pytype.bool_),     // nb_bool\n        pyFunc(pytype.invert),    // nb_invert\n        pyFunc(pytype.lshift),    // nb_lshift\n        pyFunc(pytype.rshift),    // nb_rshift\n        pyFunc(pytype.and_),      // nb_and\n        pyFunc(pytype.xor_),      // nb_xor\n        pyFunc(pytype.or_),       // nb_or\n        pyFunc(pytype.int_),      // nb_int\n        null,                     // nb_reserved\n        pyFunc(pytype.float_),    // nb_float\n        pyFunc(pytype.iadd),      // nb_inplace_add\n        pyFunc(pytype.isub),      // nb_inplace_subtract\n        pyFunc(pytype.imul),      // nb_inplace_multiply\n        pyFunc(pytype.imod),      // nb_inplace_remainder\n        pyFunc(pytype.ipow),      // nb_inplace_power\n        pyFunc(pytype.ilshift),   // nb_inplace_lshift\n        pyFunc(pytype.irshift),   // nb_inplace_rshift\n        pyFunc(pytype.iand),      // nb_inplace_and\n        pyFunc(pytype.ixor),      // nb_inplace_xor\n        pyFunc(pytype.ior),       // nb_inplace_or\n        pyFunc(pytype.floordiv),  // nb_floor_divide\n        pyFunc(pytype.truediv),   // nb_true_divide\n        pyFunc(pytype.ifloordiv), // nb_inplace_floor_divide\n        pyFunc(pytype.itruediv),  // nb_inplace_true_divide\n        pyFunc(pytype.index),     // nb_index\n        pyFunc(pytype.matmul),    // nb_matrix_multiply\n        pyFunc(pytype.imatmul),   // nb_inplace_matrix_multiply\n    };\n\n    std::vector<llvm::Constant *> sequenceSlots = {\n        pyFunc(pytype.len),      // sq_length\n        null,                    // sq_concat\n        null,                    // sq_repeat\n        null,                    // sq_item\n        null,                    // was_sq_slice\n        null,                    // sq_ass_item\n        null,                    // was_sq_ass_slice\n        pyFunc(pytype.contains), // sq_contains\n        null,                    // sq_inplace_concat\n        null,                    // sq_inplace_repeat\n    };\n\n    std::vector<llvm::Constant *> mappingSlots = {\n        null,                   // mp_length\n        pyFunc(pytype.getitem), // mp_subscript\n        pyFunc(pytype.setitem), // mp_ass_subscript\n    };\n\n    bool needNumberSlots =\n        std::find_if(numberSlots.begin(), numberSlots.end(),\n                     [&](auto *v) { return v != null; }) != numberSlots.end();\n    bool needSequenceSlots =\n        std::find_if(sequenceSlots.begin(), sequenceSlots.end(),\n                     [&](auto *v) { return v != null; }) != sequenceSlots.end();\n    bool needMappingSlots =\n        std::find_if(mappingSlots.begin(), mappingSlots.end(),\n                     [&](auto *v) { return v != null; }) != mappingSlots.end();\n\n    auto *numberSlotsConst = null;\n    auto *sequenceSlotsConst = null;\n    auto *mappingSlotsConst = null;\n\n    if (needNumberSlots) {\n      auto *pyNumberSlotsVar = new llvm::GlobalVariable(\n          *M, pyNumberMethodsType,\n          /*isConstant=*/false, llvm::GlobalValue::PrivateLinkage,\n          llvm::ConstantStruct::get(pyNumberMethodsType, numberSlots),\n          \".pyext_number_slots.\" + pytype.name);\n      numberSlotsConst = pyNumberSlotsVar;\n    }\n\n    if (needSequenceSlots) {\n      auto *pySequenceSlotsVar = new llvm::GlobalVariable(\n          *M, pySequenceMethodsType,\n          /*isConstant=*/false, llvm::GlobalValue::PrivateLinkage,\n          llvm::ConstantStruct::get(pySequenceMethodsType, sequenceSlots),\n          \".pyext_sequence_slots.\" + pytype.name);\n      sequenceSlotsConst = pySequenceSlotsVar;\n    }\n\n    if (needMappingSlots) {\n      auto *pyMappingSlotsVar = new llvm::GlobalVariable(\n          *M, pyMappingMethodsType,\n          /*isConstant=*/false, llvm::GlobalValue::PrivateLinkage,\n          llvm::ConstantStruct::get(pyMappingMethodsType, mappingSlots),\n          \".pyext_mapping_slots.\" + pytype.name);\n      mappingSlotsConst = pyMappingSlotsVar;\n    }\n\n    auto *refType = cast<types::RefType>(pytype.type);\n    if (refType) {\n      seqassertn(!refType->isPolymorphic(),\n                 \"Python extension types cannot be polymorphic\");\n    }\n    auto *llvmType = getLLVMType(pytype.type);\n    auto *objectType = llvm::StructType::get(pyObjectType, llvmType);\n    auto codonSize =\n        refType\n            ? M->getDataLayout().getTypeAllocSize(getLLVMType(refType->getContents()))\n            : 0;\n    auto pySize = M->getDataLayout().getTypeAllocSize(objectType);\n\n    auto *alloc = llvm::cast<llvm::Function>(\n        M->getOrInsertFunction(pytype.name + \".py_alloc\", ptr, ptr, i64).getCallee());\n    {\n      auto *entry = llvm::BasicBlock::Create(*context, \"entry\", alloc);\n      B->SetInsertPoint(entry);\n      auto *pythonObject = B->CreateCall(allocUncollectable, B->getInt64(pySize));\n      auto *header = B->CreateInsertValue(\n          llvm::ConstantStruct::get(pyObjectType, B->getInt64(1), null),\n          alloc->arg_begin(), 1);\n      B->CreateStore(header, pythonObject);\n      if (refType) {\n        auto *codonObject = B->CreateCall(\n            makeAllocFunc(refType->getContents()->isAtomic()), B->getInt64(codonSize));\n        B->CreateStore(codonObject, B->CreateGEP(objectType, pythonObject,\n                                                 {zero64, B->getInt32(1)}));\n      }\n      B->CreateRet(pythonObject);\n    }\n\n    auto *delFn = pyFuncWrap(pytype.del, /*wrap=*/false);\n    auto *dealloc = llvm::cast<llvm::Function>(\n        M->getOrInsertFunction(pytype.name + \".py_dealloc\", B->getVoidTy(), ptr)\n            .getCallee());\n    {\n      auto *obj = dealloc->arg_begin();\n      auto *entry = llvm::BasicBlock::Create(*context, \"entry\", dealloc);\n      B->SetInsertPoint(entry);\n      if (delFn != null)\n        B->CreateCall(llvm::FunctionCallee(dealloc->getFunctionType(), delFn), obj);\n      B->CreateCall(free, obj);\n      B->CreateRetVoid();\n    }\n\n    auto *pyNew = llvm::cast<llvm::Function>(\n        M->getOrInsertFunction(\"PyType_GenericNew\", ptr, ptr, ptr, ptr).getCallee());\n\n    std::vector<llvm::Constant *> typeSlots = {\n        llvm::ConstantStruct::get(\n            pyVarObjectType,\n            llvm::ConstantStruct::get(pyObjectType, B->getInt64(1), pyTypeType),\n            zero64),                              // PyObject_VAR_HEAD\n        pyString(pymod.name + \".\" + pytype.name), // tp_name\n        B->getInt64(pySize),                      // tp_basicsize\n        zero64,                                   // tp_itemsize\n        dealloc,                                  // tp_dealloc\n        zero64,                                   // tp_vectorcall_offset\n        null,                                     // tp_getattr\n        null,                                     // tp_setattr\n        null,                                     // tp_as_async\n        pyFunc(pytype.repr),                      // tp_repr\n        numberSlotsConst,                         // tp_as_number\n        sequenceSlotsConst,                       // tp_as_sequence\n        mappingSlotsConst,                        // tp_as_mapping\n        pyFunc(pytype.hash),                      // tp_hash\n        pyFunc(pytype.call),                      // tp_call\n        pyFunc(pytype.str),                       // tp_str\n        null,                                     // tp_getattro\n        null,                                     // tp_setattro\n        null,                                     // tp_as_buffer\n        zero64,                                   // tp_flags\n        pyString(pytype.doc),                     // tp_doc\n        null,                                     // tp_traverse\n        null,                                     // tp_clear\n        pyFunc(pytype.cmp),                       // tp_richcompare\n        zero64,                                   // tp_weaklistoffset\n        pyFunc(pytype.iter),                      // tp_iter\n        pyFunc(pytype.iternext),                  // tp_iternext\n        pyFunctions(pytype.methods),              // tp_methods\n        pyMembers(pytype.members, objectType),    // tp_members\n        pyGetSet(pytype.getset),                  // tp_getset\n        null,                                     // tp_base\n        null,                                     // tp_dict\n        null,                                     // tp_descr_get\n        null,                                     // tp_descr_set\n        zero64,                                   // tp_dictoffset\n        pyFunc(pytype.init),                      // tp_init\n        alloc,                                    // tp_alloc\n        pyNew,                                    // tp_new\n        free,                                     // tp_free\n        null,                                     // tp_is_gc\n        null,                                     // tp_bases\n        null,                                     // tp_mro\n        null,                                     // tp_cache\n        null,                                     // tp_subclasses\n        null,                                     // tp_weaklist\n        null,                                     // tp_del\n        zero32,                                   // tp_version_tag\n        free,                                     // tp_finalize\n        null,                                     // tp_vectorcall\n        B->getInt8(0),                            // tp_watched\n    };\n\n    auto *pyTypeObjectVar = new llvm::GlobalVariable(\n        *M, pyTypeObjectType,\n        /*isConstant=*/false, llvm::GlobalValue::PrivateLinkage,\n        llvm::ConstantStruct::get(pyTypeObjectType, typeSlots),\n        \".pyext_type.\" + pytype.name);\n\n    if (pytype.typePtrHook) {\n      auto *hook = llvm::cast<llvm::Function>(pyFuncWrap(pytype.typePtrHook, false));\n      for (auto it = llvm::inst_begin(hook), end = llvm::inst_end(hook); it != end;\n           ++it) {\n        if (auto *ret = llvm::dyn_cast<llvm::ReturnInst>(&*it))\n          ret->setOperand(0, pyTypeObjectVar);\n      }\n    }\n\n    typeVars.emplace(pytype.type, pyTypeObjectVar);\n  }\n\n  // Construct initialization hook\n  auto pyIncRef = llvm::cast<llvm::Function>(\n      M->getOrInsertFunction(\"Py_IncRef\", B->getVoidTy(), ptr).getCallee());\n  pyIncRef->setDoesNotThrow();\n\n  auto pyDecRef = llvm::cast<llvm::Function>(\n      M->getOrInsertFunction(\"Py_DecRef\", B->getVoidTy(), ptr).getCallee());\n  pyDecRef->setDoesNotThrow();\n\n  auto *pyModuleCreate = llvm::cast<llvm::Function>(\n      M->getOrInsertFunction(\"PyModule_Create2\", ptr, ptr, i32).getCallee());\n  pyModuleCreate->setDoesNotThrow();\n\n  auto *pyTypeReady = llvm::cast<llvm::Function>(\n      M->getOrInsertFunction(\"PyType_Ready\", i32, ptr).getCallee());\n  pyTypeReady->setDoesNotThrow();\n\n  auto *pyModuleAddObject = llvm::cast<llvm::Function>(\n      M->getOrInsertFunction(\"PyModule_AddObject\", i32, ptr, ptr, ptr).getCallee());\n  pyModuleAddObject->setDoesNotThrow();\n\n  auto *pyModuleInit = llvm::cast<llvm::Function>(\n      M->getOrInsertFunction(\"PyInit_\" + pymod.name, ptr).getCallee());\n  auto *block = llvm::BasicBlock::Create(*context, \"entry\", pyModuleInit);\n  B->SetInsertPoint(block);\n\n  if (auto *main = M->getFunction(\"main\")) {\n    main->setName(MAIN_UNCLASH);\n    B->CreateCall({main->getFunctionType(), main}, {zero32, null});\n  }\n\n  // Set base types\n  for (auto &pytype : pymod.types) {\n    if (pytype.base) {\n      auto subcIt = typeVars.find(pytype.type);\n      auto baseIt = typeVars.find(pytype.base->type);\n      seqassertn(subcIt != typeVars.end() && baseIt != typeVars.end(),\n                 \"types not found\");\n      // 30 is the index of tp_base\n      B->CreateStore(baseIt->second, B->CreateConstInBoundsGEP2_64(\n                                         pyTypeObjectType, subcIt->second, 0, 30));\n    }\n  }\n\n  // Call PyType_Ready\n  for (auto &pytype : pymod.types) {\n    auto it = typeVars.find(pytype.type);\n    seqassertn(it != typeVars.end(), \"type not found\");\n    auto *typeVar = it->second;\n\n    auto *fail = llvm::BasicBlock::Create(*context, \"failure\", pyModuleInit);\n    block = llvm::BasicBlock::Create(*context, \"success\", pyModuleInit);\n    auto *status = B->CreateCall(pyTypeReady, typeVar);\n    B->CreateCondBr(B->CreateICmpSLT(status, zero32), fail, block);\n\n    B->SetInsertPoint(fail);\n    B->CreateRet(null);\n\n    B->SetInsertPoint(block);\n  }\n\n  // Create module\n  auto *mod = B->CreateCall(pyModuleCreate,\n                            {pyModuleVar, B->getInt32(PYEXT_PYTHON_ABI_VERSION)});\n  auto *fail = llvm::BasicBlock::Create(*context, \"failure\", pyModuleInit);\n  block = llvm::BasicBlock::Create(*context, \"success\", pyModuleInit);\n\n  B->CreateCondBr(B->CreateICmpEQ(mod, null), fail, block);\n  B->SetInsertPoint(fail);\n  B->CreateRet(null);\n\n  B->SetInsertPoint(block);\n\n  // Add types\n  for (auto &pytype : pymod.types) {\n    auto it = typeVars.find(pytype.type);\n    seqassertn(it != typeVars.end(), \"type not found\");\n    auto *typeVar = it->second;\n\n    B->CreateCall(pyIncRef, typeVar);\n    auto *status =\n        B->CreateCall(pyModuleAddObject, {mod, pyString(pytype.name), typeVar});\n    fail = llvm::BasicBlock::Create(*context, \"failure\", pyModuleInit);\n    block = llvm::BasicBlock::Create(*context, \"success\", pyModuleInit);\n    B->CreateCondBr(B->CreateICmpSLT(status, zero32), fail, block);\n\n    B->SetInsertPoint(fail);\n    B->CreateCall(pyDecRef, typeVar);\n    B->CreateCall(pyDecRef, mod);\n    B->CreateRet(null);\n\n    B->SetInsertPoint(block);\n  }\n  B->CreateRet(mod);\n\n  writeToObjectFile(filename);\n}\n\nvoid LLVMVisitor::compile(const std::string &filename, const std::string &argv0,\n                          const std::vector<std::string> &libs,\n                          const std::string &lflags) {\n  llvm::StringRef f(filename);\n  if (f.ends_with(\".ll\")) {\n    writeToLLFile(filename);\n  } else if (f.ends_with(\".bc\")) {\n    writeToBitcodeFile(filename);\n  } else if (f.ends_with(\".o\") || f.ends_with(\".obj\")) {\n    writeToObjectFile(filename);\n  } else if (f.ends_with(\".s\") || f.ends_with(\".S\") || f.ends_with(\".asm\")) {\n    writeToObjectFile(filename, /*pic=*/false, /*assembly=*/true);\n  } else if (f.ends_with(\".so\") || f.ends_with(\".dylib\")) {\n    writeToExecutable(filename, argv0, /*library=*/true, libs, lflags);\n  } else {\n    writeToExecutable(filename, argv0, /*library=*/false, libs, lflags);\n  }\n}\n\nvoid LLVMVisitor::run(const std::vector<std::string> &args,\n                      const std::vector<std::string> &libs, const char *const *envp) {\n  runLLVMPipeline();\n\n  Timer t1(\"llvm/jitlink\");\n  for (auto &lib : libs) {\n    std::string err;\n    if (llvm::sys::DynamicLibrary::LoadLibraryPermanently(lib.c_str(), &err)) {\n      compilationError(err);\n    }\n  }\n\n  DebugPlugin *dbp = nullptr;\n  llvm::Triple triple(M->getTargetTriple());\n  auto epc = llvm::cantFail(llvm::orc::SelfExecutorProcessControl::Create(\n      std::make_shared<llvm::orc::SymbolStringPool>()));\n\n  llvm::orc::LLJITBuilder builder;\n  builder.setDataLayout(M->getDataLayout());\n  builder.setObjectLinkingLayerCreator(\n      [&epc, &dbp](llvm::orc::ExecutionSession &es, const llvm::Triple &triple)\n          -> llvm::Expected<std::unique_ptr<llvm::orc::ObjectLayer>> {\n        auto L = std::make_unique<llvm::orc::ObjectLinkingLayer>(\n            es, llvm::cantFail(BoehmGCJITLinkMemoryManager::Create()));\n        L->addPlugin(std::make_unique<llvm::orc::DebugObjectManagerPlugin>(\n            es, llvm::cantFail(llvm::orc::createJITLoaderGDBRegistrar(es))));\n        auto dbPlugin = std::make_unique<DebugPlugin>();\n        dbp = dbPlugin.get();\n        L->addPlugin(std::move(dbPlugin));\n        return L;\n      });\n  builder.setJITTargetMachineBuilder(llvm::orc::JITTargetMachineBuilder(triple));\n\n  auto jit = llvm::cantFail(builder.create());\n  jit->getMainJITDylib().addGenerator(\n      llvm::cantFail(llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(\n          jit->getDataLayout().getGlobalPrefix())));\n\n  llvm::cantFail(jit->addIRModule({std::move(M), std::move(context)}));\n  clearLLVMData();\n  auto mainAddr = llvm::cantFail(jit->lookup(\"main\"));\n\n  if (db.debug) {\n    runtime::setJITErrorCallback([dbp](const runtime::JITError &e) {\n      fmt::print(stderr, \"{}\\n{}\", e.getOutput(),\n                 dbp->getPrettyBacktrace(e.getBacktrace()));\n      std::abort();\n    });\n  } else {\n    runtime::setJITErrorCallback([](const runtime::JITError &e) {\n      fmt::print(stderr, \"{}\", e.getOutput());\n      std::abort();\n    });\n  }\n  t1.log();\n\n  try {\n    llvm::cantFail(epc->runAsMain(mainAddr, args));\n  } catch (const runtime::JITError &e) {\n    fmt::print(stderr, \"{}\\n\", e.getOutput());\n    std::abort();\n  }\n}\n\n#define ALLOC_FAMILY \"seq_alloc\"\n\nllvm::FunctionCallee LLVMVisitor::makeAllocFunc(bool atomic, bool uncollectable) {\n  const std::string name =\n      atomic ? (uncollectable ? \"seq_alloc_atomic_uncollectable\" : \"seq_alloc_atomic\")\n             : (uncollectable ? \"seq_alloc_uncollectable\" : \"seq_alloc\");\n  auto f = M->getOrInsertFunction(name, B->getPtrTy(), B->getInt64Ty());\n  auto *g = cast<llvm::Function>(f.getCallee());\n  g->setDoesNotThrow();\n  g->setReturnDoesNotAlias();\n  g->setOnlyAccessesInaccessibleMemory();\n  g->addRetAttr(llvm::Attribute::AttrKind::NoUndef);\n  g->addRetAttr(llvm::Attribute::AttrKind::NonNull);\n  g->addFnAttrs(\n      llvm::AttrBuilder(*context)\n          .addAllocKindAttr(llvm::AllocFnKind::Alloc | llvm::AllocFnKind::Uninitialized)\n          .addAllocSizeAttr(0, {})\n          .addAttribute(\"alloc-family\", ALLOC_FAMILY));\n  return f;\n}\n\nllvm::FunctionCallee LLVMVisitor::makeReallocFunc() {\n  // note that seq_realloc takes arguments (ptr, new_size, old_size)\n  auto f = M->getOrInsertFunction(\"seq_realloc\", B->getPtrTy(), B->getPtrTy(),\n                                  B->getInt64Ty(), B->getInt64Ty());\n  auto *g = cast<llvm::Function>(f.getCallee());\n  g->setDoesNotThrow();\n  g->addRetAttr(llvm::Attribute::AttrKind::NoUndef);\n  g->addRetAttr(llvm::Attribute::AttrKind::NonNull);\n  g->addParamAttr(0, llvm::Attribute::AttrKind::AllocatedPointer);\n  g->addFnAttrs(llvm::AttrBuilder(*context)\n                    .addAllocKindAttr(llvm::AllocFnKind::Realloc |\n                                      llvm::AllocFnKind::Uninitialized)\n                    .addAllocSizeAttr(1, {})\n                    .addAttribute(\"alloc-family\", ALLOC_FAMILY));\n  return f;\n}\n\nllvm::FunctionCallee LLVMVisitor::makeFreeFunc() {\n  auto f = M->getOrInsertFunction(\"seq_free\", B->getVoidTy(), B->getPtrTy());\n  auto *g = cast<llvm::Function>(f.getCallee());\n  g->setDoesNotThrow();\n  g->addParamAttr(0, llvm::Attribute::AttrKind::AllocatedPointer);\n  g->addFnAttrs(llvm::AttrBuilder(*context)\n                    .addAllocKindAttr(llvm::AllocFnKind::Free)\n                    .addAttribute(\"alloc-family\", ALLOC_FAMILY));\n  return f;\n}\n\n#undef ALLOC_FAMILY\n\nllvm::FunctionCallee LLVMVisitor::makePersonalityFunc() {\n  return M->getOrInsertFunction(\"seq_personality\", B->getInt32Ty(), B->getInt32Ty(),\n                                B->getInt32Ty(), B->getInt64Ty(), B->getPtrTy(),\n                                B->getPtrTy());\n}\n\nllvm::FunctionCallee LLVMVisitor::makeExcAllocFunc() {\n  auto f = M->getOrInsertFunction(\"seq_alloc_exc\", B->getPtrTy(), B->getPtrTy());\n  auto *g = cast<llvm::Function>(f.getCallee());\n  g->setDoesNotThrow();\n  return f;\n}\n\nllvm::FunctionCallee LLVMVisitor::makeThrowFunc() {\n  auto f = M->getOrInsertFunction(\"seq_throw\", B->getVoidTy(), B->getPtrTy());\n  auto *g = cast<llvm::Function>(f.getCallee());\n  g->setDoesNotReturn();\n  return f;\n}\n\nllvm::FunctionCallee LLVMVisitor::makeTerminateFunc() {\n  auto f = M->getOrInsertFunction(\"seq_terminate\", B->getVoidTy(), B->getPtrTy());\n  auto *g = cast<llvm::Function>(f.getCallee());\n  g->setDoesNotReturn();\n  return f;\n}\n\nllvm::StructType *LLVMVisitor::getTypeInfoType() {\n  return llvm::StructType::get(B->getInt32Ty());\n}\n\nllvm::StructType *LLVMVisitor::getPadType() {\n  return llvm::StructType::get(B->getPtrTy(), B->getInt32Ty());\n}\n\nnamespace {\nint typeIdxLookup(types::Type *type) {\n  if (!type)\n    return 0;\n  auto *M = type->getModule();\n  return M->getCache()->getRealizationId(type->getAstType()->getClass());\n}\n} // namespace\n\nllvm::GlobalVariable *LLVMVisitor::getTypeIdxVar(types::Type *type) {\n  auto *typeInfoType = getTypeInfoType();\n  const std::string name = type ? type->getName() : \"\";\n  const std::string typeVarName = \"codon.typeidx.\" + (type ? name : \"<all>\");\n  auto *tidx = M->getGlobalVariable(typeVarName);\n  int idx = typeIdxLookup(type);\n  if (!tidx) {\n    tidx = new llvm::GlobalVariable(\n        *M, typeInfoType, /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage,\n        llvm::ConstantStruct::get(typeInfoType, B->getInt32(idx)), typeVarName);\n    tidx->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);\n  }\n  return tidx;\n}\n\nint LLVMVisitor::getTypeIdx(types::Type *catchType) { return typeIdxLookup(catchType); }\n\nllvm::Value *LLVMVisitor::call(llvm::FunctionCallee callee,\n                               llvm::ArrayRef<llvm::Value *> args) {\n  B->SetInsertPoint(block);\n  if ((trycatch.empty() && finally.empty()) || DisableExceptions) {\n    return B->CreateCall(callee, args);\n  } else {\n    auto *normalBlock = llvm::BasicBlock::Create(*context, \"invoke.normal\", func);\n    // use non-empty of finally-stack and try-stack, or whichever is most recent if both\n    // are non-empty\n    auto *unwindBlock =\n        finally.empty()\n            ? trycatch.back().exceptionBlock\n            : (trycatch.empty()\n                   ? finally.back().finallyExceptionBlock\n                   : (trycatch.back().sequenceNumber > finally.back().sequenceNumber\n                          ? trycatch.back().exceptionBlock\n                          : finally.back().finallyExceptionBlock));\n    auto *result = B->CreateInvoke(callee, normalBlock, unwindBlock, args);\n    block = normalBlock;\n    return result;\n  }\n}\n\nstatic int nextSequenceNumber = 0;\n\nvoid LLVMVisitor::enterLoop(LoopData data) {\n  loops.push_back(std::move(data));\n  loops.back().sequenceNumber = nextSequenceNumber++;\n}\n\nvoid LLVMVisitor::exitLoop() {\n  seqassertn(!loops.empty(), \"no loops present\");\n  loops.pop_back();\n}\n\nvoid LLVMVisitor::enterTry(TryCatchData data) {\n  trycatch.push_back(std::move(data));\n  trycatch.back().sequenceNumber = nextSequenceNumber++;\n}\n\nvoid LLVMVisitor::exitTry() {\n  seqassertn(!trycatch.empty(), \"no try catches present\");\n  trycatch.pop_back();\n}\n\nvoid LLVMVisitor::enterFinally(TryCatchData data) {\n  finally.push_back(std::move(data));\n  finally.back().sequenceNumber = nextSequenceNumber++;\n}\n\nvoid LLVMVisitor::exitFinally() {\n  seqassertn(!finally.empty(), \"no finally present\");\n  finally.pop_back();\n}\n\nvoid LLVMVisitor::enterCatch(CatchData data) {\n  catches.push_back(std::move(data));\n  catches.back().sequenceNumber = nextSequenceNumber++;\n}\n\nvoid LLVMVisitor::exitCatch() {\n  seqassertn(!catches.empty(), \"no catches present\");\n  catches.pop_back();\n}\n\nLLVMVisitor::TryCatchData *LLVMVisitor::getInnermostTryCatch() {\n  return trycatch.empty() ? nullptr : &trycatch.back();\n}\n\nLLVMVisitor::TryCatchData *LLVMVisitor::getInnermostTryCatchBeforeLoop() {\n  if (!trycatch.empty() &&\n      (loops.empty() || trycatch.back().sequenceNumber > loops.back().sequenceNumber))\n    return &trycatch.back();\n  return nullptr;\n}\n\n/*\n * General values, module, functions, vars\n */\n\nvoid LLVMVisitor::visit(const Module *x) {\n  // initialize module\n  M = makeModule(*context, getSrcInfo(x));\n\n  // args variable\n  seqassertn(x->getArgVar()->isGlobal(), \"arg var is not global\");\n  registerGlobal(x->getArgVar());\n\n  // set up global variables and initialize functions\n  for (auto *var : *x) {\n    registerGlobal(var);\n  }\n\n  // process functions\n  for (auto *var : *x) {\n    if (auto *f = cast<Func>(var)) {\n      process(f);\n    }\n  }\n\n  const Func *main = x->getMainFunc();\n  llvm::FunctionCallee realMain = makeLLVMFunction(main);\n  process(main);\n  setDebugInfoForNode(nullptr);\n\n  // build canonical main function\n  auto *strType = llvm::StructType::get(*context, {B->getInt64Ty(), B->getPtrTy()});\n  auto *arrType =\n      llvm::StructType::get(*context, {B->getInt64Ty(), strType->getPointerTo()});\n\n  auto *initFunc = llvm::cast<llvm::Function>(\n      M->getOrInsertFunction(\"seq_init\", B->getVoidTy(), B->getInt32Ty()).getCallee());\n  auto *strlenFunc = llvm::cast<llvm::Function>(\n      M->getOrInsertFunction(\"strlen\", B->getInt64Ty(), B->getPtrTy()).getCallee());\n\n  // check if main exists already as an exported function\n  const std::string mainName = M->getFunction(\"main\") ? MAIN_UNCLASH : \"main\";\n  auto *canonicalMainFunc = llvm::cast<llvm::Function>(\n      M->getOrInsertFunction(mainName, B->getInt32Ty(), B->getInt32Ty(),\n                             B->getPtrTy()->getPointerTo())\n          .getCallee());\n\n  canonicalMainFunc->setPersonalityFn(\n      llvm::cast<llvm::Constant>(makePersonalityFunc().getCallee()));\n  auto argiter = canonicalMainFunc->arg_begin();\n  auto *argc = argiter++;\n  auto *argv = argiter;\n  argc->setName(\"argc\");\n  argv->setName(\"argv\");\n\n  // The following generates code to put program arguments in an array, i.e.:\n  //    for (int i = 0; i < argc; i++)\n  //      array[i] = {strlen(argv[i]), argv[i]}\n  auto *entryBlock = llvm::BasicBlock::Create(*context, \"entry\", canonicalMainFunc);\n  auto *loopBlock = llvm::BasicBlock::Create(*context, \"loop\", canonicalMainFunc);\n  auto *bodyBlock = llvm::BasicBlock::Create(*context, \"body\", canonicalMainFunc);\n  auto *exitBlock = llvm::BasicBlock::Create(*context, \"exit\", canonicalMainFunc);\n\n  B->SetInsertPoint(entryBlock);\n  auto allocFunc = makeAllocFunc(/*atomic=*/false);\n  auto *len = B->CreateZExt(argc, B->getInt64Ty());\n  auto *elemSize = B->getInt64(M->getDataLayout().getTypeAllocSize(strType));\n  auto *allocSize = B->CreateMul(len, elemSize);\n  auto *ptr = B->CreateCall(allocFunc, allocSize);\n  llvm::Value *arr = llvm::UndefValue::get(arrType);\n  arr = B->CreateInsertValue(arr, len, 0);\n  arr = B->CreateInsertValue(arr, ptr, 1);\n  B->CreateBr(loopBlock);\n\n  B->SetInsertPoint(loopBlock);\n  auto *control = B->CreatePHI(B->getInt32Ty(), 2, \"i\");\n  auto *next = B->CreateAdd(control, B->getInt32(1), \"next\");\n  auto *cond = B->CreateICmpSLT(control, argc);\n  control->addIncoming(B->getInt32(0), entryBlock);\n  control->addIncoming(next, bodyBlock);\n  B->CreateCondBr(cond, bodyBlock, exitBlock);\n\n  B->SetInsertPoint(bodyBlock);\n  auto *arg = B->CreateLoad(B->getPtrTy(), B->CreateGEP(B->getPtrTy(), argv, control));\n  auto *argLen = B->CreateZExtOrTrunc(B->CreateCall(strlenFunc, arg), B->getInt64Ty());\n  llvm::Value *str = llvm::UndefValue::get(strType);\n  str = B->CreateInsertValue(str, argLen, 0);\n  str = B->CreateInsertValue(str, arg, 1);\n  B->CreateStore(str, B->CreateGEP(strType, ptr, control));\n  B->CreateBr(loopBlock);\n\n  B->SetInsertPoint(exitBlock);\n  auto *argStorage = getVar(x->getArgVar());\n  seqassertn(argStorage, \"argument storage missing\");\n  B->CreateStore(arr, argStorage);\n  const int flags = (db.debug ? SEQ_FLAG_DEBUG : 0) |\n                    (db.capture ? SEQ_FLAG_CAPTURE_OUTPUT : 0) |\n                    (db.standalone ? SEQ_FLAG_STANDALONE : 0);\n  B->CreateCall(initFunc, B->getInt32(flags));\n\n  // Put the entire program in a new function\n  {\n    auto *proxyMainTy = llvm::FunctionType::get(B->getVoidTy(), {}, false);\n    auto *proxyMain = llvm::cast<llvm::Function>(\n        M->getOrInsertFunction(\"codon.proxy_main\", proxyMainTy).getCallee());\n    proxyMain->setLinkage(llvm::GlobalValue::PrivateLinkage);\n    proxyMain->setPersonalityFn(\n        llvm::cast<llvm::Constant>(makePersonalityFunc().getCallee()));\n    auto *proxyBlockEntry = llvm::BasicBlock::Create(*context, \"entry\", proxyMain);\n    auto *proxyBlockMain = llvm::BasicBlock::Create(*context, \"main\", proxyMain);\n    auto *proxyBlockExit = llvm::BasicBlock::Create(*context, \"exit\", proxyMain);\n    B->SetInsertPoint(proxyBlockEntry);\n\n    auto *shouldExit = B->getFalse();\n    B->CreateCondBr(shouldExit, proxyBlockExit, proxyBlockMain);\n\n    B->SetInsertPoint(proxyBlockExit);\n    B->CreateRetVoid();\n\n    // invoke real main\n    auto *normal = llvm::BasicBlock::Create(*context, \"normal\", proxyMain);\n    auto *unwind = llvm::BasicBlock::Create(*context, \"unwind\", proxyMain);\n    B->SetInsertPoint(proxyBlockMain);\n    B->CreateInvoke(realMain, normal, unwind);\n\n    B->SetInsertPoint(unwind);\n    auto *caughtResult = B->CreateLandingPad(getPadType(), 1);\n    caughtResult->setCleanup(true);\n    caughtResult->addClause(getTypeIdxVar(nullptr));\n    auto *unwindException = B->CreateExtractValue(caughtResult, 0);\n    B->CreateCall(makeTerminateFunc(), unwindException);\n    B->CreateUnreachable();\n\n    B->SetInsertPoint(normal);\n    B->CreateRetVoid();\n\n    // actually make the call\n    B->SetInsertPoint(exitBlock);\n    B->CreateCall(proxyMain);\n  }\n\n  B->SetInsertPoint(exitBlock);\n  B->CreateRet(B->getInt32(0));\n\n  // make sure allocation functions have the correct attributes\n  if (M->getFunction(\"seq_alloc\"))\n    makeAllocFunc(/*atomic=*/false, /*uncollectable=*/false);\n  if (M->getFunction(\"seq_alloc_atomic\"))\n    makeAllocFunc(/*atomic=*/true, /*uncollectable=*/false);\n  if (M->getFunction(\"seq_alloc_uncollectable\"))\n    makeAllocFunc(/*atomic=*/false, /*uncollectable=*/true);\n  if (M->getFunction(\"seq_alloc_atomic_uncollectable\"))\n    makeAllocFunc(/*atomic=*/true, /*uncollectable=*/true);\n  if (M->getFunction(\"seq_realloc\"))\n    makeReallocFunc();\n  if (M->getFunction(\"seq_free\"))\n    makeFreeFunc();\n}\n\nllvm::DISubprogram *LLVMVisitor::getDISubprogramForFunc(const Func *x) {\n  auto *srcInfo = getSrcInfo(x);\n  auto *file = db.getFile(srcInfo->file);\n  auto *derivedType = llvm::cast<llvm::DIDerivedType>(getDIType(x->getType()));\n  auto *subroutineType =\n      llvm::cast<llvm::DISubroutineType>(derivedType->getRawBaseType());\n\n  std::string baseName = x->getUnmangledName();\n  if (auto *parent = x->getParentType())\n    baseName = parent->getName() + \".\" + baseName;\n  auto *subprogram = db.builder->createFunction(\n      file, baseName, getNameForFunction(x), file, srcInfo->line, subroutineType,\n      /*ScopeLine=*/0, llvm::DINode::FlagZero,\n      llvm::DISubprogram::toSPFlags(/*IsLocalToUnit=*/true,\n                                    /*IsDefinition=*/true, /*IsOptimized=*/!db.debug));\n  return subprogram;\n}\n\nllvm::Function *LLVMVisitor::makeLLVMFunction(const Func *x) {\n  // process LLVM functions in full immediately\n  if (auto *llvmFunc = cast<LLVMFunc>(x)) {\n    auto *oldFunc = func;\n    process(llvmFunc);\n    setDebugInfoForNode(nullptr);\n    auto *newFunc = func;\n    func = oldFunc;\n    return newFunc;\n  }\n\n  auto *funcType = cast<types::FuncType>(x->getType());\n  auto *returnType = getLLVMType(funcType->getReturnType());\n  std::vector<llvm::Type *> argTypes;\n  for (const auto &argType : *funcType) {\n    argTypes.push_back(getLLVMType(argType));\n  }\n\n  auto *llvmFuncType =\n      llvm::FunctionType::get(returnType, argTypes, funcType->isVariadic());\n  const std::string functionName = getNameForFunction(x);\n  auto *f = llvm::cast<llvm::Function>(\n      M->getOrInsertFunction(functionName, llvmFuncType).getCallee());\n  if (!cast<ExternalFunc>(x)) {\n    f->setSubprogram(getDISubprogramForFunc(x));\n  }\n  return f;\n}\n\nvoid LLVMVisitor::makeYield(llvm::Value *value, bool finalYield) {\n  B->SetInsertPoint(block);\n  if (value) {\n    seqassertn(coro.promise, \"promise is null\");\n    B->CreateStore(value, coro.promise);\n  }\n  llvm::FunctionCallee coroSuspend =\n      llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_suspend);\n  auto *suspendResult = B->CreateCall(\n      coroSuspend, {llvm::ConstantTokenNone::get(*context), B->getInt1(finalYield)});\n\n  block = llvm::BasicBlock::Create(*context, \"yield.new\", func);\n\n  seqassertn(coro.suspend && coro.cleanup, \"suspend and/or cleanup is null\");\n  auto *inst = B->CreateSwitch(suspendResult, coro.suspend, 2);\n  inst->addCase(B->getInt8(0), block);\n  inst->addCase(B->getInt8(1), coro.cleanup);\n}\n\nvoid LLVMVisitor::visit(const ExternalFunc *x) {\n  func = M->getFunction(getNameForFunction(x));\n  if (!func) {\n    func = makeLLVMFunction(x);\n    insertFunc(x, func);\n  }\n  coro = {};\n  func->setDoesNotThrow();\n  func->setWillReturn();\n}\n\nnamespace {\n// internal function type checking\ntemplate <typename ParentType>\nbool internalFuncMatchesIgnoreArgs(const std::string &name, const InternalFunc *x) {\n  return name == x->getUnmangledName() && cast<ParentType>(x->getParentType());\n}\n\ntemplate <typename ParentType, typename... ArgTypes, std::size_t... Index>\nbool internalFuncMatches(const std::string &name, const InternalFunc *x,\n                         std::index_sequence<Index...>) {\n  auto *funcType = cast<types::FuncType>(x->getType());\n  if (name != x->getUnmangledName() ||\n      std::distance(funcType->begin(), funcType->end()) != sizeof...(ArgTypes))\n    return false;\n  std::vector<types::Type *> argTypes(funcType->begin(), funcType->end());\n  std::vector<bool> m = {bool(cast<ParentType>(x->getParentType())),\n                         bool(cast<ArgTypes>(argTypes[Index]))...};\n  const bool match = std::all_of(m.begin(), m.end(), [](bool b) { return b; });\n  return match;\n}\n\ntemplate <typename ParentType, typename... ArgTypes>\nbool internalFuncMatches(const std::string &name, const InternalFunc *x) {\n  return internalFuncMatches<ParentType, ArgTypes...>(\n      name, x, std::make_index_sequence<sizeof...(ArgTypes)>());\n}\n} // namespace\n\nvoid LLVMVisitor::visit(const InternalFunc *x) {\n  using namespace types;\n  func = M->getFunction(getNameForFunction(x));\n  coro = {};\n  seqassertn(func, \"{} not inserted\", *x);\n  setDebugInfoForNode(x);\n\n  Type *parentType = x->getParentType();\n  auto *funcType = cast<FuncType>(x->getType());\n  std::vector<Type *> argTypes(funcType->begin(), funcType->end());\n\n  func->setLinkage(llvm::GlobalValue::PrivateLinkage);\n  func->addFnAttr(llvm::Attribute::AttrKind::AlwaysInline);\n  std::vector<llvm::Value *> args;\n  for (auto it = func->arg_begin(); it != func->arg_end(); ++it) {\n    args.push_back(it);\n  }\n  block = llvm::BasicBlock::Create(*context, \"entry\", func);\n  B->SetInsertPoint(block);\n  llvm::Value *result = nullptr;\n\n  if (internalFuncMatches<PointerType, IntType>(\"__new__\", x)) {\n    auto *pointerType = cast<PointerType>(parentType);\n    Type *baseType = pointerType->getBase();\n    auto *llvmBaseType = getLLVMType(baseType);\n    auto allocFunc = makeAllocFunc(baseType->isAtomic());\n    auto *elemSize = B->getInt64(M->getDataLayout().getTypeAllocSize(llvmBaseType));\n    auto *allocSize = B->CreateMul(elemSize, args[0]);\n    result = B->CreateCall(allocFunc, allocSize);\n  }\n\n  else if (internalFuncMatches<IntType, IntNType>(\"__new__\", x)) {\n    auto *intNType = cast<IntNType>(argTypes[0]);\n    if (intNType->isSigned()) {\n      result = B->CreateSExtOrTrunc(args[0], B->getInt64Ty());\n    } else {\n      result = B->CreateZExtOrTrunc(args[0], B->getInt64Ty());\n    }\n  }\n\n  else if (internalFuncMatches<IntNType, IntType>(\"__new__\", x)) {\n    auto *intNType = cast<IntNType>(parentType);\n    if (intNType->isSigned()) {\n      result = B->CreateSExtOrTrunc(args[0], getLLVMType(intNType));\n    } else {\n      result = B->CreateZExtOrTrunc(args[0], getLLVMType(intNType));\n    }\n  }\n\n  else if (internalFuncMatches<GeneratorType, GeneratorType>(\"__promise__\", x)) {\n    auto *generatorType = cast<GeneratorType>(parentType);\n    auto *baseType = getLLVMType(generatorType->getBase());\n    if (baseType->isVoidTy()) {\n      result = llvm::ConstantPointerNull::get(B->getVoidTy()->getPointerTo());\n    } else {\n      llvm::FunctionCallee coroPromise =\n          llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_promise);\n      auto *aln = B->getInt32(M->getDataLayout().getPrefTypeAlign(baseType).value());\n      auto *from = B->getFalse();\n      auto *ptr = B->CreateCall(coroPromise, {args[0], aln, from});\n      result = ptr;\n    }\n  }\n\n  else if (internalFuncMatchesIgnoreArgs<RecordType>(\"__new__\", x)) {\n    auto *recordType = cast<RecordType>(cast<FuncType>(x->getType())->getReturnType());\n    seqassertn(args.size() == std::distance(recordType->begin(), recordType->end()),\n               \"args size does not match: {} vs {}\", args.size(),\n               std::distance(recordType->begin(), recordType->end()));\n    result = llvm::UndefValue::get(getLLVMType(recordType));\n    for (auto i = 0; i < args.size(); i++) {\n      result = B->CreateInsertValue(result, args[i], i);\n    }\n  }\n\n  seqassertn(result, \"internal function {} not found\", *x);\n  B->CreateRet(result);\n}\n\nstd::string LLVMVisitor::buildLLVMCodeString(const LLVMFunc *x) {\n  auto *funcType = cast<types::FuncType>(x->getType());\n  seqassertn(funcType, \"{} is not a function type\", *x->getType());\n  std::string bufStr;\n  llvm::raw_string_ostream buf(bufStr);\n\n  // build function signature\n  buf << \"define \";\n  getLLVMType(funcType->getReturnType())->print(buf);\n  buf << \" @\\\"\" << getNameForFunction(x) << \"\\\"(\";\n  const int numArgs = std::distance(x->arg_begin(), x->arg_end());\n  int argIndex = 0;\n  for (auto it = x->arg_begin(); it != x->arg_end(); ++it) {\n    getLLVMType((*it)->getType())->print(buf);\n    buf << \" %\" << (*it)->getName();\n    if (argIndex < numArgs - 1)\n      buf << \", \";\n    ++argIndex;\n  }\n  buf << \")\";\n  std::string signature = buf.str();\n  bufStr.clear();\n\n  // replace literal '{' and '}'\n  std::string::size_type n = 0;\n  while ((n = signature.find(\"{\", n)) != std::string::npos) {\n    signature.replace(n, 1, \"{{\");\n    n += 2;\n  }\n  n = 0;\n  while ((n = signature.find(\"}\", n)) != std::string::npos) {\n    signature.replace(n, 1, \"}}\");\n    n += 2;\n  }\n\n  // build remaining code\n  auto body = x->getLLVMBody();\n  buf << x->getLLVMDeclarations() << \"\\n\" << signature << \" {{\\n\" << body << \"\\n}}\";\n  return buf.str();\n}\n\nvoid LLVMVisitor::visit(const LLVMFunc *x) {\n  func = M->getFunction(getNameForFunction(x));\n  coro = {};\n  if (func)\n    return;\n\n  // build code\n  std::string code = buildLLVMCodeString(x);\n\n  // format code\n  fmt::dynamic_format_arg_store<fmt::format_context> store;\n  for (auto it = x->literal_begin(); it != x->literal_end(); ++it) {\n    if (it->isStatic()) {\n      store.push_back(it->getStaticValue());\n    } else if (it->isStaticStr()) {\n      store.push_back(it->getStaticStringValue());\n    } else if (it->isType()) {\n      auto *llvmType = getLLVMType(it->getTypeValue());\n      std::string bufStr;\n      llvm::raw_string_ostream buf(bufStr);\n      llvmType->print(buf);\n      store.push_back(buf.str());\n    } else {\n      seqassertn(0, \"formatting failed\");\n    }\n  }\n  code = fmt::vformat(code, store);\n\n  llvm::SMDiagnostic err;\n  std::unique_ptr<llvm::MemoryBuffer> buf = llvm::MemoryBuffer::getMemBuffer(code);\n  seqassertn(buf, \"could not create buffer\");\n  std::unique_ptr<llvm::Module> sub =\n      llvm::parseIR(buf->getMemBufferRef(), err, *context);\n  if (!sub) {\n    std::string bufStr;\n    llvm::raw_string_ostream buf(bufStr);\n    err.print(\"LLVM\", buf);\n    compilationError(fmt::format(\"{} ({})\", buf.str(), x->getName()));\n  }\n  sub->setDataLayout(M->getDataLayout());\n\n  llvm::Linker L(*M);\n  const bool fail = L.linkInModule(std::move(sub));\n  seqassertn(!fail, \"linking failed\");\n  func = M->getFunction(getNameForFunction(x));\n  seqassertn(func, \"function not linked in\");\n  func->setLinkage(llvm::GlobalValue::PrivateLinkage);\n  func->addFnAttr(llvm::Attribute::AttrKind::AlwaysInline);\n  func->setSubprogram(getDISubprogramForFunc(x));\n\n  // set up debug info\n  // for now we just set all to func's source location\n  auto *srcInfo = getSrcInfo(x);\n  for (auto &block : *func) {\n    for (auto &inst : block) {\n      if (!inst.getDebugLoc()) {\n        inst.setDebugLoc(llvm::DebugLoc(llvm::DILocation::get(\n            *context, srcInfo->line, srcInfo->col, func->getSubprogram())));\n      }\n    }\n  }\n}\n\nvoid LLVMVisitor::visit(const BodiedFunc *x) {\n  func = M->getFunction(getNameForFunction(x));\n  coro = {};\n  seqassertn(func, \"{} not inserted\", *x);\n  setDebugInfoForNode(x);\n\n  auto *fnAttributes = x->getAttribute<KeyValueAttribute>();\n  if (x->isJIT()) {\n    func->addFnAttr(llvm::Attribute::get(*context, \"jit\"));\n  }\n  if (x->isJIT() || (fnAttributes && fnAttributes->has(EXPORT_ATTR))) {\n    func->setLinkage(llvm::GlobalValue::ExternalLinkage);\n  } else {\n    func->setLinkage(llvm::GlobalValue::PrivateLinkage);\n  }\n  if (fnAttributes && fnAttributes->has(INLINE_ATTR)) {\n    func->addFnAttr(llvm::Attribute::AttrKind::AlwaysInline);\n  }\n  if (fnAttributes && fnAttributes->has(NOINLINE_ATTR)) {\n    func->addFnAttr(llvm::Attribute::AttrKind::NoInline);\n  }\n  if (fnAttributes && fnAttributes->has(GPU_KERNEL_ATTR)) {\n    func->addFnAttr(llvm::Attribute::AttrKind::NoInline);\n    func->addFnAttr(llvm::Attribute::get(*context, \"kernel\"));\n    func->setLinkage(llvm::GlobalValue::ExternalLinkage);\n  }\n  if (!DisableExceptions)\n    func->setPersonalityFn(\n        llvm::cast<llvm::Constant>(makePersonalityFunc().getCallee()));\n\n  auto *funcType = cast<types::FuncType>(x->getType());\n  seqassertn(funcType, \"{} is not a function type\", *x->getType());\n  auto *returnType = funcType->getReturnType();\n  auto *entryBlock = llvm::BasicBlock::Create(*context, \"entry\", func);\n  B->SetInsertPoint(entryBlock);\n\n  // set up arguments and other symbols\n  seqassertn(std::distance(func->arg_begin(), func->arg_end()) ==\n                 std::distance(x->arg_begin(), x->arg_end()),\n             \"argument length does not match\");\n  unsigned argIdx = 1;\n  auto argIter = func->arg_begin();\n  for (auto varIter = x->arg_begin(); varIter != x->arg_end(); ++varIter) {\n    const Var *var = *varIter;\n    auto *storage = B->CreateAlloca(getLLVMType(var->getType()));\n    B->CreateStore(argIter, storage);\n    insertVar(var, storage);\n\n    // debug info\n    auto *srcInfo = getSrcInfo(var);\n    auto *file = db.getFile(srcInfo->file);\n    auto *scope = func->getSubprogram();\n    auto *debugVar = db.builder->createParameterVariable(\n        scope, getDebugNameForVariable(var), argIdx, file, srcInfo->line,\n        getDIType(var->getType()), db.debug);\n    db.builder->insertDeclare(\n        storage, debugVar, db.builder->createExpression(),\n        llvm::DILocation::get(*context, srcInfo->line, srcInfo->col, scope),\n        entryBlock);\n\n    ++argIter;\n    ++argIdx;\n  }\n\n  for (auto *var : *x) {\n    auto *llvmType = getLLVMType(var->getType());\n    if (llvmType->isVoidTy()) {\n      insertVar(var, getDummyVoidValue());\n    } else {\n      auto *storage = B->CreateAlloca(llvmType);\n      insertVar(var, storage);\n\n      // debug info\n      auto *srcInfo = getSrcInfo(var);\n      auto *file = db.getFile(srcInfo->file);\n      auto *scope = func->getSubprogram();\n      auto *debugVar = db.builder->createAutoVariable(\n          scope, getDebugNameForVariable(var), file, srcInfo->line,\n          getDIType(var->getType()), db.debug);\n      db.builder->insertDeclare(\n          storage, debugVar, db.builder->createExpression(),\n          llvm::DILocation::get(*context, srcInfo->line, srcInfo->col, scope),\n          entryBlock);\n    }\n  }\n\n  auto *startBlock = llvm::BasicBlock::Create(*context, \"start\", func);\n  const bool generator = x->isGenerator() || x->isAsync();\n\n  if (generator) {\n    func->setPresplitCoroutine();\n    auto *generatorType = cast<types::GeneratorType>(returnType);\n    seqassertn(generatorType, \"{} is not a generator type\", *returnType);\n\n    llvm::FunctionCallee coroId =\n        llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_id);\n    llvm::FunctionCallee coroBegin =\n        llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_begin);\n    llvm::FunctionCallee coroSize = llvm::Intrinsic::getDeclaration(\n        M.get(), llvm::Intrinsic::coro_size, {B->getInt64Ty()});\n    llvm::FunctionCallee coroEnd =\n        llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_end);\n    llvm::FunctionCallee coroAlloc =\n        llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_alloc);\n    llvm::FunctionCallee coroFree =\n        llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_free);\n\n    coro.cleanup = llvm::BasicBlock::Create(*context, \"coro.cleanup\", func);\n    coro.suspend = llvm::BasicBlock::Create(*context, \"coro.suspend\", func);\n    coro.exit = llvm::BasicBlock::Create(*context, \"coro.exit\", func);\n    coro.async = x->isAsync();\n    auto *allocBlock = llvm::BasicBlock::Create(*context, \"coro.alloc\", func);\n    auto *freeBlock = llvm::BasicBlock::Create(*context, \"coro.free\", func);\n\n    // coro ID and promise\n    llvm::Value *id = nullptr;\n    auto *nullPtr = llvm::ConstantPointerNull::get(B->getPtrTy());\n    if (!cast<types::VoidType>(generatorType->getBase())) {\n      coro.promise = B->CreateAlloca(getLLVMType(generatorType->getBase()));\n      coro.promise->setName(\"coro.promise\");\n      id = B->CreateCall(coroId, {B->getInt32(0), coro.promise, nullPtr, nullPtr});\n    } else {\n      id = B->CreateCall(coroId, {B->getInt32(0), nullPtr, nullPtr, nullPtr});\n    }\n    id->setName(\"coro.id\");\n    auto *needAlloc = B->CreateCall(coroAlloc, id);\n    B->CreateCondBr(needAlloc, allocBlock, startBlock);\n\n    // coro alloc\n    B->SetInsertPoint(allocBlock);\n    auto *size = B->CreateCall(coroSize);\n    auto allocFunc = makeAllocFunc(/*atomic=*/false);\n    auto *alloc = B->CreateCall(allocFunc, size);\n    B->CreateBr(startBlock);\n\n    // coro start\n    B->SetInsertPoint(startBlock);\n    auto *phi = B->CreatePHI(B->getPtrTy(), 2);\n    phi->addIncoming(nullPtr, entryBlock);\n    phi->addIncoming(alloc, allocBlock);\n    coro.handle = B->CreateCall(coroBegin, {id, phi});\n    coro.handle->setName(\"coro.handle\");\n\n    // coro cleanup\n    B->SetInsertPoint(coro.cleanup);\n    auto *mem = B->CreateCall(coroFree, {id, coro.handle});\n    auto *needFree = B->CreateIsNotNull(mem);\n    B->CreateCondBr(needFree, freeBlock, coro.suspend);\n\n    // coro free\n    B->SetInsertPoint(freeBlock); // no-op: GC will free automatically\n    B->CreateBr(coro.suspend);\n\n    // coro suspend\n    B->SetInsertPoint(coro.suspend);\n    B->CreateCall(coroEnd,\n                  {coro.handle, B->getFalse(), llvm::ConstantTokenNone::get(*context)});\n    B->CreateRet(coro.handle);\n\n    // coro exit\n    block = coro.exit;\n    makeYield(nullptr, /*finalYield=*/true);\n    B->SetInsertPoint(block);\n    B->CreateUnreachable();\n\n    // initial yield\n    block = startBlock;\n    makeYield(); // coroutine will be initially suspended\n  } else {\n    B->CreateBr(startBlock);\n    block = startBlock;\n  }\n\n  seqassertn(x->getBody(), \"{} has no body [{}]\", x->getName(), x->getSrcInfo());\n  process(x->getBody());\n  B->SetInsertPoint(block);\n\n  if (generator) {\n    B->CreateBr(coro.exit);\n  } else {\n    if (cast<types::VoidType>(returnType)) {\n      B->CreateRetVoid();\n    } else {\n      B->CreateRet(llvm::Constant::getNullValue(getLLVMType(returnType)));\n    }\n  }\n}\n\nvoid LLVMVisitor::visit(const Var *x) { seqassertn(0, \"cannot visit var\"); }\n\nvoid LLVMVisitor::visit(const VarValue *x) {\n  if (auto *f = cast<Func>(x->getVar())) {\n    value = getFunc(f);\n    seqassertn(value, \"{} value not found\", *x);\n  } else {\n    auto *varPtr = getVar(x->getVar());\n    seqassertn(varPtr, \"{} value not found\", *x);\n    B->SetInsertPoint(block);\n    if (x->getVar()->isThreadLocal())\n      varPtr = B->CreateThreadLocalAddress(varPtr);\n    value = B->CreateLoad(getLLVMType(x->getType()), varPtr);\n  }\n}\n\nvoid LLVMVisitor::visit(const PointerValue *x) {\n  const auto &fields = x->getFields();\n  auto *var = getVar(x->getVar());\n  seqassertn(var, \"{} variable not found\", *x);\n  B->SetInsertPoint(block);\n\n  if (x->getVar()->isThreadLocal())\n    var = B->CreateThreadLocalAddress(var);\n\n  if (fields.empty()) {\n    value = var; // note: we don't load the pointer\n    return;\n  }\n\n  auto *type = x->getVar()->getType();\n  std::vector<llvm::Value *> gepIndices = {B->getInt32(0)};\n  for (auto &field : x->getFields()) {\n    if (auto *ref = cast<types::RefType>(type)) {\n      auto membIndex = ref->getMemberIndex(field);\n      auto membType = ref->getMemberType(field);\n      seqassertn(membIndex >= 0 && membType, \"field {} not found in referecne type\",\n                 field);\n      gepIndices.push_back(B->getInt32(0));\n      gepIndices.push_back(B->getInt32(membIndex));\n      type = membType;\n    } else if (auto *rec = cast<types::RecordType>(type)) {\n      auto membIndex = rec->getMemberIndex(field);\n      auto membType = rec->getMemberType(field);\n      seqassertn(membIndex >= 0 && membType, \"field {} not found in record type\",\n                 field);\n      gepIndices.push_back(B->getInt32(membIndex));\n      type = membType;\n    } else {\n      seqassertn(false, \"type in pointer value was not a record or reference type\");\n    }\n  }\n\n  value = B->CreateInBoundsGEP(getLLVMType(x->getVar()->getType()), var, gepIndices);\n}\n\n/*\n * Types\n */\n\nllvm::Type *LLVMVisitor::getLLVMType(types::Type *t) {\n  if (auto *x = cast<types::IntType>(t)) {\n    return B->getInt64Ty();\n  }\n\n  if (auto *x = cast<types::FloatType>(t)) {\n    return B->getDoubleTy();\n  }\n\n  if (auto *x = cast<types::Float32Type>(t)) {\n    return B->getFloatTy();\n  }\n\n  if (auto *x = cast<types::Float16Type>(t)) {\n    return B->getHalfTy();\n  }\n\n  if (auto *x = cast<types::BFloat16Type>(t)) {\n    return B->getBFloatTy();\n  }\n\n  if (auto *x = cast<types::Float128Type>(t)) {\n    return llvm::Type::getFP128Ty(*context);\n  }\n\n  if (auto *x = cast<types::BoolType>(t)) {\n    return B->getInt8Ty();\n  }\n\n  if (auto *x = cast<types::ByteType>(t)) {\n    return B->getInt8Ty();\n  }\n\n  if (auto *x = cast<types::VoidType>(t)) {\n    return B->getVoidTy();\n  }\n\n  if (auto *x = cast<types::RecordType>(t)) {\n    std::vector<llvm::Type *> body;\n    for (const auto &field : *x) {\n      body.push_back(getLLVMType(field.getType()));\n    }\n    return llvm::StructType::get(*context, body);\n  }\n\n  if (auto *x = cast<types::RefType>(t)) {\n    return B->getPtrTy();\n  }\n\n  if (auto *x = cast<types::FuncType>(t)) {\n    return getLLVMFuncType(x)->getPointerTo();\n  }\n\n  if (auto *x = cast<types::OptionalType>(t)) {\n    if (cast<types::RefType>(x->getBase())) {\n      return getLLVMType(x->getBase());\n    } else {\n      return llvm::StructType::get(B->getInt1Ty(), getLLVMType(x->getBase()));\n    }\n  }\n\n  if (auto *x = cast<types::PointerType>(t)) {\n    return getLLVMType(x->getBase())->getPointerTo();\n  }\n\n  if (auto *x = cast<types::GeneratorType>(t)) {\n    return B->getPtrTy();\n  }\n\n  if (auto *x = cast<types::IntNType>(t)) {\n    return B->getIntNTy(x->getLen());\n  }\n\n  if (auto *x = cast<types::VectorType>(t)) {\n    return llvm::VectorType::get(getLLVMType(x->getBase()), x->getCount(),\n                                 /*Scalable=*/false);\n  }\n\n  if (auto *x = cast<types::UnionType>(t)) {\n    auto &layout = M->getDataLayout();\n    llvm::Type *largest = nullptr;\n    size_t maxSize = 0;\n\n    for (auto *t : *x) {\n      auto *llvmType = getLLVMType(t);\n      size_t size = layout.getTypeAllocSizeInBits(llvmType);\n      if (!largest || size > maxSize) {\n        largest = llvmType;\n        maxSize = size;\n      }\n    }\n\n    if (!largest)\n      largest = llvm::StructType::get(*context, {});\n\n    return llvm::StructType::get(*context, {B->getInt8Ty(), largest});\n  }\n\n  if (auto *x = cast<dsl::types::CustomType>(t)) {\n    return x->getBuilder()->buildType(this);\n  }\n\n  seqassertn(0, \"unknown type: {}\", *t);\n  return nullptr;\n}\n\nllvm::FunctionType *LLVMVisitor::getLLVMFuncType(types::Type *t) {\n  auto *x = cast<types::FuncType>(t);\n  seqassertn(x, \"input type was not a func type\");\n  auto *returnType = getLLVMType(x->getReturnType());\n  std::vector<llvm::Type *> argTypes;\n  for (auto *argType : *x) {\n    argTypes.push_back(getLLVMType(argType));\n  }\n  return llvm::FunctionType::get(returnType, argTypes, x->isVariadic());\n}\n\nllvm::DIType *LLVMVisitor::getDITypeHelper(\n    types::Type *t, std::unordered_map<std::string, llvm::DICompositeType *> &cache) {\n  auto *type = getLLVMType(t);\n  auto &layout = M->getDataLayout();\n\n  if (auto *x = cast<types::IntType>(t)) {\n    return db.builder->createBasicType(\n        x->getName(), layout.getTypeAllocSizeInBits(type), llvm::dwarf::DW_ATE_signed);\n  }\n\n  if (auto *x = cast<types::FloatType>(t)) {\n    return db.builder->createBasicType(\n        x->getName(), layout.getTypeAllocSizeInBits(type), llvm::dwarf::DW_ATE_float);\n  }\n\n  if (auto *x = cast<types::Float32Type>(t)) {\n    return db.builder->createBasicType(\n        x->getName(), layout.getTypeAllocSizeInBits(type), llvm::dwarf::DW_ATE_float);\n  }\n\n  if (auto *x = cast<types::Float16Type>(t)) {\n    return db.builder->createBasicType(\n        x->getName(), layout.getTypeAllocSizeInBits(type), llvm::dwarf::DW_ATE_float);\n  }\n\n  if (auto *x = cast<types::BFloat16Type>(t)) {\n    return db.builder->createBasicType(\n        x->getName(), layout.getTypeAllocSizeInBits(type), llvm::dwarf::DW_ATE_float);\n  }\n\n  if (auto *x = cast<types::Float128Type>(t)) {\n    return db.builder->createBasicType(x->getName(),\n                                       layout.getTypeAllocSizeInBits(type),\n                                       llvm::dwarf::DW_ATE_HP_float128);\n  }\n\n  if (auto *x = cast<types::BoolType>(t)) {\n    return db.builder->createBasicType(\n        x->getName(), layout.getTypeAllocSizeInBits(type), llvm::dwarf::DW_ATE_boolean);\n  }\n\n  if (auto *x = cast<types::ByteType>(t)) {\n    return db.builder->createBasicType(x->getName(),\n                                       layout.getTypeAllocSizeInBits(type),\n                                       llvm::dwarf::DW_ATE_signed_char);\n  }\n\n  if (auto *x = cast<types::VoidType>(t)) {\n    return nullptr;\n  }\n\n  if (auto *x = cast<types::RecordType>(t)) {\n    auto it = cache.find(x->getName());\n    if (it != cache.end()) {\n      return it->second;\n    } else {\n      auto *structType = llvm::cast<llvm::StructType>(type);\n      auto *structLayout = layout.getStructLayout(structType);\n      auto *srcInfo = getSrcInfo(x);\n      auto *memberInfo = x->getAttribute<MemberAttribute>();\n      auto *file = db.getFile(srcInfo->file);\n      std::vector<llvm::Metadata *> members;\n\n      auto *diType = db.builder->createStructType(\n          file, x->getName(), file, srcInfo->line, structLayout->getSizeInBits(),\n          /*AlignInBits=*/0, llvm::DINode::FlagZero, /*DerivedFrom=*/nullptr,\n          db.builder->getOrCreateArray(members));\n\n      // prevent infinite recursion on recursive types\n      cache.emplace(x->getName(), diType);\n\n      unsigned memberIdx = 0;\n      for (const auto &field : *x) {\n        auto *subSrcInfo = srcInfo;\n        auto *subFile = file;\n        if (memberInfo) {\n          auto it = memberInfo->memberSrcInfo.find(field.getName());\n          if (it != memberInfo->memberSrcInfo.end()) {\n            subSrcInfo = &it->second;\n            subFile = db.getFile(subSrcInfo->file);\n          }\n        }\n        members.push_back(db.builder->createMemberType(\n            diType, field.getName(), subFile, subSrcInfo->line,\n            layout.getTypeAllocSizeInBits(getLLVMType(field.getType())),\n            /*AlignInBits=*/0, structLayout->getElementOffsetInBits(memberIdx),\n            llvm::DINode::FlagZero, getDITypeHelper(field.getType(), cache)));\n        ++memberIdx;\n      }\n\n      db.builder->replaceArrays(diType, db.builder->getOrCreateArray(members));\n      return diType;\n    }\n  }\n\n  if (auto *x = cast<types::RefType>(t)) {\n    auto *ref = db.builder->createReferenceType(\n        llvm::dwarf::DW_TAG_reference_type, getDITypeHelper(x->getContents(), cache));\n    return ref;\n  }\n\n  if (auto *x = cast<types::FuncType>(t)) {\n    std::vector<llvm::Metadata *> argTypes = {\n        getDITypeHelper(x->getReturnType(), cache)};\n    for (auto *argType : *x) {\n      argTypes.push_back(getDITypeHelper(argType, cache));\n    }\n    return db.builder->createPointerType(\n        db.builder->createSubroutineType(llvm::MDTuple::get(*context, argTypes)),\n        layout.getTypeAllocSizeInBits(type));\n  }\n\n  if (auto *x = cast<types::OptionalType>(t)) {\n    if (cast<types::RefType>(x->getBase())) {\n      return getDITypeHelper(x->getBase(), cache);\n    } else {\n      auto *baseType = getLLVMType(x->getBase());\n      auto *structType = llvm::StructType::get(B->getInt1Ty(), baseType);\n      auto *structLayout = layout.getStructLayout(structType);\n      auto *srcInfo = getSrcInfo(x);\n      auto i1SizeInBits = layout.getTypeAllocSizeInBits(B->getInt1Ty());\n      auto *i1DebugType =\n          db.builder->createBasicType(\"i1\", i1SizeInBits, llvm::dwarf::DW_ATE_boolean);\n      auto *file = db.getFile(srcInfo->file);\n      std::vector<llvm::Metadata *> members;\n\n      auto *diType = db.builder->createStructType(\n          file, x->getName(), file, srcInfo->line, structLayout->getSizeInBits(),\n          /*AlignInBits=*/0, llvm::DINode::FlagZero, /*DerivedFrom=*/nullptr,\n          db.builder->getOrCreateArray(members));\n\n      members.push_back(db.builder->createMemberType(\n          diType, \"has\", file, srcInfo->line, i1SizeInBits,\n          /*AlignInBits=*/0, structLayout->getElementOffsetInBits(0),\n          llvm::DINode::FlagZero, i1DebugType));\n\n      members.push_back(db.builder->createMemberType(\n          diType, \"val\", file, srcInfo->line, layout.getTypeAllocSizeInBits(baseType),\n          /*AlignInBits=*/0, structLayout->getElementOffsetInBits(1),\n          llvm::DINode::FlagZero, getDITypeHelper(x->getBase(), cache)));\n\n      db.builder->replaceArrays(diType, db.builder->getOrCreateArray(members));\n      return diType;\n    }\n  }\n\n  if (auto *x = cast<types::PointerType>(t)) {\n    return db.builder->createPointerType(getDITypeHelper(x->getBase(), cache),\n                                         layout.getTypeAllocSizeInBits(type));\n  }\n\n  if (auto *x = cast<types::GeneratorType>(t)) {\n    return db.builder->createBasicType(\n        x->getName(), layout.getTypeAllocSizeInBits(type), llvm::dwarf::DW_ATE_address);\n  }\n\n  if (auto *x = cast<types::IntNType>(t)) {\n    return db.builder->createBasicType(\n        x->getName(), layout.getTypeAllocSizeInBits(type),\n        x->isSigned() ? llvm::dwarf::DW_ATE_signed : llvm::dwarf::DW_ATE_unsigned);\n  }\n\n  if (auto *x = cast<types::VectorType>(t)) {\n    return db.builder->createBasicType(x->getName(),\n                                       layout.getTypeAllocSizeInBits(type),\n                                       llvm::dwarf::DW_ATE_unsigned);\n  }\n\n  if (auto *x = cast<types::UnionType>(t)) {\n    return db.builder->createBasicType(x->getName(),\n                                       layout.getTypeAllocSizeInBits(type),\n                                       llvm::dwarf::DW_ATE_unsigned);\n  }\n\n  if (auto *x = cast<dsl::types::CustomType>(t)) {\n    return x->getBuilder()->buildDebugType(this);\n  }\n\n  seqassertn(0, \"unknown type\");\n  return nullptr;\n}\n\nllvm::DIType *LLVMVisitor::getDIType(types::Type *t) {\n  std::unordered_map<std::string, llvm::DICompositeType *> cache;\n  return getDITypeHelper(t, cache);\n}\n\nLLVMVisitor::LoopData *LLVMVisitor::getLoopData(id_t loopId) {\n  for (auto &d : loops) {\n    if (d.loopId == loopId)\n      return &d;\n  }\n  return nullptr;\n}\n\n/*\n * Constants\n */\n\nvoid LLVMVisitor::visit(const IntConst *x) {\n  B->SetInsertPoint(block);\n  value = B->getInt64(x->getVal());\n}\n\nvoid LLVMVisitor::visit(const FloatConst *x) {\n  B->SetInsertPoint(block);\n  value = llvm::ConstantFP::get(B->getDoubleTy(), x->getVal());\n}\n\nvoid LLVMVisitor::visit(const BoolConst *x) {\n  B->SetInsertPoint(block);\n  value = B->getInt8(x->getVal() ? 1 : 0);\n}\n\nvoid LLVMVisitor::visit(const StringConst *x) {\n  B->SetInsertPoint(block);\n  std::string s = x->getVal();\n  auto *strVar =\n      new llvm::GlobalVariable(*M, llvm::ArrayType::get(B->getInt8Ty(), s.length() + 1),\n                               /*isConstant=*/true, llvm::GlobalValue::PrivateLinkage,\n                               llvm::ConstantDataArray::getString(*context, s), \".str\");\n  strVar->setUnnamedAddr(llvm::GlobalValue::UnnamedAddr::Global);\n  auto *strType = llvm::StructType::get(B->getInt64Ty(), B->getPtrTy());\n  auto *ptr = B->CreateBitCast(strVar, B->getPtrTy());\n  auto *len = B->getInt64(s.length());\n  llvm::Value *str = llvm::UndefValue::get(strType);\n  str = B->CreateInsertValue(str, len, 0);\n  str = B->CreateInsertValue(str, ptr, 1);\n  value = str;\n}\n\nvoid LLVMVisitor::visit(const dsl::CustomConst *x) {\n  x->getBuilder()->buildValue(this);\n}\n\n/*\n * Control flow\n */\n\nvoid LLVMVisitor::visit(const SeriesFlow *x) {\n  for (auto *value : *x) {\n    process(value);\n  }\n}\n\nvoid LLVMVisitor::visit(const IfFlow *x) {\n  auto *trueBlock = llvm::BasicBlock::Create(*context, \"if.true\", func);\n  auto *falseBlock = llvm::BasicBlock::Create(*context, \"if.false\", func);\n  auto *exitBlock = llvm::BasicBlock::Create(*context, \"if.exit\", func);\n\n  process(x->getCond());\n  auto *cond = value;\n  B->SetInsertPoint(block);\n  cond = B->CreateTrunc(cond, B->getInt1Ty());\n  B->CreateCondBr(cond, trueBlock, falseBlock);\n\n  block = trueBlock;\n  if (x->getTrueBranch()) {\n    process(x->getTrueBranch());\n  }\n  B->SetInsertPoint(block);\n  B->CreateBr(exitBlock);\n\n  block = falseBlock;\n  if (x->getFalseBranch()) {\n    process(x->getFalseBranch());\n  }\n  B->SetInsertPoint(block);\n  B->CreateBr(exitBlock);\n\n  block = exitBlock;\n}\n\nvoid LLVMVisitor::visit(const WhileFlow *x) {\n  auto *condBlock = llvm::BasicBlock::Create(*context, \"while.cond\", func);\n  auto *bodyBlock = llvm::BasicBlock::Create(*context, \"while.body\", func);\n  auto *exitBlock = llvm::BasicBlock::Create(*context, \"while.exit\", func);\n\n  B->SetInsertPoint(block);\n  B->CreateBr(condBlock);\n\n  block = condBlock;\n  process(x->getCond());\n  auto *cond = value;\n  B->SetInsertPoint(block);\n  cond = B->CreateTrunc(cond, B->getInt1Ty());\n  B->CreateCondBr(cond, bodyBlock, exitBlock);\n\n  block = bodyBlock;\n  enterLoop(\n      {/*breakBlock=*/exitBlock, /*continueBlock=*/condBlock, /*loopId=*/x->getId()});\n  process(x->getBody());\n  exitLoop();\n  B->SetInsertPoint(block);\n  B->CreateBr(condBlock);\n\n  block = exitBlock;\n}\n\nvoid LLVMVisitor::visit(const ForFlow *x) {\n  seqassertn(!x->isParallel(), \"parallel for-loop not lowered\");\n  auto *loopVarType = getLLVMType(x->getVar()->getType());\n  auto *loopVar = getVar(x->getVar());\n  seqassertn(loopVar, \"{} loop variable not found\", *x);\n\n  auto *condBlock = llvm::BasicBlock::Create(*context, \"for.cond\", func);\n  auto *bodyBlock = llvm::BasicBlock::Create(*context, \"for.body\", func);\n  auto *cleanupBlock = llvm::BasicBlock::Create(*context, \"for.cleanup\", func);\n  auto *exitBlock = llvm::BasicBlock::Create(*context, \"for.exit\", func);\n\n  // LLVM coroutine intrinsics\n  // https://prereleases.llvm.org/6.0.0/rc3/docs/Coroutines.html\n  llvm::FunctionCallee coroResume =\n      llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_resume);\n  llvm::FunctionCallee coroDone =\n      llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_done);\n  llvm::FunctionCallee coroPromise =\n      llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_promise);\n  llvm::FunctionCallee coroDestroy =\n      llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_destroy);\n\n  process(x->getIter());\n  auto *iter = value;\n  B->SetInsertPoint(block);\n  B->CreateBr(condBlock);\n\n  block = condBlock;\n  call(coroResume, {iter});\n  B->SetInsertPoint(block);\n  auto *done = B->CreateCall(coroDone, iter);\n  B->CreateCondBr(done, cleanupBlock, bodyBlock);\n\n  if (!loopVarType->isVoidTy()) {\n    B->SetInsertPoint(bodyBlock);\n    auto *alignment =\n        B->getInt32(M->getDataLayout().getPrefTypeAlign(loopVarType).value());\n    auto *from = B->getFalse();\n    auto *promise = B->CreateCall(coroPromise, {iter, alignment, from});\n    auto *generatedValue = B->CreateLoad(loopVarType, promise);\n    B->CreateStore(generatedValue, loopVar);\n  }\n\n  block = bodyBlock;\n  enterLoop(\n      {/*breakBlock=*/exitBlock, /*continueBlock=*/condBlock, /*loopId=*/x->getId()});\n  process(x->getBody());\n  exitLoop();\n  B->SetInsertPoint(block);\n  B->CreateBr(condBlock);\n\n  B->SetInsertPoint(cleanupBlock);\n  B->CreateCall(coroDestroy, iter);\n  B->CreateBr(exitBlock);\n\n  block = exitBlock;\n}\n\nvoid LLVMVisitor::visit(const ImperativeForFlow *x) {\n  seqassertn(!x->isParallel(), \"parallel for-loop not lowered\");\n  auto *loopVar = getVar(x->getVar());\n  seqassertn(loopVar, \"{} loop variable not found\", *x);\n  seqassertn(x->getStep() != 0, \"step cannot be 0\");\n\n  auto *condBlock = llvm::BasicBlock::Create(*context, \"imp_for.cond\", func);\n  auto *bodyBlock = llvm::BasicBlock::Create(*context, \"imp_for.body\", func);\n  auto *updateBlock = llvm::BasicBlock::Create(*context, \"imp_for.update\", func);\n  auto *exitBlock = llvm::BasicBlock::Create(*context, \"imp_for.exit\", func);\n\n  process(x->getStart());\n  auto *start = value;\n\n  process(x->getEnd());\n  auto *end = value;\n\n  B->SetInsertPoint(block);\n  B->CreateBr(condBlock);\n  B->SetInsertPoint(condBlock);\n\n  auto *phi = B->CreatePHI(B->getInt64Ty(), 2);\n  phi->addIncoming(start, block);\n\n  auto *done =\n      (x->getStep() > 0) ? B->CreateICmpSGE(phi, end) : B->CreateICmpSLE(phi, end);\n  B->CreateCondBr(done, exitBlock, bodyBlock);\n\n  B->SetInsertPoint(bodyBlock);\n  B->CreateStore(phi, loopVar);\n  block = bodyBlock;\n\n  enterLoop(\n      {/*breakBlock=*/exitBlock, /*continueBlock=*/updateBlock, /*loopId=*/x->getId()});\n  process(x->getBody());\n  exitLoop();\n  B->SetInsertPoint(block);\n  B->CreateBr(updateBlock);\n\n  B->SetInsertPoint(updateBlock);\n  phi->addIncoming(B->CreateAdd(phi, B->getInt64(x->getStep())), updateBlock);\n  B->CreateBr(condBlock);\n\n  block = exitBlock;\n}\n\nnamespace {\nbool anyMatch(types::Type *type, std::vector<types::Type *> types) {\n  if (type) {\n    for (auto *t : types) {\n      if (t && t->getName() == type->getName())\n        return true;\n    }\n  } else {\n    for (auto *t : types) {\n      if (!t)\n        return true;\n    }\n  }\n  return false;\n}\n} // namespace\n\nvoid LLVMVisitor::visit(const TryCatchFlow *x) {\n  const bool isRoot = trycatch.empty();\n  const bool supportBreakAndContinue = !loops.empty();\n  auto *finallyFlow = cast<SeriesFlow>(x->getFinally());\n  const bool haveFinally = (finallyFlow && finallyFlow->begin() != finallyFlow->end());\n  B->SetInsertPoint(block);\n  auto *entryBlock = llvm::BasicBlock::Create(*context, \"trycatch.entry\", func);\n  B->CreateBr(entryBlock);\n\n  TryCatchData tc;\n  tc.exceptionBlock = llvm::BasicBlock::Create(*context, \"trycatch.exception\", func);\n  tc.exceptionRouteBlock =\n      llvm::BasicBlock::Create(*context, \"trycatch.exception_route\", func);\n  tc.finallyBlock = llvm::BasicBlock::Create(*context, \"trycatch.finally\", func);\n  tc.finallyExceptionBlock =\n      llvm::BasicBlock::Create(*context, \"trycatch.finally.exception\", func);\n\n  auto *externalExcBlock =\n      llvm::BasicBlock::Create(*context, \"trycatch.exception_external\", func);\n  auto *unwindResumeBlock =\n      llvm::BasicBlock::Create(*context, \"trycatch.unwind_resume\", func);\n  auto *rethrowBlock = llvm::BasicBlock::Create(*context, \"trycatch.rethrow\", func);\n  auto *endBlock = llvm::BasicBlock::Create(*context, \"trycatch.end\", func);\n\n  B->SetInsertPoint(func->getEntryBlock().getTerminator());\n  auto *excStateNotThrown = B->getInt8(TryCatchData::State::NOT_THROWN);\n  auto *excStateThrown = B->getInt8(TryCatchData::State::THROWN);\n  auto *excStateCaught = B->getInt8(TryCatchData::State::CAUGHT);\n  auto *excStateReturn = B->getInt8(TryCatchData::State::RETURN);\n  auto *excStateBreak = B->getInt8(TryCatchData::State::BREAK);\n  auto *excStateContinue = B->getInt8(TryCatchData::State::CONTINUE);\n  auto *excStateRethrow = B->getInt8(TryCatchData::State::RETHROW);\n\n  auto *padType = getPadType();\n  auto *unwindType = llvm::StructType::get(B->getInt64Ty()); // header only\n\n  if (isRoot) {\n    tc.excFlag = B->CreateAlloca(B->getInt8Ty());\n    tc.catchStore = B->CreateAlloca(padType);\n    tc.delegateDepth = B->CreateAlloca(B->getInt64Ty());\n    tc.retStore = (coro.exit || func->getReturnType()->isVoidTy())\n                      ? nullptr\n                      : B->CreateAlloca(func->getReturnType());\n    tc.loopSequence = B->CreateAlloca(B->getInt64Ty());\n    B->CreateStore(excStateNotThrown, tc.excFlag);\n    B->CreateStore(llvm::ConstantAggregateZero::get(padType), tc.catchStore);\n    B->CreateStore(B->getInt64(0), tc.delegateDepth);\n    B->CreateStore(B->getInt64(-1), tc.loopSequence);\n  } else {\n    tc.excFlag = trycatch[0].excFlag;\n    tc.catchStore = trycatch[0].catchStore;\n    tc.delegateDepth = trycatch[0].delegateDepth;\n    tc.retStore = trycatch[0].retStore;\n    tc.loopSequence = trycatch[0].loopSequence;\n  }\n\n  // translate finally\n  block = tc.finallyBlock;\n  process(x->getFinally());\n  auto *finallyBlock = block;\n  B->SetInsertPoint(finallyBlock);\n  auto *excFlagRead = B->CreateLoad(B->getInt8Ty(), tc.excFlag);\n\n  if (!isRoot) {\n    auto *depthRead = B->CreateLoad(B->getInt64Ty(), tc.delegateDepth);\n    auto *delegate = B->CreateICmpSGT(depthRead, B->getInt64(0));\n    auto *finallyNormal =\n        llvm::BasicBlock::Create(*context, \"trycatch.finally.normal\", func);\n    auto *finallyDelegate =\n        llvm::BasicBlock::Create(*context, \"trycatch.finally.delegate\", func);\n    B->CreateCondBr(delegate, finallyDelegate, finallyNormal);\n\n    B->SetInsertPoint(finallyDelegate);\n    auto *depthNew = B->CreateSub(depthRead, B->getInt64(1));\n    auto *delegateNew = B->CreateICmpSGT(depthNew, B->getInt64(0));\n    B->CreateStore(depthNew, tc.delegateDepth);\n    B->CreateCondBr(delegateNew, trycatch.back().finallyBlock,\n                    trycatch.back().exceptionRouteBlock);\n\n    finallyBlock = finallyNormal;\n    B->SetInsertPoint(finallyNormal);\n  }\n\n  // handle exceptions that must route through 'finally'\n  B->SetInsertPoint(tc.finallyExceptionBlock);\n  if (!DisableExceptions) {\n    auto *finallyCaughtResult = B->CreateLandingPad(padType, 1);\n    finallyCaughtResult->setCleanup(true);\n    finallyCaughtResult->addClause(getTypeIdxVar(nullptr));\n\n    B->CreateStore(finallyCaughtResult, tc.catchStore);\n    B->CreateStore(excStateRethrow, tc.excFlag);\n    auto *depthMax = B->getInt64(trycatch.size());\n    B->CreateStore(depthMax, tc.delegateDepth);\n    B->CreateBr(tc.finallyBlock);\n  } else {\n    B->CreateUnreachable();\n  }\n\n  B->SetInsertPoint(finallyBlock);\n  auto *theSwitch =\n      B->CreateSwitch(excFlagRead, endBlock, supportBreakAndContinue ? 6 : 4);\n  theSwitch->addCase(excStateCaught, endBlock);\n  theSwitch->addCase(excStateThrown, unwindResumeBlock);\n  theSwitch->addCase(excStateRethrow, rethrowBlock);\n\n  if (isRoot) {\n    auto *finallyReturn =\n        llvm::BasicBlock::Create(*context, \"trycatch.finally.return\", func);\n    theSwitch->addCase(excStateReturn, finallyReturn);\n    B->SetInsertPoint(finallyReturn);\n    if (coro.exit) {\n      B->CreateBr(coro.exit);\n    } else if (tc.retStore) {\n      auto *retVal = B->CreateLoad(func->getReturnType(), tc.retStore);\n      B->CreateRet(retVal);\n    } else {\n      B->CreateRetVoid();\n    }\n  } else {\n    theSwitch->addCase(excStateReturn, trycatch.back().finallyBlock);\n  }\n\n  if (supportBreakAndContinue) {\n    auto prevSeq = isRoot ? -1 : trycatch.back().sequenceNumber;\n\n    auto *finallyBreak =\n        llvm::BasicBlock::Create(*context, \"trycatch.finally.break\", func);\n    auto *finallyBreakDone =\n        llvm::BasicBlock::Create(*context, \"trycatch.finally.break.done\", func);\n    auto *finallyContinue =\n        llvm::BasicBlock::Create(*context, \"trycatch.finally.continue\", func);\n    auto *finallyContinueDone =\n        llvm::BasicBlock::Create(*context, \"trycatch.finally.continue.done\", func);\n\n    B->SetInsertPoint(finallyBreak);\n    auto *breakSwitch =\n        B->CreateSwitch(B->CreateLoad(B->getInt64Ty(), tc.loopSequence), endBlock, 0);\n    B->SetInsertPoint(finallyBreakDone);\n    B->CreateStore(excStateNotThrown, tc.excFlag);\n    auto *breakDoneSwitch =\n        B->CreateSwitch(B->CreateLoad(B->getInt64Ty(), tc.loopSequence), endBlock, 0);\n\n    B->SetInsertPoint(finallyContinue);\n    auto *continueSwitch =\n        B->CreateSwitch(B->CreateLoad(B->getInt64Ty(), tc.loopSequence), endBlock, 0);\n    B->SetInsertPoint(finallyContinueDone);\n    B->CreateStore(excStateNotThrown, tc.excFlag);\n    auto *continueDoneSwitch =\n        B->CreateSwitch(B->CreateLoad(B->getInt64Ty(), tc.loopSequence), endBlock, 0);\n\n    for (auto &l : loops) {\n      if (!trycatch.empty() && l.sequenceNumber < prevSeq) {\n        breakSwitch->addCase(B->getInt64(l.sequenceNumber),\n                             trycatch.back().finallyBlock);\n        continueSwitch->addCase(B->getInt64(l.sequenceNumber),\n                                trycatch.back().finallyBlock);\n      } else {\n        breakSwitch->addCase(B->getInt64(l.sequenceNumber), finallyBreakDone);\n        breakDoneSwitch->addCase(B->getInt64(l.sequenceNumber), l.breakBlock);\n        continueSwitch->addCase(B->getInt64(l.sequenceNumber), finallyContinueDone);\n        continueDoneSwitch->addCase(B->getInt64(l.sequenceNumber), l.continueBlock);\n      }\n    }\n    theSwitch->addCase(excStateBreak, finallyBreak);\n    theSwitch->addCase(excStateContinue, finallyContinue);\n  }\n\n  // try and catch translate\n  std::vector<const TryCatchFlow::Catch *> catches;\n  for (auto &c : *x) {\n    catches.push_back(&c);\n  }\n  llvm::BasicBlock *catchAll = nullptr;\n\n  for (auto *c : catches) {\n    auto *catchBlock = llvm::BasicBlock::Create(*context, \"trycatch.catch\", func);\n    tc.catchTypes.push_back(c->getType());\n    tc.handlers.push_back(catchBlock);\n\n    if (!c->getType()) {\n      seqassertn(!catchAll, \"cannot be catch all\");\n      catchAll = catchBlock;\n    }\n  }\n\n  // translate try\n  if (!loops.empty()) {\n    // make sure we reset the state to avoid issues with 'break'/'continue'\n    B->SetInsertPoint(entryBlock);\n    B->CreateStore(excStateNotThrown, tc.excFlag);\n  }\n  block = entryBlock;\n  if (haveFinally)\n    enterFinally(tc);\n  enterTry(tc); // this is last so as to have larger sequence number\n  process(x->getBody());\n  exitTry();\n\n  // translate else\n  if (x->getElse()) {\n    B->SetInsertPoint(block);\n    auto *elseBlock = llvm::BasicBlock::Create(*context, \"trycatch.else\", func);\n    B->CreateBr(elseBlock);\n    block = elseBlock;\n    process(x->getElse());\n  }\n\n  // make sure we always get to finally block\n  B->SetInsertPoint(block);\n  B->CreateBr(tc.finallyBlock);\n\n  // resume if uncaught\n  B->SetInsertPoint(unwindResumeBlock);\n  if (DisableExceptions) {\n    B->CreateUnreachable();\n  } else {\n    B->CreateResume(B->CreateLoad(padType, tc.catchStore));\n  }\n\n  // make sure we delegate to parent try-catch if necessary\n  std::vector<types::Type *> catchTypesFull(tc.catchTypes);\n  std::vector<llvm::BasicBlock *> handlersFull(tc.handlers);\n  std::vector<unsigned> depths(tc.catchTypes.size(), 0);\n  unsigned depth = 1;\n\n  unsigned catchAllDepth = 0;\n  for (auto it = trycatch.rbegin(); it != trycatch.rend(); ++it) {\n    if (catchAll) // can't ever delegate past catch-all\n      break;\n\n    seqassertn(it->catchTypes.size() == it->handlers.size(), \"handler mismatch\");\n    for (unsigned i = 0; i < it->catchTypes.size(); i++) {\n      if (!anyMatch(it->catchTypes[i], catchTypesFull)) {\n        catchTypesFull.push_back(it->catchTypes[i]);\n        depths.push_back(depth);\n\n        if (!it->catchTypes[i] && !catchAll) {\n          // catch-all is in parent; set finally depth\n          catchAll =\n              llvm::BasicBlock::Create(*context, \"trycatch.fdepth_catchall\", func);\n          B->SetInsertPoint(catchAll);\n          B->CreateStore(B->getInt64(depth), tc.delegateDepth);\n          B->CreateBr(it->handlers[i]);\n          handlersFull.push_back(catchAll);\n          catchAllDepth = depth;\n        } else {\n          handlersFull.push_back(it->handlers[i]);\n        }\n      }\n    }\n    ++depth;\n  }\n\n  // exception handling\n  B->SetInsertPoint(tc.exceptionBlock);\n  llvm::LandingPadInst *caughtResult = nullptr;\n  if (!DisableExceptions) {\n    caughtResult = B->CreateLandingPad(padType, catches.size());\n    caughtResult->setCleanup(true);\n  }\n  std::vector<llvm::Value *> typeIndices;\n\n  for (auto *catchType : catchTypesFull) {\n    seqassertn(!catchType || cast<types::RefType>(catchType), \"invalid catch type\");\n    const std::string typeVarName =\n        \"codon.typeidx.\" + (catchType ? catchType->getName() : \"<all>\");\n    auto *tidx = getTypeIdxVar(catchType);\n    typeIndices.push_back(tidx);\n    if (caughtResult)\n      caughtResult->addClause(tidx);\n  }\n\n  auto *caughtResultOrUndef = caughtResult ? llvm::cast<llvm::Value>(caughtResult)\n                                           : llvm::UndefValue::get(padType);\n  auto *unwindException = B->CreateExtractValue(caughtResultOrUndef, 0);\n  B->CreateStore(caughtResultOrUndef, tc.catchStore);\n  B->CreateStore(excStateThrown, tc.excFlag);\n  auto *depthMax = B->getInt64(trycatch.size());\n  B->CreateStore(depthMax, tc.delegateDepth);\n\n  auto *unwindExceptionClass = B->CreateLoad(\n      B->getInt64Ty(), B->CreateStructGEP(unwindType, unwindException, 0));\n\n  // check for foreign exceptions\n  B->CreateCondBr(\n      B->CreateICmpEQ(unwindExceptionClass, B->getInt64(SEQ_EXCEPTION_CLASS)),\n      tc.exceptionRouteBlock, externalExcBlock);\n\n  // external exception (currently assumed to be unreachable)\n  B->SetInsertPoint(externalExcBlock);\n  B->CreateUnreachable();\n\n  // reroute Codon exceptions\n  B->SetInsertPoint(tc.exceptionRouteBlock);\n  unwindException = B->CreateExtractValue(B->CreateLoad(padType, tc.catchStore), 0);\n  auto *excVal = B->CreateConstGEP1_64(B->getInt8Ty(), unwindException,\n                                       (uint64_t)seq_exc_offset());\n  auto *loadedExc = B->CreateLoad(B->getPtrTy(), excVal);\n\n  // set depth when catch-all entered\n  auto *defaultRouteBlock = llvm::BasicBlock::Create(*context, \"trycatch.fdepth\", func);\n  B->SetInsertPoint(defaultRouteBlock);\n  if (catchAll)\n    B->CreateStore(B->getInt64(catchAllDepth), tc.delegateDepth);\n  B->CreateBr(catchAll ? (catchAllDepth > 0 ? tc.finallyBlock : catchAll)\n                       : tc.finallyBlock);\n\n  B->SetInsertPoint(tc.exceptionRouteBlock);\n  auto *objType = B->CreateExtractValue(B->CreateLoad(padType, tc.catchStore), 1);\n  auto *switchToCatchBlock =\n      B->CreateSwitch(objType, defaultRouteBlock, (unsigned)handlersFull.size());\n  for (unsigned i = 0; i < handlersFull.size(); i++) {\n    // set finally depth\n    auto *depthSet = llvm::BasicBlock::Create(*context, \"trycatch.fdepth\", func);\n    B->SetInsertPoint(depthSet);\n    B->CreateStore(B->getInt64(depths[i]), tc.delegateDepth);\n    B->CreateBr((i < tc.handlers.size()) ? handlersFull[i] : tc.finallyBlock);\n\n    if (catchTypesFull[i]) {\n      switchToCatchBlock->addCase(B->getInt32((uint64_t)getTypeIdx(catchTypesFull[i])),\n                                  depthSet);\n    }\n\n    // translate catch body if this block is ours (vs. a parent's)\n    if (i < catches.size()) {\n      block = handlersFull[i];\n      B->SetInsertPoint(block);\n      const Var *var = catches[i]->getVar();\n\n      if (var) {\n        auto *varPtr = getVar(var);\n        seqassertn(varPtr, \"could not get catch var\");\n        B->CreateStore(loadedExc, varPtr);\n      }\n\n      B->CreateStore(excStateCaught, tc.excFlag);\n      CatchData cd;\n      cd.exception = loadedExc;\n      cd.typeId = objType;\n      enterCatch(cd);\n      process(catches[i]->getHandler());\n      exitCatch();\n      B->SetInsertPoint(block);\n      B->CreateBr(tc.finallyBlock);\n    }\n  }\n\n  if (haveFinally)\n    exitFinally();\n\n  // rethrow if handling 'finally' after exception raised from 'except'/'else'\n  B->SetInsertPoint(rethrowBlock);\n  if (!haveFinally || DisableExceptions) {\n    B->CreateUnreachable();\n  } else {\n    auto throwFunc = makeThrowFunc();\n    unwindException = B->CreateExtractValue(B->CreateLoad(padType, tc.catchStore), 0);\n    block = rethrowBlock;\n    call(throwFunc, unwindException);\n    B->SetInsertPoint(block);\n    B->CreateUnreachable();\n  }\n\n  block = endBlock;\n}\n\nvoid LLVMVisitor::callStage(const PipelineFlow::Stage *stage) {\n  auto *output = value;\n  process(stage->getCallee());\n  auto *f = value;\n  std::vector<llvm::Value *> args;\n  for (const auto *arg : *stage) {\n    if (arg) {\n      process(arg);\n      args.push_back(value);\n    } else {\n      args.push_back(output);\n    }\n  }\n\n  auto *funcType = getLLVMFuncType(stage->getCallee()->getType());\n  value = call({funcType, f}, args);\n}\n\nvoid LLVMVisitor::codegenPipeline(\n    const std::vector<const PipelineFlow::Stage *> &stages, unsigned where) {\n  if (where >= stages.size()) {\n    return;\n  }\n\n  auto *stage = stages[where];\n\n  if (where == 0) {\n    process(stage->getCallee());\n    codegenPipeline(stages, where + 1);\n    return;\n  }\n\n  auto *prevStage = stages[where - 1];\n  const bool generator = prevStage->isGenerator();\n\n  if (generator) {\n    auto *generatorType = cast<types::GeneratorType>(prevStage->getOutputType());\n    seqassertn(generatorType, \"{} is not a generator type\",\n               *prevStage->getOutputType());\n    auto *baseType = getLLVMType(generatorType->getBase());\n\n    auto *condBlock = llvm::BasicBlock::Create(*context, \"pipeline.cond\", func);\n    auto *bodyBlock = llvm::BasicBlock::Create(*context, \"pipeline.body\", func);\n    auto *cleanupBlock = llvm::BasicBlock::Create(*context, \"pipeline.cleanup\", func);\n    auto *exitBlock = llvm::BasicBlock::Create(*context, \"pipeline.exit\", func);\n\n    // LLVM coroutine intrinsics\n    // https://prereleases.llvm.org/6.0.0/rc3/docs/Coroutines.html\n    llvm::FunctionCallee coroResume =\n        llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_resume);\n    llvm::FunctionCallee coroDone =\n        llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_done);\n    llvm::FunctionCallee coroPromise =\n        llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_promise);\n    llvm::FunctionCallee coroDestroy =\n        llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_destroy);\n\n    auto *iter = value;\n    B->SetInsertPoint(block);\n    B->CreateBr(condBlock);\n\n    block = condBlock;\n    call(coroResume, {iter});\n    B->SetInsertPoint(block);\n    auto *done = B->CreateCall(coroDone, iter);\n    B->CreateCondBr(done, cleanupBlock, bodyBlock);\n\n    B->SetInsertPoint(bodyBlock);\n    auto *alignment =\n        B->getInt32(M->getDataLayout().getPrefTypeAlign(baseType).value());\n    auto *from = B->getFalse();\n    auto *promise = B->CreateCall(coroPromise, {iter, alignment, from});\n    value = B->CreateLoad(baseType, promise);\n\n    block = bodyBlock;\n    callStage(stage);\n    codegenPipeline(stages, where + 1);\n\n    B->SetInsertPoint(block);\n    B->CreateBr(condBlock);\n\n    B->SetInsertPoint(cleanupBlock);\n    B->CreateCall(coroDestroy, iter);\n    B->CreateBr(exitBlock);\n\n    block = exitBlock;\n  } else {\n    callStage(stage);\n    codegenPipeline(stages, where + 1);\n  }\n}\n\nvoid LLVMVisitor::visit(const PipelineFlow *x) {\n  std::vector<const PipelineFlow::Stage *> stages;\n  for (const auto &stage : *x) {\n    stages.push_back(&stage);\n  }\n  codegenPipeline(stages);\n}\n\nvoid LLVMVisitor::visit(const dsl::CustomFlow *x) {\n  B->SetInsertPoint(block);\n  value = x->getBuilder()->buildValue(this);\n}\n\n/*\n * Instructions\n */\n\nvoid LLVMVisitor::visit(const AssignInstr *x) {\n  auto *var = getVar(x->getLhs());\n  seqassertn(var, \"could not find {} var\", *x->getLhs());\n  process(x->getRhs());\n  if (var != getDummyVoidValue()) {\n    B->SetInsertPoint(block);\n    if (x->getLhs()->isThreadLocal())\n      var = B->CreateThreadLocalAddress(var);\n    B->CreateStore(value, var);\n  }\n}\n\nvoid LLVMVisitor::visit(const ExtractInstr *x) {\n  auto *memberedType = cast<types::MemberedType>(x->getVal()->getType());\n  seqassertn(memberedType, \"{} is not a membered type\", *x->getVal()->getType());\n  const int index = memberedType->getMemberIndex(x->getField());\n  seqassertn(index >= 0, \"invalid index\");\n\n  process(x->getVal());\n  B->SetInsertPoint(block);\n  if (auto *refType = cast<types::RefType>(memberedType)) {\n    if (refType->isPolymorphic()) {\n      // polymorphic ref type is ref to (data, rtti)\n      value = B->CreateLoad(B->getPtrTy(), value);\n    }\n    value = B->CreateLoad(getLLVMType(refType->getContents()), value);\n  }\n  value = B->CreateExtractValue(value, index);\n}\n\nvoid LLVMVisitor::visit(const InsertInstr *x) {\n  auto *refType = cast<types::RefType>(x->getLhs()->getType());\n  seqassertn(refType, \"{} is not a reference type\", *x->getLhs()->getType());\n  const int index = refType->getMemberIndex(x->getField());\n  seqassertn(index >= 0, \"invalid index\");\n\n  process(x->getLhs());\n  auto *lhs = value;\n  process(x->getRhs());\n  auto *rhs = value;\n\n  B->SetInsertPoint(block);\n  if (refType->isPolymorphic()) {\n    // polymorphic ref type is ref to (data, rtti)\n    lhs = B->CreateLoad(B->getPtrTy(), lhs);\n  }\n  llvm::Value *load = B->CreateLoad(getLLVMType(refType->getContents()), lhs);\n  load = B->CreateInsertValue(load, rhs, index);\n  B->CreateStore(load, lhs);\n}\n\nvoid LLVMVisitor::visit(const CallInstr *x) {\n  B->SetInsertPoint(block);\n  process(x->getCallee());\n  auto *f = value;\n\n  std::vector<llvm::Value *> args;\n  for (auto *arg : *x) {\n    B->SetInsertPoint(block);\n    process(arg);\n    args.push_back(value);\n  }\n\n  auto *funcType = getLLVMFuncType(x->getCallee()->getType());\n  value = call({funcType, f}, args);\n}\n\nvoid LLVMVisitor::visit(const TypePropertyInstr *x) {\n  B->SetInsertPoint(block);\n  switch (x->getProperty()) {\n  case TypePropertyInstr::Property::SIZEOF:\n    value = B->getInt64(\n        M->getDataLayout().getTypeAllocSize(getLLVMType(x->getInspectType())));\n    break;\n  case TypePropertyInstr::Property::IS_ATOMIC:\n    value = B->getInt8(x->getInspectType()->isAtomic() ? 1 : 0);\n    break;\n  case TypePropertyInstr::Property::IS_CONTENT_ATOMIC:\n    value = B->getInt8(x->getInspectType()->isContentAtomic() ? 1 : 0);\n    break;\n  default:\n    seqassertn(0, \"unknown type property\");\n  }\n}\n\nvoid LLVMVisitor::visit(const YieldInInstr *x) {\n  B->SetInsertPoint(block);\n  if (x->isSuspending()) {\n    llvm::FunctionCallee coroSuspend =\n        llvm::Intrinsic::getDeclaration(M.get(), llvm::Intrinsic::coro_suspend);\n    auto *tok = llvm::ConstantTokenNone::get(*context);\n    auto *final = B->getFalse();\n    auto *susp = B->CreateCall(coroSuspend, {tok, final});\n\n    block = llvm::BasicBlock::Create(*context, \"yieldin.new\", func);\n    auto *inst = B->CreateSwitch(susp, coro.suspend, 2);\n    inst->addCase(B->getInt8(0), block);\n    inst->addCase(B->getInt8(1), coro.cleanup);\n    B->SetInsertPoint(block);\n  }\n  value = B->CreateLoad(getLLVMType(x->getType()), coro.promise);\n}\n\nvoid LLVMVisitor::visit(const StackAllocInstr *x) {\n  auto *recordType = cast<types::RecordType>(x->getType());\n  seqassertn(recordType, \"stack alloc does not have record type\");\n  auto *ptrType = cast<types::PointerType>(recordType->back().getType());\n  seqassertn(ptrType, \"array did not have ptr type\");\n\n  auto *arrayType = llvm::cast<llvm::StructType>(getLLVMType(x->getType()));\n  B->SetInsertPoint(func->getEntryBlock().getTerminator());\n  auto *len = B->getInt64(x->getCount());\n  auto *ptr = B->CreateAlloca(getLLVMType(ptrType->getBase()), len);\n  llvm::Value *arr = llvm::UndefValue::get(arrayType);\n  arr = B->CreateInsertValue(arr, len, 0);\n  arr = B->CreateInsertValue(arr, ptr, 1);\n  value = arr;\n}\n\nvoid LLVMVisitor::visit(const TernaryInstr *x) {\n  auto *trueBlock = llvm::BasicBlock::Create(*context, \"ternary.true\", func);\n  auto *falseBlock = llvm::BasicBlock::Create(*context, \"ternary.false\", func);\n  auto *exitBlock = llvm::BasicBlock::Create(*context, \"ternary.exit\", func);\n\n  auto *valueType = getLLVMType(x->getType());\n  process(x->getCond());\n  auto *cond = value;\n\n  B->SetInsertPoint(block);\n  cond = B->CreateTrunc(cond, B->getInt1Ty());\n  B->CreateCondBr(cond, trueBlock, falseBlock);\n\n  block = trueBlock;\n  process(x->getTrueValue());\n  auto *trueValue = value;\n  trueBlock = block;\n  B->SetInsertPoint(trueBlock);\n  B->CreateBr(exitBlock);\n\n  block = falseBlock;\n  process(x->getFalseValue());\n  auto *falseValue = value;\n  falseBlock = block;\n  B->SetInsertPoint(falseBlock);\n  B->CreateBr(exitBlock);\n\n  B->SetInsertPoint(exitBlock);\n  auto *phi = B->CreatePHI(valueType, 2);\n  phi->addIncoming(trueValue, trueBlock);\n  phi->addIncoming(falseValue, falseBlock);\n  value = phi;\n  block = exitBlock;\n}\n\nvoid LLVMVisitor::visit(const BreakInstr *x) {\n  seqassertn(!loops.empty(), \"not in a loop\");\n  B->SetInsertPoint(block);\n\n  auto *loop = !x->getLoop() ? &loops.back() : getLoopData(x->getLoop()->getId());\n\n  if (finally.empty() || finally.back().sequenceNumber < loop->sequenceNumber) {\n    B->CreateBr(loop->breakBlock);\n  } else {\n    auto *tc = &finally.back();\n    auto *excStateBreak = B->getInt8(TryCatchData::State::BREAK);\n    B->CreateStore(excStateBreak, tc->excFlag);\n    B->CreateStore(B->getInt64(loop->sequenceNumber), tc->loopSequence);\n    B->CreateBr(tc->finallyBlock);\n  }\n\n  block = llvm::BasicBlock::Create(*context, \"break.new\", func);\n}\n\nvoid LLVMVisitor::visit(const ContinueInstr *x) {\n  seqassertn(!loops.empty(), \"not in a loop\");\n  B->SetInsertPoint(block);\n  auto *loop = !x->getLoop() ? &loops.back() : getLoopData(x->getLoop()->getId());\n\n  if (finally.empty() || finally.back().sequenceNumber < loop->sequenceNumber) {\n    B->CreateBr(loop->continueBlock);\n  } else {\n    auto *tc = &finally.back();\n    auto *excStateContinue = B->getInt8(TryCatchData::State::CONTINUE);\n    B->CreateStore(excStateContinue, tc->excFlag);\n    B->CreateStore(B->getInt64(loop->sequenceNumber), tc->loopSequence);\n    B->CreateBr(tc->finallyBlock);\n  }\n\n  block = llvm::BasicBlock::Create(*context, \"continue.new\", func);\n}\n\nvoid LLVMVisitor::visit(const ReturnInstr *x) {\n  if (x->getValue()) {\n    process(x->getValue());\n  }\n  B->SetInsertPoint(block);\n  if (coro.exit) {\n    if (coro.async)\n      B->CreateStore(value, coro.promise);\n\n    if (auto *tc = getInnermostTryCatch()) {\n      auto *excStateReturn = B->getInt8(TryCatchData::State::RETURN);\n      B->CreateStore(excStateReturn, tc->excFlag);\n      B->CreateBr(tc->finallyBlock);\n    } else {\n      B->CreateBr(coro.exit);\n    }\n  } else {\n    if (auto *tc = getInnermostTryCatch()) {\n      auto *excStateReturn = B->getInt8(TryCatchData::State::RETURN);\n      B->CreateStore(excStateReturn, tc->excFlag);\n      if (tc->retStore) {\n        seqassertn(value, \"no return value storage\");\n        B->CreateStore(value, tc->retStore);\n      }\n      B->CreateBr(tc->finallyBlock);\n    } else {\n      if (x->getValue()) {\n        B->CreateRet(value);\n      } else {\n        B->CreateRetVoid();\n      }\n    }\n  }\n  block = llvm::BasicBlock::Create(*context, \"return.new\", func);\n}\n\nvoid LLVMVisitor::visit(const YieldInstr *x) {\n  if (x->isFinal()) {\n    if (x->getValue()) {\n      seqassertn(coro.promise, \"no coroutine promise\");\n      process(x->getValue());\n      B->SetInsertPoint(block);\n      B->CreateStore(value, coro.promise);\n    }\n    B->SetInsertPoint(block);\n    if (auto *tc = getInnermostTryCatch()) {\n      auto *excStateReturn = B->getInt8(TryCatchData::State::RETURN);\n      B->CreateStore(excStateReturn, tc->excFlag);\n      B->CreateBr(tc->finallyBlock);\n    } else {\n      B->CreateBr(coro.exit);\n    }\n    block = llvm::BasicBlock::Create(*context, \"yield.new\", func);\n  } else {\n    if (x->getValue()) {\n      process(x->getValue());\n      makeYield(value);\n    } else {\n      makeYield(nullptr);\n    }\n  }\n}\n\nvoid LLVMVisitor::visit(const AwaitInstr *x) {\n  seqassertn(false, \"await instruction not lowered\");\n}\n\nvoid LLVMVisitor::visit(const ThrowInstr *x) {\n  if (DisableExceptions) {\n    B->SetInsertPoint(block);\n    B->CreateUnreachable();\n    block = llvm::BasicBlock::Create(*context, \"throw_unreachable.new\", func);\n    return;\n  }\n\n  // note: exception header should be set in the frontend\n  auto excAllocFunc = makeExcAllocFunc();\n  auto throwFunc = makeThrowFunc();\n  llvm::Value *obj = nullptr;\n  llvm::Value *typ = nullptr;\n\n  if (x->getValue()) {\n    process(x->getValue());\n    obj = value;\n    typ = B->getInt32(getTypeIdx(x->getValue()->getType()));\n  } else {\n    seqassertn(!catches.empty(), \"empty raise outside of except block\");\n    obj = catches.back().exception;\n    typ = catches.back().typeId;\n  }\n\n  B->SetInsertPoint(block);\n  auto *exc = B->CreateCall(excAllocFunc, {obj});\n  call(throwFunc, exc);\n}\n\nvoid LLVMVisitor::visit(const FlowInstr *x) {\n  process(x->getFlow());\n  process(x->getValue());\n}\n\nvoid LLVMVisitor::visit(const dsl::CustomInstr *x) {\n  B->SetInsertPoint(block);\n  value = x->getBuilder()->buildValue(this);\n}\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/llvm/llvisitor.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/cir.h\"\n#include \"codon/cir/llvm/llvm.h\"\n#include \"codon/cir/pyextension.h\"\n#include \"codon/dsl/plugins.h\"\n#include \"codon/util/common.h\"\n\n#include <string>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\nnamespace codon {\nnamespace ir {\n\nclass LLVMVisitor : public util::ConstVisitor {\nprivate:\n  struct CoroData {\n    /// Coroutine promise (where yielded values are stored)\n    llvm::Value *promise;\n    /// Coroutine handle\n    llvm::Value *handle;\n    /// Coroutine cleanup block\n    llvm::BasicBlock *cleanup;\n    /// Coroutine suspend block\n    llvm::BasicBlock *suspend;\n    /// Coroutine exit block\n    llvm::BasicBlock *exit;\n    /// True if coroutine represents 'async' function\n    bool async;\n\n    void reset() {\n      promise = handle = cleanup = suspend = exit = nullptr;\n      async = false;\n    }\n  };\n\n  struct NestableData {\n    int sequenceNumber;\n\n    NestableData() : sequenceNumber(-1) {}\n  };\n\n  struct LoopData : NestableData {\n    /// Block to branch to in case of \"break\"\n    llvm::BasicBlock *breakBlock;\n    /// Block to branch to in case of \"continue\"\n    llvm::BasicBlock *continueBlock;\n    /// Loop id\n    id_t loopId;\n\n    LoopData(llvm::BasicBlock *breakBlock, llvm::BasicBlock *continueBlock, id_t loopId)\n        : NestableData(), breakBlock(breakBlock), continueBlock(continueBlock),\n          loopId(loopId) {}\n\n    void reset() { breakBlock = continueBlock = nullptr; }\n  };\n\n  struct TryCatchData : NestableData {\n    /// Possible try-catch states when reaching finally block\n    enum State { NOT_THROWN = 0, THROWN, CAUGHT, RETURN, BREAK, CONTINUE, RETHROW };\n    /// Exception block\n    llvm::BasicBlock *exceptionBlock;\n    /// Exception route block\n    llvm::BasicBlock *exceptionRouteBlock;\n    /// Finally start block\n    llvm::BasicBlock *finallyBlock;\n    /// Block to support exceptions raised from 'except' and 'else' blocks\n    llvm::BasicBlock *finallyExceptionBlock;\n    /// Try-catch catch types\n    std::vector<types::Type *> catchTypes;\n    /// Try-catch handlers, corresponding to catch types\n    std::vector<llvm::BasicBlock *> handlers;\n    /// Exception state flag (see \"State\")\n    llvm::Value *excFlag;\n    /// Storage for caught exception\n    llvm::Value *catchStore;\n    /// How far to delegate up the finally chain\n    llvm::Value *delegateDepth;\n    /// Storage for postponed return\n    llvm::Value *retStore;\n    /// Loop being manipulated\n    llvm::Value *loopSequence;\n\n    TryCatchData()\n        : NestableData(), exceptionBlock(nullptr), exceptionRouteBlock(nullptr),\n          finallyBlock(nullptr), finallyExceptionBlock(nullptr), catchTypes(),\n          handlers(), excFlag(nullptr), catchStore(nullptr), delegateDepth(nullptr),\n          retStore(nullptr), loopSequence(nullptr) {}\n\n    void reset() {\n      exceptionBlock = exceptionRouteBlock = finallyBlock = finallyExceptionBlock =\n          nullptr;\n      catchTypes.clear();\n      handlers.clear();\n      excFlag = catchStore = delegateDepth = loopSequence = nullptr;\n    }\n  };\n\n  struct CatchData : NestableData {\n    /// Exception object\n    llvm::Value *exception;\n    /// Exception type ID\n    llvm::Value *typeId;\n  };\n\n  struct DebugInfo {\n    /// LLVM debug info builder\n    std::unique_ptr<llvm::DIBuilder> builder;\n    /// Current compilation unit\n    llvm::DICompileUnit *unit;\n    /// Whether we are compiling in debug mode\n    bool debug;\n    /// Whether we are compiling in JIT mode\n    bool jit;\n    /// Whether we are compiling a standalone object/executable\n    bool standalone;\n    /// Whether to capture writes to stdout/stderr\n    bool capture;\n    /// Program command-line flags\n    std::string flags;\n\n    DebugInfo()\n        : builder(), unit(nullptr), debug(false), jit(false), standalone(false),\n          capture(false), flags() {}\n\n    llvm::DIFile *getFile(const std::string &path);\n\n    void reset() {\n      builder = {};\n      unit = nullptr;\n    }\n  };\n\n  /// LLVM context used for compilation\n  std::unique_ptr<llvm::LLVMContext> context;\n  /// Module we are compiling\n  std::unique_ptr<llvm::Module> M;\n  /// LLVM IR builder used for constructing LLVM IR\n  std::unique_ptr<llvm::IRBuilder<>> B;\n  /// Current function we are compiling\n  llvm::Function *func;\n  /// Current basic block we are compiling\n  llvm::BasicBlock *block;\n  /// Last compiled value\n  llvm::Value *value;\n  /// LLVM values corresponding to IR variables\n  std::unordered_map<id_t, llvm::Value *> vars;\n  /// LLVM functions corresponding to IR functions\n  std::unordered_map<id_t, llvm::Function *> funcs;\n  /// Coroutine data, if current function is a coroutine\n  CoroData coro;\n  /// Loop data stack, containing break/continue blocks\n  std::vector<LoopData> loops;\n  /// Try-block data stack\n  std::vector<TryCatchData> trycatch;\n  /// Finally-block data stack\n  std::vector<TryCatchData> finally;\n  /// Catch-block data stack\n  std::vector<CatchData> catches;\n  /// Debug information\n  DebugInfo db;\n  /// Plugin manager\n  PluginManager *plugins;\n\n  llvm::DIType *\n  getDITypeHelper(types::Type *t,\n                  std::unordered_map<std::string, llvm::DICompositeType *> &cache);\n\n  /// GC allocation functions\n  llvm::FunctionCallee makeAllocFunc(bool atomic, bool uncollectable = false);\n  // GC reallocation function\n  llvm::FunctionCallee makeReallocFunc();\n  // GC free function\n  llvm::FunctionCallee makeFreeFunc();\n  /// Personality function for exception handling\n  llvm::FunctionCallee makePersonalityFunc();\n  /// Exception allocation function\n  llvm::FunctionCallee makeExcAllocFunc();\n  /// Exception throw function\n  llvm::FunctionCallee makeThrowFunc();\n  /// Program termination function\n  llvm::FunctionCallee makeTerminateFunc();\n\n  // Try-catch types and utilities\n  llvm::StructType *getTypeInfoType();\n  llvm::StructType *getPadType();\n  llvm::StructType *getExceptionType();\n  llvm::GlobalVariable *getTypeIdxVar(types::Type *catchType);\n  int getTypeIdx(types::Type *catchType = nullptr);\n\n  // General function helpers\n  llvm::Value *call(llvm::FunctionCallee callee, llvm::ArrayRef<llvm::Value *> args);\n  llvm::Function *makeLLVMFunction(const Func *);\n  void makeYield(llvm::Value *value = nullptr, bool finalYield = false);\n  std::string buildLLVMCodeString(const LLVMFunc *);\n  void callStage(const PipelineFlow::Stage *stage);\n  void codegenPipeline(const std::vector<const PipelineFlow::Stage *> &stages,\n                       unsigned where = 0);\n\n  // Loop and try-catch state\n  void enterLoop(LoopData data);\n  void exitLoop();\n  void enterTry(TryCatchData data);\n  void exitTry();\n  void enterFinally(TryCatchData data);\n  void exitFinally();\n  void enterCatch(CatchData data);\n  void exitCatch();\n  TryCatchData *getInnermostTryCatch();\n  TryCatchData *getInnermostTryCatchBeforeLoop();\n\n  // Global constructor setup\n  void setupGlobalCtor();\n\n  // Python extension setup\n  llvm::Function *createPyTryCatchWrapper(llvm::Function *func);\n\n  // LLVM passes\n  void runLLVMPipeline();\n\n  llvm::Value *getVar(const Var *var);\n  void insertVar(const Var *var, llvm::Value *x) { vars.emplace(var->getId(), x); }\n  llvm::Function *getFunc(const Func *func);\n  void insertFunc(const Func *func, llvm::Function *x) {\n    funcs.emplace(func->getId(), x);\n  }\n  llvm::Value *getDummyVoidValue() { return llvm::ConstantTokenNone::get(*context); }\n  llvm::DISubprogram *getDISubprogramForFunc(const Func *x);\n  void clearLLVMData();\n\npublic:\n  static std::string getGlobalCtorName();\n  static std::string getNameForFunction(const Func *x);\n  static std::string getNameForVar(const Var *x);\n\n  static std::string getDebugNameForVariable(const Var *x) {\n    std::string name = x->getName();\n    auto pos = name.find(\".\");\n    if (pos != 0 && pos != std::string::npos) {\n      return name.substr(0, pos);\n    } else {\n      return name;\n    }\n  }\n\n  static const SrcInfo *getDefaultSrcInfo() {\n    static SrcInfo defaultSrcInfo(\"<internal>\", 0, 0, 0);\n    return &defaultSrcInfo;\n  }\n\n  static const SrcInfo *getSrcInfo(const Node *x) {\n    if (auto *srcInfo = x->getAttribute<SrcInfoAttribute>()) {\n      return &srcInfo->info;\n    } else {\n      return getDefaultSrcInfo();\n    }\n  }\n\n  /// Constructs an LLVM visitor.\n  LLVMVisitor();\n\n  /// @return true if in debug mode, false otherwise\n  bool getDebug() const { return db.debug; }\n  /// Sets debug status.\n  /// @param d true if debug mode\n  void setDebug(bool d = true) { db.debug = d; }\n\n  /// @return true if in JIT mode, false otherwise\n  bool getJIT() const { return db.jit; }\n  /// Sets JIT status.\n  /// @param j true if JIT mode\n  void setJIT(bool j = true) { db.jit = j; }\n\n  /// @return true if in standalone mode, false otherwise\n  bool getStandalone() const { return db.standalone; }\n  /// Sets standalone status.\n  /// @param s true if standalone\n  void setStandalone(bool s = true) { db.standalone = s; }\n\n  /// @return true if capturing outputs, false otherwise\n  bool getCapture() const { return db.capture; }\n  /// Sets capture status.\n  /// @param c true to capture\n  void setCapture(bool c = true) { db.capture = c; }\n\n  /// @return program flags\n  std::string getFlags() const { return db.flags; }\n  /// Sets program flags.\n  /// @param f flags\n  void setFlags(const std::string &f) { db.flags = f; }\n\n  llvm::LLVMContext &getContext() { return *context; }\n  llvm::IRBuilder<> &getBuilder() { return *B; }\n  llvm::Module *getModule() { return M.get(); }\n  llvm::FunctionCallee getFunc() { return func; }\n  llvm::BasicBlock *getBlock() { return block; }\n  llvm::Value *getValue() { return value; }\n  std::unordered_map<id_t, llvm::Value *> &getVars() { return vars; }\n  std::unordered_map<id_t, llvm::Function *> &getFuncs() { return funcs; }\n  CoroData &getCoro() { return coro; }\n  std::vector<LoopData> &getLoops() { return loops; }\n  std::vector<TryCatchData> &getTryCatch() { return trycatch; }\n  DebugInfo &getDebugInfo() { return db; }\n\n  void setFunc(llvm::Function *f) { func = f; }\n  void setBlock(llvm::BasicBlock *b) { block = b; }\n  void setValue(llvm::Value *v) { value = v; }\n\n  /// Registers a new global variable or function with\n  /// this visitor.\n  /// @param var the global variable (or function) to register\n  void registerGlobal(const Var *var);\n\n  /// Returns a new LLVM module initialized for the host\n  /// architecture.\n  /// @param context LLVM context used for creating module\n  /// @param src source information for the new module\n  /// @return a new module\n  std::unique_ptr<llvm::Module> makeModule(llvm::LLVMContext &context,\n                                           const SrcInfo *src = nullptr);\n\n  /// Returns the current module/LLVM context and replaces them\n  /// with new, fresh ones. References to variables or functions\n  /// from the old module will be included as \"external\".\n  /// @param module the IR module\n  /// @param src source information for the new module\n  /// @return the current module/context, replaced internally\n  std::pair<std::unique_ptr<llvm::Module>, std::unique_ptr<llvm::LLVMContext>>\n  takeModule(Module *module, const SrcInfo *src = nullptr);\n\n  /// Sets current debug info based on a given node.\n  /// @param node the node whose debug info to use\n  void setDebugInfoForNode(const Node *node);\n\n  /// Compiles a given IR node, updating the internal\n  /// LLVM value and/or function as a result.\n  /// @param node the node to compile\n  void process(const Node *node);\n\n  /// Dumps the unoptimized module IR to a file.\n  /// @param filename name of file to write IR to\n  void dump(const std::string &filename = \"_dump.ll\");\n  /// Writes module as native object file.\n  /// @param filename the .o file to write to\n  /// @param pic true to write position-independent code\n  /// @param assembly true to write assembly instead of binary\n  void writeToObjectFile(const std::string &filename, bool pic = false,\n                         bool assembly = false);\n  /// Writes module as LLVM bitcode file.\n  /// @param filename the .bc file to write to\n  void writeToBitcodeFile(const std::string &filename);\n  /// Writes module as LLVM IR file.\n  /// @param filename the .ll file to write to\n  void writeToLLFile(const std::string &filename, bool optimize = true);\n  /// Writes module as native executable. Invokes an\n  /// external linker to generate the final executable.\n  /// @param filename the file to write to\n  /// @param argv0 compiler's argv[0] used to set rpath\n  /// @param library whether to make a shared library\n  /// @param libs library names to link\n  /// @param lflags extra flags to pass linker\n  void writeToExecutable(const std::string &filename, const std::string &argv0,\n                         bool library = false,\n                         const std::vector<std::string> &libs = {},\n                         const std::string &lflags = \"\");\n  /// Writes module as Python extension object.\n  /// @param pymod extension module\n  /// @param filename the file to write to\n  void writeToPythonExtension(const PyModule &pymod, const std::string &filename);\n  /// Runs optimization passes on module and writes the result\n  /// to the specified file. The output type is determined by\n  /// the file extension (.ll for LLVM IR, .bc for LLVM bitcode\n  /// .o or .obj for object file, other for executable).\n  /// @param filename name of the file to write to\n  /// @param argv0 compiler's argv[0] used to set rpath\n  /// @param libs library names to link to, if creating executable\n  /// @param lflags extra flags to pass linker, if creating executable\n  void compile(const std::string &filename, const std::string &argv0,\n               const std::vector<std::string> &libs = {},\n               const std::string &lflags = \"\");\n  /// Runs optimization passes on module and executes it.\n  /// @param args vector of arguments to program\n  /// @param libs vector of libraries to load\n  /// @param envp program environment\n  void run(const std::vector<std::string> &args = {},\n           const std::vector<std::string> &libs = {},\n           const char *const *envp = nullptr);\n\n  /// Gets LLVM type from IR type\n  /// @param t the IR type\n  /// @return corresponding LLVM type\n  llvm::Type *getLLVMType(types::Type *t);\n  /// Gets LLVM function type from IR function type\n  /// @param t the IR type (must be FuncType)\n  /// @return corresponding LLVM function type\n  llvm::FunctionType *getLLVMFuncType(types::Type *t);\n  /// Gets the LLVM debug info type from the IR type\n  /// @param t the IR type\n  /// @return corresponding LLVM DI type\n  llvm::DIType *getDIType(types::Type *t);\n  /// Gets loop data for a given loop id\n  /// @param loopId the IR id of the loop\n  /// @return the loop's datas\n  LoopData *getLoopData(id_t loopId);\n\n  /// Sets the plugin manager\n  /// @param p the plugin manager\n  void setPluginManager(PluginManager *p) { plugins = p; }\n  /// @return the plugin manager\n  PluginManager *getPluginManager() { return plugins; }\n\n  void visit(const Module *) override;\n  void visit(const BodiedFunc *) override;\n  void visit(const ExternalFunc *) override;\n  void visit(const InternalFunc *) override;\n  void visit(const LLVMFunc *) override;\n  void visit(const Var *) override;\n  void visit(const VarValue *) override;\n  void visit(const PointerValue *) override;\n\n  void visit(const IntConst *) override;\n  void visit(const FloatConst *) override;\n  void visit(const BoolConst *) override;\n  void visit(const StringConst *) override;\n  void visit(const dsl::CustomConst *) override;\n\n  void visit(const SeriesFlow *) override;\n  void visit(const IfFlow *) override;\n  void visit(const WhileFlow *) override;\n  void visit(const ForFlow *) override;\n  void visit(const ImperativeForFlow *) override;\n  void visit(const TryCatchFlow *) override;\n  void visit(const PipelineFlow *) override;\n  void visit(const dsl::CustomFlow *) override;\n\n  void visit(const AssignInstr *) override;\n  void visit(const ExtractInstr *) override;\n  void visit(const InsertInstr *) override;\n  void visit(const CallInstr *) override;\n  void visit(const StackAllocInstr *) override;\n  void visit(const TypePropertyInstr *) override;\n  void visit(const YieldInInstr *) override;\n  void visit(const TernaryInstr *) override;\n  void visit(const BreakInstr *) override;\n  void visit(const ContinueInstr *) override;\n  void visit(const ReturnInstr *) override;\n  void visit(const YieldInstr *) override;\n  void visit(const AwaitInstr *) override;\n  void visit(const ThrowInstr *) override;\n  void visit(const FlowInstr *) override;\n  void visit(const dsl::CustomInstr *) override;\n};\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/llvm/llvm.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"llvm/ADT/BitVector.h\"\n#include \"llvm/ADT/FunctionExtras.h\"\n#include \"llvm/ADT/SmallSet.h\"\n#include \"llvm/ADT/SmallVector.h\"\n#include \"llvm/ADT/StringRef.h\"\n#include \"llvm/Analysis/CallGraph.h\"\n#include \"llvm/Analysis/CallGraphSCCPass.h\"\n#include \"llvm/Analysis/CaptureTracking.h\"\n#include \"llvm/Analysis/CycleAnalysis.h\"\n#include \"llvm/Analysis/DomTreeUpdater.h\"\n#include \"llvm/Analysis/LoopPass.h\"\n#include \"llvm/Analysis/MemorySSAUpdater.h\"\n#include \"llvm/Analysis/RegionPass.h\"\n#include \"llvm/Analysis/TargetLibraryInfo.h\"\n#include \"llvm/Analysis/TargetTransformInfo.h\"\n#include \"llvm/AsmParser/Parser.h\"\n#include \"llvm/Bitcode/BitcodeWriter.h\"\n#include \"llvm/CodeGen/CommandFlags.h\"\n#include \"llvm/CodeGen/MachineModuleInfo.h\"\n#include \"llvm/CodeGen/TargetPassConfig.h\"\n#include \"llvm/DebugInfo/Symbolize/Symbolize.h\"\n#include \"llvm/ExecutionEngine/ExecutionEngine.h\"\n#include \"llvm/ExecutionEngine/GenericValue.h\"\n#include \"llvm/ExecutionEngine/JITEventListener.h\"\n#include \"llvm/ExecutionEngine/JITLink/JITLink.h\"\n#include \"llvm/ExecutionEngine/JITLink/JITLinkDylib.h\"\n#include \"llvm/ExecutionEngine/JITLink/JITLinkMemoryManager.h\"\n#include \"llvm/ExecutionEngine/JITSymbol.h\"\n#include \"llvm/ExecutionEngine/MCJIT.h\"\n#include \"llvm/ExecutionEngine/Orc/CompileOnDemandLayer.h\"\n#include \"llvm/ExecutionEngine/Orc/CompileUtils.h\"\n#include \"llvm/ExecutionEngine/Orc/Core.h\"\n#include \"llvm/ExecutionEngine/Orc/DebugObjectManagerPlugin.h\"\n#include \"llvm/ExecutionEngine/Orc/DebugUtils.h\"\n#include \"llvm/ExecutionEngine/Orc/ELFNixPlatform.h\"\n#include \"llvm/ExecutionEngine/Orc/EPCDebugObjectRegistrar.h\"\n#include \"llvm/ExecutionEngine/Orc/EPCDynamicLibrarySearchGenerator.h\"\n#include \"llvm/ExecutionEngine/Orc/EPCEHFrameRegistrar.h\"\n#include \"llvm/ExecutionEngine/Orc/EPCGenericRTDyldMemoryManager.h\"\n#include \"llvm/ExecutionEngine/Orc/EPCIndirectionUtils.h\"\n#include \"llvm/ExecutionEngine/Orc/ExecutionUtils.h\"\n#include \"llvm/ExecutionEngine/Orc/ExecutorProcessControl.h\"\n#include \"llvm/ExecutionEngine/Orc/IRCompileLayer.h\"\n#include \"llvm/ExecutionEngine/Orc/IRTransformLayer.h\"\n#include \"llvm/ExecutionEngine/Orc/JITTargetMachineBuilder.h\"\n#include \"llvm/ExecutionEngine/Orc/LLJIT.h\"\n#include \"llvm/ExecutionEngine/Orc/MachOPlatform.h\"\n#include \"llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h\"\n#include \"llvm/ExecutionEngine/Orc/Shared/AllocationActions.h\"\n#include \"llvm/ExecutionEngine/Orc/Shared/ExecutorAddress.h\"\n#include \"llvm/ExecutionEngine/Orc/SimpleRemoteEPC.h\"\n#include \"llvm/ExecutionEngine/Orc/SymbolStringPool.h\"\n#include \"llvm/ExecutionEngine/Orc/TargetProcess/JITLoaderGDB.h\"\n#include \"llvm/ExecutionEngine/Orc/TargetProcess/RegisterEHFrames.h\"\n#include \"llvm/ExecutionEngine/Orc/TargetProcess/TargetExecutionUtils.h\"\n#include \"llvm/ExecutionEngine/RuntimeDyld.h\"\n#include \"llvm/ExecutionEngine/SectionMemoryManager.h\"\n#include \"llvm/IR/Argument.h\"\n#include \"llvm/IR/BasicBlock.h\"\n#include \"llvm/IR/Constants.h\"\n#include \"llvm/IR/DIBuilder.h\"\n#include \"llvm/IR/DataLayout.h\"\n#include \"llvm/IR/DebugInfo.h\"\n#include \"llvm/IR/DerivedTypes.h\"\n#include \"llvm/IR/Function.h\"\n#include \"llvm/IR/IRBuilder.h\"\n#include \"llvm/IR/InstIterator.h\"\n#include \"llvm/IR/InstrTypes.h\"\n#include \"llvm/IR/Instructions.h\"\n#include \"llvm/IR/LLVMContext.h\"\n#include \"llvm/IR/LLVMRemarkStreamer.h\"\n#include \"llvm/IR/LegacyPassManager.h\"\n#include \"llvm/IR/LegacyPassNameParser.h\"\n#include \"llvm/IR/Module.h\"\n#include \"llvm/IR/Type.h\"\n#include \"llvm/IR/Verifier.h\"\n#include \"llvm/IRReader/IRReader.h\"\n#include \"llvm/InitializePasses.h\"\n#include \"llvm/LinkAllIR.h\"\n#include \"llvm/LinkAllPasses.h\"\n#include \"llvm/Linker/Linker.h\"\n#include \"llvm/MC/TargetRegistry.h\"\n#include \"llvm/Passes/PassBuilder.h\"\n#include \"llvm/Support/Allocator.h\"\n#include \"llvm/Support/Casting.h\"\n#include \"llvm/Support/CommandLine.h\"\n#include \"llvm/Support/Debug.h\"\n#include \"llvm/Support/DynamicLibrary.h\"\n#include \"llvm/Support/Error.h\"\n#include \"llvm/Support/FileSystem.h\"\n#include \"llvm/Support/FormatVariadic.h\"\n#include \"llvm/Support/InitLLVM.h\"\n#include \"llvm/Support/Memory.h\"\n#include \"llvm/Support/Process.h\"\n#include \"llvm/Support/RecyclingAllocator.h\"\n#include \"llvm/Support/SourceMgr.h\"\n#include \"llvm/Support/SystemUtils.h\"\n#include \"llvm/Support/TargetSelect.h\"\n#include \"llvm/Support/ToolOutputFile.h\"\n#include \"llvm/Support/YAMLTraits.h\"\n#include \"llvm/Support/raw_ostream.h\"\n#include \"llvm/Target/TargetLoweringObjectFile.h\"\n#include \"llvm/Target/TargetMachine.h\"\n#include \"llvm/TargetParser/Host.h\"\n#include \"llvm/Transforms/IPO.h\"\n#include \"llvm/Transforms/IPO/AlwaysInliner.h\"\n#include \"llvm/Transforms/IPO/GlobalDCE.h\"\n#include \"llvm/Transforms/IPO/Internalize.h\"\n#include \"llvm/Transforms/IPO/StripDeadPrototypes.h\"\n#include \"llvm/Transforms/IPO/StripSymbols.h\"\n#include \"llvm/Transforms/IPO/WholeProgramDevirt.h\"\n#include \"llvm/Transforms/Utils/BasicBlockUtils.h\"\n#include \"llvm/Transforms/Utils/Cloning.h\"\n#include \"llvm/Transforms/Utils/Debugify.h\"\n"
  },
  {
    "path": "codon/cir/llvm/native/native.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"native.h\"\n\n#include \"codon/cir/llvm/llvm.h\"\n#include \"codon/cir/llvm/native/targets/aarch64.h\"\n#include \"codon/cir/llvm/native/targets/arm.h\"\n#include \"codon/cir/llvm/native/targets/x86.h\"\n\n// Targets adapted from\n// https://github.com/llvm/llvm-project/tree/main/clang/lib/Driver/ToolChains/Arch\n\nnamespace codon {\nnamespace ir {\nnamespace {\nstd::unique_ptr<Target> getNativeTarget(const llvm::Triple &triple) {\n  std::unique_ptr<Target> result = std::unique_ptr<Target>();\n  switch (triple.getArch()) {\n  default:\n    break;\n  case llvm::Triple::mips:\n  case llvm::Triple::mipsel:\n  case llvm::Triple::mips64:\n  case llvm::Triple::mips64el:\n    // nothing\n    break;\n\n  case llvm::Triple::arm:\n  case llvm::Triple::armeb:\n  case llvm::Triple::thumb:\n  case llvm::Triple::thumbeb:\n    result = std::make_unique<ARM>();\n    break;\n\n  case llvm::Triple::ppc:\n  case llvm::Triple::ppcle:\n  case llvm::Triple::ppc64:\n  case llvm::Triple::ppc64le:\n    // nothing\n    break;\n  case llvm::Triple::riscv32:\n  case llvm::Triple::riscv64:\n    // nothing\n    break;\n  case llvm::Triple::systemz:\n    // nothing\n    break;\n  case llvm::Triple::aarch64:\n  case llvm::Triple::aarch64_32:\n  case llvm::Triple::aarch64_be:\n    result = std::make_unique<Aarch64>();\n    break;\n  case llvm::Triple::x86:\n  case llvm::Triple::x86_64:\n    result = std::make_unique<X86>();\n    break;\n  case llvm::Triple::hexagon:\n    // nothing\n    break;\n  case llvm::Triple::wasm32:\n  case llvm::Triple::wasm64:\n    // nothing\n    break;\n  case llvm::Triple::sparc:\n  case llvm::Triple::sparcel:\n  case llvm::Triple::sparcv9:\n    // nothing\n    break;\n  case llvm::Triple::r600:\n  case llvm::Triple::amdgcn:\n    // nothing\n    break;\n  case llvm::Triple::msp430:\n    // nothing\n    break;\n  case llvm::Triple::ve:\n    // nothing\n    break;\n  }\n  return result;\n}\n\nclass ArchNativePass : public llvm::PassInfoMixin<ArchNativePass> {\nprivate:\n  std::string cpu;\n  std::string features;\n\npublic:\n  explicit ArchNativePass(const std::string &cpu = \"\", const std::string &features = \"\")\n      : cpu(cpu), features(features) {}\n\n  llvm::PreservedAnalyses run(llvm::Function &F, llvm::FunctionAnalysisManager &) {\n    if (!cpu.empty())\n      F.addFnAttr(\"target-cpu\", cpu);\n    if (!features.empty())\n      F.addFnAttr(\"target-features\", features);\n    F.addFnAttr(\"frame-pointer\", \"none\");\n    return llvm::PreservedAnalyses::all();\n  }\n};\n} // namespace\n\nvoid addNativeLLVMPasses(llvm::PassBuilder *pb) {\n  llvm::Triple triple = llvm::EngineBuilder().selectTarget()->getTargetTriple();\n  auto target = getNativeTarget(triple);\n  if (!target)\n    return;\n  std::string cpu = target->getCPU(triple);\n  std::string features = target->getFeatures(triple);\n\n  pb->registerPipelineEarlySimplificationEPCallback(\n      [cpu, features](llvm::ModulePassManager &pm, llvm::OptimizationLevel opt,\n                      llvm::ThinOrFullLTOPhase lto) {\n        pm.addPass(\n            llvm::createModuleToFunctionPassAdaptor(ArchNativePass(cpu, features)));\n      });\n}\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/llvm/native/native.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/llvm/llvm.h\"\n\nnamespace codon {\nnamespace ir {\n\nvoid addNativeLLVMPasses(llvm::PassBuilder *pb);\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/llvm/native/targets/aarch64.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"aarch64.h\"\n\n#include \"llvm/TargetParser/AArch64TargetParser.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace {\ntemplate <typename T> std::string join(const T &v, const std::string &delim = \",\") {\n  std::ostringstream s;\n  for (const auto &i : v) {\n    if (&i != &v[0])\n      s << delim;\n    s << std::string(i);\n  }\n  return s.str();\n}\n} // namespace\n\nstd::string Aarch64::getCPU(const llvm::Triple &triple) const {\n  return llvm::sys::getHostCPUName().str();\n}\n\nstd::string Aarch64::getFeatures(const llvm::Triple &triple) const {\n  llvm::AArch64::ExtensionSet extensions;\n  std::vector<std::string> features;\n\n  std::string cpu(llvm::sys::getHostCPUName());\n  const std::optional<llvm::AArch64::CpuInfo> cpuInfo = llvm::AArch64::parseCpu(cpu);\n  if (!cpuInfo)\n    return \"\";\n\n  auto *archInfo = llvm::AArch64::getArchForCpu(cpu);\n  extensions.addCPUDefaults(*cpuInfo);\n  extensions.addArchDefaults(*archInfo);\n  extensions.toLLVMFeatureList(features);\n\n  for (auto &f : llvm::sys::getHostCPUFeatures()) {\n    features.push_back((f.second ? \"+\" : \"-\") + f.first().str());\n  }\n\n  if (cpu == \"cyclone\" || llvm::StringRef(cpu).starts_with(\"apple\")) {\n    features.push_back(\"+zcm\");\n    features.push_back(\"+zcz\");\n  }\n\n  if (triple.isAndroid() || triple.isOHOSFamily()) {\n    // Enabled A53 errata (835769) workaround by default on android\n    features.push_back(\"+fix-cortex-a53-835769\");\n  } else if (triple.isOSFuchsia()) {\n    if (cpu.empty() || cpu == \"generic\" || cpu == \"cortex-a53\")\n      features.push_back(\"+fix-cortex-a53-835769\");\n  }\n\n  if (triple.isOSOpenBSD())\n    features.push_back(\"+strict-align\");\n\n  return join(features);\n}\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/llvm/native/targets/aarch64.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/llvm/native/targets/target.h\"\n\nnamespace codon {\nnamespace ir {\n\nclass Aarch64 : public Target {\npublic:\n  std::string getCPU(const llvm::Triple &triple) const override;\n  std::string getFeatures(const llvm::Triple &triple) const override;\n};\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/llvm/native/targets/arm.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"arm.h\"\n\n#include \"llvm/TargetParser/ARMTargetParser.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace {\ntemplate <typename T> std::string join(const T &v, const std::string &delim = \",\") {\n  std::ostringstream s;\n  for (const auto &i : v) {\n    if (&i != &v[0])\n      s << delim;\n    s << std::string(i);\n  }\n  return s.str();\n}\n\nint getARMSubArchVersionNumber(const llvm::Triple &triple) {\n  auto arch = triple.getArchName();\n  return llvm::ARM::parseArchVersion(arch);\n}\n\nbool isARMMProfile(const llvm::Triple &triple) {\n  auto arch = triple.getArchName();\n  return llvm::ARM::parseArchProfile(arch) == llvm::ARM::ProfileKind::M;\n}\n\nbool isARMBigEndian(const llvm::Triple &triple) {\n  return triple.getArch() == llvm::Triple::armeb ||\n         triple.getArch() == llvm::Triple::thumbeb;\n}\n\nbool isARMAProfile(const llvm::Triple &triple) {\n  auto arch = triple.getArchName();\n  return llvm::ARM::parseArchProfile(arch) == llvm::ARM::ProfileKind::A;\n}\n\nbool isARMEABIBareMetal(const llvm::Triple &triple) {\n  auto arch = triple.getArch();\n  if (arch != llvm::Triple::arm && arch != llvm::Triple::thumb &&\n      arch != llvm::Triple::armeb && arch != llvm::Triple::thumbeb)\n    return false;\n\n  if (triple.getVendor() != llvm::Triple::UnknownVendor)\n    return false;\n\n  if (triple.getOS() != llvm::Triple::UnknownOS)\n    return false;\n\n  if (triple.getEnvironment() != llvm::Triple::EABI &&\n      triple.getEnvironment() != llvm::Triple::EABIHF)\n    return false;\n\n  return true;\n}\n\nbool useAAPCSForMachO(const llvm::Triple &triple) {\n  return triple.getEnvironment() == llvm::Triple::EABI ||\n         triple.getEnvironment() == llvm::Triple::EABIHF ||\n         triple.getOS() == llvm::Triple::UnknownOS || isARMMProfile(triple);\n}\n\nenum FloatABI { Invalid, Hard, Soft, SoftFP };\n\nFloatABI getDefaultFloatABI(const llvm::Triple &triple) {\n  auto sub = getARMSubArchVersionNumber(triple);\n  switch (triple.getOS()) {\n  case llvm::Triple::Darwin:\n  case llvm::Triple::MacOSX:\n  case llvm::Triple::IOS:\n  case llvm::Triple::TvOS:\n  case llvm::Triple::DriverKit:\n  case llvm::Triple::XROS:\n    // Darwin defaults to \"softfp\" for v6 and v7.\n    if (triple.isWatchABI())\n      return FloatABI::Hard;\n    else\n      return (sub == 6 || sub == 7) ? FloatABI::SoftFP : FloatABI::Soft;\n\n  case llvm::Triple::WatchOS:\n    return FloatABI::Hard;\n\n  // FIXME: this is invalid for WindowsCE\n  case llvm::Triple::Win32:\n    // It is incorrect to select hard float ABI on MachO platforms if the ABI is\n    // \"apcs-gnu\".\n    if (triple.isOSBinFormatMachO() && !useAAPCSForMachO(triple))\n      return FloatABI::Soft;\n    return FloatABI::Hard;\n\n  case llvm::Triple::NetBSD:\n    switch (triple.getEnvironment()) {\n    case llvm::Triple::EABIHF:\n    case llvm::Triple::GNUEABIHF:\n      return FloatABI::Hard;\n    default:\n      return FloatABI::Soft;\n    }\n    break;\n\n  case llvm::Triple::FreeBSD:\n    switch (triple.getEnvironment()) {\n    case llvm::Triple::GNUEABIHF:\n      return FloatABI::Hard;\n    default:\n      // FreeBSD defaults to soft float\n      return FloatABI::Soft;\n    }\n    break;\n\n  case llvm::Triple::Haiku:\n  case llvm::Triple::OpenBSD:\n    return FloatABI::SoftFP;\n\n  default:\n    if (triple.isOHOSFamily())\n      return FloatABI::Soft;\n    switch (triple.getEnvironment()) {\n    case llvm::Triple::GNUEABIHF:\n    case llvm::Triple::GNUEABIHFT64:\n    case llvm::Triple::MuslEABIHF:\n    case llvm::Triple::EABIHF:\n      return FloatABI::Hard;\n    case llvm::Triple::Android:\n    case llvm::Triple::GNUEABI:\n    case llvm::Triple::GNUEABIT64:\n    case llvm::Triple::MuslEABI:\n    case llvm::Triple::EABI:\n      // EABI is always AAPCS, and if it was not marked 'hard', it's softfp\n      return FloatABI::SoftFP;\n    default:\n      return FloatABI::Invalid;\n    }\n  }\n  return FloatABI::Invalid;\n}\n\nFloatABI getARMFloatABI(const llvm::Triple &triple) {\n  FloatABI abi = getDefaultFloatABI(triple);\n  if (abi == FloatABI::Invalid) {\n    // Assume \"soft\", but warn the user we are guessing.\n    if (triple.isOSBinFormatMachO() &&\n        triple.getSubArch() == llvm::Triple::ARMSubArch_v7em)\n      abi = FloatABI::Hard;\n    else\n      abi = FloatABI::Soft;\n  }\n  return abi;\n}\n\nllvm::ARM::ArchKind getLLVMArchKindForARM(llvm::StringRef cpu, llvm::StringRef arch,\n                                          const llvm::Triple &triple) {\n  return (arch == \"armv7k\" || arch == \"thumbv7k\") ? llvm::ARM::ArchKind::ARMV7K\n                                                  : llvm::ARM::parseCPUArch(cpu);\n}\n\nllvm::StringRef getLLVMArchSuffixForARM(llvm::StringRef cpu, llvm::StringRef arch,\n                                        const llvm::Triple &triple) {\n  llvm::ARM::ArchKind archKind = getLLVMArchKindForARM(cpu, arch, triple);\n  if (archKind == llvm::ARM::ArchKind::INVALID)\n    return \"\";\n  return llvm::ARM::getSubArch(archKind);\n}\n\nbool hasIntegerMVE(const std::vector<llvm::StringRef> &F) {\n  auto MVE = llvm::find(llvm::reverse(F), \"+mve\");\n  auto NoMVE = llvm::find(llvm::reverse(F), \"-mve\");\n  return MVE != F.rend() && (NoMVE == F.rend() || std::distance(MVE, NoMVE) > 0);\n}\n} // namespace\n\nstd::string ARM::getCPU(const llvm::Triple &triple) const {\n  return llvm::sys::getHostCPUName().str();\n}\n\nstd::string ARM::getFeatures(const llvm::Triple &triple) const {\n  std::vector<llvm::StringRef> features;\n\n  auto abi = getARMFloatABI(triple);\n  // uint64_t HWDivID = llvm::ARM::parseHWDiv(HWDiv);\n\n  // Use software floating point operations?\n  if (abi == FloatABI::Soft)\n    features.push_back(\"+soft-float\");\n\n  // Use software floating point argument passing?\n  if (abi != FloatABI::Hard)\n    features.push_back(\"+soft-float-abi\");\n\n  std::vector<std::string> tmp; // make sure we don't delete string data\n  for (auto &f : llvm::sys::getHostCPUFeatures()) {\n    tmp.push_back((f.second ? \"+\" : \"-\") + f.first().str());\n    features.push_back(tmp.back());\n  }\n\n  llvm::ARM::FPUKind fpu = llvm::ARM::FK_INVALID;\n  auto arch = triple.getArchName();\n\n  // Honor -mfpu=. ClangAs gives preference to -Wa,-mfpu=.\n  if (triple.isAndroid() && getARMSubArchVersionNumber(triple) == 7) {\n    const char *androidFPU = \"neon\";\n    fpu = llvm::ARM::parseFPU(androidFPU);\n  } else {\n    std::string cpu = getCPU(triple);\n    llvm::ARM::ArchKind archkind = getLLVMArchKindForARM(cpu, arch, triple);\n    fpu = llvm::ARM::getDefaultFPU(cpu, archkind);\n  }\n  (void)llvm::ARM::getFPUFeatures(fpu, features);\n\n  // Handle (arch-dependent) fp16fml/fullfp16 relationship.\n  // Must happen before any features are disabled due to soft-float.\n  // FIXME: this fp16fml option handling will be reimplemented after the\n  // TargetParser rewrite.\n  const auto ItRNoFullFP16 = std::find(features.rbegin(), features.rend(), \"-fullfp16\");\n  const auto ItRFP16FML = std::find(features.rbegin(), features.rend(), \"+fp16fml\");\n  if (triple.getSubArch() == llvm::Triple::SubArchType::ARMSubArch_v8_4a) {\n    const auto ItRFullFP16 = std::find(features.rbegin(), features.rend(), \"+fullfp16\");\n    if (ItRFullFP16 < ItRNoFullFP16 && ItRFullFP16 < ItRFP16FML) {\n      // Only entangled feature that can be to the right of this +fullfp16 is -fp16fml.\n      // Only append the +fp16fml if there is no -fp16fml after the +fullfp16.\n      if (std::find(features.rbegin(), ItRFullFP16, \"-fp16fml\") == ItRFullFP16)\n        features.push_back(\"+fp16fml\");\n    } else\n      goto fp16_fml_fallthrough;\n  } else {\n  fp16_fml_fallthrough:\n    // In both of these cases, putting the 'other' feature on the end of the vector will\n    // result in the same effect as placing it immediately after the current feature.\n    if (ItRNoFullFP16 < ItRFP16FML)\n      features.push_back(\"-fp16fml\");\n    else if (ItRNoFullFP16 > ItRFP16FML)\n      features.push_back(\"+fullfp16\");\n  }\n\n  // Setting -msoft-float/-mfloat-abi=soft, -mfpu=none, or adding +nofp to\n  // -march/-mcpu effectively disables the FPU (GCC ignores the -mfpu options in\n  // this case). Note that the ABI can also be set implicitly by the target\n  // selected.\n  bool HasFPRegs = true;\n  if (abi == FloatABI::Soft) {\n    llvm::ARM::getFPUFeatures(llvm::ARM::FK_NONE, features);\n\n    // Disable all features relating to hardware FP, not already disabled by the\n    // above call.\n    features.insert(features.end(),\n                    {\"-dotprod\", \"-fp16fml\", \"-bf16\", \"-mve\", \"-mve.fp\"});\n    HasFPRegs = false;\n    fpu = llvm::ARM::FK_NONE;\n  } else if (fpu == llvm::ARM::FK_NONE) {\n    // -mfpu=none, -march=armvX+nofp or -mcpu=X+nofp is *very* similar to\n    // -mfloat-abi=soft, only that it should not disable MVE-I. They disable the\n    // FPU, but not the FPU registers, thus MVE-I, which depends only on the\n    // latter, is still supported.\n    features.insert(features.end(), {\"-dotprod\", \"-fp16fml\", \"-bf16\", \"-mve.fp\"});\n    HasFPRegs = hasIntegerMVE(features);\n  }\n  if (!HasFPRegs)\n    features.emplace_back(\"-fpregs\");\n\n  // Invalid value of the __ARM_FEATURE_MVE macro when an explicit -mfpu= option\n  // disables MVE-FP -mfpu=fpv5-d16 or -mfpu=fpv5-sp-d16 disables the scalar\n  // half-precision floating-point operations feature. Therefore, because the\n  // M-profile Vector Extension (MVE) floating-point feature requires the scalar\n  // half-precision floating-point operations, this option also disables the MVE\n  // floating-point feature: -mve.fp\n  if (fpu == llvm::ARM::FK_FPV5_D16 || fpu == llvm::ARM::FK_FPV5_SP_D16)\n    features.push_back(\"-mve.fp\");\n\n  // For Arch >= ARMv8.0 && A or R profile:  crypto = sha2 + aes\n  // Rather than replace within the feature vector, determine whether each\n  // algorithm is enabled and append this to the end of the vector.\n  // The algorithms can be controlled by their specific feature or the crypto\n  // feature, so their status can be determined by the last occurance of\n  // either in the vector. This allows one to supercede the other.\n  // e.g. +crypto+noaes in -march/-mcpu should enable sha2, but not aes\n  // FIXME: this needs reimplementation after the TargetParser rewrite\n  bool HasSHA2 = false;\n  bool HasAES = false;\n  const auto ItCrypto =\n      llvm::find_if(llvm::reverse(features),\n                    [](const llvm::StringRef F) { return F.contains(\"crypto\"); });\n  const auto ItSHA2 =\n      llvm::find_if(llvm::reverse(features), [](const llvm::StringRef F) {\n        return F.contains(\"crypto\") || F.contains(\"sha2\");\n      });\n  const auto ItAES =\n      llvm::find_if(llvm::reverse(features), [](const llvm::StringRef F) {\n        return F.contains(\"crypto\") || F.contains(\"aes\");\n      });\n  const bool FoundSHA2 = ItSHA2 != features.rend();\n  const bool FoundAES = ItAES != features.rend();\n  if (FoundSHA2)\n    HasSHA2 = ItSHA2->take_front() == \"+\";\n  if (FoundAES)\n    HasAES = ItAES->take_front() == \"+\";\n  if (ItCrypto != features.rend()) {\n    if (HasSHA2 && HasAES)\n      features.push_back(\"+crypto\");\n    else\n      features.push_back(\"-crypto\");\n    if (HasSHA2)\n      features.push_back(\"+sha2\");\n    else\n      features.push_back(\"-sha2\");\n    if (HasAES)\n      features.push_back(\"+aes\");\n    else\n      features.push_back(\"-aes\");\n  }\n\n  if (HasSHA2 || HasAES) {\n    auto ArchSuffix = getLLVMArchSuffixForARM(getCPU(triple), arch, triple);\n    llvm::ARM::ProfileKind ArchProfile = llvm::ARM::parseArchProfile(ArchSuffix);\n    if (!((llvm::ARM::parseArchVersion(ArchSuffix) >= 8) &&\n          (ArchProfile == llvm::ARM::ProfileKind::A ||\n           ArchProfile == llvm::ARM::ProfileKind::R))) {\n      features.push_back(\"-sha2\");\n      features.push_back(\"-aes\");\n    }\n  }\n\n  // Assume pre-ARMv6 doesn't support unaligned accesses.\n  //\n  // ARMv6 may or may not support unaligned accesses depending on the\n  // SCTLR.U bit, which is architecture-specific. We assume ARMv6\n  // Darwin and NetBSD targets support unaligned accesses, and others don't.\n  //\n  // ARMv7 always has SCTLR.U set to 1, but it has a new SCTLR.A bit which\n  // raises an alignment fault on unaligned accesses. Assume ARMv7+ supports\n  // unaligned accesses, except ARMv6-M, and ARMv8-M without the Main\n  // Extension. This aligns with the default behavior of ARM's downstream\n  // versions of GCC and Clang.\n  //\n  // Users can change the default behavior via -m[no-]unaliged-access.\n  int versionNum = getARMSubArchVersionNumber(triple);\n  if (triple.isOSDarwin() || triple.isOSNetBSD()) {\n    if (versionNum < 6 ||\n        triple.getSubArch() == llvm::Triple::SubArchType::ARMSubArch_v6m)\n      features.push_back(\"+strict-align\");\n  } else if (triple.getVendor() == llvm::Triple::Apple && triple.isOSBinFormatMachO()) {\n    // Firmwares on Apple platforms are strict-align by default.\n    features.push_back(\"+strict-align\");\n  } else if (versionNum < 7 ||\n             triple.getSubArch() == llvm::Triple::SubArchType::ARMSubArch_v6m ||\n             triple.getSubArch() ==\n                 llvm::Triple::SubArchType::ARMSubArch_v8m_baseline) {\n    features.push_back(\"+strict-align\");\n  }\n\n  return join(features);\n}\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/llvm/native/targets/arm.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/llvm/native/targets/target.h\"\n\nnamespace codon {\nnamespace ir {\n\nclass ARM : public Target {\npublic:\n  std::string getCPU(const llvm::Triple &triple) const override;\n  std::string getFeatures(const llvm::Triple &triple) const override;\n};\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/llvm/native/targets/target.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <sstream>\n#include <string>\n\n#include \"codon/cir/llvm/llvm.h\"\n\nnamespace codon {\nnamespace ir {\n\nclass Target {\npublic:\n  virtual ~Target() {}\n  virtual std::string getCPU(const llvm::Triple &triple) const = 0;\n  virtual std::string getFeatures(const llvm::Triple &triple) const = 0;\n};\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/llvm/native/targets/x86.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"x86.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace {\ntemplate <typename T> std::string join(const T &v, const std::string &delim = \",\") {\n  std::ostringstream s;\n  for (const auto &i : v) {\n    if (&i != &v[0])\n      s << delim;\n    s << std::string(i);\n  }\n  return s.str();\n}\n} // namespace\n\nstd::string X86::getCPU(const llvm::Triple &triple) const {\n  auto CPU = llvm::sys::getHostCPUName();\n  if (!CPU.empty() && CPU != \"generic\")\n    return std::string(CPU);\n\n  // Select the default CPU if none was given (or detection failed).\n\n  if (!triple.isX86())\n    return \"\"; // This routine is only handling x86 targets.\n\n  bool is64Bit = triple.getArch() == llvm::Triple::x86_64;\n\n  // FIXME: Need target hooks.\n  if (triple.isOSDarwin()) {\n    if (triple.getArchName() == \"x86_64h\")\n      return \"core-avx2\";\n    // macosx10.12 drops support for all pre-Penryn Macs.\n    // Simulators can still run on 10.11 though, like Xcode.\n    if (triple.isMacOSX() && !triple.isOSVersionLT(10, 12))\n      return \"penryn\";\n\n    if (triple.isDriverKit())\n      return \"nehalem\";\n\n    // The oldest x86_64 Macs have core2/Merom; the oldest x86 Macs have Yonah.\n    return is64Bit ? \"core2\" : \"yonah\";\n  }\n\n  // Set up default CPU name for PS4/PS5 compilers.\n  if (triple.isPS4())\n    return \"btver2\";\n  if (triple.isPS5())\n    return \"znver2\";\n\n  // On Android use targets compatible with gcc\n  if (triple.isAndroid())\n    return is64Bit ? \"x86-64\" : \"i686\";\n\n  // Everything else goes to x86-64 in 64-bit mode.\n  if (is64Bit)\n    return \"x86-64\";\n\n  switch (triple.getOS()) {\n  case llvm::Triple::NetBSD:\n    return \"i486\";\n  case llvm::Triple::Haiku:\n  case llvm::Triple::OpenBSD:\n    return \"i586\";\n  case llvm::Triple::FreeBSD:\n    return \"i686\";\n  default:\n    // Fallback to p4.\n    return \"pentium4\";\n  }\n}\n\nstd::string X86::getFeatures(const llvm::Triple &triple) const {\n  std::vector<std::string> features;\n  for (auto &f : llvm::sys::getHostCPUFeatures()) {\n    features.push_back((f.second ? \"+\" : \"-\") + f.first().str());\n  }\n\n  if (triple.getArchName() == \"x86_64h\") {\n    // x86_64h implies quite a few of the more modern subtarget features\n    // for Haswell class CPUs, but not all of them. Opt-out of a few.\n    features.push_back(\"-rdrnd\");\n    features.push_back(\"-aes\");\n    features.push_back(\"-pclmul\");\n    features.push_back(\"-rtm\");\n    features.push_back(\"-fsgsbase\");\n  }\n\n  const llvm::Triple::ArchType ArchType = triple.getArch();\n  // Add features to be compatible with gcc for Android.\n  if (triple.isAndroid()) {\n    if (ArchType == llvm::Triple::x86_64) {\n      features.push_back(\"+sse4.2\");\n      features.push_back(\"+popcnt\");\n      features.push_back(\"+cx16\");\n    } else\n      features.push_back(\"+ssse3\");\n  }\n  return join(features);\n}\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/llvm/native/targets/x86.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/llvm/native/targets/target.h\"\n\nnamespace codon {\nnamespace ir {\n\nclass X86 : public Target {\npublic:\n  std::string getCPU(const llvm::Triple &triple) const override;\n  std::string getFeatures(const llvm::Triple &triple) const override;\n};\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/llvm/optimize.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"optimize.h\"\n\n#include <algorithm>\n#include <deque>\n\n#include \"codon/cir/llvm/gpu.h\"\n#include \"codon/cir/llvm/llvisitor.h\"\n#include \"codon/cir/llvm/native/native.h\"\n#include \"codon/util/common.h\"\n\nstatic llvm::codegen::RegisterCodeGenFlags CFG;\n\nnamespace codon {\nnamespace ir {\nnamespace {\nllvm::cl::opt<bool>\n    AutoFree(\"auto-free\",\n             llvm::cl::desc(\"Insert free() calls on allocated memory automatically\"),\n             llvm::cl::init(false), llvm::cl::Hidden);\n\nllvm::cl::opt<bool> FastMath(\"fast-math\",\n                             llvm::cl::desc(\"Apply fastmath optimizations\"),\n                             llvm::cl::init(false));\n} // namespace\n\nstd::unique_ptr<llvm::TargetMachine>\ngetTargetMachine(llvm::Triple triple, llvm::StringRef cpuStr,\n                 llvm::StringRef featuresStr, const llvm::TargetOptions &options,\n                 bool pic) {\n  std::string err;\n  const llvm::Target *target =\n      llvm::TargetRegistry::lookupTarget(llvm::codegen::getMArch(), triple, err);\n\n  if (!target)\n    return nullptr;\n\n  return std::unique_ptr<llvm::TargetMachine>(target->createTargetMachine(\n      triple.getTriple(), cpuStr, featuresStr, options,\n      pic ? llvm::Reloc::Model::PIC_ : llvm::codegen::getExplicitRelocModel(),\n      llvm::codegen::getExplicitCodeModel(), llvm::CodeGenOptLevel::Aggressive));\n}\n\nstd::unique_ptr<llvm::TargetMachine>\ngetTargetMachine(llvm::Module *module, bool setFunctionAttributes, bool pic) {\n  llvm::Triple moduleTriple(module->getTargetTriple());\n  std::string cpuStr, featuresStr;\n  const llvm::TargetOptions options =\n      llvm::codegen::InitTargetOptionsFromCodeGenFlags(moduleTriple);\n  llvm::TargetLibraryInfoImpl tlii(moduleTriple);\n\n  if (moduleTriple.getArch()) {\n    cpuStr = llvm::codegen::getCPUStr();\n    featuresStr = llvm::codegen::getFeaturesStr();\n    auto machine = getTargetMachine(moduleTriple, cpuStr, featuresStr, options);\n    if (setFunctionAttributes)\n      llvm::codegen::setFunctionAttributes(cpuStr, featuresStr, *module);\n    return machine;\n  }\n  return {};\n}\n\nnamespace {\nvoid applyDebugTransformations(llvm::Module *module, bool debug, bool jit) {\n  if (debug) {\n    auto ctor = LLVMVisitor::getGlobalCtorName();\n    // remove tail calls and fix linkage for stack traces\n    for (auto &f : *module) {\n      // needed for debug symbols\n      if (!jit && f.getName() != ctor)\n        f.setLinkage(llvm::GlobalValue::ExternalLinkage);\n      if (!f.hasFnAttribute(llvm::Attribute::AttrKind::AlwaysInline))\n        f.addFnAttr(llvm::Attribute::AttrKind::NoInline);\n      f.setUWTableKind(llvm::UWTableKind::Default);\n      f.addFnAttr(\"no-frame-pointer-elim\", \"true\");\n      f.addFnAttr(\"no-frame-pointer-elim-non-leaf\");\n      f.addFnAttr(\"no-jump-tables\", \"false\");\n\n      for (auto &block : f) {\n        for (auto &inst : block) {\n          if (auto *call = llvm::dyn_cast<llvm::CallInst>(&inst)) {\n            call->setTailCall(false);\n          }\n        }\n      }\n    }\n  } else {\n    llvm::StripDebugInfo(*module);\n  }\n}\n\nvoid applyFastMathTransformations(llvm::Module *module) {\n  if (!FastMath)\n    return;\n\n  for (auto &f : *module) {\n    for (auto &block : f) {\n      for (auto &inst : block) {\n        if (auto *binop = llvm::dyn_cast<llvm::BinaryOperator>(&inst)) {\n          if (binop->getType()->isFloatingPointTy())\n            binop->setFast(true);\n        }\n\n        if (auto *intrinsic = llvm::dyn_cast<llvm::IntrinsicInst>(&inst)) {\n          if (intrinsic->getType()->isFloatingPointTy())\n            intrinsic->setFast(true);\n        }\n      }\n    }\n  }\n}\n\nstruct AllocInfo {\n  std::vector<std::string> allocators;\n  std::string realloc;\n  std::string free;\n\n  AllocInfo(std::vector<std::string> allocators, const std::string &realloc,\n            const std::string &free)\n      : allocators(std::move(allocators)), realloc(realloc), free(free) {}\n\n  static bool getFixedArg(llvm::CallBase &cb, uint64_t &size, unsigned idx = 0) {\n    if (cb.arg_empty())\n      return false;\n\n    if (auto *ci = llvm::dyn_cast<llvm::ConstantInt>(cb.getArgOperand(idx))) {\n      size = ci->getZExtValue();\n      return true;\n    }\n\n    return false;\n  }\n\n  bool isAlloc(const llvm::Value *value) {\n    if (auto *func = getCalledFunction(value)) {\n      return func->arg_size() == 1 && std::find(allocators.begin(), allocators.end(),\n                                                func->getName()) != allocators.end();\n    }\n    return false;\n  }\n\n  bool isRealloc(const llvm::Value *value) {\n    if (auto *func = getCalledFunction(value)) {\n      // Note: 3 args are (ptr, new_size, old_size)\n      return func->arg_size() == 3 && func->getName() == realloc;\n    }\n    return false;\n  }\n\n  bool isFree(const llvm::Value *value) {\n    if (auto *func = getCalledFunction(value)) {\n      return func->arg_size() == 1 && func->getName() == free;\n    }\n    return false;\n  }\n\n  static const llvm::Function *getCalledFunction(const llvm::Value *value) {\n    // Don't care about intrinsics in this case.\n    if (llvm::isa<llvm::IntrinsicInst>(value))\n      return nullptr;\n\n    const auto *cb = llvm::dyn_cast<llvm::CallBase>(value);\n    if (!cb)\n      return nullptr;\n\n    if (const llvm::Function *callee = cb->getCalledFunction())\n      return callee;\n    return nullptr;\n  }\n\n  bool isNeverEqualToUnescapedAlloc(llvm::Value *value, llvm::Instruction *ai) {\n    using namespace llvm;\n\n    if (isa<ConstantPointerNull>(value))\n      return true;\n    if (auto *li = dyn_cast<LoadInst>(value))\n      return isa<GlobalVariable>(li->getPointerOperand());\n    // Two distinct allocations will never be equal.\n    return isAlloc(value) && value != ai;\n  }\n\n  bool isAllocSiteRemovable(llvm::Instruction *ai,\n                            llvm::SmallVectorImpl<llvm::WeakTrackingVH> &users) {\n    using namespace llvm;\n\n    // Should never be an invoke, so just check right away.\n    if (isa<InvokeInst>(ai))\n      return false;\n\n    SmallVector<Instruction *, 4> worklist;\n    worklist.push_back(ai);\n\n    do {\n      Instruction *pi = worklist.pop_back_val();\n      for (User *u : pi->users()) {\n        Instruction *instr = cast<Instruction>(u);\n        switch (instr->getOpcode()) {\n        default:\n          // Give up the moment we see something we can't handle.\n          return false;\n\n        case Instruction::AddrSpaceCast:\n        case Instruction::BitCast:\n        case Instruction::GetElementPtr:\n          users.emplace_back(instr);\n          worklist.push_back(instr);\n          continue;\n\n        case Instruction::ICmp: {\n          ICmpInst *cmp = cast<ICmpInst>(instr);\n          // We can fold eq/ne comparisons with null to false/true, respectively.\n          // We also fold comparisons in some conditions provided the alloc has\n          // not escaped (see isNeverEqualToUnescapedAlloc).\n          if (!cmp->isEquality())\n            return false;\n          unsigned otherIndex = (cmp->getOperand(0) == pi) ? 1 : 0;\n          if (!isNeverEqualToUnescapedAlloc(cmp->getOperand(otherIndex), ai))\n            return false;\n          users.emplace_back(instr);\n          continue;\n        }\n\n        case Instruction::Call:\n          // Ignore no-op and store intrinsics.\n          if (IntrinsicInst *intrinsic = dyn_cast<IntrinsicInst>(instr)) {\n            switch (intrinsic->getIntrinsicID()) {\n            default:\n              return false;\n\n            case Intrinsic::memmove:\n            case Intrinsic::memcpy:\n            case Intrinsic::memset: {\n              MemIntrinsic *MI = cast<MemIntrinsic>(intrinsic);\n              if (MI->isVolatile() || MI->getRawDest() != pi)\n                return false;\n              LLVM_FALLTHROUGH;\n            }\n            case Intrinsic::assume:\n            case Intrinsic::invariant_start:\n            case Intrinsic::invariant_end:\n            case Intrinsic::lifetime_start:\n            case Intrinsic::lifetime_end:\n              users.emplace_back(instr);\n              continue;\n            case Intrinsic::launder_invariant_group:\n            case Intrinsic::strip_invariant_group:\n              users.emplace_back(instr);\n              worklist.push_back(instr);\n              continue;\n            }\n          }\n\n          if (isFree(instr)) {\n            users.emplace_back(instr);\n            continue;\n          }\n\n          if (isRealloc(instr)) {\n            users.emplace_back(instr);\n            worklist.push_back(instr);\n            continue;\n          }\n\n          return false;\n\n        case Instruction::Store: {\n          StoreInst *si = cast<StoreInst>(instr);\n          if (si->isVolatile() || si->getPointerOperand() != pi)\n            return false;\n          users.emplace_back(instr);\n          continue;\n        }\n        }\n        seqassertn(false, \"missing a return?\");\n      }\n    } while (!worklist.empty());\n    return true;\n  }\n\n  bool isAllocSiteDemotable(llvm::Instruction *ai, uint64_t &size,\n                            llvm::SmallVectorImpl<llvm::WeakTrackingVH> &users,\n                            uint64_t maxSize = 1024) {\n    using namespace llvm;\n\n    // Should never be an invoke, so just check right away.\n    if (isa<InvokeInst>(ai))\n      return false;\n\n    if (!(getFixedArg(*dyn_cast<CallBase>(&*ai), size) && 0 < size && size <= maxSize))\n      return false;\n\n    SmallVector<Instruction *, 4> worklist;\n    worklist.push_back(ai);\n\n    do {\n      Instruction *pi = worklist.pop_back_val();\n      for (User *u : pi->users()) {\n        Instruction *instr = cast<Instruction>(u);\n        switch (instr->getOpcode()) {\n        default:\n          // Give up the moment we see something we can't handle.\n          return false;\n\n        case Instruction::AddrSpaceCast:\n        case Instruction::BitCast:\n        case Instruction::GetElementPtr:\n          worklist.push_back(instr);\n          continue;\n\n        case Instruction::ICmp: {\n          ICmpInst *cmp = cast<ICmpInst>(instr);\n          // We can fold eq/ne comparisons with null to false/true, respectively.\n          // We also fold comparisons in some conditions provided the alloc has\n          // not escaped (see isNeverEqualToUnescapedAlloc).\n          if (!cmp->isEquality())\n            return false;\n          unsigned otherIndex = (cmp->getOperand(0) == pi) ? 1 : 0;\n          if (!isNeverEqualToUnescapedAlloc(cmp->getOperand(otherIndex), ai))\n            return false;\n          continue;\n        }\n\n        case Instruction::Call:\n          // Ignore no-op and store intrinsics.\n          if (IntrinsicInst *intrinsic = dyn_cast<IntrinsicInst>(instr)) {\n            switch (intrinsic->getIntrinsicID()) {\n            default:\n              return false;\n\n            case Intrinsic::memmove:\n            case Intrinsic::memcpy:\n            case Intrinsic::memset: {\n              MemIntrinsic *MI = cast<MemIntrinsic>(intrinsic);\n              if (MI->isVolatile())\n                return false;\n              LLVM_FALLTHROUGH;\n            }\n            case Intrinsic::assume:\n            case Intrinsic::invariant_start:\n            case Intrinsic::invariant_end:\n            case Intrinsic::lifetime_start:\n            case Intrinsic::lifetime_end:\n              users.emplace_back(instr);\n              continue;\n            case Intrinsic::launder_invariant_group:\n            case Intrinsic::strip_invariant_group:\n              users.emplace_back(instr);\n              worklist.push_back(instr);\n              continue;\n            }\n          }\n\n          if (isFree(instr)) {\n            users.emplace_back(instr);\n            continue;\n          }\n\n          if (isRealloc(instr)) {\n            // If the realloc also has constant small size,\n            // then we can just update the assumed size to be\n            // max of original alloc's and this realloc's.\n            uint64_t newSize = 0;\n            if (getFixedArg(*dyn_cast<CallBase>(instr), newSize, 1) &&\n                (0 < newSize && newSize <= maxSize)) {\n              size = std::max(size, newSize);\n            } else {\n              return false;\n            }\n\n            users.emplace_back(instr);\n            worklist.push_back(instr);\n            continue;\n          }\n\n          return false;\n\n        case Instruction::Store: {\n          StoreInst *si = cast<StoreInst>(instr);\n          if (si->isVolatile() || si->getPointerOperand() != pi)\n            return false;\n          continue;\n        }\n\n        case Instruction::Load: {\n          LoadInst *li = cast<LoadInst>(instr);\n          if (li->isVolatile())\n            return false;\n          continue;\n        }\n        }\n        seqassertn(false, \"missing a return?\");\n      }\n    } while (!worklist.empty());\n    return true;\n  }\n\n  bool isAllocSiteHoistable(llvm::Instruction *ai, llvm::Loop &loop,\n                            llvm::CycleInfo &cycles) {\n    using namespace llvm;\n\n    auto inIrreducibleCycle = [&](Instruction *ins) {\n      auto *cycle = cycles.getCycle(ins->getParent());\n      while (cycle) {\n        if (!cycle->isReducible())\n          return true;\n        cycle = cycle->getParentCycle();\n      }\n      return false;\n    };\n\n    auto anySubLoopContains = [&](Instruction *ins) {\n      for (auto *sub : loop.getSubLoops()) {\n        if (sub->contains(ins))\n          return true;\n      }\n      return false;\n    };\n\n    // Some preliminary checks\n    auto *parent = ai->getParent();\n    if (isa<InvokeInst>(ai) || !loop.hasLoopInvariantOperands(ai) ||\n        anySubLoopContains(ai) || inIrreducibleCycle(ai))\n      return false;\n\n    // Need to track insertvalue/extractvalue to make this effective.\n    // This maps each \"insertvalue\" of the pointer (or derived value)\n    // to a list of indices at which it is inserted (usually there will\n    // be just one).\n    SmallDenseMap<Instruction *, SmallVector<ArrayRef<unsigned>, 1>> inserts;\n\n    std::deque<Instruction *> worklist;\n    SmallSet<Instruction *, 20> visited;\n    auto add_to_worklist = [&](Instruction *instr) {\n      if (!visited.contains(instr)) {\n        visited.insert(instr);\n        worklist.push_front(instr);\n      }\n    };\n    add_to_worklist(ai);\n\n    do {\n      Instruction *pi = worklist.back();\n      worklist.pop_back();\n\n      for (User *u : pi->users()) {\n        Instruction *instr = cast<Instruction>(u);\n        if (!loop.contains(instr))\n          return false;\n\n        switch (instr->getOpcode()) {\n        default:\n          // Give up the moment we see something we can't handle.\n          return false;\n\n        case Instruction::PHI:\n          if (instr->getParent() == loop.getHeader())\n            return false;\n          LLVM_FALLTHROUGH;\n\n        case Instruction::PtrToInt:\n        case Instruction::IntToPtr:\n        case Instruction::Add:\n        case Instruction::Sub:\n        case Instruction::AddrSpaceCast:\n        case Instruction::BitCast:\n        case Instruction::GetElementPtr:\n          add_to_worklist(instr);\n          continue;\n\n        case Instruction::InsertValue: {\n          auto *op0 = instr->getOperand(0);\n          auto *op1 = instr->getOperand(1);\n          if (isa<InsertValueInst>(op0) || isa<FreezeInst>(op0) ||\n              isa<UndefValue>(op0)) {\n            // Add for this insertvalue\n            if (op1 == pi) {\n              auto *insertValueInst = cast<InsertValueInst>(instr);\n              inserts[instr].push_back(insertValueInst->getIndices());\n            }\n            // Add for previous insertvalue\n            if (auto *instrOp = dyn_cast<Instruction>(op0)) {\n              auto it = inserts.find(instrOp);\n              if (it != inserts.end())\n                inserts[instr].append(it->second);\n            }\n          }\n          add_to_worklist(instr);\n          continue;\n        }\n\n        case Instruction::ExtractValue: {\n          auto *extractValueInst = cast<ExtractValueInst>(instr);\n          auto it = inserts.end();\n          if (auto *instrOp = dyn_cast<Instruction>(instr->getOperand(0)))\n            it = inserts.find(instrOp);\n          if (it != inserts.end()) {\n            for (auto &indices : it->second) {\n              if (indices == extractValueInst->getIndices()) {\n                add_to_worklist(instr);\n                break;\n              }\n            }\n          } else {\n            add_to_worklist(instr);\n          }\n          continue;\n        }\n\n        case Instruction::Freeze: {\n          if (auto *instrOp = dyn_cast<Instruction>(instr->getOperand(0))) {\n            auto it = inserts.find(instrOp);\n            if (it != inserts.end())\n              inserts[instr] = it->second;\n          }\n          add_to_worklist(instr);\n          continue;\n        }\n\n        case Instruction::ICmp:\n          continue;\n\n        case Instruction::Call:\n        case Instruction::Invoke:\n          // Ignore no-op and store intrinsics.\n          if (IntrinsicInst *intrinsic = dyn_cast<IntrinsicInst>(instr)) {\n            switch (intrinsic->getIntrinsicID()) {\n            default:\n              return false;\n\n            case Intrinsic::memmove:\n            case Intrinsic::memcpy:\n            case Intrinsic::memset: {\n              MemIntrinsic *MI = cast<MemIntrinsic>(intrinsic);\n              if (MI->isVolatile())\n                return false;\n              LLVM_FALLTHROUGH;\n            }\n            case Intrinsic::assume:\n            case Intrinsic::invariant_start:\n            case Intrinsic::invariant_end:\n            case Intrinsic::lifetime_start:\n            case Intrinsic::lifetime_end:\n              continue;\n            case Intrinsic::launder_invariant_group:\n            case Intrinsic::strip_invariant_group:\n              add_to_worklist(instr);\n              continue;\n            }\n          }\n          return false;\n\n        case Instruction::Store: {\n          StoreInst *si = cast<StoreInst>(instr);\n          if (si->isVolatile() || si->getPointerOperand() != pi)\n            return false;\n          continue;\n        }\n\n        case Instruction::Load: {\n          LoadInst *li = cast<LoadInst>(instr);\n          if (li->isVolatile())\n            return false;\n          continue;\n        }\n        }\n        seqassertn(false, \"missing a return?\");\n      }\n    } while (!worklist.empty());\n    return true;\n  }\n};\n\n/// Lowers allocations of known, small size to alloca when possible.\n/// Also removes unused allocations.\nstruct AllocationRemover : public llvm::PassInfoMixin<AllocationRemover> {\n  AllocInfo info;\n\n  explicit AllocationRemover(\n      std::vector<std::string> allocators = {\"seq_alloc\", \"seq_alloc_atomic\",\n                                             \"seq_alloc_uncollectable\",\n                                             \"seq_alloc_atomic_uncollectable\"},\n      const std::string &realloc = \"seq_realloc\", const std::string &free = \"seq_free\")\n      : info(std::move(allocators), realloc, free) {}\n\n  void getErasesAndReplacementsForAlloc(\n      llvm::Instruction &mi, llvm::SmallPtrSetImpl<llvm::Instruction *> &erase,\n      llvm::SmallVectorImpl<std::pair<llvm::Instruction *, llvm::Value *>> &replace,\n      llvm::SmallVectorImpl<llvm::AllocaInst *> &alloca,\n      llvm::SmallVectorImpl<llvm::CallInst *> &untail) {\n    using namespace llvm;\n\n    uint64_t size = 0;\n    SmallVector<WeakTrackingVH, 64> users;\n\n    if (info.isAllocSiteRemovable(&mi, users)) {\n      for (unsigned i = 0, e = users.size(); i != e; ++i) {\n        if (!users[i])\n          continue;\n\n        Instruction *instr = cast<Instruction>(&*users[i]);\n        if (ICmpInst *cmp = dyn_cast<ICmpInst>(instr)) {\n          replace.emplace_back(cmp, ConstantInt::get(Type::getInt1Ty(cmp->getContext()),\n                                                     cmp->isFalseWhenEqual()));\n        } else if (!isa<StoreInst>(instr)) {\n          // Casts, GEP, or anything else: we're about to delete this instruction,\n          // so it can not have any valid uses.\n          replace.emplace_back(instr, PoisonValue::get(instr->getType()));\n        }\n        erase.insert(instr);\n      }\n      erase.insert(&mi);\n      return;\n    } else {\n      users.clear();\n    }\n\n    if (info.isAllocSiteDemotable(&mi, size, users)) {\n      auto *replacement = new AllocaInst(\n          Type::getInt8Ty(mi.getContext()), 0,\n          ConstantInt::get(Type::getInt64Ty(mi.getContext()), size), Align());\n      alloca.push_back(replacement);\n      replace.emplace_back(&mi, replacement);\n      erase.insert(&mi);\n\n      for (unsigned i = 0, e = users.size(); i != e; ++i) {\n        if (!users[i])\n          continue;\n\n        Instruction *instr = cast<Instruction>(&*users[i]);\n        if (info.isFree(instr)) {\n          erase.insert(instr);\n        } else if (info.isRealloc(instr)) {\n          replace.emplace_back(instr, replacement);\n          erase.insert(instr);\n        } else if (auto *ci = dyn_cast<CallInst>(&*instr)) {\n          if (ci->isTailCall() || ci->isMustTailCall())\n            untail.push_back(ci);\n        }\n      }\n    }\n  }\n\n  llvm::PreservedAnalyses run(llvm::Function &func, llvm::FunctionAnalysisManager &am) {\n    using namespace llvm;\n\n    SmallSet<Instruction *, 32> erase;\n    SmallVector<std::pair<Instruction *, llvm::Value *>, 32> replace;\n    SmallVector<AllocaInst *, 32> alloca;\n    SmallVector<CallInst *, 32> untail;\n\n    for (inst_iterator instr = inst_begin(func), end = inst_end(func); instr != end;\n         ++instr) {\n      auto *cb = dyn_cast<CallBase>(&*instr);\n      if (!cb || !info.isAlloc(cb))\n        continue;\n\n      getErasesAndReplacementsForAlloc(*cb, erase, replace, alloca, untail);\n    }\n\n    for (auto *A : alloca) {\n      A->insertBefore(func.getEntryBlock().getFirstNonPHI());\n    }\n\n    for (auto *C : untail) {\n      C->setTailCall(false);\n    }\n\n    for (auto &P : replace) {\n      P.first->replaceAllUsesWith(P.second);\n    }\n\n    for (auto *I : erase) {\n      I->dropAllReferences();\n    }\n\n    for (auto *I : erase) {\n      I->eraseFromParent();\n    }\n\n    if (!erase.empty() || !replace.empty() || !alloca.empty() || !untail.empty())\n      return PreservedAnalyses::none();\n    else\n      return PreservedAnalyses::all();\n  }\n};\n\n/// Hoists allocations that are inside a loop out of the loop.\nstruct AllocationHoister : public llvm::PassInfoMixin<AllocationHoister> {\n  AllocInfo info;\n\n  explicit AllocationHoister(std::vector<std::string> allocators = {\"seq_alloc\",\n                                                                    \"seq_alloc_atomic\"},\n                             const std::string &realloc = \"seq_realloc\",\n                             const std::string &free = \"seq_free\")\n      : info(std::move(allocators), realloc, free) {}\n\n  bool processLoop(llvm::Loop &loop, llvm::LoopInfo &loops, llvm::CycleInfo &cycles,\n                   llvm::PostDominatorTree &postdom) {\n    llvm::SmallSet<llvm::CallBase *, 32> hoist;\n    for (auto *block : loop.blocks()) {\n      for (auto &ins : *block) {\n        if (info.isAlloc(&ins) && info.isAllocSiteHoistable(&ins, loop, cycles))\n          hoist.insert(llvm::cast<llvm::CallBase>(&ins));\n      }\n    }\n\n    if (hoist.empty())\n      return false;\n\n    auto *preheader = loop.getLoopPreheader();\n    auto *terminator = preheader->getTerminator();\n    auto *parent = preheader->getParent();\n    auto *M = preheader->getModule();\n    auto &C = preheader->getContext();\n\n    llvm::IRBuilder<> B(C);\n    auto *ptr = B.getPtrTy();\n    llvm::DomTreeUpdater dtu(postdom, llvm::DomTreeUpdater::UpdateStrategy::Lazy);\n\n    for (auto *ins : hoist) {\n      if (postdom.dominates(ins, preheader->getTerminator())) {\n        // Simple case - loop must execute allocation, so\n        // just hoist it directly.\n        ins->removeFromParent();\n        ins->insertBefore(preheader->getTerminator());\n      } else {\n        // Complex case - loop might not execute allocation,\n        // so have to keep it where it is but cache it.\n        // Transformation is as follows:\n        //   Before:\n        //     p = alloc(n)\n        //   After:\n        //     if cache is null:\n        //       p = alloc(n)\n        //       cache = p\n        //     else:\n        //       p = cache\n        B.SetInsertPointPastAllocas(parent);\n        auto *cache = B.CreateAlloca(ptr);\n        cache->setName(\"alloc_hoist.cache\");\n        B.CreateStore(llvm::ConstantPointerNull::get(ptr), cache);\n        B.SetInsertPoint(ins);\n        auto *cachedAlloc = B.CreateLoad(ptr, cache);\n\n        // Split the block at the call site\n        llvm::BasicBlock *allocYes = nullptr;\n        llvm::BasicBlock *allocNo = nullptr;\n        llvm::SplitBlockAndInsertIfThenElse(B.CreateIsNull(cachedAlloc), ins, &allocYes,\n                                            &allocNo,\n                                            /*UnreachableThen=*/false,\n                                            /*UnreachableElse=*/false,\n                                            /*BranchWeights=*/nullptr, &dtu, &loops);\n\n        B.SetInsertPoint(&allocYes->getSingleSuccessor()->front());\n        llvm::PHINode *phi = B.CreatePHI(ptr, 2);\n        ins->replaceAllUsesWith(phi);\n\n        ins->removeFromParent();\n        ins->insertBefore(allocYes->getTerminator());\n        B.SetInsertPoint(allocYes->getTerminator());\n        B.CreateStore(ins, cache);\n\n        phi->addIncoming(ins, allocYes);\n        phi->addIncoming(cachedAlloc, allocNo);\n      }\n    }\n\n    dtu.flush();\n    return true;\n  }\n\n  llvm::PreservedAnalyses run(llvm::Function &F, llvm::FunctionAnalysisManager &am) {\n    auto &loops = am.getResult<llvm::LoopAnalysis>(F);\n    auto &cycles = am.getResult<llvm::CycleAnalysis>(F);\n    auto &postdom = am.getResult<llvm::PostDominatorTreeAnalysis>(F);\n    bool changed = false;\n    llvm::SmallPriorityWorklist<llvm::Loop *, 4> worklist;\n    llvm::appendLoopsToWorklist(loops, worklist);\n\n    while (!worklist.empty())\n      changed |= processLoop(*worklist.pop_back_val(), loops, cycles, postdom);\n\n    return changed ? llvm::PreservedAnalyses::none() : llvm::PreservedAnalyses::all();\n  }\n};\n\nstruct AllocationAutoFree : public llvm::PassInfoMixin<AllocationAutoFree> {\n  AllocInfo info;\n\n  explicit AllocationAutoFree(\n      std::vector<std::string> allocators = {\"seq_alloc\", \"seq_alloc_atomic\",\n                                             \"seq_alloc_uncollectable\",\n                                             \"seq_alloc_atomic_uncollectable\"},\n      const std::string &realloc = \"seq_realloc\", const std::string &free = \"seq_free\")\n      : info(std::move(allocators), realloc, free) {}\n\n  llvm::PreservedAnalyses run(llvm::Function &F, llvm::FunctionAnalysisManager &FAM) {\n    // Get the necessary analysis results.\n    auto &MSSA = FAM.getResult<llvm::MemorySSAAnalysis>(F);\n    auto &TLI = FAM.getResult<llvm::TargetLibraryAnalysis>(F);\n    auto &AA = FAM.getResult<llvm::AAManager>(F);\n    auto &DT = FAM.getResult<llvm::DominatorTreeAnalysis>(F);\n    auto &PDT = FAM.getResult<llvm::PostDominatorTreeAnalysis>(F);\n    auto &LI = FAM.getResult<llvm::LoopAnalysis>(F);\n    auto &CI = FAM.getResult<llvm::CycleAnalysis>(F);\n    bool Changed = false;\n\n    // Traverse the function to find allocs and insert corresponding frees.\n    for (auto &BB : F) {\n      for (auto &I : BB) {\n        if (auto *Alloc = llvm::dyn_cast<llvm::CallInst>(&I)) {\n          auto *Callee = Alloc->getCalledFunction();\n          if (!Callee || !Callee->isDeclaration())\n            continue;\n\n          if (info.isAlloc(Alloc)) {\n            if (llvm::PointerMayBeCaptured(Alloc, /*ReturnCaptures=*/true,\n                                           /*StoreCaptures=*/true))\n              continue;\n\n            Changed |= insertFree(Alloc, F, DT, PDT, LI, CI);\n          }\n        }\n      }\n    }\n\n    return (Changed ? llvm::PreservedAnalyses::none() : llvm::PreservedAnalyses::all());\n  }\n\n  bool insertFree(llvm::Instruction *Alloc, llvm::Function &F, llvm::DominatorTree &DT,\n                  llvm::PostDominatorTree &PDT, llvm::LoopInfo &LI,\n                  llvm::CycleInfo &CI) {\n    llvm::SmallVector<llvm::Value *, 8> Worklist;\n    llvm::SmallPtrSet<llvm::Value *, 8> Visited;\n    llvm::SmallVector<llvm::BasicBlock *, 8> UseBlocks;\n\n    // We need to find a basic block that:\n    //   1. Post-dominates the allocation block (so we always free it)\n    //   2. Is dominated by the allocation block (so the use is valid)\n    //   3. Post-dominates all uses\n\n    // Start with the original pointer.\n    Worklist.push_back(Alloc);\n    UseBlocks.push_back(Alloc->getParent());\n\n    // Track all blocks where the pointer or its derived values are used.\n    while (!Worklist.empty()) {\n      auto *CurrentPtr = Worklist.pop_back_val();\n      if (!Visited.insert(CurrentPtr).second)\n        continue;\n\n      // Traverse all users of the current pointer.\n      for (auto *U : CurrentPtr->users()) {\n        if (auto *Inst = llvm::dyn_cast<llvm::Instruction>(U)) {\n          if (auto *call = llvm::dyn_cast<llvm::CallBase>(Inst))\n            if (call->getCalledFunction() && info.isFree(call->getCalledFunction()))\n              return false;\n\n          if (llvm::isa<llvm::GetElementPtrInst>(Inst) ||\n              llvm::isa<llvm::BitCastInst>(Inst) || llvm::isa<llvm::PHINode>(Inst) ||\n              llvm::isa<llvm::SelectInst>(Inst)) {\n            Worklist.push_back(Inst);\n          } else {\n            // If this is a real use, record the block.\n            UseBlocks.push_back(Inst->getParent());\n          }\n        }\n      }\n    }\n\n    // Find the closest post-dominating block of all the use blocks.\n    llvm::BasicBlock *PostDomBlock = nullptr;\n    for (auto *BB : UseBlocks) {\n      if (!PostDomBlock) {\n        PostDomBlock = BB;\n      } else {\n        PostDomBlock = PDT.findNearestCommonDominator(PostDomBlock, BB);\n        if (!PostDomBlock) {\n          return false;\n        }\n      }\n    }\n\n    auto *allocLoop = LI.getLoopFor(Alloc->getParent());\n    auto *freeLoop = LI.getLoopFor(PostDomBlock);\n\n    while (allocLoop != freeLoop) {\n      if (!freeLoop)\n        return false;\n      PostDomBlock = freeLoop->getExitBlock();\n      if (!PostDomBlock)\n        return false;\n      freeLoop = LI.getLoopFor(PostDomBlock);\n    }\n\n    if (!DT.dominates(Alloc->getParent(), PostDomBlock)) {\n      return false;\n    }\n\n    llvm::IRBuilder<> B(PostDomBlock->getTerminator());\n    auto *FreeFunc = F.getParent()->getFunction(info.free);\n    if (!FreeFunc) {\n      FreeFunc = llvm::Function::Create(\n          llvm::FunctionType::get(B.getVoidTy(), {B.getPtrTy()}, false),\n          llvm::Function::ExternalLinkage, info.free, F.getParent());\n      FreeFunc->setWillReturn();\n      FreeFunc->setDoesNotThrow();\n    }\n\n    // Add free\n    B.CreateCall(FreeFunc, Alloc);\n\n    return true;\n  }\n};\n\n/// Sometimes coroutine lowering produces hard-to-analyze loops involving\n/// function pointer comparisons. This pass puts them into a somewhat\n/// easier-to-analyze form.\nstruct CoroBranchSimplifier : public llvm::PassInfoMixin<CoroBranchSimplifier> {\n  static llvm::Value *getNonNullOperand(llvm::Value *op1, llvm::Value *op2) {\n    auto *ptr = llvm::dyn_cast<llvm::PointerType>(op1->getType());\n    if (!ptr)\n      return nullptr;\n\n    auto *c1 = llvm::dyn_cast<llvm::Constant>(op1);\n    auto *c2 = llvm::dyn_cast<llvm::Constant>(op2);\n    const bool isNull1 = (c1 && c1->isNullValue());\n    const bool isNull2 = (c2 && c2->isNullValue());\n    if (!(isNull1 ^ isNull2))\n      return nullptr;\n    return isNull1 ? op2 : op1;\n  }\n\n  llvm::PreservedAnalyses run(llvm::Loop &loop, llvm::LoopAnalysisManager &am,\n                              llvm::LoopStandardAnalysisResults &ar,\n                              llvm::LPMUpdater &u) {\n    if (auto *exit = loop.getExitingBlock()) {\n      if (auto *br = llvm::dyn_cast<llvm::BranchInst>(exit->getTerminator())) {\n        if (!br->isConditional() || br->getNumSuccessors() != 2 ||\n            loop.contains(br->getSuccessor(0)) || !loop.contains(br->getSuccessor(1)))\n          return llvm::PreservedAnalyses::all();\n\n        auto *cond = br->getCondition();\n        if (auto *cmp = llvm::dyn_cast<llvm::CmpInst>(cond)) {\n          if (cmp->getPredicate() != llvm::CmpInst::Predicate::ICMP_EQ)\n            return llvm::PreservedAnalyses::all();\n\n          if (auto *f = getNonNullOperand(cmp->getOperand(0), cmp->getOperand(1))) {\n            if (auto *sel = llvm::dyn_cast<llvm::SelectInst>(f)) {\n              if (auto *g =\n                      getNonNullOperand(sel->getTrueValue(), sel->getFalseValue())) {\n                // If we can deduce that g is not null, we can replace the condition.\n                if (auto *phi = llvm::dyn_cast<llvm::PHINode>(g)) {\n                  bool ok = true;\n                  for (unsigned i = 0; i < phi->getNumIncomingValues(); i++) {\n                    auto *phiBlock = phi->getIncomingBlock(i);\n                    auto *phiValue = phi->getIncomingValue(i);\n\n                    if (auto *c = llvm::dyn_cast<llvm::Constant>(phiValue)) {\n                      if (c->isNullValue()) {\n                        ok = false;\n                        break;\n                      }\n                    } else {\n                      // There is no way for the value to be null if the incoming phi\n                      // value is predicated on this exit condition, which checks for a\n                      // non-null function pointer.\n\n                      if (phiBlock != exit || phiValue != f) {\n                        ok = false;\n                        break;\n                      }\n                    }\n                  }\n                  if (!ok)\n                    return llvm::PreservedAnalyses::all();\n\n                  br->setCondition(sel->getCondition());\n                  return llvm::PreservedAnalyses::none();\n                }\n              }\n            }\n          }\n        }\n      }\n    }\n    return llvm::PreservedAnalyses::all();\n  }\n};\n\nllvm::cl::opt<bool>\n    DisableNative(\"disable-native\",\n                  llvm::cl::desc(\"Disable architecture-specific optimizations\"),\n                  llvm::cl::init(false));\n\nvoid runLLVMOptimizationPasses(llvm::Module *module, bool debug, bool jit,\n                               PluginManager *plugins) {\n  applyDebugTransformations(module, debug, jit);\n  applyFastMathTransformations(module);\n\n  llvm::LoopAnalysisManager lam;\n  llvm::FunctionAnalysisManager fam;\n  llvm::CGSCCAnalysisManager cgam;\n  llvm::ModuleAnalysisManager mam;\n  auto machine = getTargetMachine(module, /*setFunctionAttributes=*/true);\n  llvm::PassBuilder pb(machine.get());\n\n  llvm::Triple moduleTriple(module->getTargetTriple());\n  llvm::TargetLibraryInfoImpl tlii(moduleTriple);\n  fam.registerPass([&] { return llvm::TargetLibraryAnalysis(tlii); });\n\n  pb.registerModuleAnalyses(mam);\n  pb.registerCGSCCAnalyses(cgam);\n  pb.registerFunctionAnalyses(fam);\n  pb.registerLoopAnalyses(lam);\n  pb.crossRegisterProxies(lam, fam, cgam, mam);\n\n  pb.registerLateLoopOptimizationsEPCallback(\n      [&](llvm::LoopPassManager &pm, llvm::OptimizationLevel opt) {\n        if (opt.isOptimizingForSpeed())\n          pm.addPass(CoroBranchSimplifier());\n      });\n\n  pb.registerPeepholeEPCallback(\n      [&](llvm::FunctionPassManager &pm, llvm::OptimizationLevel opt) {\n        if (opt.isOptimizingForSpeed()) {\n          pm.addPass(AllocationRemover());\n          pm.addPass(llvm::LoopSimplifyPass());\n          pm.addPass(llvm::LCSSAPass());\n          pm.addPass(AllocationHoister());\n          if (AutoFree)\n            pm.addPass(AllocationAutoFree());\n        }\n      });\n\n  if (!DisableNative)\n    addNativeLLVMPasses(&pb);\n\n  if (plugins) {\n    for (auto *plugin : *plugins) {\n      plugin->dsl->addLLVMPasses(&pb, debug);\n    }\n  }\n\n  if (debug) {\n    llvm::ModulePassManager mpm =\n        pb.buildO0DefaultPipeline(llvm::OptimizationLevel::O0);\n    mpm.run(*module, mam);\n  } else {\n    llvm::ModulePassManager mpm =\n        pb.buildPerModuleDefaultPipeline(llvm::OptimizationLevel::O3);\n    mpm.run(*module, mam);\n  }\n\n  applyDebugTransformations(module, debug, jit);\n}\n\nvoid verify(llvm::Module *module) {\n  const bool broken = llvm::verifyModule(*module, &llvm::errs());\n  if (broken) {\n    auto fo = fopen(\"_dump.ll\", \"w\");\n    llvm::raw_fd_ostream fout(fileno(fo), true);\n    fout << *module;\n    fout.close();\n  }\n  seqassertn(!broken, \"Generated LLVM IR is invalid and has been dumped to '_dump.ll'. \"\n                      \"Please submit a bug report at https://github.com/exaloop/codon \"\n                      \"including the code and generated LLVM IR.\");\n}\n\n} // namespace\n\nvoid optimize(llvm::Module *module, bool debug, bool jit, PluginManager *plugins) {\n  verify(module);\n  {\n    TIME(\"llvm/opt1\");\n    runLLVMOptimizationPasses(module, debug, jit, plugins);\n  }\n  if (!debug) {\n    TIME(\"llvm/opt2\");\n    runLLVMOptimizationPasses(module, debug, jit, plugins);\n  }\n  {\n    TIME(\"llvm/gpu\");\n    applyGPUTransformations(module);\n  }\n  verify(module);\n}\n\nbool isFastMathOn() { return FastMath; }\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/llvm/optimize.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n\n#include \"codon/cir/llvm/llvm.h\"\n#include \"codon/dsl/plugins.h\"\n\nnamespace codon {\nnamespace ir {\nstd::unique_ptr<llvm::TargetMachine>\ngetTargetMachine(llvm::Triple triple, llvm::StringRef cpuStr,\n                 llvm::StringRef featuresStr, const llvm::TargetOptions &options,\n                 bool pic = false);\n\nstd::unique_ptr<llvm::TargetMachine>\ngetTargetMachine(llvm::Module *module, bool setFunctionAttributes = false,\n                 bool pic = false);\n\nvoid optimize(llvm::Module *module, bool debug, bool jit = false,\n              PluginManager *plugins = nullptr);\n\nbool isFastMathOn();\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/module.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"module.h\"\n\n#include <algorithm>\n#include <memory>\n\n#include \"codon/cir/func.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace {\nstd::vector<codon::ast::types::TypePtr>\ntranslateGenerics(codon::ast::Cache *cache, std::vector<types::Generic> &generics) {\n  std::vector<codon::ast::types::TypePtr> ret;\n  for (auto &g : generics) {\n    seqassertn(g.isStatic() || g.getTypeValue(), \"generic must be static or a type\");\n    if (g.isStaticStr())\n      ret.push_back(std::make_shared<codon::ast::types::LinkType>(\n          std::make_shared<codon::ast::types::StrStaticType>(\n              cache, g.getStaticStringValue())));\n    else if (g.isStatic())\n      ret.push_back(std::make_shared<codon::ast::types::LinkType>(\n          std::make_shared<codon::ast::types::IntStaticType>(cache,\n                                                             g.getStaticValue())));\n    else\n      ret.push_back(std::make_shared<codon::ast::types::LinkType>(\n          g.getTypeValue()->getAstType()));\n  }\n  return ret;\n}\n\nstd::vector<codon::ast::types::Type *>\ngenerateDummyNames(std::vector<types::Type *> &types) {\n  std::vector<codon::ast::types::Type *> ret;\n  for (auto *t : types) {\n    seqassertn(t->getAstType(), \"{} must have an ast type\", *t);\n    ret.emplace_back(t->getAstType().get());\n  }\n  return ret;\n}\n\nstd::vector<codon::ast::types::TypePtr>\ntranslateArgs(codon::ast::Cache *cache, std::vector<types::Type *> &types) {\n  std::vector<codon::ast::types::TypePtr> ret = {\n      std::make_shared<codon::ast::types::LinkType>(\n          cache, codon::ast::types::LinkType::Kind::Unbound, 0)};\n  for (auto *t : types) {\n    seqassertn(t->getAstType(), \"{} must have an ast type\", *t);\n    if (auto f = t->getAstType()->getFunc()) {\n      auto *irType = cast<types::FuncType>(t);\n      std::vector<char> mask(std::distance(irType->begin(), irType->end()), 0);\n      ret.push_back(t->getAstType());\n    } else {\n      ret.push_back(t->getAstType());\n    }\n  }\n  return ret;\n}\n} // namespace\n\nconst std::string Module::VOID_NAME = \"void\";\nconst std::string Module::BOOL_NAME = \"bool\";\nconst std::string Module::BYTE_NAME = \"byte\";\nconst std::string Module::INT_NAME = \"int\";\nconst std::string Module::FLOAT_NAME = \"float\";\nconst std::string Module::FLOAT32_NAME = \"float32\";\nconst std::string Module::FLOAT16_NAME = \"float16\";\nconst std::string Module::BFLOAT16_NAME = \"bfloat16\";\nconst std::string Module::FLOAT128_NAME = \"float128\";\nconst std::string Module::STRING_NAME = \"str\";\n\nconst std::string Module::EQ_MAGIC_NAME = \"__eq__\";\nconst std::string Module::NE_MAGIC_NAME = \"__ne__\";\nconst std::string Module::LT_MAGIC_NAME = \"__lt__\";\nconst std::string Module::GT_MAGIC_NAME = \"__gt__\";\nconst std::string Module::LE_MAGIC_NAME = \"__le__\";\nconst std::string Module::GE_MAGIC_NAME = \"__ge__\";\n\nconst std::string Module::POS_MAGIC_NAME = \"__pos__\";\nconst std::string Module::NEG_MAGIC_NAME = \"__neg__\";\nconst std::string Module::INVERT_MAGIC_NAME = \"__invert__\";\nconst std::string Module::ABS_MAGIC_NAME = \"__abs__\";\n\nconst std::string Module::ADD_MAGIC_NAME = \"__add__\";\nconst std::string Module::SUB_MAGIC_NAME = \"__sub__\";\nconst std::string Module::MUL_MAGIC_NAME = \"__mul__\";\nconst std::string Module::MATMUL_MAGIC_NAME = \"__matmul__\";\nconst std::string Module::TRUE_DIV_MAGIC_NAME = \"__truediv__\";\nconst std::string Module::FLOOR_DIV_MAGIC_NAME = \"__floordiv__\";\nconst std::string Module::MOD_MAGIC_NAME = \"__mod__\";\nconst std::string Module::POW_MAGIC_NAME = \"__pow__\";\nconst std::string Module::LSHIFT_MAGIC_NAME = \"__lshift__\";\nconst std::string Module::RSHIFT_MAGIC_NAME = \"__rshift__\";\nconst std::string Module::AND_MAGIC_NAME = \"__and__\";\nconst std::string Module::OR_MAGIC_NAME = \"__or__\";\nconst std::string Module::XOR_MAGIC_NAME = \"__xor__\";\n\nconst std::string Module::IADD_MAGIC_NAME = \"__iadd__\";\nconst std::string Module::ISUB_MAGIC_NAME = \"__isub__\";\nconst std::string Module::IMUL_MAGIC_NAME = \"__imul__\";\nconst std::string Module::IMATMUL_MAGIC_NAME = \"__imatmul__\";\nconst std::string Module::ITRUE_DIV_MAGIC_NAME = \"__itruediv__\";\nconst std::string Module::IFLOOR_DIV_MAGIC_NAME = \"__ifloordiv__\";\nconst std::string Module::IMOD_MAGIC_NAME = \"__imod__\";\nconst std::string Module::IPOW_MAGIC_NAME = \"__ipow__\";\nconst std::string Module::ILSHIFT_MAGIC_NAME = \"__ilshift__\";\nconst std::string Module::IRSHIFT_MAGIC_NAME = \"__irshift__\";\nconst std::string Module::IAND_MAGIC_NAME = \"__iand__\";\nconst std::string Module::IOR_MAGIC_NAME = \"__ior__\";\nconst std::string Module::IXOR_MAGIC_NAME = \"__ixor__\";\n\nconst std::string Module::RADD_MAGIC_NAME = \"__radd__\";\nconst std::string Module::RSUB_MAGIC_NAME = \"__rsub__\";\nconst std::string Module::RMUL_MAGIC_NAME = \"__rmul__\";\nconst std::string Module::RMATMUL_MAGIC_NAME = \"__rmatmul__\";\nconst std::string Module::RTRUE_DIV_MAGIC_NAME = \"__rtruediv__\";\nconst std::string Module::RFLOOR_DIV_MAGIC_NAME = \"__rfloordiv__\";\nconst std::string Module::RMOD_MAGIC_NAME = \"__rmod__\";\nconst std::string Module::RPOW_MAGIC_NAME = \"__rpow__\";\nconst std::string Module::RLSHIFT_MAGIC_NAME = \"__rlshift__\";\nconst std::string Module::RRSHIFT_MAGIC_NAME = \"__rrshift__\";\nconst std::string Module::RAND_MAGIC_NAME = \"__rand__\";\nconst std::string Module::ROR_MAGIC_NAME = \"__ror__\";\nconst std::string Module::RXOR_MAGIC_NAME = \"__rxor__\";\n\nconst std::string Module::INT_MAGIC_NAME = \"__int__\";\nconst std::string Module::FLOAT_MAGIC_NAME = \"__float__\";\nconst std::string Module::BOOL_MAGIC_NAME = \"__bool__\";\nconst std::string Module::STR_MAGIC_NAME = \"__str__\";\nconst std::string Module::REPR_MAGIC_NAME = \"__repr__\";\nconst std::string Module::CALL_MAGIC_NAME = \"__call__\";\n\nconst std::string Module::GETITEM_MAGIC_NAME = \"__getitem__\";\nconst std::string Module::SETITEM_MAGIC_NAME = \"__setitem__\";\nconst std::string Module::ITER_MAGIC_NAME = \"__iter__\";\nconst std::string Module::LEN_MAGIC_NAME = \"__len__\";\n\nconst std::string Module::NEW_MAGIC_NAME = \"__new__\";\nconst std::string Module::INIT_MAGIC_NAME = \"__init__\";\n\nconst char Module::NodeId = 0;\n\nModule::Module(const std::string &name) : AcceptorExtend(name) {\n  mainFunc = std::make_unique<BodiedFunc>(\"main\");\n  mainFunc->realize(cast<types::FuncType>(unsafeGetDummyFuncType()), {});\n  mainFunc->setModule(this);\n  mainFunc->setReplaceable(false);\n  argVar = std::make_unique<Var>(unsafeGetArrayType(getStringType()), /*global=*/true,\n                                 /*external=*/false, /*tls=*/false, \".argv\");\n  argVar->setModule(this);\n  argVar->setReplaceable(false);\n}\n\nvoid Module::parseCode(const std::string &code) { cache->parseCode(code); }\n\nFunc *Module::getOrRealizeMethod(types::Type *parent, const std::string &methodName,\n                                 std::vector<types::Type *> args,\n                                 std::vector<types::Generic> generics) {\n\n  auto cls =\n      std::const_pointer_cast<ast::types::Type>(parent->getAstType())->getClass();\n  auto method = cache->findMethod(cls, methodName, generateDummyNames(args));\n  if (!method)\n    return nullptr;\n  try {\n    return cache->realizeFunction(method, translateArgs(cache, args),\n                                  translateGenerics(cache, generics), cls);\n  } catch (const exc::ParserException &e) {\n    for (auto &trace : e.getErrors())\n      for (auto &msg : trace)\n        LOG_IR(\"getOrRealizeMethod parser error at {}: {}\", msg.getSrcInfo(),\n               msg.getMessage());\n    return nullptr;\n  }\n}\n\nFunc *Module::getOrRealizeFunc(const std::string &funcName,\n                               std::vector<types::Type *> args,\n                               std::vector<types::Generic> generics,\n                               const std::string &module) {\n  auto fqName =\n      module.empty() ? funcName : fmt::format(FMT_STRING(\"{}.{}\"), module, funcName);\n  auto func = cache->findFunction(fqName);\n  if (!func)\n    func = cache->findFunction(fqName + \".0\");\n  if (!func)\n    return nullptr;\n  auto arg = translateArgs(cache, args);\n  auto gens = translateGenerics(cache, generics);\n  try {\n    return cache->realizeFunction(func, arg, gens);\n  } catch (const exc::ParserException &e) {\n    for (auto &trace : e.getErrors())\n      for (auto &msg : trace)\n        LOG(\"getOrRealizeFunc parser error at {}: {}\", msg.getSrcInfo(),\n            msg.getMessage());\n    return nullptr;\n  }\n}\n\ntypes::Type *Module::getOrRealizeType(const std::string &typeName,\n                                      std::vector<types::Generic> generics) {\n  auto type = cache->findClass(typeName);\n  if (!type)\n    return nullptr;\n  try {\n    return cache->realizeType(type, translateGenerics(cache, generics));\n  } catch (const exc::ParserException &e) {\n    for (auto &trace : e.getErrors())\n      for (auto &msg : trace)\n        LOG_IR(\"getOrRealizeType parser error at {}: {}\", msg.getSrcInfo(),\n               msg.getMessage());\n    return nullptr;\n  }\n}\n\ntypes::Type *Module::getVoidType() {\n  if (auto *rVal = getType(VOID_NAME))\n    return rVal;\n  return Nr<types::VoidType>();\n}\n\ntypes::Type *Module::getBoolType() {\n  if (auto *rVal = getType(BOOL_NAME))\n    return rVal;\n  return Nr<types::BoolType>();\n}\n\ntypes::Type *Module::getByteType() {\n  if (auto *rVal = getType(BYTE_NAME))\n    return rVal;\n  return Nr<types::ByteType>();\n}\n\ntypes::Type *Module::getIntType() {\n  if (auto *rVal = getType(INT_NAME))\n    return rVal;\n  return Nr<types::IntType>();\n}\n\ntypes::Type *Module::getFloatType() {\n  if (auto *rVal = getType(FLOAT_NAME))\n    return rVal;\n  return Nr<types::FloatType>();\n}\n\ntypes::Type *Module::getFloat32Type() {\n  if (auto *rVal = getType(FLOAT32_NAME))\n    return rVal;\n  return Nr<types::Float32Type>();\n}\n\ntypes::Type *Module::getFloat16Type() {\n  if (auto *rVal = getType(FLOAT16_NAME))\n    return rVal;\n  return Nr<types::Float16Type>();\n}\n\ntypes::Type *Module::getBFloat16Type() {\n  if (auto *rVal = getType(BFLOAT16_NAME))\n    return rVal;\n  return Nr<types::BFloat16Type>();\n}\n\ntypes::Type *Module::getFloat128Type() {\n  if (auto *rVal = getType(FLOAT128_NAME))\n    return rVal;\n  return Nr<types::Float128Type>();\n}\n\ntypes::Type *Module::getStringType() {\n  if (auto *rVal = getType(STRING_NAME))\n    return rVal;\n  return Nr<types::RecordType>(\n      STRING_NAME,\n      std::vector<types::Type *>{getIntType(), unsafeGetPointerType(getByteType())},\n      std::vector<std::string>{\"len\", \"ptr\"});\n}\n\ntypes::Type *Module::getPointerType(types::Type *base) {\n  return getOrRealizeType(\"Ptr\", {base});\n}\n\ntypes::Type *Module::getArrayType(types::Type *base) {\n  return getOrRealizeType(\"Array\", {base});\n}\n\ntypes::Type *Module::getGeneratorType(types::Type *base) {\n  return getOrRealizeType(\"Generator\", {base});\n}\n\ntypes::Type *Module::getOptionalType(types::Type *base) {\n  return getOrRealizeType(\"Optional\", {base});\n}\n\ntypes::Type *Module::getFuncType(types::Type *rType,\n                                 std::vector<types::Type *> argTypes, bool variadic) {\n  auto args = translateArgs(cache, argTypes);\n  args[0] = std::make_shared<codon::ast::types::LinkType>(rType->getAstType());\n  auto *result = cache->makeFunction(args);\n  if (variadic) {\n    // Type checker types have no concept of variadic functions, so we will\n    // create a new IR type here with the same AST type.\n    auto *f = cast<types::FuncType>(result);\n    result = unsafeGetFuncType(f->getName() + \"$variadic\", f->getReturnType(),\n                               std::vector<types::Type *>(f->begin(), f->end()),\n                               /*variadic=*/true);\n    result->setAstType(f->getAstType());\n  }\n  return result;\n}\n\ntypes::Type *Module::getIntNType(unsigned int len, bool sign) {\n  return getOrRealizeType(sign ? \"Int\" : \"UInt\", {len});\n}\n\ntypes::Type *Module::getVectorType(unsigned count, types::Type *base) {\n  return getOrRealizeType(ast::getMangledClass(\"std.simd\", \"Vec\"), {base, count});\n}\n\ntypes::Type *Module::getTupleType(std::vector<types::Type *> args) {\n  std::vector<ast::types::TypePtr> argTypes;\n  for (auto *t : args) {\n    seqassertn(t->getAstType(), \"{} must have an ast type\", *t);\n    argTypes.push_back(t->getAstType());\n  }\n  return cache->makeTuple(argTypes);\n}\n\ntypes::Type *Module::getUnionType(std::vector<types::Type *> types) {\n  std::vector<ast::types::TypePtr> argTypes;\n  for (auto *t : types) {\n    seqassertn(t->getAstType(), \"{} must have an ast type\", *t);\n    argTypes.push_back(t->getAstType());\n  }\n  return cache->makeUnion(argTypes);\n}\n\ntypes::Type *Module::getNoneType() { return getOrRealizeType(\"NoneType\"); }\n\nValue *Module::getInt(int64_t v) { return Nr<IntConst>(v, getIntType()); }\n\nValue *Module::getFloat(double v) { return Nr<FloatConst>(v, getFloatType()); }\n\nValue *Module::getBool(bool v) { return Nr<BoolConst>(v, getBoolType()); }\n\nValue *Module::getString(std::string v) {\n  return Nr<StringConst>(std::move(v), getStringType());\n}\n\ntypes::Type *Module::unsafeGetDummyFuncType() {\n  return unsafeGetFuncType(\"<internal_func_type>\", getVoidType(), {});\n}\n\ntypes::Type *Module::unsafeGetPointerType(types::Type *base) {\n  auto name = types::PointerType::getInstanceName(base);\n  if (auto *rVal = getType(name))\n    return rVal;\n  return Nr<types::PointerType>(base);\n}\n\ntypes::Type *Module::unsafeGetArrayType(types::Type *base) {\n  auto name = fmt::format(FMT_STRING(\".Array[{}]\"), base->referenceString());\n  if (auto *rVal = getType(name))\n    return rVal;\n  std::vector<types::Type *> members = {getIntType(), unsafeGetPointerType(base)};\n  std::vector<std::string> names = {\"len\", \"ptr\"};\n  return Nr<types::RecordType>(name, members, names);\n}\n\ntypes::Type *Module::unsafeGetGeneratorType(types::Type *base) {\n  auto name = types::GeneratorType::getInstanceName(base);\n  if (auto *rVal = getType(name))\n    return rVal;\n  return Nr<types::GeneratorType>(base);\n}\n\ntypes::Type *Module::unsafeGetOptionalType(types::Type *base) {\n  auto name = types::OptionalType::getInstanceName(base);\n  if (auto *rVal = getType(name))\n    return rVal;\n  return Nr<types::OptionalType>(base);\n}\n\ntypes::Type *Module::unsafeGetFuncType(const std::string &name, types::Type *rType,\n                                       std::vector<types::Type *> argTypes,\n                                       bool variadic) {\n  if (auto *rVal = getType(name))\n    return rVal;\n  return Nr<types::FuncType>(name, rType, std::move(argTypes), variadic);\n}\n\ntypes::Type *Module::unsafeGetMemberedType(const std::string &name, bool ref) {\n  auto *rVal = getType(name);\n\n  if (!rVal) {\n    if (ref) {\n      auto contentName = name + \".contents\";\n      auto *record = getType(contentName);\n      if (!record) {\n        record = Nr<types::RecordType>(contentName);\n      }\n      rVal = Nr<types::RefType>(name, cast<types::RecordType>(record));\n    } else {\n      rVal = Nr<types::RecordType>(name);\n    }\n  }\n\n  return rVal;\n}\n\ntypes::Type *Module::unsafeGetIntNType(unsigned int len, bool sign) {\n  auto name = types::IntNType::getInstanceName(len, sign);\n  if (auto *rVal = getType(name))\n    return rVal;\n  return Nr<types::IntNType>(len, sign);\n}\n\ntypes::Type *Module::unsafeGetVectorType(unsigned int count, types::Type *base) {\n  auto *primitive = cast<types::PrimitiveType>(base);\n  auto name = types::VectorType::getInstanceName(count, primitive);\n  if (auto *rVal = getType(name))\n    return rVal;\n  seqassertn(primitive, \"base type must be a primitive type\");\n  return Nr<types::VectorType>(count, primitive);\n}\n\ntypes::Type *Module::unsafeGetUnionType(const std::vector<types::Type *> &types) {\n  auto name = types::UnionType::getInstanceName(types);\n  if (auto *rVal = getType(name))\n    return rVal;\n  return Nr<types::UnionType>(types);\n}\n\nvoid Module::pushArena() { arenas.emplace_back(); }\n\nvoid Module::popArena() {\n  auto &arena = arenas.back();\n  for (auto id : arena.values) {\n    auto it = valueMap.find(id);\n    if (it == valueMap.end())\n      continue;\n    values.erase(it->second);\n    valueMap.erase(it);\n  }\n  for (auto id : arena.vars) {\n    auto it = varMap.find(id);\n    if (it == varMap.end())\n      continue;\n    vars.erase(it->second);\n    varMap.erase(it);\n  }\n  for (auto &type : arena.types) {\n    auto it = typesMap.find(type);\n    if (it == typesMap.end())\n      continue;\n    types.erase(it->second);\n    typesMap.erase(it);\n  }\n  arenas.pop_back();\n}\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/module.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <iterator>\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"codon/cir/func.h\"\n#include \"codon/cir/util/iterators.h\"\n#include \"codon/cir/value.h\"\n#include \"codon/cir/var.h\"\n#include \"codon/util/common.h\"\n#include <fmt/format.h>\n#include <fmt/ostream.h>\n\nnamespace codon {\n\nnamespace ast {\nstruct Cache;\nclass TranslateVisitor;\nclass TypecheckVisitor;\n} // namespace ast\n\nnamespace ir {\n\n/// CIR object representing a program.\nclass Module : public AcceptorExtend<Module, Node> {\npublic:\n  static const std::string VOID_NAME;\n  static const std::string BOOL_NAME;\n  static const std::string BYTE_NAME;\n  static const std::string INT_NAME;\n  static const std::string FLOAT_NAME;\n  static const std::string FLOAT32_NAME;\n  static const std::string FLOAT16_NAME;\n  static const std::string BFLOAT16_NAME;\n  static const std::string FLOAT128_NAME;\n  static const std::string STRING_NAME;\n\n  static const std::string EQ_MAGIC_NAME;\n  static const std::string NE_MAGIC_NAME;\n  static const std::string LT_MAGIC_NAME;\n  static const std::string GT_MAGIC_NAME;\n  static const std::string LE_MAGIC_NAME;\n  static const std::string GE_MAGIC_NAME;\n\n  static const std::string POS_MAGIC_NAME;\n  static const std::string NEG_MAGIC_NAME;\n  static const std::string INVERT_MAGIC_NAME;\n  static const std::string ABS_MAGIC_NAME;\n\n  static const std::string ADD_MAGIC_NAME;\n  static const std::string SUB_MAGIC_NAME;\n  static const std::string MUL_MAGIC_NAME;\n  static const std::string MATMUL_MAGIC_NAME;\n  static const std::string TRUE_DIV_MAGIC_NAME;\n  static const std::string FLOOR_DIV_MAGIC_NAME;\n  static const std::string MOD_MAGIC_NAME;\n  static const std::string POW_MAGIC_NAME;\n  static const std::string LSHIFT_MAGIC_NAME;\n  static const std::string RSHIFT_MAGIC_NAME;\n  static const std::string AND_MAGIC_NAME;\n  static const std::string OR_MAGIC_NAME;\n  static const std::string XOR_MAGIC_NAME;\n\n  static const std::string IADD_MAGIC_NAME;\n  static const std::string ISUB_MAGIC_NAME;\n  static const std::string IMUL_MAGIC_NAME;\n  static const std::string IMATMUL_MAGIC_NAME;\n  static const std::string ITRUE_DIV_MAGIC_NAME;\n  static const std::string IFLOOR_DIV_MAGIC_NAME;\n  static const std::string IMOD_MAGIC_NAME;\n  static const std::string IPOW_MAGIC_NAME;\n  static const std::string ILSHIFT_MAGIC_NAME;\n  static const std::string IRSHIFT_MAGIC_NAME;\n  static const std::string IAND_MAGIC_NAME;\n  static const std::string IOR_MAGIC_NAME;\n  static const std::string IXOR_MAGIC_NAME;\n\n  static const std::string RADD_MAGIC_NAME;\n  static const std::string RSUB_MAGIC_NAME;\n  static const std::string RMUL_MAGIC_NAME;\n  static const std::string RMATMUL_MAGIC_NAME;\n  static const std::string RTRUE_DIV_MAGIC_NAME;\n  static const std::string RFLOOR_DIV_MAGIC_NAME;\n  static const std::string RMOD_MAGIC_NAME;\n  static const std::string RPOW_MAGIC_NAME;\n  static const std::string RLSHIFT_MAGIC_NAME;\n  static const std::string RRSHIFT_MAGIC_NAME;\n  static const std::string RAND_MAGIC_NAME;\n  static const std::string ROR_MAGIC_NAME;\n  static const std::string RXOR_MAGIC_NAME;\n\n  static const std::string INT_MAGIC_NAME;\n  static const std::string FLOAT_MAGIC_NAME;\n  static const std::string BOOL_MAGIC_NAME;\n  static const std::string STR_MAGIC_NAME;\n  static const std::string REPR_MAGIC_NAME;\n  static const std::string CALL_MAGIC_NAME;\n\n  static const std::string GETITEM_MAGIC_NAME;\n  static const std::string SETITEM_MAGIC_NAME;\n  static const std::string ITER_MAGIC_NAME;\n  static const std::string LEN_MAGIC_NAME;\n\n  static const std::string NEW_MAGIC_NAME;\n  static const std::string INIT_MAGIC_NAME;\n\nprivate:\n  struct Arena {\n    std::vector<id_t> values;\n    std::vector<id_t> vars;\n    std::vector<std::string> types;\n  };\n\n  /// the module's \"main\" function\n  std::unique_ptr<Func> mainFunc;\n  /// the module's argv variable\n  std::unique_ptr<Var> argVar;\n  /// the global variables list\n  std::list<std::unique_ptr<Var>> vars;\n  /// the global variables map\n  std::unordered_map<id_t, std::list<std::unique_ptr<Var>>::iterator> varMap;\n  /// the global value list\n  std::list<std::unique_ptr<Value>> values;\n  /// the global value map\n  std::unordered_map<id_t, std::list<std::unique_ptr<Value>>::iterator> valueMap;\n  /// the global types list\n  std::list<std::unique_ptr<types::Type>> types;\n  /// the global types map\n  std::unordered_map<std::string, std::list<std::unique_ptr<types::Type>>::iterator>\n      typesMap;\n  /// the arena stack\n  std::vector<Arena> arenas;\n\n  /// the type-checker cache\n  ast::Cache *cache = nullptr;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs an CIR module.\n  /// @param name the module name\n  explicit Module(const std::string &name = \"\");\n\n  virtual ~Module() noexcept = default;\n\n  /// @return the main function\n  Func *getMainFunc() { return mainFunc.get(); }\n  /// @return the main function\n  const Func *getMainFunc() const { return mainFunc.get(); }\n\n  /// @return the arg var\n  Var *getArgVar() { return argVar.get(); }\n  /// @return the arg var\n  const Var *getArgVar() const { return argVar.get(); }\n\n  /// @return iterator to the first symbol\n  auto begin() { return util::raw_ptr_adaptor(vars.begin()); }\n  /// @return iterator beyond the last symbol\n  auto end() { return util::raw_ptr_adaptor(vars.end()); }\n  /// @return iterator to the first symbol\n  auto begin() const { return util::const_raw_ptr_adaptor(vars.begin()); }\n  /// @return iterator beyond the last symbol\n  auto end() const { return util::const_raw_ptr_adaptor(vars.end()); }\n  /// @return a pointer to the first symbol\n  Var *front() { return vars.front().get(); }\n  /// @return a pointer to the last symbol\n  Var *back() { return vars.back().get(); }\n  /// @return a pointer to the first symbol\n  const Var *front() const { return vars.front().get(); }\n  /// @return a pointer to the last symbol\n  const Var *back() const { return vars.back().get(); }\n  /// Gets a var by id.\n  /// @param id the id\n  /// @return the variable or nullptr\n  Var *getVar(id_t id) {\n    auto it = varMap.find(id);\n    return it != varMap.end() ? it->second->get() : nullptr;\n  }\n  /// Gets a var by id.\n  /// @param id the id\n  /// @return the variable or nullptr\n  const Var *getVar(id_t id) const {\n    auto it = varMap.find(id);\n    return it != varMap.end() ? it->second->get() : nullptr;\n  }\n  /// Removes a given var.\n  /// @param v the var\n  void remove(const Var *v) {\n    auto it = varMap.find(v->getId());\n    vars.erase(it->second);\n    varMap.erase(it);\n  }\n\n  /// @return iterator to the first value\n  auto values_begin() { return util::raw_ptr_adaptor(values.begin()); }\n  /// @return iterator beyond the last value\n  auto values_end() { return util::raw_ptr_adaptor(values.end()); }\n  /// @return iterator to the first value\n  auto values_begin() const { return util::const_raw_ptr_adaptor(values.begin()); }\n  /// @return iterator beyond the last value\n  auto values_end() const { return util::const_raw_ptr_adaptor(values.end()); }\n  /// @return a pointer to the first value\n  Value *values_front() { return values.front().get(); }\n  /// @return a pointer to the last value\n  Value *values_back() { return values.back().get(); }\n  /// @return a pointer to the first value\n  const Value *values_front() const { return values.front().get(); }\n  /// @return a pointer to the last value\n  const Value *values_back() const { return values.back().get(); }\n  /// Gets a value by id.\n  /// @param id the id\n  /// @return the value or nullptr\n  Value *getValue(id_t id) {\n    auto it = valueMap.find(id);\n    return it != valueMap.end() ? it->second->get() : nullptr;\n  }\n  /// Gets a value by id.\n  /// @param id the id\n  /// @return the value or nullptr\n  const Value *getValue(id_t id) const {\n    auto it = valueMap.find(id);\n    return it != valueMap.end() ? it->second->get() : nullptr;\n  }\n  /// Removes a given value.\n  /// @param v the value\n  void remove(const Value *v) {\n    auto it = valueMap.find(v->getId());\n    values.erase(it->second);\n    valueMap.erase(it);\n  }\n\n  /// @return iterator to the first type\n  auto types_begin() { return util::raw_ptr_adaptor(types.begin()); }\n  /// @return iterator beyond the last type\n  auto types_end() { return util::raw_ptr_adaptor(types.end()); }\n  /// @return iterator to the first type\n  auto types_begin() const { return util::const_raw_ptr_adaptor(types.begin()); }\n  /// @return iterator beyond the last type\n  auto types_end() const { return util::const_raw_ptr_adaptor(types.end()); }\n  /// @return a pointer to the first type\n  types::Type *types_front() const { return types.front().get(); }\n  /// @return a pointer to the last type\n  types::Type *types_back() const { return types.back().get(); }\n  /// @param name the type's name\n  /// @return the type with the given name\n  types::Type *getType(const std::string &name) {\n    auto it = typesMap.find(name);\n    return it == typesMap.end() ? nullptr : it->second->get();\n  }\n  /// @param name the type's name\n  /// @return the type with the given name\n  types::Type *getType(const std::string &name) const {\n    auto it = typesMap.find(name);\n    return it == typesMap.end() ? nullptr : it->second->get();\n  }\n  /// Removes a given type.\n  /// @param t the type\n  void remove(types::Type *t) {\n    auto it = typesMap.find(t->getName());\n    types.erase(it->second);\n    typesMap.erase(it);\n  }\n\n  /// Constructs and registers an IR node with provided source information.\n  /// @param s the source information\n  /// @param args the arguments\n  /// @return the new node\n  template <typename DesiredType, typename... Args>\n  DesiredType *N(codon::SrcInfo s, Args &&...args) {\n    auto *ret = new DesiredType(std::forward<Args>(args)...);\n    ret->setModule(this);\n    ret->setSrcInfo(s);\n\n    store(ret);\n    return ret;\n  }\n  /// Constructs and registers an IR node with provided source node.\n  /// @param s the source node\n  /// @param args the arguments\n  /// @return the new node\n  template <typename DesiredType, typename... Args>\n  DesiredType *N(const codon::SrcObject *s, Args &&...args) {\n    return N<DesiredType>(s->getSrcInfo(), std::forward<Args>(args)...);\n  }\n  /// Constructs and registers an IR node with provided source node.\n  /// @param s the source node\n  /// @param args the arguments\n  /// @return the new node\n  template <typename DesiredType, typename... Args>\n  DesiredType *N(const Node *s, Args &&...args) {\n    return N<DesiredType>(s->getSrcInfo(), std::forward<Args>(args)...);\n  }\n  /// Constructs and registers an IR node with no source information.\n  /// @param args the arguments\n  /// @return the new node\n  template <typename DesiredType, typename... Args> DesiredType *Nr(Args &&...args) {\n    return N<DesiredType>(codon::SrcInfo(), std::forward<Args>(args)...);\n  }\n\n  /// @return the type-checker cache\n  ast::Cache *getCache() const { return cache; }\n  /// Sets the type-checker cache.\n  /// @param c the cache\n  void setCache(ast::Cache *c) { cache = c; }\n\n  /// Parse a codon code block.\n  void parseCode(const std::string &code);\n\n  /// Gets or realizes a method.\n  /// @param parent the parent class\n  /// @param methodName the method name\n  /// @param args the argument types\n  /// @param generics the generics\n  /// @return the method or nullptr\n  Func *getOrRealizeMethod(types::Type *parent, const std::string &methodName,\n                           std::vector<types::Type *> args,\n                           std::vector<types::Generic> generics = {});\n\n  /// Gets or realizes a function.\n  /// @param funcName the function name\n  /// @param args the argument types\n  /// @param generics the generics\n  /// @param module the module of the function\n  /// @return the function or nullptr\n  Func *getOrRealizeFunc(const std::string &funcName, std::vector<types::Type *> args,\n                         std::vector<types::Generic> generics = {},\n                         const std::string &module = \"\");\n\n  /// Gets or realizes a type.\n  /// @param typeName the type name\n  /// @param generics the generics\n  /// @param module the module of the type\n  /// @return the function or nullptr\n  types::Type *getOrRealizeType(const std::string &typeName,\n                                std::vector<types::Generic> generics = {});\n\n  /// @return the void type\n  types::Type *getVoidType();\n  /// @return the bool type\n  types::Type *getBoolType();\n  /// @return the byte type\n  types::Type *getByteType();\n  /// @return the int type\n  types::Type *getIntType();\n  /// @return the float type\n  types::Type *getFloatType();\n  /// @return the float32 type\n  types::Type *getFloat32Type();\n  /// @return the float16 type\n  types::Type *getFloat16Type();\n  /// @return the bfloat16 type\n  types::Type *getBFloat16Type();\n  /// @return the float128 type\n  types::Type *getFloat128Type();\n  /// @return the string type\n  types::Type *getStringType();\n  /// Gets a pointer type.\n  /// @param base the base type\n  /// @return a pointer type that references the base\n  types::Type *getPointerType(types::Type *base);\n  /// Gets an array type.\n  /// @param base the base type\n  /// @return an array type that contains the base\n  types::Type *getArrayType(types::Type *base);\n  /// Gets a generator type.\n  /// @param base the base type\n  /// @return a generator type that yields the base\n  types::Type *getGeneratorType(types::Type *base);\n  /// Gets an optional type.\n  /// @param base the base type\n  /// @return an optional type that contains the base\n  types::Type *getOptionalType(types::Type *base);\n  /// Gets a function type.\n  /// @param rType the return type\n  /// @param argTypes the argument types\n  /// @param variadic true if variadic (e.g. \"printf\" in C)\n  /// @return the void type\n  types::Type *getFuncType(types::Type *rType, std::vector<types::Type *> argTypes,\n                           bool variadic = false);\n  /// Gets a variable length integer type.\n  /// @param len the length\n  /// @param sign true if signed\n  /// @return a variable length integer type\n  types::Type *getIntNType(unsigned len, bool sign);\n  /// Gets a vector type.\n  /// @param count the vector size\n  /// @param base the vector base type (MUST be a primitive type)\n  /// @return a vector type\n  types::Type *getVectorType(unsigned count, types::Type *base);\n  /// Gets a tuple type.\n  /// @param args the arg types\n  /// @return the tuple type\n  types::Type *getTupleType(std::vector<types::Type *> args);\n  /// Gets a union type.\n  /// @param types the alternative types\n  /// @return the union type\n  types::Type *getUnionType(std::vector<types::Type *> types);\n  /// Gets the \"none\" type (i.e. empty tuple).\n  /// @return none type\n  types::Type *getNoneType();\n\n  /// @param v the value\n  /// @return an int constant\n  Value *getInt(int64_t v);\n  /// @param v the value\n  /// @return a float constant\n  Value *getFloat(double v);\n  /// @param v the value\n  /// @return a bool constant\n  Value *getBool(bool v);\n  /// @param v the value\n  /// @return a string constant\n  Value *getString(std::string v);\n\n  /// Gets a dummy function type. Should generally not be used as no type-checker\n  /// information is generated.\n  /// @return a func type with no args and void return type.\n  types::Type *unsafeGetDummyFuncType();\n  /// Gets a pointer type. Should generally not be used as no type-checker\n  /// information is generated.\n  /// @param base the base type\n  /// @return a pointer type that references the base\n  types::Type *unsafeGetPointerType(types::Type *base);\n  /// Gets an array type. Should generally not be used as no type-checker\n  /// information is generated.\n  /// @param base the base type\n  /// @return an array type that contains the base\n  types::Type *unsafeGetArrayType(types::Type *base);\n  /// Gets a generator type. Should generally not be used as no type-checker\n  /// information is generated.\n  /// @param base the base type\n  /// @return a generator type that yields the base\n  types::Type *unsafeGetGeneratorType(types::Type *base);\n  /// Gets an optional type. Should generally not be used as no type-checker\n  /// information is generated.\n  /// @param base the base type\n  /// @return an optional type that contains the base\n  types::Type *unsafeGetOptionalType(types::Type *base);\n  /// Gets a function type. Should generally not be used as no type-checker\n  /// information is generated.\n  /// @param rType the return type\n  /// @param argTypes the argument types\n  /// @param variadic true if variadic (e.g. \"printf\" in C)\n  /// @return the void type\n  types::Type *unsafeGetFuncType(const std::string &name, types::Type *rType,\n                                 std::vector<types::Type *> argTypes,\n                                 bool variadic = false);\n  /// Gets a membered type. Should generally not be used as no type-checker\n  /// information is generated.\n  /// @param name the type's name\n  /// @param ref whether the type should be a ref\n  /// @return an empty membered/ref type\n  types::Type *unsafeGetMemberedType(const std::string &name, bool ref = false);\n  /// Gets a variable length integer type. Should generally not be used as no\n  /// type-checker information is generated.\n  /// @param len the length\n  /// @param sign true if signed\n  /// @return a variable length integer type\n  types::Type *unsafeGetIntNType(unsigned len, bool sign);\n  /// Gets a vector type. Should generally not be used as no\n  /// type-checker information is generated.\n  /// @param count the vector size\n  /// @param base the vector base type (MUST be a primitive type)\n  /// @return a vector type\n  types::Type *unsafeGetVectorType(unsigned count, types::Type *base);\n  /// Gets a union type. Should generally not be used as no\n  /// type-checker information is generated.\n  /// @param types the alternative types\n  /// @return a union type\n  types::Type *unsafeGetUnionType(const std::vector<types::Type *> &types);\n\n  /// Push an arena on the arena stack that stores all nodes\n  /// that are created subsequently.\n  void pushArena();\n\n  /// Pop the top arena of the arena stack, deallocating all\n  /// the nodes stored therein.\n  void popArena();\n\nprivate:\n  void store(types::Type *t) {\n    types.emplace_back(t);\n    typesMap[t->getName()] = std::prev(types.end());\n    if (!arenas.empty())\n      arenas.back().types.push_back(t->getName());\n  }\n  void store(Value *v) {\n    values.emplace_back(v);\n    valueMap[v->getId()] = std::prev(values.end());\n    if (!arenas.empty())\n      arenas.back().values.push_back(v->getId());\n  }\n  void store(Var *v) {\n    vars.emplace_back(v);\n    varMap[v->getId()] = std::prev(vars.end());\n    if (!arenas.empty())\n      arenas.back().vars.push_back(v->getId());\n  }\n};\n\n} // namespace ir\n} // namespace codon\n\ntemplate <> struct fmt::formatter<codon::ir::Module> : fmt::ostream_formatter {};\n"
  },
  {
    "path": "codon/cir/pyextension.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <string>\n#include <vector>\n\n#include \"codon/cir/func.h\"\n#include \"codon/cir/types/types.h\"\n\nnamespace codon {\nnamespace ir {\n\nstruct PyFunction {\n  enum Type { TOPLEVEL, METHOD, CLASS, STATIC };\n  std::string name;\n  std::string doc;\n  Func *func = nullptr;\n  Type type = Type::TOPLEVEL;\n  int nargs = 0;\n  bool keywords = false;\n  bool coexist = false;\n};\n\nstruct PyMember {\n  enum Type {\n    SHORT = 0,\n    INT = 1,\n    LONG = 2,\n    FLOAT = 3,\n    DOUBLE = 4,\n    STRING = 5,\n    OBJECT = 6,\n    CHAR = 7,\n    BYTE = 8,\n    UBYTE = 9,\n    USHORT = 10,\n    UINT = 11,\n    ULONG = 12,\n    STRING_INPLACE = 13,\n    BOOL = 14,\n    OBJECT_EX = 16,\n    LONGLONG = 17,\n    ULONGLONG = 18,\n    PYSSIZET = 19,\n  };\n\n  std::string name;\n  std::string doc;\n  Type type = Type::SHORT;\n  bool readonly = false;\n  /// Indexes of the member. For example, in the\n  /// tuple (a, (b, c, (d,))), 'a' would have indexes\n  /// [0], 'b' would have indexes [1, 0], 'c' would\n  /// have indexes [1, 1], and 'd' would have indexes\n  /// [1, 2, 0]. This corresponds to an LLVM GEP.\n  std::vector<int> indexes;\n};\n\nstruct PyGetSet {\n  std::string name;\n  std::string doc;\n  Func *get = nullptr;\n  Func *set = nullptr;\n};\n\nstruct PyType {\n  std::string name;\n  std::string doc;\n  types::Type *type = nullptr;\n  PyType *base = nullptr;\n  Func *repr = nullptr;\n  Func *add = nullptr;\n  Func *iadd = nullptr;\n  Func *sub = nullptr;\n  Func *isub = nullptr;\n  Func *mul = nullptr;\n  Func *imul = nullptr;\n  Func *mod = nullptr;\n  Func *imod = nullptr;\n  Func *divmod = nullptr;\n  Func *pow = nullptr;\n  Func *ipow = nullptr;\n  Func *neg = nullptr;\n  Func *pos = nullptr;\n  Func *abs = nullptr;\n  Func *bool_ = nullptr;\n  Func *invert = nullptr;\n  Func *lshift = nullptr;\n  Func *ilshift = nullptr;\n  Func *rshift = nullptr;\n  Func *irshift = nullptr;\n  Func *and_ = nullptr;\n  Func *iand = nullptr;\n  Func *xor_ = nullptr;\n  Func *ixor = nullptr;\n  Func *or_ = nullptr;\n  Func *ior = nullptr;\n  Func *int_ = nullptr;\n  Func *float_ = nullptr;\n  Func *floordiv = nullptr;\n  Func *ifloordiv = nullptr;\n  Func *truediv = nullptr;\n  Func *itruediv = nullptr;\n  Func *index = nullptr;\n  Func *matmul = nullptr;\n  Func *imatmul = nullptr;\n  Func *len = nullptr;\n  Func *getitem = nullptr;\n  Func *setitem = nullptr;\n  Func *contains = nullptr;\n  Func *hash = nullptr;\n  Func *call = nullptr;\n  Func *str = nullptr;\n  Func *cmp = nullptr;\n  Func *iter = nullptr;\n  Func *iternext = nullptr;\n  Func *del = nullptr;\n  Func *init = nullptr;\n  std::vector<PyFunction> methods;\n  std::vector<PyMember> members;\n  std::vector<PyGetSet> getset;\n  Func *typePtrHook = nullptr;\n};\n\nstruct PyModule {\n  std::string name;\n  std::string doc;\n  std::vector<PyFunction> functions;\n  std::vector<PyType> types;\n};\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/cleanup/canonical.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"canonical.h\"\n\n#include <algorithm>\n#include <functional>\n#include <tuple>\n#include <unordered_set>\n#include <utility>\n\n#include \"codon/cir/analyze/module/side_effect.h\"\n#include \"codon/cir/transform/rewrite.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/cir/util/matching.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace cleanup {\nnamespace {\nstruct NodeRanker : public util::Operator {\n  // Nodes are ranked lexicographically by:\n  //   - Whether the node is constant (constants come last)\n  //   - Max node depth (deeper nodes first)\n  //   - Node hash\n  // The hash imposes an arbitrary but well-defined ordering\n  // to ensure a single canonical representation for (most)\n  // nodes.\n  using Rank = std::tuple<int, int, uint64_t>;\n  Node *root = nullptr;\n  int maxDepth = 0;\n  uint64_t hash = 0;\n\n  // boost's hash_combine\n  template <class T> void hash_combine(const T &v) {\n    std::hash<T> hasher;\n    hash ^= hasher(v) + 0x9e3779b9 + (hash << 6) + (hash >> 2);\n  }\n\n  void preHook(Node *node) {\n    if (!root)\n      root = node;\n    maxDepth = std::max(maxDepth, depth());\n    for (auto *v : node->getUsedVariables()) {\n      hash_combine(v->getName());\n    }\n    for (auto *v : node->getUsedTypes()) {\n      hash_combine(v->getName());\n    }\n  }\n\n  Rank getRank() {\n    return std::make_tuple((isA<Const>(root) ? 1 : -1), -maxDepth, hash);\n  }\n};\n\nNodeRanker::Rank getRank(Node *node) {\n  NodeRanker ranker;\n  node->accept(ranker);\n  return ranker.getRank();\n}\n\nbool isCommutativeOp(Func *fn) {\n  return fn && util::hasAttribute(\n                   fn, ast::getMangledFunc(\"std.internal.attributes\", \"commutative\"));\n}\n\nbool isAssociativeOp(Func *fn) {\n  return fn && util::hasAttribute(\n                   fn, ast::getMangledFunc(\"std.internal.attributes\", \"associative\"));\n}\n\nbool isDistributiveOp(Func *fn) {\n  return fn && util::hasAttribute(\n                   fn, ast::getMangledFunc(\"std.internal.attributes\", \"distributive\"));\n}\n\nbool isInequalityOp(Func *fn) {\n  static const std::unordered_set<std::string> ops = {\n      Module::EQ_MAGIC_NAME, Module::NE_MAGIC_NAME, Module::LT_MAGIC_NAME,\n      Module::LE_MAGIC_NAME, Module::GT_MAGIC_NAME, Module::GE_MAGIC_NAME};\n  return fn && ops.find(fn->getUnmangledName()) != ops.end();\n}\n\n// c + b + a --> a + b + c\nstruct CanonOpChain : public RewriteRule {\n  static void extractAssociativeOpChain(Value *v, const std::string &op,\n                                        types::Type *type,\n                                        std::vector<Value *> &result) {\n    if (util::isCallOf(v, op, {type, type}, type, /*method=*/true)) {\n      auto *call = cast<CallInstr>(v);\n      extractAssociativeOpChain(call->front(), op, type, result);\n      extractAssociativeOpChain(call->back(), op, type, result);\n    } else {\n      result.push_back(v);\n    }\n  }\n\n  static void orderOperands(std::vector<Value *> &operands) {\n    std::vector<std::pair<NodeRanker::Rank, Value *>> rankedOperands;\n    for (auto *v : operands) {\n      rankedOperands.push_back({getRank(v), v});\n    }\n    std::sort(rankedOperands.begin(), rankedOperands.end());\n\n    operands.clear();\n    for (auto &p : rankedOperands) {\n      operands.push_back(std::get<1>(p));\n    }\n  }\n\n  void visit(CallInstr *v) override {\n    auto *fn = util::getFunc(v->getCallee());\n    if (!fn)\n      return;\n\n    std::string op = fn->getUnmangledName();\n    types::Type *type = v->getType();\n    const bool isAssociative = isAssociativeOp(fn);\n    const bool isCommutative = isCommutativeOp(fn);\n\n    if (util::isCallOf(v, op, {type, type}, type, /*method=*/true)) {\n      std::vector<Value *> operands;\n      if (isAssociative) {\n        extractAssociativeOpChain(v, op, type, operands);\n      } else {\n        operands.push_back(v->front());\n        operands.push_back(v->back());\n      }\n      seqassertn(operands.size() >= 2, \"bad call canonicalization\");\n\n      if (isCommutative)\n        orderOperands(operands);\n\n      Value *newCall = util::call(fn, {operands[0], operands[1]});\n      for (auto it = operands.begin() + 2; it != operands.end(); ++it) {\n        newCall = util::call(fn, {newCall, *it});\n      }\n\n      if (!util::match(v, newCall, /*checkNames=*/false, /*varIdMatch=*/true))\n        return setResult(newCall);\n    }\n  }\n};\n\n// b > a --> a < b (etc.)\nstruct CanonInequality : public RewriteRule {\n  void visit(CallInstr *v) override {\n    auto *fn = util::getFunc(v->getCallee());\n    if (!fn)\n      return;\n\n    std::string op = fn->getUnmangledName();\n    types::Type *type = v->getType();\n\n    // canonicalize inequalities\n    if (v->numArgs() == 2 && isInequalityOp(fn)) {\n      Value *newCall = nullptr;\n      auto *lhs = v->front();\n      auto *rhs = v->back();\n      if (getRank(lhs) > getRank(rhs)) { // are we out of order?\n        // re-order\n        if (op == Module::EQ_MAGIC_NAME) { // lhs == rhs\n          newCall = *rhs == *lhs;\n        } else if (op == Module::NE_MAGIC_NAME) { // lhs != rhs\n          newCall = *rhs != *lhs;\n        } else if (op == Module::LT_MAGIC_NAME) { // lhs < rhs\n          newCall = *rhs > *lhs;\n        } else if (op == Module::LE_MAGIC_NAME) { // lhs <= rhs\n          newCall = *rhs >= *lhs;\n        } else if (op == Module::GT_MAGIC_NAME) { // lhs > rhs\n          newCall = *rhs < *lhs;\n        } else if (op == Module::GE_MAGIC_NAME) { // lhs >= rhs\n          newCall = *rhs <= *lhs;\n        } else {\n          seqassertn(false, \"unknown comparison op: {}\", op);\n        }\n\n        if (newCall && newCall->getType()->is(type) &&\n            !util::match(v, newCall, /*checkNames=*/false, /*varIdMatch=*/true))\n          return setResult(newCall);\n      }\n    }\n  }\n};\n\n// a*x + b*x --> (a + b) * x\nstruct CanonAddMul : public RewriteRule {\n  static bool varMatch(Value *a, Value *b) {\n    auto *v1 = cast<VarValue>(a);\n    auto *v2 = cast<VarValue>(b);\n    return v1 && v2 && v1->getVar()->getId() == v2->getVar()->getId();\n  }\n\n  static Func *getOp(Value *v) {\n    return isA<CallInstr>(v) ? util::getFunc(cast<CallInstr>(v)->getCallee()) : nullptr;\n  }\n\n  // (a + b) * x, or null if invalid\n  static Value *addMul(Value *a, Value *b, Value *x) {\n    if (!a || !b || !x)\n      return nullptr;\n\n    auto *y = (*a + *b);\n    if (!y) {\n      y = (*b + *a);\n      if (y && !isCommutativeOp(getOp(y)))\n        return nullptr;\n    }\n    if (!y)\n      return nullptr;\n\n    auto *z = (*y) * (*x);\n    if (!z) {\n      z = (*x) * (*y);\n      if (z && !isCommutativeOp(getOp(z)))\n        return nullptr;\n    }\n    if (!z)\n      return nullptr;\n\n    return z;\n  }\n\n  void visit(CallInstr *v) override {\n    auto *M = v->getModule();\n    auto *fn = util::getFunc(v->getCallee());\n    if (!isCommutativeOp(fn) ||\n        !util::isCallOf(v, Module::ADD_MAGIC_NAME, 2, /*output=*/nullptr,\n                        /*method=*/true))\n      return;\n\n    // decompose the operation\n    Value *lhs = v->front();\n    Value *rhs = v->back();\n    Value *lhs1 = nullptr, *lhs2 = nullptr, *rhs1 = nullptr, *rhs2 = nullptr;\n\n    if (util::isCallOf(lhs, Module::MUL_MAGIC_NAME, 2, /*output=*/nullptr,\n                       /*method=*/true)) {\n      auto *lhsCall = cast<CallInstr>(lhs);\n      lhs1 = lhsCall->front();\n      lhs2 = lhsCall->back();\n    } else {\n      lhs1 = lhs;\n      lhs2 = M->getInt(1);\n    }\n\n    if (util::isCallOf(rhs, Module::MUL_MAGIC_NAME, 2, /*output=*/nullptr,\n                       /*method=*/true)) {\n      auto *rhsCall = cast<CallInstr>(rhs);\n      rhs1 = rhsCall->front();\n      rhs2 = rhsCall->back();\n    } else {\n      rhs1 = rhs;\n      rhs2 = M->getInt(1);\n    }\n\n    Value *newCall = nullptr;\n    if (varMatch(lhs1, rhs1)) {\n      newCall = addMul(lhs2, rhs2, lhs1);\n    } else if (varMatch(lhs1, rhs2)) {\n      newCall = addMul(lhs2, rhs1, lhs1);\n    } else if (varMatch(lhs2, rhs1)) {\n      newCall = addMul(lhs1, rhs2, lhs2);\n    } else if (varMatch(lhs2, rhs2)) {\n      newCall = addMul(lhs1, rhs1, lhs2);\n    }\n\n    if (newCall && isDistributiveOp(getOp(newCall)) &&\n        newCall->getType()->is(v->getType()) &&\n        !util::match(v, newCall, /*checkNames=*/false, /*varIdMatch=*/true))\n      return setResult(newCall);\n  }\n};\n\n// x - c --> x + (-c)\nstruct CanonConstSub : public RewriteRule {\n  void visit(CallInstr *v) override {\n    auto *M = v->getModule();\n    auto *type = v->getType();\n\n    if (!util::isCallOf(v, Module::SUB_MAGIC_NAME, 2, /*output=*/nullptr,\n                        /*method=*/true))\n      return;\n\n    Value *lhs = v->front();\n    Value *rhs = v->back();\n\n    if (!lhs->getType()->is(rhs->getType()))\n      return;\n\n    Value *newCall = nullptr;\n    if (util::isConst<int64_t>(rhs)) {\n      auto c = util::getConst<int64_t>(rhs);\n      if (c != -(1ull << 63)) // ensure no overflow\n        newCall = *lhs + *(M->getInt(-c));\n    } else if (util::isConst<double>(rhs)) {\n      auto c = util::getConst<double>(rhs);\n      newCall = *lhs + *(M->getFloat(-c));\n    }\n\n    if (newCall && newCall->getType()->is(type) &&\n        !util::match(v, newCall, /*checkNames=*/false, /*varIdMatch=*/true))\n      return setResult(newCall);\n  }\n};\n} // namespace\n\nconst std::string CanonicalizationPass::KEY = \"core-cleanup-canon\";\n\nvoid CanonicalizationPass::run(Module *m) {\n  registerStandardRules(m);\n  Rewriter::reset();\n  OperatorPass::run(m);\n}\n\nvoid CanonicalizationPass::handle(CallInstr *v) {\n  auto *r = getAnalysisResult<analyze::module::SideEffectResult>(sideEffectsKey);\n  if (!r->hasSideEffect(v))\n    rewrite(v);\n}\n\nvoid CanonicalizationPass::handle(SeriesFlow *v) {\n  auto it = v->begin();\n  while (it != v->end()) {\n    if (auto *series = cast<SeriesFlow>(*it)) {\n      it = v->erase(it);\n      for (auto *x : *series) {\n        it = v->insert(it, x);\n        ++it;\n      }\n    } else if (auto *flowInstr = cast<FlowInstr>(*it)) {\n      it = v->erase(it);\n      // inserting in reverse order causes [flow, value] to be added\n      it = v->insert(it, flowInstr->getValue());\n      it = v->insert(it, flowInstr->getFlow());\n      // don't increment; re-traverse in case a new series flow added\n    } else {\n      ++it;\n    }\n  }\n}\n\nvoid CanonicalizationPass::registerStandardRules(Module *m) {\n  registerRule(\"op-chain\", std::make_unique<CanonOpChain>());\n  registerRule(\"inequality\", std::make_unique<CanonInequality>());\n  registerRule(\"add-mul\", std::make_unique<CanonAddMul>());\n  registerRule(\"const-sub\", std::make_unique<CanonConstSub>());\n}\n\n} // namespace cleanup\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/cleanup/canonical.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n#include \"codon/cir/transform/rewrite.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace cleanup {\n\n/// Canonicalization pass that flattens nested series\n/// flows, puts operands in a predefined order, etc.\nclass CanonicalizationPass : public OperatorPass, public Rewriter {\nprivate:\n  std::string sideEffectsKey;\n\npublic:\n  /// Constructs a canonicalization pass\n  /// @param sideEffectsKey the side effect analysis' key\n  CanonicalizationPass(const std::string &sideEffectsKey)\n      : OperatorPass(/*childrenFirst=*/true), sideEffectsKey(sideEffectsKey) {}\n\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n\n  void run(Module *m) override;\n  void handle(CallInstr *) override;\n  void handle(SeriesFlow *) override;\n\nprivate:\n  void registerStandardRules(Module *m);\n};\n\n} // namespace cleanup\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/cleanup/dead_code.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"dead_code.h\"\n\n#include \"codon/cir/analyze/module/side_effect.h\"\n#include \"codon/cir/util/cloning.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace cleanup {\nnamespace {\nBoolConst *boolConst(Value *v) { return cast<BoolConst>(v); }\n\nIntConst *intConst(Value *v) { return cast<IntConst>(v); }\n} // namespace\n\nconst std::string DeadCodeCleanupPass::KEY = \"core-cleanup-dce\";\n\nvoid DeadCodeCleanupPass::run(Module *m) {\n  numReplacements = 0;\n  OperatorPass::run(m);\n}\n\nvoid DeadCodeCleanupPass::handle(SeriesFlow *v) {\n  auto *r = getAnalysisResult<analyze::module::SideEffectResult>(sideEffectsKey);\n  auto it = v->begin();\n  while (it != v->end()) {\n    if (!r->hasSideEffect(*it)) {\n      LOG_IR(\"[{}] no side effect, deleting: {}\", KEY, **it);\n      numReplacements++;\n      it = v->erase(it);\n    } else {\n      ++it;\n    }\n  }\n}\n\nvoid DeadCodeCleanupPass::handle(IfFlow *v) {\n  auto *cond = boolConst(v->getCond());\n  if (!cond)\n    return;\n\n  auto *M = v->getModule();\n  auto condVal = cond->getVal();\n\n  util::CloneVisitor cv(M);\n  if (condVal) {\n    doReplacement(v, cv.clone(v->getTrueBranch()));\n  } else if (auto *f = v->getFalseBranch()) {\n    doReplacement(v, cv.clone(f));\n  } else {\n    doReplacement(v, M->Nr<SeriesFlow>());\n  }\n}\n\nvoid DeadCodeCleanupPass::handle(WhileFlow *v) {\n  auto *cond = boolConst(v->getCond());\n  if (!cond)\n    return;\n\n  auto *M = v->getModule();\n  auto condVal = cond->getVal();\n  if (!condVal) {\n    doReplacement(v, M->Nr<SeriesFlow>());\n  }\n}\n\nvoid DeadCodeCleanupPass::handle(ImperativeForFlow *v) {\n  auto *start = intConst(v->getStart());\n  auto *end = intConst(v->getEnd());\n  if (!start || !end)\n    return;\n\n  auto stepVal = v->getStep();\n  auto startVal = start->getVal();\n  auto endVal = end->getVal();\n\n  auto *M = v->getModule();\n  if ((stepVal < 0 && startVal <= endVal) || (stepVal > 0 && startVal >= endVal)) {\n    doReplacement(v, M->Nr<SeriesFlow>());\n  }\n}\n\nvoid DeadCodeCleanupPass::handle(TernaryInstr *v) {\n  auto *cond = boolConst(v->getCond());\n  if (!cond)\n    return;\n\n  auto *M = v->getModule();\n  auto condVal = cond->getVal();\n\n  util::CloneVisitor cv(M);\n  if (condVal) {\n    doReplacement(v, cv.clone(v->getTrueValue()));\n  } else {\n    doReplacement(v, cv.clone(v->getFalseValue()));\n  }\n}\n\nvoid DeadCodeCleanupPass::doReplacement(Value *og, Value *v) {\n  numReplacements++;\n  og->replaceAll(v);\n}\n\n} // namespace cleanup\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/cleanup/dead_code.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace cleanup {\n\n/// Cleanup pass that removes dead code.\nclass DeadCodeCleanupPass : public OperatorPass {\nprivate:\n  std::string sideEffectsKey;\n  int numReplacements;\n\npublic:\n  static const std::string KEY;\n\n  /// Constructs a dead code elimination pass\n  /// @param sideEffectsKey the side effect analysis' key\n  DeadCodeCleanupPass(std::string sideEffectsKey)\n      : OperatorPass(), sideEffectsKey(std::move(sideEffectsKey)), numReplacements(0) {}\n\n  std::string getKey() const override { return KEY; }\n\n  void run(Module *m) override;\n\n  void handle(SeriesFlow *v) override;\n  void handle(IfFlow *v) override;\n  void handle(WhileFlow *v) override;\n  void handle(ImperativeForFlow *v) override;\n  void handle(TernaryInstr *v) override;\n\n  /// @return the number of replacements\n  int getNumReplacements() const { return numReplacements; }\n\nprivate:\n  void doReplacement(Value *og, Value *v);\n};\n\n} // namespace cleanup\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/cleanup/global_demote.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"global_demote.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace cleanup {\nnamespace {\nstruct GetUsedGlobals : public util::Operator {\n  std::vector<Var *> vars;\n  void preHook(Node *v) override {\n    for (auto *var : v->getUsedVariables()) {\n      if (!isA<Func>(var) && var->isGlobal() && !var->isThreadLocal())\n        vars.push_back(var);\n    }\n  }\n};\n} // namespace\n\nconst std::string GlobalDemotionPass::KEY = \"core-cleanup-global-demote\";\n\nvoid GlobalDemotionPass::run(Module *M) {\n  numDemotions = 0;\n  std::unordered_map<Var *, Func *> localGlobals;\n\n  std::vector<Func *> worklist = {M->getMainFunc()};\n  for (auto *var : *M) {\n    if (auto *func = cast<Func>(var))\n      worklist.push_back(func);\n  }\n\n  for (auto *var : worklist) {\n    if (auto *func = cast<Func>(var)) {\n      GetUsedGlobals globals;\n      func->accept(globals);\n\n      for (auto *g : globals.vars) {\n        LOG_IR(\"[{}] global {} used in {}\", KEY, *g, func->getName());\n        auto it = localGlobals.find(g);\n        if (it == localGlobals.end()) {\n          localGlobals.emplace(g, func);\n        } else if (it->second && it->second != func) {\n          it->second = nullptr;\n        }\n      }\n    }\n  }\n\n  for (auto it : localGlobals) {\n    if (!it.second || it.first->getId() == M->getArgVar()->getId() ||\n        it.first->isExternal())\n      continue;\n    seqassertn(it.first->isGlobal(), \"var was not global [{}]\", it.first->getSrcInfo());\n    it.first->setGlobal(false);\n    if (auto *func = cast<BodiedFunc>(it.second)) {\n      func->push_back(it.first);\n      ++numDemotions;\n      LOG_IR(\"[{}] demoted {} to a local of {}\", KEY, *it.first, func->getName());\n    }\n  }\n}\n\n} // namespace cleanup\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/cleanup/global_demote.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace cleanup {\n\n/// Demotes global variables that are used in only one\n/// function to locals of that function.\nclass GlobalDemotionPass : public Pass {\nprivate:\n  /// number of variables we've demoted\n  int numDemotions;\n\npublic:\n  static const std::string KEY;\n\n  /// Constructs a global variable demotion pass\n  GlobalDemotionPass() : Pass(), numDemotions(0) {}\n\n  std::string getKey() const override { return KEY; }\n  void run(Module *v) override;\n\n  /// @return number of variables we've demoted\n  int getNumDemotions() const { return numDemotions; }\n};\n\n} // namespace cleanup\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/cleanup/replacer.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"replacer.h\"\n\n#include <unordered_set>\n\n#include \"codon/cir/types/types.h\"\n#include \"codon/cir/value.h\"\n#include \"codon/cir/var.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace cleanup {\n\nconst std::string ReplaceCleanupPass::KEY = \"core-cleanup-physical-replace\";\n\nvoid ReplaceCleanupPass::run(Module *module) {\n  std::unordered_set<Value *> valuesToDelete;\n  std::unordered_set<types::Type *> typesToDelete;\n  std::unordered_set<Var *> varsToDelete;\n\n  {\n    auto *f = module->getMainFunc();\n    for (auto *c : f->getUsedValues()) {\n      if (c->hasReplacement()) {\n        f->replaceUsedValue(c, c->getActual());\n        valuesToDelete.insert(c);\n      }\n    }\n    for (auto *t : f->getUsedTypes()) {\n      if (t->hasReplacement()) {\n        f->replaceUsedType(t, t->getActual());\n        typesToDelete.insert(t);\n      }\n    }\n    for (auto *v : f->getUsedVariables()) {\n      if (v->hasReplacement()) {\n        f->replaceUsedVariable(v, v->getActual());\n        varsToDelete.insert(v);\n      }\n    }\n  }\n\n  {\n    auto *v = module->getArgVar();\n    for (auto *c : v->getUsedValues()) {\n      if (c->hasReplacement()) {\n        v->replaceUsedValue(c, c->getActual());\n        valuesToDelete.insert(c);\n      }\n    }\n    for (auto *t : v->getUsedTypes()) {\n      if (t->hasReplacement()) {\n        v->replaceUsedType(t, t->getActual());\n        typesToDelete.insert(t);\n      }\n    }\n    for (auto *v2 : v->getUsedVariables()) {\n      if (v2->hasReplacement()) {\n        v->replaceUsedVariable(v2, v2->getActual());\n        varsToDelete.insert(v2);\n      }\n    }\n  }\n\n  for (auto it = module->values_begin(); it != module->values_end(); ++it) {\n    for (auto *c : it->getUsedValues()) {\n      if (c->hasReplacement()) {\n        it->replaceUsedValue(c, c->getActual());\n        valuesToDelete.insert(c);\n      }\n    }\n    for (auto *t : it->getUsedTypes()) {\n      if (t->hasReplacement()) {\n        it->replaceUsedType(t, t->getActual());\n        typesToDelete.insert(t);\n      }\n    }\n    for (auto *v : it->getUsedVariables()) {\n      if (v->hasReplacement()) {\n        it->replaceUsedVariable(v, v->getActual());\n        varsToDelete.insert(v);\n      }\n    }\n  }\n\n  for (auto it = module->begin(); it != module->end(); ++it) {\n    for (auto *c : it->getUsedValues()) {\n      if (c->hasReplacement()) {\n        it->replaceUsedValue(c, c->getActual());\n        valuesToDelete.insert(c);\n      }\n    }\n    for (auto *t : it->getUsedTypes()) {\n      if (t->hasReplacement()) {\n        it->replaceUsedType(t, t->getActual());\n        typesToDelete.insert(t);\n      }\n    }\n    for (auto *v : it->getUsedVariables()) {\n      if (v->hasReplacement()) {\n        it->replaceUsedVariable(v, v->getActual());\n        varsToDelete.insert(v);\n      }\n    }\n  }\n\n  for (auto it = module->types_begin(); it != module->types_end(); ++it) {\n    for (auto *c : it->getUsedValues()) {\n      if (c->hasReplacement()) {\n        it->replaceUsedValue(c, c->getActual());\n        valuesToDelete.insert(c);\n      }\n    }\n    for (auto *t : it->getUsedTypes()) {\n      if (t->hasReplacement()) {\n        it->replaceUsedType(t, t->getActual());\n        typesToDelete.insert(t);\n      }\n    }\n    for (auto *v : it->getUsedVariables()) {\n      if (v->hasReplacement()) {\n        it->replaceUsedVariable(v, v->getActual());\n        varsToDelete.insert(v);\n      }\n    }\n  }\n\n  for (auto *v : valuesToDelete)\n    module->remove(v);\n  for (auto *v : varsToDelete)\n    module->remove(v);\n  for (auto *t : typesToDelete)\n    module->remove(t);\n}\n\n} // namespace cleanup\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/cleanup/replacer.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace cleanup {\n\n/// Cleanup pass that physically replaces nodes.\nclass ReplaceCleanupPass : public Pass {\npublic:\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n  void run(Module *module) override;\n};\n\n} // namespace cleanup\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/folding/const_fold.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"const_fold.h\"\n\n#include <cmath>\n#include <utility>\n\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n\n#define BINOP(o) [](auto x, auto y) -> auto { return x o y; }\n#define UNOP(o) [](auto x) -> auto { return o x; }\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace folding {\nnamespace {\nauto pyDivmod(int64_t self, int64_t other) {\n  auto d = self / other;\n  auto m = self - d * other;\n  if (m && ((other ^ m) < 0)) {\n    m += other;\n    d -= 1;\n  }\n  return std::make_pair(d, m);\n}\n\ntemplate <typename Func, typename Out> class IntFloatBinaryRule : public RewriteRule {\nprivate:\n  Func f;\n  std::string magic;\n  types::Type *out;\n  bool excludeRHSZero;\n\npublic:\n  IntFloatBinaryRule(Func f, std::string magic, types::Type *out,\n                     bool excludeRHSZero = false)\n      : f(std::move(f)), magic(std::move(magic)), out(out),\n        excludeRHSZero(excludeRHSZero) {}\n\n  virtual ~IntFloatBinaryRule() noexcept = default;\n\n  void visit(CallInstr *v) override {\n    if (!util::isCallOf(v, magic, 2, /*output=*/nullptr, /*method=*/true))\n      return;\n\n    auto *leftConst = cast<Const>(v->front());\n    auto *rightConst = cast<Const>(v->back());\n\n    if (!leftConst || !rightConst)\n      return;\n\n    auto *M = v->getModule();\n    if (isA<FloatConst>(leftConst) && isA<IntConst>(rightConst)) {\n      auto left = cast<FloatConst>(leftConst)->getVal();\n      auto right = cast<IntConst>(rightConst)->getVal();\n      if (excludeRHSZero && right == 0)\n        return;\n      return setResult(M->template N<TemplatedConst<Out>>(v->getSrcInfo(),\n                                                          f(left, (double)right), out));\n    } else if (isA<IntConst>(leftConst) && isA<FloatConst>(rightConst)) {\n      auto left = cast<IntConst>(leftConst)->getVal();\n      auto right = cast<FloatConst>(rightConst)->getVal();\n      if (excludeRHSZero && right == 0.0)\n        return;\n      return setResult(M->template N<TemplatedConst<Out>>(v->getSrcInfo(),\n                                                          f((double)left, right), out));\n    }\n  }\n};\n\n/// Binary rule that requires two constants.\ntemplate <typename ConstantType, typename Func, typename OutputType = ConstantType>\nclass DoubleConstantBinaryRuleExcludeRHSZero\n    : public DoubleConstantBinaryRule<ConstantType, Func, OutputType> {\npublic:\n  DoubleConstantBinaryRuleExcludeRHSZero(Func f, std::string magic,\n                                         types::Type *inputType,\n                                         types::Type *resultType)\n      : DoubleConstantBinaryRule<ConstantType, Func, OutputType>(f, magic, inputType,\n                                                                 resultType) {}\n\n  virtual ~DoubleConstantBinaryRuleExcludeRHSZero() noexcept = default;\n\n  void visit(CallInstr *v) override {\n    if (v->numArgs() == 2) {\n      auto *rightConst = cast<TemplatedConst<ConstantType>>(v->back());\n      if (rightConst && rightConst->getVal() == ConstantType())\n        return;\n    }\n    DoubleConstantBinaryRule<ConstantType, Func, OutputType>::visit(v);\n  }\n};\n\nauto id_val(Module *m) {\n  return [=](Value *v) -> Value * {\n    util::CloneVisitor cv(m);\n    return cv.clone(v);\n  };\n}\n\nint64_t int_pow(int64_t base, int64_t exp) {\n  if (exp < 0)\n    return 0;\n  int64_t result = 1;\n  while (true) {\n    if (exp & 1) {\n      result *= base;\n    }\n    exp = exp >> 1;\n    if (!exp)\n      break;\n    base = base * base;\n  }\n  return result;\n}\n\ntemplate <typename From, typename To> To convert(From x) { return To(x); }\n\ntemplate <typename... Args> auto intSingleRule(Module *m, Args &&...args) {\n  return std::make_unique<SingleConstantCommutativeRule<int64_t>>(\n      std::forward<Args>(args)..., m->getIntType());\n}\n\nauto intNoOp(Module *m, std::string magic) {\n  return std::make_unique<NoOpRule>(std::move(magic), m->getIntType());\n}\n\nauto intDoubleApplyNoOp(Module *m, std::string magic) {\n  return std::make_unique<DoubleApplicationNoOpRule>(std::move(magic), m->getIntType());\n}\n\ntemplate <typename Func> auto intToIntBinary(Module *m, Func f, std::string magic) {\n  return std::make_unique<DoubleConstantBinaryRule<int64_t, Func, int64_t>>(\n      std::move(f), std::move(magic), m->getIntType(), m->getIntType());\n}\n\ntemplate <typename Func>\nauto intToIntBinaryNoZeroRHS(Module *m, Func f, std::string magic) {\n  return std::make_unique<\n      DoubleConstantBinaryRuleExcludeRHSZero<int64_t, Func, int64_t>>(\n      std::move(f), std::move(magic), m->getIntType(), m->getIntType());\n}\n\ntemplate <typename Func> auto intToBoolBinary(Module *m, Func f, std::string magic) {\n  return std::make_unique<DoubleConstantBinaryRule<int64_t, Func, bool>>(\n      std::move(f), std::move(magic), m->getIntType(), m->getBoolType());\n}\n\ntemplate <typename Func> auto boolToBoolBinary(Module *m, Func f, std::string magic) {\n  return std::make_unique<DoubleConstantBinaryRule<bool, Func, bool>>(\n      std::move(f), std::move(magic), m->getBoolType(), m->getBoolType());\n}\n\ntemplate <typename Func> auto floatToFloatBinary(Module *m, Func f, std::string magic) {\n  return std::make_unique<DoubleConstantBinaryRule<double, Func, double>>(\n      std::move(f), std::move(magic), m->getFloatType(), m->getFloatType());\n}\n\ntemplate <typename Func>\nauto floatToFloatBinaryNoZeroRHS(Module *m, Func f, std::string magic) {\n  return std::make_unique<DoubleConstantBinaryRuleExcludeRHSZero<double, Func, double>>(\n      std::move(f), std::move(magic), m->getFloatType(), m->getFloatType());\n}\n\ntemplate <typename Func> auto floatToBoolBinary(Module *m, Func f, std::string magic) {\n  return std::make_unique<DoubleConstantBinaryRule<double, Func, bool>>(\n      std::move(f), std::move(magic), m->getFloatType(), m->getBoolType());\n}\n\ntemplate <typename Func>\nauto intFloatToFloatBinary(Module *m, Func f, std::string magic,\n                           bool excludeRHSZero = false) {\n  return std::make_unique<IntFloatBinaryRule<Func, double>>(\n      std::move(f), std::move(magic), m->getFloatType(), excludeRHSZero);\n}\n\ntemplate <typename Func>\nauto intFloatToBoolBinary(Module *m, Func f, std::string magic) {\n  return std::make_unique<IntFloatBinaryRule<Func, bool>>(\n      std::move(f), std::move(magic), m->getBoolType());\n}\n\ntemplate <typename Func> auto intToIntUnary(Module *m, Func f, std::string magic) {\n  return std::make_unique<SingleConstantUnaryRule<int64_t, Func>>(\n      std::move(f), std::move(magic), m->getIntType(), m->getIntType());\n}\n\ntemplate <typename Func> auto floatToFloatUnary(Module *m, Func f, std::string magic) {\n  return std::make_unique<SingleConstantUnaryRule<double, Func>>(\n      std::move(f), std::move(magic), m->getFloatType(), m->getFloatType());\n}\n\ntemplate <typename Func> auto boolToBoolUnary(Module *m, Func f, std::string magic) {\n  return std::make_unique<SingleConstantUnaryRule<bool, Func>>(\n      std::move(f), std::move(magic), m->getBoolType(), m->getBoolType());\n}\n\nauto identityConvert(Module *m, std::string magic, types::Type *type) {\n  return std::make_unique<UnaryRule<decltype(id_val(m))>>(id_val(m), std::move(magic),\n                                                          type);\n}\n\ntemplate <typename From, typename To>\nauto typeConvert(Module *m, std::string magic, types::Type *fromType,\n                 types::Type *toType) {\n  return std::make_unique<\n      SingleConstantUnaryRule<From, std::function<decltype(convert<From, To>)>>>(\n      convert<From, To>, std::move(magic), fromType, toType);\n}\n} // namespace\n\nconst std::string FoldingPass::KEY = \"core-folding-const-fold\";\n\nvoid FoldingPass::run(Module *m) {\n  registerStandardRules(m);\n  Rewriter::reset();\n  OperatorPass::run(m);\n}\n\nvoid FoldingPass::handle(CallInstr *v) { rewrite(v); }\n\nvoid FoldingPass::registerStandardRules(Module *m) {\n  // binary, single constant, int->int\n  using Kind = SingleConstantCommutativeRule<int64_t>::Kind;\n  registerRule(\"int-multiply-by-zero\",\n               intSingleRule(m, 0, 0, Module::MUL_MAGIC_NAME, Kind::COMMUTATIVE));\n  registerRule(\n      \"int-multiply-by-one\",\n      intSingleRule(m, 1, id_val(m), Module::MUL_MAGIC_NAME, Kind::COMMUTATIVE));\n  registerRule(\"int-subtract-zero\",\n               intSingleRule(m, 0, id_val(m), Module::SUB_MAGIC_NAME, Kind::RIGHT));\n  registerRule(\"int-add-zero\", intSingleRule(m, 0, id_val(m), Module::ADD_MAGIC_NAME,\n                                             Kind::COMMUTATIVE));\n  registerRule(\n      \"int-floor-div-by-one\",\n      intSingleRule(m, 1, id_val(m), Module::FLOOR_DIV_MAGIC_NAME, Kind::RIGHT));\n  registerRule(\"int-zero-floor-div\",\n               intSingleRule(m, 0, 0, Module::FLOOR_DIV_MAGIC_NAME, Kind::LEFT));\n  registerRule(\"int-pos\", intNoOp(m, Module::POS_MAGIC_NAME));\n  registerRule(\"int-double-neg\", intDoubleApplyNoOp(m, Module::NEG_MAGIC_NAME));\n  registerRule(\"int-double-inv\", intDoubleApplyNoOp(m, Module::INVERT_MAGIC_NAME));\n\n  // binary, double constant, int->int\n  registerRule(\"int-constant-addition\",\n               intToIntBinary(m, BINOP(+), Module::ADD_MAGIC_NAME));\n  registerRule(\"int-constant-subtraction\",\n               intToIntBinary(m, BINOP(-), Module::SUB_MAGIC_NAME));\n  if (pyNumerics) {\n    registerRule(\"int-constant-floor-div\",\n                 intToIntBinaryNoZeroRHS(\n                     m, [](auto x, auto y) -> auto { return pyDivmod(x, y).first; },\n                     Module::FLOOR_DIV_MAGIC_NAME));\n  } else {\n    registerRule(\"int-constant-floor-div\",\n                 intToIntBinaryNoZeroRHS(m, BINOP(/), Module::FLOOR_DIV_MAGIC_NAME));\n  }\n  registerRule(\"int-constant-mul\", intToIntBinary(m, BINOP(*), Module::MUL_MAGIC_NAME));\n  registerRule(\"int-constant-lshift\",\n               intToIntBinary(m, BINOP(<<), Module::LSHIFT_MAGIC_NAME));\n  registerRule(\"int-constant-rshift\",\n               intToIntBinary(m, BINOP(>>), Module::RSHIFT_MAGIC_NAME));\n  registerRule(\"int-constant-pow\", intToIntBinary(m, int_pow, Module::POW_MAGIC_NAME));\n  registerRule(\"int-constant-xor\", intToIntBinary(m, BINOP(^), Module::XOR_MAGIC_NAME));\n  registerRule(\"int-constant-or\", intToIntBinary(m, BINOP(|), Module::OR_MAGIC_NAME));\n  registerRule(\"int-constant-and\", intToIntBinary(m, BINOP(&), Module::AND_MAGIC_NAME));\n  if (pyNumerics) {\n    registerRule(\"int-constant-mod\",\n                 intToIntBinaryNoZeroRHS(\n                     m, [](auto x, auto y) -> auto { return pyDivmod(x, y).second; },\n                     Module::MOD_MAGIC_NAME));\n  } else {\n    registerRule(\"int-constant-mod\",\n                 intToIntBinaryNoZeroRHS(m, BINOP(%), Module::MOD_MAGIC_NAME));\n  }\n\n  // binary, double constant, int->bool\n  registerRule(\"int-constant-eq\", intToBoolBinary(m, BINOP(==), Module::EQ_MAGIC_NAME));\n  registerRule(\"int-constant-ne\", intToBoolBinary(m, BINOP(!=), Module::NE_MAGIC_NAME));\n  registerRule(\"int-constant-gt\", intToBoolBinary(m, BINOP(>), Module::GT_MAGIC_NAME));\n  registerRule(\"int-constant-ge\", intToBoolBinary(m, BINOP(>=), Module::GE_MAGIC_NAME));\n  registerRule(\"int-constant-lt\", intToBoolBinary(m, BINOP(<), Module::LT_MAGIC_NAME));\n  registerRule(\"int-constant-le\", intToBoolBinary(m, BINOP(<=), Module::LE_MAGIC_NAME));\n\n  // binary, double constant, bool->bool\n  registerRule(\"bool-constant-xor\",\n               boolToBoolBinary(m, BINOP(^), Module::XOR_MAGIC_NAME));\n  registerRule(\"bool-constant-or\",\n               boolToBoolBinary(m, BINOP(|), Module::OR_MAGIC_NAME));\n  registerRule(\"bool-constant-and\",\n               boolToBoolBinary(m, BINOP(&), Module::AND_MAGIC_NAME));\n\n  // unary, single constant, int->int\n  registerRule(\"int-constant-pos\", intToIntUnary(m, UNOP(+), Module::POS_MAGIC_NAME));\n  registerRule(\"int-constant-neg\", intToIntUnary(m, UNOP(-), Module::NEG_MAGIC_NAME));\n  registerRule(\"int-constant-inv\",\n               intToIntUnary(m, UNOP(~), Module::INVERT_MAGIC_NAME));\n\n  // unary, singe constant, float->float\n  registerRule(\"float-constant-pos\",\n               floatToFloatUnary(m, UNOP(+), Module::POS_MAGIC_NAME));\n  registerRule(\"float-constant-neg\",\n               floatToFloatUnary(m, UNOP(-), Module::NEG_MAGIC_NAME));\n\n  // unary, single constant, bool->bool\n  registerRule(\"bool-constant-inv\",\n               boolToBoolUnary(m, UNOP(!), Module::INVERT_MAGIC_NAME));\n\n  // binary, double constant, float->float\n  registerRule(\"float-constant-addition\",\n               floatToFloatBinary(m, BINOP(+), Module::ADD_MAGIC_NAME));\n  registerRule(\"float-constant-subtraction\",\n               floatToFloatBinary(m, BINOP(-), Module::SUB_MAGIC_NAME));\n  if (pyNumerics) {\n    registerRule(\"float-constant-floor-div\",\n                 floatToFloatBinaryNoZeroRHS(m, BINOP(/), Module::TRUE_DIV_MAGIC_NAME));\n  } else {\n    registerRule(\"float-constant-floor-div\",\n                 floatToFloatBinary(m, BINOP(/), Module::TRUE_DIV_MAGIC_NAME));\n  }\n  registerRule(\"float-constant-mul\",\n               floatToFloatBinary(m, BINOP(*), Module::MUL_MAGIC_NAME));\n  registerRule(\n      \"float-constant-pow\",\n      floatToFloatBinary(\n          m, [](auto a, auto b) { return std::pow(a, b); }, Module::POW_MAGIC_NAME));\n\n  // binary, double constant, float->bool\n  registerRule(\"float-constant-eq\",\n               floatToBoolBinary(m, BINOP(==), Module::EQ_MAGIC_NAME));\n  registerRule(\"float-constant-ne\",\n               floatToBoolBinary(m, BINOP(!=), Module::NE_MAGIC_NAME));\n  registerRule(\"float-constant-gt\",\n               floatToBoolBinary(m, BINOP(>), Module::GT_MAGIC_NAME));\n  registerRule(\"float-constant-ge\",\n               floatToBoolBinary(m, BINOP(>=), Module::GE_MAGIC_NAME));\n  registerRule(\"float-constant-lt\",\n               floatToBoolBinary(m, BINOP(<), Module::LT_MAGIC_NAME));\n  registerRule(\"float-constant-le\",\n               floatToBoolBinary(m, BINOP(<=), Module::LE_MAGIC_NAME));\n\n  // binary, double constant, int,float->float\n  registerRule(\"int-float-constant-addition\",\n               intFloatToFloatBinary(m, BINOP(+), Module::ADD_MAGIC_NAME));\n  registerRule(\"int-float-constant-subtraction\",\n               intFloatToFloatBinary(m, BINOP(-), Module::SUB_MAGIC_NAME));\n  registerRule(\n      \"int-float-constant-floor-div\",\n      intFloatToFloatBinary(m, BINOP(/), Module::TRUE_DIV_MAGIC_NAME, pyNumerics));\n  registerRule(\"int-float-constant-mul\",\n               intFloatToFloatBinary(m, BINOP(*), Module::MUL_MAGIC_NAME));\n\n  // binary, double constant, int,float->bool\n  registerRule(\"int-float-constant-eq\",\n               intFloatToBoolBinary(m, BINOP(==), Module::EQ_MAGIC_NAME));\n  registerRule(\"int-float-constant-ne\",\n               intFloatToBoolBinary(m, BINOP(!=), Module::NE_MAGIC_NAME));\n  registerRule(\"int-float-constant-gt\",\n               intFloatToBoolBinary(m, BINOP(>), Module::GT_MAGIC_NAME));\n  registerRule(\"int-float-constant-ge\",\n               intFloatToBoolBinary(m, BINOP(>=), Module::GE_MAGIC_NAME));\n  registerRule(\"int-float-constant-lt\",\n               intFloatToBoolBinary(m, BINOP(<), Module::LT_MAGIC_NAME));\n  registerRule(\"int-float-constant-le\",\n               intFloatToBoolBinary(m, BINOP(<=), Module::LE_MAGIC_NAME));\n\n  // type conversions, identity\n  registerRule(\"int-constant-int\",\n               identityConvert(m, Module::INT_MAGIC_NAME, m->getIntType()));\n  registerRule(\"float-constant-float\",\n               identityConvert(m, Module::FLOAT_MAGIC_NAME, m->getFloatType()));\n  registerRule(\"bool-constant-bool\",\n               identityConvert(m, Module::BOOL_MAGIC_NAME, m->getBoolType()));\n\n  // type conversions, distinct\n  registerRule(\"float-constant-int\",\n               typeConvert<double, int64_t>(m, Module::INT_MAGIC_NAME,\n                                            m->getFloatType(), m->getIntType()));\n  registerRule(\"bool-constant-int\",\n               typeConvert<bool, int64_t>(m, Module::INT_MAGIC_NAME, m->getBoolType(),\n                                          m->getIntType()));\n  registerRule(\"int-constant-float\",\n               typeConvert<int64_t, double>(m, Module::FLOAT_MAGIC_NAME,\n                                            m->getIntType(), m->getFloatType()));\n  registerRule(\"bool-constant-float\",\n               typeConvert<bool, double>(m, Module::FLOAT_MAGIC_NAME, m->getBoolType(),\n                                         m->getFloatType()));\n  registerRule(\"int-constant-bool\",\n               typeConvert<int64_t, bool>(m, Module::BOOL_MAGIC_NAME, m->getIntType(),\n                                          m->getBoolType()));\n  registerRule(\"float-constant-bool\",\n               typeConvert<double, bool>(m, Module::BOOL_MAGIC_NAME, m->getFloatType(),\n                                         m->getBoolType()));\n}\n\n} // namespace folding\n} // namespace transform\n} // namespace ir\n} // namespace codon\n\n#undef BINOP\n#undef UNOP\n"
  },
  {
    "path": "codon/cir/transform/folding/const_fold.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <unordered_map>\n\n#include \"codon/cir/transform/folding/rule.h\"\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace folding {\n\nclass FoldingPass : public OperatorPass, public Rewriter {\nprivate:\n  bool pyNumerics;\n\n  void registerStandardRules(Module *m);\n\npublic:\n  /// Constructs a folding pass.\n  FoldingPass(bool pyNumerics = false)\n      : OperatorPass(/*childrenFirst=*/true), pyNumerics(pyNumerics) {}\n\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n\n  void run(Module *m) override;\n  void handle(CallInstr *v) override;\n};\n\n} // namespace folding\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/folding/const_prop.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"const_prop.h\"\n\n#include \"codon/cir/analyze/dataflow/reaching.h\"\n#include \"codon/cir/analyze/module/global_vars.h\"\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace folding {\nnamespace {\nbool okConst(const Value *v) {\n  return v && (isA<IntConst>(v) || isA<FloatConst>(v) || isA<BoolConst>(v));\n}\n} // namespace\n\nconst std::string ConstPropPass::KEY = \"core-folding-const-prop\";\n\nvoid ConstPropPass::handle(VarValue *v) {\n  auto *M = v->getModule();\n  auto *var = v->getVar();\n  if (var->isThreadLocal())\n    return;\n  Value *replacement;\n\n  if (var->isGlobal()) {\n    auto *r = getAnalysisResult<analyze::module::GlobalVarsResult>(globalVarsKey);\n    if (!r)\n      return;\n\n    auto it = r->assignments.find(var->getId());\n    if (it == r->assignments.end())\n      return;\n\n    auto *constDef = M->getValue(it->second);\n    if (!okConst(constDef))\n      return;\n\n    util::CloneVisitor cv(M);\n    replacement = cv.clone(constDef);\n  } else {\n    auto *r = getAnalysisResult<analyze::dataflow::RDResult>(reachingDefKey);\n    if (!r)\n      return;\n    auto *c = r->cfgResult;\n\n    auto it = r->results.find(getParentFunc()->getId());\n    if (it == r->results.end())\n      return;\n\n    auto *rd = it->second.get();\n    auto reaching = rd->getReachingDefinitions(var, v);\n\n    if (reaching.size() != 1 || !reaching[0].known())\n      return;\n\n    auto *constDef = reaching[0].assignee;\n    if (!okConst(constDef))\n      return;\n\n    util::CloneVisitor cv(M);\n    replacement = cv.clone(constDef);\n  }\n\n  v->replaceAll(replacement);\n}\n\nvoid ConstPropPass::handle(ExtractInstr *v) {\n  // Propagate constant tuples\n\n  if (!isA<VarValue>(v->getVal()) || !isA<types::RecordType>(v->getVal()->getType()))\n    return;\n\n  auto *var = cast<VarValue>(v->getVal())->getVar();\n  if (var->isGlobal())\n    return;\n\n  auto *r = getAnalysisResult<analyze::dataflow::RDResult>(reachingDefKey);\n  if (!r)\n    return;\n\n  auto *c = r->cfgResult;\n  auto it = r->results.find(getParentFunc()->getId());\n  if (it == r->results.end())\n    return;\n\n  auto *rd = it->second.get();\n  auto reaching = rd->getReachingDefinitions(var, v);\n  if (reaching.size() != 1 || !reaching[0].known())\n    return;\n\n  auto *call = cast<CallInstr>(reaching[0].assignee);\n  if (!call)\n    return;\n\n  auto *func = util::getFunc(call->getCallee());\n  if (!func || func->getUnmangledName() != Module::NEW_MAGIC_NAME)\n    return;\n\n  auto *tuple = cast<types::RecordType>(func->getParentType());\n  if (!tuple || tuple->getName() != \"Tuple\")\n    return;\n\n  auto idx =\n      cast<types::RecordType>(v->getVal()->getType())->getMemberIndex(v->getField());\n  if (idx < 0 || idx >= call->numArgs() || !okConst(*(call->begin() + idx)))\n    return;\n\n  util::CloneVisitor cv(v->getModule());\n  auto *replacement = cv.clone(*(call->begin() + idx));\n  v->replaceAll(replacement);\n}\n\n} // namespace folding\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/folding/const_prop.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace folding {\n\n/// Constant propagation pass.\nclass ConstPropPass : public OperatorPass {\nprivate:\n  /// Key of the reaching definition analysis\n  std::string reachingDefKey;\n  /// Key of the global variables analysis\n  std::string globalVarsKey;\n\npublic:\n  static const std::string KEY;\n\n  /// Constructs a constant propagation pass.\n  /// @param reachingDefKey the reaching definition analysis' key\n  /// @param globalVarsKey global variables analysis' key\n  ConstPropPass(const std::string &reachingDefKey, const std::string &globalVarsKey)\n      : reachingDefKey(reachingDefKey), globalVarsKey(globalVarsKey) {}\n\n  std::string getKey() const override { return KEY; }\n  void handle(VarValue *v) override;\n  void handle(ExtractInstr *v) override;\n};\n\n} // namespace folding\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/folding/folding.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"folding.h\"\n\n#include \"codon/cir/transform/folding/const_fold.h\"\n#include \"codon/cir/transform/folding/const_prop.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace folding {\n\nconst std::string FoldingPassGroup::KEY = \"core-folding-pass-group\";\n\nFoldingPassGroup::FoldingPassGroup(const std::string &sideEffectsPass,\n                                   const std::string &reachingDefPass,\n                                   const std::string &globalVarPass, int repeat,\n                                   bool runGlobalDemotion, bool pyNumerics)\n    : PassGroup(repeat) {\n  auto gdUnique = runGlobalDemotion ? std::make_unique<cleanup::GlobalDemotionPass>()\n                                    : std::unique_ptr<cleanup::GlobalDemotionPass>();\n  auto canonUnique = std::make_unique<cleanup::CanonicalizationPass>(sideEffectsPass);\n  auto fpUnique = std::make_unique<FoldingPass>(pyNumerics);\n  auto dceUnique = std::make_unique<cleanup::DeadCodeCleanupPass>(sideEffectsPass);\n\n  gd = gdUnique.get();\n  canon = canonUnique.get();\n  fp = fpUnique.get();\n  dce = dceUnique.get();\n\n  if (runGlobalDemotion)\n    push_back(std::move(gdUnique));\n  push_back(std::make_unique<ConstPropPass>(reachingDefPass, globalVarPass));\n  push_back(std::move(canonUnique));\n  push_back(std::move(fpUnique));\n  push_back(std::move(dceUnique));\n}\n\nbool FoldingPassGroup::shouldRepeat(int num) const {\n  return PassGroup::shouldRepeat(num) &&\n         ((gd && gd->getNumDemotions() != 0) || canon->getNumReplacements() != 0 ||\n          fp->getNumReplacements() != 0 || dce->getNumReplacements() != 0);\n}\n\n} // namespace folding\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/folding/folding.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/cleanup/canonical.h\"\n#include \"codon/cir/transform/cleanup/dead_code.h\"\n#include \"codon/cir/transform/cleanup/global_demote.h\"\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace folding {\n\nclass FoldingPass;\n\n/// Group of constant folding passes.\nclass FoldingPassGroup : public PassGroup {\nprivate:\n  cleanup::GlobalDemotionPass *gd;\n  cleanup::CanonicalizationPass *canon;\n  FoldingPass *fp;\n  cleanup::DeadCodeCleanupPass *dce;\n\npublic:\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n\n  /// @param sideEffectsPass the key of the side effects pass\n  /// @param reachingDefPass the key of the reaching definitions pass\n  /// @param globalVarPass the key of the global variables pass\n  /// @param repeat default number of times to repeat the pass\n  /// @param runGlobalDemotion whether to demote globals if possible\n  /// @param pyNumerics whether to use Python (vs. C) semantics when folding\n  FoldingPassGroup(const std::string &sideEffectsPass,\n                   const std::string &reachingDefPass, const std::string &globalVarPass,\n                   int repeat = 5, bool runGlobalDemotion = true,\n                   bool pyNumerics = false);\n\n  bool shouldRepeat(int num) const override;\n};\n\n} // namespace folding\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/folding/rule.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <algorithm>\n#include <utility>\n\n#include \"codon/cir/transform/pass.h\"\n#include \"codon/cir/transform/rewrite.h\"\n#include \"codon/cir/util/irtools.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace folding {\n\n/// Commutative, binary rule that requires a single constant.\ntemplate <typename ConstantType>\nclass SingleConstantCommutativeRule : public RewriteRule {\npublic:\n  using Calculator = std::function<Value *(Value *)>;\n  enum Kind { LEFT, RIGHT, COMMUTATIVE };\n\nprivate:\n  /// the value being matched against\n  ConstantType val;\n  /// the type being matched\n  types::Type *type;\n  /// the magic method name\n  std::string magic;\n  /// the calculator\n  Calculator calc;\n  /// left, right or commutative\n  Kind kind;\n\npublic:\n  /// Constructs a commutative rule.\n  /// @param val the matched value\n  /// @param newVal the result\n  /// @param magic the magic name\n  /// @param kind left, right, or commutative\n  /// @param type the matched type\n  SingleConstantCommutativeRule(ConstantType val, ConstantType newVal,\n                                std::string magic, Kind kind, types::Type *type)\n      : val(val), type(type), magic(std::move(magic)), kind(kind) {\n    calc = [=](Value *v) -> Value * {\n      return v->getModule()->N<TemplatedConst<ConstantType>>(v->getSrcInfo(), val,\n                                                             type);\n    };\n  }\n  /// Constructs a commutative rule.\n  /// @param val the matched value\n  /// @param newVal the result\n  /// @param magic the magic name\n  /// @param calc the calculator\n  /// @param kind left, right, or commutative\n  /// @param type the matched type\n  SingleConstantCommutativeRule(ConstantType val, Calculator calc, std::string magic,\n                                Kind kind, types::Type *type)\n      : val(val), type(type), magic(std::move(magic)), calc(std::move(calc)),\n        kind(kind) {}\n\n  virtual ~SingleConstantCommutativeRule() noexcept = default;\n\n  void visit(CallInstr *v) override {\n    if (!util::isCallOf(v, magic, {type, type}, type, /*method=*/true))\n      return;\n\n    auto *left = v->front();\n    auto *right = v->back();\n    auto *leftConst = cast<TemplatedConst<ConstantType>>(left);\n    auto *rightConst = cast<TemplatedConst<ConstantType>>(right);\n\n    if ((kind == Kind::COMMUTATIVE || kind == Kind::LEFT) && leftConst &&\n        leftConst->getVal() == val)\n      return setResult(calc(right));\n    if ((kind == Kind::COMMUTATIVE || kind == Kind::RIGHT) && rightConst &&\n        rightConst->getVal() == val)\n      return setResult(calc(left));\n  }\n};\n\n/// Binary rule that requires two constants.\ntemplate <typename ConstantType, typename Func, typename OutputType = ConstantType>\nclass DoubleConstantBinaryRule : public RewriteRule {\nprivate:\n  /// the calculator\n  Func f;\n  /// the input type\n  types::Type *inputType;\n  /// the output type\n  types::Type *resultType;\n  /// the magic method name\n  std::string magic;\n\npublic:\n  /// Constructs a binary rule.\n  /// @param f the calculator\n  /// @param magic the magic method name\n  /// @param inputType the input type\n  /// @param resultType the output type\n  DoubleConstantBinaryRule(Func f, std::string magic, types::Type *inputType,\n                           types::Type *resultType)\n      : f(std::move(f)), inputType(inputType), resultType(resultType),\n        magic(std::move(magic)) {}\n\n  virtual ~DoubleConstantBinaryRule() noexcept = default;\n\n  void visit(CallInstr *v) override {\n    if (!util::isCallOf(v, magic, {inputType, inputType}, resultType, /*method=*/true))\n      return;\n\n    auto *left = v->front();\n    auto *right = v->back();\n    auto *leftConst = cast<TemplatedConst<ConstantType>>(left);\n    auto *rightConst = cast<TemplatedConst<ConstantType>>(right);\n\n    if (leftConst && rightConst)\n      return setResult(toValue(v, f(leftConst->getVal(), rightConst->getVal())));\n  }\n\nprivate:\n  Value *toValue(Value *, Value *v) { return v; }\n\n  Value *toValue(Value *og, OutputType v) {\n    return og->getModule()->template N<TemplatedConst<OutputType>>(og->getSrcInfo(), v,\n                                                                   resultType);\n  }\n};\n\n/// Unary rule that requires one constant.\ntemplate <typename ConstantType, typename Func>\nclass SingleConstantUnaryRule : public RewriteRule {\nprivate:\n  /// the calculator\n  Func f;\n  /// the input type\n  types::Type *inputType;\n  /// the output type\n  types::Type *resultType;\n  /// the magic method name\n  std::string magic;\n\npublic:\n  /// Constructs a unary rule.\n  /// @param f the calculator\n  /// @param magic the magic method name\n  /// @param inputType the input type\n  /// @param resultType the output type\n  SingleConstantUnaryRule(Func f, std::string magic, types::Type *inputType,\n                          types::Type *resultType)\n      : f(std::move(f)), inputType(inputType), resultType(resultType),\n        magic(std::move(magic)) {}\n\n  virtual ~SingleConstantUnaryRule() noexcept = default;\n\n  void visit(CallInstr *v) override {\n    if (!util::isCallOf(v, magic, {inputType}, resultType, /*method=*/true))\n      return;\n\n    auto *arg = v->front();\n    auto *argConst = cast<TemplatedConst<ConstantType>>(arg);\n    if (argConst)\n      return setResult(toValue(v, f(argConst->getVal())));\n  }\n\nprivate:\n  Value *toValue(Value *, Value *v) { return v; }\n\n  template <typename NewType> Value *toValue(Value *og, NewType v) {\n    return og->getModule()->template N<TemplatedConst<NewType>>(og->getSrcInfo(), v,\n                                                                resultType);\n  }\n};\n\n/// Unary rule that requires no constant.\ntemplate <typename Func> class UnaryRule : public RewriteRule {\nprivate:\n  /// the calculator\n  Func f;\n  /// the input type\n  types::Type *inputType;\n  /// the magic method name\n  std::string magic;\n\npublic:\n  /// Constructs a unary rule.\n  /// @param f the calculator\n  /// @param magic the magic method name\n  /// @param inputType the input type\n  UnaryRule(Func f, std::string magic, types::Type *inputType)\n      : f(std::move(f)), inputType(inputType), magic(std::move(magic)) {}\n\n  virtual ~UnaryRule() noexcept = default;\n\n  void visit(CallInstr *v) override {\n    if (!util::isCallOf(v, magic, {inputType}, inputType, /*method=*/true))\n      return;\n\n    auto *arg = v->front();\n    return setResult(f(arg));\n  }\n};\n\n/// Rule that eliminates an operation, like \"+x\".\nclass NoOpRule : public RewriteRule {\nprivate:\n  /// the input type\n  types::Type *inputType;\n  /// the magic method name\n  std::string magic;\n\npublic:\n  /// Constructs a no-op rule.\n  /// @param magic the magic method name\n  /// @param inputType the input type\n  NoOpRule(std::string magic, types::Type *inputType)\n      : inputType(inputType), magic(std::move(magic)) {}\n\n  virtual ~NoOpRule() noexcept = default;\n\n  void visit(CallInstr *v) override {\n    if (!util::isCallOf(v, magic, {inputType}, inputType, /*method=*/true))\n      return;\n\n    auto *arg = v->front();\n    return setResult(arg);\n  }\n};\n\n/// Rule that eliminates a double-application of an operation, like \"-(-x)\".\nclass DoubleApplicationNoOpRule : public RewriteRule {\nprivate:\n  /// the input type\n  types::Type *inputType;\n  /// the magic method name\n  std::string magic;\n\npublic:\n  /// Constructs a double-application no-op rule.\n  /// @param magic the magic method name\n  /// @param inputType the input type\n  DoubleApplicationNoOpRule(std::string magic, types::Type *inputType)\n      : inputType(inputType), magic(std::move(magic)) {}\n\n  virtual ~DoubleApplicationNoOpRule() noexcept = default;\n\n  void visit(CallInstr *v) override {\n    if (!util::isCallOf(v, magic, {inputType}, inputType, /*method=*/true))\n      return;\n\n    if (!util::isCallOf(v->front(), magic, {inputType}, inputType, /*method=*/true))\n      return;\n\n    auto *arg = v->front();\n    return setResult(cast<CallInstr>(arg)->front());\n  }\n};\n\n} // namespace folding\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/lowering/async_for.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"async_for.h\"\n\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace lowering {\n\nconst std::string AsyncForLowering::KEY = \"core-async-for-lowering\";\n\nvoid AsyncForLowering::handle(ForFlow *v) {\n  if (!v->isAsync())\n    return;\n\n  auto *M = v->getModule();\n  auto *coro = v->getIter();\n  auto *coroType = coro->getType();\n  util::CloneVisitor cv(M);\n\n  auto *promise = M->getOrRealizeFunc(\"_promise\", {coroType}, {}, \"std.asyncio\");\n  seqassertn(promise, \"promise func not found\");\n  auto *done = M->getOrRealizeFunc(\"_done\", {coroType}, {}, \"std.asyncio\");\n  seqassertn(done, \"done func not found\");\n  auto *resume = M->getOrRealizeFunc(\"_resume\", {coroType}, {}, \"std.asyncio\");\n  seqassertn(resume, \"resume func not found\");\n  auto *currTask = M->getOrRealizeFunc(\"_curr_task\", {}, {}, \"std.asyncio\");\n  seqassertn(currTask, \"curr-task func not found\");\n  auto *waiting = M->getOrRealizeFunc(\"_is_waiting\", {util::getReturnType(currTask)},\n                                      {}, \"std.asyncio\");\n  seqassertn(waiting, \"is-waiting func not found\");\n\n  // Construct the following:\n  //   task = curr_task()\n  //   if not coro.__done__():\n  //       while True:\n  //           coro.__resume__()\n  //           if coro.__done__():\n  //               break\n  //           if is_waiting(task):\n  //               yield\n  //           else:\n  //               i = coro.__promise__()\n  //               <BODY>\n  auto *series = M->Nr<SeriesFlow>();\n  auto *taskVar = util::makeVar(util::call(currTask, {}), series,\n                                cast<BodiedFunc>(getParentFunc()));\n  auto *coroVar =\n      util::makeVar(cv.clone(coro), series, cast<BodiedFunc>(getParentFunc()));\n\n  series->push_back(M->Nr<IfFlow>(\n      ~*util::call(done, {M->Nr<VarValue>(coroVar)}),\n      util::series(M->Nr<WhileFlow>(\n          M->getBool(true),\n          util::series(\n              util::call(resume, {M->Nr<VarValue>(coroVar)}),\n              M->Nr<IfFlow>(util::call(done, {M->Nr<VarValue>(coroVar)}),\n                            util::series(M->Nr<BreakInstr>())),\n              M->Nr<IfFlow>(\n                  util::call(waiting, {M->Nr<VarValue>(taskVar)}),\n                  util::series(M->Nr<YieldInstr>()),\n                  util::series(\n                      M->Nr<AssignInstr>(\n                          v->getVar(), util::call(promise, {M->Nr<VarValue>(coroVar)})),\n                      cv.clone(v->getBody()))))))));\n\n  v->replaceAll(series);\n}\n\n} // namespace lowering\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/lowering/async_for.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace lowering {\n\nclass AsyncForLowering : public OperatorPass {\npublic:\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n  void handle(ForFlow *v) override;\n};\n\n} // namespace lowering\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/lowering/await.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"await.h\"\n\n#include <algorithm>\n\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace lowering {\nnamespace {\n\nbool isFuture(const types::Type *type) {\n  return type->getName().rfind(\"std.asyncio.Future.\", 0) == 0;\n}\n\nbool isTask(const types::Type *type) {\n  return type->getName().rfind(\"std.asyncio.Task.\", 0) == 0;\n}\n\nconst types::GeneratorType *isCoroutine(const types::Type *type) {\n  return cast<types::GeneratorType>(type);\n}\n\n} // namespace\n\nconst std::string AwaitLowering::KEY = \"core-await-lowering\";\n\nvoid AwaitLowering::handle(AwaitInstr *v) {\n  auto *M = v->getModule();\n  auto *value = v->getValue();\n  auto *resultType = v->getType();\n  auto *valueType = value->getType();\n  util::CloneVisitor cv(M);\n\n  if (isFuture(valueType) || isTask(valueType)) {\n    auto *getResult = M->getOrRealizeMethod(valueType, \"result\", {valueType});\n    seqassertn(getResult, \"get-result method not found\");\n    auto *waitOn = M->getOrRealizeFunc(\"_wait_on\", {valueType}, {}, \"std.asyncio\");\n    seqassertn(waitOn, \"wait-on function not found\");\n    auto *cancelCheck =\n        M->getOrRealizeFunc(\"_cancel_checkpoint\", {}, {}, \"std.asyncio\");\n    seqassertn(cancelCheck, \"cancel-checkpoint function not found\");\n\n    // Construct the following:\n    //   cancel_checkpoint()\n    //   if _wait_on(value, future):\n    //      yield\n    //      cancel_checkpoint()\n    //   future.result()\n    auto *series = M->Nr<SeriesFlow>();\n    auto *futureVar =\n        util::makeVar(cv.clone(value), series, cast<BodiedFunc>(getParentFunc()));\n    series->push_back(util::call(cancelCheck, {}));\n    series->push_back(\n        M->Nr<IfFlow>(util::call(waitOn, {M->Nr<VarValue>(futureVar)}),\n                      util::series(M->Nr<YieldInstr>(), util::call(cancelCheck, {}))));\n    auto *replacement =\n        M->Nr<FlowInstr>(series, util::call(getResult, {M->Nr<VarValue>(futureVar)}));\n    v->replaceAll(replacement);\n  } else if (auto *genType = isCoroutine(valueType)) {\n    auto *promise = M->getOrRealizeFunc(\"_promise\", {valueType}, {}, \"std.asyncio\");\n    seqassertn(promise, \"promise function not found\");\n\n    // Construct the following:\n    //   for _ in coro:\n    //       yield\n    //   coro.__promise__()\n    auto *series = M->Nr<SeriesFlow>();\n    auto *coroVar =\n        util::makeVar(cv.clone(value), series, cast<BodiedFunc>(getParentFunc()));\n\n    auto *var = M->Nr<Var>(genType->getBase(), /*global=*/false);\n    cast<BodiedFunc>(getParentFunc())->push_back(var);\n    SeriesFlow *body;\n    if (v->isGenerator()) {\n      auto *requeue = M->getOrRealizeFunc(\"_requeue\", {}, {}, \"std.asyncio\");\n      seqassertn(requeue, \"requeue function not found\");\n      body = util::series(util::call(requeue, {}), M->Nr<YieldInstr>());\n    } else {\n      body = util::series(M->Nr<YieldInstr>());\n    }\n\n    series->push_back(M->Nr<ForFlow>(M->Nr<VarValue>(coroVar), body, var));\n    auto *replacement =\n        M->Nr<FlowInstr>(series, util::call(promise, {M->Nr<VarValue>(coroVar)}));\n    v->replaceAll(replacement);\n  } else {\n    seqassertn(false, \"unexpected value type '{}' in await instruction\",\n               valueType->getName());\n  }\n}\n\n} // namespace lowering\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/lowering/await.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace lowering {\n\nclass AwaitLowering : public OperatorPass {\npublic:\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n  void handle(AwaitInstr *v) override;\n};\n\n} // namespace lowering\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/lowering/imperative.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"imperative.h\"\n\n#include <algorithm>\n\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/cir/util/matching.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace lowering {\nnamespace {\nCallInstr *getRangeIter(Value *iter) {\n  auto *M = iter->getModule();\n\n  auto *iterCall = cast<CallInstr>(iter);\n  if (!iterCall || iterCall->numArgs() != 1)\n    return nullptr;\n\n  auto *iterFunc = util::getFunc(iterCall->getCallee());\n  if (!iterFunc || iterFunc->getUnmangledName() != Module::ITER_MAGIC_NAME)\n    return nullptr;\n\n  auto *rangeCall = cast<CallInstr>(iterCall->front());\n  if (!rangeCall)\n    return nullptr;\n\n  auto *newRangeFunc = util::getFunc(rangeCall->getCallee());\n  if (!newRangeFunc || newRangeFunc->getUnmangledName() != Module::NEW_MAGIC_NAME)\n    return nullptr;\n  auto *parentType = newRangeFunc->getParentType();\n  auto *rangeType =\n      M->getOrRealizeType(ast::getMangledClass(\"std.internal.types.range\", \"range\"));\n\n  if (!parentType || !rangeType || parentType->getName() != rangeType->getName())\n    return nullptr;\n\n  return rangeCall;\n}\n\nValue *getListIter(Value *iter) {\n  auto *iterCall = cast<CallInstr>(iter);\n  if (!iterCall || iterCall->numArgs() != 1)\n    return nullptr;\n\n  auto *iterFunc = util::getFunc(iterCall->getCallee());\n  if (!iterFunc || iterFunc->getUnmangledName() != Module::ITER_MAGIC_NAME)\n    return nullptr;\n\n  auto *list = iterCall->front();\n  if (list->getType()->getName().rfind(\n          ast::getMangledClass(\"std.internal.types.array\", \"List\") + \"[\", 0) != 0)\n    return nullptr;\n\n  return list;\n}\n} // namespace\n\nconst std::string ImperativeForFlowLowering::KEY = \"core-imperative-for-lowering\";\n\nvoid ImperativeForFlowLowering::handle(ForFlow *v) {\n  auto *M = v->getModule();\n  auto *iter = v->getIter();\n  std::unique_ptr<parallel::OMPSched> sched;\n  if (v->isParallel())\n    sched = std::make_unique<parallel::OMPSched>(*v->getSchedule());\n\n  if (auto *rangeCall = getRangeIter(iter)) {\n    auto it = rangeCall->begin();\n    auto argCount = std::distance(it, rangeCall->end());\n\n    util::CloneVisitor cv(M);\n\n    IntConst *stepConst;\n    Value *start;\n    Value *end;\n    int64_t step = 0;\n\n    switch (argCount) {\n    case 1:\n      start = M->getInt(0);\n      end = cv.clone(*it);\n      step = 1;\n      break;\n    case 2:\n      start = cv.clone(*it++);\n      end = cv.clone(*it);\n      step = 1;\n      break;\n    case 3:\n      start = cv.clone(*it++);\n      end = cv.clone(*it++);\n      stepConst = cast<IntConst>(*it);\n      if (!stepConst)\n        return;\n      step = stepConst->getVal();\n      break;\n    default:\n      seqassertn(false, \"unknown range constructor\");\n    }\n    if (step == 0)\n      return;\n\n    v->replaceAll(M->N<ImperativeForFlow>(v->getSrcInfo(), start, step, end,\n                                          v->getBody(), v->getVar(), std::move(sched)));\n  } else if (auto *list = getListIter(iter)) {\n    // convert:\n    //   for a in list:\n    //     body\n    // into:\n    //   v = list\n    //   n = v.len\n    //   p = v.arr.ptr\n    //   imp_for i in range(0, n, 1):\n    //     a = p[i]\n    //     body\n    auto *parent = cast<BodiedFunc>(getParentFunc());\n    auto *series = M->N<SeriesFlow>(v->getSrcInfo());\n    auto *listVar = util::makeVar(list, series, parent);\n    auto *lenVal = M->Nr<ExtractInstr>(M->Nr<VarValue>(listVar), \"len\");\n    auto *lenVar = util::makeVar(lenVal, series, parent);\n    auto *ptrVal = M->Nr<ExtractInstr>(\n        M->Nr<ExtractInstr>(M->Nr<VarValue>(listVar), \"arr\"), \"ptr\");\n    auto *ptrVar = util::makeVar(ptrVal, series, parent);\n\n    auto *body = cast<SeriesFlow>(v->getBody());\n    seqassertn(body, \"loop body is not a series flow [{}]\", v->getSrcInfo());\n    auto *oldLoopVar = v->getVar();\n    auto *newLoopVar = M->Nr<Var>(M->getIntType());\n    parent->push_back(newLoopVar);\n    auto *replacement = M->N<ImperativeForFlow>(v->getSrcInfo(), M->getInt(0), 1,\n                                                M->Nr<VarValue>(lenVar), body,\n                                                newLoopVar, std::move(sched));\n    series->push_back(replacement);\n    body->insert(\n        body->begin(),\n        M->Nr<AssignInstr>(oldLoopVar,\n                           (*M->Nr<VarValue>(ptrVar))[*M->Nr<VarValue>(newLoopVar)]));\n    v->replaceAll(series);\n  }\n}\n\n} // namespace lowering\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/lowering/imperative.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace lowering {\n\nclass ImperativeForFlowLowering : public OperatorPass {\npublic:\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n  void handle(ForFlow *v) override;\n};\n\n} // namespace lowering\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/lowering/pipeline.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"pipeline.h\"\n\n#include <algorithm>\n\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/cir/util/matching.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace lowering {\nnamespace {\nValue *callStage(Module *M, PipelineFlow::Stage *stage, Value *last) {\n  std::vector<Value *> args;\n  for (auto *arg : *stage) {\n    args.push_back(arg ? arg : last);\n  }\n  return M->N<CallInstr>(stage->getCallee()->getSrcInfo(), stage->getCallee(), args);\n}\n\nValue *convertPipelineToForLoopsHelper(Module *M, BodiedFunc *parent,\n                                       const std::vector<PipelineFlow::Stage *> &stages,\n                                       unsigned idx = 0, Value *last = nullptr) {\n  if (idx >= stages.size())\n    return last;\n\n  auto *stage = stages[idx];\n  if (idx == 0)\n    return convertPipelineToForLoopsHelper(M, parent, stages, idx + 1,\n                                           stage->getCallee());\n\n  auto *prev = stages[idx - 1];\n  if (prev->isGenerator()) {\n    auto *var = M->Nr<Var>(prev->getOutputElementType());\n    parent->push_back(var);\n    auto *body = convertPipelineToForLoopsHelper(\n        M, parent, stages, idx + 1, callStage(M, stage, M->Nr<VarValue>(var)));\n    auto *loop = M->N<ForFlow>(last->getSrcInfo(), last, util::series(body), var);\n    if (stage->isParallel())\n      loop->setParallel();\n    return loop;\n  } else {\n    return convertPipelineToForLoopsHelper(M, parent, stages, idx + 1,\n                                           callStage(M, stage, last));\n  }\n}\n\nValue *convertPipelineToForLoops(PipelineFlow *p, BodiedFunc *parent) {\n  std::vector<PipelineFlow::Stage *> stages;\n  for (auto &stage : *p) {\n    stages.push_back(&stage);\n  }\n  return convertPipelineToForLoopsHelper(p->getModule(), parent, stages);\n}\n} // namespace\n\nconst std::string PipelineLowering::KEY = \"core-pipeline-lowering\";\n\nvoid PipelineLowering::handle(PipelineFlow *v) {\n  v->replaceAll(convertPipelineToForLoops(v, cast<BodiedFunc>(getParentFunc())));\n}\n\n} // namespace lowering\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/lowering/pipeline.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace lowering {\n\n/// Converts pipelines to for-loops\nclass PipelineLowering : public OperatorPass {\npublic:\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n  void handle(PipelineFlow *v) override;\n};\n\n} // namespace lowering\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/manager.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"manager.h\"\n\n#include <unordered_set>\n\n#include \"codon/cir/analyze/analysis.h\"\n#include \"codon/cir/analyze/dataflow/capture.h\"\n#include \"codon/cir/analyze/dataflow/cfg.h\"\n#include \"codon/cir/analyze/dataflow/dominator.h\"\n#include \"codon/cir/analyze/dataflow/reaching.h\"\n#include \"codon/cir/analyze/module/global_vars.h\"\n#include \"codon/cir/analyze/module/side_effect.h\"\n#include \"codon/cir/transform/folding/folding.h\"\n#include \"codon/cir/transform/lowering/async_for.h\"\n#include \"codon/cir/transform/lowering/await.h\"\n#include \"codon/cir/transform/lowering/imperative.h\"\n#include \"codon/cir/transform/lowering/pipeline.h\"\n#include \"codon/cir/transform/manager.h\"\n#include \"codon/cir/transform/numpy/indexing.h\"\n#include \"codon/cir/transform/numpy/numpy.h\"\n#include \"codon/cir/transform/parallel/openmp.h\"\n#include \"codon/cir/transform/pass.h\"\n#include \"codon/cir/transform/pythonic/dict.h\"\n#include \"codon/cir/transform/pythonic/generator.h\"\n#include \"codon/cir/transform/pythonic/io.h\"\n#include \"codon/cir/transform/pythonic/list.h\"\n#include \"codon/cir/transform/pythonic/str.h\"\n#include \"codon/util/common.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\n\nstd::string PassManager::KeyManager::getUniqueKey(const std::string &key) {\n  // make sure we can't ever produce duplicate \"unique'd\" keys\n  seqassertn(key.find(':') == std::string::npos,\n             \"pass key '{}' contains invalid character ':'\", key);\n  auto it = keys.find(key);\n  if (it == keys.end()) {\n    keys.emplace(key, 1);\n    return key;\n  } else {\n    auto id = ++(it->second);\n    return key + \":\" + std::to_string(id);\n  }\n}\n\nstd::string PassManager::registerPass(std::unique_ptr<Pass> pass,\n                                      const std::string &insertBefore,\n                                      std::vector<std::string> reqs,\n                                      std::vector<std::string> invalidates) {\n  std::string key = pass->getKey();\n  if (isDisabled(key))\n    return \"\";\n  key = km.getUniqueKey(key);\n\n  for (const auto &req : reqs) {\n    seqassertn(deps.find(req) != deps.end(), \"required key '{}' not found\", req);\n    deps[req].push_back(key);\n  }\n\n  passes.insert(std::make_pair(\n      key, PassMetadata(std::move(pass), std::move(reqs), std::move(invalidates))));\n  passes[key].pass->setManager(this);\n  if (insertBefore.empty()) {\n    executionOrder.push_back(key);\n  } else {\n    auto it = std::find(executionOrder.begin(), executionOrder.end(), insertBefore);\n    seqassertn(it != executionOrder.end(), \"pass with key '{}' not found in manager\",\n               insertBefore);\n    executionOrder.insert(it, key);\n  }\n  return key;\n}\n\nstd::string PassManager::registerAnalysis(std::unique_ptr<analyze::Analysis> analysis,\n                                          std::vector<std::string> reqs) {\n\n  std::string key = analysis->getKey();\n  if (isDisabled(key))\n    return \"\";\n  key = km.getUniqueKey(key);\n\n  for (const auto &req : reqs) {\n    seqassertn(deps.find(req) != deps.end(), \"required key '{}' not found\", req);\n    deps[req].push_back(key);\n  }\n\n  analyses.insert(\n      std::make_pair(key, AnalysisMetadata(std::move(analysis), std::move(reqs))));\n  analyses[key].analysis->setManager(this);\n  deps[key] = {};\n  return key;\n}\n\nvoid PassManager::run(Module *module) {\n  for (auto &p : executionOrder) {\n    runPass(module, p);\n  }\n}\n\nvoid PassManager::runPass(Module *module, const std::string &name) {\n  auto &meta = passes[name];\n\n  auto run = true;\n  auto it = 0;\n\n  while (run) {\n    for (auto &dep : meta.reqs) {\n      runAnalysis(module, dep);\n    }\n\n    Timer timer(\"  ir pass    : \" + meta.pass->getKey());\n    meta.pass->run(module);\n    timer.log();\n\n    for (auto &inv : meta.invalidates)\n      invalidate(inv);\n\n    run = meta.pass->shouldRepeat(++it);\n  }\n}\n\nvoid PassManager::runAnalysis(Module *module, const std::string &name) {\n  if (results.find(name) != results.end())\n    return;\n\n  auto &meta = analyses[name];\n  for (auto &dep : meta.reqs) {\n    runAnalysis(module, dep);\n  }\n\n  Timer timer(\"  ir analysis: \" + meta.analysis->getKey());\n  results[name] = meta.analysis->run(module);\n  timer.log();\n}\n\nvoid PassManager::invalidate(const std::string &key) {\n  std::unordered_set<std::string> open = {key};\n\n  while (!open.empty()) {\n    std::unordered_set<std::string> newOpen;\n    for (const auto &k : open) {\n      if (results.find(k) != results.end()) {\n        results.erase(k);\n        newOpen.insert(deps[k].begin(), deps[k].end());\n      }\n    }\n    open = std::move(newOpen);\n  }\n}\n\nvoid PassManager::registerStandardPasses(PassManager::Init init) {\n  switch (init) {\n  case Init::EMPTY:\n    break;\n  case Init::DEBUG: {\n    registerPass(std::make_unique<lowering::PipelineLowering>());\n    registerPass(std::make_unique<lowering::ImperativeForFlowLowering>());\n    registerPass(std::make_unique<lowering::AsyncForLowering>());\n    registerPass(std::make_unique<lowering::AwaitLowering>());\n    registerPass(std::make_unique<parallel::OpenMPPass>());\n    break;\n  }\n  case Init::RELEASE:\n  case Init::JIT: {\n    // Pythonic\n    registerPass(std::make_unique<pythonic::DictArithmeticOptimization>());\n    registerPass(std::make_unique<pythonic::ListAdditionOptimization>());\n    registerPass(std::make_unique<pythonic::StrAdditionOptimization>());\n    registerPass(std::make_unique<pythonic::GeneratorArgumentOptimization>());\n    registerPass(std::make_unique<pythonic::IOCatOptimization>());\n\n    // lowering\n    registerPass(std::make_unique<lowering::PipelineLowering>());\n    registerPass(std::make_unique<lowering::ImperativeForFlowLowering>());\n\n    // folding\n    auto cfgKey = registerAnalysis(std::make_unique<analyze::dataflow::CFAnalysis>());\n    auto rdKey = registerAnalysis(\n        std::make_unique<analyze::dataflow::RDAnalysis>(cfgKey), {cfgKey});\n    auto domKey = registerAnalysis(\n        std::make_unique<analyze::dataflow::DominatorAnalysis>(cfgKey), {cfgKey});\n    auto capKey = registerAnalysis(\n        std::make_unique<analyze::dataflow::CaptureAnalysis>(rdKey, domKey),\n        {rdKey, domKey});\n    auto globalKey =\n        registerAnalysis(std::make_unique<analyze::module::GlobalVarsAnalyses>());\n    auto seKey1 =\n        registerAnalysis(std::make_unique<analyze::module::SideEffectAnalysis>(\n                             capKey,\n                             /*globalAssignmentHasSideEffects=*/true),\n                         {capKey});\n    auto seKey2 =\n        registerAnalysis(std::make_unique<analyze::module::SideEffectAnalysis>(\n                             capKey,\n                             /*globalAssignmentHasSideEffects=*/false),\n                         {capKey});\n    registerPass(std::make_unique<folding::FoldingPassGroup>(\n                     seKey1, rdKey, globalKey, /*repeat=*/5, /*runGlobalDemoton=*/false,\n                     pyNumerics),\n                 /*insertBefore=*/\"\", {seKey1, rdKey, globalKey},\n                 {seKey1, rdKey, cfgKey, globalKey, capKey});\n    registerPass(std::make_unique<numpy::NumPyFusionPass>(rdKey, seKey2),\n                 /*insertBefore=*/\"\", {rdKey, seKey2},\n                 {seKey1, rdKey, cfgKey, globalKey, capKey});\n    registerPass(std::make_unique<lowering::ImperativeForFlowLowering>());\n    registerPass(std::make_unique<numpy::NumPyBoundsCheckElisionPass>(rdKey),\n                 /*insertBefore=*/\"\", {rdKey},\n                 {seKey1, rdKey, cfgKey, globalKey, capKey});\n\n    // async & parallel\n    registerPass(std::make_unique<lowering::AsyncForLowering>());\n    registerPass(std::make_unique<lowering::AwaitLowering>());\n    registerPass(std::make_unique<parallel::OpenMPPass>(), /*insertBefore=*/\"\", {},\n                 {cfgKey, globalKey});\n\n    if (init != Init::JIT) {\n      // Don't demote globals in JIT mode, since they might be used later\n      // by another user input.\n      registerPass(std::make_unique<folding::FoldingPassGroup>(\n                       seKey2, rdKey, globalKey,\n                       /*repeat=*/5,\n                       /*runGlobalDemoton=*/true, pyNumerics),\n                   /*insertBefore=*/\"\", {seKey2, rdKey, globalKey},\n                   {seKey2, rdKey, cfgKey, globalKey});\n    }\n    break;\n  }\n  default:\n    seqassertn(false, \"unknown PassManager init value\");\n  }\n}\n\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/manager.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <algorithm>\n#include <memory>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"codon/cir/analyze/analysis.h\"\n#include \"codon/cir/module.h\"\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\n\n/// Utility class to run a series of passes.\nclass PassManager {\nprivate:\n  /// Manager for keys of passes.\n  class KeyManager {\n  private:\n    /// mapping of raw key to number of occurences\n    std::unordered_map<std::string, int> keys;\n\n  public:\n    KeyManager() = default;\n    /// Returns a unique'd key for a given raw key.\n    /// Does so by appending \":<number>\" if the key\n    /// has been seen.\n    /// @param key the raw key\n    /// @return the unique'd key\n    std::string getUniqueKey(const std::string &key);\n  };\n\n  /// Container for pass metadata.\n  struct PassMetadata {\n    /// pointer to the pass instance\n    std::unique_ptr<Pass> pass;\n    /// vector of required analyses\n    std::vector<std::string> reqs;\n    /// vector of invalidated analyses\n    std::vector<std::string> invalidates;\n\n    PassMetadata() = default;\n    PassMetadata(std::unique_ptr<Pass> pass, std::vector<std::string> reqs,\n                 std::vector<std::string> invalidates)\n        : pass(std::move(pass)), reqs(std::move(reqs)),\n          invalidates(std::move(invalidates)) {}\n    PassMetadata(PassMetadata &&) = default;\n\n    PassMetadata &operator=(PassMetadata &&) = default;\n  };\n\n  /// Container for analysis metadata.\n  struct AnalysisMetadata {\n    /// pointer to the analysis instance\n    std::unique_ptr<analyze::Analysis> analysis;\n    /// vector of required analyses\n    std::vector<std::string> reqs;\n    /// vector of invalidated analyses\n    std::vector<std::string> invalidates;\n\n    AnalysisMetadata() = default;\n    AnalysisMetadata(std::unique_ptr<analyze::Analysis> analysis,\n                     std::vector<std::string> reqs)\n        : analysis(std::move(analysis)), reqs(std::move(reqs)) {}\n    AnalysisMetadata(AnalysisMetadata &&) = default;\n\n    AnalysisMetadata &operator=(AnalysisMetadata &&) = default;\n  };\n\n  /// key manager to handle duplicate keys (i.e. passes being added twice)\n  KeyManager km;\n\n  /// map of keys to passes\n  std::unordered_map<std::string, PassMetadata> passes;\n  /// map of keys to analyses\n  std::unordered_map<std::string, AnalysisMetadata> analyses;\n  /// reverse dependency map\n  std::unordered_map<std::string, std::vector<std::string>> deps;\n\n  /// execution order of passes\n  std::vector<std::string> executionOrder;\n  /// map of valid analysis results\n  std::unordered_map<std::string, std::unique_ptr<analyze::Result>> results;\n\n  /// passes to avoid registering\n  std::vector<std::string> disabled;\n\n  /// whether to use Python (vs. C) numeric semantics in passes\n  bool pyNumerics;\n\n  /// true if we are compiling as a Python extension\n  bool pyExtension;\n\npublic:\n  /// PassManager initialization mode.\n  enum Init {\n    EMPTY,\n    DEBUG,\n    RELEASE,\n    JIT,\n  };\n\n  explicit PassManager(Init init, std::vector<std::string> disabled = {},\n                       bool pyNumerics = false, bool pyExtension = false)\n      : km(), passes(), analyses(), executionOrder(), results(),\n        disabled(std::move(disabled)), pyNumerics(pyNumerics),\n        pyExtension(pyExtension) {\n    registerStandardPasses(init);\n  }\n\n  explicit PassManager(bool debug = false, std::vector<std::string> disabled = {},\n                       bool pyNumerics = false, bool pyExtension = false)\n      : PassManager(debug ? Init::DEBUG : Init::RELEASE, std::move(disabled),\n                    pyNumerics, pyExtension) {}\n\n  /// Checks if the given pass is included in this manager.\n  /// @param key the pass key\n  /// @return true if manager has the given pass\n  bool hasPass(const std::string &key) {\n    for (auto &pair : passes) {\n      if (pair.first == key)\n        return true;\n    }\n    return false;\n  }\n\n  /// Checks if the given analysis is included in this manager.\n  /// @param key the analysis key\n  /// @return true if manager has the given analysis\n  bool hasAnalysis(const std::string &key) {\n    for (auto &pair : analyses) {\n      if (pair.first == key)\n        return true;\n    }\n    return false;\n  }\n\n  /// Registers a pass and appends it to the execution order.\n  /// @param pass the pass\n  /// @param insertBefore insert pass before the pass with this given key\n  /// @param reqs keys of passes that must be run before the current one\n  /// @param invalidates keys of passes that are invalidated by the current one\n  /// @return unique'd key for the added pass, or empty string if not added\n  std::string registerPass(std::unique_ptr<Pass> pass,\n                           const std::string &insertBefore = \"\",\n                           std::vector<std::string> reqs = {},\n                           std::vector<std::string> invalidates = {});\n\n  /// Registers an analysis.\n  /// @param analysis the analysis\n  /// @param reqs keys of analyses that must be run before the current one\n  /// @return unique'd key for the added analysis, or empty string if not added\n  std::string registerAnalysis(std::unique_ptr<analyze::Analysis> analysis,\n                               std::vector<std::string> reqs = {});\n\n  /// Run all passes.\n  /// @param module the module\n  void run(Module *module);\n\n  /// Gets the result of a given analysis.\n  /// @param key the (unique'd) analysis key\n  /// @return the result\n  analyze::Result *getAnalysisResult(const std::string &key) {\n    auto it = results.find(key);\n    return it != results.end() ? it->second.get() : nullptr;\n  }\n\n  /// Returns whether a given pass or analysis is disabled.\n  /// @param key the (unique'd) pass or analysis key\n  /// @return true if the pass or analysis is disabled\n  bool isDisabled(const std::string &key) {\n    return std::find(disabled.begin(), disabled.end(), key) != disabled.end();\n  }\n\nprivate:\n  void runPass(Module *module, const std::string &name);\n  void registerStandardPasses(Init init);\n  void runAnalysis(Module *module, const std::string &name);\n  void invalidate(const std::string &key);\n};\n\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/numpy/expr.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"numpy.h\"\n\n#include \"codon/cir/util/irtools.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace numpy {\nnamespace {\ntypes::Type *coerceScalarArray(NumPyType &scalar, NumPyType &array,\n                               NumPyPrimitiveTypes &T) {\n  auto xtype = scalar.dtype;\n  auto atype = array.dtype;\n  bool aIsInt = false;\n  bool xIsInt = false;\n  bool aIsFloat = false;\n  bool xIsFloat = false;\n  bool aIsComplex = false;\n  bool xIsComplex = false;\n\n  switch (atype) {\n  case NumPyType::NP_TYPE_ARR_BOOL:\n    break;\n  case NumPyType::NP_TYPE_ARR_I8:\n  case NumPyType::NP_TYPE_ARR_U8:\n  case NumPyType::NP_TYPE_ARR_I16:\n  case NumPyType::NP_TYPE_ARR_U16:\n  case NumPyType::NP_TYPE_ARR_I32:\n  case NumPyType::NP_TYPE_ARR_U32:\n  case NumPyType::NP_TYPE_ARR_I64:\n  case NumPyType::NP_TYPE_ARR_U64:\n    aIsInt = true;\n    break;\n  case NumPyType::NP_TYPE_ARR_F16:\n  case NumPyType::NP_TYPE_ARR_F32:\n  case NumPyType::NP_TYPE_ARR_F64:\n    aIsFloat = true;\n    break;\n  case NumPyType::NP_TYPE_ARR_C64:\n  case NumPyType::NP_TYPE_ARR_C128:\n    aIsComplex = true;\n    break;\n  default:\n    seqassertn(false, \"unexpected type\");\n  }\n\n  xIsInt = (xtype == NumPyType::NP_TYPE_BOOL || xtype == NumPyType::NP_TYPE_I64);\n  xIsFloat = (xtype == NumPyType::NP_TYPE_F64);\n  xIsComplex = (xtype == NumPyType::NP_TYPE_C128);\n\n  bool shouldCast =\n      ((xIsInt && (aIsInt || aIsFloat || aIsComplex)) ||\n       (xIsFloat && (aIsFloat || aIsComplex)) || (xIsComplex && aIsComplex));\n\n  if ((atype == NumPyType::NP_TYPE_ARR_F16 || atype == NumPyType::NP_TYPE_ARR_F32) &&\n      xtype == NumPyType::NP_TYPE_C128)\n    return T.c64;\n  else if (shouldCast)\n    return array.getIRBaseType(T);\n  else\n    return scalar.getIRBaseType(T);\n}\n\nbool isPythonScalar(NumPyType &t) {\n  if (t.isArray())\n    return false;\n  auto dt = t.dtype;\n  return (dt == NumPyType::NP_TYPE_BOOL || dt == NumPyType::NP_TYPE_I64 ||\n          dt == NumPyType::NP_TYPE_F64 || dt == NumPyType::NP_TYPE_C128);\n}\n\ntemplate <typename E>\ntypes::Type *decideTypes(E *expr, NumPyType &lhs, NumPyType &rhs,\n                         NumPyPrimitiveTypes &T) {\n  // Special case(s)\n  if (expr->op == E::NP_OP_COPYSIGN)\n    return expr->type.getIRBaseType(T);\n\n  if (lhs.isArray() && isPythonScalar(rhs))\n    return coerceScalarArray(rhs, lhs, T);\n\n  if (isPythonScalar(lhs) && rhs.isArray())\n    return coerceScalarArray(lhs, rhs, T);\n\n  auto *t1 = lhs.getIRBaseType(T);\n  auto *t2 = rhs.getIRBaseType(T);\n  auto *M = t1->getModule();\n  auto *coerceFunc = M->getOrRealizeFunc(\"_coerce\", {}, {t1, t2}, FUSION_MODULE);\n  seqassertn(coerceFunc, \"coerce func not found\");\n  return util::getReturnType(coerceFunc);\n}\n} // namespace\n\nvoid NumPyExpr::replace(NumPyExpr &e) {\n  type = e.type;\n  val = e.val;\n  op = e.op;\n  lhs = std::move(e.lhs);\n  rhs = std::move(e.rhs);\n  freeable = e.freeable;\n\n  e.type = {};\n  e.val = nullptr;\n  e.op = NP_OP_NONE;\n  e.lhs = {};\n  e.rhs = {};\n  e.freeable = false;\n}\n\nbool NumPyExpr::haveVectorizedLoop() const {\n  if (lhs && !(lhs->type.dtype == NumPyType::NP_TYPE_ARR_F32 ||\n               lhs->type.dtype == NumPyType::NP_TYPE_ARR_F64))\n    return false;\n\n  if (rhs && !(rhs->type.dtype == NumPyType::NP_TYPE_ARR_F32 ||\n               rhs->type.dtype == NumPyType::NP_TYPE_ARR_F64))\n    return false;\n\n  if (lhs && rhs && lhs->type.dtype != rhs->type.dtype)\n    return false;\n\n  // These are the loops available in the runtime library.\n  static const std::vector<std::string> VecLoops = {\n      \"arccos\", \"arccosh\", \"arcsin\", \"arcsinh\", \"arctan\", \"arctanh\", \"arctan2\",\n      \"cos\",    \"exp\",     \"exp2\",   \"expm1\",   \"log\",    \"log10\",   \"log1p\",\n      \"log2\",   \"sin\",     \"sinh\",   \"tanh\",    \"hypot\"};\n  return std::find(VecLoops.begin(), VecLoops.end(), opstring()) != VecLoops.end();\n}\n\nint64_t NumPyExpr::opcost() const {\n  switch (op) {\n  case NP_OP_NONE:\n    return 0;\n  case NP_OP_POS:\n    return 0;\n  case NP_OP_NEG:\n    return 0;\n  case NP_OP_INVERT:\n    return 0;\n  case NP_OP_ABS:\n    return 1;\n  case NP_OP_TRANSPOSE:\n    return 0;\n  case NP_OP_ADD:\n    return 1;\n  case NP_OP_SUB:\n    return 1;\n  case NP_OP_MUL:\n    return 1;\n  case NP_OP_MATMUL:\n    return 20;\n  case NP_OP_TRUE_DIV:\n    return 8;\n  case NP_OP_FLOOR_DIV:\n    return 8;\n  case NP_OP_MOD:\n    return 8;\n  case NP_OP_FMOD:\n    return 8;\n  case NP_OP_POW:\n    return 8;\n  case NP_OP_LSHIFT:\n    return 1;\n  case NP_OP_RSHIFT:\n    return 1;\n  case NP_OP_AND:\n    return 1;\n  case NP_OP_OR:\n    return 1;\n  case NP_OP_XOR:\n    return 1;\n  case NP_OP_LOGICAL_AND:\n    return 1;\n  case NP_OP_LOGICAL_OR:\n    return 1;\n  case NP_OP_LOGICAL_XOR:\n    return 1;\n  case NP_OP_EQ:\n    return 1;\n  case NP_OP_NE:\n    return 1;\n  case NP_OP_LT:\n    return 1;\n  case NP_OP_LE:\n    return 1;\n  case NP_OP_GT:\n    return 1;\n  case NP_OP_GE:\n    return 1;\n  case NP_OP_MIN:\n    return 3;\n  case NP_OP_MAX:\n    return 3;\n  case NP_OP_FMIN:\n    return 3;\n  case NP_OP_FMAX:\n    return 3;\n  case NP_OP_SIN:\n    return 10;\n  case NP_OP_COS:\n    return 10;\n  case NP_OP_TAN:\n    return 10;\n  case NP_OP_ARCSIN:\n    return 20;\n  case NP_OP_ARCCOS:\n    return 20;\n  case NP_OP_ARCTAN:\n    return 20;\n  case NP_OP_ARCTAN2:\n    return 35;\n  case NP_OP_HYPOT:\n    return 5;\n  case NP_OP_SINH:\n    return 10;\n  case NP_OP_COSH:\n    return 10;\n  case NP_OP_TANH:\n    return 10;\n  case NP_OP_ARCSINH:\n    return 10;\n  case NP_OP_ARCCOSH:\n    return 10;\n  case NP_OP_ARCTANH:\n    return 10;\n  case NP_OP_CONJ:\n    return 1;\n  case NP_OP_EXP:\n    return 5;\n  case NP_OP_EXP2:\n    return 5;\n  case NP_OP_LOG:\n    return 5;\n  case NP_OP_LOG2:\n    return 5;\n  case NP_OP_LOG10:\n    return 5;\n  case NP_OP_EXPM1:\n    return 5;\n  case NP_OP_LOG1P:\n    return 5;\n  case NP_OP_SQRT:\n    return 2;\n  case NP_OP_SQUARE:\n    return 1;\n  case NP_OP_CBRT:\n    return 5;\n  case NP_OP_LOGADDEXP:\n    return 10;\n  case NP_OP_LOGADDEXP2:\n    return 10;\n  case NP_OP_RECIPROCAL:\n    return 1;\n  case NP_OP_RINT:\n    return 1;\n  case NP_OP_FLOOR:\n    return 1;\n  case NP_OP_CEIL:\n    return 1;\n  case NP_OP_TRUNC:\n    return 1;\n  case NP_OP_ISNAN:\n    return 1;\n  case NP_OP_ISINF:\n    return 1;\n  case NP_OP_ISFINITE:\n    return 1;\n  case NP_OP_SIGN:\n    return 1;\n  case NP_OP_SIGNBIT:\n    return 1;\n  case NP_OP_COPYSIGN:\n    return 1;\n  case NP_OP_SPACING:\n    return 1;\n  case NP_OP_NEXTAFTER:\n    return 1;\n  case NP_OP_DEG2RAD:\n    return 2;\n  case NP_OP_RAD2DEG:\n    return 2;\n  case NP_OP_HEAVISIDE:\n    return 3;\n  }\n}\n\nint64_t NumPyExpr::cost() const {\n  auto c = opcost();\n  if (c == -1)\n    return -1;\n\n  // Account for the fact that the vectorized loops are much faster.\n  if (haveVectorizedLoop()) {\n    c *= 3;\n    if (lhs->type.dtype == NumPyType::NP_TYPE_ARR_F32)\n      c *= 2;\n  }\n\n  bool lhsIntConst = (lhs && lhs->isLeaf() && isA<IntConst>(lhs->val));\n  bool rhsIntConst = (rhs && rhs->isLeaf() && isA<IntConst>(rhs->val));\n  bool lhsFloatConst = (lhs && lhs->isLeaf() && isA<FloatConst>(lhs->val));\n  bool rhsFloatConst = (rhs && rhs->isLeaf() && isA<FloatConst>(rhs->val));\n  bool lhsConst = lhsIntConst || lhsFloatConst;\n  bool rhsConst = rhsIntConst || rhsFloatConst;\n\n  if (rhsConst || lhsConst) {\n    switch (op) {\n    case NP_OP_TRUE_DIV:\n    case NP_OP_FLOOR_DIV:\n    case NP_OP_MOD:\n    case NP_OP_FMOD:\n      c = 1;\n      break;\n    case NP_OP_POW:\n      if (rhsIntConst)\n        c = (cast<IntConst>(rhs->val)->getVal() == 2) ? 1 : 5;\n      break;\n    default:\n      break;\n    }\n  }\n\n  if (lhs) {\n    auto cl = lhs->cost();\n    if (cl == -1)\n      return -1;\n    c += cl;\n  }\n\n  if (rhs) {\n    auto cr = rhs->cost();\n    if (cr == -1)\n      return -1;\n    c += cr;\n  }\n\n  return c;\n}\n\nstd::string NumPyExpr::opstring() const {\n  static const std::unordered_map<Op, std::string> m = {\n      {NP_OP_NONE, \"a\"},\n      {NP_OP_POS, \"pos\"},\n      {NP_OP_NEG, \"neg\"},\n      {NP_OP_INVERT, \"invert\"},\n      {NP_OP_ABS, \"abs\"},\n      {NP_OP_TRANSPOSE, \"transpose\"},\n      {NP_OP_ADD, \"add\"},\n      {NP_OP_SUB, \"sub\"},\n      {NP_OP_MUL, \"mul\"},\n      {NP_OP_MATMUL, \"matmul\"},\n      {NP_OP_TRUE_DIV, \"true_div\"},\n      {NP_OP_FLOOR_DIV, \"floor_div\"},\n      {NP_OP_MOD, \"mod\"},\n      {NP_OP_FMOD, \"fmod\"},\n      {NP_OP_POW, \"pow\"},\n      {NP_OP_LSHIFT, \"lshift\"},\n      {NP_OP_RSHIFT, \"rshift\"},\n      {NP_OP_AND, \"and\"},\n      {NP_OP_OR, \"or\"},\n      {NP_OP_XOR, \"xor\"},\n      {NP_OP_LOGICAL_AND, \"logical_and\"},\n      {NP_OP_LOGICAL_OR, \"logical_or\"},\n      {NP_OP_LOGICAL_XOR, \"logical_xor\"},\n      {NP_OP_EQ, \"eq\"},\n      {NP_OP_NE, \"ne\"},\n      {NP_OP_LT, \"lt\"},\n      {NP_OP_LE, \"le\"},\n      {NP_OP_GT, \"gt\"},\n      {NP_OP_GE, \"ge\"},\n      {NP_OP_MIN, \"minimum\"},\n      {NP_OP_MAX, \"maximum\"},\n      {NP_OP_FMIN, \"fmin\"},\n      {NP_OP_FMAX, \"fmax\"},\n      {NP_OP_SIN, \"sin\"},\n      {NP_OP_COS, \"cos\"},\n      {NP_OP_TAN, \"tan\"},\n      {NP_OP_ARCSIN, \"arcsin\"},\n      {NP_OP_ARCCOS, \"arccos\"},\n      {NP_OP_ARCTAN, \"arctan\"},\n      {NP_OP_ARCTAN2, \"arctan2\"},\n      {NP_OP_HYPOT, \"hypot\"},\n      {NP_OP_SINH, \"sinh\"},\n      {NP_OP_COSH, \"cosh\"},\n      {NP_OP_TANH, \"tanh\"},\n      {NP_OP_ARCSINH, \"arcsinh\"},\n      {NP_OP_ARCCOSH, \"arccosh\"},\n      {NP_OP_ARCTANH, \"arctanh\"},\n      {NP_OP_CONJ, \"conj\"},\n      {NP_OP_EXP, \"exp\"},\n      {NP_OP_EXP2, \"exp2\"},\n      {NP_OP_LOG, \"log\"},\n      {NP_OP_LOG2, \"log2\"},\n      {NP_OP_LOG10, \"log10\"},\n      {NP_OP_EXPM1, \"expm1\"},\n      {NP_OP_LOG1P, \"log1p\"},\n      {NP_OP_SQRT, \"sqrt\"},\n      {NP_OP_SQUARE, \"square\"},\n      {NP_OP_CBRT, \"cbrt\"},\n      {NP_OP_LOGADDEXP, \"logaddexp\"},\n      {NP_OP_LOGADDEXP2, \"logaddexp2\"},\n      {NP_OP_RECIPROCAL, \"reciprocal\"},\n      {NP_OP_RINT, \"rint\"},\n      {NP_OP_FLOOR, \"floor\"},\n      {NP_OP_CEIL, \"ceil\"},\n      {NP_OP_TRUNC, \"trunc\"},\n      {NP_OP_ISNAN, \"isnan\"},\n      {NP_OP_ISINF, \"isinf\"},\n      {NP_OP_ISFINITE, \"isfinite\"},\n      {NP_OP_SIGN, \"sign\"},\n      {NP_OP_SIGNBIT, \"signbit\"},\n      {NP_OP_COPYSIGN, \"copysign\"},\n      {NP_OP_SPACING, \"spacing\"},\n      {NP_OP_NEXTAFTER, \"nextafter\"},\n      {NP_OP_DEG2RAD, \"deg2rad\"},\n      {NP_OP_RAD2DEG, \"rad2deg\"},\n      {NP_OP_HEAVISIDE, \"heaviside\"},\n  };\n\n  auto it = m.find(op);\n  seqassertn(it != m.end(), \"op not found\");\n  return it->second;\n}\n\nvoid NumPyExpr::dump(std::ostream &os, int level, int &leafId) const {\n  auto indent = [&]() {\n    for (int i = 0; i < level; i++)\n      os << \"  \";\n  };\n\n  indent();\n  if (op == NP_OP_NONE) {\n    os << \"\\033[1;36m\" << opstring() << leafId;\n    ++leafId;\n  } else {\n    os << \"\\033[1;33m\" << opstring();\n  }\n  os << \"\\033[0m <\" << type << \">\";\n  if (op != NP_OP_NONE)\n    os << \" \\033[1;35m[cost=\" << cost() << \"]\\033[0m\";\n  os << \"\\n\";\n  if (lhs)\n    lhs->dump(os, level + 1, leafId);\n  if (rhs)\n    rhs->dump(os, level + 1, leafId);\n}\n\nstd::ostream &operator<<(std::ostream &os, NumPyExpr const &expr) {\n  int leafId = 0;\n  expr.dump(os, 0, leafId);\n  return os;\n}\n\nstd::string NumPyExpr::str() const {\n  std::stringstream buffer;\n  buffer << *this;\n  return buffer.str();\n}\n\nvoid NumPyExpr::apply(std::function<void(NumPyExpr &)> f) {\n  f(*this);\n  if (lhs)\n    lhs->apply(f);\n  if (rhs)\n    rhs->apply(f);\n}\n\nValue *NumPyExpr::codegenBroadcasts(CodegenContext &C) {\n  auto *M = C.M;\n  auto &vars = C.vars;\n\n  Value *targetShape = nullptr;\n  Value *result = nullptr;\n\n  apply([&](NumPyExpr &e) {\n    if (e.isLeaf() && e.type.isArray()) {\n      auto it = vars.find(&e);\n      seqassertn(it != vars.end(),\n                 \"NumPyExpr not found in vars map (codegen broadcasts)\");\n      auto *var = it->second;\n      auto *shape = M->getOrRealizeFunc(\"_shape\", {var->getType()}, {}, FUSION_MODULE);\n      seqassertn(shape, \"shape function not found\");\n      auto *leafShape = util::call(shape, {M->Nr<VarValue>(var)});\n\n      if (!targetShape) {\n        targetShape = leafShape;\n      } else {\n        auto *diff = (*targetShape != *leafShape);\n        if (result) {\n          result = *result | *diff;\n        } else {\n          result = diff;\n        }\n      }\n    }\n  });\n\n  return result ? result : M->getBool(false);\n}\n\nVar *NumPyExpr::codegenFusedEval(CodegenContext &C) {\n  auto *M = C.M;\n  auto *series = C.series;\n  auto *func = C.func;\n  auto &vars = C.vars;\n  auto &T = C.T;\n\n  std::vector<std::pair<NumPyExpr *, Var *>> leaves;\n  apply([&](NumPyExpr &e) {\n    if (e.isLeaf()) {\n      auto it = vars.find(&e);\n      seqassertn(it != vars.end(), \"NumPyExpr not found in vars map (fused eval)\");\n      auto *var = it->second;\n      leaves.emplace_back(&e, var);\n    }\n  });\n\n  // Arrays for scalar expression function\n  std::vector<Value *> arrays;\n  std::vector<std::string> scalarFuncArgNames;\n  std::vector<types::Type *> scalarFuncArgTypes;\n  std::unordered_map<NumPyExpr *, Var *> scalarFuncArgMap;\n\n  // Scalars passed through 'extra' arg of ndarray._loop()\n  std::vector<Value *> extra;\n  std::unordered_map<NumPyExpr *, unsigned> extraMap;\n\n  auto *baseType = type.getIRBaseType(T);\n  scalarFuncArgNames.push_back(\"out\");\n  scalarFuncArgTypes.push_back(M->getPointerType(baseType));\n\n  unsigned argIdx = 0;\n  unsigned extraIdx = 0;\n\n  for (auto &e : leaves) {\n    if (e.first->type.isArray()) {\n      arrays.push_back(M->Nr<VarValue>(e.second));\n      scalarFuncArgNames.push_back(\"in\" + std::to_string(argIdx++));\n      scalarFuncArgTypes.push_back(M->getPointerType(e.first->type.getIRBaseType(T)));\n    } else {\n      extra.push_back(M->Nr<VarValue>(e.second));\n      extraMap.emplace(e.first, extraIdx++);\n    }\n  }\n\n  auto *extraTuple = util::makeTuple(extra, M);\n  scalarFuncArgNames.push_back(\"extra\");\n  scalarFuncArgTypes.push_back(extraTuple->getType());\n  auto *scalarFuncType = M->getFuncType(M->getNoneType(), scalarFuncArgTypes);\n  auto *scalarFunc = M->Nr<BodiedFunc>(\"__numpy_fusion_scalar_fn\");\n  scalarFunc->realize(scalarFuncType, scalarFuncArgNames);\n  std::vector<Var *> scalarFuncArgVars(scalarFunc->arg_begin(), scalarFunc->arg_end());\n\n  argIdx = 1;\n  for (auto &e : leaves) {\n    if (e.first->type.isArray()) {\n      scalarFuncArgMap.emplace(e.first, scalarFuncArgVars[argIdx++]);\n    }\n  }\n  auto *scalarExpr =\n      codegenScalarExpr(C, scalarFuncArgMap, extraMap, scalarFuncArgVars.back());\n  auto *ptrsetFunc = M->getOrRealizeFunc(\"_ptrset\", {scalarFuncArgTypes[0], baseType},\n                                         {}, FUSION_MODULE);\n  seqassertn(ptrsetFunc, \"ptrset func not found\");\n  scalarFunc->setBody(util::series(\n      util::call(ptrsetFunc, {M->Nr<VarValue>(scalarFuncArgVars[0]), scalarExpr})));\n\n  auto *arraysTuple = util::makeTuple(arrays);\n  auto *loopFunc = M->getOrRealizeFunc(\n      \"_loop_alloc\",\n      {arraysTuple->getType(), scalarFunc->getType(), extraTuple->getType()},\n      {baseType}, FUSION_MODULE);\n  seqassertn(loopFunc, \"loop_alloc func not found\");\n\n  auto *result = util::makeVar(\n      util::call(loopFunc, {arraysTuple, M->Nr<VarValue>(scalarFunc), extraTuple}),\n      series, func);\n\n  // Free temporary arrays\n  apply([&](NumPyExpr &e) {\n    if (e.isLeaf() && e.freeable) {\n      auto it = vars.find(&e);\n      seqassertn(it != vars.end(), \"NumPyExpr not found in vars map (fused eval)\");\n      auto *var = it->second;\n      auto *freeFunc =\n          M->getOrRealizeFunc(\"_free\", {var->getType()}, {}, FUSION_MODULE);\n      seqassertn(freeFunc, \"free func not found\");\n      series->push_back(util::call(freeFunc, {M->Nr<VarValue>(var)}));\n    }\n  });\n\n  return result;\n}\n\nVar *NumPyExpr::codegenSequentialEval(CodegenContext &C) {\n  auto *M = C.M;\n  auto *series = C.series;\n  auto *func = C.func;\n  auto &vars = C.vars;\n  auto &T = C.T;\n\n  if (isLeaf()) {\n    auto it = vars.find(this);\n    seqassertn(it != vars.end(),\n               \"NumPyExpr not found in vars map (codegen sequential eval)\");\n    return it->second;\n  }\n\n  Var *lv = lhs->codegenSequentialEval(C);\n  Var *rv = rhs ? rhs->codegenSequentialEval(C) : nullptr;\n  Var *like = nullptr;\n  Value *outShapeVal = nullptr;\n\n  if (rv) {\n    // Can't do anything special with matmul here...\n    if (op == NP_OP_MATMUL) {\n      auto *matmul = M->getOrRealizeFunc(\"_matmul\", {lv->getType(), rv->getType()}, {},\n                                         FUSION_MODULE);\n      return util::makeVar(\n          util::call(matmul, {M->Nr<VarValue>(lv), M->Nr<VarValue>(rv)}), series, func);\n    }\n\n    auto *lshape = M->getOrRealizeFunc(\"_shape\", {lv->getType()}, {}, FUSION_MODULE);\n    seqassertn(lshape, \"shape func not found for left arg\");\n    auto *rshape = M->getOrRealizeFunc(\"_shape\", {rv->getType()}, {}, FUSION_MODULE);\n    seqassertn(rshape, \"shape func not found for right arg\");\n    auto *leftShape = util::call(lshape, {M->Nr<VarValue>(lv)});\n    auto *rightShape = util::call(rshape, {M->Nr<VarValue>(rv)});\n    auto *shape = M->getOrRealizeFunc(\n        \"_broadcast\", {leftShape->getType(), rightShape->getType()}, {}, FUSION_MODULE);\n    seqassertn(shape, \"output shape func not found\");\n    like = rhs->type.ndim > lhs->type.ndim ? rv : lv;\n    outShapeVal = util::call(shape, {leftShape, rightShape});\n  } else {\n    auto *shape = M->getOrRealizeFunc(\"_shape\", {lv->getType()}, {}, FUSION_MODULE);\n    seqassertn(shape, \"shape func not found\");\n    like = lv;\n    outShapeVal = util::call(shape, {M->Nr<VarValue>(lv)});\n  }\n\n  auto *outShape = util::makeVar(outShapeVal, series, func);\n  Var *result = nullptr;\n\n  bool lfreeable = lhs && lhs->type.isArray() && (lhs->freeable || !lhs->isLeaf());\n  bool rfreeable = rhs && rhs->type.isArray() && (rhs->freeable || !rhs->isLeaf());\n  bool ltmp = lfreeable && lhs->type.dtype == type.dtype && lhs->type.ndim == type.ndim;\n  bool rtmp = rfreeable && rhs->type.dtype == type.dtype && rhs->type.ndim == type.ndim;\n\n  auto *t = type.getIRBaseType(T);\n  auto newArray = [&]() {\n    auto *create = M->getOrRealizeFunc(\n        \"_create\", {like->getType(), outShape->getType()}, {t}, FUSION_MODULE);\n    seqassertn(create, \"create func not found\");\n    return util::call(create, {M->Nr<VarValue>(like), M->Nr<VarValue>(outShape)});\n  };\n\n  bool freeLeftStatic = false;\n  bool freeRightStatic = false;\n  Var *lcond = nullptr;\n  Var *rcond = nullptr;\n\n  if (rv) {\n    if (ltmp && rhs->type.ndim == 0) {\n      // We are adding lhs temp array to const or 0-dim array, so reuse lhs array.\n      result = lv;\n    } else if (rtmp && lhs->type.ndim == 0) {\n      // We are adding rhs temp array to const or 0-dim array, so reuse rhs array.\n      result = rv;\n    } else if (!ltmp && !rtmp) {\n      // Neither operand is a temp array, so we must allocate a new array.\n      result = util::makeVar(newArray(), series, func);\n      freeLeftStatic = lfreeable;\n      freeRightStatic = rfreeable;\n    } else if (ltmp && rtmp) {\n      // We won't know until runtime if we can reuse the temp array(s) since they\n      // might broadcast.\n      auto *lshape = M->getOrRealizeFunc(\"_shape\", {lv->getType()}, {}, FUSION_MODULE);\n      seqassertn(lshape, \"shape function func not found for left arg\");\n      auto *rshape = M->getOrRealizeFunc(\"_shape\", {rv->getType()}, {}, FUSION_MODULE);\n      seqassertn(rshape, \"shape function func not found for right arg\");\n      auto *leftShape = util::call(lshape, {M->Nr<VarValue>(lv)});\n      auto *rightShape = util::call(rshape, {M->Nr<VarValue>(rv)});\n      lcond = util::makeVar(*leftShape == *M->Nr<VarValue>(outShape), series, func);\n      rcond = util::makeVar(*rightShape == *M->Nr<VarValue>(outShape), series, func);\n      auto *arr = M->Nr<TernaryInstr>(\n          M->Nr<VarValue>(lcond), M->Nr<VarValue>(lv),\n          M->Nr<TernaryInstr>(M->Nr<VarValue>(rcond), M->Nr<VarValue>(rv), newArray()));\n      result = util::makeVar(arr, series, func);\n    } else if (ltmp && !rtmp) {\n      // We won't know until runtime if we can reuse the temp array(s) since they\n      // might broadcast.\n      auto *lshape = M->getOrRealizeFunc(\"_shape\", {lv->getType()}, {}, FUSION_MODULE);\n      seqassertn(lshape, \"shape function func not found for left arg\");\n      auto *leftShape = util::call(lshape, {M->Nr<VarValue>(lv)});\n      lcond = util::makeVar(*leftShape == *M->Nr<VarValue>(outShape), series, func);\n      auto *arr =\n          M->Nr<TernaryInstr>(M->Nr<VarValue>(lcond), M->Nr<VarValue>(lv), newArray());\n      result = util::makeVar(arr, series, func);\n      freeRightStatic = rfreeable;\n    } else if (!ltmp && rtmp) {\n      // We won't know until runtime if we can reuse the temp array(s) since they\n      // might broadcast.\n      auto *rshape = M->getOrRealizeFunc(\"_shape\", {rv->getType()}, {}, FUSION_MODULE);\n      seqassertn(rshape, \"shape function func not found for right arg\");\n      auto *rightShape = util::call(rshape, {M->Nr<VarValue>(rv)});\n      rcond = util::makeVar(*rightShape == *M->Nr<VarValue>(outShape), series, func);\n      auto *arr =\n          M->Nr<TernaryInstr>(M->Nr<VarValue>(rcond), M->Nr<VarValue>(rv), newArray());\n      result = util::makeVar(arr, series, func);\n      freeLeftStatic = lfreeable;\n    }\n  } else {\n    if (ltmp) {\n      result = lv;\n    } else {\n      result = util::makeVar(newArray(), series, func);\n      freeLeftStatic = lfreeable;\n    }\n  }\n\n  auto opstr = opstring();\n\n  if (haveVectorizedLoop()) {\n    // We have a vectorized loop available for this operations.\n    if (rv) {\n      auto *vecloop = M->getOrRealizeFunc(\n          \"_apply_vectorized_loop_binary\",\n          {lv->getType(), rv->getType(), result->getType()}, {opstr}, FUSION_MODULE);\n      seqassertn(vecloop, \"binary vec loop func not found ({})\", opstr);\n      series->push_back(util::call(vecloop, {M->Nr<VarValue>(lv), M->Nr<VarValue>(rv),\n                                             M->Nr<VarValue>(result)}));\n    } else {\n      auto *vecloop = M->getOrRealizeFunc(\"_apply_vectorized_loop_unary\",\n                                          {lv->getType(), result->getType()}, {opstr},\n                                          FUSION_MODULE);\n      seqassertn(vecloop, \"unary vec loop func not found ({})\", opstr);\n      series->push_back(\n          util::call(vecloop, {M->Nr<VarValue>(lv), M->Nr<VarValue>(result)}));\n    }\n  } else {\n    // Arrays for scalar expression function\n    std::vector<Value *> arrays = {M->Nr<VarValue>(result)};\n    std::vector<std::string> scalarFuncArgNames;\n    std::vector<types::Type *> scalarFuncArgTypes;\n    std::unordered_map<NumPyExpr *, Var *> scalarFuncArgMap;\n\n    // Scalars passed through 'extra' arg of ndarray._loop()\n    std::vector<Value *> extra;\n\n    auto *baseType = type.getIRBaseType(T);\n    scalarFuncArgNames.push_back(\"out\");\n    scalarFuncArgTypes.push_back(M->getPointerType(baseType));\n\n    if (lhs->type.isArray()) {\n      if (result != lv) {\n        scalarFuncArgNames.push_back(\"in0\");\n        scalarFuncArgTypes.push_back(M->getPointerType(lhs->type.getIRBaseType(T)));\n        arrays.push_back(M->Nr<VarValue>(lv));\n      }\n    } else {\n      extra.push_back(M->Nr<VarValue>(lv));\n    }\n\n    if (rv) {\n      if (rhs->type.isArray()) {\n        if (result != rv) {\n          scalarFuncArgNames.push_back(\"in1\");\n          scalarFuncArgTypes.push_back(M->getPointerType(rhs->type.getIRBaseType(T)));\n          arrays.push_back(M->Nr<VarValue>(rv));\n        }\n      } else {\n        extra.push_back(M->Nr<VarValue>(rv));\n      }\n    }\n\n    auto *extraTuple = util::makeTuple(extra, M);\n    scalarFuncArgNames.push_back(\"extra\");\n    scalarFuncArgTypes.push_back(extraTuple->getType());\n    auto *scalarFuncType = M->getFuncType(M->getNoneType(), scalarFuncArgTypes);\n    auto *scalarFunc = M->Nr<BodiedFunc>(\"__numpy_fusion_scalar_fn\");\n    scalarFunc->realize(scalarFuncType, scalarFuncArgNames);\n    std::vector<Var *> scalarFuncArgVars(scalarFunc->arg_begin(),\n                                         scalarFunc->arg_end());\n    auto *body = M->Nr<SeriesFlow>();\n    auto name = \"_\" + opstr;\n\n    auto deref = [&](unsigned idx) {\n      return (*M->Nr<VarValue>(scalarFuncArgVars[idx]))[*M->getInt(0)];\n    };\n\n    if (rv) {\n      Value *litem = nullptr;\n      Value *ritem = nullptr;\n\n      if (lhs->type.isArray() && rhs->type.isArray()) {\n        if (result == lv) {\n          litem = deref(0);\n          ritem = deref(1);\n        } else if (result == rv) {\n          litem = deref(1);\n          ritem = deref(0);\n        } else {\n          litem = deref(1);\n          ritem = deref(2);\n        }\n      } else if (lhs->type.isArray()) {\n        if (result == lv) {\n          litem = deref(0);\n        } else {\n          litem = deref(1);\n        }\n        ritem = util::tupleGet(M->Nr<VarValue>(scalarFuncArgVars.back()), 0);\n      } else if (rhs->type.isArray()) {\n        if (result == rv) {\n          ritem = deref(0);\n        } else {\n          ritem = deref(1);\n        }\n        litem = util::tupleGet(M->Nr<VarValue>(scalarFuncArgVars.back()), 0);\n      } else {\n        seqassertn(false, \"both lhs are rhs are scalars\");\n      }\n\n      auto *commonType = decideTypes(this, lhs->type, rhs->type, T);\n\n      auto *lcast =\n          M->getOrRealizeFunc(\"_cast\", {litem->getType()}, {commonType}, FUSION_MODULE);\n      seqassertn(lcast, \"cast func not found for left arg\");\n      litem = util::call(lcast, {litem});\n\n      auto *rcast =\n          M->getOrRealizeFunc(\"_cast\", {ritem->getType()}, {commonType}, FUSION_MODULE);\n      seqassertn(rcast, \"cast func not found for left arg\");\n      ritem = util::call(rcast, {ritem});\n\n      auto *op = M->getOrRealizeFunc(name, {litem->getType(), ritem->getType()}, {},\n                                     FUSION_MODULE);\n      seqassertn(op, \"2-op func '{}' not found\", name);\n      auto *oitem = util::call(op, {litem, ritem});\n      auto *ptrsetFunc = M->getOrRealizeFunc(\n          \"_ptrset\", {scalarFuncArgTypes[0], oitem->getType()}, {}, FUSION_MODULE);\n      seqassertn(ptrsetFunc, \"ptrset func not found\");\n      body->push_back(\n          util::call(ptrsetFunc, {M->Nr<VarValue>(scalarFuncArgVars[0]), oitem}));\n    } else {\n      auto *litem = deref(result == lv ? 0 : 1);\n      auto *op = M->getOrRealizeFunc(name, {litem->getType()}, {}, FUSION_MODULE);\n      seqassertn(op, \"1-op func '{}' not found\", name);\n      auto *oitem = util::call(op, {litem});\n      auto *ptrsetFunc = M->getOrRealizeFunc(\n          \"_ptrset\", {scalarFuncArgTypes[0], oitem->getType()}, {}, FUSION_MODULE);\n      seqassertn(ptrsetFunc, \"ptrset func not found\");\n      body->push_back(\n          util::call(ptrsetFunc, {M->Nr<VarValue>(scalarFuncArgVars[0]), oitem}));\n    }\n\n    scalarFunc->setBody(body);\n    auto *arraysTuple = util::makeTuple(arrays);\n    auto *loopFunc = M->getOrRealizeFunc(\n        \"_loop_basic\",\n        {arraysTuple->getType(), scalarFunc->getType(), extraTuple->getType()}, {},\n        FUSION_MODULE);\n    seqassertn(loopFunc, \"loop_basic func not found\");\n    series->push_back(\n        util::call(loopFunc, {arraysTuple, M->Nr<VarValue>(scalarFunc), extraTuple}));\n  }\n\n  auto freeArray = [&](Var *arr) {\n    auto *freeFunc = M->getOrRealizeFunc(\"_free\", {arr->getType()}, {}, FUSION_MODULE);\n    seqassertn(freeFunc, \"free func not found\");\n    return util::call(freeFunc, {M->Nr<VarValue>(arr)});\n  };\n\n  seqassertn(!(freeLeftStatic && lcond), \"unexpected free conditions for left arg\");\n  seqassertn(!(freeRightStatic && rcond), \"unexpected free conditions for right arg\");\n\n  if (lcond && rcond) {\n    series->push_back(M->Nr<IfFlow>(\n        M->Nr<VarValue>(lcond), util::series(freeArray(rv)),\n        util::series(freeArray(lv),\n                     M->Nr<IfFlow>(M->Nr<VarValue>(rcond), M->Nr<SeriesFlow>(),\n                                   util::series(freeArray(rv))))));\n  } else {\n    if (freeLeftStatic) {\n      series->push_back(freeArray(lv));\n    } else if (lcond) {\n      series->push_back(M->Nr<IfFlow>(M->Nr<VarValue>(lcond), M->Nr<SeriesFlow>(),\n                                      util::series(freeArray(lv))));\n    }\n\n    if (freeRightStatic) {\n      series->push_back(freeArray(rv));\n    } else if (rcond) {\n      series->push_back(M->Nr<IfFlow>(M->Nr<VarValue>(rcond), M->Nr<SeriesFlow>(),\n                                      util::series(freeArray(rv))));\n    }\n  }\n\n  return result;\n}\n\nBroadcastInfo NumPyExpr::getBroadcastInfo() {\n  int64_t arrDim = -1;\n  Var *varLeaf = nullptr;\n  bool multipleLeafVars = false;\n  int numNonVarLeafArrays = 0;\n  bool definitelyBroadcasts = false;\n\n  apply([&](NumPyExpr &e) {\n    if (e.isLeaf() && e.type.isArray()) {\n      if (arrDim == -1) {\n        arrDim = e.type.ndim;\n      } else if (arrDim != e.type.ndim) {\n        definitelyBroadcasts = true;\n      }\n\n      if (auto *v = cast<VarValue>(e.val)) {\n        if (varLeaf) {\n          if (varLeaf != v->getVar())\n            multipleLeafVars = true;\n        } else {\n          varLeaf = v->getVar();\n        }\n      } else {\n        ++numNonVarLeafArrays;\n      }\n    }\n  });\n\n  bool mightBroadcast = numNonVarLeafArrays > 1 || multipleLeafVars ||\n                        (numNonVarLeafArrays == 1 && varLeaf);\n  if (definitelyBroadcasts) {\n    return BroadcastInfo::YES;\n  } else if (mightBroadcast) {\n    return BroadcastInfo::MAYBE;\n  } else {\n    return BroadcastInfo::NO;\n  }\n}\n\nValue *NumPyExpr::codegenScalarExpr(\n    CodegenContext &C, const std::unordered_map<NumPyExpr *, Var *> &args,\n    const std::unordered_map<NumPyExpr *, unsigned> &scalarMap, Var *scalars) {\n  auto *M = C.M;\n  auto &T = C.T;\n\n  Value *lv = lhs ? lhs->codegenScalarExpr(C, args, scalarMap, scalars) : nullptr;\n  Value *rv = rhs ? rhs->codegenScalarExpr(C, args, scalarMap, scalars) : nullptr;\n  auto name = \"_\" + opstring();\n\n  if (lv && rv) {\n    auto *t = type.getIRBaseType(T);\n    auto *commonType = decideTypes(this, lhs->type, rhs->type, T);\n    auto *cast1 =\n        M->getOrRealizeFunc(\"_cast\", {lv->getType()}, {commonType}, FUSION_MODULE);\n    auto *cast2 =\n        M->getOrRealizeFunc(\"_cast\", {rv->getType()}, {commonType}, FUSION_MODULE);\n    lv = util::call(cast1, {lv});\n    rv = util::call(cast2, {rv});\n    auto *f =\n        M->getOrRealizeFunc(name, {lv->getType(), rv->getType()}, {}, FUSION_MODULE);\n    seqassertn(f, \"2-op func '{}' not found\", name);\n    return util::call(f, {lv, rv});\n  } else if (lv) {\n    auto *t = type.getIRBaseType(T);\n    auto *f = M->getOrRealizeFunc(name, {lv->getType()}, {}, FUSION_MODULE);\n    seqassertn(f, \"1-op func '{}' not found\", name);\n    return util::call(f, {lv});\n  } else {\n    if (type.isArray()) {\n      auto it = args.find(this);\n      seqassertn(it != args.end(), \"NumPyExpr not found in args map (codegen expr)\");\n      auto *var = it->second;\n      return (*M->Nr<VarValue>(var))[*M->getInt(0)];\n    } else {\n      auto it = scalarMap.find(this);\n      seqassertn(it != scalarMap.end(),\n                 \"NumPyExpr not found in scalar map (codegen expr)\");\n      auto idx = it->second;\n      return util::tupleGet(M->Nr<VarValue>(scalars), idx);\n    }\n  }\n}\n\n} // namespace numpy\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/numpy/forward.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"numpy.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace numpy {\nnamespace {\nusing CFG = analyze::dataflow::CFGraph;\nusing CFBlock = analyze::dataflow::CFBlock;\nusing RD = analyze::dataflow::RDInspector;\nusing SE = analyze::module::SideEffectResult;\n\nstruct GetVars : public util::Operator {\n  std::unordered_set<id_t> &vids;\n\n  explicit GetVars(std::unordered_set<id_t> &vids) : util::Operator(), vids(vids) {}\n\n  void preHook(Node *v) override {\n    for (auto *var : v->getUsedVariables()) {\n      if (!isA<Func>(var))\n        vids.insert(var->getId());\n    }\n  }\n};\n\nstruct OkToForwardPast : public util::Operator {\n  std::unordered_set<id_t> &vids;\n  const std::unordered_map<id_t, NumPyExpr *> &parsedValues;\n  SE *se;\n  bool ok;\n\n  OkToForwardPast(std::unordered_set<id_t> &vids,\n                  const std::unordered_map<id_t, NumPyExpr *> &parsedValues, SE *se)\n      : util::Operator(), vids(vids), parsedValues(parsedValues), se(se), ok(true) {}\n\n  void preHook(Node *v) override {\n    if (!ok) {\n      return;\n    } else if (auto *assign = cast<AssignInstr>(v)) {\n      if (vids.count(assign->getLhs()->getId()))\n        ok = false;\n    } else if (auto *val = cast<Value>(v)) {\n      auto it = parsedValues.find(val->getId());\n      if (it != parsedValues.end()) {\n        it->second->apply([&](NumPyExpr &e) {\n          if (e.isLeaf() && se->hasSideEffect(e.val))\n            ok = false;\n        });\n        // Skip children since we are processing them manually above.\n        for (auto *used : val->getUsedValues())\n          see(used);\n      } else if (se->hasSideEffect(val)) {\n        ok = false;\n      }\n    }\n  }\n};\n\nstruct GetAllUses : public util::Operator {\n  Var *var;\n  std::vector<Value *> &uses;\n\n  GetAllUses(Var *var, std::vector<Value *> &uses)\n      : util::Operator(), var(var), uses(uses) {}\n\n  void preHook(Node *n) override {\n    if (auto *v = cast<Value>(n)) {\n      auto vars = v->getUsedVariables();\n      if (std::find(vars.begin(), vars.end(), var) != vars.end())\n        uses.push_back(v);\n    }\n  }\n};\n\nbool canForwardExpressionAlongPath(\n    Value *source, Value *destination, std::unordered_set<id_t> &vids,\n    const std::unordered_map<id_t, NumPyExpr *> &parsedValues, SE *se,\n    const std::vector<CFBlock *> &path) {\n  if (path.empty())\n    return true;\n\n  bool go = false;\n  for (auto *block : path) {\n    for (const auto *value : *block) {\n      // Skip things before 'source' in first block\n      if (!go && block == path.front() && value == source) {\n        go = true;\n        continue;\n      }\n\n      // Skip things after 'destination' in last block\n      if (go && block == path.back() && value == destination) {\n        go = false;\n        break;\n      }\n\n      if (!go)\n        continue;\n\n      OkToForwardPast check(vids, parsedValues, se);\n      const_cast<Value *>(value)->accept(check);\n      if (!check.ok)\n        return false;\n    }\n  }\n  return true;\n}\n\nbool canForwardExpression(NumPyOptimizationUnit *expr, Value *target,\n                          const std::unordered_map<id_t, NumPyExpr *> &parsedValues,\n                          CFG *cfg, SE *se) {\n  std::unordered_set<id_t> vids;\n  bool pure = true;\n\n  expr->expr->apply([&](NumPyExpr &e) {\n    if (e.isLeaf()) {\n      if (se->hasSideEffect(e.val)) {\n        pure = false;\n      } else {\n        GetVars gv(vids);\n        e.val->accept(gv);\n      }\n    }\n  });\n\n  if (!pure)\n    return false;\n\n  auto *source = expr->assign;\n  auto *start = cfg->getBlock(source);\n  auto *end = cfg->getBlock(target);\n  seqassertn(start, \"start CFG block not found\");\n  seqassertn(end, \"end CFG block not found\");\n  bool ok = true;\n\n  std::function<void(CFBlock *, std::vector<CFBlock *> &)> dfs =\n      [&](CFBlock *curr, std::vector<CFBlock *> &path) {\n        path.push_back(curr);\n        if (curr == end) {\n          if (!canForwardExpressionAlongPath(source, target, vids, parsedValues, se,\n                                             path))\n            ok = false;\n        } else {\n          for (auto it = curr->successors_begin(); it != curr->successors_end(); ++it) {\n            if (std::find(path.begin(), path.end(), *it) != path.end())\n              dfs(*it, path);\n          }\n        }\n        path.pop_back();\n      };\n\n  std::vector<CFBlock *> path;\n  dfs(start, path);\n  return ok;\n}\n\nbool canForwardVariable(AssignInstr *assign, Value *destination, BodiedFunc *func,\n                        RD *rd) {\n  auto *var = assign->getLhs();\n\n  // Check 1: Only the given assignment should reach the destination.\n  auto reaching = rd->getReachingDefinitions(var, destination);\n  if (reaching.size() != 1 || reaching[0].assignment->getId() != assign->getId())\n    return false;\n\n  // Check 2: There should be no other uses of the variable that the given assignment\n  // reaches.\n  std::vector<Value *> uses;\n  GetAllUses gu(var, uses);\n  func->accept(gu);\n  for (auto *use : uses) {\n    if (use != destination && use->getId() != assign->getId()) {\n      auto defs = rd->getReachingDefinitions(var, use);\n      for (auto &def : defs) {\n        if (def.assignment->getId() == assign->getId())\n          return false;\n      }\n    }\n  }\n\n  return true;\n}\n\nForwardingDAG buildForwardingDAG(BodiedFunc *func, RD *rd, CFG *cfg, SE *se,\n                                 std::vector<NumPyOptimizationUnit> &exprs) {\n  std::unordered_map<id_t, NumPyExpr *> parsedValues;\n  for (auto &e : exprs) {\n    e.expr->apply([&](NumPyExpr &e) {\n      if (e.val)\n        parsedValues.emplace(e.val->getId(), &e);\n    });\n  }\n\n  ForwardingDAG dag;\n  int64_t dstId = 0;\n  for (auto &dst : exprs) {\n    auto *target = dst.expr.get();\n    auto &forwardingVec = dag[&dst];\n\n    std::vector<std::pair<Var *, NumPyExpr *>> vars;\n    target->apply([&](NumPyExpr &e) {\n      if (e.isLeaf()) {\n        if (auto *v = cast<VarValue>(e.val)) {\n          vars.emplace_back(v->getVar(), &e);\n        }\n      }\n    });\n\n    for (auto &p : vars) {\n      int64_t srcId = 0;\n      for (auto &src : exprs) {\n        if (srcId != dstId && src.assign && src.assign->getLhs() == p.first) {\n          auto checkFwdVar = canForwardVariable(src.assign, p.second->val, func, rd);\n          auto checkFwdExpr =\n              canForwardExpression(&src, p.second->val, parsedValues, cfg, se);\n          if (checkFwdVar && checkFwdExpr)\n            forwardingVec.push_back({&dst, &src, p.first, p.second, dstId, srcId});\n        }\n        ++srcId;\n      }\n    }\n    ++dstId;\n  }\n\n  return dag;\n}\n\nstruct UnionFind {\n  std::vector<int64_t> parent;\n  std::vector<int64_t> rank;\n\n  explicit UnionFind(int64_t n) : parent(n), rank(n) {\n    for (auto i = 0; i < n; i++) {\n      parent[i] = i;\n      rank[i] = 0;\n    }\n  }\n\n  int64_t find(int64_t u) {\n    if (parent[u] != u)\n      parent[u] = find(parent[u]);\n    return parent[u];\n  }\n\n  void union_(int64_t u, int64_t v) {\n    auto ru = find(u);\n    auto rv = find(v);\n    if (ru != rv) {\n      if (rank[ru] > rank[rv]) {\n        parent[rv] = ru;\n      } else if (rank[ru] < rank[rv]) {\n        parent[ru] = rv;\n      } else {\n        parent[rv] = ru;\n        ++rank[ru];\n      }\n    }\n  }\n};\n\nstd::vector<ForwardingDAG>\ngetForwardingDAGConnectedComponents(ForwardingDAG &dag,\n                                    std::vector<NumPyOptimizationUnit> &exprs) {\n  auto n = exprs.size();\n  UnionFind uf(n);\n\n  for (auto i = 0; i < n; i++) {\n    for (auto &fwd : dag[&exprs[i]]) {\n      uf.union_(i, fwd.srcId);\n    }\n  }\n\n  std::vector<std::vector<NumPyOptimizationUnit *>> components(n);\n  for (auto i = 0; i < n; i++) {\n    auto root = uf.find(i);\n    components[root].push_back(&exprs[i]);\n  }\n\n  std::vector<ForwardingDAG> result;\n  for (auto &c : components) {\n    if (c.empty())\n      continue;\n\n    ForwardingDAG d;\n    for (auto *expr : c)\n      d.emplace(expr, dag[expr]);\n    result.push_back(d);\n  }\n\n  return result;\n}\n\nbool hasCycleHelper(int64_t v, ForwardingDAG &dag,\n                    std::vector<NumPyOptimizationUnit> &exprs,\n                    std::vector<bool> &visited, std::vector<bool> &recStack) {\n  visited[v] = true;\n  recStack[v] = true;\n\n  for (auto &neighbor : dag[&exprs[v]]) {\n    if (!visited[neighbor.srcId]) {\n      if (hasCycleHelper(neighbor.srcId, dag, exprs, visited, recStack))\n        return true;\n    } else if (recStack[neighbor.srcId]) {\n      return true;\n    }\n  }\n\n  recStack[v] = false;\n  return false;\n}\n\nbool hasCycle(ForwardingDAG &dag, std::vector<NumPyOptimizationUnit> &exprs) {\n  auto n = exprs.size();\n  std::vector<bool> visited(n, false);\n  std::vector<bool> recStack(n, false);\n\n  for (auto i = 0; i < n; i++) {\n    if (dag.find(&exprs[i]) != dag.end() && !visited[i] &&\n        hasCycleHelper(i, dag, exprs, visited, recStack))\n      return true;\n  }\n  return false;\n}\n\nvoid doForwardingHelper(ForwardingDAG &dag, NumPyOptimizationUnit *curr,\n                        std::unordered_set<NumPyOptimizationUnit *> &done,\n                        std::vector<AssignInstr *> &assignsToDelete) {\n  if (done.count(curr))\n    return;\n\n  auto forwardings = dag[curr];\n  for (auto &fwd : forwardings) {\n    doForwardingHelper(dag, fwd.src, done, assignsToDelete);\n    // Note that order of leaves here doesn't matter since they're guaranteed to have no\n    // side effects based on forwarding checks.\n    fwd.dst->leaves.insert(fwd.dst->leaves.end(), fwd.src->leaves.begin(),\n                           fwd.src->leaves.end());\n    fwd.dstLeaf->replace(*fwd.src->expr);\n    assignsToDelete.push_back(fwd.src->assign);\n  }\n\n  done.insert(curr);\n}\n} // namespace\n\nstd::vector<ForwardingDAG>\ngetForwardingDAGs(BodiedFunc *func, RD *rd, CFG *cfg, SE *se,\n                  std::vector<NumPyOptimizationUnit> &exprs) {\n  auto dag = buildForwardingDAG(func, rd, cfg, se, exprs);\n  auto dags = getForwardingDAGConnectedComponents(dag, exprs);\n  dags.erase(std::remove_if(dags.begin(), dags.end(),\n                            [&](ForwardingDAG &dag) { return hasCycle(dag, exprs); }),\n             dags.end());\n  return dags;\n}\n\nNumPyOptimizationUnit *doForwarding(ForwardingDAG &dag,\n                                    std::vector<AssignInstr *> &assignsToDelete) {\n  seqassertn(!dag.empty(), \"empty forwarding DAG encountered\");\n  std::unordered_set<NumPyOptimizationUnit *> done;\n  for (auto &e : dag) {\n    doForwardingHelper(dag, e.first, done, assignsToDelete);\n  }\n\n  // Find the root\n  std::unordered_set<NumPyOptimizationUnit *> notRoot;\n  for (auto &e : dag) {\n    for (auto &f : e.second) {\n      notRoot.insert(f.src);\n    }\n  }\n  seqassertn(notRoot.size() == dag.size() - 1,\n             \"multiple roots found in forwarding DAG\");\n\n  for (auto &e : dag) {\n    if (notRoot.count(e.first) == 0)\n      return e.first;\n  }\n\n  seqassertn(false, \"could not find root in forwarding DAG\");\n  return nullptr;\n}\n\n} // namespace numpy\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/numpy/indexing.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"indexing.h\"\n\n#include \"codon/cir/analyze/dataflow/reaching.h\"\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n\n#include <algorithm>\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace numpy {\nnamespace {\nconst std::string FUSION_MODULE = \"std.numpy.fusion\";\n\nstruct Term {\n  enum Kind { INT, VAR, LEN } kind;\n  int64_t val;\n  const VarValue *var;\n\n  Term(Kind kind, int64_t val, const VarValue *var) : kind(kind), val(val), var(var) {}\n\n  static Term valTerm(int64_t v) { return {Kind::INT, v, nullptr}; }\n\n  static Term varTerm(VarValue *v) { return {Kind::VAR, 1, v}; }\n\n  static Term lenTerm(VarValue *v) { return {Kind::LEN, 1, v}; }\n\n  void negate() { val = -val; }\n\n  void multiply(int64_t n) { val *= n; }\n\n  bool combine(const Term &other) {\n    if (kind == other.kind &&\n        (kind == Kind::INT || var->getVar()->getId() == other.var->getVar()->getId())) {\n      val += other.val;\n      return true;\n    }\n    return false;\n  }\n\n  bool zero() const { return val == 0; }\n\n  std::string str() const {\n    switch (kind) {\n    case Kind::INT:\n      return std::to_string(val);\n    case Kind::VAR:\n      return (val == 1 ? \"\" : std::to_string(val) + \"*\") + var->getVar()->getName();\n    case Kind::LEN:\n      return (val == 1 ? \"\" : std::to_string(val) + \"*\") + \"len(\" +\n             var->getVar()->getName() + \")\";\n    }\n    return \"(?)\";\n  }\n};\n\nstd::string t2s(const std::vector<Term> &terms) {\n  if (terms.empty()) {\n    return \"[]\";\n  }\n  std::string s = \"[\" + terms[0].str();\n  for (auto i = 1; i < terms.size(); i++)\n    s += \", \" + terms[i].str();\n  s += \"]\";\n  return s;\n}\n\nvoid simplify(std::vector<Term> &terms) {\n  // Presumably the number of terms will be small,\n  // so we use a simple quadratic algorithm.\n  for (auto it1 = terms.begin(); it1 != terms.end(); ++it1) {\n    auto it2 = it1 + 1;\n    while (it2 != terms.end()) {\n      auto &t1 = *it1;\n      auto &t2 = *it2;\n      if (t1.combine(t2)) {\n        it2 = terms.erase(it2);\n      } else {\n        ++it2;\n      }\n    }\n  }\n\n  terms.erase(std::remove_if(terms.begin(), terms.end(),\n                             [](const Term &t) { return t.zero(); }),\n              terms.end());\n}\n\nbool checkTotal(const std::vector<Term> &terms, bool strict) {\n  int64_t total = 0;\n  for (const auto &term : terms) {\n    switch (term.kind) {\n    case Term::Kind::INT:\n      total += term.val;\n      break;\n    case Term::Kind::VAR:\n      if (term.val != 0)\n        return false;\n      break;\n    case Term::Kind::LEN:\n      // len is never negative\n      break;\n    }\n  }\n  return strict ? (total > 0) : (total >= 0);\n}\n\nbool lessCheck(const std::vector<Term> &terms1, const std::vector<Term> &terms2,\n               bool strict) {\n  // Checking if: t1_0 + t1_1 + ... + t1_N < t2_0 + t2_1 + ... + t2_M\n  // Same as: t2_0 + t2_1 + ... + t2_M - t1_0 - t1_1 - ... - t1_N > 0\n  std::vector<Term> tmp(terms1.begin(), terms1.end());\n  for (auto &t : tmp)\n    t.negate();\n\n  std::vector<Term> terms(terms2.begin(), terms2.end());\n  terms.insert(terms.end(), tmp.begin(), tmp.end());\n  simplify(terms);\n  return checkTotal(terms, strict);\n}\n\nbool lessThan(const std::vector<Term> &terms1, const std::vector<Term> &terms2) {\n  return lessCheck(terms1, terms2, /*strict=*/true);\n}\n\nbool lessThanOrEqual(const std::vector<Term> &terms1, const std::vector<Term> &terms2) {\n  return lessCheck(terms1, terms2, /*strict=*/false);\n}\n\nstd::vector<Term> replaceLoopVariable(const std::vector<Term> &terms, Var *loopVar,\n                                      const std::vector<Term> &replacement) {\n  std::vector<Term> ans;\n  for (auto &term : terms) {\n    if (term.kind == Term::Kind::VAR &&\n        term.var->getVar()->getId() == loopVar->getId()) {\n      for (auto &rep : replacement) {\n        ans.push_back(rep);\n        ans.back().multiply(term.val);\n      }\n    } else {\n      ans.push_back(term);\n    }\n  }\n  return ans;\n}\n\nbool isArrayType(types::Type *t, bool dim1 = false) {\n  bool result = t && isA<types::RecordType>(t) &&\n                t->getName().rfind(\n                    ast::getMangledClass(\"std.numpy.ndarray\", \"ndarray\") + \"[\", 0) == 0;\n  if (result && dim1) {\n    auto generics = t->getGenerics();\n    seqassertn(generics.size() == 2 && generics[0].isType() && generics[1].isStatic(),\n               \"unrecognized ndarray generics\");\n    auto ndim = generics[1].getStaticValue();\n    result &= (ndim == 1);\n  }\n  return result;\n}\n\nbool isLen(Func *f) {\n  return f->getName().rfind(ast::getMangledFunc(\"std.internal.builtin\", \"len\") + \"[\",\n                            0) == 0;\n}\n\nbool parse(Value *x, std::vector<Term> &terms, bool negate = false) {\n  auto push = [&](const Term &t) {\n    terms.push_back(t);\n    if (negate)\n      terms.back().negate();\n  };\n\n  if (auto *v = cast<IntConst>(x)) {\n    push(Term::valTerm(v->getVal()));\n    return true;\n  }\n\n  if (auto *v = cast<VarValue>(x)) {\n    push(Term::varTerm(v));\n    return true;\n  }\n\n  auto *M = x->getModule();\n  auto *intType = M->getIntType();\n\n  if (auto *v = cast<CallInstr>(x)) {\n    if (util::isCallOf(v, Module::ADD_MAGIC_NAME, {intType, intType}, intType,\n                       /*method=*/true)) {\n      return parse(v->front(), terms, negate) && parse(v->back(), terms, negate);\n    }\n\n    if (util::isCallOf(v, Module::SUB_MAGIC_NAME, {intType, intType}, intType,\n                       /*method=*/true)) {\n      return parse(v->front(), terms, negate) && parse(v->back(), terms, !negate);\n    }\n\n    if (v->numArgs() == 1 && isArrayType(v->front()->getType()) &&\n        isA<VarValue>(v->front()) &&\n        util::isCallOf(v, \"size\", {v->front()->getType()}, intType, /*method=*/true)) {\n      push(Term::lenTerm(cast<VarValue>(v->front())));\n      return true;\n    }\n\n    if (v->numArgs() == 1 && isArrayType(v->front()->getType()) &&\n        isA<VarValue>(v->front()) &&\n        util::isCallOf(v, \"len\", {v->front()->getType()}, intType, /*method=*/false) &&\n        isLen(util::getFunc(v->getCallee()))) {\n      push(Term::lenTerm(cast<VarValue>(v->front())));\n      return true;\n    }\n  }\n\n  return false;\n}\n\nstruct IndexInfo {\n  CallInstr *orig;\n  VarValue *arr;\n  Value *idx;\n  Value *item;\n\n  IndexInfo(CallInstr *orig, VarValue *arr, Value *idx, Value *item)\n      : orig(orig), arr(arr), idx(idx), item(item) {}\n};\n\nstruct FindArrayIndex : public util::Operator {\n  std::vector<IndexInfo> indexes;\n\n  FindArrayIndex() : util::Operator(/*childrenFirst=*/true), indexes() {}\n\n  void handle(CallInstr *v) override {\n    if (v->numArgs() < 1 || !isArrayType(v->front()->getType(), /*dim1=*/true) ||\n        !isA<VarValue>(v->front()))\n      return;\n\n    auto *M = v->getModule();\n    auto *arrType = v->front()->getType();\n    auto *arrVar = cast<VarValue>(v->front());\n    auto *intType = v->getModule()->getIntType();\n\n    if (util::isCallOf(v, Module::GETITEM_MAGIC_NAME, {arrType, intType},\n                       /*output=*/nullptr,\n                       /*method=*/true)) {\n      indexes.emplace_back(v, arrVar, v->back(), nullptr);\n    } else if (util::isCallOf(v, Module::SETITEM_MAGIC_NAME,\n                              {arrType, intType, v->back()->getType()},\n                              /*output=*/nullptr,\n                              /*method=*/true)) {\n      indexes.emplace_back(v, arrVar, *(v->begin() + 1), v->back());\n    }\n  }\n};\n\nvoid elideBoundsCheck(IndexInfo &index) {\n  auto *M = index.orig->getModule();\n  util::CloneVisitor cv(M);\n\n  if (index.item) {\n    auto *setitem = M->getOrRealizeFunc(\n        \"_array1d_set_nocheck\",\n        {index.arr->getType(), M->getIntType(), index.item->getType()}, {},\n        FUSION_MODULE);\n    seqassertn(setitem, \"setitem function not found\");\n    index.orig->replaceAll(\n        util::call(setitem, {M->Nr<VarValue>(index.arr->getVar()), cv.clone(index.idx),\n                             cv.clone(index.item)}));\n  } else {\n    auto *getitem =\n        M->getOrRealizeFunc(\"_array1d_get_nocheck\",\n                            {index.arr->getType(), M->getIntType()}, {}, FUSION_MODULE);\n    seqassertn(getitem, \"getitem function not found\");\n    index.orig->replaceAll(util::call(\n        getitem, {M->Nr<VarValue>(index.arr->getVar()), cv.clone(index.idx)}));\n  }\n}\n\nbool isOriginalLoopVar(const Value *loc, ImperativeForFlow *loop,\n                       analyze::dataflow::RDInspector *rd) {\n  // The loop variable should have exactly two reaching definitions:\n  //   - The initial assignment for the loop\n  //   - The update assignment\n  // Both are represented as `SyntheticAssignInstr` in the CFG.\n  auto *loopVar = loop->getVar();\n  auto defs = rd->getReachingDefinitions(loopVar, loc);\n  if (defs.size() != 2)\n    return false;\n\n  using SAI = analyze::dataflow::SyntheticAssignInstr;\n  auto *s1 = cast<SAI>(defs[0].assignment);\n  auto *s2 = cast<SAI>(defs[1].assignment);\n\n  if (!s1 || !s2)\n    return false;\n\n  if (s1->getKind() == SAI::Kind::ADD && s2->getKind() == SAI::Kind::KNOWN) {\n    auto *tmp = s1;\n    s1 = s2;\n    s2 = tmp;\n  } else if (!(s1->getKind() == SAI::Kind::KNOWN && s2->getKind() == SAI::Kind::ADD)) {\n    return false;\n  }\n\n  auto *loop1 = cast<ImperativeForFlow>(s1->getSource());\n  auto *loop2 = cast<ImperativeForFlow>(s2->getSource());\n\n  if (!loop1 || !loop2 || loop1->getId() != loop->getId() ||\n      loop2->getId() != loop->getId())\n    return false;\n\n  return true;\n}\n\nconst VarValue *isAliasOfLoopVar(const VarValue *v, ImperativeForFlow *loop,\n                                 analyze::dataflow::RDInspector *rd) {\n  auto defs = rd->getReachingDefinitions(v->getVar(), v);\n  auto *loopVar = loop->getVar();\n\n  if (defs.size() != 1 || !defs[0].known() || !isA<VarValue>(defs[0].assignee) ||\n      cast<VarValue>(defs[0].assignee)->getVar()->getId() != loopVar->getId() ||\n      !isOriginalLoopVar(v, loop, rd))\n    return nullptr;\n\n  return cast<VarValue>(defs[0].assignee);\n}\n\nbool canElideBoundsCheck(ImperativeForFlow *loop, IndexInfo &index,\n                         const std::vector<Term> &startTerms,\n                         const std::vector<Term> &stopTerms,\n                         analyze::dataflow::RDInspector *rd) {\n  auto *loopVar = loop->getVar();\n  std::vector<Term> idxTerms;\n  if (!parse(index.idx, idxTerms))\n    return false;\n\n  // First, check that all involved variables refer to a consistent\n  // value. We do this by making sure there is just one reaching def\n  // for all VarValues referring to the same Var.\n  std::unordered_map<id_t, id_t> reach; // \"[var id] -> [reaching def id]\" map\n  auto check = [&](const VarValue *v) {\n    auto id = v->getVar()->getId();\n    if (id == loopVar->getId()) {\n      return isOriginalLoopVar(v, loop, rd);\n    } else {\n      auto defs = rd->getReachingDefinitions(v->getVar(), v);\n      if (defs.size() != 1)\n        return false;\n\n      auto rid = defs[0].getId();\n      auto it = reach.find(id);\n\n      if (it == reach.end()) {\n        reach.emplace(id, rid);\n        return true;\n      } else {\n        return it->second == rid;\n      }\n    }\n  };\n\n  if (!check(index.arr))\n    return false;\n\n  for (auto &term : startTerms) {\n    if (term.kind != Term::Kind::INT && !check(term.var))\n      return false;\n  }\n\n  for (auto &term : stopTerms) {\n    if (term.kind != Term::Kind::INT && !check(term.var))\n      return false;\n  }\n\n  for (auto &term : idxTerms) {\n    if (term.kind != Term::Kind::INT && !check(term.var))\n      return false;\n  }\n\n  // Update vars that are aliases of the loop var. This can\n  // happen in e.g. compound assignments which need to create\n  // a temporary copy of the index.\n  for (auto &term : idxTerms) {\n    if (term.kind != Term::Kind::VAR)\n      continue;\n    if (auto *loopVarValue = isAliasOfLoopVar(term.var, loop, rd))\n      term.var = loopVarValue;\n  }\n\n  // Next, see if we can prove that indexes are in range.\n  std::vector<Term> limit = {Term::lenTerm(index.arr)};\n  auto terms1 = replaceLoopVariable(idxTerms, loopVar, startTerms);\n  auto terms2 = replaceLoopVariable(idxTerms, loopVar, stopTerms);\n\n  if (loop->getStep() > 0) {\n    return lessThanOrEqual({Term::valTerm(0)}, terms1) &&\n           lessThanOrEqual(terms2, limit);\n  } else {\n    return lessThan(terms1, limit) && lessThanOrEqual({Term::valTerm(-1)}, terms2);\n  }\n}\n\n} // namespace\n\nvoid NumPyBoundsCheckElisionPass::visit(ImperativeForFlow *f) {\n  if (f->getStep() == 0 || f->getVar()->isGlobal())\n    return;\n\n  std::vector<Term> startTerms;\n  std::vector<Term> stopTerms;\n  FindArrayIndex find;\n  f->getBody()->accept(find);\n\n  if (find.indexes.empty() || !parse(f->getStart(), startTerms) ||\n      !parse(f->getEnd(), stopTerms))\n    return;\n\n  auto *r = getAnalysisResult<analyze::dataflow::RDResult>(reachingDefKey);\n  auto *c = r->cfgResult;\n  auto it = r->results.find(getParentFunc()->getId());\n  if (it == r->results.end())\n    return;\n  auto *rd = it->second.get();\n\n  for (auto &index : find.indexes) {\n    if (canElideBoundsCheck(f, index, startTerms, stopTerms, rd)) {\n      elideBoundsCheck(index);\n    }\n  }\n}\n\nconst std::string NumPyBoundsCheckElisionPass::KEY = \"core-numpy-bounds-check-elision\";\n\n} // namespace numpy\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/numpy/indexing.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"codon/cir/transform/pass.h\"\n#include \"codon/cir/types/types.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace numpy {\n\n/// NumPy bounds check elision pass\nclass NumPyBoundsCheckElisionPass : public OperatorPass {\nprivate:\n  /// Key of the reaching definition analysis\n  std::string reachingDefKey;\n\npublic:\n  static const std::string KEY;\n\n  /// Constructs a NumPy bounds check elision pass.\n  /// @param reachingDefKey the reaching definition analysis' key\n  NumPyBoundsCheckElisionPass(const std::string &reachingDefKey)\n      : OperatorPass(), reachingDefKey(reachingDefKey) {}\n\n  std::string getKey() const override { return KEY; }\n  void visit(ImperativeForFlow *f) override;\n};\n\n} // namespace numpy\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/numpy/numpy.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"numpy.h\"\n\n#include \"codon/cir/analyze/dataflow/reaching.h\"\n#include \"codon/cir/analyze/module/global_vars.h\"\n#include \"codon/cir/analyze/module/side_effect.h\"\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n\n#include \"llvm/Support/CommandLine.h\"\n\n#include <algorithm>\n#include <complex>\n#include <sstream>\n#include <utility>\n\n#define XLOG(c, ...)                                                                   \\\n  do {                                                                                 \\\n    if (Verbose)                                                                       \\\n      LOG(c, ##__VA_ARGS__);                                                           \\\n  } while (false)\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace numpy {\nnamespace {\nllvm::cl::opt<int> AlwaysFuseCostThreshold(\n    \"npfuse-always\", llvm::cl::desc(\"Expression cost below which (<=) to always fuse\"),\n    llvm::cl::init(10));\n\nllvm::cl::opt<int> NeverFuseCostThreshold(\n    \"npfuse-never\", llvm::cl::desc(\"Expression cost above which (>) to never fuse\"),\n    llvm::cl::init(50));\n\nllvm::cl::opt<bool> Verbose(\"npfuse-verbose\",\n                            llvm::cl::desc(\"Print information about fused expressions\"),\n                            llvm::cl::init(false));\n\nbool isArrayType(types::Type *t) {\n  return t && isA<types::RecordType>(t) &&\n         t->getName().rfind(ast::getMangledClass(\"std.numpy.ndarray\", \"ndarray\") + \"[\",\n                            0) == 0;\n}\n\nbool isUFuncType(types::Type *t) {\n  return t &&\n         (t->getName().rfind(\n              ast::getMangledClass(\"std.numpy.ufunc\", \"UnaryUFunc\") + \"[\", 0) == 0 ||\n          t->getName().rfind(\n              ast::getMangledClass(\"std.numpy.ufunc\", \"BinaryUFunc\") + \"[\", 0) == 0);\n}\n\nbool isNoneType(types::Type *t, NumPyPrimitiveTypes &T) {\n  return t && (t->is(T.none) || t->is(T.optnone));\n}\n} // namespace\n\nconst std::string FUSION_MODULE = \"std.numpy.fusion\";\n\nNumPyPrimitiveTypes::NumPyPrimitiveTypes(Module *M)\n    : none(M->getNoneType()), optnone(M->getOptionalType(none)),\n      bool_(M->getBoolType()), i8(M->getIntNType(8, true)),\n      u8(M->getIntNType(8, false)), i16(M->getIntNType(16, true)),\n      u16(M->getIntNType(16, false)), i32(M->getIntNType(32, true)),\n      u32(M->getIntNType(32, false)), i64(M->getIntType()),\n      u64(M->getIntNType(64, false)), f16(M->getFloat16Type()),\n      f32(M->getFloat32Type()), f64(M->getFloatType()),\n      c64(M->getType(ast::getMangledClass(\"std.internal.types.complex\", \"complex64\"))),\n      c128(M->getType(ast::getMangledClass(\"std.internal.types.complex\", \"complex\"))) {}\n\nNumPyType::NumPyType(Type dtype, int64_t ndim) : dtype(dtype), ndim(ndim) {\n  seqassertn(ndim >= 0, \"ndim must be non-negative\");\n}\n\nNumPyType::NumPyType() : NumPyType(NP_TYPE_NONE) {}\n\nNumPyType NumPyType::get(types::Type *t, NumPyPrimitiveTypes &T) {\n  if (t->is(T.bool_))\n    return {NumPyType::NP_TYPE_BOOL};\n  if (t->is(T.i8))\n    return {NumPyType::NP_TYPE_I8};\n  if (t->is(T.u8))\n    return {NumPyType::NP_TYPE_U8};\n  if (t->is(T.i16))\n    return {NumPyType::NP_TYPE_I16};\n  if (t->is(T.u16))\n    return {NumPyType::NP_TYPE_U16};\n  if (t->is(T.i32))\n    return {NumPyType::NP_TYPE_I32};\n  if (t->is(T.u32))\n    return {NumPyType::NP_TYPE_U32};\n  if (t->is(T.i64))\n    return {NumPyType::NP_TYPE_I64};\n  if (t->is(T.u64))\n    return {NumPyType::NP_TYPE_U64};\n  if (t->is(T.f16))\n    return {NumPyType::NP_TYPE_F16};\n  if (t->is(T.f32))\n    return {NumPyType::NP_TYPE_F32};\n  if (t->is(T.f64))\n    return {NumPyType::NP_TYPE_F64};\n  if (t->is(T.c64))\n    return {NumPyType::NP_TYPE_C64};\n  if (t->is(T.c128))\n    return {NumPyType::NP_TYPE_C128};\n  if (isArrayType(t)) {\n    auto generics = t->getGenerics();\n    seqassertn(generics.size() == 2 && generics[0].isType() && generics[1].isStatic(),\n               \"unrecognized ndarray generics\");\n    auto *dtype = generics[0].getTypeValue();\n    auto ndim = generics[1].getStaticValue();\n    if (dtype->is(T.bool_))\n      return {NumPyType::NP_TYPE_ARR_BOOL, ndim};\n    if (dtype->is(T.i8))\n      return {NumPyType::NP_TYPE_ARR_I8, ndim};\n    if (dtype->is(T.u8))\n      return {NumPyType::NP_TYPE_ARR_U8, ndim};\n    if (dtype->is(T.i16))\n      return {NumPyType::NP_TYPE_ARR_I16, ndim};\n    if (dtype->is(T.u16))\n      return {NumPyType::NP_TYPE_ARR_U16, ndim};\n    if (dtype->is(T.i32))\n      return {NumPyType::NP_TYPE_ARR_I32, ndim};\n    if (dtype->is(T.u32))\n      return {NumPyType::NP_TYPE_ARR_U32, ndim};\n    if (dtype->is(T.i64))\n      return {NumPyType::NP_TYPE_ARR_I64, ndim};\n    if (dtype->is(T.u64))\n      return {NumPyType::NP_TYPE_ARR_U64, ndim};\n    if (dtype->is(T.f16))\n      return {NumPyType::NP_TYPE_ARR_F16, ndim};\n    if (dtype->is(T.f32))\n      return {NumPyType::NP_TYPE_ARR_F32, ndim};\n    if (dtype->is(T.f64))\n      return {NumPyType::NP_TYPE_ARR_F64, ndim};\n    if (dtype->is(T.c64))\n      return {NumPyType::NP_TYPE_ARR_C64, ndim};\n    if (dtype->is(T.c128))\n      return {NumPyType::NP_TYPE_ARR_C128, ndim};\n  }\n  return {};\n}\n\ntypes::Type *NumPyType::getIRBaseType(NumPyPrimitiveTypes &T) const {\n  switch (dtype) {\n  case NP_TYPE_NONE:\n    seqassertn(false, \"unexpected type code (NONE)\");\n    return nullptr;\n  case NP_TYPE_BOOL:\n    return T.bool_;\n  case NP_TYPE_I8:\n    return T.i8;\n  case NP_TYPE_U8:\n    return T.u8;\n  case NP_TYPE_I16:\n    return T.i16;\n  case NP_TYPE_U16:\n    return T.u16;\n  case NP_TYPE_I32:\n    return T.i32;\n  case NP_TYPE_U32:\n    return T.u32;\n  case NP_TYPE_I64:\n    return T.i64;\n  case NP_TYPE_U64:\n    return T.u64;\n  case NP_TYPE_F16:\n    return T.f16;\n  case NP_TYPE_F32:\n    return T.f32;\n  case NP_TYPE_F64:\n    return T.f64;\n  case NP_TYPE_C64:\n    return T.c64;\n  case NP_TYPE_C128:\n    return T.c128;\n  case NP_TYPE_SCALAR_END:\n    seqassertn(false, \"unexpected type code (SCALAR_END)\");\n    return nullptr;\n  case NP_TYPE_ARR_BOOL:\n    return T.bool_;\n  case NP_TYPE_ARR_I8:\n    return T.i8;\n  case NP_TYPE_ARR_U8:\n    return T.u8;\n  case NP_TYPE_ARR_I16:\n    return T.i16;\n  case NP_TYPE_ARR_U16:\n    return T.u16;\n  case NP_TYPE_ARR_I32:\n    return T.i32;\n  case NP_TYPE_ARR_U32:\n    return T.u32;\n  case NP_TYPE_ARR_I64:\n    return T.i64;\n  case NP_TYPE_ARR_U64:\n    return T.u64;\n  case NP_TYPE_ARR_F16:\n    return T.f16;\n  case NP_TYPE_ARR_F32:\n    return T.f32;\n  case NP_TYPE_ARR_F64:\n    return T.f64;\n  case NP_TYPE_ARR_C64:\n    return T.c64;\n  case NP_TYPE_ARR_C128:\n    return T.c128;\n  default:\n    seqassertn(false, \"unexpected type code (?)\");\n    return nullptr;\n  }\n}\n\nstd::ostream &operator<<(std::ostream &os, NumPyType const &type) {\n  static const std::unordered_map<NumPyType::Type, std::string> typestrings = {\n      {NumPyType::NP_TYPE_NONE, \"none\"},     {NumPyType::NP_TYPE_BOOL, \"bool\"},\n      {NumPyType::NP_TYPE_I8, \"i8\"},         {NumPyType::NP_TYPE_U8, \"u8\"},\n      {NumPyType::NP_TYPE_I16, \"i16\"},       {NumPyType::NP_TYPE_U16, \"u16\"},\n      {NumPyType::NP_TYPE_I32, \"i32\"},       {NumPyType::NP_TYPE_U32, \"u32\"},\n      {NumPyType::NP_TYPE_I64, \"i64\"},       {NumPyType::NP_TYPE_U64, \"u64\"},\n      {NumPyType::NP_TYPE_F16, \"f16\"},       {NumPyType::NP_TYPE_F32, \"f32\"},\n      {NumPyType::NP_TYPE_F64, \"f64\"},       {NumPyType::NP_TYPE_C64, \"c64\"},\n      {NumPyType::NP_TYPE_C128, \"c128\"},     {NumPyType::NP_TYPE_SCALAR_END, \"\"},\n      {NumPyType::NP_TYPE_ARR_BOOL, \"bool\"}, {NumPyType::NP_TYPE_ARR_I8, \"i8\"},\n      {NumPyType::NP_TYPE_ARR_U8, \"u8\"},     {NumPyType::NP_TYPE_ARR_I16, \"i16\"},\n      {NumPyType::NP_TYPE_ARR_U16, \"u16\"},   {NumPyType::NP_TYPE_ARR_I32, \"i32\"},\n      {NumPyType::NP_TYPE_ARR_U32, \"u32\"},   {NumPyType::NP_TYPE_ARR_I64, \"i64\"},\n      {NumPyType::NP_TYPE_ARR_U64, \"u64\"},   {NumPyType::NP_TYPE_ARR_F16, \"f16\"},\n      {NumPyType::NP_TYPE_ARR_F32, \"f32\"},   {NumPyType::NP_TYPE_ARR_F64, \"f64\"},\n      {NumPyType::NP_TYPE_ARR_C64, \"c64\"},   {NumPyType::NP_TYPE_ARR_C128, \"c128\"},\n  };\n\n  auto it = typestrings.find(type.dtype);\n  seqassertn(it != typestrings.end(), \"type not found\");\n  auto s = it->second;\n  if (type.isArray())\n    os << \"array[\" << s << \", \" << type.ndim << \"]\";\n  else\n    os << s;\n  return os;\n}\n\nstd::string NumPyType::str() const {\n  std::stringstream buffer;\n  buffer << *this;\n  return buffer.str();\n}\n\nCodegenContext::CodegenContext(Module *M, SeriesFlow *series, BodiedFunc *func,\n                               NumPyPrimitiveTypes &T)\n    : M(M), series(series), func(func), vars(), T(T) {}\n\nstd::unique_ptr<NumPyExpr> parse(Value *v,\n                                 std::vector<std::pair<NumPyExpr *, Value *>> &leaves,\n                                 NumPyPrimitiveTypes &T) {\n  struct NumPyMagicMethod {\n    std::string name;\n    NumPyExpr::Op op;\n    int args;\n    bool right;\n  };\n\n  struct NumPyUFunc {\n    std::string name;\n    NumPyExpr::Op op;\n    int args;\n  };\n\n  static std::vector<NumPyMagicMethod> magics = {\n      {Module::POS_MAGIC_NAME, NumPyExpr::NP_OP_POS, 1, false},\n      {Module::NEG_MAGIC_NAME, NumPyExpr::NP_OP_NEG, 1, false},\n      {Module::INVERT_MAGIC_NAME, NumPyExpr::NP_OP_INVERT, 1, false},\n      {Module::ABS_MAGIC_NAME, NumPyExpr::NP_OP_ABS, 1, false},\n\n      {Module::ADD_MAGIC_NAME, NumPyExpr::NP_OP_ADD, 2, false},\n      {Module::SUB_MAGIC_NAME, NumPyExpr::NP_OP_SUB, 2, false},\n      {Module::MUL_MAGIC_NAME, NumPyExpr::NP_OP_MUL, 2, false},\n      {Module::MATMUL_MAGIC_NAME, NumPyExpr::NP_OP_MATMUL, 2, false},\n      {Module::TRUE_DIV_MAGIC_NAME, NumPyExpr::NP_OP_TRUE_DIV, 2, false},\n      {Module::FLOOR_DIV_MAGIC_NAME, NumPyExpr::NP_OP_FLOOR_DIV, 2, false},\n      {Module::MOD_MAGIC_NAME, NumPyExpr::NP_OP_MOD, 2, false},\n      {Module::POW_MAGIC_NAME, NumPyExpr::NP_OP_POW, 2, false},\n      {Module::LSHIFT_MAGIC_NAME, NumPyExpr::NP_OP_LSHIFT, 2, false},\n      {Module::RSHIFT_MAGIC_NAME, NumPyExpr::NP_OP_RSHIFT, 2, false},\n      {Module::AND_MAGIC_NAME, NumPyExpr::NP_OP_AND, 2, false},\n      {Module::OR_MAGIC_NAME, NumPyExpr::NP_OP_OR, 2, false},\n      {Module::XOR_MAGIC_NAME, NumPyExpr::NP_OP_XOR, 2, false},\n\n      {Module::RADD_MAGIC_NAME, NumPyExpr::NP_OP_ADD, 2, true},\n      {Module::RSUB_MAGIC_NAME, NumPyExpr::NP_OP_SUB, 2, true},\n      {Module::RMUL_MAGIC_NAME, NumPyExpr::NP_OP_MUL, 2, true},\n      {Module::RMATMUL_MAGIC_NAME, NumPyExpr::NP_OP_MATMUL, 2, true},\n      {Module::RTRUE_DIV_MAGIC_NAME, NumPyExpr::NP_OP_TRUE_DIV, 2, true},\n      {Module::RFLOOR_DIV_MAGIC_NAME, NumPyExpr::NP_OP_FLOOR_DIV, 2, true},\n      {Module::RMOD_MAGIC_NAME, NumPyExpr::NP_OP_MOD, 2, true},\n      {Module::RPOW_MAGIC_NAME, NumPyExpr::NP_OP_POW, 2, true},\n      {Module::RLSHIFT_MAGIC_NAME, NumPyExpr::NP_OP_LSHIFT, 2, true},\n      {Module::RRSHIFT_MAGIC_NAME, NumPyExpr::NP_OP_RSHIFT, 2, true},\n      {Module::RAND_MAGIC_NAME, NumPyExpr::NP_OP_AND, 2, true},\n      {Module::ROR_MAGIC_NAME, NumPyExpr::NP_OP_OR, 2, true},\n      {Module::RXOR_MAGIC_NAME, NumPyExpr::NP_OP_XOR, 2, true},\n\n      {Module::EQ_MAGIC_NAME, NumPyExpr::NP_OP_EQ, 2, false},\n      {Module::NE_MAGIC_NAME, NumPyExpr::NP_OP_NE, 2, false},\n      {Module::LT_MAGIC_NAME, NumPyExpr::NP_OP_LT, 2, false},\n      {Module::LE_MAGIC_NAME, NumPyExpr::NP_OP_LE, 2, false},\n      {Module::GT_MAGIC_NAME, NumPyExpr::NP_OP_GT, 2, false},\n      {Module::GE_MAGIC_NAME, NumPyExpr::NP_OP_GE, 2, false},\n  };\n\n  static std::vector<NumPyUFunc> ufuncs = {\n      {\"positive\", NumPyExpr::NP_OP_POS, 1},\n      {\"negative\", NumPyExpr::NP_OP_NEG, 1},\n      {\"invert\", NumPyExpr::NP_OP_INVERT, 1},\n      {\"abs\", NumPyExpr::NP_OP_ABS, 1},\n      {\"absolute\", NumPyExpr::NP_OP_ABS, 1},\n      {\"add\", NumPyExpr::NP_OP_ADD, 2},\n      {\"subtract\", NumPyExpr::NP_OP_SUB, 2},\n      {\"multiply\", NumPyExpr::NP_OP_MUL, 2},\n      {\"divide\", NumPyExpr::NP_OP_TRUE_DIV, 2},\n      {\"floor_divide\", NumPyExpr::NP_OP_FLOOR_DIV, 2},\n      {\"remainder\", NumPyExpr::NP_OP_MOD, 2},\n      {\"fmod\", NumPyExpr::NP_OP_FMOD, 2},\n      {\"power\", NumPyExpr::NP_OP_POW, 2},\n      {\"left_shift\", NumPyExpr::NP_OP_LSHIFT, 2},\n      {\"right_shift\", NumPyExpr::NP_OP_RSHIFT, 2},\n      {\"bitwise_and\", NumPyExpr::NP_OP_AND, 2},\n      {\"bitwise_or\", NumPyExpr::NP_OP_OR, 2},\n      {\"bitwise_xor\", NumPyExpr::NP_OP_XOR, 2},\n      {\"logical_and\", NumPyExpr::NP_OP_LOGICAL_AND, 2},\n      {\"logical_or\", NumPyExpr::NP_OP_LOGICAL_OR, 2},\n      {\"logical_xor\", NumPyExpr::NP_OP_LOGICAL_XOR, 2},\n      {\"equal\", NumPyExpr::NP_OP_EQ, 2},\n      {\"not_equal\", NumPyExpr::NP_OP_NE, 2},\n      {\"less\", NumPyExpr::NP_OP_LT, 2},\n      {\"less_equal\", NumPyExpr::NP_OP_LE, 2},\n      {\"greater\", NumPyExpr::NP_OP_GT, 2},\n      {\"greater_equal\", NumPyExpr::NP_OP_GE, 2},\n      {\"minimum\", NumPyExpr::NP_OP_MIN, 2},\n      {\"maximum\", NumPyExpr::NP_OP_MAX, 2},\n      {\"fmin\", NumPyExpr::NP_OP_FMIN, 2},\n      {\"fmax\", NumPyExpr::NP_OP_FMAX, 2},\n      {\"sin\", NumPyExpr::NP_OP_SIN, 1},\n      {\"cos\", NumPyExpr::NP_OP_COS, 1},\n      {\"tan\", NumPyExpr::NP_OP_TAN, 1},\n      {\"arcsin\", NumPyExpr::NP_OP_ARCSIN, 1},\n      {\"arccos\", NumPyExpr::NP_OP_ARCCOS, 1},\n      {\"arctan\", NumPyExpr::NP_OP_ARCTAN, 1},\n      {\"arctan2\", NumPyExpr::NP_OP_ARCTAN2, 2},\n      {\"hypot\", NumPyExpr::NP_OP_HYPOT, 2},\n      {\"sinh\", NumPyExpr::NP_OP_SINH, 1},\n      {\"cosh\", NumPyExpr::NP_OP_COSH, 1},\n      {\"tanh\", NumPyExpr::NP_OP_TANH, 1},\n      {\"arcsinh\", NumPyExpr::NP_OP_ARCSINH, 1},\n      {\"arccosh\", NumPyExpr::NP_OP_ARCCOSH, 1},\n      {\"arctanh\", NumPyExpr::NP_OP_ARCTANH, 1},\n      {\"conjugate\", NumPyExpr::NP_OP_CONJ, 1},\n      {\"exp\", NumPyExpr::NP_OP_EXP, 1},\n      {\"exp2\", NumPyExpr::NP_OP_EXP2, 1},\n      {\"log\", NumPyExpr::NP_OP_LOG, 1},\n      {\"log2\", NumPyExpr::NP_OP_LOG2, 1},\n      {\"log10\", NumPyExpr::NP_OP_LOG10, 1},\n      {\"expm1\", NumPyExpr::NP_OP_EXPM1, 1},\n      {\"log1p\", NumPyExpr::NP_OP_LOG1P, 1},\n      {\"sqrt\", NumPyExpr::NP_OP_SQRT, 1},\n      {\"square\", NumPyExpr::NP_OP_SQUARE, 1},\n      {\"cbrt\", NumPyExpr::NP_OP_CBRT, 1},\n      {\"logaddexp\", NumPyExpr::NP_OP_LOGADDEXP, 2},\n      {\"logaddexp2\", NumPyExpr::NP_OP_LOGADDEXP2, 2},\n      {\"reciprocal\", NumPyExpr::NP_OP_RECIPROCAL, 1},\n      {\"rint\", NumPyExpr::NP_OP_RINT, 1},\n      {\"floor\", NumPyExpr::NP_OP_FLOOR, 1},\n      {\"ceil\", NumPyExpr::NP_OP_CEIL, 1},\n      {\"trunc\", NumPyExpr::NP_OP_TRUNC, 1},\n      {\"isnan\", NumPyExpr::NP_OP_ISNAN, 1},\n      {\"isinf\", NumPyExpr::NP_OP_ISINF, 1},\n      {\"isfinite\", NumPyExpr::NP_OP_ISFINITE, 1},\n      {\"sign\", NumPyExpr::NP_OP_SIGN, 1},\n      {\"signbit\", NumPyExpr::NP_OP_SIGNBIT, 1},\n      {\"copysign\", NumPyExpr::NP_OP_COPYSIGN, 2},\n      {\"spacing\", NumPyExpr::NP_OP_SPACING, 1},\n      {\"nextafter\", NumPyExpr::NP_OP_NEXTAFTER, 2},\n      {\"deg2rad\", NumPyExpr::NP_OP_DEG2RAD, 1},\n      {\"radians\", NumPyExpr::NP_OP_DEG2RAD, 1},\n      {\"rad2deg\", NumPyExpr::NP_OP_RAD2DEG, 1},\n      {\"degrees\", NumPyExpr::NP_OP_RAD2DEG, 1},\n      {\"heaviside\", NumPyExpr::NP_OP_HEAVISIDE, 2},\n  };\n\n  auto getNumPyExprType = [](types::Type *t, NumPyPrimitiveTypes &T) -> NumPyType {\n    if (t->is(T.bool_))\n      return {NumPyType::NP_TYPE_BOOL};\n    if (t->is(T.i8))\n      return {NumPyType::NP_TYPE_I8};\n    if (t->is(T.u8))\n      return {NumPyType::NP_TYPE_U8};\n    if (t->is(T.i16))\n      return {NumPyType::NP_TYPE_I16};\n    if (t->is(T.u16))\n      return {NumPyType::NP_TYPE_U16};\n    if (t->is(T.i32))\n      return {NumPyType::NP_TYPE_I32};\n    if (t->is(T.u32))\n      return {NumPyType::NP_TYPE_U32};\n    if (t->is(T.i64))\n      return {NumPyType::NP_TYPE_I64};\n    if (t->is(T.u64))\n      return {NumPyType::NP_TYPE_U64};\n    if (t->is(T.f16))\n      return {NumPyType::NP_TYPE_F16};\n    if (t->is(T.f32))\n      return {NumPyType::NP_TYPE_F32};\n    if (t->is(T.f64))\n      return {NumPyType::NP_TYPE_F64};\n    if (t->is(T.c64))\n      return {NumPyType::NP_TYPE_C64};\n    if (t->is(T.c128))\n      return {NumPyType::NP_TYPE_C128};\n    if (isArrayType(t)) {\n      auto generics = t->getGenerics();\n      seqassertn(generics.size() == 2 && generics[0].isType() && generics[1].isStatic(),\n                 \"unrecognized ndarray generics\");\n      auto *dtype = generics[0].getTypeValue();\n      auto ndim = generics[1].getStaticValue();\n      if (dtype->is(T.bool_))\n        return {NumPyType::NP_TYPE_ARR_BOOL, ndim};\n      if (dtype->is(T.i8))\n        return {NumPyType::NP_TYPE_ARR_I8, ndim};\n      if (dtype->is(T.u8))\n        return {NumPyType::NP_TYPE_ARR_U8, ndim};\n      if (dtype->is(T.i16))\n        return {NumPyType::NP_TYPE_ARR_I16, ndim};\n      if (dtype->is(T.u16))\n        return {NumPyType::NP_TYPE_ARR_U16, ndim};\n      if (dtype->is(T.i32))\n        return {NumPyType::NP_TYPE_ARR_I32, ndim};\n      if (dtype->is(T.u32))\n        return {NumPyType::NP_TYPE_ARR_U32, ndim};\n      if (dtype->is(T.i64))\n        return {NumPyType::NP_TYPE_ARR_I64, ndim};\n      if (dtype->is(T.u64))\n        return {NumPyType::NP_TYPE_ARR_U64, ndim};\n      if (dtype->is(T.f16))\n        return {NumPyType::NP_TYPE_ARR_F16, ndim};\n      if (dtype->is(T.f32))\n        return {NumPyType::NP_TYPE_ARR_F32, ndim};\n      if (dtype->is(T.f64))\n        return {NumPyType::NP_TYPE_ARR_F64, ndim};\n      if (dtype->is(T.c64))\n        return {NumPyType::NP_TYPE_ARR_C64, ndim};\n      if (dtype->is(T.c128))\n        return {NumPyType::NP_TYPE_ARR_C128, ndim};\n    }\n    return {};\n  };\n\n  auto type = getNumPyExprType(v->getType(), T);\n  if (!type)\n    return {};\n\n  // Don't break up expressions that result in scalars or 0-dim arrays since those\n  // should only be computed once\n  if (type.ndim == 0) {\n    auto res = std::make_unique<NumPyExpr>(type, v);\n    leaves.emplace_back(res.get(), v);\n    return std::move(res);\n  }\n\n  if (auto *c = cast<CallInstr>(v)) {\n    auto *f = util::getFunc(c->getCallee());\n\n    // Check for matmul\n    if (f && c->numArgs() == 3 && isNoneType(c->back()->getType(), T) &&\n        (f->getName().rfind(ast::getMangledFunc(\"std.numpy.linalg_sym\", \"matmul\") + \"[\",\n                            0) == 0 ||\n         (f->getName().rfind(ast::getMangledFunc(\"std.numpy.linalg_sym\", \"dot\") + \"[]\",\n                             0) == 0 &&\n          type.ndim == 2))) {\n      std::vector<Value *> args(c->begin(), c->end());\n      auto op = NumPyExpr::NP_OP_MATMUL;\n      auto lhs = parse(args[0], leaves, T);\n      if (!lhs)\n        return {};\n\n      auto rhs = parse(args[1], leaves, T);\n      if (!rhs)\n        return {};\n\n      return std::make_unique<NumPyExpr>(type, v, op, std::move(lhs), std::move(rhs));\n    }\n\n    // Check for builtin abs()\n    if (f && c->numArgs() == 1 &&\n        (f->getName().rfind(ast::getMangledFunc(\"std.internal.builtin\", \"abs\") + \"[\",\n                            0) == 0)) {\n      auto op = NumPyExpr::NP_OP_ABS;\n      auto lhs = parse(c->front(), leaves, T);\n      if (!lhs)\n        return {};\n\n      return std::make_unique<NumPyExpr>(type, v, op, std::move(lhs));\n    }\n\n    // Check for transpose\n    if (f && isArrayType(f->getParentType()) && c->numArgs() == 1 &&\n        f->getUnmangledName() == \"T\") {\n      auto op = NumPyExpr::NP_OP_TRANSPOSE;\n      auto lhs = parse(c->front(), leaves, T);\n      if (!lhs)\n        return {};\n\n      return std::make_unique<NumPyExpr>(type, v, op, std::move(lhs));\n    }\n\n    // Check for ufunc (e.g. \"np.exp()\") call\n    if (f && f->getUnmangledName() == Module::CALL_MAGIC_NAME &&\n        isUFuncType(f->getParentType())) {\n\n      auto ufuncGenerics = f->getParentType()->getGenerics();\n      seqassertn(!ufuncGenerics.empty() && ufuncGenerics[0].isStaticStr(),\n                 \"unrecognized ufunc class generics\");\n      auto ufunc = ufuncGenerics[0].getStaticStringValue();\n\n      auto callGenerics = f->getType()->getGenerics();\n      seqassertn(!callGenerics.empty() && callGenerics[0].isType(),\n                 \"unrecognized ufunc call generics\");\n      auto *dtype = callGenerics[0].getTypeValue();\n\n      if (dtype->is(T.none)) {\n        for (auto &u : ufuncs) {\n          if (u.name == ufunc) {\n            seqassertn(u.args == 1 || u.args == 2,\n                       \"unexpected number of arguments (ufunc)\");\n\n            // Argument order:\n            //   - ufunc self\n            //   - operand 1\n            //   - (if binary) operand 2\n            //   - 'out'\n            //   - 'where'\n            std::vector<Value *> args(c->begin(), c->end());\n            seqassertn(args.size() == u.args + 3, \"unexpected call of {}\", u.name);\n            auto *where = args[args.size() - 1];\n            auto *out = args[args.size() - 2];\n\n            if (auto *whereConst = cast<BoolConst>(where)) {\n              if (!whereConst->getVal())\n                break;\n            } else {\n              break;\n            }\n\n            if (!isNoneType(out->getType(), T))\n              break;\n\n            auto op = u.op;\n            auto lhs = parse(args[1], leaves, T);\n            if (!lhs)\n              return {};\n\n            if (u.args == 1)\n              return std::make_unique<NumPyExpr>(type, v, op, std::move(lhs));\n\n            auto rhs = parse(args[2], leaves, T);\n            if (!rhs)\n              return {};\n\n            return std::make_unique<NumPyExpr>(type, v, op, std::move(lhs),\n                                               std::move(rhs));\n          }\n        }\n      }\n    }\n\n    // Check for magic method call\n    if (f && isArrayType(f->getParentType())) {\n      for (auto &m : magics) {\n        if (f->getUnmangledName() == m.name && c->numArgs() == m.args) {\n          seqassertn(m.args == 1 || m.args == 2,\n                     \"unexpected number of arguments (magic)\");\n          std::vector<Value *> args(c->begin(), c->end());\n          auto op = m.op;\n          auto lhs = parse(args[0], leaves, T);\n          if (!lhs)\n            return {};\n\n          if (m.args == 1)\n            return std::make_unique<NumPyExpr>(type, v, op, std::move(lhs));\n\n          auto rhs = parse(args[1], leaves, T);\n          if (!rhs)\n            return {};\n\n          return m.right ? std::make_unique<NumPyExpr>(type, v, op, std::move(rhs),\n                                                       std::move(lhs))\n                         : std::make_unique<NumPyExpr>(type, v, op, std::move(lhs),\n                                                       std::move(rhs));\n        }\n      }\n    }\n  }\n\n  // Check for right-hand-side magic method call\n  // Right-hand-side magics (e.g. __radd__) are compiled into FlowInstr:\n  //   <lhs_expr> + <rhs_expr>\n  // becomes:\n  //   { v1 = <lhs expr> ; v2 = <rhs expr> ; return rhs_class.__radd__(v2, v1) }\n  // So we need to check for this to detect r-magics.\n  if (auto *flow = cast<FlowInstr>(v)) {\n    auto *series = cast<SeriesFlow>(flow->getFlow());\n    auto *value = cast<CallInstr>(flow->getValue());\n    auto *f = value ? util::getFunc(value->getCallee()) : nullptr;\n\n    if (series && f && value->numArgs() == 2) {\n      std::vector<Value *> assignments(series->begin(), series->end());\n      auto *arg1 = value->front();\n      auto *arg2 = value->back();\n      auto *vv1 = cast<VarValue>(arg1);\n      auto *vv2 = cast<VarValue>(arg2);\n      auto *arg1Var = vv1 ? vv1->getVar() : nullptr;\n      auto *arg2Var = vv2 ? vv2->getVar() : nullptr;\n\n      for (auto &m : magics) {\n        if (f->getUnmangledName() == m.name && value->numArgs() == m.args && m.right) {\n          auto op = m.op;\n\n          if (assignments.size() == 0) {\n            // Case 1: Degenerate flow instruction\n            return parse(value, leaves, T);\n          } else if (assignments.size() == 1) {\n            // Case 2: One var -- check if it's either of the r-magic operands\n            auto *a1 = cast<AssignInstr>(assignments.front());\n            if (a1 && a1->getLhs() == arg1Var) {\n              auto rhs = parse(a1->getRhs(), leaves, T);\n              if (!rhs)\n                return {};\n\n              auto lhs = parse(arg2, leaves, T);\n              if (!lhs)\n                return {};\n\n              return std::make_unique<NumPyExpr>(type, v, op, std::move(lhs),\n                                                 std::move(rhs));\n            } else if (a1 && a1->getLhs() == arg2Var) {\n              auto lhs = parse(a1->getRhs(), leaves, T);\n              if (!lhs)\n                return {};\n\n              auto rhs = parse(arg1, leaves, T);\n              if (!rhs)\n                return {};\n\n              return std::make_unique<NumPyExpr>(type, v, op, std::move(lhs),\n                                                 std::move(rhs));\n            }\n          } else if (assignments.size() == 2) {\n            // Case 2: Two vars -- check both permutations\n            auto *a1 = cast<AssignInstr>(assignments.front());\n            auto *a2 = cast<AssignInstr>(assignments.back());\n\n            if (a1 && a2 && a1->getLhs() == arg1Var && a2->getLhs() == arg2Var) {\n              auto rhs = parse(a1->getRhs(), leaves, T);\n              if (!rhs)\n                return {};\n\n              auto lhs = parse(a2->getRhs(), leaves, T);\n              if (!lhs)\n                return {};\n\n              return std::make_unique<NumPyExpr>(type, v, op, std::move(lhs),\n                                                 std::move(rhs));\n            } else if (a1 && a2 && a2->getLhs() == arg1Var && a1->getLhs() == arg2Var) {\n              auto lhs = parse(a1->getRhs(), leaves, T);\n              if (!lhs)\n                return {};\n\n              auto rhs = parse(a2->getRhs(), leaves, T);\n              if (!rhs)\n                return {};\n\n              return std::make_unique<NumPyExpr>(type, v, op, std::move(lhs),\n                                                 std::move(rhs));\n            }\n          }\n          break;\n        }\n      }\n    }\n  }\n\n  auto res = std::make_unique<NumPyExpr>(type, v);\n  leaves.emplace_back(res.get(), v);\n  return std::move(res);\n}\n\nnamespace {\nVar *optimizeHelper(NumPyOptimizationUnit &unit, NumPyExpr *expr, CodegenContext &C) {\n  auto *M = unit.value->getModule();\n  auto *series = C.series;\n\n  // Remove some operations that cannot be done element-wise easily by optimizing them\n  // separately, recursively.\n  expr->apply([&](NumPyExpr &e) {\n    if (!e.type.isArray())\n      return;\n\n    if (e.op == NumPyExpr::NP_OP_TRANSPOSE) {\n      auto *lv = optimizeHelper(unit, e.lhs.get(), C);\n      auto *transposeFunc =\n          M->getOrRealizeFunc(\"_transpose\", {lv->getType()}, {}, FUSION_MODULE);\n      seqassertn(transposeFunc, \"transpose func not found\");\n      auto *var = util::makeVar(util::call(transposeFunc, {M->Nr<VarValue>(lv)}),\n                                C.series, C.func);\n      C.vars[&e] = var;\n      NumPyExpr replacement(e.type, M->Nr<VarValue>(var));\n      replacement.freeable = e.lhs->freeable;\n      e.replace(replacement);\n    }\n\n    if (e.op == NumPyExpr::NP_OP_MATMUL) {\n      auto *lv = optimizeHelper(unit, e.lhs.get(), C);\n      auto *rv = optimizeHelper(unit, e.rhs.get(), C);\n      auto *matmulFunc = M->getOrRealizeFunc(\"_matmul\", {lv->getType(), rv->getType()},\n                                             {}, FUSION_MODULE);\n      seqassertn(matmulFunc, \"matmul func not found\");\n      auto *var = util::makeVar(\n          util::call(matmulFunc, {M->Nr<VarValue>(lv), M->Nr<VarValue>(rv)}), C.series,\n          C.func);\n      C.vars[&e] = var;\n      NumPyExpr replacement(e.type, M->Nr<VarValue>(var));\n      replacement.freeable = true;\n      e.replace(replacement);\n    }\n  });\n\n  // Optimize the given expression\n  bool changed;\n  do {\n    changed = false;\n    expr->apply([&](NumPyExpr &e) {\n      if (e.depth() <= 2)\n        return;\n\n      auto cost = e.cost();\n      auto bcinfo = e.getBroadcastInfo();\n      Var *result = nullptr;\n\n      if (cost <= AlwaysFuseCostThreshold ||\n          (cost <= NeverFuseCostThreshold && bcinfo == BroadcastInfo::NO)) {\n        // Don't care about broadcasting; just fuse.\n        XLOG(\"-> static fuse:\\n{}\", e.str());\n        result = e.codegenFusedEval(C);\n      } else if (cost <= NeverFuseCostThreshold && bcinfo != BroadcastInfo::YES) {\n        // Check at runtime if we're broadcasting and fuse conditionally.\n        XLOG(\"-> conditional fuse:\\n{}\", e.str());\n        auto *broadcasts = e.codegenBroadcasts(C);\n        auto *seqtSeries = M->Nr<SeriesFlow>();\n        auto *fuseSeries = M->Nr<SeriesFlow>();\n        auto *branch = M->Nr<IfFlow>(broadcasts, seqtSeries, fuseSeries);\n\n        C.series = seqtSeries;\n        auto *seqtResult = e.codegenSequentialEval(C);\n        C.series = fuseSeries;\n        auto *fuseResult = e.codegenFusedEval(C);\n        seqassertn(seqtResult->getType()->is(fuseResult->getType()),\n                   \"types are not the same: {} {}\", seqtResult->getType()->getName(),\n                   fuseResult->getType()->getName());\n\n        result = M->Nr<Var>(seqtResult->getType(), false);\n        unit.func->push_back(result);\n        seqtSeries->push_back(M->Nr<AssignInstr>(result, M->Nr<VarValue>(seqtResult)));\n        fuseSeries->push_back(M->Nr<AssignInstr>(result, M->Nr<VarValue>(fuseResult)));\n        C.series = series;\n        series->push_back(branch);\n      }\n\n      if (result) {\n        NumPyExpr tmp(e.type, M->Nr<VarValue>(result));\n        e.replace(tmp);\n        e.freeable = true;\n        C.vars[&e] = result;\n        changed = true;\n      }\n    });\n  } while (changed);\n\n  XLOG(\"-> sequential eval:\\n{}\", expr->str());\n  return expr->codegenSequentialEval(C);\n}\n} // namespace\n\nbool NumPyOptimizationUnit::optimize(NumPyPrimitiveTypes &T) {\n  if (!expr->type.isArray() || expr->depth() <= 2)\n    return false;\n\n  XLOG(\"Optimizing expression at {}\\n{}\", value->getSrcInfo(), expr->str());\n\n  auto *M = value->getModule();\n  auto *series = M->Nr<SeriesFlow>();\n  CodegenContext C(M, series, func, T);\n  util::CloneVisitor cv(M);\n\n  for (auto &p : leaves) {\n    auto *var = util::makeVar(cv.clone(p.second), series, func);\n    C.vars.emplace(p.first, var);\n  }\n\n  auto *result = optimizeHelper(*this, expr.get(), C);\n  auto *replacement = M->Nr<FlowInstr>(C.series, M->Nr<VarValue>(result));\n  value->replaceAll(replacement);\n  return true;\n}\n\nstruct ExtractArrayExpressions : public util::Operator {\n  BodiedFunc *func;\n  NumPyPrimitiveTypes types;\n  std::vector<NumPyOptimizationUnit> exprs;\n  std::unordered_set<id_t> extracted;\n\n  explicit ExtractArrayExpressions(BodiedFunc *func)\n      : util::Operator(), func(func), types(func->getModule()), exprs(), extracted() {}\n\n  void extract(Value *v, AssignInstr *assign = nullptr) {\n    if (extracted.count(v->getId()))\n      return;\n\n    std::vector<std::pair<NumPyExpr *, Value *>> leaves;\n    auto expr = parse(v, leaves, types);\n    if (expr) {\n      int64_t numArrayNodes = 0;\n      expr->apply([&](NumPyExpr &e) {\n        if (e.type.isArray())\n          ++numArrayNodes;\n        extracted.emplace(e.val->getId());\n      });\n      if (numArrayNodes > 0 && expr->depth() > 1) {\n        exprs.push_back({v, func, std::move(expr), std::move(leaves), assign});\n      }\n    }\n  }\n\n  void preHook(Node *n) override {\n    if (auto *v = cast<AssignInstr>(n)) {\n      extract(v->getRhs(), v->getLhs()->isGlobal() ? nullptr : v);\n    } else if (auto *v = cast<Value>(n)) {\n      extract(v);\n    }\n  }\n};\n\nconst std::string NumPyFusionPass::KEY = \"core-numpy-fusion\";\n\nvoid NumPyFusionPass::visit(BodiedFunc *func) {\n  ExtractArrayExpressions extractor(func);\n  func->accept(extractor);\n\n  if (extractor.exprs.empty())\n    return;\n\n  auto *rdres = getAnalysisResult<analyze::dataflow::RDResult>(reachingDefKey);\n  auto it = rdres->results.find(func->getId());\n  if (it == rdres->results.end())\n    return;\n  auto *rd = it->second.get();\n  auto *se = getAnalysisResult<analyze::module::SideEffectResult>(sideEffectsKey);\n  auto *cfg = rdres->cfgResult->graphs.find(func->getId())->second.get();\n  auto fwd = getForwardingDAGs(func, rd, cfg, se, extractor.exprs);\n\n  for (auto &dag : fwd) {\n    std::vector<AssignInstr *> assignsToDelete;\n    auto *e = doForwarding(dag, assignsToDelete);\n    if (e->optimize(extractor.types)) {\n      for (auto *a : assignsToDelete)\n        a->replaceAll(func->getModule()->Nr<SeriesFlow>());\n    }\n  }\n}\n\n} // namespace numpy\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/numpy/numpy.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/analyze/dataflow/reaching.h\"\n#include \"codon/cir/analyze/module/global_vars.h\"\n#include \"codon/cir/analyze/module/side_effect.h\"\n#include \"codon/cir/transform/pass.h\"\n#include \"codon/cir/types/types.h\"\n\n#include <functional>\n#include <memory>\n#include <vector>\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace numpy {\nextern const std::string FUSION_MODULE;\n\n/// NumPy operator fusion pass.\nclass NumPyFusionPass : public OperatorPass {\nprivate:\n  /// Key of the reaching definition analysis\n  std::string reachingDefKey;\n  /// Key of the side effect analysis\n  std::string sideEffectsKey;\n\npublic:\n  static const std::string KEY;\n\n  /// Constructs a NumPy fusion pass.\n  /// @param reachingDefKey the reaching definition analysis' key\n  /// @param sideEffectsKey side effect analysis' key\n  NumPyFusionPass(const std::string &reachingDefKey, const std::string &sideEffectsKey)\n      : OperatorPass(), reachingDefKey(reachingDefKey), sideEffectsKey(sideEffectsKey) {\n  }\n\n  std::string getKey() const override { return KEY; }\n  void visit(BodiedFunc *f) override;\n};\n\nstruct NumPyPrimitiveTypes {\n  types::Type *none;\n  types::Type *optnone;\n  types::Type *bool_;\n  types::Type *i8;\n  types::Type *u8;\n  types::Type *i16;\n  types::Type *u16;\n  types::Type *i32;\n  types::Type *u32;\n  types::Type *i64;\n  types::Type *u64;\n  types::Type *f16;\n  types::Type *f32;\n  types::Type *f64;\n  types::Type *c64;\n  types::Type *c128;\n\n  explicit NumPyPrimitiveTypes(Module *M);\n};\n\nstruct NumPyType {\n  enum Type {\n    NP_TYPE_NONE = -1,\n    NP_TYPE_BOOL,\n    NP_TYPE_I8,\n    NP_TYPE_U8,\n    NP_TYPE_I16,\n    NP_TYPE_U16,\n    NP_TYPE_I32,\n    NP_TYPE_U32,\n    NP_TYPE_I64,\n    NP_TYPE_U64,\n    NP_TYPE_F16,\n    NP_TYPE_F32,\n    NP_TYPE_F64,\n    NP_TYPE_C64,\n    NP_TYPE_C128,\n    NP_TYPE_SCALAR_END, // separator value\n    NP_TYPE_ARR_BOOL,\n    NP_TYPE_ARR_I8,\n    NP_TYPE_ARR_U8,\n    NP_TYPE_ARR_I16,\n    NP_TYPE_ARR_U16,\n    NP_TYPE_ARR_I32,\n    NP_TYPE_ARR_U32,\n    NP_TYPE_ARR_I64,\n    NP_TYPE_ARR_U64,\n    NP_TYPE_ARR_F16,\n    NP_TYPE_ARR_F32,\n    NP_TYPE_ARR_F64,\n    NP_TYPE_ARR_C64,\n    NP_TYPE_ARR_C128,\n  } dtype;\n  int64_t ndim;\n\n  NumPyType(Type dtype, int64_t ndim = 0);\n  NumPyType();\n\n  static NumPyType get(types::Type *t, NumPyPrimitiveTypes &T);\n\n  types::Type *getIRBaseType(NumPyPrimitiveTypes &T) const;\n\n  operator bool() const { return dtype != NP_TYPE_NONE; }\n  bool isArray() const { return dtype > NP_TYPE_SCALAR_END; }\n\n  friend std::ostream &operator<<(std::ostream &os, NumPyType const &type);\n\n  std::string str() const;\n};\n\nstruct NumPyExpr;\n\nstruct CodegenContext {\n  Module *M;\n  SeriesFlow *series;\n  BodiedFunc *func;\n  std::unordered_map<NumPyExpr *, Var *> vars;\n  NumPyPrimitiveTypes &T;\n\n  CodegenContext(Module *M, SeriesFlow *series, BodiedFunc *func,\n                 NumPyPrimitiveTypes &T);\n};\n\nenum BroadcastInfo {\n  UNKNOWN,\n  YES,\n  NO,\n  MAYBE,\n};\n\nstruct NumPyExpr {\n  NumPyType type;\n  Value *val;\n  enum Op {\n    NP_OP_NONE,\n    NP_OP_POS,\n    NP_OP_NEG,\n    NP_OP_INVERT,\n    NP_OP_ABS,\n    NP_OP_TRANSPOSE,\n    NP_OP_ADD,\n    NP_OP_SUB,\n    NP_OP_MUL,\n    NP_OP_MATMUL,\n    NP_OP_TRUE_DIV,\n    NP_OP_FLOOR_DIV,\n    NP_OP_MOD,\n    NP_OP_FMOD,\n    NP_OP_POW,\n    NP_OP_LSHIFT,\n    NP_OP_RSHIFT,\n    NP_OP_AND,\n    NP_OP_OR,\n    NP_OP_XOR,\n    NP_OP_LOGICAL_AND,\n    NP_OP_LOGICAL_OR,\n    NP_OP_LOGICAL_XOR,\n    NP_OP_EQ,\n    NP_OP_NE,\n    NP_OP_LT,\n    NP_OP_LE,\n    NP_OP_GT,\n    NP_OP_GE,\n    NP_OP_MIN,\n    NP_OP_MAX,\n    NP_OP_FMIN,\n    NP_OP_FMAX,\n    NP_OP_SIN,\n    NP_OP_COS,\n    NP_OP_TAN,\n    NP_OP_ARCSIN,\n    NP_OP_ARCCOS,\n    NP_OP_ARCTAN,\n    NP_OP_ARCTAN2,\n    NP_OP_HYPOT,\n    NP_OP_SINH,\n    NP_OP_COSH,\n    NP_OP_TANH,\n    NP_OP_ARCSINH,\n    NP_OP_ARCCOSH,\n    NP_OP_ARCTANH,\n    NP_OP_CONJ,\n    NP_OP_EXP,\n    NP_OP_EXP2,\n    NP_OP_LOG,\n    NP_OP_LOG2,\n    NP_OP_LOG10,\n    NP_OP_EXPM1,\n    NP_OP_LOG1P,\n    NP_OP_SQRT,\n    NP_OP_SQUARE,\n    NP_OP_CBRT,\n    NP_OP_LOGADDEXP,\n    NP_OP_LOGADDEXP2,\n    NP_OP_RECIPROCAL,\n    NP_OP_RINT,\n    NP_OP_FLOOR,\n    NP_OP_CEIL,\n    NP_OP_TRUNC,\n    NP_OP_ISNAN,\n    NP_OP_ISINF,\n    NP_OP_ISFINITE,\n    NP_OP_SIGN,\n    NP_OP_SIGNBIT,\n    NP_OP_COPYSIGN,\n    NP_OP_SPACING,\n    NP_OP_NEXTAFTER,\n    NP_OP_DEG2RAD,\n    NP_OP_RAD2DEG,\n    NP_OP_HEAVISIDE,\n  } op;\n  std::unique_ptr<NumPyExpr> lhs;\n  std::unique_ptr<NumPyExpr> rhs;\n  bool freeable;\n\n  NumPyExpr(NumPyType type, Value *val)\n      : type(std::move(type)), val(val), op(NP_OP_NONE), lhs(), rhs(), freeable(false) {\n  }\n  NumPyExpr(NumPyType type, Value *val, NumPyExpr::Op op,\n            std::unique_ptr<NumPyExpr> lhs)\n      : type(std::move(type)), val(val), op(op), lhs(std::move(lhs)), rhs(),\n        freeable(false) {}\n  NumPyExpr(NumPyType type, Value *val, NumPyExpr::Op op,\n            std::unique_ptr<NumPyExpr> lhs, std::unique_ptr<NumPyExpr> rhs)\n      : type(std::move(type)), val(val), op(op), lhs(std::move(lhs)),\n        rhs(std::move(rhs)), freeable(false) {}\n\n  static std::unique_ptr<NumPyExpr>\n  parse(Value *v, std::vector<std::pair<NumPyExpr *, Value *>> &leaves,\n        NumPyPrimitiveTypes &T);\n\n  void replace(NumPyExpr &e);\n  bool haveVectorizedLoop() const;\n\n  int64_t opcost() const;\n  int64_t cost() const;\n\n  std::string opstring() const;\n  void dump(std::ostream &os, int level, int &leafId) const;\n  friend std::ostream &operator<<(std::ostream &os, NumPyExpr const &expr);\n  std::string str() const;\n\n  bool isLeaf() const { return !lhs && !rhs; }\n\n  int depth() const {\n    return std::max(lhs ? lhs->depth() : 0, rhs ? rhs->depth() : 0) + 1;\n  }\n\n  int nodes() const { return (lhs ? lhs->nodes() : 0) + (rhs ? rhs->nodes() : 0) + 1; }\n\n  void apply(std::function<void(NumPyExpr &)> f);\n\n  Value *codegenBroadcasts(CodegenContext &C);\n\n  Var *codegenFusedEval(CodegenContext &C);\n\n  Var *codegenSequentialEval(CodegenContext &C);\n\n  BroadcastInfo getBroadcastInfo();\n\n  Value *codegenScalarExpr(CodegenContext &C,\n                           const std::unordered_map<NumPyExpr *, Var *> &args,\n                           const std::unordered_map<NumPyExpr *, unsigned> &scalarMap,\n                           Var *scalars);\n};\n\nstd::unique_ptr<NumPyExpr> parse(Value *v,\n                                 std::vector<std::pair<NumPyExpr *, Value *>> &leaves,\n                                 NumPyPrimitiveTypes &T);\n\nstruct NumPyOptimizationUnit {\n  /// Original IR value being corresponding to expression\n  Value *value;\n  /// Function in which the value exists\n  BodiedFunc *func;\n  /// Root expression\n  std::unique_ptr<NumPyExpr> expr;\n  /// Leaves ordered by execution in original expression\n  std::vector<std::pair<NumPyExpr *, Value *>> leaves;\n  /// AssignInstr in which RHS is represented by this expression, or null if none\n  AssignInstr *assign;\n\n  bool optimize(NumPyPrimitiveTypes &T);\n};\n\nstruct Forwarding {\n  NumPyOptimizationUnit *dst;\n  NumPyOptimizationUnit *src;\n  Var *var;\n  NumPyExpr *dstLeaf;\n  int64_t dstId;\n  int64_t srcId;\n};\n\nusing ForwardingDAG =\n    std::unordered_map<NumPyOptimizationUnit *, std::vector<Forwarding>>;\n\nNumPyOptimizationUnit *doForwarding(ForwardingDAG &dag,\n                                    std::vector<AssignInstr *> &assignsToDelete);\n\nstd::vector<ForwardingDAG> getForwardingDAGs(BodiedFunc *func,\n                                             analyze::dataflow::RDInspector *rd,\n                                             analyze::dataflow::CFGraph *cfg,\n                                             analyze::module::SideEffectResult *se,\n                                             std::vector<NumPyOptimizationUnit> &exprs);\n\n} // namespace numpy\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/parallel/openmp.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"openmp.h\"\n\n#include <algorithm>\n#include <iterator>\n#include <limits>\n#include <unordered_set>\n\n#include \"codon/cir/transform/parallel/schedule.h\"\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/cir/util/outlining.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace parallel {\nnamespace {\nconst std::string ompModule = \"std.openmp\";\nconst std::string gpuModule = \"std.internal.gpu\";\nconst std::string builtinModule = \"std.internal.builtin\";\n\nvoid warn(const std::string &msg, const Value *v) {\n  auto src = v->getSrcInfo();\n  compilationWarning(msg, src.file, src.line, src.col);\n}\n\nstruct OMPTypes {\n  types::Type *i64 = nullptr;\n  types::Type *i32 = nullptr;\n  types::Type *i8ptr = nullptr;\n  types::Type *i32ptr = nullptr;\n\n  explicit OMPTypes(Module *M) {\n    i64 = M->getIntType();\n    i32 = M->getIntNType(32, /*sign=*/true);\n    i8ptr = M->getPointerType(M->getByteType());\n    i32ptr = M->getPointerType(i32);\n  }\n};\n\nVar *getVarFromOutlinedArg(Value *arg) {\n  if (auto *val = cast<VarValue>(arg)) {\n    return val->getVar();\n  } else if (auto *val = cast<PointerValue>(arg)) {\n    return val->getVar();\n  } else {\n    seqassertn(false, \"unknown outline var\");\n  }\n  return nullptr;\n}\n\nValue *ptrFromFunc(Func *func) {\n  auto *M = func->getModule();\n  auto *funcType = func->getType();\n  auto *rawMethod = M->getOrRealizeMethod(funcType, \"__raw__\", {funcType});\n  seqassertn(rawMethod, \"cannot find function __raw__ method\");\n  return util::call(rawMethod, {M->Nr<VarValue>(func)});\n}\n\n// we create the locks lazily to avoid them when they're not needed\nstruct ReductionLocks {\n  Var *mainLock =\n      nullptr; // lock used in calls to _reduce_no_wait and _end_reduce_no_wait\n  Var *critLock = nullptr; // lock used in reduction critical sections\n\n  Var *createLock(Module *M) {\n    auto *lockType = M->getOrRealizeType(ast::getMangledClass(ompModule, \"Lock\"));\n    seqassertn(lockType, \"openmp.Lock type not found\");\n    auto *var = M->Nr<Var>(lockType, /*global=*/true);\n    static int counter = 1;\n    var->setName(\".omp_lock.\" + std::to_string(counter++));\n\n    // add it to main function so it doesn't get demoted by IR pass\n    auto *series = cast<SeriesFlow>(cast<BodiedFunc>(M->getMainFunc())->getBody());\n    auto *init = (*lockType)();\n    seqassertn(init, \"could not initialize openmp.Lock\");\n    series->insert(series->begin(), M->Nr<AssignInstr>(var, init));\n\n    return var;\n  }\n\n  Var *getMainLock(Module *M) {\n    if (!mainLock)\n      mainLock = createLock(M);\n    return mainLock;\n  }\n\n  Var *getCritLock(Module *M) {\n    if (!critLock)\n      critLock = createLock(M);\n    return critLock;\n  }\n};\n\nstruct Reduction {\n  enum Kind {\n    NONE,\n    ADD,\n    MUL,\n    AND,\n    OR,\n    XOR,\n    MIN,\n    MAX,\n  };\n\n  Kind kind = Kind::NONE;\n  Var *shared = nullptr;\n\n  types::Type *getType() {\n    auto *ptrType = cast<types::PointerType>(shared->getType());\n    seqassertn(ptrType, \"expected shared var to be of pointer type\");\n    return ptrType->getBase();\n  }\n\n  Value *getInitial() {\n    if (!*this)\n      return nullptr;\n    auto *M = shared->getModule();\n    auto *type = getType();\n\n    if (isA<types::IntType>(type)) {\n      switch (kind) {\n      case Kind::ADD:\n        return M->getInt(0);\n      case Kind::MUL:\n        return M->getInt(1);\n      case Kind::AND:\n        return M->getInt(~0);\n      case Kind::OR:\n        return M->getInt(0);\n      case Kind::XOR:\n        return M->getInt(0);\n      case Kind::MIN:\n        return M->getInt(std::numeric_limits<int64_t>::max());\n      case Kind::MAX:\n        return M->getInt(std::numeric_limits<int64_t>::min());\n      default:\n        return nullptr;\n      }\n    } else if (isA<types::FloatType>(type)) {\n      switch (kind) {\n      case Kind::ADD:\n        return M->getFloat(0.);\n      case Kind::MUL:\n        return M->getFloat(1.);\n      case Kind::MIN:\n        return M->getFloat(std::numeric_limits<double>::max());\n      case Kind::MAX:\n        return M->getFloat(std::numeric_limits<double>::min());\n      default:\n        return nullptr;\n      }\n    } else if (isA<types::Float32Type>(type)) {\n      auto *f32 = M->getOrRealizeType(\"float32\");\n      float value = 0.0;\n\n      switch (kind) {\n      case Kind::ADD:\n        value = 0.0;\n        break;\n      case Kind::MUL:\n        value = 1.0;\n        break;\n      case Kind::MIN:\n        value = std::numeric_limits<float>::max();\n        break;\n      case Kind::MAX:\n        value = std::numeric_limits<float>::min();\n        break;\n      default:\n        return nullptr;\n      }\n\n      return (*f32)(*M->getFloat(value));\n    }\n\n    auto *init = (*type)();\n    if (!init || !init->getType()->is(type))\n      return nullptr;\n    return init;\n  }\n\n  Value *generateNonAtomicReduction(Value *ptr, Value *arg) {\n    auto *M = ptr->getModule();\n    Value *lhs = util::ptrLoad(ptr);\n    Value *result = nullptr;\n    switch (kind) {\n    case Kind::ADD:\n      result = *lhs + *arg;\n      break;\n    case Kind::MUL:\n      result = *lhs * *arg;\n      break;\n    case Kind::AND:\n      result = *lhs & *arg;\n      break;\n    case Kind::OR:\n      result = *lhs | *arg;\n      break;\n    case Kind::XOR:\n      result = *lhs ^ *arg;\n      break;\n    case Kind::MIN:\n    case Kind::MAX: {\n      // signature is (tuple of args, key, default)\n      auto name = (kind == Kind::MIN ? \"min\" : \"max\");\n      auto *tup = util::makeTuple({lhs, arg});\n      auto *none = (*M->getNoneType())();\n      auto *fn = M->getOrRealizeFunc(\n          name, {tup->getType(), none->getType(), none->getType()}, {}, builtinModule);\n      seqassertn(fn, \"{} function not found\", name);\n      result = util::call(fn, {tup, none, none});\n      break;\n    }\n    default:\n      return nullptr;\n    }\n    return util::ptrStore(ptr, result);\n  }\n\n  Value *generateAtomicReduction(Value *ptr, Value *arg, Var *loc, Var *gtid,\n                                 ReductionLocks &locks) {\n    auto *M = ptr->getModule();\n    auto *type = getType();\n    std::string func = \"\";\n\n    if (isA<types::IntType>(type)) {\n      switch (kind) {\n      case Kind::ADD:\n        func = \"_atomic_int_add\";\n        break;\n      case Kind::MUL:\n        func = \"_atomic_int_mul\";\n        break;\n      case Kind::AND:\n        func = \"_atomic_int_and\";\n        break;\n      case Kind::OR:\n        func = \"_atomic_int_or\";\n        break;\n      case Kind::XOR:\n        func = \"_atomic_int_xor\";\n        break;\n      case Kind::MIN:\n        func = \"_atomic_int_min\";\n        break;\n      case Kind::MAX:\n        func = \"_atomic_int_max\";\n        break;\n      default:\n        break;\n      }\n    } else if (isA<types::FloatType>(type)) {\n      switch (kind) {\n      case Kind::ADD:\n        func = \"_atomic_float_add\";\n        break;\n      case Kind::MUL:\n        func = \"_atomic_float_mul\";\n        break;\n      case Kind::MIN:\n        func = \"_atomic_float_min\";\n        break;\n      case Kind::MAX:\n        func = \"_atomic_float_max\";\n        break;\n      default:\n        break;\n      }\n    } else if (isA<types::Float32Type>(type)) {\n      switch (kind) {\n      case Kind::ADD:\n        func = \"_atomic_float32_add\";\n        break;\n      case Kind::MUL:\n        func = \"_atomic_float32_mul\";\n        break;\n      case Kind::MIN:\n        func = \"_atomic_float32_min\";\n        break;\n      case Kind::MAX:\n        func = \"_atomic_float32_max\";\n        break;\n      default:\n        break;\n      }\n    }\n\n    if (!func.empty()) {\n      auto *atomicOp =\n          M->getOrRealizeFunc(func, {ptr->getType(), arg->getType()}, {}, ompModule);\n      seqassertn(atomicOp, \"atomic op '{}' not found\", func);\n      return util::call(atomicOp, {ptr, arg});\n    }\n\n    switch (kind) {\n    case Kind::ADD:\n      func = \"__atomic_add__\";\n      break;\n    case Kind::MUL:\n      func = \"__atomic_mul__\";\n      break;\n    case Kind::AND:\n      func = \"__atomic_and__\";\n      break;\n    case Kind::OR:\n      func = \"__atomic_or__\";\n      break;\n    case Kind::XOR:\n      func = \"__atomic_xor__\";\n      break;\n    case Kind::MIN:\n      func = \"__atomic_min__\";\n      break;\n    case Kind::MAX:\n      func = \"__atomic_max__\";\n      break;\n    default:\n      break;\n    }\n\n    if (!func.empty()) {\n      auto *atomicOp =\n          M->getOrRealizeMethod(arg->getType(), func, {ptr->getType(), arg->getType()});\n      if (atomicOp)\n        return util::call(atomicOp, {ptr, arg});\n    }\n\n    seqassertn(loc && gtid, \"loc and/or gtid are null\");\n    auto *lck = locks.getCritLock(M);\n    auto *lckPtrType = M->getPointerType(lck->getType());\n    auto *critBegin = M->getOrRealizeFunc(\"_critical_begin\",\n                                          {loc->getType(), gtid->getType(), lckPtrType},\n                                          {}, ompModule);\n    seqassertn(critBegin, \"critical begin function not found\");\n    auto *critEnd = M->getOrRealizeFunc(\n        \"_critical_end\", {loc->getType(), gtid->getType(), lckPtrType}, {}, ompModule);\n    seqassertn(critEnd, \"critical end function not found\");\n\n    auto *critEnter =\n        util::call(critBegin, {M->Nr<VarValue>(loc), M->Nr<VarValue>(gtid),\n                               M->Nr<PointerValue>(lck)});\n    auto *operation = generateNonAtomicReduction(ptr, arg);\n    auto *critExit = util::call(critEnd, {M->Nr<VarValue>(loc), M->Nr<VarValue>(gtid),\n                                          M->Nr<PointerValue>(lck)});\n    // make sure the unlock is in a finally-block\n    return util::series(critEnter, M->Nr<TryCatchFlow>(util::series(operation),\n                                                       util::series(critExit)));\n  }\n\n  operator bool() const { return kind != Kind::NONE; }\n};\n\nstruct ReductionFunction {\n  std::string name;\n  Reduction::Kind kind;\n  bool method;\n};\n\nstruct ReductionIdentifier : public util::Operator {\n  std::vector<Var *> shareds;\n  Var *loopVarArg;\n  std::unordered_map<id_t, Reduction> reductions;\n\n  ReductionIdentifier()\n      : util::Operator(), shareds(), loopVarArg(nullptr), reductions() {}\n\n  ReductionIdentifier(std::vector<Var *> shareds, Var *loopVarArg)\n      : util::Operator(), shareds(std::move(shareds)), loopVarArg(loopVarArg),\n        reductions() {}\n\n  bool isShared(Var *shared) {\n    if (loopVarArg && shared->getId() == loopVarArg->getId())\n      return false;\n    for (auto *v : shareds) {\n      if (shared->getId() == v->getId())\n        return true;\n    }\n    return false;\n  }\n\n  bool isSharedDeref(Var *shared, Value *v) {\n    auto *M = v->getModule();\n    auto *ptrType = cast<types::PointerType>(shared->getType());\n    seqassertn(ptrType, \"expected shared var to be of pointer type\");\n    auto *type = ptrType->getBase();\n\n    if (util::isCallOf(v, Module::GETITEM_MAGIC_NAME, {ptrType, M->getIntType()}, type,\n                       /*method=*/true)) {\n      auto *call = cast<CallInstr>(v);\n      auto *var = util::getVar(call->front());\n      return util::isConst<int64_t>(call->back(), 0) && var &&\n             var->getId() == shared->getId();\n    }\n\n    return false;\n  }\n\n  static void extractAssociativeOpChain(Value *v, const std::string &op,\n                                        types::Type *type,\n                                        std::vector<Value *> &result) {\n    if (util::isCallOf(v, op, {type, nullptr}, type, /*method=*/true) ||\n        util::isCallOf(v, op, {nullptr, type}, type, /*method=*/true)) {\n      auto *call = cast<CallInstr>(v);\n      extractAssociativeOpChain(call->front(), op, type, result);\n      extractAssociativeOpChain(call->back(), op, type, result);\n    } else {\n      result.push_back(v);\n    }\n  }\n\n  Reduction getReductionFromCall(CallInstr *v) {\n    auto *M = v->getModule();\n    auto *func = util::getFunc(v->getCallee());\n    if (v->numArgs() != 3 || !func ||\n        func->getUnmangledName() != Module::SETITEM_MAGIC_NAME)\n      return {};\n\n    std::vector<Value *> args(v->begin(), v->end());\n    Value *self = args[0];\n    Value *idx = args[1];\n    Value *item = args[2];\n\n    Var *shared = util::getVar(self);\n    if (!shared || !isShared(shared) || !util::isConst<int64_t>(idx, 0))\n      return {};\n\n    auto *ptrType = cast<types::PointerType>(shared->getType());\n    seqassertn(ptrType, \"expected shared var to be of pointer type\");\n    auto *type = ptrType->getBase();\n    auto *noneType = M->getOptionalType(M->getNoneType());\n\n    // double-check the call\n    if (!util::isCallOf(v, Module::SETITEM_MAGIC_NAME,\n                        {self->getType(), idx->getType(), item->getType()},\n                        M->getNoneType(), /*method=*/true))\n      return {};\n\n    const std::vector<ReductionFunction> reductionFunctions = {\n        {Module::ADD_MAGIC_NAME, Reduction::Kind::ADD, true},\n        {Module::MUL_MAGIC_NAME, Reduction::Kind::MUL, true},\n        {Module::AND_MAGIC_NAME, Reduction::Kind::AND, true},\n        {Module::OR_MAGIC_NAME, Reduction::Kind::OR, true},\n        {Module::XOR_MAGIC_NAME, Reduction::Kind::XOR, true},\n        {\"min\", Reduction::Kind::MIN, false},\n        {\"max\", Reduction::Kind::MAX, false},\n    };\n\n    for (auto &rf : reductionFunctions) {\n      if (rf.method) {\n        if (!(util::isCallOf(item, rf.name, {type, nullptr}, type, /*method=*/true) ||\n              util::isCallOf(item, rf.name, {nullptr, type}, type, /*method=*/true)))\n          continue;\n      } else {\n        if (!util::isCallOf(item, rf.name,\n                            {M->getTupleType({type, type}), noneType, noneType}, type,\n                            /*method=*/false))\n          continue;\n      }\n\n      auto *callRHS = cast<CallInstr>(item);\n      Value *deref = nullptr;\n\n      if (rf.method) {\n        std::vector<Value *> opChain;\n        extractAssociativeOpChain(callRHS, rf.name, type, opChain);\n        if (opChain.size() < 2)\n          continue;\n\n        for (auto *val : opChain) {\n          if (isSharedDeref(shared, val)) {\n            deref = val;\n            break;\n          }\n        }\n      } else {\n        callRHS = cast<CallInstr>(callRHS->front()); // this will be Tuple.__new__\n        if (!callRHS)\n          continue;\n\n        for (auto *val : *callRHS) {\n          if (isSharedDeref(shared, val)) {\n            deref = val;\n            break;\n          }\n        }\n      }\n\n      if (!deref)\n        return {};\n\n      Reduction reduction = {rf.kind, shared};\n      if (!reduction.getInitial())\n        return {};\n\n      return reduction;\n    }\n\n    return {};\n  }\n\n  Reduction getReduction(Var *shared) {\n    auto it = reductions.find(shared->getId());\n    return (it != reductions.end()) ? it->second : Reduction();\n  }\n\n  void handle(CallInstr *v) override {\n    if (auto reduction = getReductionFromCall(v)) {\n      auto it = reductions.find(reduction.shared->getId());\n      // if we've seen the var before, make sure it's consistent\n      // otherwise mark as invalid via an empty reduction\n      if (it == reductions.end()) {\n        reductions.emplace(reduction.shared->getId(), reduction);\n      } else if (it->second && it->second.kind != reduction.kind) {\n        it->second = {};\n      }\n    }\n  }\n};\n\nstruct SharedInfo {\n  unsigned memb;       // member index in template's `extra` arg\n  Var *local;          // the local var we create to store current value\n  Reduction reduction; // the reduction we're performing, or empty if none\n};\n\nstruct LoopTemplateReplacer : public util::Operator {\n  BodiedFunc *parent;\n  CallInstr *replacement;\n  Var *loopVar;\n\n  LoopTemplateReplacer(BodiedFunc *parent, CallInstr *replacement, Var *loopVar)\n      : util::Operator(), parent(parent), replacement(replacement), loopVar(loopVar) {}\n};\n\nstruct ParallelLoopTemplateReplacer : public LoopTemplateReplacer {\n  ReductionIdentifier *reds;\n  std::vector<SharedInfo> sharedInfo;\n  ReductionLocks locks;\n  Var *locRef;\n  Var *reductionLocRef;\n  Var *gtid;\n\n  ParallelLoopTemplateReplacer(BodiedFunc *parent, CallInstr *replacement, Var *loopVar,\n                               ReductionIdentifier *reds)\n      : LoopTemplateReplacer(parent, replacement, loopVar), reds(reds), sharedInfo(),\n        locks(), locRef(nullptr), reductionLocRef(nullptr), gtid(nullptr) {}\n\n  unsigned numReductions() {\n    unsigned num = 0;\n    for (auto &info : sharedInfo) {\n      if (info.reduction)\n        num += 1;\n    }\n    return num;\n  }\n\n  Value *getReductionTuple() {\n    auto *M = parent->getModule();\n    std::vector<Value *> elements;\n    for (auto &info : sharedInfo) {\n      if (info.reduction)\n        elements.push_back(M->Nr<PointerValue>(info.local));\n    }\n    return util::makeTuple(elements, M);\n  }\n\n  BodiedFunc *makeReductionFunc() {\n    auto *M = parent->getModule();\n    auto *tupleType = getReductionTuple()->getType();\n    auto *argType = M->getPointerType(tupleType);\n    auto *funcType = M->getFuncType(M->getNoneType(), {argType, argType});\n    auto *reducer = M->Nr<BodiedFunc>(\"__omp_reducer\");\n    reducer->realize(funcType, {\"lhs\", \"rhs\"});\n\n    auto *lhsVar = reducer->arg_front();\n    auto *rhsVar = reducer->arg_back();\n    auto *body = M->Nr<SeriesFlow>();\n    unsigned next = 0;\n    for (auto &info : sharedInfo) {\n      if (info.reduction) {\n        auto *lhs = util::ptrLoad(M->Nr<VarValue>(lhsVar));\n        auto *rhs = util::ptrLoad(M->Nr<VarValue>(rhsVar));\n        auto *lhsElem = util::tupleGet(lhs, next);\n        auto *rhsElem = util::tupleGet(rhs, next);\n        body->push_back(\n            info.reduction.generateNonAtomicReduction(lhsElem, util::ptrLoad(rhsElem)));\n        ++next;\n      }\n    }\n    reducer->setBody(body);\n    return reducer;\n  }\n\n  void handle(CallInstr *v) override {\n    auto *M = v->getModule();\n    auto *func = util::getFunc(v->getCallee());\n    if (!func)\n      return;\n    auto name = func->getUnmangledName();\n\n    if (name == \"_loop_loc_and_gtid\") {\n      seqassertn(v->numArgs() == 3 &&\n                     std::all_of(v->begin(), v->end(),\n                                 [](auto x) { return isA<VarValue>(x); }),\n                 \"unexpected loop loc and gtid stub\");\n      std::vector<Value *> args(v->begin(), v->end());\n      locRef = util::getVar(args[0]);\n      reductionLocRef = util::getVar(args[1]);\n      gtid = util::getVar(args[2]);\n    }\n\n    if (name == \"_loop_reductions\") {\n      seqassertn(reductionLocRef && gtid, \"bad visit order in template\");\n      seqassertn(v->numArgs() == 1 && isA<VarValue>(v->front()),\n                 \"unexpected shared updates stub\");\n      if (numReductions() == 0)\n        return;\n\n      auto *M = parent->getModule();\n      auto *extras = util::getVar(v->front());\n      auto *reductionTuple = getReductionTuple();\n      auto *reducer = makeReductionFunc();\n      auto *lck = locks.getMainLock(M);\n      auto *rawReducer = ptrFromFunc(reducer);\n\n      auto *lckPtrType = M->getPointerType(lck->getType());\n      auto *reduceNoWait = M->getOrRealizeFunc(\n          \"_reduce_nowait\",\n          {reductionLocRef->getType(), gtid->getType(), reductionTuple->getType(),\n           rawReducer->getType(), lckPtrType},\n          {}, ompModule);\n      seqassertn(reduceNoWait, \"reduce nowait function not found\");\n      auto *reduceNoWaitEnd = M->getOrRealizeFunc(\n          \"_end_reduce_nowait\",\n          {reductionLocRef->getType(), gtid->getType(), lckPtrType}, {}, ompModule);\n      seqassertn(reduceNoWaitEnd, \"end reduce nowait function not found\");\n\n      auto *series = M->Nr<SeriesFlow>();\n      auto *tupleVal = util::makeVar(reductionTuple, series, parent);\n      auto *reduceCode =\n          util::call(reduceNoWait,\n                     {M->Nr<VarValue>(reductionLocRef), M->Nr<VarValue>(gtid),\n                      M->Nr<VarValue>(tupleVal), rawReducer, M->Nr<PointerValue>(lck)});\n      auto *codeVar = util::makeVar(reduceCode, series, parent);\n      seqassertn(codeVar->getType()->is(M->getIntType()), \"wrong reduce code type\");\n\n      auto *sectionNonAtomic = M->Nr<SeriesFlow>();\n      auto *sectionAtomic = M->Nr<SeriesFlow>();\n\n      for (auto &info : sharedInfo) {\n        if (info.reduction) {\n          Value *ptr = util::tupleGet(M->Nr<VarValue>(extras), info.memb);\n          Value *arg = M->Nr<VarValue>(info.local);\n          sectionNonAtomic->push_back(\n              info.reduction.generateNonAtomicReduction(ptr, arg));\n        }\n      }\n      sectionNonAtomic->push_back(util::call(\n          reduceNoWaitEnd, {M->Nr<VarValue>(reductionLocRef), M->Nr<VarValue>(gtid),\n                            M->Nr<PointerValue>(lck)}));\n\n      for (auto &info : sharedInfo) {\n        if (info.reduction) {\n          Value *ptr = util::tupleGet(M->Nr<VarValue>(extras), info.memb);\n          Value *arg = M->Nr<VarValue>(info.local);\n          sectionAtomic->push_back(\n              info.reduction.generateAtomicReduction(ptr, arg, locRef, gtid, locks));\n        }\n      }\n\n      // make: if code == 1 { sectionNonAtomic } elif code == 2 { sectionAtomic }\n      auto *theSwitch = M->Nr<IfFlow>(\n          *M->Nr<VarValue>(codeVar) == *M->getInt(1), sectionNonAtomic,\n          util::series(M->Nr<IfFlow>(*M->Nr<VarValue>(codeVar) == *M->getInt(2),\n                                     sectionAtomic)));\n      series->push_back(theSwitch);\n      v->replaceAll(series);\n    }\n  }\n};\n\nstruct ImperativeLoopTemplateReplacer : public ParallelLoopTemplateReplacer {\n  OMPSched *sched;\n  int64_t step;\n\n  ImperativeLoopTemplateReplacer(BodiedFunc *parent, CallInstr *replacement,\n                                 Var *loopVar, ReductionIdentifier *reds,\n                                 OMPSched *sched, int64_t step)\n      : ParallelLoopTemplateReplacer(parent, replacement, loopVar, reds), sched(sched),\n        step(step) {}\n\n  void handle(CallInstr *v) override {\n    ParallelLoopTemplateReplacer::handle(v);\n    auto *M = v->getModule();\n    auto *func = util::getFunc(v->getCallee());\n    if (!func)\n      return;\n    auto name = func->getUnmangledName();\n\n    if (name == \"_loop_step\") {\n      v->replaceAll(M->getInt(step));\n    }\n\n    if (name == \"_loop_body_stub\") {\n      seqassertn(replacement, \"unexpected double replacement\");\n      seqassertn(v->numArgs() == 2 && isA<VarValue>(v->front()) &&\n                     isA<VarValue>(v->back()),\n                 \"unexpected loop body stub\");\n\n      auto *outlinedFunc = util::getFunc(replacement->getCallee());\n\n      // the template passes the new loop var and extra args\n      // to the body stub for convenience\n      auto *newLoopVar = util::getVar(v->front());\n      auto *extras = util::getVar(v->back());\n\n      std::vector<Value *> newArgs;\n      auto outlinedArgs = outlinedFunc->arg_begin(); // arg vars of *outlined func*\n      unsigned next = 0; // next index in \"extra\" args tuple, passed to template\n      // `arg` is an argument of the original outlined func call\n      for (auto *arg : *replacement) {\n        if (getVarFromOutlinedArg(arg)->getId() != loopVar->getId()) {\n          Value *newArg = nullptr;\n\n          // shared vars will be stored in a new var\n          if (isA<PointerValue>(arg)) {\n            types::Type *base = cast<types::PointerType>(arg->getType())->getBase();\n\n            // get extras again since we'll be inserting the new var before extras local\n            Var *lastArg = parent->arg_back(); // ptr to {chunk, start, stop, extras}\n            Value *val = util::tupleGet(util::ptrLoad(M->Nr<VarValue>(lastArg)), 3);\n            Value *initVal = util::ptrLoad(util::tupleGet(val, next));\n\n            Reduction reduction = reds->getReduction(*outlinedArgs);\n            if (reduction) {\n              initVal = reduction.getInitial();\n              seqassertn(initVal && initVal->getType()->is(base),\n                         \"unknown reduction init value\");\n            }\n\n            auto *newVar = util::makeVar(initVal, cast<SeriesFlow>(parent->getBody()),\n                                         parent, /*prepend=*/true);\n            sharedInfo.push_back({next, newVar, reduction});\n\n            newArg = M->Nr<PointerValue>(newVar);\n            ++next;\n          } else {\n            newArg = util::tupleGet(M->Nr<VarValue>(extras), next++);\n          }\n\n          newArgs.push_back(newArg);\n        } else {\n          if (isA<VarValue>(arg)) {\n            newArgs.push_back(M->Nr<VarValue>(newLoopVar));\n          } else if (isA<PointerValue>(arg)) {\n            newArgs.push_back(M->Nr<PointerValue>(newLoopVar));\n          } else {\n            seqassertn(false, \"unknown outline var\");\n          }\n        }\n\n        ++outlinedArgs;\n      }\n\n      v->replaceAll(util::call(outlinedFunc, newArgs));\n      replacement = nullptr;\n    }\n\n    if (name == \"_loop_shared_updates\") {\n      // for all non-reduction shareds, set the final values\n      // this will be similar to OpenMP's \"lastprivate\"\n      seqassertn(v->numArgs() == 1 && isA<VarValue>(v->front()),\n                 \"unexpected shared updates stub\");\n      auto *extras = util::getVar(v->front());\n      auto *series = M->Nr<SeriesFlow>();\n\n      for (auto &info : sharedInfo) {\n        if (info.reduction)\n          continue;\n\n        auto *finalValue = M->Nr<VarValue>(info.local);\n        auto *val = M->Nr<VarValue>(extras);\n        auto *origPtr = util::tupleGet(val, info.memb);\n        series->push_back(util::ptrStore(origPtr, finalValue));\n      }\n\n      v->replaceAll(series);\n    }\n\n    if (name == \"_loop_schedule\") {\n      v->replaceAll(M->getInt(sched->code));\n    }\n\n    if (name == \"_loop_ordered\") {\n      v->replaceAll(M->getBool(sched->ordered));\n    }\n  }\n};\n\nstruct TaskLoopReductionVarReplacer : public util::Operator {\n  std::vector<Var *> reductionArgs;\n  std::vector<std::pair<Var *, Var *>> reductionRemap;\n  BodiedFunc *parent;\n\n  void setupReductionRemap() {\n    auto *M = parent->getModule();\n\n    for (auto *var : reductionArgs) {\n      auto *newVar = M->Nr<Var>(var->getType(), /*global=*/false);\n      reductionRemap.emplace_back(var, newVar);\n    }\n  }\n\n  TaskLoopReductionVarReplacer(std::vector<Var *> reductionArgs, BodiedFunc *parent)\n      : util::Operator(), reductionArgs(std::move(reductionArgs)), reductionRemap(),\n        parent(parent) {\n    setupReductionRemap();\n  }\n\n  void preHook(Node *v) override {\n    for (auto &p : reductionRemap) {\n      v->replaceUsedVariable(p.first->getId(), p.second);\n    }\n  }\n\n  // need to do this as a separate step since otherwise the old variable\n  // in the assignment will be replaced, which we don't want\n  void finalize() {\n    auto *M = parent->getModule();\n    auto *body = cast<SeriesFlow>(parent->getBody());\n    auto *gtid = parent->arg_back();\n\n    for (auto &p : reductionRemap) {\n      auto *taskRedData = M->getOrRealizeFunc(\n          \"_taskred_data\", {M->getIntType(), p.first->getType()}, {}, ompModule);\n      seqassertn(taskRedData, \"could not find '_taskred_data'\");\n\n      auto *assign = M->Nr<AssignInstr>(\n          p.second,\n          util::call(taskRedData, {M->Nr<VarValue>(gtid), M->Nr<VarValue>(p.first)}));\n      body->insert(body->begin(), assign);\n      parent->push_back(p.second);\n    }\n  }\n};\n\nstruct TaskLoopBodyStubReplacer : public util::Operator {\n  CallInstr *replacement;\n  std::vector<bool> reduceArgs;\n\n  TaskLoopBodyStubReplacer(CallInstr *replacement, std::vector<bool> reduceArgs)\n      : util::Operator(), replacement(replacement), reduceArgs(std::move(reduceArgs)) {}\n\n  void handle(CallInstr *v) override {\n    auto *func = util::getFunc(v->getCallee());\n    if (func && func->getUnmangledName() == \"_task_loop_body_stub\") {\n      seqassertn(replacement, \"unexpected double replacement\");\n      seqassertn(v->numArgs() == 3 && isA<VarValue>(v->front()) &&\n                     isA<VarValue>(v->back()),\n                 \"unexpected loop body stub\");\n\n      // the template passes gtid, privs and shareds to the body stub for convenience\n      std::vector<Value *> args(v->begin(), v->end());\n      auto *gtid = args[0];\n      auto *privatesTuple = args[1];\n      auto *sharedsTuple = args[2];\n      unsigned privatesNext = 0;\n      unsigned sharedsNext = 0;\n      std::vector<Value *> newArgs;\n      bool hasReductions =\n          std::any_of(reduceArgs.begin(), reduceArgs.end(), [](bool b) { return b; });\n\n      for (auto *arg : *replacement) {\n        if (isA<VarValue>(arg)) {\n          newArgs.push_back(util::tupleGet(privatesTuple, privatesNext++));\n        } else if (isA<PointerValue>(arg)) {\n          newArgs.push_back(util::tupleGet(sharedsTuple, sharedsNext++));\n        } else {\n          // make sure we're on the last arg, which should be gtid\n          // in case of reductions\n          seqassertn(hasReductions && arg == replacement->back(),\n                     \"unknown outline var\");\n        }\n      }\n\n      auto *outlinedFunc = cast<BodiedFunc>(util::getFunc(replacement->getCallee()));\n\n      if (hasReductions) {\n        newArgs.push_back(gtid);\n\n        std::vector<Var *> reductionArgs;\n        unsigned i = 0;\n        for (auto it = outlinedFunc->arg_begin(); it != outlinedFunc->arg_end(); ++it) {\n          if (reduceArgs[i++])\n            reductionArgs.push_back(*it);\n        }\n        TaskLoopReductionVarReplacer redrep(reductionArgs, outlinedFunc);\n        outlinedFunc->accept(redrep);\n        redrep.finalize();\n      }\n\n      v->replaceAll(util::call(outlinedFunc, newArgs));\n      replacement = nullptr;\n    }\n  }\n};\n\nstruct TaskLoopRoutineStubReplacer : public ParallelLoopTemplateReplacer {\n  std::vector<Value *> privates;\n  std::vector<Value *> shareds;\n  Var *array;  // task reduction input array\n  Var *tskgrp; // task group identifier\n\n  void setupSharedInfo(std::vector<Reduction> &sharedRedux) {\n    unsigned sharedsNext = 0;\n    for (auto *val : shareds) {\n      if (getVarFromOutlinedArg(val)->getId() != loopVar->getId()) {\n        if (auto &reduction = sharedRedux[sharedsNext]) {\n          auto *newVar = util::makeVar(reduction.getInitial(),\n                                       cast<SeriesFlow>(parent->getBody()), parent,\n                                       /*prepend=*/true);\n          sharedInfo.push_back({sharedsNext, newVar, reduction});\n        }\n      }\n      ++sharedsNext;\n    }\n  }\n\n  TaskLoopRoutineStubReplacer(BodiedFunc *parent, CallInstr *replacement, Var *loopVar,\n                              ReductionIdentifier *reds, std::vector<Value *> privates,\n                              std::vector<Value *> shareds,\n                              std::vector<Reduction> sharedRedux)\n      : ParallelLoopTemplateReplacer(parent, replacement, loopVar, reds),\n        privates(std::move(privates)), shareds(std::move(shareds)), array(nullptr),\n        tskgrp(nullptr) {\n    setupSharedInfo(sharedRedux);\n  }\n\n  BodiedFunc *makeTaskRedInitFunc(Reduction *reduction) {\n    auto *M = parent->getModule();\n    auto *argType = M->getPointerType(reduction->getType());\n    auto *funcType = M->getFuncType(M->getNoneType(), {argType, argType});\n    auto *initializer = M->Nr<BodiedFunc>(\"__red_init\");\n    initializer->realize(funcType, {\"lhs\", \"rhs\"});\n\n    auto *lhsVar = initializer->arg_front();\n    auto *body = M->Nr<SeriesFlow>();\n    auto *lhsPtr = M->Nr<VarValue>(lhsVar);\n    body->push_back(util::ptrStore(lhsPtr, reduction->getInitial()));\n    initializer->setBody(body);\n    return initializer;\n  }\n\n  BodiedFunc *makeTaskRedCombFunc(Reduction *reduction) {\n    auto *M = parent->getModule();\n    auto *argType = M->getPointerType(reduction->getType());\n    auto *funcType = M->getFuncType(M->getNoneType(), {argType, argType});\n    auto *reducer = M->Nr<BodiedFunc>(\"__red_comb\");\n    reducer->realize(funcType, {\"lhs\", \"rhs\"});\n\n    auto *lhsVar = reducer->arg_front();\n    auto *rhsVar = reducer->arg_back();\n    auto *body = M->Nr<SeriesFlow>();\n    auto *lhsPtr = M->Nr<VarValue>(lhsVar);\n    auto *rhsPtr = M->Nr<VarValue>(rhsVar);\n    body->push_back(\n        reduction->generateNonAtomicReduction(lhsPtr, util::ptrLoad(rhsPtr)));\n    reducer->setBody(body);\n    return reducer;\n  }\n\n  Value *makeTaskRedInput(Reduction *reduction, Value *shar, Value *orig) {\n    auto *M = shar->getModule();\n    auto *size = M->Nr<TypePropertyInstr>(reduction->getType(),\n                                          TypePropertyInstr::Property::SIZEOF);\n    auto *init = ptrFromFunc(makeTaskRedInitFunc(reduction));\n    auto *comb = ptrFromFunc(makeTaskRedCombFunc(reduction));\n\n    auto *taskRedInputType =\n        M->getOrRealizeType(ast::getMangledClass(ompModule, \"TaskReductionInput\"));\n    seqassertn(taskRedInputType, \"could not find 'TaskReductionInput' type\");\n    auto *result = taskRedInputType->construct({shar, orig, size, init, comb});\n    seqassertn(result, \"bad construction of 'TaskReductionInput' type\");\n    return result;\n  }\n\n  void handle(VarValue *v) override {\n    auto *M = v->getModule();\n    auto *func = util::getFunc(v);\n\n    if (func && func->getUnmangledName() == \"_routine_stub\") {\n      std::vector<bool> reduceArgs;\n      unsigned sharedsNext = 0;\n      unsigned infoNext = 0;\n\n      for (auto *arg : *replacement) {\n        if (isA<VarValue>(arg)) {\n          reduceArgs.push_back(false);\n        } else if (isA<PointerValue>(arg)) {\n          if (infoNext < sharedInfo.size() &&\n              sharedInfo[infoNext].memb == sharedsNext &&\n              sharedInfo[infoNext].reduction) {\n            reduceArgs.push_back(true);\n            ++infoNext;\n          } else {\n            reduceArgs.push_back(false);\n          }\n          ++sharedsNext;\n        } else {\n          // make sure we're on the last arg, which should be gtid\n          // in case of reductions\n          seqassertn(numReductions() > 0 && arg == replacement->back(),\n                     \"unknown outline var\");\n          reduceArgs.push_back(false);\n        }\n      }\n\n      util::CloneVisitor cv(M);\n      auto *newRoutine = cv.forceClone(func);\n      TaskLoopBodyStubReplacer rep(replacement, reduceArgs);\n      newRoutine->accept(rep);\n      v->setVar(newRoutine);\n    }\n  }\n\n  void handle(CallInstr *v) override {\n    ParallelLoopTemplateReplacer::handle(v);\n    auto *M = v->getModule();\n    auto *func = util::getFunc(v->getCallee());\n    if (!func)\n      return;\n    auto name = func->getUnmangledName();\n\n    if (name == \"_taskred_setup\") {\n      seqassertn(reductionLocRef && gtid, \"bad visit order in template\");\n      seqassertn(v->numArgs() == 1 && isA<VarValue>(v->front()),\n                 \"unexpected shared updates stub\");\n      unsigned numRed = numReductions();\n      if (numRed == 0)\n        return;\n\n      auto *M = parent->getModule();\n      auto *extras = util::getVar(v->front());\n\n      // add task reduction inputs\n      auto *taskRedInitSeries = M->Nr<SeriesFlow>();\n      auto *taskRedInputType =\n          M->getOrRealizeType(ast::getMangledClass(ompModule, \"TaskReductionInput\"));\n      seqassertn(taskRedInputType, \"could not find 'TaskReductionInput' type\");\n      auto *irArrayType = M->getOrRealizeType(\n          ast::getMangledClass(ompModule, \"TaskReductionInputArray\"));\n      seqassertn(irArrayType, \"could not find 'TaskReductionInputArray' type\");\n      auto *taskRedInputsArray = util::makeVar(\n          M->Nr<StackAllocInstr>(irArrayType, numRed), taskRedInitSeries, parent);\n      array = taskRedInputsArray;\n      auto *taskRedInputsArrayType = taskRedInputsArray->getType();\n\n      auto *taskRedSetItem = M->getOrRealizeMethod(\n          taskRedInputsArrayType, Module::SETITEM_MAGIC_NAME,\n          {taskRedInputsArrayType, M->getIntType(), taskRedInputType});\n      seqassertn(taskRedSetItem,\n                 \"could not find 'TaskReductionInputArray.__setitem__' method\");\n      int i = 0;\n      for (auto &info : sharedInfo) {\n        if (info.reduction) {\n          Value *shar = M->Nr<PointerValue>(info.local);\n          Value *orig = util::tupleGet(M->Nr<VarValue>(extras), info.memb);\n          auto *taskRedInput = makeTaskRedInput(&info.reduction, shar, orig);\n          taskRedInitSeries->push_back(util::call(\n              taskRedSetItem, {M->Nr<VarValue>(array), M->getInt(i++), taskRedInput}));\n        }\n      }\n\n      auto *arrayPtr = M->Nr<ExtractInstr>(M->Nr<VarValue>(array), \"ptr\");\n      auto *taskRedInitFunc =\n          M->getOrRealizeFunc(\"_taskred_init\",\n                              {reductionLocRef->getType(), gtid->getType(),\n                               M->getIntType(), arrayPtr->getType()},\n                              {}, ompModule);\n      seqassertn(taskRedInitFunc, \"task red init function not found\");\n      auto *taskRedInitResult =\n          util::makeVar(util::call(taskRedInitFunc, {M->Nr<VarValue>(reductionLocRef),\n                                                     M->Nr<VarValue>(gtid),\n                                                     M->getInt(numRed), arrayPtr}),\n                        taskRedInitSeries, parent);\n      tskgrp = taskRedInitResult;\n      v->replaceAll(taskRedInitSeries);\n    }\n\n    if (name == \"_fix_privates_and_shareds\") {\n      std::vector<Value *> args(v->begin(), v->end());\n      seqassertn(args.size() == 3, \"invalid _fix_privates_and_shareds call found\");\n      unsigned numRed = numReductions();\n      auto *newLoopVar = args[0];\n      auto *privatesTuple = args[1];\n      auto *sharedsTuple = args[2];\n\n      unsigned privatesNext = 0;\n      unsigned sharedsNext = 0;\n      unsigned infoNext = 0;\n\n      bool needNewPrivates = false;\n      bool needNewShareds = false;\n\n      std::vector<Value *> newPrivates;\n      std::vector<Value *> newShareds;\n\n      for (auto *val : privates) {\n        if (numRed > 0 && val == privates.back()) { // i.e. task group identifier\n          seqassertn(tskgrp, \"tskgrp var not set\");\n          newPrivates.push_back(M->Nr<VarValue>(tskgrp));\n          needNewPrivates = true;\n        } else if (getVarFromOutlinedArg(val)->getId() != loopVar->getId()) {\n          newPrivates.push_back(util::tupleGet(privatesTuple, privatesNext));\n        } else {\n          newPrivates.push_back(newLoopVar);\n          needNewPrivates = true;\n        }\n        ++privatesNext;\n      }\n\n      for (auto *val : shareds) {\n        if (getVarFromOutlinedArg(val)->getId() != loopVar->getId()) {\n          if (infoNext < sharedInfo.size() &&\n              sharedInfo[infoNext].memb == sharedsNext &&\n              sharedInfo[infoNext].reduction) {\n            newShareds.push_back(M->Nr<PointerValue>(sharedInfo[infoNext].local));\n            needNewShareds = true;\n            ++infoNext;\n          } else {\n            newShareds.push_back(util::tupleGet(sharedsTuple, sharedsNext));\n          }\n        } else {\n          newShareds.push_back(M->Nr<PointerValue>(util::getVar(newLoopVar)));\n          needNewShareds = true;\n        }\n        ++sharedsNext;\n      }\n\n      privatesTuple = needNewPrivates ? util::makeTuple(newPrivates, M) : privatesTuple;\n      sharedsTuple = needNewShareds ? util::makeTuple(newShareds, M) : sharedsTuple;\n\n      Value *result = util::makeTuple({privatesTuple, sharedsTuple}, M);\n      v->replaceAll(result);\n    }\n\n    if (name == \"_taskred_finish\") {\n      seqassertn(reductionLocRef && gtid, \"bad visit order in template\");\n      if (numReductions() == 0)\n        return;\n\n      auto *taskRedFini = M->getOrRealizeFunc(\n          \"_taskred_fini\", {reductionLocRef->getType(), gtid->getType()}, {},\n          ompModule);\n      seqassertn(taskRedFini, \"taskred finish function not found not found\");\n      v->replaceAll(util::call(\n          taskRedFini, {M->Nr<VarValue>(reductionLocRef), M->Nr<VarValue>(gtid)}));\n    }\n  }\n};\n\nstruct GPULoopBodyStubReplacer : public util::Operator {\n  CallInstr *replacement;\n  Var *loopVar;\n  int64_t step;\n\n  GPULoopBodyStubReplacer(CallInstr *replacement, Var *loopVar, int64_t step)\n      : util::Operator(), replacement(replacement), loopVar(loopVar), step(step) {}\n\n  void handle(CallInstr *v) override {\n    auto *M = v->getModule();\n    auto *func = util::getFunc(v->getCallee());\n    if (!func)\n      return;\n    auto name = func->getUnmangledName();\n\n    if (name == \"_gpu_loop_body_stub\") {\n      seqassertn(replacement, \"unexpected double replacement\");\n      seqassertn(v->numArgs() == 2, \"unexpected loop body stub\");\n\n      // the template passes gtid, privs and shareds to the body stub for convenience\n      auto *idx = v->front();\n      auto *args = v->back();\n      unsigned next = 0;\n\n      std::vector<Value *> newArgs;\n      for (auto *arg : *replacement) {\n        if (getVarFromOutlinedArg(arg)->getId() == loopVar->getId()) {\n          newArgs.push_back(idx);\n        } else {\n          newArgs.push_back(util::tupleGet(args, next++));\n        }\n      }\n\n      auto *outlinedFunc = cast<BodiedFunc>(util::getFunc(replacement->getCallee()));\n      v->replaceAll(util::call(outlinedFunc, newArgs));\n      replacement = nullptr;\n    }\n\n    if (name == \"_loop_step\") {\n      v->replaceAll(M->getInt(step));\n    }\n  }\n};\n\nstruct GPULoopTemplateReplacer : public LoopTemplateReplacer {\n  int64_t step;\n\n  GPULoopTemplateReplacer(BodiedFunc *parent, CallInstr *replacement, Var *loopVar,\n                          int64_t step)\n      : LoopTemplateReplacer(parent, replacement, loopVar), step(step) {}\n\n  void handle(CallInstr *v) override {\n    auto *M = v->getModule();\n    auto *func = util::getFunc(v->getCallee());\n    if (!func)\n      return;\n    auto name = func->getUnmangledName();\n\n    if (name == \"_loop_step\") {\n      v->replaceAll(M->getInt(step));\n    }\n  }\n};\n\nstruct OpenMPTransformData {\n  util::OutlineResult outline;\n  std::vector<Var *> sharedVars;\n  ReductionIdentifier reds;\n};\n\ntemplate <typename T> OpenMPTransformData unpar(T *v) {\n  v->setParallel(false);\n  return {{}, {}, {}};\n}\n\ntemplate <typename T>\nOpenMPTransformData setupOpenMPTransform(T *v, BodiedFunc *parent, bool gpu) {\n  if (!v->isParallel())\n    return unpar(v);\n  auto *M = v->getModule();\n  auto *body = cast<SeriesFlow>(v->getBody());\n  if (!parent || !body)\n    return unpar(v);\n  auto outline = util::outlineRegion(parent, body, /*allowOutflows=*/false,\n                                     /*outlineGlobals=*/true, /*allByValue=*/gpu);\n  if (!outline)\n    return unpar(v);\n\n  // set up args to pass fork_call\n  Var *loopVar = v->getVar();\n  std::vector<Value *> outlineCallArgs(outline.call->begin(), outline.call->end());\n\n  // shared argument vars\n  std::vector<Var *> sharedVars;\n  Var *loopVarArg = nullptr;\n  unsigned i = 0;\n  for (auto it = outline.func->arg_begin(); it != outline.func->arg_end(); ++it) {\n    // pick out loop variable to pass to reduction identifier, which will\n    // ensure we don't reduce over it\n    if (getVarFromOutlinedArg(outlineCallArgs[i])->getId() == loopVar->getId())\n      loopVarArg = *it;\n    if (outline.argKinds[i] == util::OutlineResult::ArgKind::MODIFIED)\n      sharedVars.push_back(*it);\n    ++i;\n  }\n  ReductionIdentifier reds(sharedVars, loopVarArg);\n  outline.func->accept(reds);\n\n  return {outline, sharedVars, reds};\n}\n\nstruct ForkCallData {\n  CallInstr *fork = nullptr;\n  CallInstr *pushNumThreads = nullptr;\n};\n\nForkCallData createForkCall(Module *M, OMPTypes &types, Value *rawTemplateFunc,\n                            const std::vector<Value *> &forkExtraArgs,\n                            transform::parallel::OMPSched *sched) {\n  ForkCallData result;\n  auto *forkExtra = util::makeTuple(forkExtraArgs, M);\n  std::vector<types::Type *> forkArgTypes = {types.i8ptr, forkExtra->getType()};\n  auto *forkFunc = M->getOrRealizeFunc(\"_fork_call\", forkArgTypes, {}, ompModule);\n  seqassertn(forkFunc, \"fork call function not found\");\n  result.fork = util::call(forkFunc, {rawTemplateFunc, forkExtra});\n\n  if (sched->threads && sched->threads->getType()->is(types.i64)) {\n    auto *pushNumThreadsFunc =\n        M->getOrRealizeFunc(\"_push_num_threads\", {types.i64}, {}, ompModule);\n    seqassertn(pushNumThreadsFunc, \"push num threads func not found\");\n    result.pushNumThreads = util::call(pushNumThreadsFunc, {sched->threads});\n  }\n  return result;\n}\n\nstruct CollapseResult {\n  ImperativeForFlow *collapsed = nullptr;\n  SeriesFlow *setup = nullptr;\n  std::string error;\n\n  operator bool() const { return collapsed != nullptr; }\n};\n\nstruct LoopRange {\n  ImperativeForFlow *loop;\n  Var *start;\n  Var *stop;\n  int64_t step;\n  Var *len;\n};\n\nCollapseResult collapseLoop(BodiedFunc *parent, ImperativeForFlow *v, int64_t levels) {\n  auto fail = [](const std::string &error) {\n    CollapseResult bad;\n    bad.error = error;\n    return bad;\n  };\n\n  auto *M = v->getModule();\n  CollapseResult res;\n  if (levels < 1)\n    return fail(\"'collapse' must be at least 1\");\n\n  std::vector<ImperativeForFlow *> loopNests = {v};\n  ImperativeForFlow *curr = v;\n\n  for (auto i = 0; i < levels - 1; i++) {\n    auto *body = cast<SeriesFlow>(curr->getBody());\n    seqassertn(body, \"unexpected loop body\");\n    if (std::distance(body->begin(), body->end()) != 1 ||\n        !isA<ImperativeForFlow>(body->front()))\n      return fail(\"loop nest not collapsible\");\n\n    curr = cast<ImperativeForFlow>(body->front());\n    loopNests.push_back(curr);\n  }\n\n  std::vector<LoopRange> ranges;\n  auto *setup = M->Nr<SeriesFlow>();\n\n  auto *intType = M->getIntType();\n  auto *lenCalc =\n      M->getOrRealizeFunc(\"_range_len\", {intType, intType, intType}, {}, ompModule);\n  seqassertn(lenCalc, \"range length calculation function not found\");\n\n  for (auto *loop : loopNests) {\n    LoopRange range;\n    range.loop = loop;\n    range.start = util::makeVar(loop->getStart(), setup, parent);\n    range.stop = util::makeVar(loop->getEnd(), setup, parent);\n    range.step = loop->getStep();\n    range.len = util::makeVar(\n        util::call(lenCalc, {M->Nr<VarValue>(range.start), M->Nr<VarValue>(range.stop),\n                             M->getInt(range.step)}),\n        setup, parent);\n    ranges.push_back(range);\n  }\n\n  auto *numIters = M->getInt(1);\n  for (auto &range : ranges) {\n    numIters = (*numIters) * (*M->Nr<VarValue>(range.len));\n  }\n\n  auto *collapsedVar = M->Nr<Var>(M->getIntType(), /*global=*/false);\n  parent->push_back(collapsedVar);\n  auto *body = M->Nr<SeriesFlow>();\n  auto sched = std::make_unique<OMPSched>(*v->getSchedule());\n  sched->collapse = 0;\n  auto *collapsed = M->Nr<ImperativeForFlow>(M->getInt(0), 1, numIters, body,\n                                             collapsedVar, std::move(sched));\n\n  // reconstruct indices by successive divmods\n  Var *lastDiv = nullptr;\n  for (auto it = ranges.rbegin(); it != ranges.rend(); ++it) {\n    auto *k = lastDiv ? lastDiv : collapsedVar;\n    auto *div =\n        util::makeVar(*M->Nr<VarValue>(k) / *M->Nr<VarValue>(it->len), body, parent);\n    auto *mod =\n        util::makeVar(*M->Nr<VarValue>(k) % *M->Nr<VarValue>(it->len), body, parent);\n    auto *i =\n        *M->Nr<VarValue>(it->start) + *(*M->Nr<VarValue>(mod) * *M->getInt(it->step));\n    body->push_back(M->Nr<AssignInstr>(it->loop->getVar(), i));\n    lastDiv = div;\n  }\n\n  auto *oldBody = cast<SeriesFlow>(loopNests.back()->getBody());\n  for (auto *x : *oldBody) {\n    body->push_back(x);\n  }\n\n  res.collapsed = collapsed;\n  res.setup = setup;\n\n  return res;\n}\n} // namespace\n\nconst std::string OpenMPPass::KEY = \"core-parallel-openmp\";\n\nvoid OpenMPPass::handle(ForFlow *v) {\n  auto data = setupOpenMPTransform(v, cast<BodiedFunc>(getParentFunc()), /*gpu=*/false);\n  if (!v->isParallel())\n    return;\n\n  auto &outline = data.outline;\n  auto &sharedVars = data.sharedVars;\n  auto &reds = data.reds;\n\n  auto *M = v->getModule();\n  auto *loopVar = v->getVar();\n  auto *sched = v->getSchedule();\n  OMPTypes types(M);\n\n  // separate arguments into 'private' and 'shared'\n  std::vector<Reduction> sharedRedux; // reductions corresponding to shared vars\n  std::vector<Value *> privates, shareds;\n  unsigned i = 0;\n  for (auto *arg : *outline.call) {\n    if (isA<VarValue>(arg)) {\n      privates.push_back(arg);\n    } else {\n      shareds.push_back(arg);\n      sharedRedux.push_back(reds.getReduction(sharedVars[i++]));\n    }\n  }\n\n  util::CloneVisitor cv(M);\n\n  // We need to pass the task group identifier returned from\n  // __kmpc_taskred_modifier_init to the task entry, so append\n  // it to private data (initially as null void pointer). Also\n  // we add an argument to the end of the outlined function for\n  // the gtid.\n  if (reds.reductions.size() > 0) {\n    auto *nullPtr = types.i8ptr->construct({});\n    privates.push_back(nullPtr);\n\n    auto *outlinedFuncType = cast<types::FuncType>(outline.func->getType());\n    std::vector<types::Type *> argTypes(outlinedFuncType->begin(),\n                                        outlinedFuncType->end());\n    argTypes.push_back(M->getIntType());\n    auto *retType = outlinedFuncType->getReturnType();\n\n    std::vector<Var *> oldArgVars(outline.func->arg_begin(), outline.func->arg_end());\n    std::vector<std::string> argNames;\n\n    for (auto *var : oldArgVars) {\n      argNames.push_back(var->getName());\n    }\n    argNames.push_back(\"gtid\");\n\n    auto *newOutlinedFunc = M->Nr<BodiedFunc>(\"__outlined_new\");\n    newOutlinedFunc->realize(M->getFuncType(retType, argTypes), argNames);\n\n    std::vector<Var *> newArgVars(newOutlinedFunc->arg_begin(),\n                                  newOutlinedFunc->arg_end());\n\n    std::unordered_map<id_t, Var *> remaps;\n    for (unsigned i = 0; i < oldArgVars.size(); i++) {\n      remaps.emplace(oldArgVars[i]->getId(), newArgVars[i]);\n    }\n    auto *newBody =\n        cast<SeriesFlow>(cv.clone(outline.func->getBody(), newOutlinedFunc, remaps));\n    newOutlinedFunc->setBody(newBody);\n\n    // update outline struct\n    outline.func = newOutlinedFunc;\n    outline.call->setCallee(M->Nr<VarValue>(newOutlinedFunc));\n    outline.call->insert(outline.call->end(), M->getInt(0));\n    outline.argKinds.push_back(util::OutlineResult::ArgKind::CONSTANT);\n  }\n\n  auto *privatesTuple = util::makeTuple(privates, M);\n  auto *sharedsTuple = util::makeTuple(shareds, M);\n\n  // template call\n  std::vector<types::Type *> templateFuncArgs = {\n      types.i32ptr, types.i32ptr,\n      M->getPointerType(\n          M->getTupleType({v->getIter()->getType(), privatesTuple->getType(),\n                           sharedsTuple->getType()}))};\n  auto *templateFunc = M->getOrRealizeFunc(\"_task_loop_outline_template\",\n                                           templateFuncArgs, {}, ompModule);\n  seqassertn(templateFunc, \"task loop outline template not found\");\n\n  templateFunc = cv.forceClone(templateFunc);\n  TaskLoopRoutineStubReplacer rep(cast<BodiedFunc>(templateFunc), outline.call, loopVar,\n                                  &reds, privates, shareds, sharedRedux);\n  templateFunc->accept(rep);\n  auto *rawTemplateFunc = ptrFromFunc(templateFunc);\n\n  std::vector<Value *> forkExtraArgs = {v->getIter(), privatesTuple, sharedsTuple};\n\n  // fork call\n  auto forkData = createForkCall(M, types, rawTemplateFunc, forkExtraArgs, sched);\n  if (forkData.pushNumThreads)\n    insertBefore(forkData.pushNumThreads);\n  v->replaceAll(forkData.fork);\n}\n\nvoid OpenMPPass::handle(ImperativeForFlow *v) {\n  auto *parent = cast<BodiedFunc>(getParentFunc());\n\n  if (v->isParallel() && v->getSchedule()->collapse != 0) {\n    auto levels = v->getSchedule()->collapse;\n    auto collapse = collapseLoop(parent, v, levels);\n\n    if (collapse) {\n      v->replaceAll(collapse.collapsed);\n      v = collapse.collapsed;\n      insertBefore(collapse.setup);\n    } else if (!collapse.error.empty()) {\n      warn(\"could not collapse loop: \" + collapse.error, v);\n    }\n  }\n\n  auto data =\n      setupOpenMPTransform(v, parent, (v->isParallel() && v->getSchedule()->gpu));\n  if (!v->isParallel())\n    return;\n\n  auto &outline = data.outline;\n  auto &sharedVars = data.sharedVars;\n  auto &reds = data.reds;\n\n  auto *M = v->getModule();\n  auto *loopVar = v->getVar();\n  auto *sched = v->getSchedule();\n  OMPTypes types(M);\n\n  // we disable shared vars for GPU loops\n  seqassertn(!(sched->gpu && !sharedVars.empty()), \"GPU-parallel loop had shared vars\");\n\n  // gather extra arguments\n  std::vector<Value *> extraArgs;\n  std::vector<types::Type *> extraArgTypes;\n  for (auto *arg : *outline.call) {\n    if (getVarFromOutlinedArg(arg)->getId() != loopVar->getId()) {\n      extraArgs.push_back(arg);\n      extraArgTypes.push_back(arg->getType());\n    }\n  }\n\n  // template call\n  std::string templateFuncName;\n  if (sched->gpu) {\n    templateFuncName = \"_gpu_loop_outline_template\";\n  } else if (sched->dynamic) {\n    templateFuncName = \"_dynamic_loop_outline_template\";\n  } else if (sched->chunk) {\n    templateFuncName = \"_static_chunked_loop_outline_template\";\n  } else {\n    templateFuncName = \"_static_loop_outline_template\";\n  }\n\n  if (sched->gpu) {\n    std::unordered_set<id_t> kernels;\n    const std::string gpuAttr = ast::getMangledFunc(gpuModule, \"kernel\");\n    for (auto *var : *M) {\n      if (auto *func = cast<BodiedFunc>(var)) {\n        if (util::hasAttribute(func, gpuAttr)) {\n          kernels.insert(func->getId());\n        }\n      }\n    }\n\n    std::vector<types::Type *> templateFuncArgs = {types.i64, types.i64,\n                                                   M->getTupleType(extraArgTypes)};\n    static int64_t instance = 0;\n    auto *templateFunc = M->getOrRealizeFunc(templateFuncName, templateFuncArgs,\n                                             {instance++}, gpuModule);\n\n    if (!templateFunc) {\n      warn(\"loop not compilable for GPU; ignoring\", v);\n      v->setParallel(false);\n      return;\n    }\n\n    BodiedFunc *kernel = nullptr;\n    for (auto *var : *M) {\n      if (auto *func = cast<BodiedFunc>(var)) {\n        if (util::hasAttribute(func, gpuAttr) && kernels.count(func->getId()) == 0) {\n          seqassertn(!kernel, \"multiple new kernels found after instantiation\");\n          kernel = func;\n        }\n      }\n    }\n    seqassertn(kernel, \"no new kernel found\");\n    GPULoopBodyStubReplacer brep(outline.call, loopVar, v->getStep());\n    kernel->accept(brep);\n\n    util::CloneVisitor cv(M);\n    templateFunc = cast<Func>(cv.forceClone(templateFunc));\n    GPULoopTemplateReplacer rep(cast<BodiedFunc>(templateFunc), outline.call, loopVar,\n                                v->getStep());\n    templateFunc->accept(rep);\n    v->replaceAll(util::call(\n        templateFunc, {v->getStart(), v->getEnd(), util::makeTuple(extraArgs, M)}));\n  } else {\n    std::vector<types::Type *> templateFuncArgs = {\n        types.i32ptr, types.i32ptr,\n        M->getPointerType(M->getTupleType(\n            {types.i64, types.i64, types.i64, M->getTupleType(extraArgTypes)}))};\n    auto *templateFunc =\n        M->getOrRealizeFunc(templateFuncName, templateFuncArgs, {}, ompModule);\n    seqassertn(templateFunc, \"imperative loop outline template not found\");\n\n    util::CloneVisitor cv(M);\n    templateFunc = cast<Func>(cv.forceClone(templateFunc));\n    ImperativeLoopTemplateReplacer rep(cast<BodiedFunc>(templateFunc), outline.call,\n                                       loopVar, &reds, sched, v->getStep());\n    templateFunc->accept(rep);\n    auto *rawTemplateFunc = ptrFromFunc(templateFunc);\n\n    auto *chunk = (sched->chunk && sched->chunk->getType()->is(types.i64))\n                      ? sched->chunk\n                      : M->getInt(1);\n    std::vector<Value *> forkExtraArgs = {chunk, v->getStart(), v->getEnd()};\n    for (auto *arg : extraArgs) {\n      forkExtraArgs.push_back(arg);\n    }\n\n    // fork call\n    auto forkData = createForkCall(M, types, rawTemplateFunc, forkExtraArgs, sched);\n    if (forkData.pushNumThreads)\n      insertBefore(forkData.pushNumThreads);\n    v->replaceAll(forkData.fork);\n  }\n}\n\n} // namespace parallel\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/parallel/openmp.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace parallel {\n\nclass OpenMPPass : public OperatorPass {\npublic:\n  /// Constructs an OpenMP pass.\n  OpenMPPass() : OperatorPass(/*childrenFirst=*/true) {}\n\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n\n  void handle(ForFlow *) override;\n  void handle(ImperativeForFlow *) override;\n};\n\n} // namespace parallel\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/parallel/schedule.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"schedule.h\"\n\n#include \"codon/cir/cir.h\"\n#include \"codon/cir/util/irtools.h\"\n\n#include <cctype>\n#include <sstream>\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace parallel {\nnamespace {\nint getScheduleCode(const std::string &schedule = \"static\", bool chunked = false,\n                    bool ordered = false, bool monotonic = false) {\n  // codes from \"enum sched_type\" at\n  // https://github.com/llvm/llvm-project/blob/main/openmp/runtime/src/kmp.h\n  int modifier = monotonic ? (1 << 29) : (1 << 30);\n  if (schedule == \"static\") {\n    if (chunked) {\n      if (ordered)\n        return 65;\n      else\n        return 33;\n    } else {\n      if (ordered)\n        return 66;\n      else\n        return 34;\n    }\n  } else if (schedule == \"dynamic\") {\n    return (ordered ? 67 : 35) | modifier;\n  } else if (schedule == \"guided\") {\n    return (ordered ? 68 : 36) | modifier;\n  } else if (schedule == \"runtime\") {\n    return (ordered ? 69 : 37) | modifier;\n  } else if (schedule == \"auto\") {\n    return (ordered ? 70 : 38) | modifier;\n  }\n  return getScheduleCode(); // default\n}\n\nValue *nullIfNeg(Value *v) {\n  if (v && util::isConst<int64_t>(v) && util::getConst<int64_t>(v) <= 0)\n    return nullptr;\n  return v;\n}\n} // namespace\n\nOMPSched::OMPSched(int code, bool dynamic, Value *threads, Value *chunk, bool ordered,\n                   int64_t collapse, bool gpu)\n    : code(code), dynamic(dynamic), threads(nullIfNeg(threads)),\n      chunk(nullIfNeg(chunk)), ordered(ordered), collapse(collapse), gpu(gpu) {\n  if (code < 0)\n    this->code = getScheduleCode();\n}\n\nOMPSched::OMPSched(const std::string &schedule, Value *threads, Value *chunk,\n                   bool ordered, int64_t collapse, bool gpu)\n    : OMPSched(getScheduleCode(schedule, nullIfNeg(chunk) != nullptr, ordered),\n               (schedule != \"static\") || ordered, threads, chunk, ordered, collapse,\n               gpu) {}\n\nstd::vector<Value *> OMPSched::getUsedValues() const {\n  std::vector<Value *> ret;\n  if (threads)\n    ret.push_back(threads);\n  if (chunk)\n    ret.push_back(chunk);\n  return ret;\n}\n\nint OMPSched::replaceUsedValue(id_t id, Value *newValue) {\n  auto count = 0;\n  if (threads && threads->getId() == id) {\n    threads = newValue;\n    ++count;\n  }\n  if (chunk && chunk->getId() == id) {\n    chunk = newValue;\n    ++count;\n  }\n  return count;\n}\n\n} // namespace parallel\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/parallel/schedule.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/value.h\"\n\nnamespace codon {\nnamespace ir {\n\nclass Value;\n\nnamespace transform {\nnamespace parallel {\n\nstruct OMPSched {\n  int code;\n  bool dynamic;\n  Value *threads;\n  Value *chunk;\n  bool ordered;\n  int64_t collapse;\n  bool gpu;\n\n  explicit OMPSched(int code = -1, bool dynamic = false, Value *threads = nullptr,\n                    Value *chunk = nullptr, bool ordered = false, int64_t collapse = 0,\n                    bool gpu = false);\n  explicit OMPSched(const std::string &code, Value *threads = nullptr,\n                    Value *chunk = nullptr, bool ordered = false, int64_t collapse = 0,\n                    bool gpu = false);\n  OMPSched(const OMPSched &s)\n      : code(s.code), dynamic(s.dynamic), threads(s.threads), chunk(s.chunk),\n        ordered(s.ordered), collapse(s.collapse), gpu(s.gpu) {}\n\n  std::vector<Value *> getUsedValues() const;\n  int replaceUsedValue(id_t id, Value *newValue);\n};\n\n} // namespace parallel\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/pass.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"pass.h\"\n\n#include \"codon/cir/transform/manager.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\n\nanalyze::Result *Pass::doGetAnalysis(const std::string &key) {\n  return manager ? manager->getAnalysisResult(key) : nullptr;\n}\n\nvoid PassGroup::run(Module *module) {\n  for (auto &p : passes)\n    p->run(module);\n}\n\nvoid PassGroup::setManager(PassManager *mng) {\n  Pass::setManager(mng);\n  for (auto &p : passes)\n    p->setManager(mng);\n}\n\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/pass.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/module.h\"\n#include \"codon/cir/util/operator.h\"\n\nnamespace codon {\nnamespace ir {\n\nnamespace analyze {\nstruct Result;\n}\n\nnamespace transform {\n\nclass PassManager;\n\n/// General pass base class.\nclass Pass {\nprivate:\n  PassManager *manager = nullptr;\n\npublic:\n  virtual ~Pass() noexcept = default;\n\n  /// @return a unique key for this pass\n  virtual std::string getKey() const = 0;\n\n  /// Execute the pass.\n  /// @param module the module\n  virtual void run(Module *module) = 0;\n\n  /// Determine if pass should repeat.\n  /// @param num how many times this pass has already run\n  /// @return true if pass should repeat\n  virtual bool shouldRepeat(int num) const { return false; }\n\n  /// Sets the manager.\n  /// @param mng the new manager\n  virtual void setManager(PassManager *mng) { manager = mng; }\n  /// Returns the result of a given analysis.\n  /// @param key the analysis key\n  /// @return the analysis result\n  template <typename AnalysisType>\n  AnalysisType *getAnalysisResult(const std::string &key) {\n    return static_cast<AnalysisType *>(doGetAnalysis(key));\n  }\n\nprivate:\n  analyze::Result *doGetAnalysis(const std::string &key);\n};\n\nclass PassGroup : public Pass {\nprivate:\n  int repeat;\n  std::vector<std::unique_ptr<Pass>> passes;\n\npublic:\n  explicit PassGroup(int repeat = 0, std::vector<std::unique_ptr<Pass>> passes = {})\n      : Pass(), repeat(repeat), passes(std::move(passes)) {}\n\n  virtual ~PassGroup() noexcept = default;\n\n  void push_back(std::unique_ptr<Pass> p) { passes.push_back(std::move(p)); }\n\n  /// @return default number of times pass should repeat\n  int getRepeat() const { return repeat; }\n\n  /// Sets the default number of times pass should repeat.\n  /// @param r number of repeats\n  void setRepeat(int r) { repeat = r; }\n\n  bool shouldRepeat(int num) const override { return num < repeat; }\n\n  void run(Module *module) override;\n\n  void setManager(PassManager *mng) override;\n};\n\n/// Pass that runs a single Operator.\nclass OperatorPass : public Pass, public util::Operator {\npublic:\n  /// Constructs an operator pass.\n  /// @param childrenFirst true if children should be iterated first\n  explicit OperatorPass(bool childrenFirst = false) : util::Operator(childrenFirst) {}\n\n  void run(Module *module) override {\n    reset();\n    process(module);\n  }\n};\n\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/pythonic/dict.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"dict.h\"\n\n#include <algorithm>\n\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/cir/util/matching.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace pythonic {\nnamespace {\n/// get or __getitem__ call metadata\nstruct GetCall {\n  /// the function, nullptr if not a get call\n  Func *func = nullptr;\n  /// the dictionary, must not be a call\n  Value *dict = nullptr;\n  /// the key, must not be a call\n  Value *key = nullptr;\n  /// the default value, may be null\n  Const *dflt = nullptr;\n};\n\n/// Identify the call and return its metadata.\n/// @param call the call\n/// @return the metadata\nGetCall analyzeGet(CallInstr *call) {\n  // extract the function\n  auto *func = util::getFunc(call->getCallee());\n  if (!func)\n    return {};\n\n  auto unmangled = func->getUnmangledName();\n\n  // canonical get/__getitem__ calls have at least two arguments\n  auto it = call->begin();\n  auto dist = std::distance(it, call->end());\n  if (dist < 2)\n    return {};\n\n  // extract the dictionary and keys\n  auto *dict = *it++;\n  auto *k = *it++;\n\n  // dictionary and key must not be calls\n  if (isA<CallInstr>(dict) || isA<CallInstr>(k))\n    return {};\n\n  // get calls have a default\n  if (unmangled == \"get\" && std::distance(it, call->end()) == 1) {\n    auto *dflt = cast<Const>(*it);\n    return {func, dict, k, dflt};\n  } else if (unmangled == \"__getitem__\" && std::distance(it, call->end()) == 0) {\n    return {func, dict, k, nullptr};\n  }\n\n  // call is not correct\n  return {};\n}\n} // namespace\n\nconst std::string DictArithmeticOptimization::KEY = \"core-pythonic-dict-arithmetic-opt\";\n\nvoid DictArithmeticOptimization::handle(CallInstr *v) {\n  auto *M = v->getModule();\n\n  // get and check the exterior function (should be a __setitem__ with 3 args)\n  auto *setFunc = util::getFunc(v->getCallee());\n  if (setFunc && setFunc->getUnmangledName() == \"__setitem__\" &&\n      std::distance(v->begin(), v->end()) == 3) {\n    auto it = v->begin();\n\n    // extract all the arguments to the function\n    // the dictionary and key must not be calls, and the value must\n    // be a call\n    auto *dictValue = *it++;\n    auto *keyValue = *it++;\n    if (isA<CallInstr>(dictValue) || isA<CallInstr>(keyValue))\n      return;\n    auto *opCall = cast<CallInstr>(*it++);\n\n    // the call must take exactly two arguments\n    if (!dictValue || !opCall || std::distance(opCall->begin(), opCall->end()) != 2)\n      return;\n\n    // grab the function, which needs to be an int or float call for now\n    auto *opFunc = util::getFunc(opCall->getCallee());\n    auto *getCall = cast<CallInstr>(opCall->front());\n    if (!opFunc || !getCall)\n      return;\n\n    auto *intType = M->getIntType();\n    auto *floatType = M->getFloatType();\n    auto *parentType = opFunc->getParentType();\n    if (!parentType || !(parentType->is(intType) || parentType->is(floatType)))\n      return;\n\n    // check the first argument\n    auto getAnalysis = analyzeGet(getCall);\n    if (!getAnalysis.func)\n      return;\n\n    // second argument can be any non-null value\n    auto *secondValue = opCall->back();\n\n    // verify that we are dealing with the same dictionary and key\n    if (util::match(dictValue, getAnalysis.dict, false, true) &&\n        util::match(keyValue, getAnalysis.key, false, true)) {\n      util::CloneVisitor cv(M);\n      Func *replacementFunc;\n\n      // call non-throwing version if we have a default\n      if (getAnalysis.dflt) {\n        replacementFunc = M->getOrRealizeMethod(\n            dictValue->getType(), \"__dict_do_op__\",\n            {dictValue->getType(), keyValue->getType(), secondValue->getType(),\n             getAnalysis.dflt->getType(), opFunc->getType()});\n      } else {\n        replacementFunc =\n            M->getOrRealizeMethod(dictValue->getType(), \"__dict_do_op_throws__\",\n                                  {dictValue->getType(), keyValue->getType(),\n                                   secondValue->getType(), opFunc->getType()});\n      }\n\n      if (replacementFunc) {\n        std::vector<Value *> args = {cv.clone(dictValue), cv.clone(keyValue),\n                                     cv.clone(secondValue)};\n        if (getAnalysis.dflt)\n          args.push_back(cv.clone(getAnalysis.dflt));\n\n        // sanity check to make sure function is inlined\n        if (args.size() !=\n            std::distance(replacementFunc->arg_begin(), replacementFunc->arg_end()))\n          args.push_back(M->N<VarValue>(v, opFunc));\n\n        v->replaceAll(util::call(replacementFunc, args));\n      }\n    }\n  }\n}\n\n} // namespace pythonic\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/pythonic/dict.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace pythonic {\n\n/// Pass to optimize calls of form d[x] = func(d[x], any).\n/// This will work on any dictionary-like object that implements _do_op and\n/// _do_op_throws as well as getters.\nclass DictArithmeticOptimization : public OperatorPass {\npublic:\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n  void handle(CallInstr *v) override;\n};\n\n} // namespace pythonic\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/pythonic/generator.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"generator.h\"\n\n#include <algorithm>\n\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/cir/util/matching.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace pythonic {\nnamespace {\nbool isSum(Func *f) {\n  return f &&\n         f->getName().rfind(ast::getMangledFunc(\"std.internal.builtin\", \"sum\"), 0) == 0;\n}\n\nbool isAny(Func *f) {\n  return f &&\n         f->getName().rfind(ast::getMangledFunc(\"std.internal.builtin\", \"any\"), 0) == 0;\n}\n\nbool isAll(Func *f) {\n  return f &&\n         f->getName().rfind(ast::getMangledFunc(\"std.internal.builtin\", \"all\"), 0) == 0;\n}\n\n// Replaces yields with updates to the accumulator variable.\nstruct GeneratorSumTransformer : public util::Operator {\n  Var *accumulator;\n  bool valid;\n\n  explicit GeneratorSumTransformer(Var *accumulator)\n      : util::Operator(), accumulator(accumulator), valid(true) {}\n\n  void handle(YieldInstr *v) override {\n    auto *M = v->getModule();\n    auto *val = v->getValue();\n    if (!val) {\n      valid = false;\n      return;\n    }\n\n    Value *rhs = val;\n    if (val->getType()->is(M->getBoolType())) {\n      rhs = M->Nr<TernaryInstr>(rhs, M->getInt(1), M->getInt(0));\n    }\n\n    Value *add = *M->Nr<VarValue>(accumulator) + *rhs;\n    if (!add || !add->getType()->is(accumulator->getType())) {\n      valid = false;\n      return;\n    }\n\n    auto *assign = M->Nr<AssignInstr>(accumulator, add);\n    v->replaceAll(assign);\n  }\n\n  void handle(ReturnInstr *v) override {\n    auto *M = v->getModule();\n    auto *newReturn = M->Nr<ReturnInstr>(M->Nr<VarValue>(accumulator));\n    see(newReturn);\n    if (v->getValue()) {\n      v->replaceAll(util::series(v->getValue(), newReturn));\n    } else {\n      v->replaceAll(newReturn);\n    }\n  }\n\n  void handle(YieldInInstr *v) override { valid = false; }\n};\n\n// Replaces yields with conditional returns of the any/all answer.\nstruct GeneratorAnyAllTransformer : public util::Operator {\n  bool any; // true=any, false=all\n  bool valid;\n\n  explicit GeneratorAnyAllTransformer(bool any)\n      : util::Operator(), any(any), valid(true) {}\n\n  void handle(YieldInstr *v) override {\n    auto *M = v->getModule();\n    auto *val = v->getValue();\n    auto *valBool = val ? (*M->getBoolType())(*val) : nullptr;\n    if (!valBool) {\n      valid = false;\n      return;\n    } else if (!any) {\n      valBool = M->Nr<TernaryInstr>(valBool, M->getBool(false), M->getBool(true));\n    }\n\n    auto *newReturn = M->Nr<ReturnInstr>(M->getBool(any));\n    see(newReturn);\n    auto *rep = M->Nr<IfFlow>(valBool, util::series(newReturn));\n    v->replaceAll(rep);\n  }\n\n  void handle(ReturnInstr *v) override {\n    if (saw(v))\n      return;\n    auto *M = v->getModule();\n    auto *newReturn = M->Nr<ReturnInstr>(M->getBool(!any));\n    see(newReturn);\n    if (v->getValue()) {\n      v->replaceAll(util::series(v->getValue(), newReturn));\n    } else {\n      v->replaceAll(newReturn);\n    }\n  }\n\n  void handle(YieldInInstr *v) override { valid = false; }\n};\n\nFunc *genToSum(BodiedFunc *gen, types::Type *startType, types::Type *outType) {\n  if (!gen || !gen->isGenerator())\n    return nullptr;\n\n  auto *M = gen->getModule();\n  auto *genType = cast<types::FuncType>(gen->getType());\n  if (!genType)\n    return nullptr;\n\n  auto *fn = M->Nr<BodiedFunc>(\"__sum_wrapper\");\n  std::vector<types::Type *> argTypes(genType->begin(), genType->end());\n  argTypes.push_back(startType);\n\n  std::vector<std::string> names;\n  for (auto it = gen->arg_begin(); it != gen->arg_end(); ++it) {\n    names.push_back((*it)->getName());\n  }\n  names.push_back(\"start\");\n\n  auto *fnType = M->getFuncType(outType, argTypes);\n  fn->realize(fnType, names);\n\n  std::unordered_map<id_t, Var *> argRemap;\n  for (auto it1 = gen->arg_begin(), it2 = fn->arg_begin();\n       it1 != gen->arg_end() && it2 != fn->arg_end(); ++it1, ++it2) {\n    argRemap.emplace((*it1)->getId(), *it2);\n  }\n\n  util::CloneVisitor cv(M);\n  auto *body = cast<SeriesFlow>(cv.clone(gen->getBody(), fn, argRemap));\n  fn->setBody(body);\n\n  Value *init = M->Nr<VarValue>(fn->arg_back());\n  if (startType->is(M->getIntType()) && outType->is(M->getFloatType()))\n    init = (*M->getFloatType())(*init);\n\n  if (!init || !init->getType()->is(outType)) {\n    M->remove(fn);\n    return nullptr;\n  }\n\n  auto *accumulator = util::makeVar(init, body, fn, /*prepend=*/true);\n  GeneratorSumTransformer xgen(accumulator);\n  fn->accept(xgen);\n  body->push_back(M->Nr<ReturnInstr>(M->Nr<VarValue>(accumulator)));\n\n  if (!xgen.valid) {\n    M->remove(fn);\n    return nullptr;\n  }\n\n  return fn;\n}\n\nFunc *genToAnyAll(BodiedFunc *gen, bool any) {\n  if (!gen || !gen->isGenerator())\n    return nullptr;\n\n  auto *M = gen->getModule();\n  auto *fn = M->Nr<BodiedFunc>(any ? \"__any_wrapper\" : \"__all_wrapper\");\n  auto *genType = cast<types::FuncType>(gen->getType());\n\n  std::vector<types::Type *> argTypes(genType->begin(), genType->end());\n  std::vector<std::string> names;\n  for (auto it = gen->arg_begin(); it != gen->arg_end(); ++it) {\n    names.push_back((*it)->getName());\n  }\n\n  auto *fnType = M->getFuncType(M->getBoolType(), argTypes);\n  fn->realize(fnType, names);\n\n  std::unordered_map<id_t, Var *> argRemap;\n  for (auto it1 = gen->arg_begin(), it2 = fn->arg_begin();\n       it1 != gen->arg_end() && it2 != fn->arg_end(); ++it1, ++it2) {\n    argRemap.emplace((*it1)->getId(), *it2);\n  }\n\n  util::CloneVisitor cv(M);\n  auto *body = cast<SeriesFlow>(cv.clone(gen->getBody(), fn, argRemap));\n  fn->setBody(body);\n\n  GeneratorAnyAllTransformer xgen(any);\n  fn->accept(xgen);\n  body->push_back(M->Nr<ReturnInstr>(M->getBool(!any)));\n\n  if (!xgen.valid) {\n    M->remove(fn);\n    return nullptr;\n  }\n\n  return fn;\n}\n} // namespace\n\nconst std::string GeneratorArgumentOptimization::KEY =\n    \"core-pythonic-generator-argument-opt\";\n\nvoid GeneratorArgumentOptimization::handle(CallInstr *v) {\n  auto *M = v->getModule();\n  auto *func = util::getFunc(v->getCallee());\n\n  if (isSum(func) && v->numArgs() == 2) {\n    auto *call = cast<CallInstr>(v->front());\n    if (!call)\n      return;\n\n    auto *gen = util::getFunc(call->getCallee());\n    auto *start = v->back();\n\n    if (auto *fn = genToSum(cast<BodiedFunc>(gen), start->getType(), v->getType())) {\n      std::vector<Value *> args(call->begin(), call->end());\n      args.push_back(start);\n      v->replaceAll(util::call(fn, args));\n    }\n  } else {\n    bool any = isAny(func), all = isAll(func);\n    if (!(any || all) || v->numArgs() != 1 || !v->getType()->is(M->getBoolType()))\n      return;\n\n    auto *call = cast<CallInstr>(v->front());\n    if (!call)\n      return;\n\n    auto *gen = util::getFunc(call->getCallee());\n\n    if (auto *fn = genToAnyAll(cast<BodiedFunc>(gen), any)) {\n      std::vector<Value *> args(call->begin(), call->end());\n      v->replaceAll(util::call(fn, args));\n    }\n  }\n}\n\n} // namespace pythonic\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/pythonic/generator.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace pythonic {\n\n/// Pass to optimize passing a generator to some built-in functions\n/// like sum(), any() or all(), which will be converted to regular\n/// for-loops.\nclass GeneratorArgumentOptimization : public OperatorPass {\npublic:\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n  void handle(CallInstr *v) override;\n};\n\n} // namespace pythonic\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/pythonic/io.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"io.h\"\n\n#include <algorithm>\n\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace pythonic {\nnamespace {\nvoid optimizePrint(CallInstr *v) {\n  auto *M = v->getModule();\n\n  auto *inner = cast<CallInstr>(v->front());\n  if (!inner)\n    return;\n  auto *innerFunc = util::getFunc(inner->getCallee());\n  if (!innerFunc || innerFunc->getUnmangledName() != \"__new__\" ||\n      std::distance(inner->begin(), inner->end()) != 1)\n    return;\n\n  auto *cat = cast<CallInstr>(inner->front());\n  if (!cat)\n    return;\n  auto *catFunc = util::getFunc(cat->getCallee());\n  if (!catFunc || catFunc->getUnmangledName() != \"cat\")\n    return;\n\n  auto *realCat =\n      M->getOrRealizeMethod(M->getStringType(), \"cat\", {cat->front()->getType()});\n  if (realCat->getId() != catFunc->getId())\n    return;\n\n  util::CloneVisitor cv(M);\n  std::vector<Value *> args;\n  std::vector<types::Type *> types;\n  for (auto *printArg : *v) {\n    args.push_back(cv.clone(printArg));\n    types.push_back(printArg->getType());\n  }\n  args[0] = cv.clone(cat->front());\n  types[0] = args[0]->getType();\n  args[1] = M->getString(\"\");\n\n  auto *replacement = M->getOrRealizeFunc(\"print\", types, {}, \"std.internal.builtin\");\n  if (!replacement)\n    return;\n\n  v->replaceAll(util::call(replacement, args));\n}\n\nvoid optimizeWrite(CallInstr *v) {\n  auto *M = v->getModule();\n\n  auto it = v->begin();\n  auto *file = *it++;\n\n  auto *cat = cast<CallInstr>(*it++);\n  if (!cat)\n    return;\n  auto *catFunc = util::getFunc(cat->getCallee());\n  if (!catFunc || catFunc->getUnmangledName() != \"cat\")\n    return;\n\n  auto *realCat =\n      M->getOrRealizeMethod(M->getStringType(), \"cat\", {cat->front()->getType()});\n  if (realCat->getId() != catFunc->getId())\n    return;\n\n  util::CloneVisitor cv(M);\n  auto *iter = cv.clone(cat->front())->iter();\n  if (!iter)\n    return;\n\n  std::vector<Value *> args = {cv.clone(file), iter};\n\n  auto *replacement = M->getOrRealizeMethod(file->getType(), \"__file_write_gen__\",\n                                            {args[0]->getType(), args[1]->getType()});\n  if (!replacement)\n    return;\n\n  v->replaceAll(util::call(replacement, args));\n}\n} // namespace\n\nconst std::string IOCatOptimization::KEY = \"core-pythonic-io-cat-opt\";\n\nvoid IOCatOptimization::handle(CallInstr *v) {\n  if (util::getStdlibFunc(v->getCallee(), \"print\")) {\n    optimizePrint(v);\n  } else if (auto *f = cast<Func>(util::getFunc(v->getCallee()))) {\n    if (f->getUnmangledName() == \"write\")\n      optimizeWrite(v);\n  }\n}\n\n} // namespace pythonic\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/pythonic/io.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace pythonic {\n\n/// Pass to optimize print str.cat(...) or file.write(str.cat(...)).\nclass IOCatOptimization : public OperatorPass {\npublic:\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n  void handle(CallInstr *v) override;\n};\n\n} // namespace pythonic\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/pythonic/list.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"list.h\"\n\n#include <algorithm>\n\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace pythonic {\nnamespace {\n\nconst std::string LIST = ast::getMangledClass(\"std.internal.types.array\", \"List\");\nconst std::string SLICE =\n    ast::getMangledClass(\"std.internal.types.slice\", \"Slice\") + \"[int,int,int]\";\n\nbool isList(Value *v) { return v->getType()->getName().rfind(LIST + \"[\", 0) == 0; }\nbool isSlice(Value *v) { return v->getType()->getName() == SLICE; }\n\n// The following \"handlers\" account for the possible sub-expressions we might\n// see when optimizing list1 + list2 + ... listN. Currently, we optimize:\n//   - Slices: x[a:b:c] (avoid constructing the temporary sliced list)\n//   - Literals: [a, b, c] (just append elements directly)\n//   - Default: <any list expr> (append by iterating over the list)\n// It is easy to handle new sub-expression types by adding new handlers.\n// There are three stages in the optimized code:\n//   - Setup: assign all the relevant expressions to variables, making\n//            sure they're evaluated in the same order as before\n//   - Count: figure out the total length of the resulting list\n//   - Create: initialize a new list with the appropriate capacity and\n//             append all the elements\n// The handlers have virtual functions to generate IR for each of these steps.\n\nstruct ElementHandler {\n  std::vector<Var *> vars;\n\n  ElementHandler() : vars() {}\n  virtual ~ElementHandler() {}\n  virtual void setup(SeriesFlow *block, BodiedFunc *parent) = 0;\n  virtual Value *length(Module *M) = 0;\n  virtual Value *append(Value *result) = 0;\n\n  void doSetup(const std::vector<Value *> &values, SeriesFlow *block,\n               BodiedFunc *parent) {\n    for (auto *v : values) {\n      vars.push_back(util::makeVar(v, block, parent));\n    }\n  }\n\n  static std::unique_ptr<ElementHandler> get(Value *v, types::Type *ty);\n};\n\nstruct DefaultHandler : public ElementHandler {\n  Value *element;\n\n  DefaultHandler(Value *element) : ElementHandler(), element(element) {}\n\n  void setup(SeriesFlow *block, BodiedFunc *parent) override {\n    doSetup({element}, block, parent);\n  }\n\n  Value *length(Module *M) override {\n    auto *e = M->Nr<VarValue>(vars[0]);\n    auto *ty = element->getType();\n    auto *fn = M->getOrRealizeMethod(ty, \"_list_add_opt_default_len\", {ty});\n    seqassertn(fn, \"could not find default list length helper\");\n    return util::call(fn, {e});\n  }\n\n  Value *append(Value *result) override {\n    auto *M = result->getModule();\n    auto *e = M->Nr<VarValue>(vars[0]);\n    auto *ty = result->getType();\n    auto *fn = M->getOrRealizeMethod(ty, \"_list_add_opt_default_append\", {ty, ty});\n    seqassertn(fn, \"could not find default list append helper\");\n    return util::call(fn, {result, e});\n  }\n\n  static std::unique_ptr<ElementHandler> get(Value *v, types::Type *ty) {\n    if (!v->getType()->is(ty))\n      return {};\n    return std::make_unique<DefaultHandler>(v);\n  }\n};\n\nstruct SliceHandler : public ElementHandler {\n  Value *element;\n  Value *slice;\n\n  SliceHandler(Value *element, Value *slice)\n      : ElementHandler(), element(element), slice(slice) {}\n\n  void setup(SeriesFlow *block, BodiedFunc *parent) override {\n    doSetup({element, slice}, block, parent);\n  }\n\n  Value *length(Module *M) override {\n    auto *e = M->Nr<VarValue>(vars[0]);\n    auto *s = M->Nr<VarValue>(vars[1]);\n    auto *ty = element->getType();\n    auto *fn =\n        M->getOrRealizeMethod(ty, \"_list_add_opt_slice_len\", {ty, slice->getType()});\n    seqassertn(fn, \"could not find slice list length helper\");\n    return util::call(fn, {e, s});\n  }\n\n  Value *append(Value *result) override {\n    auto *M = result->getModule();\n    auto *e = M->Nr<VarValue>(vars[0]);\n    auto *s = M->Nr<VarValue>(vars[1]);\n    auto *ty = result->getType();\n    auto *fn = M->getOrRealizeMethod(ty, \"_list_add_opt_slice_append\",\n                                     {ty, ty, slice->getType()});\n    seqassertn(fn, \"could not find slice list append helper\");\n    return util::call(fn, {result, e, s});\n  }\n\n  static std::unique_ptr<ElementHandler> get(Value *v, types::Type *ty) {\n    if (!v->getType()->is(ty))\n      return {};\n\n    if (auto *c = cast<CallInstr>(v)) {\n      auto *func = util::getFunc(c->getCallee());\n      if (func && func->getUnmangledName() == Module::GETITEM_MAGIC_NAME &&\n          std::distance(c->begin(), c->end()) == 2 && isList(c->front()) &&\n          isSlice(c->back())) {\n        return std::make_unique<SliceHandler>(c->front(), c->back());\n      }\n    }\n\n    return {};\n  }\n};\n\nstruct LiteralHandler : public ElementHandler {\n  std::vector<Value *> elements;\n\n  LiteralHandler(std::vector<Value *> elements)\n      : ElementHandler(), elements(std::move(elements)) {}\n\n  void setup(SeriesFlow *block, BodiedFunc *parent) override {\n    doSetup(elements, block, parent);\n  }\n\n  Value *length(Module *M) override { return M->getInt(elements.size()); }\n\n  Value *append(Value *result) override {\n    auto *M = result->getModule();\n    auto *ty = result->getType();\n    auto *block = M->Nr<SeriesFlow>();\n    if (vars.empty())\n      return block;\n    auto *fn = M->getOrRealizeMethod(ty, \"_list_add_opt_literal_append\",\n                                     {ty, elements[0]->getType()});\n    seqassertn(fn, \"could not find literal list append helper\");\n    for (auto *var : vars) {\n      block->push_back(util::call(fn, {result, M->Nr<VarValue>(var)}));\n    }\n    return block;\n  }\n\n  static std::unique_ptr<ElementHandler> get(Value *v, types::Type *ty) {\n    if (!v->getType()->is(ty))\n      return {};\n\n    if (auto *attr = v->getAttribute<ListLiteralAttribute>()) {\n      std::vector<Value *> elements;\n      for (auto &element : attr->elements) {\n        if (element.star)\n          return {};\n        elements.push_back(element.value);\n      }\n      return std::make_unique<LiteralHandler>(std::move(elements));\n    }\n\n    return {};\n  }\n};\n\nstd::unique_ptr<ElementHandler> ElementHandler::get(Value *v, types::Type *ty) {\n  if (auto h = SliceHandler::get(v, ty))\n    return std::move(h);\n\n  if (auto h = LiteralHandler::get(v, ty))\n    return std::move(h);\n\n  return DefaultHandler::get(v, ty);\n}\n\nstruct InspectionResult {\n  bool valid = true;\n  std::vector<Value *> args;\n};\n\nvoid inspect(Value *v, InspectionResult &r) {\n  // check if add first then go from there\n  if (isList(v)) {\n    if (auto *c = cast<CallInstr>(v)) {\n      auto *func = util::getFunc(c->getCallee());\n      if (func && func->getUnmangledName() == Module::ADD_MAGIC_NAME &&\n          c->numArgs() == 2 && isList(c->front()) && isList(c->back())) {\n        inspect(c->front(), r);\n        inspect(c->back(), r);\n        return;\n      }\n    }\n    r.args.push_back(v);\n  } else {\n    r.valid = false;\n  }\n}\n\nValue *optimize(BodiedFunc *parent, InspectionResult &r) {\n  if (!r.valid || r.args.size() <= 1)\n    return nullptr;\n\n  auto *M = parent->getModule();\n  auto *ty = r.args[0]->getType();\n  util::CloneVisitor cv(M);\n  std::vector<std::unique_ptr<ElementHandler>> handlers;\n\n  for (auto *v : r.args) {\n    handlers.push_back(ElementHandler::get(cv.clone(v), ty));\n  }\n\n  auto *opt = M->Nr<SeriesFlow>();\n  auto *len = util::makeVar(M->getInt(0), opt, parent);\n\n  for (auto &h : handlers) {\n    h->setup(opt, parent);\n  }\n\n  for (auto &h : handlers) {\n    opt->push_back(M->Nr<AssignInstr>(len, *M->Nr<VarValue>(len) + *h->length(M)));\n  }\n\n  auto *fn = M->getOrRealizeMethod(ty, \"_list_add_opt_opt_new\", {M->getIntType()});\n  seqassertn(fn, \"could not find list new helper\");\n  auto *result = util::makeVar(util::call(fn, {M->Nr<VarValue>(len)}), opt, parent);\n\n  for (auto &h : handlers) {\n    opt->push_back(h->append(M->Nr<VarValue>(result)));\n  }\n\n  return M->Nr<FlowInstr>(opt, M->Nr<VarValue>(result));\n}\n} // namespace\n\nconst std::string ListAdditionOptimization::KEY = \"core-pythonic-list-addition-opt\";\n\nvoid ListAdditionOptimization::handle(CallInstr *v) {\n  auto *M = v->getModule();\n\n  auto *f = util::getFunc(v->getCallee());\n  if (!f || f->getUnmangledName() != Module::ADD_MAGIC_NAME)\n    return;\n\n  InspectionResult r;\n  inspect(v, r);\n  auto *parent = cast<BodiedFunc>(getParentFunc());\n  if (auto *opt = optimize(parent, r))\n    v->replaceAll(opt);\n}\n\n} // namespace pythonic\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/pythonic/list.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace pythonic {\n\n/// Pass to optimize list1 + list2 + ...\n/// Also handles list slices and list literals efficiently.\nclass ListAdditionOptimization : public OperatorPass {\npublic:\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n  void handle(CallInstr *v) override;\n};\n\n} // namespace pythonic\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/pythonic/str.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"str.h\"\n\n#include <algorithm>\n\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace pythonic {\nnamespace {\nstruct InspectionResult {\n  bool valid = true;\n  std::vector<Value *> args;\n};\n\nbool isString(Value *v) {\n  auto *M = v->getModule();\n  return v->getType()->is(M->getStringType());\n}\n\nvoid inspect(Value *v, InspectionResult &r) {\n  // check if add first then go from there\n  if (isString(v)) {\n    if (auto *c = cast<CallInstr>(v)) {\n      auto *func = util::getFunc(c->getCallee());\n      if (func && func->getUnmangledName() == Module::ADD_MAGIC_NAME &&\n          c->numArgs() == 2 && isString(c->front()) && isString(c->back())) {\n        inspect(c->front(), r);\n        inspect(c->back(), r);\n        return;\n      }\n    }\n    r.args.push_back(v);\n  } else {\n    r.valid = false;\n  }\n}\n} // namespace\n\nconst std::string StrAdditionOptimization::KEY = \"core-pythonic-str-addition-opt\";\n\nvoid StrAdditionOptimization::handle(CallInstr *v) {\n  auto *M = v->getModule();\n\n  auto *f = util::getFunc(v->getCallee());\n  if (!f || f->getUnmangledName() != Module::ADD_MAGIC_NAME)\n    return;\n\n  InspectionResult r;\n  inspect(v, r);\n\n  if (r.valid && r.args.size() > 2) {\n    std::vector<Value *> args;\n    util::CloneVisitor cv(M);\n\n    for (auto *arg : r.args) {\n      args.push_back(cv.clone(arg));\n    }\n\n    auto *arg = util::makeTuple(args, M);\n    args = {arg};\n    auto *replacementFunc =\n        M->getOrRealizeMethod(M->getStringType(), \"cat\", {arg->getType()});\n    seqassertn(replacementFunc, \"could not find cat function [{}]\", v->getSrcInfo());\n    v->replaceAll(util::call(replacementFunc, args));\n  }\n}\n\n} // namespace pythonic\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/pythonic/str.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\nnamespace pythonic {\n\n/// Pass to optimize str1 + str2 + ...\nclass StrAdditionOptimization : public OperatorPass {\npublic:\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n  void handle(CallInstr *v) override;\n};\n\n} // namespace pythonic\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/transform/rewrite.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/transform/pass.h\"\n#include \"codon/cir/util/visitor.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace transform {\n\n/// Base for rewrite rules.\nclass RewriteRule : public util::Visitor {\nprivate:\n  Value *result = nullptr;\n\nprotected:\n  void defaultVisit(Node *) override {}\n  void setResult(Value *r) { result = r; }\n  void resetResult() { setResult(nullptr); }\n  Value *getResult() const { return result; }\n\npublic:\n  virtual ~RewriteRule() noexcept = default;\n\n  /// Apply the rule.\n  /// @param v the value to rewrite\n  /// @return nullptr if no rewrite, the replacement otherwise\n  Value *apply(Value *v) {\n    v->accept(*this);\n    auto *replacement = getResult();\n    resetResult();\n    return replacement;\n  }\n};\n\n/// A collection of rewrite rules.\nclass Rewriter {\nprivate:\n  std::unordered_map<std::string, std::unique_ptr<RewriteRule>> rules;\n  int numReplacements = 0;\n\npublic:\n  /// Adds a given rewrite rule with the given key.\n  /// @param key the rule's key\n  /// @param rule the rewrite rule\n  void registerRule(const std::string &key, std::unique_ptr<RewriteRule> rule) {\n    rules.emplace(std::make_pair(key, std::move(rule)));\n  }\n\n  /// Applies all rewrite rules to the given node, and replaces the given\n  /// node with the result of the rewrites.\n  /// @param v the node to rewrite\n  void rewrite(Value *v) {\n    Value *result = v;\n    for (auto &r : rules) {\n      if (auto *rep = r.second->apply(result)) {\n        ++numReplacements;\n        result = rep;\n      }\n    }\n    if (v != result)\n      v->replaceAll(result);\n  }\n\n  /// @return the number of replacements\n  int getNumReplacements() const { return numReplacements; }\n\n  /// Sets the replacement count to zero.\n  void reset() { numReplacements = 0; }\n};\n\n} // namespace transform\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/types/types.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"types.h\"\n\n#include <algorithm>\n#include <memory>\n#include <utility>\n\n#include \"codon/cir/module.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/cir/util/iterators.h\"\n#include \"codon/cir/util/visitor.h\"\n#include \"codon/cir/value.h\"\n#include \"codon/parser/cache.h\"\n#include <fmt/format.h>\n\nnamespace codon {\nnamespace ir {\nnamespace types {\nnamespace {\nstd::vector<codon::ast::types::TypePtr>\nextractTypes(const std::vector<codon::ast::types::ClassType::Generic> &gens) {\n  std::vector<codon::ast::types::TypePtr> ret;\n  for (auto &g : gens)\n    ret.push_back(g.type);\n  return ret;\n}\n} // namespace\n\nconst char Type::NodeId = 0;\n\nstd::vector<Generic> Type::doGetGenerics() const {\n  if (!astType)\n    return {};\n\n  std::vector<Generic> ret;\n  for (auto &g : astType->getClass()->generics) {\n    if (auto ai = g.type->getIntStatic()) {\n      ret.emplace_back(ai->value);\n    } else if (auto ai = g.type->getBoolStatic()) {\n      ret.emplace_back(int(ai->value));\n    } else if (auto as = g.type->getStrStatic()) {\n      ret.emplace_back(as->value);\n    } else if (auto ac = g.type->getClass()) {\n      ret.emplace_back(\n          getModule()->getCache()->realizeType(ac, extractTypes(ac->generics)));\n    } else {\n      seqassertn(false, \"IR only supports int, bool or str statics [{}]\",\n                 g.type->getSrcInfo());\n    }\n  }\n\n  return ret;\n}\n\nValue *Type::doConstruct(std::vector<Value *> args) {\n  auto *module = getModule();\n  std::vector<Type *> argTypes;\n  for (auto *a : args)\n    argTypes.push_back(a->getType());\n\n  auto *fn = module->getOrRealizeMethod(this, Module::NEW_MAGIC_NAME, argTypes);\n  if (!fn)\n    return nullptr;\n\n  return module->Nr<CallInstr>(module->Nr<VarValue>(fn), args);\n}\n\nconst char PrimitiveType::NodeId = 0;\n\nconst char IntType::NodeId = 0;\n\nconst char FloatType::NodeId = 0;\n\nconst char Float32Type::NodeId = 0;\n\nconst char Float16Type::NodeId = 0;\n\nconst char BFloat16Type::NodeId = 0;\n\nconst char Float128Type::NodeId = 0;\n\nconst char BoolType::NodeId = 0;\n\nconst char ByteType::NodeId = 0;\n\nconst char VoidType::NodeId = 0;\n\nconst char MemberedType::NodeId = 0;\n\nconst char RecordType::NodeId = 0;\n\nRecordType::RecordType(std::string name, std::vector<Type *> fieldTypes,\n                       std::vector<std::string> fieldNames)\n    : AcceptorExtend(std::move(name)) {\n  for (auto i = 0; i < fieldTypes.size(); ++i) {\n    fields.emplace_back(fieldNames[i], fieldTypes[i]);\n  }\n}\n\nRecordType::RecordType(std::string name, std::vector<Type *> mTypes)\n    : AcceptorExtend(std::move(name)) {\n  for (int i = 0; i < mTypes.size(); ++i) {\n    fields.emplace_back(std::to_string(i + 1), mTypes[i]);\n  }\n}\n\nstd::vector<Type *> RecordType::doGetUsedTypes() const {\n  std::vector<Type *> ret;\n  for (auto &f : fields)\n    ret.push_back(const_cast<Type *>(f.getType()));\n  return ret;\n}\n\nType *RecordType::getMemberType(const std::string &n) const {\n  auto it = std::find_if(fields.begin(), fields.end(),\n                         [n](auto &x) { return x.getName() == n; });\n  return (it != fields.end()) ? it->getType() : nullptr;\n}\n\nint RecordType::getMemberIndex(const std::string &n) const {\n  auto it = std::find_if(fields.begin(), fields.end(),\n                         [n](auto &x) { return x.getName() == n; });\n  int index = std::distance(fields.begin(), it);\n  return (index < fields.size()) ? index : -1;\n}\n\nvoid RecordType::realize(std::vector<Type *> mTypes, std::vector<std::string> mNames) {\n  fields.clear();\n  for (auto i = 0; i < mTypes.size(); ++i) {\n    fields.emplace_back(mNames[i], mTypes[i]);\n  }\n}\n\nconst char RefType::NodeId = 0;\n\nbool RefType::doIsContentAtomic() const {\n  auto *contents = getContents();\n  return !std::any_of(contents->begin(), contents->end(), [](auto &field) {\n    return field.getName().rfind(\".__vtable__\", 0) != 0 && !field.getType()->isAtomic();\n  });\n}\n\nValue *RefType::doConstruct(std::vector<Value *> args) {\n  auto *module = getModule();\n  auto *argsTuple = util::makeTuple(args, module);\n  auto *constructFn = module->getOrRealizeFunc(\"construct_ref\", {argsTuple->getType()},\n                                               {this}, \"std.internal.gc\");\n  if (!constructFn)\n    return nullptr;\n\n  std::vector<Value *> callArgs = {argsTuple};\n  return module->Nr<CallInstr>(module->Nr<VarValue>(constructFn), callArgs);\n}\n\nconst char FuncType::NodeId = 0;\n\nstd::vector<Generic> FuncType::doGetGenerics() const {\n  auto t = getAstType();\n  if (!t)\n    return {};\n  auto astType = t->getFunc();\n  if (!astType)\n    return {};\n\n  std::vector<Generic> ret;\n\n  for (auto &g : astType->funcGenerics) {\n    if (auto ai = g.type->getIntStatic()) {\n      ret.emplace_back(ai->value);\n    } else if (auto ai = g.type->getBoolStatic()) {\n      ret.emplace_back(int(ai->value));\n    } else if (auto as = g.type->getStrStatic()) {\n      ret.emplace_back(as->value);\n    } else if (auto ac = g.type->getClass()) {\n      ret.emplace_back(\n          getModule()->getCache()->realizeType(ac, extractTypes(ac->generics)));\n    } else {\n      seqassertn(false, \"IR only supports int, bool or str statics [{}]\",\n                 g.type->getSrcInfo());\n    }\n  }\n\n  return ret;\n}\n\nstd::vector<Type *> FuncType::doGetUsedTypes() const {\n  auto ret = argTypes;\n  ret.push_back(rType);\n  return ret;\n}\n\nconst char DerivedType::NodeId = 0;\n\nconst char PointerType::NodeId = 0;\n\nstd::string PointerType::getInstanceName(Type *base) {\n  return fmt::format(FMT_STRING(\"Pointer[{}]\"), base->referenceString());\n}\n\nconst char OptionalType::NodeId = 0;\n\nstd::string OptionalType::getInstanceName(Type *base) {\n  return fmt::format(FMT_STRING(\"Optional[{}]\"), base->referenceString());\n}\n\nconst char GeneratorType::NodeId = 0;\n\nstd::string GeneratorType::getInstanceName(Type *base) {\n  return fmt::format(FMT_STRING(\"Generator[{}]\"), base->referenceString());\n}\n\nconst char IntNType::NodeId = 0;\n\nstd::string IntNType::getInstanceName(unsigned int len, bool sign) {\n  return fmt::format(FMT_STRING(\"{}Int{}\"), sign ? \"\" : \"U\", len);\n}\n\nconst char VectorType::NodeId = 0;\n\nstd::string VectorType::getInstanceName(unsigned int count, PrimitiveType *base) {\n  return fmt::format(FMT_STRING(\"Vector[{}, {}]\"), count, base->referenceString());\n}\n\nconst char UnionType::NodeId = 0;\n\nstd::string UnionType::getInstanceName(const std::vector<types::Type *> &types) {\n  std::vector<std::string> names;\n  for (auto *type : types) {\n    names.push_back(type->referenceString());\n  }\n  return fmt::format(FMT_STRING(\"Union[{}]\"),\n                     fmt::join(names.begin(), names.end(), \", \"));\n}\n\n} // namespace types\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/types/types.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <algorithm>\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"codon/cir/base.h\"\n#include \"codon/cir/util/packs.h\"\n#include \"codon/cir/util/visitor.h\"\n#include \"codon/parser/ast.h\"\n#include <fmt/format.h>\n#include <fmt/ostream.h>\n\nnamespace codon {\nnamespace ir {\n\nclass Value;\n\nnamespace types {\n\nclass Type;\n\nclass Generic {\nprivate:\n  union {\n    int64_t staticValue;\n    char *staticStringValue;\n    types::Type *typeValue;\n  } value;\n  enum { STATIC, STATIC_STR, TYPE } tag;\n\npublic:\n  Generic(int64_t staticValue) : value(), tag(STATIC) {\n    value.staticValue = staticValue;\n  }\n  Generic(const std::string &staticValue) : value(), tag(STATIC_STR) {\n    value.staticStringValue = new char[staticValue.size() + 1];\n    strncpy(value.staticStringValue, staticValue.data(), staticValue.size());\n    value.staticStringValue[staticValue.size()] = 0;\n  }\n  Generic(types::Type *typeValue) : value(), tag(TYPE) { value.typeValue = typeValue; }\n  Generic(const types::Generic &) = default;\n  ~Generic() {\n    // if (tag == STATIC_STR)\n    //   delete[] value.staticStringValue;\n  }\n\n  /// @return true if the generic is a type\n  bool isType() const { return tag == TYPE; }\n  /// @return true if the generic is static\n  bool isStatic() const { return tag == STATIC; }\n  /// @return true if the generic is static\n  bool isStaticStr() const { return tag == STATIC_STR; }\n\n  /// @return the static value\n  int64_t getStaticValue() const { return value.staticValue; }\n  /// @return the static string value\n  std::string getStaticStringValue() const { return value.staticStringValue; }\n  /// @return the type value\n  types::Type *getTypeValue() const { return value.typeValue; }\n};\n\n/// Type from which other CIR types derive. Generally types are immutable.\nclass Type : public ReplaceableNodeBase<Type> {\nprivate:\n  ast::types::TypePtr astType;\n\npublic:\n  static const char NodeId;\n\n  using ReplaceableNodeBase::ReplaceableNodeBase;\n\n  virtual ~Type() noexcept = default;\n\n  std::vector<Type *> getUsedTypes() const final {\n    return getActual()->doGetUsedTypes();\n  }\n  int replaceUsedType(const std::string &name, Type *newType) final {\n    seqassertn(false, \"types not replaceable\");\n    return -1;\n  }\n  using Node::replaceUsedType;\n\n  /// @param other another type\n  /// @return true if this type is equal to the argument type\n  bool is(types::Type *other) const { return getName() == other->getName(); }\n\n  /// A type is \"atomic\" iff it contains no pointers to dynamically\n  /// allocated memory. Atomic types do not need to be scanned during\n  /// garbage collection.\n  /// @return true if the type is atomic\n  bool isAtomic() const { return getActual()->doIsAtomic(); }\n\n  /// Checks if the contents (i.e. within an allocated block of memory)\n  /// of a type are atomic. Currently only meaningful for reference types.\n  /// @return true if the type's content is atomic\n  bool isContentAtomic() const { return getActual()->doIsContentAtomic(); }\n\n  /// @return the ast type\n  ast::types::TypePtr getAstType() const { return getActual()->astType; }\n  /// Sets the ast type. Should not generally be used.\n  /// @param t the new type\n  void setAstType(ast::types::TypePtr t) { getActual()->astType = std::move(t); }\n\n  /// @return the generics used in the type\n  std::vector<Generic> getGenerics() const { return getActual()->doGetGenerics(); }\n\n  /// Constructs an instance of the type given the supplied args.\n  /// @param args the arguments\n  /// @return the new value\n  Value *construct(std::vector<Value *> args) {\n    return getActual()->doConstruct(std::move(args));\n  }\n  template <typename... Args> Value *operator()(Args &&...args) {\n    std::vector<Value *> dst;\n    util::stripPack(dst, std::forward<Args>(args)...);\n    return construct(dst);\n  }\n\nprivate:\n  virtual std::vector<Generic> doGetGenerics() const;\n\n  virtual std::vector<Type *> doGetUsedTypes() const { return {}; }\n  virtual bool doIsAtomic() const = 0;\n  virtual bool doIsContentAtomic() const { return true; }\n\n  virtual Value *doConstruct(std::vector<Value *> args);\n};\n\n/// Type from which primitive atomic types derive.\nclass PrimitiveType : public AcceptorExtend<PrimitiveType, Type> {\npublic:\n  static const char NodeId;\n\n  using AcceptorExtend::AcceptorExtend;\n\nprivate:\n  bool doIsAtomic() const final { return true; }\n};\n\n/// Int type (64-bit signed integer)\nclass IntType : public AcceptorExtend<IntType, PrimitiveType> {\npublic:\n  static const char NodeId;\n\n  /// Constructs an int type.\n  IntType() : AcceptorExtend(\"int\") {}\n};\n\n/// Float type (64-bit double)\nclass FloatType : public AcceptorExtend<FloatType, PrimitiveType> {\npublic:\n  static const char NodeId;\n\n  /// Constructs a float type.\n  FloatType() : AcceptorExtend(\"float\") {}\n};\n\n/// Float32 type (32-bit float)\nclass Float32Type : public AcceptorExtend<Float32Type, PrimitiveType> {\npublic:\n  static const char NodeId;\n\n  /// Constructs a float32 type.\n  Float32Type() : AcceptorExtend(\"float32\") {}\n};\n\n/// Float16 type (16-bit float)\nclass Float16Type : public AcceptorExtend<Float16Type, PrimitiveType> {\npublic:\n  static const char NodeId;\n\n  /// Constructs a float16 type.\n  Float16Type() : AcceptorExtend(\"float16\") {}\n};\n\n/// BFloat16 type (16-bit brain float)\nclass BFloat16Type : public AcceptorExtend<BFloat16Type, PrimitiveType> {\npublic:\n  static const char NodeId;\n\n  /// Constructs a bfloat16 type.\n  BFloat16Type() : AcceptorExtend(\"bfloat16\") {}\n};\n\n/// Float128 type (128-bit float)\nclass Float128Type : public AcceptorExtend<Float128Type, PrimitiveType> {\npublic:\n  static const char NodeId;\n\n  /// Constructs a float128 type.\n  Float128Type() : AcceptorExtend(\"float128\") {}\n};\n\n/// Bool type (8-bit unsigned integer; either 0 or 1)\nclass BoolType : public AcceptorExtend<BoolType, PrimitiveType> {\npublic:\n  static const char NodeId;\n\n  /// Constructs a bool type.\n  BoolType() : AcceptorExtend(\"bool\") {}\n};\n\n/// Byte type (8-bit unsigned integer)\nclass ByteType : public AcceptorExtend<ByteType, PrimitiveType> {\npublic:\n  static const char NodeId;\n\n  /// Constructs a byte type.\n  ByteType() : AcceptorExtend(\"byte\") {}\n};\n\n/// Void type\nclass VoidType : public AcceptorExtend<VoidType, PrimitiveType> {\npublic:\n  static const char NodeId;\n\n  /// Constructs a void type.\n  VoidType() : AcceptorExtend(\"void\") {}\n};\n\n/// Type from which membered types derive.\nclass MemberedType : public AcceptorExtend<MemberedType, Type> {\npublic:\n  static const char NodeId;\n\n  /// Object that represents a field in a membered type.\n  class Field {\n  private:\n    /// the field's name\n    std::string name;\n    /// the field's type\n    Type *type;\n\n  public:\n    /// Constructs a field.\n    /// @param name the field's name\n    /// @param type the field's type\n    Field(std::string name, Type *type) : name(std::move(name)), type(type) {}\n\n    /// @return the field's name\n    const std::string &getName() const { return name; }\n    /// @return the field type\n    Type *getType() const { return type; }\n  };\n\n  using const_iterator = std::vector<Field>::const_iterator;\n  using const_reference = std::vector<Field>::const_reference;\n\n  /// Constructs a membered type.\n  /// @param name the type's name\n  explicit MemberedType(std::string name) : AcceptorExtend(std::move(name)) {}\n\n  /// Gets a field type by name.\n  /// @param name the field's name\n  /// @return the type if it exists\n  virtual Type *getMemberType(const std::string &name) const = 0;\n  /// Gets the index of a field by name.\n  /// @param name the field's name\n  /// @return 0-based field index, or -1 if not found\n  virtual int getMemberIndex(const std::string &name) const = 0;\n\n  /// @return iterator to the first field\n  virtual const_iterator begin() const = 0;\n  /// @return iterator beyond the last field\n  virtual const_iterator end() const = 0;\n  /// @return a reference to the first field\n  virtual const_reference front() const = 0;\n  /// @return a reference to the last field\n  virtual const_reference back() const = 0;\n\n  /// Changes the body of the membered type.\n  /// @param mTypes the new body\n  /// @param mNames the new names\n  virtual void realize(std::vector<Type *> mTypes, std::vector<std::string> mNames) = 0;\n};\n\n/// Membered type equivalent to C structs/C++ PODs\nclass RecordType : public AcceptorExtend<RecordType, MemberedType> {\nprivate:\n  std::vector<Field> fields;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a record type.\n  /// @param name the type's name\n  /// @param fieldTypes the member types\n  /// @param fieldNames the member names\n  RecordType(std::string name, std::vector<Type *> fieldTypes,\n             std::vector<std::string> fieldNames);\n  /// Constructs a record type. The field's names are \"1\", \"2\"...\n  /// @param name the type's name\n  /// @param mTypes a vector of member types\n  RecordType(std::string name, std::vector<Type *> mTypes);\n  /// Constructs an empty record type.\n  /// @param name the name\n  explicit RecordType(std::string name) : AcceptorExtend(std::move(name)) {}\n\n  Type *getMemberType(const std::string &n) const override;\n  int getMemberIndex(const std::string &n) const override;\n\n  const_iterator begin() const override { return fields.begin(); }\n  const_iterator end() const override { return fields.end(); }\n  const_reference front() const override { return fields.front(); }\n  const_reference back() const override { return fields.back(); }\n\n  void realize(std::vector<Type *> mTypes, std::vector<std::string> mNames) override;\n\nprivate:\n  std::vector<Type *> doGetUsedTypes() const override;\n\n  bool doIsAtomic() const override {\n    return !std::any_of(fields.begin(), fields.end(),\n                        [](auto &field) { return !field.getType()->isAtomic(); });\n  }\n};\n\n/// Membered type that is passed by reference. Similar to Python classes.\nclass RefType : public AcceptorExtend<RefType, MemberedType> {\nprivate:\n  /// the internal contents of the type\n  Type *contents;\n  /// true if type is polymorphic and needs RTTI\n  bool polymorphic;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a reference type.\n  /// @param name the type's name\n  /// @param contents the type's contents\n  /// @param polymorphic true if type is polymorphic\n  RefType(std::string name, RecordType *contents, bool polymorphic = false)\n      : AcceptorExtend(std::move(name)), contents(contents), polymorphic(polymorphic) {}\n\n  /// @return true if the type is polymorphic and needs RTTI\n  bool isPolymorphic() const { return polymorphic; }\n  /// Sets whether the type is polymorphic. Should not generally be used.\n  /// @param p true if polymorphic\n  void setPolymorphic(bool p = true) { polymorphic = p; }\n\n  Type *getMemberType(const std::string &n) const override {\n    return getContents()->getMemberType(n);\n  }\n  int getMemberIndex(const std::string &n) const override {\n    return getContents()->getMemberIndex(n);\n  }\n\n  const_iterator begin() const override { return getContents()->begin(); }\n  const_iterator end() const override { return getContents()->end(); }\n  const_reference front() const override { return getContents()->front(); }\n  const_reference back() const override { return getContents()->back(); }\n\n  /// @return the reference type's contents\n  RecordType *getContents() const { return cast<RecordType>(contents); }\n  /// Sets the reference type's contents. Should not generally be used.\n  /// @param t the new contents\n  void setContents(RecordType *t) { contents = t; }\n\n  void realize(std::vector<Type *> mTypes, std::vector<std::string> mNames) override {\n    getContents()->realize(std::move(mTypes), std::move(mNames));\n  }\n\nprivate:\n  std::vector<Type *> doGetUsedTypes() const override { return {contents}; }\n\n  bool doIsAtomic() const override { return false; }\n\n  bool doIsContentAtomic() const override;\n\n  Value *doConstruct(std::vector<Value *> args) override;\n};\n\n/// Type associated with a CIR function.\nclass FuncType : public AcceptorExtend<FuncType, Type> {\npublic:\n  using const_iterator = std::vector<Type *>::const_iterator;\n  using const_reference = std::vector<Type *>::const_reference;\n\nprivate:\n  /// return type\n  Type *rType;\n  /// argument types\n  std::vector<Type *> argTypes;\n  /// whether the function is variadic (e.g. \"printf\" in C)\n  bool variadic;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a function type.\n  /// @param rType the function's return type\n  /// @param argTypes the function's arg types\n  FuncType(std::string name, Type *rType, std::vector<Type *> argTypes,\n           bool variadic = false)\n      : AcceptorExtend(std::move(name)), rType(rType), argTypes(std::move(argTypes)),\n        variadic(variadic) {}\n\n  /// @return the function's return type\n  Type *getReturnType() const { return rType; }\n  /// @return true if the function is variadic\n  bool isVariadic() const { return variadic; }\n\n  /// @return iterator to the first argument\n  const_iterator begin() const { return argTypes.begin(); }\n  /// @return iterator beyond the last argument\n  const_iterator end() const { return argTypes.end(); }\n  /// @return a reference to the first argument\n  const_reference front() const { return argTypes.front(); }\n  /// @return a reference to the last argument\n  const_reference back() const { return argTypes.back(); }\n\nprivate:\n  std::vector<Generic> doGetGenerics() const override;\n\n  std::vector<Type *> doGetUsedTypes() const override;\n\n  bool doIsAtomic() const override { return false; }\n};\n\n/// Base for simple derived types.\nclass DerivedType : public AcceptorExtend<DerivedType, Type> {\nprivate:\n  /// the base type\n  Type *base;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a derived type.\n  /// @param name the type's name\n  /// @param base the type's base\n  explicit DerivedType(std::string name, Type *base)\n      : AcceptorExtend(std::move(name)), base(base) {}\n\n  /// @return the type's base\n  Type *getBase() const { return base; }\n\nprivate:\n  bool doIsAtomic() const override { return base->isAtomic(); }\n\n  std::vector<Type *> doGetUsedTypes() const override { return {base}; }\n};\n\n/// Type of a pointer to another CIR type\nclass PointerType : public AcceptorExtend<PointerType, DerivedType> {\npublic:\n  static const char NodeId;\n\n  /// Constructs a pointer type.\n  /// @param base the type's base\n  explicit PointerType(Type *base) : AcceptorExtend(getInstanceName(base), base) {}\n\n  static std::string getInstanceName(Type *base);\n\nprivate:\n  bool doIsAtomic() const override { return false; }\n};\n\n/// Type of an optional containing another CIR type\nclass OptionalType : public AcceptorExtend<OptionalType, DerivedType> {\npublic:\n  static const char NodeId;\n\n  /// Constructs an optional type.\n  /// @param base the type's base\n  explicit OptionalType(Type *base) : AcceptorExtend(getInstanceName(base), base) {}\n\n  static std::string getInstanceName(Type *base);\n\nprivate:\n  bool doIsAtomic() const override { return getBase()->isAtomic(); }\n};\n\n/// Type of a generator yielding another CIR type\nclass GeneratorType : public AcceptorExtend<GeneratorType, DerivedType> {\npublic:\n  static const char NodeId;\n\n  /// Constructs a generator type.\n  /// @param base the type's base\n  explicit GeneratorType(Type *base) : AcceptorExtend(getInstanceName(base), base) {}\n\n  static std::string getInstanceName(Type *base);\n\nprivate:\n  bool doIsAtomic() const override { return false; }\n};\n\n/// Type of a variably sized integer\nclass IntNType : public AcceptorExtend<IntNType, PrimitiveType> {\nprivate:\n  /// length of the integer\n  unsigned len;\n  /// whether the variable is signed\n  bool sign;\n\npublic:\n  static const char NodeId;\n\n  static const unsigned MAX_LEN = 2048;\n\n  /// Constructs a variably sized integer type.\n  /// @param len the length of the integer\n  /// @param sign true if signed, false otherwise\n  IntNType(unsigned len, bool sign)\n      : AcceptorExtend(getInstanceName(len, sign)), len(len), sign(sign) {}\n\n  /// @return the length of the integer\n  unsigned getLen() const { return len; }\n  /// @return true if signed\n  bool isSigned() const { return sign; }\n\n  /// @return the name of the opposite signed corresponding type\n  std::string oppositeSignName() const { return getInstanceName(len, !sign); }\n\n  static std::string getInstanceName(unsigned len, bool sign);\n};\n\n/// Type of a vector of primitives\nclass VectorType : public AcceptorExtend<VectorType, PrimitiveType> {\nprivate:\n  /// number of elements\n  unsigned count;\n  /// base type\n  PrimitiveType *base;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a vector type.\n  /// @param count the number of elements\n  /// @param base the base type\n  VectorType(unsigned count, PrimitiveType *base)\n      : AcceptorExtend(getInstanceName(count, base)), count(count), base(base) {}\n\n  /// @return the count of the vector\n  unsigned getCount() const { return count; }\n  /// @return the base type of the vector\n  PrimitiveType *getBase() const { return base; }\n\n  static std::string getInstanceName(unsigned count, PrimitiveType *base);\n};\n\nclass UnionType : public AcceptorExtend<UnionType, Type> {\nprivate:\n  /// alternative types\n  std::vector<types::Type *> types;\n\npublic:\n  static const char NodeId;\n\n  using const_iterator = std::vector<types::Type *>::const_iterator;\n  using const_reference = std::vector<types::Type *>::const_reference;\n\n  /// Constructs a UnionType.\n  /// @param types the alternative types (must be sorted by caller)\n  explicit UnionType(std::vector<types::Type *> types)\n      : AcceptorExtend(), types(std::move(types)) {}\n\n  const_iterator begin() const { return types.begin(); }\n  const_iterator end() const { return types.end(); }\n  const_reference front() const { return types.front(); }\n  const_reference back() const { return types.back(); }\n\n  static std::string getInstanceName(const std::vector<types::Type *> &types);\n\nprivate:\n  std::vector<types::Type *> doGetUsedTypes() const override { return types; }\n\n  bool doIsAtomic() const override {\n    return !std::any_of(types.begin(), types.end(),\n                        [](auto *type) { return !type->isAtomic(); });\n  }\n};\n\n} // namespace types\n} // namespace ir\n} // namespace codon\n\ntemplate <> struct fmt::formatter<codon::ir::types::Type> : fmt::ostream_formatter {};\n"
  },
  {
    "path": "codon/cir/util/cloning.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"cloning.h\"\n\n#include \"codon/cir/util/operator.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace util {\nnamespace {\nstruct GatherLocals : public util::Operator {\n  std::vector<Var *> locals;\n  void preHook(Node *node) override {\n    for (auto *v : node->getUsedVariables()) {\n      if (!v->isGlobal())\n        locals.push_back(v);\n    }\n  }\n};\n} // namespace\n\nValue *CloneVisitor::clone(const Value *other, BodiedFunc *cloneTo,\n                           const std::unordered_map<id_t, Var *> &remaps) {\n  if (!other)\n    return nullptr;\n\n  if (cloneTo) {\n    auto *M = cloneTo->getModule();\n    GatherLocals gl;\n    const_cast<Value *>(other)->accept(gl);\n    for (auto *v : gl.locals) {\n      auto it = remaps.find(v->getId());\n      if (it != remaps.end()) {\n        forceRemap(v, it->second);\n      } else {\n        auto *clonedVar = M->N<Var>(v, v->getType(), v->isGlobal(), v->isExternal(),\n                                    v->isThreadLocal(), v->getName());\n        cloneTo->push_back(clonedVar);\n        forceRemap(v, clonedVar);\n      }\n    }\n  } else {\n    auto *M = other->getModule();\n    for (const auto &e : remaps) {\n      forceRemap(M->getVar(e.first), e.second);\n    }\n  }\n\n  auto id = other->getId();\n  if (ctx.find(id) == ctx.end()) {\n    other->accept(*this);\n    ctx[id] = result;\n\n    for (auto it = other->attributes_begin(); it != other->attributes_end(); ++it) {\n      const auto *attr = other->getAttribute(*it);\n      if (attr->needsClone()) {\n        ctx[id]->setAttribute(attr->clone(*this), *it);\n      }\n    }\n  }\n  return cast<Value>(ctx[id]);\n}\n\nVar *CloneVisitor::clone(const Var *other) {\n  if (!other)\n    return nullptr;\n  auto id = other->getId();\n  if (ctx.find(id) != ctx.end())\n    return cast<Var>(ctx[id]);\n\n  return const_cast<Var *>(other);\n}\n\nvoid CloneVisitor::visit(const Var *v) {\n  result = module->N<Var>(v, v->getType(), v->isGlobal(), v->isExternal(),\n                          v->isThreadLocal(), v->getName());\n}\n\nvoid CloneVisitor::visit(const BodiedFunc *v) {\n  auto *res = Nt(v);\n  std::vector<std::string> argNames;\n\n  for (auto it = v->arg_begin(); it != v->arg_end(); ++it)\n    argNames.push_back((*it)->getName());\n  for (const auto *var : *v) {\n    auto *newVar = forceClone(var);\n    res->push_back(newVar);\n  }\n  res->setUnmangledName(v->getUnmangledName());\n  res->setGenerator(v->isGenerator());\n  res->setAsync(v->isAsync());\n  res->realize(cast<types::FuncType>(v->getType()), argNames);\n\n  auto argIt1 = v->arg_begin();\n  auto argIt2 = res->arg_begin();\n  while (argIt1 != v->arg_end()) {\n    forceRemap(*argIt1, *argIt2);\n    ++argIt1;\n    ++argIt2;\n  }\n\n  // body might reference this!\n  forceRemap(v, res);\n\n  if (v->getBody())\n    res->setBody(clone(v->getBody()));\n  res->setJIT(v->isJIT());\n  result = res;\n}\n\nvoid CloneVisitor::visit(const ExternalFunc *v) {\n  auto *res = Nt(v);\n  std::vector<std::string> argNames;\n  for (auto it = v->arg_begin(); it != v->arg_end(); ++it)\n    argNames.push_back((*it)->getName());\n  res->setUnmangledName(v->getUnmangledName());\n  res->setGenerator(v->isGenerator());\n  res->setAsync(v->isAsync());\n  res->realize(cast<types::FuncType>(v->getType()), argNames);\n\n  auto argIt1 = v->arg_begin();\n  auto argIt2 = res->arg_begin();\n  while (argIt1 != v->arg_end()) {\n    forceRemap(*argIt1, *argIt2);\n    ++argIt1;\n    ++argIt2;\n  }\n\n  result = res;\n}\n\nvoid CloneVisitor::visit(const InternalFunc *v) {\n  auto *res = Nt(v);\n  std::vector<std::string> argNames;\n  for (auto it = v->arg_begin(); it != v->arg_end(); ++it)\n    argNames.push_back((*it)->getName());\n  res->setUnmangledName(v->getUnmangledName());\n  res->setGenerator(v->isGenerator());\n  res->setAsync(v->isAsync());\n  res->realize(cast<types::FuncType>(v->getType()), argNames);\n\n  auto argIt1 = v->arg_begin();\n  auto argIt2 = res->arg_begin();\n  while (argIt1 != v->arg_end()) {\n    forceRemap(*argIt1, *argIt2);\n    ++argIt1;\n    ++argIt2;\n  }\n\n  res->setParentType(v->getParentType());\n  result = res;\n}\n\nvoid CloneVisitor::visit(const LLVMFunc *v) {\n  auto *res = Nt(v);\n  std::vector<std::string> argNames;\n  for (auto it = v->arg_begin(); it != v->arg_end(); ++it)\n    argNames.push_back((*it)->getName());\n  res->setUnmangledName(v->getUnmangledName());\n  res->setGenerator(v->isGenerator());\n  res->setAsync(v->isAsync());\n  res->realize(cast<types::FuncType>(v->getType()), argNames);\n\n  auto argIt1 = v->arg_begin();\n  auto argIt2 = res->arg_begin();\n  while (argIt1 != v->arg_end()) {\n    forceRemap(*argIt1, *argIt2);\n    ++argIt1;\n    ++argIt2;\n  }\n\n  res->setLLVMBody(v->getLLVMBody());\n  res->setLLVMDeclarations(v->getLLVMDeclarations());\n  res->setLLVMLiterals(\n      std::vector<types::Generic>(v->literal_begin(), v->literal_end()));\n  result = res;\n}\n\nvoid CloneVisitor::visit(const VarValue *v) { result = Nt(v, clone(v->getVar())); }\n\nvoid CloneVisitor::visit(const PointerValue *v) {\n  result = Nt(v, clone(v->getVar()), v->getFields());\n}\n\nvoid CloneVisitor::visit(const SeriesFlow *v) {\n  auto *res = Nt(v);\n  for (auto *c : *v)\n    res->push_back(clone(c));\n  result = res;\n}\n\nvoid CloneVisitor::visit(const IfFlow *v) {\n  result =\n      Nt(v, clone(v->getCond()), clone(v->getTrueBranch()), clone(v->getFalseBranch()));\n}\n\nvoid CloneVisitor::visit(const WhileFlow *v) {\n  auto *loop = Nt(v, nullptr, nullptr);\n  forceRemap(v, loop);\n  loop->setCond(clone(v->getCond()));\n  loop->setBody(clone(v->getBody()));\n  result = loop;\n}\n\nvoid CloneVisitor::visit(const ForFlow *v) {\n  auto *loop = Nt(v, nullptr, nullptr, nullptr,\n                  std::unique_ptr<transform::parallel::OMPSched>(), false);\n  forceRemap(v, loop);\n  loop->setIter(clone(v->getIter()));\n  loop->setBody(clone(v->getBody()));\n  loop->setVar(clone(v->getVar()));\n  if (auto *sched = v->getSchedule()) {\n    auto schedCloned = std::make_unique<transform::parallel::OMPSched>(*sched);\n    for (auto *val : sched->getUsedValues()) {\n      schedCloned->replaceUsedValue(val->getId(), clone(val));\n    }\n    loop->setSchedule(std::move(schedCloned));\n  }\n  loop->setAsync(v->isAsync());\n\n  result = loop;\n}\n\nvoid CloneVisitor::visit(const ImperativeForFlow *v) {\n  auto *loop = Nt(v, nullptr, v->getStep(), nullptr, nullptr, nullptr,\n                  std::unique_ptr<transform::parallel::OMPSched>());\n  forceRemap(v, loop);\n  loop->setStart(clone(v->getStart()));\n  loop->setBody(clone(v->getBody()));\n  loop->setVar(clone(v->getVar()));\n  loop->setEnd(clone(v->getEnd()));\n  if (auto *sched = v->getSchedule()) {\n    auto schedCloned = std::make_unique<transform::parallel::OMPSched>(*sched);\n    for (auto *val : sched->getUsedValues()) {\n      schedCloned->replaceUsedValue(val->getId(), clone(val));\n    }\n    loop->setSchedule(std::move(schedCloned));\n  }\n  result = loop;\n}\n\nvoid CloneVisitor::visit(const TryCatchFlow *v) {\n  auto *res = Nt(v, clone(v->getBody()), clone(v->getFinally()), clone(v->getElse()));\n  for (auto &c : *v) {\n    res->emplace_back(clone(c.getHandler()), c.getType(), clone(c.getVar()));\n  }\n  result = res;\n}\n\nvoid CloneVisitor::visit(const PipelineFlow *v) {\n  std::vector<PipelineFlow::Stage> cloned;\n  for (const auto &s : *v) {\n    cloned.push_back(clone(s));\n  }\n  result = Nt(v, std::move(cloned));\n}\n\nvoid CloneVisitor::visit(const dsl::CustomFlow *v) { result = v->doClone(*this); }\n\nvoid CloneVisitor::visit(const IntConst *v) {\n  result = Nt(v, v->getVal(), v->getType());\n}\n\nvoid CloneVisitor::visit(const FloatConst *v) {\n  result = Nt(v, v->getVal(), v->getType());\n}\n\nvoid CloneVisitor::visit(const BoolConst *v) {\n  result = Nt(v, v->getVal(), v->getType());\n}\n\nvoid CloneVisitor::visit(const StringConst *v) {\n  result = Nt(v, v->getVal(), v->getType());\n}\n\nvoid CloneVisitor::visit(const dsl::CustomConst *v) { result = v->doClone(*this); }\n\nvoid CloneVisitor::visit(const AssignInstr *v) {\n  result = Nt(v, clone(v->getLhs()), clone(v->getRhs()));\n}\n\nvoid CloneVisitor::visit(const ExtractInstr *v) {\n  result = Nt(v, clone(v->getVal()), v->getField());\n}\n\nvoid CloneVisitor::visit(const InsertInstr *v) {\n  result = Nt(v, clone(v->getLhs()), v->getField(), clone(v->getRhs()));\n}\n\nvoid CloneVisitor::visit(const CallInstr *v) {\n  std::vector<Value *> args;\n  for (const auto *a : *v)\n    args.push_back(clone(a));\n  result = Nt(v, clone(v->getCallee()), std::move(args));\n}\n\nvoid CloneVisitor::visit(const StackAllocInstr *v) {\n  result = Nt(v, v->getArrayType(), v->getCount());\n}\n\nvoid CloneVisitor::visit(const TypePropertyInstr *v) {\n  result = Nt(v, v->getInspectType(), v->getProperty());\n}\n\nvoid CloneVisitor::visit(const YieldInInstr *v) {\n  result = Nt(v, v->getType(), v->isSuspending());\n}\n\nvoid CloneVisitor::visit(const TernaryInstr *v) {\n  result =\n      Nt(v, clone(v->getCond()), clone(v->getTrueValue()), clone(v->getFalseValue()));\n}\n\nvoid CloneVisitor::visit(const BreakInstr *v) {\n  result = Nt(v, cloneLoop ? clone(v->getLoop()) : v->getLoop());\n}\n\nvoid CloneVisitor::visit(const ContinueInstr *v) {\n  result = Nt(v, cloneLoop ? clone(v->getLoop()) : v->getLoop());\n}\n\nvoid CloneVisitor::visit(const ReturnInstr *v) { result = Nt(v, clone(v->getValue())); }\n\nvoid CloneVisitor::visit(const YieldInstr *v) {\n  result = Nt(v, clone(v->getValue()), v->isFinal());\n}\n\nvoid CloneVisitor::visit(const AwaitInstr *v) {\n  result = Nt(v, clone(v->getValue()), v->getType(), v->isGenerator());\n}\n\nvoid CloneVisitor::visit(const ThrowInstr *v) { result = Nt(v, clone(v->getValue())); }\n\nvoid CloneVisitor::visit(const FlowInstr *v) {\n  result = Nt(v, clone(v->getFlow()), clone(v->getValue()));\n}\n\nvoid CloneVisitor::visit(const dsl::CustomInstr *v) { result = v->doClone(*this); }\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/cloning.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <unordered_map>\n\n#include \"codon/cir/cir.h\"\n#include \"codon/cir/util/visitor.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\nclass CloneVisitor : public ConstVisitor {\nprivate:\n  /// the clone context\n  std::unordered_map<int, Node *> ctx;\n  /// the result\n  Node *result;\n  /// the module\n  Module *module;\n  /// true if break/continue loops should be cloned\n  bool cloneLoop;\n\npublic:\n  /// Constructs a clone visitor.\n  /// @param module the module\n  /// @param cloneLoop true if break/continue loops should be cloned\n  explicit CloneVisitor(Module *module, bool cloneLoop = true)\n      : ctx(), result(nullptr), module(module), cloneLoop(cloneLoop) {}\n\n  virtual ~CloneVisitor() noexcept = default;\n\n  void visit(const Var *v) override;\n\n  void visit(const BodiedFunc *v) override;\n  void visit(const ExternalFunc *v) override;\n  void visit(const InternalFunc *v) override;\n  void visit(const LLVMFunc *v) override;\n\n  void visit(const VarValue *v) override;\n  void visit(const PointerValue *v) override;\n\n  void visit(const SeriesFlow *v) override;\n  void visit(const IfFlow *v) override;\n  void visit(const WhileFlow *v) override;\n  void visit(const ForFlow *v) override;\n  void visit(const ImperativeForFlow *v) override;\n  void visit(const TryCatchFlow *v) override;\n  void visit(const PipelineFlow *v) override;\n  void visit(const dsl::CustomFlow *v) override;\n\n  void visit(const IntConst *v) override;\n  void visit(const FloatConst *v) override;\n  void visit(const BoolConst *v) override;\n  void visit(const StringConst *v) override;\n  void visit(const dsl::CustomConst *v) override;\n\n  void visit(const AssignInstr *v) override;\n  void visit(const ExtractInstr *v) override;\n  void visit(const InsertInstr *v) override;\n  void visit(const CallInstr *v) override;\n  void visit(const StackAllocInstr *v) override;\n  void visit(const TypePropertyInstr *v) override;\n  void visit(const YieldInInstr *v) override;\n  void visit(const TernaryInstr *v) override;\n  void visit(const BreakInstr *v) override;\n  void visit(const ContinueInstr *v) override;\n  void visit(const ReturnInstr *v) override;\n  void visit(const YieldInstr *v) override;\n  void visit(const AwaitInstr *v) override;\n  void visit(const ThrowInstr *v) override;\n  void visit(const FlowInstr *v) override;\n  void visit(const dsl::CustomInstr *v) override;\n\n  /// Clones a value, returning the previous value if other has already been cloned.\n  /// @param other the original\n  /// @param cloneTo the function to clone locals to, or null if none\n  /// @param remaps variable re-mappings\n  /// @return the clone\n  Value *clone(const Value *other, BodiedFunc *cloneTo = nullptr,\n               const std::unordered_map<id_t, Var *> &remaps = {});\n\n  /// Returns the original unless the variable has been force cloned.\n  /// @param other the original\n  /// @return the original or the previous clone\n  Var *clone(const Var *other);\n\n  /// Clones a flow, returning the previous value if other has already been cloned.\n  /// @param other the original\n  /// @return the clone\n  Flow *clone(const Flow *other) {\n    return cast<Flow>(clone(static_cast<const Value *>(other)));\n  }\n\n  /// Forces a clone. No difference for values but ensures that variables are actually\n  /// cloned.\n  /// @param other the original\n  /// @return the clone\n  template <typename NodeType> NodeType *forceClone(const NodeType *other) {\n    if (!other)\n      return nullptr;\n\n    auto id = other->getId();\n    if (ctx.find(id) == ctx.end()) {\n      other->accept(*this);\n      ctx[id] = result;\n\n      for (auto it = other->attributes_begin(); it != other->attributes_end(); ++it) {\n        const auto *attr = other->getAttribute(*it);\n        if (attr->needsClone()) {\n          ctx[id]->setAttribute(attr->forceClone(*this), *it);\n        }\n      }\n    }\n    return cast<NodeType>(ctx[id]);\n  }\n\n  /// Remaps a clone.\n  /// @param original the original\n  /// @param newVal the clone\n  template <typename NodeType>\n  void forceRemap(const NodeType *original, const NodeType *newVal) {\n    ctx[original->getId()] = const_cast<NodeType *>(newVal);\n  }\n\n  PipelineFlow::Stage clone(const PipelineFlow::Stage &other) {\n    std::vector<Value *> args;\n    for (const auto *a : other)\n      args.push_back(clone(a));\n    return {clone(other.getCallee()), std::move(args), other.isGenerator(),\n            other.isParallel()};\n  }\n\nprivate:\n  template <typename NodeType, typename... Args>\n  NodeType *Nt(const NodeType *source, Args... args) {\n    return module->N<NodeType>(source, std::forward<Args>(args)..., source->getName());\n  }\n};\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/context.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <vector>\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\n/// Base for CIR visitor contexts.\ntemplate <typename Frame> class CIRContext {\nprivate:\n  std::vector<Frame> frames;\n\npublic:\n  /// Emplaces a frame onto the stack.\n  /// @param args a parameter pack of the arguments\n  template <typename... Args> void emplaceFrame(Args... args) {\n    frames.emplace_back(args...);\n  }\n  /// Replaces a frame.\n  /// @param newFrame the new frame\n  void replaceFrame(Frame newFrame) {\n    frames.pop_back();\n    frames.push_back(newFrame);\n  }\n  /// @return all frames\n  std::vector<Frame> &getFrames() { return frames; }\n  /// @return the current frame\n  Frame &getFrame() { return frames.back(); }\n  /// Pops a frame.\n  void popFrame() { return frames.pop_back(); }\n};\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/format.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <algorithm>\n#include <fmt/format.h>\n#include <fmt/ostream.h>\n#include <sstream>\n#include <unordered_set>\n\n#include \"codon/cir/util/format.h\"\n#include \"codon/cir/util/visitor.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\nstruct NodeFormatter {\n  const types::Type *type = nullptr;\n  const Value *value = nullptr;\n  const Var *var = nullptr;\n  bool canShowFull = false;\n\n  std::unordered_set<id_t> &seenNodes;\n  std::unordered_set<std::string> &seenTypes;\n\n  NodeFormatter(const types::Type *type, std::unordered_set<id_t> &seenNodes,\n                std::unordered_set<std::string> &seenTypes)\n      : type(type), seenNodes(seenNodes), seenTypes(seenTypes) {}\n\n  NodeFormatter(const Value *value, std::unordered_set<id_t> &seenNodes,\n                std::unordered_set<std::string> &seenTypes)\n      : value(value), seenNodes(seenNodes), seenTypes(seenTypes) {}\n  NodeFormatter(const Var *var, std::unordered_set<id_t> &seenNodes,\n                std::unordered_set<std::string> &seenTypes)\n      : var(var), seenNodes(seenNodes), seenTypes(seenTypes) {}\n\n  friend std::ostream &operator<<(std::ostream &os, const NodeFormatter &n);\n};\n\nnamespace {\nstd::string escapeString(const std::string &str) {\n  std::stringstream escaped;\n  for (char c : str) {\n    switch (c) {\n    case '\\a':\n      escaped << \"\\\\a\";\n      break;\n    case '\\b':\n      escaped << \"\\\\b\";\n      break;\n    case '\\f':\n      escaped << \"\\\\f\";\n      break;\n    case '\\n':\n      escaped << \"\\\\n\";\n      break;\n    case '\\r':\n      escaped << \"\\\\r\";\n      break;\n    case '\\t':\n      escaped << \"\\\\t\";\n      break;\n    case '\\v':\n      escaped << \"\\\\v\";\n      break;\n    case '\\\\':\n      escaped << \"\\\\\\\\\";\n      break;\n    case '\\'':\n      escaped << \"\\\\'\";\n      break;\n    case '\\\"':\n      escaped << \"\\\\\\\"\";\n      break;\n    case '\\?':\n      escaped << \"\\\\\\?\";\n      break;\n    default:\n      escaped << c;\n    }\n  }\n  return escaped.str();\n}\n\nclass FormatVisitor : util::ConstVisitor {\nprivate:\n  std::ostream &os;\n  std::unordered_set<id_t> &seenNodes;\n  std::unordered_set<std::string> &seenTypes;\n\npublic:\n  FormatVisitor(std::ostream &os, std::unordered_set<id_t> &seenNodes,\n                std::unordered_set<std::string> &seenTypes)\n      : os(os), seenNodes(seenNodes), seenTypes(seenTypes) {}\n  virtual ~FormatVisitor() noexcept = default;\n\n  void visit(const Module *v) override {\n    auto types = makeFormatters(v->types_begin(), v->types_end(), true);\n    auto vars = makeFormatters(v->begin(), v->end(), true);\n    fmt::print(os, FMT_STRING(\"(module\\n(argv {})\\n(types {})\\n(vars {})\\n{})\"),\n               makeFormatter(v->getArgVar(), true),\n               fmt::join(types.begin(), types.end(), \"\\n\"),\n               fmt::join(vars.begin(), vars.end(), \"\\n\"),\n               makeFormatter(v->getMainFunc(), true));\n  }\n\n  void defaultVisit(const Node *) override { os << \"(unknown_node)\"; }\n\n  void visit(const Var *v) override {\n    fmt::print(\n        os, FMT_STRING(\"(var '\\\"{}\\\" {} (global {}) (external {}) (thread-local {}))\"),\n        v->referenceString(), makeFormatter(v->getType()), v->isGlobal(),\n        v->isExternal(), v->isThreadLocal());\n  }\n\n  void visit(const BodiedFunc *v) override {\n    auto args = makeFormatters(v->arg_begin(), v->arg_end(), true);\n    auto symbols = makeFormatters(v->begin(), v->end(), true);\n    fmt::print(os, FMT_STRING(\"(bodied_func '\\\"{}\\\" {}\\n(args {})\\n(vars {})\\n{})\"),\n               v->referenceString(), makeFormatter(v->getType()),\n               fmt::join(args.begin(), args.end(), \" \"),\n               fmt::join(symbols.begin(), symbols.end(), \" \"),\n               makeFormatter(v->getBody()));\n  }\n  void visit(const ExternalFunc *v) override {\n    fmt::print(os, FMT_STRING(\"(external_func '\\\"{}\\\" {})\"), v->referenceString(),\n               makeFormatter(v->getType()));\n  }\n  void visit(const InternalFunc *v) override {\n    fmt::print(os, FMT_STRING(\"(internal_func '\\\"{}\\\" {})\"), v->referenceString(),\n               makeFormatter(v->getType()));\n  }\n  void visit(const LLVMFunc *v) override {\n    std::vector<std::string> literals;\n\n    for (auto it = v->literal_begin(); it != v->literal_end(); ++it) {\n      const auto &l = *it;\n      if (l.isStatic()) {\n        literals.push_back(fmt::format(FMT_STRING(\"(static {})\"), l.getStaticValue()));\n      } else if (l.isStaticStr()) {\n        literals.push_back(\n            fmt::format(FMT_STRING(\"(static \\\"{}\\\")\"), l.getStaticStringValue()));\n      } else {\n        literals.push_back(\n            fmt::format(FMT_STRING(\"(type {})\"), makeFormatter(l.getTypeValue())));\n      }\n    }\n\n    fmt::print(os,\n               FMT_STRING(\"(llvm_func '\\\"{}\\\" {}\\n(decls \\\"{}\\\")\\n\"\n                          \"\\\"{}\\\"\\n(literals {}))\"),\n               v->referenceString(), makeFormatter(v->getType()),\n               escapeString(v->getLLVMDeclarations()), escapeString(v->getLLVMBody()),\n               fmt::join(literals.begin(), literals.end(), \"\\n\"));\n  }\n\n  void visit(const VarValue *v) override {\n    fmt::print(os, FMT_STRING(\"'\\\"{}\\\"\"), v->getVar()->referenceString());\n  }\n  void visit(const PointerValue *v) override {\n    fmt::print(os, FMT_STRING(\"(ptr '\\\"{}\\\" \\\"{}\\\")\"), v->getVar()->referenceString(),\n               fmt::join(v->getFields().begin(), v->getFields().end(), \".\"));\n  }\n\n  void visit(const SeriesFlow *v) override {\n    auto series = makeFormatters(v->begin(), v->end());\n    fmt::print(os, FMT_STRING(\"(series\\n{}\\n)\"),\n               fmt::join(series.begin(), series.end(), \"\\n\"));\n  }\n  void visit(const IfFlow *v) override {\n    fmt::print(os, FMT_STRING(\"(if {}\\n{}\\n{}\\n)\"), makeFormatter(v->getCond()),\n               makeFormatter(v->getTrueBranch()), makeFormatter(v->getFalseBranch()));\n  }\n  void visit(const WhileFlow *v) override {\n    fmt::print(os, FMT_STRING(\"(while {}\\n{}\\n)\"), makeFormatter(v->getCond()),\n               makeFormatter(v->getBody()));\n  }\n  void visit(const ForFlow *v) override {\n    fmt::print(os, FMT_STRING(\"({}{}for {}\\n{}\\n{}\\n)\"), v->isParallel() ? \"par_\" : \"\",\n               v->isAsync() ? \"async_\" : \"\", makeFormatter(v->getIter()),\n               makeFormatter(v->getVar()), makeFormatter(v->getBody()));\n  }\n  void visit(const ImperativeForFlow *v) override {\n    fmt::print(os, FMT_STRING(\"({}imp_for {}\\n{}\\n{}\\n{}\\n{}\\n)\"),\n               v->isParallel() ? \"par_\" : \"\", makeFormatter(v->getStart()),\n               v->getStep(), makeFormatter(v->getEnd()), makeFormatter(v->getVar()),\n               makeFormatter(v->getBody()));\n  }\n  void visit(const TryCatchFlow *v) override {\n    std::vector<std::string> catches;\n\n    for (auto &c : *v) {\n      catches.push_back(\n          fmt::format(FMT_STRING(\"(catch {} {}\\n{}\\n)\"), makeFormatter(c.getType()),\n                      makeFormatter(c.getVar()), makeFormatter(c.getHandler())));\n    }\n\n    fmt::print(os, FMT_STRING(\"(try {}\\n{}\\n(else\\n{}\\n)\\n(finally\\n{})\\n)\"),\n               makeFormatter(v->getBody()),\n               fmt::join(catches.begin(), catches.end(), \"\\n\"),\n               makeFormatter(v->getElse()), makeFormatter(v->getFinally()));\n  }\n  void visit(const PipelineFlow *v) override {\n    std::vector<std::string> stages;\n    for (const auto &s : *v) {\n      auto args = makeFormatters(s.begin(), s.end());\n      stages.push_back(fmt::format(\n          FMT_STRING(\"(stage {} {}\\n(generator {})\\n(parallel {}))\"),\n          makeFormatter(s.getCallee()), fmt::join(args.begin(), args.end(), \"\\n\"),\n          s.isGenerator(), s.isParallel()));\n    }\n    fmt::print(os, FMT_STRING(\"(pipeline {})\"),\n               fmt::join(stages.begin(), stages.end(), \"\\n\"));\n  }\n  void visit(const dsl::CustomFlow *v) override { v->doFormat(os); }\n\n  void visit(const IntConst *v) override {\n    fmt::print(os, FMT_STRING(\"{}\"), v->getVal());\n  }\n  void visit(const FloatConst *v) override {\n    fmt::print(os, FMT_STRING(\"{}\"), v->getVal());\n  }\n  void visit(const BoolConst *v) override {\n    fmt::print(os, FMT_STRING(\"{}\"), v->getVal());\n  }\n  void visit(const StringConst *v) override {\n    fmt::print(os, FMT_STRING(\"\\\"{}\\\"\"), escapeString(v->getVal()));\n  }\n  void visit(const dsl::CustomConst *v) override { v->doFormat(os); }\n\n  void visit(const AssignInstr *v) override {\n    fmt::print(os, FMT_STRING(\"(assign {} {})\"), makeFormatter(v->getLhs()),\n               makeFormatter(v->getRhs()));\n  }\n  void visit(const ExtractInstr *v) override {\n    fmt::print(os, FMT_STRING(\"(extract {} \\\"{}\\\")\"), makeFormatter(v->getVal()),\n               v->getField());\n  }\n  void visit(const InsertInstr *v) override {\n    fmt::print(os, FMT_STRING(\"(insert {} \\\"{}\\\" {})\"), makeFormatter(v->getLhs()),\n               v->getField(), makeFormatter(v->getRhs()));\n  }\n  void visit(const CallInstr *v) override {\n    auto args = makeFormatters(v->begin(), v->end());\n    fmt::print(os, FMT_STRING(\"(call {}\\n{}\\n)\"), makeFormatter(v->getCallee()),\n               fmt::join(args.begin(), args.end(), \"\\n\"));\n  }\n  void visit(const StackAllocInstr *v) override {\n    fmt::print(os, FMT_STRING(\"(stack_alloc {} {})\"), makeFormatter(v->getArrayType()),\n               v->getCount());\n  }\n  void visit(const TypePropertyInstr *v) override {\n    std::string property;\n    if (v->getProperty() == TypePropertyInstr::Property::IS_ATOMIC) {\n      property = \"atomic\";\n    } else if (v->getProperty() == TypePropertyInstr::Property::SIZEOF) {\n      property = \"sizeof\";\n    } else {\n      property = \"unknown\";\n    }\n    fmt::print(os, FMT_STRING(\"(property {} {})\"), property,\n               makeFormatter(v->getInspectType()));\n  }\n  void visit(const YieldInInstr *v) override {\n    fmt::print(os, FMT_STRING(\"(yield_in {})\"), makeFormatter(v->getType()));\n  }\n  void visit(const TernaryInstr *v) override {\n    fmt::print(os, FMT_STRING(\"(select {}\\n{}\\n{}\\n)\"), makeFormatter(v->getCond()),\n               makeFormatter(v->getTrueValue()), makeFormatter(v->getFalseValue()));\n  }\n  void visit(const BreakInstr *v) override {\n    os << \"(break \" << (v->getLoop() ? v->getLoop()->getId() : -1) << ')';\n  }\n  void visit(const ContinueInstr *v) override {\n    os << \"(continue \" << (v->getLoop() ? v->getLoop()->getId() : -1) << ')';\n  }\n  void visit(const ReturnInstr *v) override {\n    fmt::print(os, FMT_STRING(\"(return {})\"), makeFormatter(v->getValue()));\n  }\n  void visit(const YieldInstr *v) override {\n    fmt::print(os, FMT_STRING(\"(yield {})\"), makeFormatter(v->getValue()));\n  }\n  void visit(const AwaitInstr *v) override {\n    fmt::print(os, FMT_STRING(\"(await {} {} {})\"), makeFormatter(v->getType()),\n               makeFormatter(v->getValue()), v->isGenerator());\n  }\n  void visit(const ThrowInstr *v) override {\n    fmt::print(os, FMT_STRING(\"(throw {})\"), makeFormatter(v->getValue()));\n  }\n  void visit(const FlowInstr *v) override {\n    fmt::print(os, FMT_STRING(\"(flow {} {})\"), makeFormatter(v->getFlow()),\n               makeFormatter(v->getValue()));\n  }\n  void visit(const dsl::CustomInstr *v) override { v->doFormat(os); }\n\n  void visit(const types::IntType *v) override {\n    fmt::print(os, FMT_STRING(\"(int '\\\"{}\\\")\"), v->referenceString());\n  }\n  void visit(const types::FloatType *v) override {\n    fmt::print(os, FMT_STRING(\"(float '\\\"{}\\\")\"), v->referenceString());\n  }\n  void visit(const types::Float32Type *v) override {\n    fmt::print(os, FMT_STRING(\"(float32 '\\\"{}\\\")\"), v->referenceString());\n  }\n  void visit(const types::Float16Type *v) override {\n    fmt::print(os, FMT_STRING(\"(float16 '\\\"{}\\\")\"), v->referenceString());\n  }\n  void visit(const types::BFloat16Type *v) override {\n    fmt::print(os, FMT_STRING(\"(bfloat16 '\\\"{}\\\")\"), v->referenceString());\n  }\n  void visit(const types::Float128Type *v) override {\n    fmt::print(os, FMT_STRING(\"(float128 '\\\"{}\\\")\"), v->referenceString());\n  }\n  void visit(const types::BoolType *v) override {\n    fmt::print(os, FMT_STRING(\"(bool '\\\"{}\\\")\"), v->referenceString());\n  }\n  void visit(const types::ByteType *v) override {\n    fmt::print(os, FMT_STRING(\"(byte '\\\"{}\\\")\"), v->referenceString());\n  }\n  void visit(const types::VoidType *v) override {\n    fmt::print(os, FMT_STRING(\"(void '\\\"{}\\\")\"), v->referenceString());\n  }\n  void visit(const types::RecordType *v) override {\n    std::vector<std::string> fields;\n    std::vector<NodeFormatter> formatters;\n    for (const auto &m : *v) {\n      fields.push_back(fmt::format(FMT_STRING(\"(\\\"{}\\\" {})\"), m.getName(),\n                                   makeFormatter(m.getType())));\n    }\n\n    fmt::print(os, FMT_STRING(\"(record '\\\"{}\\\" {})\"), v->referenceString(),\n               fmt::join(fields.begin(), fields.end(), \" \"));\n  }\n  void visit(const types::RefType *v) override {\n    fmt::print(os, FMT_STRING(\"(ref '\\\"{}\\\" {})\"), v->referenceString(),\n               makeFormatter(v->getContents()));\n  }\n  void visit(const types::FuncType *v) override {\n    auto args = makeFormatters(v->begin(), v->end());\n    fmt::print(os, FMT_STRING(\"(func '\\\"{}\\\" {}{} {})\"), v->referenceString(),\n               fmt::join(args.begin(), args.end(), \" \"),\n               (v->isVariadic() ? \" ...\" : \"\"), makeFormatter(v->getReturnType()));\n  }\n  void visit(const types::OptionalType *v) override {\n    fmt::print(os, FMT_STRING(\"(optional '\\\"{}\\\" {})\"), v->referenceString(),\n               makeFormatter(v->getBase()));\n  }\n  void visit(const types::PointerType *v) override {\n    fmt::print(os, FMT_STRING(\"(pointer '\\\"{}\\\" {})\"), v->referenceString(),\n               makeFormatter(v->getBase()));\n  }\n  void visit(const types::GeneratorType *v) override {\n    fmt::print(os, FMT_STRING(\"(generator '\\\"{}\\\" {})\"), v->referenceString(),\n               makeFormatter(v->getBase()));\n  }\n  void visit(const types::IntNType *v) override {\n    fmt::print(os, FMT_STRING(\"(intn '\\\"{}\\\" {} (signed {}))\"), v->referenceString(),\n               v->getLen(), v->isSigned());\n  }\n  void visit(const types::VectorType *v) override {\n    fmt::print(os, FMT_STRING(\"(vector '\\\"{}\\\" {} (count {}))\"), v->referenceString(),\n               makeFormatter(v->getBase()), v->getCount());\n  }\n  void visit(const types::UnionType *v) override {\n    auto types = makeFormatters(v->begin(), v->end());\n    fmt::print(os, FMT_STRING(\"(union '\\\"{}\\\" {})\"), v->referenceString(),\n               fmt::join(types.begin(), types.end(), \" \"));\n  }\n  void visit(const dsl::types::CustomType *v) override { v->doFormat(os); }\n\n  void format(const Node *n) {\n    if (n)\n      n->accept(*this);\n    else\n      os << \"(null)\";\n  }\n\n  void format(const types::Type *t, bool canShowFull = false) {\n    if (t) {\n      if (seenTypes.find(t->getName()) != seenTypes.end() || !canShowFull)\n        fmt::print(os, FMT_STRING(\"(type '\\\"{}\\\")\"), t->referenceString());\n      else {\n        seenTypes.insert(t->getName());\n        t->accept(*this);\n      }\n    } else\n      os << \"(null)\";\n  }\n\n  void format(const Value *t) {\n    if (t) {\n      if (seenNodes.find(t->getId()) != seenNodes.end())\n        fmt::print(os, FMT_STRING(\"(value '\\\"{}\\\")\"), t->referenceString());\n      else {\n        seenNodes.insert(t->getId());\n        t->accept(*this);\n      }\n\n    } else\n      os << \"(null)\";\n  }\n\n  void format(const Var *t, bool canShowFull = false) {\n    if (t) {\n      if (seenNodes.find(t->getId()) != seenNodes.end() || !canShowFull)\n        fmt::print(os, FMT_STRING(\"(var '\\\"{}\\\")\"), t->referenceString());\n      else {\n        seenNodes.insert(t->getId());\n        t->accept(*this);\n      }\n    } else\n      os << \"(null)\";\n  }\n\nprivate:\n  NodeFormatter makeFormatter(const types::Type *node, bool canShowFull = false) {\n    auto ret = NodeFormatter(node, seenNodes, seenTypes);\n    ret.canShowFull = canShowFull;\n    return ret;\n  }\n  NodeFormatter makeFormatter(const Value *node) {\n    return NodeFormatter(node, seenNodes, seenTypes);\n  }\n  NodeFormatter makeFormatter(const Var *node, bool canShowFull = false) {\n    auto ret = NodeFormatter(node, seenNodes, seenTypes);\n    ret.canShowFull = canShowFull;\n    return ret;\n  }\n\n  template <typename It> std::vector<NodeFormatter> makeFormatters(It begin, It end) {\n    std::vector<NodeFormatter> ret;\n    while (begin != end) {\n      ret.push_back(makeFormatter(*begin));\n      ++begin;\n    }\n    return ret;\n  }\n  template <typename It>\n  std::vector<NodeFormatter> makeFormatters(It begin, It end, bool canShowFull) {\n    std::vector<NodeFormatter> ret;\n    while (begin != end) {\n      ret.push_back(makeFormatter(*begin, canShowFull));\n      ++begin;\n    }\n    return ret;\n  }\n};\n} // namespace\n\nstd::ostream &operator<<(std::ostream &os, const NodeFormatter &n) {\n  FormatVisitor fv(os, n.seenNodes, n.seenTypes);\n  if (n.type)\n    fv.format(n.type, n.canShowFull);\n  else if (n.value)\n    fv.format(n.value);\n  else\n    fv.format(n.var, n.canShowFull);\n  return os;\n}\n\nstd::string format(const Node *node) {\n  std::stringstream ss;\n  format(ss, node);\n  return ss.str();\n}\n\nstd::ostream &format(std::ostream &os, const Node *node) {\n  std::unordered_set<id_t> seenNodes;\n  std::unordered_set<std::string> seenTypes;\n\n  FormatVisitor fv(os, seenNodes, seenTypes);\n  fv.format(node);\n\n  return os;\n}\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n\ntemplate <>\nstruct fmt::formatter<codon::ir::util::NodeFormatter> : ostream_formatter {};\n"
  },
  {
    "path": "codon/cir/util/format.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <iostream>\n\n#include \"codon/cir/cir.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\n/// Formats an IR node.\n/// @param node the node\n/// @return the formatted node\nstd::string format(const Node *node);\n\n/// Formats an IR node to an IO stream.\n/// @param os the output stream\n/// @param node the node\n/// @return the resulting output stream\nstd::ostream &format(std::ostream &os, const Node *node);\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/inlining.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"inlining.h\"\n\n#include <algorithm>\n\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/cir/util/operator.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\nnamespace {\n\nclass ReturnVerifier : public util::Operator {\npublic:\n  bool needLoop = false;\n\n  void handle(ReturnInstr *v) {\n    if (needLoop) {\n      return;\n    }\n\n    auto it = parent_begin();\n    if (it == parent_end()) {\n      needLoop = true;\n      return;\n    }\n\n    SeriesFlow *prev = nullptr;\n    while (it != parent_end()) {\n      Value *v = cast<Value>(*it++);\n      auto *cur = cast<SeriesFlow>(v);\n      if (!cur || (prev && prev->back()->getId() != cur->getId())) {\n        needLoop = true;\n        return;\n      }\n      prev = cur;\n    }\n    needLoop = prev->back()->getId() != v->getId();\n  }\n};\n\nclass ReturnReplacer : public util::Operator {\nprivate:\n  Value *implicitLoop;\n  Var *var;\n  bool aggressive;\n  util::CloneVisitor &cv;\n\npublic:\n  ReturnReplacer(Value *implicitLoop, Var *var, bool aggressive, util::CloneVisitor &cv)\n      : implicitLoop(implicitLoop), var(var), aggressive(aggressive), cv(cv) {}\n\n  void handle(ReturnInstr *v) {\n    auto *M = v->getModule();\n    auto *rep = M->N<SeriesFlow>(v);\n    if (var) {\n      rep->push_back(M->N<AssignInstr>(v, var, cv.clone(v->getValue())));\n    }\n    if (aggressive)\n      rep->push_back(M->N<BreakInstr>(v, implicitLoop));\n\n    v->replaceAll(rep);\n  }\n};\n\n} // namespace\n\nInlineResult inlineFunction(Func *func, std::vector<Value *> args, bool aggressive,\n                            codon::SrcInfo info) {\n  auto *bodied = cast<BodiedFunc>(func);\n  if (!bodied)\n    return {nullptr, {}};\n  auto *fType = cast<types::FuncType>(bodied->getType());\n  if (!fType || args.size() != std::distance(bodied->arg_begin(), bodied->arg_end()))\n    return {nullptr, {}};\n  auto *M = bodied->getModule();\n\n  util::CloneVisitor cv(M);\n  auto *newFlow = M->N<SeriesFlow>(info, bodied->getName() + \"_inlined\");\n\n  std::vector<Var *> newVars;\n  auto arg_it = bodied->arg_begin();\n  for (auto i = 0; i < args.size(); ++i) {\n    newVars.push_back(cv.forceClone(*arg_it++));\n    newFlow->push_back(M->N<AssignInstr>(info, newVars.back(), cv.clone(args[i])));\n  }\n  for (auto *v : *bodied) {\n    newVars.push_back(cv.forceClone(v));\n  }\n  Var *retVal = nullptr;\n  if (!fType->getReturnType()->is(M->getVoidType()) &&\n      !fType->getReturnType()->is(M->getNoneType())) {\n    retVal = M->N<Var>(info, fType->getReturnType());\n    newVars.push_back(retVal);\n  }\n\n  Flow *clonedBody = cv.clone(bodied->getBody());\n\n  ReturnVerifier rv;\n  rv.process(clonedBody);\n\n  if (!aggressive && rv.needLoop)\n    return {nullptr, {}};\n\n  WhileFlow *implicit = nullptr;\n  if (rv.needLoop) {\n    auto *loopBody = M->N<SeriesFlow>(info);\n    implicit = M->N<WhileFlow>(info, M->getBool(true), loopBody);\n    loopBody->push_back(clonedBody);\n    if (!retVal)\n      loopBody->push_back(M->N<BreakInstr>(info, implicit));\n  }\n\n  ReturnReplacer rr(implicit, retVal, rv.needLoop, cv);\n  rr.process(clonedBody);\n\n  newFlow->push_back(implicit ? implicit : clonedBody);\n\n  if (retVal) {\n    return {M->N<FlowInstr>(info, newFlow, M->N<VarValue>(info, retVal)),\n            std::move(newVars)};\n  }\n  return {newFlow, std::move(newVars)};\n}\n\nInlineResult inlineCall(CallInstr *v, bool aggressive) {\n  return inlineFunction(util::getFunc(v->getCallee()),\n                        std::vector<Value *>(v->begin(), v->end()), aggressive,\n                        v->getSrcInfo());\n}\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/inlining.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/cir.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\n/// Result of an inlining operation.\nstruct InlineResult {\n  /// the result, either a SeriesFlow or FlowInstr\n  Value *result;\n  /// variables added by the inlining\n  std::vector<Var *> newVars;\n\n  operator bool() const { return bool(result); }\n};\n\n/// Inline the given function with the supplied arguments.\n/// @param func the function\n/// @param args the arguments\n/// @param callInfo the call information\n/// @param aggressive true if should inline complex functions\n/// @return the inlined result, nullptr if unsuccessful\nInlineResult inlineFunction(Func *func, std::vector<Value *> args,\n                            bool aggressive = false, codon::SrcInfo callInfo = {});\n\n/// Inline the given call.\n/// @param v the instruction\n/// @param aggressive true if should inline complex functions\n/// @return the inlined result, nullptr if unsuccessful\nInlineResult inlineCall(CallInstr *v, bool aggressive = false);\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/irtools.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"irtools.h\"\n\n#include <iterator>\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\nbool hasAttribute(const Func *func, const std::string &attribute) {\n  if (auto *attr = func->getAttribute<KeyValueAttribute>()) {\n    return attr->has(attribute);\n  }\n  return false;\n}\n\nbool isStdlibFunc(const Func *func, const std::string &submodule) {\n  if (auto *attr = func->getAttribute<KeyValueAttribute>()) {\n    std::string module = attr->get(\".module\");\n    return module.rfind(\"std::\" + submodule, 0) == 0;\n  }\n  return false;\n}\n\nCallInstr *call(Func *func, const std::vector<Value *> &args) {\n  auto *M = func->getModule();\n  return M->Nr<CallInstr>(M->Nr<VarValue>(func), args);\n}\n\nbool isCallOf(const Value *value, const std::string &name,\n              const std::vector<types::Type *> &inputs, types::Type *output,\n              bool method) {\n  if (auto *call = cast<CallInstr>(value)) {\n    auto *fn = getFunc(call->getCallee());\n    if (!fn || fn->getUnmangledName() != name || call->numArgs() != inputs.size())\n      return false;\n\n    unsigned i = 0;\n    for (auto *arg : *call) {\n      if (inputs[i] && !arg->getType()->is(inputs[i]))\n        return false;\n      ++i;\n    }\n\n    if (output && !value->getType()->is(output))\n      return false;\n\n    if (method) {\n      if (inputs.empty() || !fn->getParentType())\n        return false;\n\n      if (inputs[0] && !fn->getParentType()->is(inputs[0]))\n        return false;\n    }\n\n    return true;\n  }\n\n  return false;\n}\n\nbool isCallOf(const Value *value, const std::string &name, int numArgs,\n              types::Type *output, bool method) {\n  if (auto *call = cast<CallInstr>(value)) {\n    auto *fn = getFunc(call->getCallee());\n    if (!fn || fn->getUnmangledName() != name ||\n        (numArgs >= 0 && call->numArgs() != numArgs))\n      return false;\n\n    if (output && !value->getType()->is(output))\n      return false;\n\n    if (method && (!fn->getParentType() || call->numArgs() == 0 ||\n                   !call->front()->getType()->is(fn->getParentType())))\n      return false;\n\n    return true;\n  }\n\n  return false;\n}\n\nbool isMagicMethodCall(const Value *value) {\n  if (auto *call = cast<CallInstr>(value)) {\n    auto *fn = getFunc(call->getCallee());\n    if (!fn || !fn->getParentType() || call->numArgs() == 0 ||\n        !call->front()->getType()->is(fn->getParentType()))\n      return false;\n\n    auto name = fn->getUnmangledName();\n    auto size = name.size();\n    if (size < 5 || !(name[0] == '_' && name[1] == '_' && name[size - 1] == '_' &&\n                      name[size - 2] == '_'))\n      return false;\n\n    return true;\n  }\n\n  return false;\n}\n\nValue *makeTuple(const std::vector<Value *> &args, Module *M) {\n  if (!M) {\n    seqassertn(!args.empty(), \"unknown module for empty tuple construction\");\n    M = args[0]->getModule();\n  }\n\n  std::vector<types::Type *> types;\n  for (auto *arg : args) {\n    types.push_back(arg->getType());\n  }\n  auto *tupleType = M->getTupleType(types);\n  auto *newFunc = M->getOrRealizeMethod(tupleType, \"__new__\", types);\n  seqassertn(newFunc, \"could not realize {} new function\", *tupleType);\n  return M->Nr<CallInstr>(M->Nr<VarValue>(newFunc), args);\n}\n\nVar *makeVar(Value *x, SeriesFlow *flow, BodiedFunc *parent, bool prepend) {\n  const bool global = (parent == nullptr);\n  auto *M = x->getModule();\n  auto *v = M->Nr<Var>(x->getType(), global);\n  if (global) {\n    static int counter = 1;\n    v->setName(\".anon_global.\" + std::to_string(counter++));\n  }\n  auto *a = M->Nr<AssignInstr>(v, x);\n  if (prepend) {\n    flow->insert(flow->begin(), a);\n  } else {\n    flow->push_back(a);\n  }\n  if (!global) {\n    parent->push_back(v);\n  }\n  return v;\n}\n\nValue *alloc(types::Type *type, Value *count) {\n  auto *M = type->getModule();\n  auto *ptrType = M->getPointerType(type);\n  return (*ptrType)(*count);\n}\n\nValue *alloc(types::Type *type, int64_t count) {\n  auto *M = type->getModule();\n  return alloc(type, M->getInt(count));\n}\n\nVar *getVar(Value *x) {\n  if (auto *v = cast<VarValue>(x)) {\n    if (auto *var = cast<Var>(v->getVar())) {\n      if (!isA<Func>(var)) {\n        return var;\n      }\n    }\n  }\n  return nullptr;\n}\n\nconst Var *getVar(const Value *x) {\n  if (auto *v = cast<VarValue>(x)) {\n    if (auto *var = cast<Var>(v->getVar())) {\n      if (!isA<Func>(var)) {\n        return var;\n      }\n    }\n  }\n  return nullptr;\n}\n\nFunc *getFunc(Value *x) {\n  if (auto *v = cast<VarValue>(x)) {\n    if (auto *func = cast<Func>(v->getVar())) {\n      return func;\n    }\n  }\n  return nullptr;\n}\n\nconst Func *getFunc(const Value *x) {\n  if (auto *v = cast<VarValue>(x)) {\n    if (auto *func = cast<Func>(v->getVar())) {\n      return func;\n    }\n  }\n  return nullptr;\n}\n\nValue *ptrLoad(Value *ptr) {\n  auto *M = ptr->getModule();\n  auto *deref = (*ptr)[*M->getInt(0)];\n  seqassertn(deref, \"pointer getitem not found [{}]\", ptr->getSrcInfo());\n  return deref;\n}\n\nValue *ptrStore(Value *ptr, Value *val) {\n  auto *M = ptr->getModule();\n  auto *setitem =\n      M->getOrRealizeMethod(ptr->getType(), Module::SETITEM_MAGIC_NAME,\n                            {ptr->getType(), M->getIntType(), val->getType()});\n  seqassertn(setitem, \"pointer setitem not found [{}]\", ptr->getSrcInfo());\n  return call(setitem, {ptr, M->getInt(0), val});\n}\n\nValue *tupleGet(Value *tuple, unsigned index) {\n  auto *M = tuple->getModule();\n  return M->Nr<ExtractInstr>(tuple, \"item\" + std::to_string(index + 1));\n}\n\nValue *tupleStore(Value *tuple, unsigned index, Value *val) {\n  auto *M = tuple->getModule();\n  auto *type = cast<types::RecordType>(tuple->getType());\n  seqassertn(type, \"argument is not a tuple [{}]\", tuple->getSrcInfo());\n  std::vector<Value *> newElements;\n  for (unsigned i = 0; i < std::distance(type->begin(), type->end()); i++) {\n    newElements.push_back(i == index ? val : tupleGet(tuple, i));\n  }\n  return makeTuple(newElements, M);\n}\n\nBodiedFunc *getStdlibFunc(Value *x, const std::string &name,\n                          const std::string &submodule) {\n  if (auto *f = getFunc(x)) {\n    if (auto *g = cast<BodiedFunc>(f)) {\n      if (isStdlibFunc(g, submodule) && g->getUnmangledName() == name) {\n        return g;\n      }\n    }\n  }\n  return nullptr;\n}\n\nconst BodiedFunc *getStdlibFunc(const Value *x, const std::string &name,\n                                const std::string &submodule) {\n  if (auto *f = getFunc(x)) {\n    if (auto *g = cast<BodiedFunc>(f)) {\n      if (isStdlibFunc(g, submodule) && g->getUnmangledName() == name) {\n        return g;\n      }\n    }\n  }\n  return nullptr;\n}\n\ntypes::Type *getReturnType(const Func *func) {\n  return cast<types::FuncType>(func->getType())->getReturnType();\n}\n\nvoid setReturnType(Func *func, types::Type *rType) {\n  auto *M = func->getModule();\n  auto *t = cast<types::FuncType>(func->getType());\n  seqassertn(t, \"{} is not a function type [{}]\", *func->getType(), func->getSrcInfo());\n  std::vector<types::Type *> argTypes(t->begin(), t->end());\n  func->setType(M->getFuncType(rType, argTypes));\n}\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/irtools.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/cir.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\n/// Checks whether a function has a given attribute.\n/// @param func the function\n/// @param attribute the attribute name\n/// @return true if the function has the given attribute\nbool hasAttribute(const Func *func, const std::string &attribute);\n\n/// Checks whether a function comes from the standard library, and\n/// optionally a specific module therein.\n/// @param func the function\n/// @param submodule module name (e.g. \"std::bio\"), or empty if\n///                  no module check is required\n/// @return true if the function is from the standard library in\n///         the given module\nbool isStdlibFunc(const Func *func, const std::string &submodule = \"\");\n\n/// Calls a function.\n/// @param func the function\n/// @param args vector of call arguments\n/// @return call instruction with the given function and arguments\nCallInstr *call(Func *func, const std::vector<Value *> &args);\n\n/// Checks if a value represents a call of a particular function.\n/// @param value the value to check\n/// @param name the function's (unmangled) name\n/// @param inputs vector of input types\n/// @param output output type, null for no check\n/// @param method true to ensure this call is a method call\n/// @return true if value is a call matching all parameters above\nbool isCallOf(const Value *value, const std::string &name,\n              const std::vector<types::Type *> &inputs, types::Type *output = nullptr,\n              bool method = false);\n\n/// Checks if a value represents a call of a particular function.\n/// @param value the value to check\n/// @param name the function's (unmangled) name\n/// @param numArgs argument count, negative for no check\n/// @param output output type, null for no check\n/// @param method true to ensure this call is a method call\n/// @return true if value is a call matching all parameters above\nbool isCallOf(const Value *value, const std::string &name, int numArgs = -1,\n              types::Type *output = nullptr, bool method = false);\n\n/// Checks if a value represents a call to a magic method.\n/// Magic method names start and end in \"__\" (two underscores).\n/// @param value the value to check\n/// @return true if value is a magic method call\nbool isMagicMethodCall(const Value *value);\n\n/// Constructs a new tuple.\n/// @param args vector of tuple contents\n/// @param M the module; inferred from elements if null\n/// @return value represents a tuple with the given contents\nValue *makeTuple(const std::vector<Value *> &args, Module *M = nullptr);\n\n/// Constructs and assigns a new variable.\n/// @param x the value to assign to the new variable\n/// @param flow series flow in which to assign the new variable\n/// @param parent function to add the new variable to, or null for global variable\n/// @param prepend true to insert assignment at start of block\n/// @return value containing the new variable\nVar *makeVar(Value *x, SeriesFlow *flow, BodiedFunc *parent, bool prepend = false);\n\n/// Dynamically allocates memory for the given type with the given\n/// number of elements.\n/// @param type the type\n/// @param count integer value representing the number of elements\n/// @return value representing a pointer to the allocated memory\nValue *alloc(types::Type *type, Value *count);\n\n/// Dynamically allocates memory for the given type with the given\n/// number of elements.\n/// @param type the type\n/// @param count the number of elements\n/// @return value representing a pointer to the allocated memory\nValue *alloc(types::Type *type, int64_t count);\n\n/// Builds a new series flow with the given contents. Returns\n/// null if no contents are provided.\n/// @param args contents of the series flow\n/// @return new series flow\ntemplate <typename... Args> SeriesFlow *series(Args... args) {\n  std::vector<Value *> vals = {args...};\n  if (vals.empty())\n    return nullptr;\n  auto *series = vals[0]->getModule()->Nr<SeriesFlow>();\n  for (auto *val : vals) {\n    series->push_back(val);\n  }\n  return series;\n}\n\n/// Checks whether the given value is a constant of the given\n/// type. Note that standard \"int\" corresponds to the C type\n/// \"int64_t\", which should be used here.\n/// @param x the value to check\n/// @return true if the value is constant\ntemplate <typename T> bool isConst(const Value *x) { return isA<TemplatedConst<T>>(x); }\n\n/// Checks whether the given value is a constant of the given\n/// type, and that is has a particular value. Note that standard\n/// \"int\" corresponds to the C type \"int64_t\", which should be used here.\n/// @param x the value to check\n/// @param value constant value to compare to\n/// @return true if the value is constant with the given value\ntemplate <typename T> bool isConst(const Value *x, const T &value) {\n  if (auto *c = cast<TemplatedConst<T>>(x)) {\n    return c->getVal() == value;\n  }\n  return false;\n}\n\n/// Returns the constant represented by a given value. Raises an assertion\n/// error if the given value is not constant. Note that standard\n/// \"int\" corresponds to the C type \"int64_t\", which should be used here.\n/// @param x the (constant) value\n/// @return the constant represented by the given value\ntemplate <typename T> T getConst(const Value *x) {\n  auto *c = cast<TemplatedConst<T>>(x);\n  seqassertn(c, \"{} is not a constant [{}]\", *x, x->getSrcInfo());\n  return c->getVal();\n}\n\n/// Gets a variable from a value.\n/// @param x the value\n/// @return the variable represented by the given value, or null if none\nVar *getVar(Value *x);\n\n/// Gets a variable from a value.\n/// @param x the value\n/// @return the variable represented by the given value, or null if none\nconst Var *getVar(const Value *x);\n\n/// Gets a function from a value.\n/// @param x the value\n/// @return the function represented by the given value, or null if none\nFunc *getFunc(Value *x);\n\n/// Gets a function from a value.\n/// @param x the value\n/// @return the function represented by the given value, or null if none\nconst Func *getFunc(const Value *x);\n\n/// Loads value from a pointer.\n/// @param ptr the pointer\n/// @return the value pointed to by the argument\nValue *ptrLoad(Value *ptr);\n\n/// Stores a value into a pointer.\n/// @param ptr the pointer\n/// @param val the value to store\n/// @return \"__setitem__\" call representing the store\nValue *ptrStore(Value *ptr, Value *val);\n\n/// Gets value from a tuple at the given index.\n/// @param tuple the tuple\n/// @param index the 0-based index\n/// @return tuple element at the given index\nValue *tupleGet(Value *tuple, unsigned index);\n\n/// Stores value in a tuple at the given index. Since tuples are immutable,\n/// a new instance is returned with the appropriate element replaced.\n/// @param tuple the tuple\n/// @param index the 0-based index\n/// @param val the value to store\n/// @return new tuple instance with the given value inserted\nValue *tupleStore(Value *tuple, unsigned index, Value *val);\n\n/// Gets a bodied standard library function from a value.\n/// @param x the value\n/// @param name name of the function\n/// @param submodule optional module to check\n/// @return the standard library function (with the given name, from the given\n/// submodule) represented by the given value, or null if none\nBodiedFunc *getStdlibFunc(Value *x, const std::string &name,\n                          const std::string &submodule = \"\");\n\n/// Gets a bodied standard library function from a value.\n/// @param x the value\n/// @param name name of the function\n/// @param submodule optional module to check\n/// @return the standard library function (with the given name, from the given\n/// submodule) represented by the given value, or null if none\nconst BodiedFunc *getStdlibFunc(const Value *x, const std::string &name,\n                                const std::string &submodule = \"\");\n\n/// Gets the return type of a function.\n/// @param func the function\n/// @return the return type of the given function\ntypes::Type *getReturnType(const Func *func);\n\n/// Sets the return type of a function. Argument types remain unchanged.\n/// @param func the function\n/// @param rType the new return type\nvoid setReturnType(Func *func, types::Type *rType);\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/iterators.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <iterator>\n#include <memory>\n#include <type_traits>\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\n/// Iterator wrapper that applies a function to the iterator.\ntemplate <typename It, typename DereferenceFunc, typename MemberFunc>\nstruct function_iterator_adaptor {\n  It internal;\n  DereferenceFunc d;\n  MemberFunc m;\n\n  using iterator_category = std::input_iterator_tag;\n  using value_type = typename std::remove_reference<decltype(d(*internal))>::type;\n  using reference = void;\n  using pointer = void;\n  using difference_type = typename std::iterator_traits<It>::difference_type;\n\n  /// Constructs an adaptor.\n  /// @param internal the internal iterator\n  /// @param d the dereference function\n  /// @param m the member access function\n  function_iterator_adaptor(It internal, DereferenceFunc &&d, MemberFunc &&m)\n      : internal(std::move(internal)), d(std::move(d)), m(std::move(m)) {}\n\n  decltype(auto) operator*() { return d(*internal); }\n  decltype(auto) operator->() { return m(*internal); }\n\n  function_iterator_adaptor &operator++() {\n    internal++;\n    return *this;\n  }\n  function_iterator_adaptor operator++(int) {\n    function_iterator_adaptor<It, DereferenceFunc, MemberFunc> copy(*this);\n    internal++;\n    return copy;\n  }\n\n  template <typename OtherIt, typename OtherDereferenceFunc, typename OtherMemberFunc>\n  bool operator==(const function_iterator_adaptor<OtherIt, OtherDereferenceFunc,\n                                                  OtherMemberFunc> &other) const {\n    return other.internal == internal;\n  }\n\n  template <typename OtherIt, typename OtherDereferenceFunc, typename OtherMemberFunc>\n  bool operator!=(const function_iterator_adaptor<OtherIt, OtherDereferenceFunc,\n                                                  OtherMemberFunc> &other) const {\n    return other.internal != internal;\n  }\n};\n\n/// Creates an adaptor that dereferences values.\n/// @param it the internal iterator\n/// @return the adaptor\ntemplate <typename It> auto dereference_adaptor(It it) {\n  auto f = [](const auto &v) -> auto & { return *v; };\n  auto m = [](const auto &v) -> auto { return v.get(); };\n  return function_iterator_adaptor<It, decltype(f), decltype(m)>(it, std::move(f),\n                                                                 std::move(m));\n}\n\n/// Creates an adaptor that gets the address of its values.\n/// @param it the internal iterator\n/// @return the adaptor\ntemplate <typename It> auto raw_ptr_adaptor(It it) {\n  auto f = [](auto &v) -> auto * { return v.get(); };\n  auto m = [](auto &v) -> auto * { return v.get(); };\n  return function_iterator_adaptor<It, decltype(f), decltype(m)>(it, std::move(f),\n                                                                 std::move(m));\n}\n\n/// Creates an adaptor that gets the const address of its values.\n/// @param it the internal iterator\n/// @return the adaptor\ntemplate <typename It> auto const_raw_ptr_adaptor(It it) {\n  auto f = [](auto &v) -> const auto * { return v.get(); };\n  auto m = [](auto &v) -> const auto * { return v.get(); };\n  return function_iterator_adaptor<It, decltype(f), decltype(m)>(it, std::move(f),\n                                                                 std::move(m));\n}\n\n/// Creates an adaptor that gets the keys of its values.\n/// @param it the internal iterator\n/// @return the adaptor\ntemplate <typename It> auto map_key_adaptor(It it) {\n  auto f = [](auto &v) -> auto & { return v.first; };\n  auto m = [](auto &v) -> auto & { return v.first; };\n  return function_iterator_adaptor<It, decltype(f), decltype(m)>(it, std::move(f),\n                                                                 std::move(m));\n}\n\n/// Creates an adaptor that gets the const keys of its values.\n/// @param it the internal iterator\n/// @return the adaptor\ntemplate <typename It> auto const_map_key_adaptor(It it) {\n  auto f = [](auto &v) -> const auto & { return v.first; };\n  auto m = [](auto &v) -> const auto & { return v.first; };\n  return function_iterator_adaptor<It, decltype(f), decltype(m)>(it, std::move(f),\n                                                                 std::move(m));\n}\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/matching.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"matching.h\"\n\n#include <algorithm>\n\n#include \"codon/cir/cir.h\"\n#include \"codon/cir/util/visitor.h\"\n\n#define VISIT(x)                                                                       \\\n  void visit(const x *v) override {                                                    \\\n    if (matchAny || dynamic_cast<const util::Any *>(v)) {                              \\\n      result = true;                                                                   \\\n      matchAny = true;                                                                 \\\n    } else if (!nodeId) {                                                              \\\n      nodeId = &x::NodeId;                                                             \\\n      other = v;                                                                       \\\n    } else if (nodeId != &x::NodeId ||                                                 \\\n               (!checkName && v->getName() != other->getName()))                       \\\n      result = false;                                                                  \\\n    else                                                                               \\\n      handle(v, static_cast<const x *>(other));                                        \\\n  }\n\nnamespace codon {\nnamespace ir {\nnamespace util {\nnamespace {\nclass MatchVisitor : public util::ConstVisitor {\nprivate:\n  bool matchAny = false;\n  bool checkName;\n  const char *nodeId = nullptr;\n  bool result = false;\n  const Node *other = nullptr;\n  bool varIdMatch;\n\npublic:\n  explicit MatchVisitor(bool checkName = false, bool varIdMatch = false)\n      : checkName(checkName), varIdMatch(varIdMatch) {}\n\n  VISIT(Var);\n  void handle(const Var *x, const Var *y) { result = compareVars(x, y); }\n\n  VISIT(Func);\n  void handle(const Func *x, const Func *y) {}\n  VISIT(BodiedFunc);\n  void handle(const BodiedFunc *x, const BodiedFunc *y) {\n    result = compareFuncs(x, y) &&\n             std::equal(x->begin(), x->end(), y->begin(), y->end(),\n                        [this](auto *x, auto *y) { return process(x, y); }) &&\n             process(x->getBody(), y->getBody()) && x->isJIT() == y->isJIT();\n  }\n  VISIT(ExternalFunc);\n  void handle(const ExternalFunc *x, const ExternalFunc *y) {\n    result = x->getUnmangledName() == y->getUnmangledName() && compareFuncs(x, y);\n  }\n  VISIT(InternalFunc);\n  void handle(const InternalFunc *x, const InternalFunc *y) {\n    result = x->getParentType() == y->getParentType() && compareFuncs(x, y);\n  }\n  VISIT(LLVMFunc);\n  void handle(const LLVMFunc *x, const LLVMFunc *y) {\n    result = std::equal(x->literal_begin(), x->literal_end(), y->literal_begin(),\n                        y->literal_end(),\n                        [this](auto &x, auto &y) {\n                          if (x.isStatic() && y.isStatic())\n                            return x.getStaticValue() == y.getStaticValue();\n                          else if (x.isStaticStr() && y.isStaticStr())\n                            return x.getStaticStringValue() == y.getStaticStringValue();\n                          else if (x.isType() && y.isType())\n                            return process(x.getTypeValue(), y.getTypeValue());\n                          return false;\n                        }) &&\n             x->getLLVMDeclarations() == y->getLLVMDeclarations() &&\n             x->getLLVMBody() == y->getLLVMBody() && compareFuncs(x, y);\n  }\n\n  VISIT(Value);\n  void handle(const Value *x, const Value *y) {}\n  VISIT(VarValue);\n  void handle(const VarValue *x, const VarValue *y) {\n    result = compareVars(x->getVar(), y->getVar());\n  }\n  VISIT(PointerValue);\n  void handle(const PointerValue *x, const PointerValue *y) {\n    result = compareVars(x->getVar(), y->getVar()) && x->getFields() == y->getFields();\n  }\n\n  VISIT(Flow);\n  void handle(const Flow *x, const Flow *y) {}\n  VISIT(SeriesFlow);\n  void handle(const SeriesFlow *x, const SeriesFlow *y) {\n    result = std::equal(x->begin(), x->end(), y->begin(), y->end(),\n                        [this](auto *x, auto *y) { return process(x, y); });\n  }\n  VISIT(IfFlow);\n  void handle(const IfFlow *x, const IfFlow *y) {\n    result = process(x->getCond(), y->getCond()) &&\n             process(x->getTrueBranch(), y->getTrueBranch()) &&\n             process(x->getFalseBranch(), y->getFalseBranch());\n  }\n\n  VISIT(WhileFlow);\n  void handle(const WhileFlow *x, const WhileFlow *y) {\n    result = process(x->getCond(), y->getCond()) && process(x->getBody(), y->getBody());\n  }\n  VISIT(ForFlow);\n  void handle(const ForFlow *x, const ForFlow *y) {\n    result = (x->isAsync() == y->isAsync()) && process(x->getIter(), y->getIter()) &&\n             process(x->getBody(), y->getBody()) && process(x->getVar(), y->getVar());\n  }\n  VISIT(ImperativeForFlow);\n  void handle(const ImperativeForFlow *x, const ImperativeForFlow *y) {\n    result = process(x->getVar(), y->getVar()) && process(x->getBody(), y->getBody()) &&\n             process(x->getStart(), y->getStart()) && x->getStep() == y->getStep() &&\n             process(x->getEnd(), y->getEnd());\n  }\n  VISIT(TryCatchFlow);\n  void handle(const TryCatchFlow *x, const TryCatchFlow *y) {\n    result = result && process(x->getElse(), y->getElse()) &&\n             process(x->getFinally(), y->getFinally()) &&\n             process(x->getBody(), y->getBody()) &&\n             std::equal(x->begin(), x->end(), y->begin(), y->end(),\n                        [this](auto &x, auto &y) {\n                          return process(x.getHandler(), y.getHandler()) &&\n                                 process(x.getType(), y.getType()) &&\n                                 process(x.getVar(), y.getVar());\n                        });\n  }\n  VISIT(PipelineFlow);\n  void handle(const PipelineFlow *x, const PipelineFlow *y) {\n    result = std::equal(\n        x->begin(), x->end(), y->begin(), y->end(), [this](auto &x, auto &y) {\n          return process(x.getCallee(), y.getCallee()) &&\n                 std::equal(x.begin(), x.end(), y.begin(), y.end(),\n                            [this](auto *x, auto *y) { return process(x, y); }) &&\n                 x.isGenerator() == y.isGenerator() && x.isParallel() == y.isParallel();\n        });\n  }\n  VISIT(dsl::CustomFlow);\n  void handle(const dsl::CustomFlow *x, const dsl::CustomFlow *y) {\n    result = x->match(y);\n  }\n\n  VISIT(IntConst);\n  void handle(const IntConst *x, const IntConst *y) {\n    result = process(x->getType(), y->getType()) && x->getVal() == y->getVal();\n  }\n  VISIT(FloatConst);\n  void handle(const FloatConst *x, const FloatConst *y) {\n    result = process(x->getType(), y->getType()) && x->getVal() == y->getVal();\n  }\n  VISIT(BoolConst);\n  void handle(const BoolConst *x, const BoolConst *y) {\n    result = process(x->getType(), y->getType()) && x->getVal() == y->getVal();\n  }\n  VISIT(StringConst);\n  void handle(const StringConst *x, const StringConst *y) {\n    result = process(x->getType(), y->getType()) && x->getVal() == y->getVal();\n  }\n  VISIT(dsl::CustomConst);\n  void handle(const dsl::CustomConst *x, const dsl::CustomConst *y) {\n    result = x->match(y);\n  }\n\n  VISIT(AssignInstr);\n  void handle(const AssignInstr *x, const AssignInstr *y) {\n    result = process(x->getLhs(), y->getLhs()) && process(x->getRhs(), y->getRhs());\n  }\n  VISIT(ExtractInstr);\n  void handle(const ExtractInstr *x, const ExtractInstr *y) {\n    result = process(x->getVal(), y->getVal()) && x->getField() == y->getField();\n  }\n  VISIT(InsertInstr);\n  void handle(const InsertInstr *x, const InsertInstr *y) {\n    result = process(x->getLhs(), y->getLhs()) && x->getField() == y->getField() &&\n             process(x->getRhs(), y->getRhs());\n  }\n  VISIT(CallInstr);\n  void handle(const CallInstr *x, const CallInstr *y) {\n    result = process(x->getCallee(), y->getCallee()) &&\n             std::equal(x->begin(), x->end(), y->begin(), y->end(),\n                        [this](auto *x, auto *y) { return process(x, y); });\n  }\n  VISIT(StackAllocInstr);\n  void handle(const StackAllocInstr *x, const StackAllocInstr *y) {\n    result = x->getCount() == y->getCount() && process(x->getType(), y->getType());\n  }\n  VISIT(TypePropertyInstr);\n  void handle(const TypePropertyInstr *x, const TypePropertyInstr *y) {\n    result = x->getProperty() == y->getProperty() &&\n             process(x->getInspectType(), y->getInspectType());\n  }\n  VISIT(YieldInInstr);\n  void handle(const YieldInInstr *x, const YieldInInstr *y) {\n    result = process(x->getType(), y->getType());\n  }\n  VISIT(TernaryInstr);\n  void handle(const TernaryInstr *x, const TernaryInstr *y) {\n    result = process(x->getCond(), y->getCond()) &&\n             process(x->getTrueValue(), y->getTrueValue()) &&\n             process(x->getFalseValue(), y->getFalseValue());\n  }\n  VISIT(BreakInstr);\n  void handle(const BreakInstr *x, const BreakInstr *y) {\n    result = process(x->getLoop(), y->getLoop());\n  }\n  VISIT(ContinueInstr);\n  void handle(const ContinueInstr *x, const ContinueInstr *y) {\n    result = process(x->getLoop(), y->getLoop());\n  }\n  VISIT(ReturnInstr);\n  void handle(const ReturnInstr *x, const ReturnInstr *y) {\n    result = process(x->getValue(), y->getValue());\n  }\n  VISIT(YieldInstr);\n  void handle(const YieldInstr *x, const YieldInstr *y) {\n    result = process(x->getValue(), y->getValue());\n  }\n  VISIT(AwaitInstr);\n  void handle(const AwaitInstr *x, const AwaitInstr *y) {\n    result = process(x->getType(), y->getType()) &&\n             process(x->getValue(), y->getValue()) &&\n             (x->isGenerator() == y->isGenerator());\n  }\n  VISIT(ThrowInstr);\n  void handle(const ThrowInstr *x, const ThrowInstr *y) {\n    result = process(x->getValue(), y->getValue());\n  }\n  VISIT(FlowInstr);\n  void handle(const FlowInstr *x, const FlowInstr *y) {\n    result =\n        process(x->getFlow(), y->getFlow()) && process(x->getValue(), y->getValue());\n  }\n  VISIT(dsl::CustomInstr);\n  void handle(const dsl::CustomInstr *x, const dsl::CustomInstr *y) {\n    result = x->match(y);\n  }\n\n  bool process(const Node *x, const Node *y) const {\n    if (!x && !y)\n      return true;\n    else if ((!x && y) || (x && !y))\n      return false;\n\n    auto *tx = cast<types::Type>(x);\n    auto *ty = cast<types::Type>(y);\n    if (tx || ty)\n      return tx && ty && tx->is(const_cast<types::Type *>(ty));\n\n    MatchVisitor v(checkName);\n    x->accept(v);\n    y->accept(v);\n\n    return v.result;\n  }\n\nprivate:\n  bool compareVars(const Var *x, const Var *y) const {\n    return process(x->getType(), y->getType()) &&\n           (!varIdMatch || x->getId() == y->getId());\n  }\n\n  bool compareFuncs(const Func *x, const Func *y) const {\n    if (!compareVars(x, y))\n      return false;\n\n    if (!std::equal(x->arg_begin(), x->arg_end(), y->arg_begin(), y->arg_end(),\n                    [this](auto *x, auto *y) { return process(x, y); }))\n      return false;\n\n    return true;\n  }\n};\n} // namespace\n\nconst char AnyValue::NodeId = 0;\n\nconst char AnyFlow::NodeId = 0;\n\nconst char AnyVar::NodeId = 0;\n\nconst char AnyFunc::NodeId = 0;\n\nbool match(Node *a, Node *b, bool checkNames, bool varIdMatch) {\n  return MatchVisitor(checkNames).process(a, b);\n}\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n\n#undef VISIT\n"
  },
  {
    "path": "codon/cir/util/matching.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/cir.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\n/// Base class for IR nodes that match anything.\nclass Any {};\n\n/// Any value.\nclass AnyValue : public AcceptorExtend<AnyValue, Value>, public Any {\npublic:\n  static const char NodeId;\n  using AcceptorExtend::AcceptorExtend;\n\nprivate:\n  types::Type *doGetType() const override { return getModule()->getVoidType(); }\n};\n\n/// Any flow.\nclass AnyFlow : public AcceptorExtend<AnyFlow, Flow>, public Any {\npublic:\n  static const char NodeId;\n  using AcceptorExtend::AcceptorExtend;\n};\n\n/// Any variable.\nclass AnyVar : public AcceptorExtend<AnyVar, Var>, public Any {\npublic:\n  static const char NodeId;\n  using AcceptorExtend::AcceptorExtend;\n};\n\n/// Any function.\nclass AnyFunc : public AcceptorExtend<AnyFunc, Func>, public Any {\npublic:\n  static const char NodeId;\n  using AcceptorExtend::AcceptorExtend;\n\n  AnyFunc() : AcceptorExtend() { setUnmangledName(\"any\"); }\n};\n\n/// Checks if IR nodes match.\n/// @param a the first IR node\n/// @param b the second IR node\n/// @param checkNames whether or not to check the node names\n/// @param varIdMatch whether or not variable ids must match\n/// @return true if the nodes are equal\nbool match(Node *a, Node *b, bool checkNames = false, bool varIdMatch = false);\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/operator.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <unordered_set>\n\n#include \"codon/cir/cir.h\"\n#include \"codon/cir/util/visitor.h\"\n\n#define LAMBDA_VISIT(x)                                                                \\\n  virtual void handle(codon::ir::x *v) {}                                              \\\n  void visit(codon::ir::x *v) override {                                               \\\n    if (childrenFirst)                                                                 \\\n      processChildren(v);                                                              \\\n    preHook(v);                                                                        \\\n    handle(v);                                                                         \\\n    postHook(v);                                                                       \\\n    if (!childrenFirst)                                                                \\\n      processChildren(v);                                                              \\\n  }\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\n/// Pass that visits all values in a module.\nclass Operator : public Visitor {\nprivate:\n  /// IDs of previously visited nodes\n  std::unordered_set<id_t> seen;\n  /// stack of IR nodes being visited\n  std::vector<Node *> nodeStack;\n  /// stack of iterators\n  std::vector<decltype(SeriesFlow().begin())> itStack;\n  /// true if should visit children first\n  bool childrenFirst;\n\nprotected:\n  void defaultVisit(Node *) override {}\n\npublic:\n  /// Constructs an operator.\n  /// @param childrenFirst true if children should be visited first\n  explicit Operator(bool childrenFirst = false) : childrenFirst(childrenFirst) {}\n\n  virtual ~Operator() noexcept = default;\n\n  /// This function is applied to all nodes before handling the node\n  /// itself. It provides a way to write one function that gets\n  /// applied to every visited node.\n  /// @param node the node\n  virtual void preHook(Node *node) {}\n  /// This function is applied to all nodes after handling the node\n  /// itself. It provides a way to write one function that gets\n  /// applied to every visited node.\n  /// @param node the node\n  virtual void postHook(Node *node) {}\n\n  void visit(Module *m) override {\n    nodeStack.push_back(m);\n    nodeStack.push_back(m->getMainFunc());\n    process(m->getMainFunc());\n    nodeStack.pop_back();\n    for (auto *s : *m) {\n      nodeStack.push_back(s);\n      process(s);\n      nodeStack.pop_back();\n    }\n    nodeStack.pop_back();\n  }\n\n  void visit(BodiedFunc *f) override {\n    if (f->getBody()) {\n      seen.insert(f->getBody()->getId());\n      process(f->getBody());\n    }\n  }\n\n  LAMBDA_VISIT(VarValue);\n  LAMBDA_VISIT(PointerValue);\n\n  void visit(codon::ir::SeriesFlow *v) override {\n    if (childrenFirst)\n      processSeriesFlowChildren(v);\n    preHook(v);\n    handle(v);\n    postHook(v);\n    if (!childrenFirst)\n      processSeriesFlowChildren(v);\n  }\n\n  virtual void handle(codon::ir::SeriesFlow *v) {}\n  LAMBDA_VISIT(IfFlow);\n  LAMBDA_VISIT(WhileFlow);\n  LAMBDA_VISIT(ForFlow);\n  LAMBDA_VISIT(ImperativeForFlow);\n  LAMBDA_VISIT(TryCatchFlow);\n  LAMBDA_VISIT(PipelineFlow);\n  LAMBDA_VISIT(dsl::CustomFlow);\n\n  LAMBDA_VISIT(TemplatedConst<int64_t>);\n  LAMBDA_VISIT(TemplatedConst<double>);\n  LAMBDA_VISIT(TemplatedConst<bool>);\n  LAMBDA_VISIT(TemplatedConst<std::string>);\n  LAMBDA_VISIT(dsl::CustomConst);\n\n  LAMBDA_VISIT(Instr);\n  LAMBDA_VISIT(AssignInstr);\n  LAMBDA_VISIT(ExtractInstr);\n  LAMBDA_VISIT(InsertInstr);\n  LAMBDA_VISIT(CallInstr);\n  LAMBDA_VISIT(StackAllocInstr);\n  LAMBDA_VISIT(TypePropertyInstr);\n  LAMBDA_VISIT(YieldInInstr);\n  LAMBDA_VISIT(TernaryInstr);\n  LAMBDA_VISIT(BreakInstr);\n  LAMBDA_VISIT(ContinueInstr);\n  LAMBDA_VISIT(ReturnInstr);\n  LAMBDA_VISIT(YieldInstr);\n  LAMBDA_VISIT(AwaitInstr);\n  LAMBDA_VISIT(ThrowInstr);\n  LAMBDA_VISIT(FlowInstr);\n  LAMBDA_VISIT(dsl::CustomInstr);\n\n  template <typename Node> void process(Node *v) { v->accept(*this); }\n\n  /// Return the parent of the current node.\n  /// @param level the number of levels up from the current node\n  template <typename Desired = Node> Desired *getParent(int level = 0) {\n    return cast<Desired>(nodeStack[nodeStack.size() - level - 1]);\n  }\n  /// @return current depth in the tree\n  int depth() const { return nodeStack.size(); }\n\n  /// @tparam Desired the desired type\n  /// @return the last encountered example of the desired type\n  template <typename Desired> Desired *findLast() {\n    for (auto it = nodeStack.rbegin(); it != nodeStack.rend(); ++it) {\n      if (auto *v = cast<Desired>(*it))\n        return v;\n    }\n    return nullptr;\n  }\n  /// @return the last encountered function\n  Func *getParentFunc() { return findLast<Func>(); }\n\n  /// @return an iterator to the first parent\n  auto parent_begin() const { return nodeStack.begin(); }\n  /// @return an iterator beyond the last parent\n  auto parent_end() const { return nodeStack.end(); }\n\n  /// @param v the value\n  /// @return whether we have visited (\"seen\") the given value\n  bool saw(const Value *v) const { return seen.find(v->getId()) != seen.end(); }\n  /// Avoid visiting the given value in the future.\n  /// @param v the value\n  void see(const Value *v) { seen.insert(v->getId()); }\n\n  /// Inserts the new value before the current position in the last seen SeriesFlow.\n  /// @param v the new value\n  auto insertBefore(Value *v) {\n    return findLast<SeriesFlow>()->insert(itStack.back(), v);\n  }\n  /// Inserts the new value after the current position in the last seen SeriesFlow.\n  /// @param v the new value, which is marked seen\n  auto insertAfter(Value *v) {\n    auto newPos = itStack.back();\n    ++newPos;\n    see(v);\n\n    return findLast<SeriesFlow>()->insert(newPos, v);\n  }\n\n  /// Resets the operator.\n  void reset() {\n    seen.clear();\n    nodeStack.clear();\n    itStack.clear();\n  }\n\nprivate:\n  void processChildren(Value *v) {\n    nodeStack.push_back(v);\n    for (auto *c : v->getUsedValues()) {\n      if (saw(c))\n        continue;\n      see(c);\n      process(c);\n    }\n    nodeStack.pop_back();\n  }\n\n  void processSeriesFlowChildren(codon::ir::SeriesFlow *v) {\n    nodeStack.push_back(v);\n    for (auto it = v->begin(); it != v->end(); ++it) {\n      itStack.push_back(it);\n      process(*it);\n      itStack.pop_back();\n    }\n    nodeStack.pop_back();\n  }\n};\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n\n#undef LAMBDA_VISIT\n"
  },
  {
    "path": "codon/cir/util/outlining.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"outlining.h\"\n\n#include <iterator>\n#include <unordered_set>\n#include <utility>\n\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/cir/util/operator.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace util {\nnamespace {\nstruct OutlineReplacer : public Operator {\n  std::unordered_set<id_t> &modVars;\n  std::vector<std::pair<Var *, Var *>> &remap;\n  std::vector<Value *> &outFlows;\n  CloneVisitor cv;\n\n  OutlineReplacer(Module *M, std::unordered_set<id_t> &modVars,\n                  std::vector<std::pair<Var *, Var *>> &remap,\n                  std::vector<Value *> &outFlows)\n      : Operator(), modVars(modVars), remap(remap), outFlows(outFlows), cv(M, false) {}\n\n  // Replace all used vars based on remapping.\n  void postHook(Node *node) override {\n    for (auto &pair : remap) {\n      node->replaceUsedVariable(std::get<0>(pair), std::get<1>(pair));\n    }\n  }\n\n  Var *mappedVar(Var *v) {\n    for (auto &pair : remap) {\n      if (std::get<0>(pair)->getId() == v->getId())\n        return std::get<1>(pair);\n    }\n    return nullptr;\n  }\n\n  // A return in the outlined func, or a break/continue that references a\n  // non-outlined loop, will return a status code that tells the call site\n  // what action to perform.\n  template <typename InstrType> void replaceOutFlowWithReturn(InstrType *v) {\n    auto *M = v->getModule();\n    for (unsigned i = 0; i < outFlows.size(); i++) {\n      if (outFlows[i]->getId() == v->getId()) {\n        auto *copy = cv.clone(v);\n        v->replaceAll(M->template Nr<ReturnInstr>(M->getInt(i + 1)));\n        outFlows[i] = copy;\n        break;\n      }\n    }\n  }\n\n  void handle(ReturnInstr *v) override { replaceOutFlowWithReturn(v); }\n\n  void handle(BreakInstr *v) override { replaceOutFlowWithReturn(v); }\n\n  void handle(ContinueInstr *v) override { replaceOutFlowWithReturn(v); }\n\n  // If passed by pointer (i.e. a \"mod var\"), change variable reference to\n  // a pointer dereference.\n  void handle(VarValue *v) override {\n    auto *M = v->getModule();\n    if (modVars.count(v->getVar()->getId()) > 0) {\n      // var -> pointer dereference\n      auto *deref = util::ptrLoad(M->Nr<VarValue>(mappedVar(v->getVar())));\n      saw(deref);\n      v->replaceAll(deref);\n    }\n  }\n\n  // If passed by pointer (i.e. a \"mod var\"), change pointer value to just\n  // be the var itself.\n  void handle(PointerValue *v) override {\n    auto *M = v->getModule();\n    if (modVars.count(v->getVar()->getId()) > 0) {\n      // pointer -> var\n      auto *ref = M->Nr<VarValue>(mappedVar(v->getVar()));\n      saw(ref);\n      v->replaceAll(ref);\n    }\n  }\n\n  // If passed by pointer (i.e. a \"mod var\"), change assignment to store\n  // in the pointer.\n  void handle(AssignInstr *v) override {\n    auto *M = v->getModule();\n    if (modVars.count(v->getLhs()->getId()) > 0) {\n      // store in pointer\n      Var *newVar = mappedVar(v->getLhs());\n      auto *setitem = util::ptrStore(M->Nr<VarValue>(newVar), v->getRhs());\n      saw(setitem);\n      v->replaceAll(setitem);\n    }\n  }\n};\n\nstruct Outliner : public Operator {\n  BodiedFunc *parent;\n  SeriesFlow *flowRegion;\n  decltype(flowRegion->begin()) begin, end;\n  bool outlineGlobals;              // whether to outline globals that are modified\n  bool allByValue;                  // outline all vars by value (can change semantics)\n  bool inRegion;                    // are we in the outlined region?\n  bool invalid;                     // if we can't outline for whatever reason\n  std::unordered_set<id_t> inVars;  // vars used inside region\n  std::unordered_set<id_t> outVars; // vars used outside region\n  std::unordered_set<id_t>\n      modifiedInVars; // vars modified (assigned or address'd) in region\n  std::unordered_set<id_t> globalsToOutline; // modified global vars to outline\n  std::unordered_set<id_t> inLoops;          // loops contained in region\n  std::vector<Value *>\n      outFlows; // control flows that need to be handled externally (e.g. return)\n\n  Outliner(BodiedFunc *parent, SeriesFlow *flowRegion,\n           decltype(flowRegion->begin()) begin, decltype(flowRegion->begin()) end,\n           bool outlineGlobals, bool allByValue)\n      : Operator(), parent(parent), flowRegion(flowRegion), begin(begin), end(end),\n        outlineGlobals(outlineGlobals), allByValue(allByValue), inRegion(false),\n        invalid(false), inVars(), outVars(), modifiedInVars(), globalsToOutline(),\n        inLoops(), outFlows() {}\n\n  bool isEnclosingLoopInRegion(id_t loopId = -1) {\n    int d = depth();\n    for (int i = 0; i < d; i++) {\n      Flow *v = getParent<WhileFlow>(i);\n      if (!v)\n        v = getParent<ForFlow>(i);\n      if (!v)\n        v = getParent<ImperativeForFlow>(i);\n\n      if (v && (loopId == -1 || loopId == v->getId()))\n        return inLoops.count(v->getId()) > 0;\n    }\n    return false;\n  }\n\n  void handle(WhileFlow *v) override {\n    if (inRegion)\n      inLoops.insert(v->getId());\n  }\n\n  void handle(ForFlow *v) override {\n    if (inRegion)\n      inLoops.insert(v->getId());\n  }\n\n  void handle(ImperativeForFlow *v) override {\n    if (inRegion)\n      inLoops.insert(v->getId());\n  }\n\n  void handle(ReturnInstr *v) override {\n    if (inRegion)\n      outFlows.push_back(v);\n  }\n\n  void handle(BreakInstr *v) override {\n    auto *loop = v->getLoop();\n    if (inRegion && !isEnclosingLoopInRegion(loop ? loop->getId() : -1))\n      outFlows.push_back(v);\n  }\n\n  void handle(ContinueInstr *v) override {\n    auto *loop = v->getLoop();\n    if (inRegion && !isEnclosingLoopInRegion(loop ? loop->getId() : -1))\n      outFlows.push_back(v);\n  }\n\n  void handle(YieldInstr *v) override {\n    if (inRegion)\n      invalid = true;\n  }\n\n  void handle(AwaitInstr *v) override {\n    if (inRegion)\n      invalid = true;\n  }\n\n  void handle(YieldInInstr *v) override {\n    if (inRegion)\n      invalid = true;\n  }\n\n  void handle(StackAllocInstr *v) override {\n    if (inRegion)\n      invalid = true;\n  }\n\n  void handle(AssignInstr *v) override {\n    if (inRegion) {\n      auto *var = v->getLhs();\n      modifiedInVars.insert(var->getId());\n      if (outlineGlobals && var->isGlobal() && !var->isThreadLocal())\n        globalsToOutline.insert(var->getId());\n    }\n  }\n\n  void handle(PointerValue *v) override {\n    if (inRegion) {\n      auto *var = v->getVar();\n      modifiedInVars.insert(var->getId());\n      if (outlineGlobals && var->isGlobal() && !var->isThreadLocal())\n        globalsToOutline.insert(var->getId());\n    }\n  }\n\n  void visit(SeriesFlow *v) override {\n    if (v->getId() != flowRegion->getId())\n      return Operator::visit(v);\n\n    auto it = flowRegion->begin();\n    for (; it != begin; ++it) {\n      (*it)->accept(*this);\n    }\n\n    inRegion = true;\n\n    for (; it != end; ++it) {\n      (*it)->accept(*this);\n    }\n\n    inRegion = false;\n\n    for (; it != flowRegion->end(); ++it) {\n      (*it)->accept(*this);\n    }\n  }\n\n  void visit(BodiedFunc *v) override {\n    for (auto it = v->arg_begin(); it != v->arg_end(); ++it) {\n      outVars.insert((*it)->getId());\n    }\n    Operator::visit(v);\n  }\n\n  void preHook(Node *node) override {\n    auto vars = node->getUsedVariables();\n    auto &set = (inRegion ? inVars : outVars);\n    for (auto *var : vars) {\n      if (!var->isGlobal())\n        set.insert(var->getId());\n      else if (inRegion && allByValue && !isA<Func>(var))\n        globalsToOutline.insert(var->getId());\n    }\n  }\n\n  // private = used in region AND NOT used outside region\n  std::unordered_set<id_t> getPrivateVars() {\n    std::unordered_set<id_t> privateVars;\n    for (auto id : inVars) {\n      if (outVars.count(id) == 0)\n        privateVars.insert(id);\n    }\n    return privateVars;\n  }\n\n  // shared = used in region AND used outside region\n  std::unordered_set<id_t> getSharedVars() {\n    std::unordered_set<id_t> sharedVars;\n    for (auto id : inVars) {\n      if (outVars.count(id) > 0)\n        sharedVars.insert(id);\n    }\n    return sharedVars;\n  }\n\n  // mod = shared AND modified in region\n  std::unordered_set<id_t> getModVars() {\n    if (allByValue)\n      return {};\n\n    std::unordered_set<id_t> modVars, shared = getSharedVars();\n    for (auto id : modifiedInVars) {\n      if (globalsToOutline.count(id) > 0 || shared.count(id) > 0)\n        modVars.insert(id);\n    }\n    return modVars;\n  }\n\n  OutlineResult outline(bool allowOutflows = true) {\n    if (invalid)\n      return {};\n\n    auto *M = flowRegion->getModule();\n    std::vector<std::pair<Var *, Var *>> remap; // mapping of old vars to new func vars\n    std::vector<types::Type *> argTypes;        // arg types of new func\n    std::vector<std::string> argNames;          // arg names of new func\n    std::vector<OutlineResult::ArgKind> argKinds; // arg information given back to user\n\n    // Figure out arguments and outlined function type:\n    //   - Private variables can be made local to the new function\n    //   - Shared variables will be passed as arguments\n    //   - Modified+shared variables will be passed as pointers\n    unsigned idx = 0;\n    auto shared = getSharedVars();\n    shared.insert(globalsToOutline.begin(), globalsToOutline.end());\n    auto mod = getModVars();\n    for (auto id : shared) {\n      Var *var = M->getVar(id);\n      seqassertn(var, \"unknown var id [{}]\", var->getSrcInfo());\n      remap.emplace_back(var, nullptr);\n      const bool isMod = (mod.count(id) > 0);\n      types::Type *type = isMod ? M->getPointerType(var->getType()) : var->getType();\n      argTypes.push_back(type);\n      argNames.push_back(var->getName());\n      argKinds.push_back(isMod ? OutlineResult::ArgKind::MODIFIED\n                               : OutlineResult::ArgKind::CONSTANT);\n    }\n\n    // Check if we need to handle control flow externally.\n    // If so, function will return an int code indicating control.\n    const bool callIndicatesControl = !outFlows.empty();\n    if (callIndicatesControl && !allowOutflows)\n      return {};\n    auto *funcType = M->getFuncType(\n        callIndicatesControl ? M->getIntType() : M->getNoneType(), argTypes);\n    auto *outlinedFunc = M->Nr<BodiedFunc>(\"__outlined\");\n    outlinedFunc->realize(funcType, argNames);\n\n    // Insert function arguments in variable remappings.\n    idx = 0;\n    for (auto it = outlinedFunc->arg_begin(); it != outlinedFunc->arg_end(); ++it) {\n      remap[idx] = {std::get<0>(remap[idx]), *it};\n      ++idx;\n    }\n\n    // Make private vars locals of the new function.\n    for (auto id : getPrivateVars()) {\n      Var *var = M->getVar(id);\n      seqassertn(var, \"unknown var id [{}]\", var->getSrcInfo());\n      Var *newVar = M->N<Var>(var->getSrcInfo(), var->getType(), /*global=*/false,\n                              /*external=*/false, /*tls=*/false, var->getName());\n      remap.emplace_back(var, newVar);\n      outlinedFunc->push_back(newVar);\n    }\n\n    // Delete outlined region from parent function and insert into outlined function.\n    auto *body = M->N<SeriesFlow>((*begin)->getSrcInfo());\n    auto it = begin;\n    while (it != end) {\n      body->push_back(*it);\n      it = flowRegion->erase(it);\n    }\n    outlinedFunc->setBody(body);\n\n    // Replace vars and externally-handled flows.\n    OutlineReplacer outRep(M, mod, remap, outFlows);\n    body->accept(outRep);\n\n    // Determine arguments for call to outlined function.\n    std::vector<Value *> args;\n    for (unsigned i = 0; i < shared.size(); i++) {\n      Var *var = std::get<0>(remap[i]);\n      Value *arg = (mod.count(var->getId()) > 0)\n                       ? static_cast<Value *>(M->Nr<PointerValue>(var))\n                       : M->Nr<VarValue>(var);\n      args.push_back(arg);\n    }\n    auto *outlinedCall = call(outlinedFunc, args);\n\n    // Check if we need external control-flow handling.\n    if (callIndicatesControl) {\n      auto *codeVar = M->Nr<Var>(M->getIntType()); // result of outlined func call\n      parent->push_back(codeVar);\n      it = flowRegion->insert(it, M->Nr<AssignInstr>(codeVar, outlinedCall));\n      // Check each return code of the function. 0 means normal return; do nothing.\n      for (unsigned i = 0; i < outFlows.size(); i++) {\n        // Generate \"if (result == code) { action }\".\n        auto *codeVal = M->getInt(i + 1); // 1-based by convention\n        auto *codeCheck = (*codeVal == *M->Nr<VarValue>(codeVar));\n        auto *codeBody = series(outFlows[i]);\n        auto *codeIf = M->Nr<IfFlow>(codeCheck, codeBody);\n        ++it;\n        it = flowRegion->insert(it, codeIf);\n      }\n    } else {\n      it = flowRegion->insert(it, outlinedCall);\n    }\n\n    return {outlinedFunc, outlinedCall, argKinds, static_cast<int>(outFlows.size())};\n  }\n};\n\n} // namespace\n\nOutlineResult outlineRegion(BodiedFunc *parent, SeriesFlow *series,\n                            decltype(series->begin()) begin,\n                            decltype(series->end()) end, bool allowOutflows,\n                            bool outlineGlobals, bool allByValue) {\n  if (begin == end)\n    return {};\n  Outliner outliner(parent, series, begin, end, outlineGlobals, allByValue);\n  parent->accept(outliner);\n  return outliner.outline(allowOutflows);\n}\n\nOutlineResult outlineRegion(BodiedFunc *parent, SeriesFlow *series, bool allowOutflows,\n                            bool outlineGlobals, bool allByValue) {\n  return outlineRegion(parent, series, series->begin(), series->end(), allowOutflows,\n                       outlineGlobals, allByValue);\n}\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/outlining.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/cir.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\n/// The result of an outlining operation.\nstruct OutlineResult {\n  /// Information about an argument of an outlined function.\n  enum ArgKind {\n    CONSTANT, ///< Argument is not modified by outlined function\n    MODIFIED, ///< Argument is modified and passed by pointer\n  };\n\n  /// The outlined function\n  BodiedFunc *func = nullptr;\n\n  /// The call to the outlined function\n  CallInstr *call = nullptr;\n\n  /// Information about each argument of the outlined function.\n  /// \"CONSTANT\" arguments are passed by value; \"MODIFIED\"\n  /// arguments are passed by pointer and written to by the\n  /// outlined function. The size of this vector is the same\n  /// as the number of arguments of the outlined function; each\n  /// entry corresponds to one of those arguments.\n  std::vector<ArgKind> argKinds;\n\n  /// Number of externally-handled control flows.\n  /// For example, an outlined function that contains a \"break\"\n  /// of a non-outlined loop will return an integer code that\n  /// tells the callee to perform this break. A series of\n  /// if-statements are added to the call site to check the\n  /// returned code and perform the correct action. This value\n  /// is the number of if-statements generated. If it is zero,\n  /// the function returns void and no such checks are done.\n  int numOutFlows = 0;\n\n  operator bool() const { return bool(func); }\n};\n\n/// Outlines a region of IR delineated by begin and end iterators\n/// on a particular series flow. The outlined code will be replaced\n/// by a call to the outlined function, and possibly extra logic if\n/// control flow needs to be handled.\n/// @param parent the function containing the series flow\n/// @param series the series flow on which outlining will happen\n/// @param begin start of outlining\n/// @param end end of outlining (non-inclusive like standard iterators)\n/// @param allowOutflows allow outlining regions with \"out-flows\"\n/// @param outlineGlobals outline globals as arguments to outlined function\n/// @param allByValue pass all outlined vars by value (can change semantics)\n/// @return the result of outlining\nOutlineResult outlineRegion(BodiedFunc *parent, SeriesFlow *series,\n                            decltype(series->begin()) begin,\n                            decltype(series->end()) end, bool allowOutflows = true,\n                            bool outlineGlobals = false, bool allByValue = false);\n\n/// Outlines a series flow from its parent function. The outlined code\n/// will be replaced by a call to the outlined function, and possibly\n/// extra logic if control flow needs to be handled.\n/// @param parent the function containing the series flow\n/// @param series the series flow on which outlining will happen\n/// @param allowOutflows allow outlining regions with \"out-flows\"\n/// @param outlineGlobals outline globals as arguments to outlined function\n/// @param allByValue pass all outlined vars by value (can change semantics)\n/// @return the result of outlining\nOutlineResult outlineRegion(BodiedFunc *parent, SeriesFlow *series,\n                            bool allowOutflows = true, bool outlineGlobals = false,\n                            bool allByValue = false);\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/packs.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <vector>\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\n/// Utility function to strip parameter packs.\n/// @param dst the destination vector\n/// @param first the value\ntemplate <typename Desired>\nvoid stripPack(std::vector<Desired *> &dst, Desired &first) {\n  dst.push_back(&first);\n}\n\n/// Utility function to strip parameter packs.\n/// @param dst the destination vector\ntemplate <typename Desired> void stripPack(std::vector<Desired *> &dst) {}\n\n/// Utility function to strip parameter packs.\n/// @param dst the destination vector\n/// @param first the value\n/// @param args the argument pack\ntemplate <typename Desired, typename... Args>\nvoid stripPack(std::vector<Desired *> &dst, Desired &first, Args &&...args) {\n  dst.push_back(&first);\n  stripPack<Desired>(dst, std::forward<Args>(args)...);\n}\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/side_effect.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"side_effect.h\"\n#include \"codon/parser/common.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\nconst std::string NON_PURE_ATTR =\n    ast::getMangledFunc(\"std.internal.attributes\", \"nonpure\");\nconst std::string PURE_ATTR = ast::getMangledFunc(\"std.internal.core\", \"pure\");\nconst std::string NO_SIDE_EFFECT_ATTR =\n    ast::getMangledFunc(\"std.internal.attributes\", \"no_side_effect\");\nconst std::string NO_CAPTURE_ATTR =\n    ast::getMangledFunc(\"std.internal.attributes\", \"nocapture\");\nconst std::string DERIVES_ATTR = ast::getMangledFunc(\"std.internal.core\", \"derives\");\nconst std::string SELF_CAPTURES_ATTR =\n    ast::getMangledFunc(\"std.internal.attributes\", \"self_captures\");\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/side_effect.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <string>\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\n/// Function side effect status. \"Pure\" functions by definition give the same\n/// output for the same inputs and have no side effects. \"No side effect\"\n/// functions have no side effects, but can give different outputs for the\n/// same input (e.g. time() is one such function). \"No capture\" functions do\n/// not capture any of their arguments; note that capturing an argument is\n/// considered a side effect. Therefore, we have pure < no_side_effect <\n/// no_capture < unknown, where \"<\" denotes subset. The enum values are also\n/// ordered in this way, which is relied on by the implementation.\nenum SideEffectStatus {\n  PURE = 0,\n  NO_SIDE_EFFECT,\n  NO_CAPTURE,\n  UNKNOWN,\n};\n\nextern const std::string NON_PURE_ATTR;\nextern const std::string PURE_ATTR;\nextern const std::string NO_SIDE_EFFECT_ATTR;\nextern const std::string NO_CAPTURE_ATTR;\nextern const std::string DERIVES_ATTR;\nextern const std::string SELF_CAPTURES_ATTR;\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/visitor.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"visitor.h\"\n\n#include \"codon/cir/cir.h\"\n\nnamespace codon {\nnamespace ir {\nnamespace util {\n\nvoid Visitor::visit(Module *x) { defaultVisit(x); }\nvoid Visitor::visit(Var *x) { defaultVisit(x); }\nvoid Visitor::visit(Func *x) { defaultVisit(x); }\nvoid Visitor::visit(BodiedFunc *x) { defaultVisit(x); }\nvoid Visitor::visit(ExternalFunc *x) { defaultVisit(x); }\nvoid Visitor::visit(InternalFunc *x) { defaultVisit(x); }\nvoid Visitor::visit(LLVMFunc *x) { defaultVisit(x); }\nvoid Visitor::visit(Value *x) { defaultVisit(x); }\nvoid Visitor::visit(VarValue *x) { defaultVisit(x); }\nvoid Visitor::visit(PointerValue *x) { defaultVisit(x); }\nvoid Visitor::visit(Flow *x) { defaultVisit(x); }\nvoid Visitor::visit(SeriesFlow *x) { defaultVisit(x); }\nvoid Visitor::visit(IfFlow *x) { defaultVisit(x); }\nvoid Visitor::visit(WhileFlow *x) { defaultVisit(x); }\nvoid Visitor::visit(ForFlow *x) { defaultVisit(x); }\nvoid Visitor::visit(ImperativeForFlow *x) { defaultVisit(x); }\nvoid Visitor::visit(TryCatchFlow *x) { defaultVisit(x); }\nvoid Visitor::visit(PipelineFlow *x) { defaultVisit(x); }\nvoid Visitor::visit(dsl::CustomFlow *x) { defaultVisit(x); }\nvoid Visitor::visit(Const *x) { defaultVisit(x); }\nvoid Visitor::visit(TemplatedConst<int64_t> *x) { defaultVisit(x); }\nvoid Visitor::visit(TemplatedConst<double> *x) { defaultVisit(x); }\nvoid Visitor::visit(TemplatedConst<bool> *x) { defaultVisit(x); }\nvoid Visitor::visit(TemplatedConst<std::string> *x) { defaultVisit(x); }\nvoid Visitor::visit(dsl::CustomConst *x) { defaultVisit(x); }\nvoid Visitor::visit(Instr *x) { defaultVisit(x); }\nvoid Visitor::visit(AssignInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(ExtractInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(InsertInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(CallInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(StackAllocInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(YieldInInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(TernaryInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(BreakInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(ContinueInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(ReturnInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(TypePropertyInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(YieldInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(AwaitInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(ThrowInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(FlowInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(dsl::CustomInstr *x) { defaultVisit(x); }\nvoid Visitor::visit(types::Type *x) { defaultVisit(x); }\nvoid Visitor::visit(types::PrimitiveType *x) { defaultVisit(x); }\nvoid Visitor::visit(types::IntType *x) { defaultVisit(x); }\nvoid Visitor::visit(types::FloatType *x) { defaultVisit(x); }\nvoid Visitor::visit(types::Float32Type *x) { defaultVisit(x); }\nvoid Visitor::visit(types::Float16Type *x) { defaultVisit(x); }\nvoid Visitor::visit(types::BFloat16Type *x) { defaultVisit(x); }\nvoid Visitor::visit(types::Float128Type *x) { defaultVisit(x); }\nvoid Visitor::visit(types::BoolType *x) { defaultVisit(x); }\nvoid Visitor::visit(types::ByteType *x) { defaultVisit(x); }\nvoid Visitor::visit(types::VoidType *x) { defaultVisit(x); }\nvoid Visitor::visit(types::RecordType *x) { defaultVisit(x); }\nvoid Visitor::visit(types::RefType *x) { defaultVisit(x); }\nvoid Visitor::visit(types::FuncType *x) { defaultVisit(x); }\nvoid Visitor::visit(types::OptionalType *x) { defaultVisit(x); }\nvoid Visitor::visit(types::PointerType *x) { defaultVisit(x); }\nvoid Visitor::visit(types::GeneratorType *x) { defaultVisit(x); }\nvoid Visitor::visit(types::IntNType *x) { defaultVisit(x); }\nvoid Visitor::visit(types::VectorType *x) { defaultVisit(x); }\nvoid Visitor::visit(types::UnionType *x) { defaultVisit(x); }\nvoid Visitor::visit(dsl::types::CustomType *x) { defaultVisit(x); }\n\nvoid ConstVisitor::visit(const Module *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const Var *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const Func *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const BodiedFunc *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const ExternalFunc *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const InternalFunc *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const LLVMFunc *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const Value *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const VarValue *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const PointerValue *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const Flow *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const SeriesFlow *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const IfFlow *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const WhileFlow *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const ForFlow *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const ImperativeForFlow *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const TryCatchFlow *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const PipelineFlow *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const dsl::CustomFlow *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const Const *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const TemplatedConst<int64_t> *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const TemplatedConst<double> *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const TemplatedConst<bool> *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const TemplatedConst<std::string> *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const dsl::CustomConst *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const Instr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const AssignInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const ExtractInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const InsertInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const CallInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const StackAllocInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const YieldInInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const TernaryInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const BreakInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const ContinueInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const ReturnInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const TypePropertyInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const YieldInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const AwaitInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const ThrowInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const FlowInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const dsl::CustomInstr *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::Type *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::PrimitiveType *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::IntType *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::FloatType *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::Float32Type *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::Float16Type *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::BFloat16Type *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::Float128Type *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::BoolType *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::ByteType *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::VoidType *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::RecordType *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::RefType *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::FuncType *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::OptionalType *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::PointerType *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::GeneratorType *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::IntNType *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::VectorType *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const types::UnionType *x) { defaultVisit(x); }\nvoid ConstVisitor::visit(const dsl::types::CustomType *x) { defaultVisit(x); }\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/util/visitor.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <stdexcept>\n#include <string>\n\n#define VISIT(x) virtual void visit(codon::ir::x *)\n#define CONST_VISIT(x) virtual void visit(const codon::ir::x *)\n\nnamespace codon {\nnamespace ir {\nclass Node;\n\nnamespace types {\nclass Type;\nclass PrimitiveType;\nclass IntType;\nclass FloatType;\nclass Float32Type;\nclass Float16Type;\nclass BFloat16Type;\nclass Float128Type;\nclass BoolType;\nclass ByteType;\nclass VoidType;\nclass RecordType;\nclass RefType;\nclass FuncType;\nclass OptionalType;\nclass PointerType;\nclass GeneratorType;\nclass IntNType;\nclass VectorType;\nclass UnionType;\n} // namespace types\n\nnamespace dsl {\n\nnamespace types {\nclass CustomType;\n}\n\nclass CustomConst;\nclass CustomFlow;\nclass CustomInstr;\n} // namespace dsl\n\nclass Module;\n\nclass Var;\n\nclass Func;\nclass BodiedFunc;\nclass ExternalFunc;\nclass InternalFunc;\nclass LLVMFunc;\n\nclass Value;\nclass VarValue;\nclass PointerValue;\n\nclass Flow;\nclass SeriesFlow;\nclass IfFlow;\nclass WhileFlow;\nclass ForFlow;\nclass ImperativeForFlow;\nclass TryCatchFlow;\nclass PipelineFlow;\n\nclass Const;\n\ntemplate <typename ValueType> class TemplatedConst;\n\nclass Instr;\nclass AssignInstr;\nclass ExtractInstr;\nclass InsertInstr;\nclass CallInstr;\nclass StackAllocInstr;\nclass TypePropertyInstr;\nclass YieldInInstr;\nclass TernaryInstr;\nclass BreakInstr;\nclass ContinueInstr;\nclass ReturnInstr;\nclass YieldInstr;\nclass AwaitInstr;\nclass ThrowInstr;\nclass FlowInstr;\n\nnamespace util {\n\n/// Base for CIR visitors\nclass Visitor {\nprotected:\n  virtual void defaultVisit(codon::ir::Node *) {\n    throw std::runtime_error(\"cannot visit node\");\n  }\n\npublic:\n  virtual ~Visitor() noexcept = default;\n\n  VISIT(Module);\n\n  VISIT(Var);\n\n  VISIT(Func);\n  VISIT(BodiedFunc);\n  VISIT(ExternalFunc);\n  VISIT(InternalFunc);\n  VISIT(LLVMFunc);\n\n  VISIT(Value);\n  VISIT(VarValue);\n  VISIT(PointerValue);\n\n  VISIT(Flow);\n  VISIT(SeriesFlow);\n  VISIT(IfFlow);\n  VISIT(WhileFlow);\n  VISIT(ForFlow);\n  VISIT(ImperativeForFlow);\n  VISIT(TryCatchFlow);\n  VISIT(PipelineFlow);\n  VISIT(dsl::CustomFlow);\n\n  VISIT(Const);\n  VISIT(TemplatedConst<int64_t>);\n  VISIT(TemplatedConst<double>);\n  VISIT(TemplatedConst<bool>);\n  VISIT(TemplatedConst<std::string>);\n  VISIT(dsl::CustomConst);\n\n  VISIT(Instr);\n  VISIT(AssignInstr);\n  VISIT(ExtractInstr);\n  VISIT(InsertInstr);\n  VISIT(CallInstr);\n  VISIT(StackAllocInstr);\n  VISIT(TypePropertyInstr);\n  VISIT(YieldInInstr);\n  VISIT(TernaryInstr);\n  VISIT(BreakInstr);\n  VISIT(ContinueInstr);\n  VISIT(ReturnInstr);\n  VISIT(YieldInstr);\n  VISIT(AwaitInstr);\n  VISIT(ThrowInstr);\n  VISIT(FlowInstr);\n  VISIT(dsl::CustomInstr);\n\n  VISIT(types::Type);\n  VISIT(types::PrimitiveType);\n  VISIT(types::IntType);\n  VISIT(types::FloatType);\n  VISIT(types::Float32Type);\n  VISIT(types::Float16Type);\n  VISIT(types::BFloat16Type);\n  VISIT(types::Float128Type);\n  VISIT(types::BoolType);\n  VISIT(types::ByteType);\n  VISIT(types::VoidType);\n  VISIT(types::RecordType);\n  VISIT(types::RefType);\n  VISIT(types::FuncType);\n  VISIT(types::OptionalType);\n  VISIT(types::PointerType);\n  VISIT(types::GeneratorType);\n  VISIT(types::IntNType);\n  VISIT(types::VectorType);\n  VISIT(types::UnionType);\n  VISIT(dsl::types::CustomType);\n};\n\nclass ConstVisitor {\nprotected:\n  virtual void defaultVisit(const codon::ir::Node *) {\n    throw std::runtime_error(\"cannot visit const node\");\n  }\n\npublic:\n  virtual ~ConstVisitor() noexcept = default;\n\n  CONST_VISIT(Module);\n\n  CONST_VISIT(Var);\n\n  CONST_VISIT(Func);\n  CONST_VISIT(BodiedFunc);\n  CONST_VISIT(ExternalFunc);\n  CONST_VISIT(InternalFunc);\n  CONST_VISIT(LLVMFunc);\n\n  CONST_VISIT(Value);\n  CONST_VISIT(VarValue);\n  CONST_VISIT(PointerValue);\n\n  CONST_VISIT(Flow);\n  CONST_VISIT(SeriesFlow);\n  CONST_VISIT(IfFlow);\n  CONST_VISIT(WhileFlow);\n  CONST_VISIT(ForFlow);\n  CONST_VISIT(ImperativeForFlow);\n  CONST_VISIT(TryCatchFlow);\n  CONST_VISIT(PipelineFlow);\n  CONST_VISIT(dsl::CustomFlow);\n\n  CONST_VISIT(Const);\n  CONST_VISIT(TemplatedConst<int64_t>);\n  CONST_VISIT(TemplatedConst<double>);\n  CONST_VISIT(TemplatedConst<bool>);\n  CONST_VISIT(TemplatedConst<std::string>);\n  CONST_VISIT(dsl::CustomConst);\n\n  CONST_VISIT(Instr);\n  CONST_VISIT(AssignInstr);\n  CONST_VISIT(ExtractInstr);\n  CONST_VISIT(InsertInstr);\n  CONST_VISIT(CallInstr);\n  CONST_VISIT(StackAllocInstr);\n  CONST_VISIT(TypePropertyInstr);\n  CONST_VISIT(YieldInInstr);\n  CONST_VISIT(TernaryInstr);\n  CONST_VISIT(BreakInstr);\n  CONST_VISIT(ContinueInstr);\n  CONST_VISIT(ReturnInstr);\n  CONST_VISIT(YieldInstr);\n  CONST_VISIT(AwaitInstr);\n  CONST_VISIT(ThrowInstr);\n  CONST_VISIT(FlowInstr);\n  CONST_VISIT(dsl::CustomInstr);\n\n  CONST_VISIT(types::Type);\n  CONST_VISIT(types::PrimitiveType);\n  CONST_VISIT(types::IntType);\n  CONST_VISIT(types::FloatType);\n  CONST_VISIT(types::Float32Type);\n  CONST_VISIT(types::Float16Type);\n  CONST_VISIT(types::BFloat16Type);\n  CONST_VISIT(types::Float128Type);\n  CONST_VISIT(types::BoolType);\n  CONST_VISIT(types::ByteType);\n  CONST_VISIT(types::VoidType);\n  CONST_VISIT(types::RecordType);\n  CONST_VISIT(types::RefType);\n  CONST_VISIT(types::FuncType);\n  CONST_VISIT(types::OptionalType);\n  CONST_VISIT(types::PointerType);\n  CONST_VISIT(types::GeneratorType);\n  CONST_VISIT(types::IntNType);\n  CONST_VISIT(types::VectorType);\n  CONST_VISIT(types::UnionType);\n  CONST_VISIT(dsl::types::CustomType);\n};\n\n} // namespace util\n} // namespace ir\n} // namespace codon\n\n#undef VISIT\n#undef CONST_VISIT\n"
  },
  {
    "path": "codon/cir/value.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"value.h\"\n\n#include \"codon/cir/instr.h\"\n#include \"codon/cir/module.h\"\n\nnamespace codon {\nnamespace ir {\n\nconst char Value::NodeId = 0;\n\nValue *Value::operator==(Value &other) {\n  return doBinaryOp(Module::EQ_MAGIC_NAME, other);\n}\n\nValue *Value::operator!=(Value &other) {\n  return doBinaryOp(Module::NE_MAGIC_NAME, other);\n}\n\nValue *Value::operator<(Value &other) {\n  return doBinaryOp(Module::LT_MAGIC_NAME, other);\n}\n\nValue *Value::operator>(Value &other) {\n  return doBinaryOp(Module::GT_MAGIC_NAME, other);\n}\n\nValue *Value::operator<=(Value &other) {\n  return doBinaryOp(Module::LE_MAGIC_NAME, other);\n}\n\nValue *Value::operator>=(Value &other) {\n  return doBinaryOp(Module::GE_MAGIC_NAME, other);\n}\n\nValue *Value::operator+() { return doUnaryOp(Module::POS_MAGIC_NAME); }\n\nValue *Value::operator-() { return doUnaryOp(Module::NEG_MAGIC_NAME); }\n\nValue *Value::operator~() { return doUnaryOp(Module::INVERT_MAGIC_NAME); }\n\nValue *Value::operator+(Value &other) {\n  return doBinaryOp(Module::ADD_MAGIC_NAME, other);\n}\n\nValue *Value::operator-(Value &other) {\n  return doBinaryOp(Module::SUB_MAGIC_NAME, other);\n}\n\nValue *Value::operator*(Value &other) {\n  return doBinaryOp(Module::MUL_MAGIC_NAME, other);\n}\n\nValue *Value::matMul(Value &other) {\n  return doBinaryOp(Module::MATMUL_MAGIC_NAME, other);\n}\n\nValue *Value::trueDiv(Value &other) {\n  return doBinaryOp(Module::TRUE_DIV_MAGIC_NAME, other);\n}\n\nValue *Value::operator/(Value &other) {\n  return doBinaryOp(Module::FLOOR_DIV_MAGIC_NAME, other);\n}\n\nValue *Value::operator%(Value &other) {\n  return doBinaryOp(Module::MOD_MAGIC_NAME, other);\n}\n\nValue *Value::pow(Value &other) { return doBinaryOp(Module::POW_MAGIC_NAME, other); }\n\nValue *Value::operator<<(Value &other) {\n  return doBinaryOp(Module::LSHIFT_MAGIC_NAME, other);\n}\n\nValue *Value::operator>>(Value &other) {\n  return doBinaryOp(Module::RSHIFT_MAGIC_NAME, other);\n}\n\nValue *Value::operator&(Value &other) {\n  return doBinaryOp(Module::AND_MAGIC_NAME, other);\n}\n\nValue *Value::operator|(Value &other) {\n  return doBinaryOp(Module::OR_MAGIC_NAME, other);\n}\n\nValue *Value::operator^(Value &other) {\n  return doBinaryOp(Module::XOR_MAGIC_NAME, other);\n}\n\nValue *Value::operator||(Value &other) {\n  auto *module = getModule();\n  return module->Nr<TernaryInstr>(toBool(), module->getBool(true), other.toBool());\n}\n\nValue *Value::operator&&(Value &other) {\n  auto *module = getModule();\n  return module->Nr<TernaryInstr>(toBool(), other.toBool(), module->getBool(false));\n}\n\nValue *Value::operator[](Value &other) {\n  return doBinaryOp(Module::GETITEM_MAGIC_NAME, other);\n}\n\nValue *Value::toInt() { return doUnaryOp(Module::INT_MAGIC_NAME); }\n\nValue *Value::toFloat() { return doUnaryOp(Module::FLOAT_MAGIC_NAME); }\n\nValue *Value::toBool() { return doUnaryOp(Module::BOOL_MAGIC_NAME); }\n\nValue *Value::toStr() { return doUnaryOp(Module::REPR_MAGIC_NAME); }\n\nValue *Value::len() { return doUnaryOp(Module::LEN_MAGIC_NAME); }\n\nValue *Value::iter() { return doUnaryOp(Module::ITER_MAGIC_NAME); }\n\nValue *Value::doUnaryOp(const std::string &name) {\n  auto *module = getModule();\n  auto *fn = module->getOrRealizeMethod(getType(), name,\n                                        std::vector<types::Type *>{getType()});\n\n  if (!fn)\n    return nullptr;\n\n  auto *fnVal = module->Nr<VarValue>(fn);\n  return (*fnVal)(*this);\n}\n\nValue *Value::doBinaryOp(const std::string &name, Value &other) {\n  auto *module = getModule();\n  auto *fn = module->getOrRealizeMethod(\n      getType(), name, std::vector<types::Type *>{getType(), other.getType()});\n\n  if (!fn)\n    return nullptr;\n\n  auto *fnVal = module->Nr<VarValue>(fn);\n  return (*fnVal)(*this, other);\n}\n\nValue *Value::doCall(const std::vector<Value *> &args) {\n  auto *module = getModule();\n  return module->Nr<CallInstr>(this, args);\n}\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/value.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/base.h\"\n#include \"codon/cir/types/types.h\"\n#include \"codon/cir/util/packs.h\"\n\nnamespace codon {\nnamespace ir {\n\nclass Func;\n\nclass Value : public ReplaceableNodeBase<Value>, public IdMixin {\npublic:\n  static const char NodeId;\n\n  /// Constructs a value.\n  /// @param the value's name\n  explicit Value(std::string name = \"\") : ReplaceableNodeBase(std::move(name)) {}\n\n  virtual ~Value() noexcept = default;\n\n  std::string referenceString() const final {\n    return fmt::format(FMT_STRING(\"{}.{}\"), getName(), getId());\n  }\n\n  std::vector<Value *> getUsedValues() final { return getActual()->doGetUsedValues(); }\n  std::vector<const Value *> getUsedValues() const final {\n    auto ret = getActual()->doGetUsedValues();\n    return std::vector<const Value *>(ret.begin(), ret.end());\n  }\n  int replaceUsedValue(id_t id, Value *newValue) final {\n    return getActual()->doReplaceUsedValue(id, newValue);\n  }\n  using Node::replaceUsedValue;\n\n  std::vector<types::Type *> getUsedTypes() const final {\n    return getActual()->doGetUsedTypes();\n  }\n  int replaceUsedType(const std::string &name, types::Type *newType) final {\n    return getActual()->doReplaceUsedType(name, newType);\n  }\n  using Node::replaceUsedType;\n\n  std::vector<Var *> getUsedVariables() final {\n    return getActual()->doGetUsedVariables();\n  }\n  std::vector<const Var *> getUsedVariables() const final {\n    auto ret = getActual()->doGetUsedVariables();\n    return std::vector<const Var *>(ret.begin(), ret.end());\n  }\n  int replaceUsedVariable(id_t id, Var *newVar) final {\n    return getActual()->doReplaceUsedVariable(id, newVar);\n  }\n  using Node::replaceUsedVariable;\n\n  /// @return the value's type\n  types::Type *getType() const { return getActual()->doGetType(); }\n\n  id_t getId() const override { return getActual()->id; }\n\n  Value *operator==(Value &other);\n  Value *operator!=(Value &other);\n  Value *operator<(Value &other);\n  Value *operator>(Value &other);\n  Value *operator<=(Value &other);\n  Value *operator>=(Value &other);\n\n  Value *operator+();\n  Value *operator-();\n  Value *operator~();\n\n  Value *operator+(Value &other);\n  Value *operator-(Value &other);\n  Value *operator*(Value &other);\n  Value *matMul(Value &other);\n  Value *trueDiv(Value &other);\n  Value *operator/(Value &other);\n  Value *operator%(Value &other);\n  Value *pow(Value &other);\n  Value *operator<<(Value &other);\n  Value *operator>>(Value &other);\n  Value *operator&(Value &other);\n  Value *operator|(Value &other);\n  Value *operator^(Value &other);\n\n  Value *operator||(Value &other);\n  Value *operator&&(Value &other);\n\n  template <typename... Args> Value *operator()(Args &&...args) {\n    std::vector<Value *> dst;\n    util::stripPack(dst, std::forward<Args>(args)...);\n    return doCall(dst);\n  }\n  Value *operator[](Value &other);\n\n  Value *toInt();\n  Value *toFloat();\n  Value *toBool();\n  Value *toStr();\n\n  Value *len();\n  Value *iter();\n\nprivate:\n  Value *doUnaryOp(const std::string &name);\n  Value *doBinaryOp(const std::string &name, Value &other);\n\n  Value *doCall(const std::vector<Value *> &args);\n\n  virtual types::Type *doGetType() const = 0;\n\n  virtual std::vector<Value *> doGetUsedValues() const { return {}; }\n  virtual int doReplaceUsedValue(id_t id, Value *newValue) { return 0; }\n\n  virtual std::vector<types::Type *> doGetUsedTypes() const { return {}; }\n  virtual int doReplaceUsedType(const std::string &name, types::Type *newType) {\n    return 0;\n  }\n\n  virtual std::vector<Var *> doGetUsedVariables() const { return {}; }\n  virtual int doReplaceUsedVariable(id_t id, Var *newVar) { return 0; }\n};\n\n} // namespace ir\n} // namespace codon\n\ntemplate <typename T>\nstruct fmt::formatter<\n    T, std::enable_if_t<std::is_base_of<codon::ir::Value, T>::value, char>>\n    : fmt::ostream_formatter {};\n"
  },
  {
    "path": "codon/cir/var.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"var.h\"\n\n#include \"codon/cir/module.h\"\n\nnamespace codon {\nnamespace ir {\n\nconst char Var::NodeId = 0;\n\nVar::Var(types::Type *type, bool global, bool external, bool tls, std::string name)\n    : ReplaceableNodeBase(std::move(name)), type(type), global(global),\n      external(external), tls(tls) {}\n\nint Var::doReplaceUsedType(const std::string &name, types::Type *newType) {\n  if (type->getName() == name) {\n    type = newType;\n    return 1;\n  }\n  return 0;\n}\n\nconst char VarValue::NodeId = 0;\n\nint VarValue::doReplaceUsedVariable(id_t id, Var *newVar) {\n  if (val->getId() == id) {\n    val = newVar;\n    return 1;\n  }\n  return 0;\n}\n\nconst char PointerValue::NodeId = 0;\n\ntypes::Type *PointerValue::doGetType() const {\n  return getModule()->getPointerType(val->getType());\n}\n\nint PointerValue::doReplaceUsedVariable(id_t id, Var *newVar) {\n  if (val->getId() == id) {\n    val = newVar;\n    return 1;\n  }\n  return 0;\n}\n\n} // namespace ir\n} // namespace codon\n"
  },
  {
    "path": "codon/cir/var.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"codon/cir/types/types.h\"\n#include \"codon/cir/value.h\"\n#include \"codon/util/common.h\"\n#include <fmt/format.h>\n#include <fmt/ostream.h>\n\nnamespace codon {\nnamespace ir {\n\nclass Func;\nclass Var;\n\n/// CIR object representing a variable.\nclass Var : public ReplaceableNodeBase<Var>, public IdMixin {\nprivate:\n  /// the variable's type\n  types::Type *type;\n  /// true if the variable is global\n  bool global;\n  /// true if the variable is external\n  bool external;\n  /// true if the variable is thread-local\n  bool tls;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a variable.\n  /// @param type the variable's type\n  /// @param global true if the variable is global\n  /// @param external true if the variable is external\n  /// @param tls true if the variable is thread-local\n  /// @param name the variable's name\n  explicit Var(types::Type *type, bool global = false, bool external = false,\n               bool tls = false, std::string name = \"\");\n  virtual ~Var() noexcept = default;\n\n  std::vector<Value *> getUsedValues() final { return getActual()->doGetUsedValues(); }\n  std::vector<const Value *> getUsedValues() const final {\n    auto ret = getActual()->doGetUsedValues();\n    return std::vector<const Value *>(ret.begin(), ret.end());\n  }\n  int replaceUsedValue(id_t id, Value *newValue) final {\n    return doReplaceUsedValue(id, newValue);\n  }\n  using Node::replaceUsedValue;\n\n  std::vector<types::Type *> getUsedTypes() const final {\n    return getActual()->doGetUsedTypes();\n  }\n  int replaceUsedType(const std::string &name, types::Type *newType) final {\n    return getActual()->doReplaceUsedType(name, newType);\n  }\n  using Node::replaceUsedType;\n\n  std::vector<Var *> getUsedVariables() final { return doGetUsedVariables(); }\n  std::vector<const Var *> getUsedVariables() const final {\n    auto ret = doGetUsedVariables();\n    return std::vector<const Var *>(ret.begin(), ret.end());\n  }\n  int replaceUsedVariable(id_t id, Var *newVar) final {\n    return getActual()->doReplaceUsedVariable(id, newVar);\n  }\n  using Node::replaceUsedVariable;\n\n  /// @return the type\n  types::Type *getType() const { return getActual()->type; }\n  /// Sets the type.\n  /// @param t the new type\n  void setType(types::Type *t) { getActual()->type = t; }\n\n  /// @return true if the variable is global\n  bool isGlobal() const { return getActual()->global; }\n  /// Sets the global flag.\n  /// @param v the new value\n  void setGlobal(bool v = true) { getActual()->global = v; }\n\n  /// @return true if the variable is external\n  bool isExternal() const { return getActual()->external; }\n  /// Sets the external flag.\n  /// @param v the new value\n  void setExternal(bool v = true) { getActual()->external = v; }\n\n  /// @return true if the variable is thread-local\n  bool isThreadLocal() const { return getActual()->tls; }\n  /// Sets the thread-local flag.\n  /// @param v the new value\n  void setThreadLocal(bool v = true) { getActual()->tls = v; }\n\n  std::string referenceString() const final {\n    return fmt::format(FMT_STRING(\"{}.{}\"), getName(), getId());\n  }\n\n  id_t getId() const override { return getActual()->id; }\n\nprotected:\n  virtual std::vector<Value *> doGetUsedValues() const { return {}; }\n  virtual int doReplaceUsedValue(id_t id, Value *newValue) { return 0; }\n\n  virtual std::vector<types::Type *> doGetUsedTypes() const { return {type}; }\n  virtual int doReplaceUsedType(const std::string &name, types::Type *newType);\n\n  virtual std::vector<Var *> doGetUsedVariables() const { return {}; }\n  virtual int doReplaceUsedVariable(id_t id, Var *newVar) { return 0; }\n};\n\n/// Value that contains an unowned variable reference.\nclass VarValue : public AcceptorExtend<VarValue, Value> {\nprivate:\n  /// the referenced var\n  Var *val;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a variable value.\n  /// @param val the referenced value\n  /// @param name the name\n  explicit VarValue(Var *val, std::string name = \"\")\n      : AcceptorExtend(std::move(name)), val(val) {}\n\n  /// @return the variable\n  Var *getVar() { return val; }\n  /// @return the variable\n  const Var *getVar() const { return val; }\n  /// Sets the variable.\n  /// @param v the new variable\n  void setVar(Var *v) { val = v; }\n\nprivate:\n  types::Type *doGetType() const override { return val->getType(); }\n\n  std::vector<Var *> doGetUsedVariables() const override { return {val}; }\n  int doReplaceUsedVariable(id_t id, Var *newVar) override;\n};\n\n/// Value that represents a pointer.\nclass PointerValue : public AcceptorExtend<PointerValue, Value> {\nprivate:\n  /// the referenced var\n  Var *val;\n  /// sequence of fields indicating where pointer should point\n  std::vector<std::string> fields;\n\npublic:\n  static const char NodeId;\n\n  /// Constructs a pointer value.\n  /// @param val the referenced value\n  /// @param fields the sequence of fields, or empty to get var pointer\n  /// @param name the name\n  explicit PointerValue(Var *val, std::vector<std::string> fields,\n                        std::string name = \"\")\n      : AcceptorExtend(std::move(name)), val(val), fields(std::move(fields)) {}\n\n  /// Constructs a pointer value.\n  /// @param val the referenced value\n  /// @param name the name\n  explicit PointerValue(Var *val, std::string name = \"\")\n      : PointerValue(val, {}, name) {}\n\n  /// @return the variable\n  Var *getVar() { return val; }\n  /// @return the variable\n  const Var *getVar() const { return val; }\n  /// Sets the variable.\n  /// @param v the new variable\n  void setVar(Var *v) { val = v; }\n\n  /// @return the sequence of fields\n  const std::vector<std::string> &getFields() const { return fields; }\n  /// Sets the sequence of fields\n  /// @param f the new fields\n  void setFields(std::vector<std::string> f) { fields = std::move(f); }\n\nprivate:\n  types::Type *doGetType() const override;\n\n  std::vector<Var *> doGetUsedVariables() const override { return {val}; }\n  int doReplaceUsedVariable(id_t id, Var *newVar) override;\n};\n\n} // namespace ir\n} // namespace codon\n\ntemplate <typename T>\nstruct fmt::formatter<T,\n                      std::enable_if_t<std::is_base_of<codon::ir::Var, T>::value, char>>\n    : fmt::ostream_formatter {};\n"
  },
  {
    "path": "codon/compiler/compiler.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"compiler.h\"\n\n#include \"codon/compiler/error.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/peg/peg.h\"\n#include \"codon/parser/visitors/doc/doc.h\"\n#include \"codon/parser/visitors/format/format.h\"\n#include \"codon/parser/visitors/translate/translate.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nextern double totalPeg;\n\nnamespace codon {\nnamespace {\nir::transform::PassManager::Init getPassManagerInit(Compiler::Mode mode, bool isTest) {\n  using ir::transform::PassManager;\n  switch (mode) {\n  case Compiler::Mode::DEBUG:\n    return isTest ? PassManager::Init::RELEASE : PassManager::Init::DEBUG;\n  case Compiler::Mode::RELEASE:\n    return PassManager::Init::RELEASE;\n  case Compiler::Mode::JIT:\n    return PassManager::Init::JIT;\n  default:\n    return PassManager::Init::EMPTY;\n  }\n}\n} // namespace\n\nCompiler::Compiler(const std::string &argv0, Compiler::Mode mode,\n                   const std::vector<std::string> &disabledPasses, bool isTest,\n                   bool pyNumerics, bool pyExtension,\n                   const std::shared_ptr<ast::IFilesystem> &fs)\n    : argv0(argv0), debug(mode == Mode::DEBUG), pyNumerics(pyNumerics),\n      pyExtension(pyExtension), input(), plm(std::make_unique<PluginManager>(argv0)),\n      cache(std::make_unique<ast::Cache>(argv0, fs)),\n      module(std::make_unique<ir::Module>()),\n      pm(std::make_unique<ir::transform::PassManager>(\n          getPassManagerInit(mode, isTest), disabledPasses, pyNumerics, pyExtension)),\n      llvisitor(std::make_unique<ir::LLVMVisitor>()) {\n  cache->module = module.get();\n  cache->pythonExt = pyExtension;\n  cache->pythonCompat = pyNumerics;\n  cache->compiler = this;\n  module->setCache(cache.get());\n  llvisitor->setDebug(debug);\n  llvisitor->setPluginManager(plm.get());\n}\n\nllvm::Error Compiler::load(const std::string &plugin) {\n  auto result = plm->load(plugin);\n  if (auto err = result.takeError())\n    return err;\n\n  auto *p = *result;\n  if (!p->info.stdlibPath.empty()) {\n    cache->fs->add_search_path(p->info.stdlibPath);\n  }\n  for (auto &kw : p->dsl->getExprKeywords()) {\n    cache->customExprStmts[kw.keyword] = kw.callback;\n  }\n  for (auto &kw : p->dsl->getBlockKeywords()) {\n    cache->customBlockStmts[kw.keyword] = {kw.hasExpr, kw.callback};\n  }\n  p->dsl->addIRPasses(pm.get(), debug);\n\n  loadedPlugins.insert(plugin);\n\n  return llvm::Error::success();\n}\n\n/// Checks if a plugin is already loaded.\nbool Compiler::isPluginLoaded(const std::string &path) const {\n  return loadedPlugins.find(path) != loadedPlugins.end();\n}\n\nllvm::Error\nCompiler::parse(bool isCode, const std::string &file, const std::string &code,\n                int startLine, int testFlags,\n                const std::unordered_map<std::string, std::string> &defines) {\n  input = file;\n  std::string abspath = (file != \"-\") ? std::string(cache->fs->canonical(file)) : file;\n  try {\n    auto nodeOrErr = isCode ? ast::parseCode(cache.get(), abspath, code, startLine)\n                            : ast::parseFile(cache.get(), abspath);\n    if (!nodeOrErr)\n      throw exc::ParserException(nodeOrErr.takeError());\n    auto codeStmt = *nodeOrErr;\n\n    cache->fs->set_module0(file);\n\n    Timer t2(\"typecheck\");\n    t2.logged = true;\n    auto typechecked = ast::TypecheckVisitor::apply(\n        cache.get(), codeStmt, abspath, defines, getEarlyDefines(), (testFlags > 1));\n    LOG_TIME(\"[T] parse = {:.1f}\", totalPeg);\n    LOG_TIME(\"[T] typecheck = {:.1f}\", t2.elapsed() - totalPeg);\n\n    if (codon::getLogger().flags & codon::Logger::FLAG_USER) {\n      auto fo = fopen(\"_dump_typecheck.sexp\", \"w\");\n      fmt::print(fo, \"{}\\n\", typechecked->toString(0));\n      for (auto &f : cache->functions)\n        for (auto &r : f.second.realizations) {\n          fmt::print(fo, \"{}\\n\", r.second->ast->toString(0));\n        }\n      fclose(fo);\n\n      fo = fopen(\"_dump_typecheck.htm\", \"w\");\n      auto s = ast::FormatVisitor::apply(typechecked, cache.get(), true);\n      fmt::print(fo, \"{}\\n\", s);\n      fclose(fo);\n    }\n\n    Timer t4(\"translate\");\n    ast::TranslateVisitor::apply(cache.get(), std::move(typechecked));\n    t4.log();\n  } catch (const exc::ParserException &exc) {\n    return llvm::make_error<error::ParserErrorInfo>(exc.getErrors());\n  }\n  module->setSrcInfo({abspath, 0, 0, 0});\n  if (codon::getLogger().flags & codon::Logger::FLAG_USER) {\n    auto fo = fopen(\"_dump_ir.sexp\", \"w\");\n    fmt::print(fo, \"{}\\n\", *module);\n    fclose(fo);\n  }\n  return llvm::Error::success();\n}\n\nllvm::Error\nCompiler::parseFile(const std::string &file, int testFlags,\n                    const std::unordered_map<std::string, std::string> &defines) {\n  return parse(/*isCode=*/false, file, /*code=*/\"\", /*startLine=*/0, testFlags,\n               defines);\n}\n\nllvm::Error\nCompiler::parseCode(const std::string &file, const std::string &code, int startLine,\n                    int testFlags,\n                    const std::unordered_map<std::string, std::string> &defines) {\n  return parse(/*isCode=*/true, file, code, startLine, testFlags, defines);\n}\n\nllvm::Error Compiler::compile() {\n  pm->run(module.get());\n  if (codon::getLogger().flags & codon::Logger::FLAG_USER) {\n    auto fo = fopen(\"_dump_ir_opt.sexp\", \"w\");\n    fmt::print(fo, \"{}\\n\", *module);\n    fclose(fo);\n  }\n  llvisitor->visit(module.get());\n  if (codon::getLogger().flags & codon::Logger::FLAG_USER) {\n    auto fo = fopen(\"_dump_llvm.ll\", \"w\");\n    std::string str;\n    llvm::raw_string_ostream os(str);\n    os << *(llvisitor->getModule());\n    os.flush();\n    fmt::print(fo, \"{}\\n\", str);\n    fclose(fo);\n  }\n  return llvm::Error::success();\n}\n\nllvm::Expected<std::string> Compiler::docgen(const std::vector<std::string> &files) {\n  try {\n    auto j = ast::DocVisitor::apply(argv0, files);\n    return j->toString();\n  } catch (exc::ParserException &exc) {\n    return llvm::make_error<error::ParserErrorInfo>(exc.getErrors());\n  }\n}\n\nstd::unordered_map<std::string, std::string> Compiler::getEarlyDefines() {\n  std::unordered_map<std::string, std::string> earlyDefines;\n  earlyDefines.emplace(\"__debug__\", debug ? \"1\" : \"0\");\n  earlyDefines.emplace(\"__py_numerics__\", pyNumerics ? \"1\" : \"0\");\n  earlyDefines.emplace(\"__py_extension__\", pyExtension ? \"1\" : \"0\");\n  earlyDefines.emplace(\"__apple__\",\n#if __APPLE__\n                       \"1\"\n#else\n                       \"0\"\n#endif\n  );\n  return earlyDefines;\n}\n\n} // namespace codon\n"
  },
  {
    "path": "codon/compiler/compiler.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"codon/cir/llvm/llvisitor.h\"\n#include \"codon/cir/module.h\"\n#include \"codon/cir/transform/manager.h\"\n#include \"codon/compiler/error.h\"\n#include \"codon/dsl/plugins.h\"\n#include \"codon/parser/cache.h\"\n\nnamespace codon {\n\nclass Compiler {\npublic:\n  enum Mode {\n    DEBUG,\n    RELEASE,\n    JIT,\n  };\n\nprivate:\n  std::string argv0;\n  bool debug;\n  bool pyNumerics;\n  bool pyExtension;\n  std::string input;\n  std::unique_ptr<PluginManager> plm;\n  std::unique_ptr<ast::Cache> cache;\n  std::unique_ptr<ir::Module> module;\n  std::unique_ptr<ir::transform::PassManager> pm;\n  std::unique_ptr<ir::LLVMVisitor> llvisitor;\n  std::unordered_set<std::string> loadedPlugins;\n\n  llvm::Error parse(bool isCode, const std::string &file, const std::string &code,\n                    int startLine, int testFlags,\n                    const std::unordered_map<std::string, std::string> &defines);\n\npublic:\n  Compiler(const std::string &argv0, Mode mode,\n           const std::vector<std::string> &disabledPasses = {}, bool isTest = false,\n           bool pyNumerics = false, bool pyExtension = false,\n           const std::shared_ptr<ast::IFilesystem> &fs = nullptr);\n\n  explicit Compiler(const std::string &argv0, bool debug = false,\n                    const std::vector<std::string> &disabledPasses = {},\n                    bool isTest = false, bool pyNumerics = false,\n                    bool pyExtension = false,\n                    const std::shared_ptr<ast::IFilesystem> &fs = nullptr)\n      : Compiler(argv0, debug ? Mode::DEBUG : Mode::RELEASE, disabledPasses, isTest,\n                 pyNumerics, pyExtension, fs) {}\n\n  std::string getArgv0() const { return argv0; }\n  std::string getInput() const { return input; }\n  PluginManager *getPluginManager() const { return plm.get(); }\n  ast::Cache *getCache() const { return cache.get(); }\n  ir::Module *getModule() const { return module.get(); }\n  ir::transform::PassManager *getPassManager() const { return pm.get(); }\n  ir::LLVMVisitor *getLLVMVisitor() const { return llvisitor.get(); }\n  bool isPluginLoaded(const std::string &) const;\n\n  llvm::Error load(const std::string &plugin);\n  llvm::Error\n  parseFile(const std::string &file, int testFlags = 0,\n            const std::unordered_map<std::string, std::string> &defines = {});\n  llvm::Error\n  parseCode(const std::string &file, const std::string &code, int startLine = 0,\n            int testFlags = 0,\n            const std::unordered_map<std::string, std::string> &defines = {});\n  llvm::Error compile();\n  llvm::Expected<std::string> docgen(const std::vector<std::string> &files);\n\n  std::unordered_map<std::string, std::string> getEarlyDefines();\n};\n\n} // namespace codon\n"
  },
  {
    "path": "codon/compiler/debug_listener.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"debug_listener.h\"\n\n#include <algorithm>\n#include <functional>\n#include <sstream>\n\n#include \"codon/runtime/lib.h\"\n\nnamespace codon {\nnamespace {\nstd::string\nmakeBacktrace(const std::vector<uintptr_t> &backtrace,\n              std::function<llvm::Expected<std::string>(uintptr_t)> backtraceCallback) {\n  std::ostringstream buf;\n  buf << \"\\033[1mBacktrace:\\033[0m\\n\";\n  for (auto pc : backtrace) {\n    auto line = backtraceCallback(pc);\n    if (!line)\n      break;\n    if (!line->empty())\n      buf << \"  \" << *line << \"\\n\";\n  }\n  return buf.str();\n}\n} // namespace\n\nvoid DebugListener::notifyObjectLoaded(ObjectKey key,\n                                       const llvm::object::ObjectFile &obj,\n                                       const llvm::RuntimeDyld::LoadedObjectInfo &L) {\n  uintptr_t start = 0, stop = 0;\n  for (const auto &sec : obj.sections()) {\n    if (sec.isText()) {\n      start = L.getSectionLoadAddress(sec);\n      stop = start + sec.getSize();\n      break;\n    }\n  }\n  auto buf = llvm::MemoryBuffer::getMemBufferCopy(obj.getData(), obj.getFileName());\n  auto newObj = llvm::cantFail(\n      llvm::object::ObjectFile::createObjectFile(buf->getMemBufferRef()));\n  objects.emplace_back(key, std::move(newObj), std::move(buf), start, stop);\n}\n\nvoid DebugListener::notifyFreeingObject(ObjectKey key) {\n  objects.erase(\n      std::remove_if(objects.begin(), objects.end(),\n                     [key](const ObjectInfo &o) { return key == o.getKey(); }),\n      objects.end());\n}\n\nllvm::Expected<llvm::DILineInfo> DebugListener::symbolize(uintptr_t pc) {\n  for (const auto &o : objects) {\n    if (o.contains(pc)) {\n      llvm::symbolize::LLVMSymbolizer sym;\n      return sym.symbolizeCode(\n          o.getObject(),\n          {pc - o.getStart(), llvm::object::SectionedAddress::UndefSection});\n    }\n  }\n  return llvm::DILineInfo();\n}\n\nllvm::Expected<std::string> DebugListener::getPrettyBacktrace(uintptr_t pc) {\n  auto invalid = [](const std::string &name) { return name == \"<invalid>\"; };\n  auto src = symbolize(pc);\n  if (auto err = src.takeError())\n    return std::move(err);\n  if (invalid(src->FunctionName) || invalid(src->FileName))\n    return \"\";\n  return runtime::makeBacktraceFrameString(pc, src->FunctionName, src->FileName,\n                                           src->Line, src->Column);\n}\n\nstd::string DebugListener::getPrettyBacktrace(const std::vector<uintptr_t> &backtrace) {\n  return makeBacktrace(backtrace, [&](uintptr_t pc) { return getPrettyBacktrace(pc); });\n}\n\nvoid DebugPlugin::notifyMaterializing(llvm::orc::MaterializationResponsibility &mr,\n                                      llvm::jitlink::LinkGraph &graph,\n                                      llvm::jitlink::JITLinkContext &ctx,\n                                      llvm::MemoryBufferRef inputObject) {\n  auto newBuf =\n      llvm::MemoryBuffer::getMemBufferCopy(inputObject.getBuffer(), graph.getName());\n  auto newObj = llvm::cantFail(\n      llvm::object::ObjectFile::createObjectFile(newBuf->getMemBufferRef()));\n\n  {\n    std::lock_guard<std::mutex> lock(pluginMutex);\n    assert(pendingObjs.count(&mr) == 0);\n    pendingObjs[&mr] = std::unique_ptr<JITObjectInfo>(\n        new JITObjectInfo{std::move(newBuf), std::move(newObj), {}});\n  }\n}\n\nllvm::Error DebugPlugin::notifyEmitted(llvm::orc::MaterializationResponsibility &mr) {\n  {\n    std::lock_guard<std::mutex> lock(pluginMutex);\n    auto it = pendingObjs.find(&mr);\n    if (it == pendingObjs.end())\n      return llvm::Error::success();\n\n    auto newInfo = pendingObjs[&mr].get();\n    auto getLoadAddress = [newInfo](const llvm::StringRef &name) -> uint64_t {\n      auto result = newInfo->sectionLoadAddresses.find(name);\n      if (result == newInfo->sectionLoadAddresses.end())\n        return 0;\n      return result->second;\n    };\n\n    // register(*newInfo->Object, getLoadAddress, nullptr)\n  }\n\n  llvm::cantFail(mr.withResourceKeyDo([&](llvm::orc::ResourceKey key) {\n    std::lock_guard<std::mutex> lock(pluginMutex);\n    registeredObjs[key].push_back(std::move(pendingObjs[&mr]));\n    pendingObjs.erase(&mr);\n  }));\n\n  return llvm::Error::success();\n}\n\nllvm::Error DebugPlugin::notifyFailed(llvm::orc::MaterializationResponsibility &mr) {\n  std::lock_guard<std::mutex> lock(pluginMutex);\n  pendingObjs.erase(&mr);\n  return llvm::Error::success();\n}\n\nllvm::Error DebugPlugin::notifyRemovingResources(llvm::orc::JITDylib &jd,\n                                                 llvm::orc::ResourceKey key) {\n  std::lock_guard<std::mutex> lock(pluginMutex);\n  registeredObjs.erase(key);\n  return llvm::Error::success();\n}\n\nvoid DebugPlugin::notifyTransferringResources(llvm::orc::JITDylib &jd,\n                                              llvm::orc::ResourceKey dstKey,\n                                              llvm::orc::ResourceKey srcKey) {\n  std::lock_guard<std::mutex> lock(pluginMutex);\n  auto it = registeredObjs.find(srcKey);\n  if (it != registeredObjs.end()) {\n    for (std::unique_ptr<JITObjectInfo> &info : it->second)\n      registeredObjs[dstKey].push_back(std::move(info));\n    registeredObjs.erase(it);\n  }\n}\n\nvoid DebugPlugin::modifyPassConfig(llvm::orc::MaterializationResponsibility &mr,\n                                   llvm::jitlink::LinkGraph &graph,\n                                   llvm::jitlink::PassConfiguration &config) {\n  std::lock_guard<std::mutex> lock(pluginMutex);\n  auto it = pendingObjs.find(&mr);\n  if (it == pendingObjs.end())\n    return;\n\n  JITObjectInfo &info = *it->second;\n  config.PostAllocationPasses.push_back(\n      [&info, this](llvm::jitlink::LinkGraph &graph) -> llvm::Error {\n        std::lock_guard<std::mutex> lock(pluginMutex);\n        for (const llvm::jitlink::Section &sec : graph.sections()) {\n#if defined(__APPLE__) && defined(__MACH__)\n          size_t secPos = sec.getName().find(',');\n          if (secPos >= 16 || (sec.getName().size() - (secPos + 1) > 16))\n            continue;\n          auto secName = sec.getName().substr(secPos + 1);\n#else\n          auto secName = sec.getName();\n#endif\n          info.sectionLoadAddresses[secName] =\n              llvm::jitlink::SectionRange(sec).getStart().getValue();\n        }\n        return llvm::Error::success();\n      });\n}\n\nllvm::Expected<llvm::DILineInfo> DebugPlugin::symbolize(uintptr_t pc) {\n  for (const auto &entry : registeredObjs) {\n    for (const auto &info : entry.second) {\n      const auto *o = info->object.get();\n      for (const auto &sec : o->sections()) {\n        if (sec.isText()) {\n          uintptr_t start =\n              info->sectionLoadAddresses.lookup(llvm::cantFail(sec.getName()));\n          uintptr_t stop = start + sec.getSize();\n          if (start <= pc && pc < stop) {\n            llvm::symbolize::LLVMSymbolizer sym;\n            return sym.symbolizeCode(\n                *o, {pc - start, llvm::object::SectionedAddress::UndefSection});\n          }\n        }\n      }\n    }\n  }\n  return llvm::DILineInfo();\n}\n\nllvm::Expected<std::string> DebugPlugin::getPrettyBacktrace(uintptr_t pc) {\n  auto invalid = [](const std::string &name) { return name == \"<invalid>\"; };\n  auto src = symbolize(pc);\n  if (auto err = src.takeError())\n    return std::move(err);\n  if (invalid(src->FunctionName) || invalid(src->FileName))\n    return \"\";\n  return runtime::makeBacktraceFrameString(pc, src->FunctionName, src->FileName,\n                                           src->Line, src->Column);\n}\n\nstd::string DebugPlugin::getPrettyBacktrace(const std::vector<uintptr_t> &backtrace) {\n  return makeBacktrace(backtrace, [&](uintptr_t pc) { return getPrettyBacktrace(pc); });\n}\n\n} // namespace codon\n"
  },
  {
    "path": "codon/compiler/debug_listener.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <map>\n#include <memory>\n#include <mutex>\n#include <vector>\n\n#include \"codon/cir/llvm/llvm.h\"\n\nnamespace codon {\n\n/// Debug info tracker for MCJIT.\nclass DebugListener : public llvm::JITEventListener {\npublic:\n  class ObjectInfo {\n  private:\n    ObjectKey key;\n    std::unique_ptr<llvm::object::ObjectFile> object;\n    std::unique_ptr<llvm::MemoryBuffer> buffer;\n    uintptr_t start;\n    uintptr_t stop;\n\n  public:\n    ObjectInfo(ObjectKey key, std::unique_ptr<llvm::object::ObjectFile> object,\n               std::unique_ptr<llvm::MemoryBuffer> buffer, uintptr_t start,\n               uintptr_t stop)\n        : key(key), object(std::move(object)), buffer(std::move(buffer)), start(start),\n          stop(stop) {}\n\n    ObjectKey getKey() const { return key; }\n    const llvm::object::ObjectFile &getObject() const { return *object; }\n    uintptr_t getStart() const { return start; }\n    uintptr_t getStop() const { return stop; }\n    bool contains(uintptr_t pc) const { return start <= pc && pc < stop; }\n  };\n\nprivate:\n  std::vector<ObjectInfo> objects;\n\n  void notifyObjectLoaded(ObjectKey key, const llvm::object::ObjectFile &obj,\n                          const llvm::RuntimeDyld::LoadedObjectInfo &L) override;\n  void notifyFreeingObject(ObjectKey key) override;\n\npublic:\n  DebugListener() : llvm::JITEventListener(), objects() {}\n\n  llvm::Expected<llvm::DILineInfo> symbolize(uintptr_t pc);\n  llvm::Expected<std::string> getPrettyBacktrace(uintptr_t pc);\n  std::string getPrettyBacktrace(const std::vector<uintptr_t> &backtrace);\n};\n\n/// Debug info tracker for JITLink. Adapted from Julia's implementation:\n/// https://github.com/JuliaLang/julia/blob/master/src/jitlayers.cpp\nclass DebugPlugin : public llvm::orc::ObjectLinkingLayer::Plugin {\n  struct JITObjectInfo {\n    std::unique_ptr<llvm::MemoryBuffer> backingBuffer;\n    std::unique_ptr<llvm::object::ObjectFile> object;\n    llvm::StringMap<uint64_t> sectionLoadAddresses;\n  };\n\n  std::mutex pluginMutex;\n  std::map<llvm::orc::MaterializationResponsibility *, std::unique_ptr<JITObjectInfo>>\n      pendingObjs;\n  std::map<llvm::orc::ResourceKey, std::vector<std::unique_ptr<JITObjectInfo>>>\n      registeredObjs;\n\npublic:\n  void notifyMaterializing(llvm::orc::MaterializationResponsibility &mr,\n                           llvm::jitlink::LinkGraph &graph,\n                           llvm::jitlink::JITLinkContext &ctx,\n                           llvm::MemoryBufferRef inputObject) override;\n  llvm::Error notifyEmitted(llvm::orc::MaterializationResponsibility &mr) override;\n  llvm::Error notifyFailed(llvm::orc::MaterializationResponsibility &mr) override;\n  llvm::Error notifyRemovingResources(llvm::orc::JITDylib &jd,\n                                      llvm::orc::ResourceKey key) override;\n  void notifyTransferringResources(llvm::orc::JITDylib &jd,\n                                   llvm::orc::ResourceKey dstKey,\n                                   llvm::orc::ResourceKey srcKey) override;\n  void modifyPassConfig(llvm::orc::MaterializationResponsibility &mr,\n                        llvm::jitlink::LinkGraph &,\n                        llvm::jitlink::PassConfiguration &config) override;\n\n  llvm::Expected<llvm::DILineInfo> symbolize(uintptr_t pc);\n  llvm::Expected<std::string> getPrettyBacktrace(uintptr_t pc);\n  std::string getPrettyBacktrace(const std::vector<uintptr_t> &backtrace);\n};\n\n} // namespace codon\n"
  },
  {
    "path": "codon/compiler/engine.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"engine.h\"\n\n#include \"codon/cir/llvm/optimize.h\"\n#include \"codon/compiler/memory_manager.h\"\n\nnamespace codon {\nnamespace jit {\n\nEngine::Engine() : jit(), debug(nullptr) {\n  auto eb = llvm::EngineBuilder();\n  eb.setMArch(llvm::codegen::getMArch());\n  eb.setMCPU(llvm::codegen::getCPUStr());\n  eb.setMAttrs(llvm::codegen::getFeatureList());\n\n  auto target = eb.selectTarget();\n  auto layout = target->createDataLayout();\n  auto epc = llvm::cantFail(llvm::orc::SelfExecutorProcessControl::Create(\n      std::make_shared<llvm::orc::SymbolStringPool>()));\n\n  llvm::orc::LLJITBuilder builder;\n  builder.setDataLayout(layout);\n  builder.setObjectLinkingLayerCreator(\n      [&](llvm::orc::ExecutionSession &es, const llvm::Triple &triple)\n          -> llvm::Expected<std::unique_ptr<llvm::orc::ObjectLayer>> {\n        auto L = std::make_unique<llvm::orc::ObjectLinkingLayer>(\n            es, llvm::cantFail(BoehmGCJITLinkMemoryManager::Create()));\n        if (auto regOrErr = llvm::orc::createJITLoaderGDBRegistrar(es)) {\n          L->addPlugin(std::make_unique<llvm::orc::DebugObjectManagerPlugin>(\n              es, std::move(*regOrErr)));\n        }\n        auto dbPlugin = std::make_unique<DebugPlugin>();\n        this->debug = dbPlugin.get();\n        L->addPlugin(std::move(dbPlugin));\n        L->setAutoClaimResponsibilityForObjectSymbols(true);\n        return L;\n      });\n  builder.setJITTargetMachineBuilder(\n      llvm::orc::JITTargetMachineBuilder(target->getTargetTriple()));\n  jit = llvm::cantFail(builder.create());\n\n  jit->getMainJITDylib().addGenerator(\n      llvm::cantFail(llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess(\n          layout.getGlobalPrefix())));\n\n  jit->getIRTransformLayer().setTransform(\n      [&](llvm::orc::ThreadSafeModule module,\n          const llvm::orc::MaterializationResponsibility &R) {\n        module.withModuleDo([](llvm::Module &module) {\n          ir::optimize(&module, /*debug=*/false, /*jit=*/true);\n        });\n        return std::move(module);\n      });\n}\n\nllvm::Error Engine::addModule(llvm::orc::ThreadSafeModule module,\n                              llvm::orc::ResourceTrackerSP rt) {\n  if (!rt)\n    rt = jit->getMainJITDylib().getDefaultResourceTracker();\n\n  return jit->addIRModule(rt, std::move(module));\n}\n\nllvm::Expected<llvm::orc::ExecutorAddr> Engine::lookup(llvm::StringRef name) {\n  return jit->lookup(name);\n}\n\n} // namespace jit\n} // namespace codon\n"
  },
  {
    "path": "codon/compiler/engine.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <vector>\n\n#include \"codon/cir/llvm/llvm.h\"\n#include \"codon/compiler/debug_listener.h\"\n\nnamespace codon {\nnamespace jit {\n\nclass Engine {\nprivate:\n  std::unique_ptr<llvm::orc::LLJIT> jit;\n  DebugPlugin *debug;\n\npublic:\n  Engine();\n\n  const llvm::DataLayout &getDataLayout() const { return jit->getDataLayout(); }\n\n  llvm::orc::JITDylib &getMainJITDylib() { return jit->getMainJITDylib(); }\n\n  DebugPlugin *getDebugListener() const { return debug; }\n\n  llvm::Error addModule(llvm::orc::ThreadSafeModule module,\n                        llvm::orc::ResourceTrackerSP rt = nullptr);\n\n  llvm::Expected<llvm::orc::ExecutorAddr> lookup(llvm::StringRef name);\n};\n\n} // namespace jit\n} // namespace codon\n"
  },
  {
    "path": "codon/compiler/error.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"error.h\"\n\nnamespace codon {\n\nSrcInfo::SrcInfo(std::string file, int line, int col, int len)\n    : file(std::move(file)), line(line), col(col), len(len), id(0) {\n  if (this->file.empty() && line != 0)\n    line++;\n  static int nextId = 0;\n  id = nextId++;\n};\n\nSrcInfo::SrcInfo() : SrcInfo(\"\", 0, 0, 0) {}\n\nbool SrcInfo::operator==(const SrcInfo &src) const { return id == src.id; }\n\nbool SrcInfo::operator<(const SrcInfo &src) const {\n  return std::tie(file, line, col) < std::tie(src.file, src.line, src.col);\n}\n\nbool SrcInfo::operator<=(const SrcInfo &src) const {\n  return std::tie(file, line, col) <= std::tie(src.file, src.line, src.col);\n}\n\nstd::string ErrorMessage::toString() const {\n  std::string s;\n  if (!getFile().empty()) {\n    s += getFile();\n    if (getLine() != 0) {\n      s += fmt::format(\":{}\", getLine());\n      if (getColumn() != 0)\n        s += fmt::format(\":{}\", getColumn());\n    }\n    s += \": \";\n  }\n  s += getMessage();\n  return s;\n}\n\nnamespace error {\n\nchar ParserErrorInfo::ID = 0;\n\nchar RuntimeErrorInfo::ID = 0;\n\nchar PluginErrorInfo::ID = 0;\n\nchar IOErrorInfo::ID = 0;\n\nvoid E(llvm::Error &&error) { throw exc::ParserException(std::move(error)); }\n\n} // namespace error\n\nnamespace exc {\n\nParserException::ParserException(llvm::Error &&e) noexcept : std::runtime_error(\"\") {\n  llvm::handleAllErrors(std::move(e), [this](const error::ParserErrorInfo &e) {\n    errors = e.getErrors();\n  });\n}\n\n} // namespace exc\n} // namespace codon\n"
  },
  {
    "path": "codon/compiler/error.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <string>\n#include <unordered_set>\n#include <vector>\n\n#include \"codon/parser/ast/error.h\"\n#include \"llvm/Support/Error.h\"\n#include <fmt/format.h>\n\nnamespace codon {\nnamespace error {\n\nenum Error {\n  CALL_NAME_ORDER,\n  CALL_NAME_STAR,\n  CALL_ELLIPSIS,\n  IMPORT_IDENTIFIER,\n  IMPORT_FN,\n  IMPORT_STAR,\n  FN_LLVM,\n  FN_LAST_KWARG,\n  FN_MULTIPLE_ARGS,\n  FN_DEFAULT_STARARG,\n  FN_ARG_TWICE,\n  FN_DEFAULT,\n  FN_C_DEFAULT,\n  FN_C_TYPE,\n  FN_SINGLE_DECORATOR,\n  CLASS_EXTENSION,\n  CLASS_MISSING_TYPE,\n  CLASS_ARG_TWICE,\n  CLASS_BAD_DECORATOR,\n  CLASS_MULTIPLE_DECORATORS,\n  CLASS_SINGLE_DECORATOR,\n  CLASS_CONFLICT_DECORATOR,\n  CLASS_NONSTATIC_DECORATOR,\n  CLASS_BAD_DECORATOR_ARG,\n  ID_NOT_FOUND,\n  ID_CANNOT_CAPTURE,\n  ID_INVALID_BIND,\n  UNION_TOO_BIG,\n  COMPILER_NO_FILE,\n  COMPILER_NO_STDLIB,\n  ID_NONLOCAL,\n  IMPORT_NO_MODULE,\n  IMPORT_NO_NAME,\n  DEL_NOT_ALLOWED,\n  DEL_INVALID,\n  ASSIGN_INVALID,\n  ASSIGN_LOCAL_REFERENCE,\n  ASSIGN_MULTI_STAR,\n  INT_RANGE,\n  FLOAT_RANGE,\n  STR_FSTRING_BALANCE_EXTRA,\n  STR_FSTRING_BALANCE_MISSING,\n  CALL_NO_TYPE,\n  CALL_TUPLE_COMPREHENSION,\n  CALL_NAMEDTUPLE,\n  CALL_PARTIAL,\n  EXPECTED_TOPLEVEL,\n  CLASS_ID_NOT_FOUND,\n  CLASS_INVALID_BIND,\n  CLASS_NO_INHERIT,\n  CLASS_NO_EXTEND,\n  CLASS_TUPLE_INHERIT,\n  CLASS_BAD_MRO,\n  CLASS_BAD_ATTR,\n  MATCH_MULTI_ELLIPSIS,\n  FN_OUTSIDE_ERROR,\n  FN_GLOBAL_ASSIGNED,\n  FN_GLOBAL_NOT_FOUND,\n  FN_BAD_LLVM,\n  FN_REALIZE_BUILTIN,\n  EXPECTED_LOOP,\n  LOOP_DECORATOR,\n  BAD_STATIC_TYPE,\n  EXPECTED_TYPE,\n  UNEXPECTED_TYPE,\n  DOT_NO_ATTR,\n  DOT_NO_ATTR_ARGS,\n  FN_NO_ATTR_ARGS,\n  EXPECTED_STATIC,\n  EXPECTED_STATIC_SPECIFIED,\n  ASSIGN_UNEXPECTED_STATIC,\n  ASSIGN_UNEXPECTED_FROZEN,\n  CALL_BAD_UNPACK,\n  CALL_BAD_ITER,\n  CALL_BAD_KWUNPACK,\n  CALL_REPEATED_NAME,\n  CALL_RECURSIVE_DEFAULT,\n  CALL_SUPERF,\n  CALL_SUPER_PARENT,\n  CALL_PTR_VAR,\n  EXPECTED_TUPLE,\n  CALL_REALIZED_FN,\n  CALL_ARGS_MANY,\n  CALL_ARGS_INVALID,\n  CALL_ARGS_MISSING,\n  GENERICS_MISMATCH,\n  EXPECTED_GENERATOR,\n  STATIC_RANGE_BOUNDS,\n  TUPLE_RANGE_BOUNDS,\n  STATIC_DIV_ZERO,\n  SLICE_STEP_ZERO,\n  OP_NO_MAGIC,\n  INST_CALLABLE_STATIC,\n  CATCH_EXCEPTION_TYPE,\n  TYPE_CANNOT_REALIZE_ATTR,\n  TYPE_UNIFY,\n  TYPE_FAILED,\n  MAX_REALIZATION,\n  CUSTOM,\n  __END__\n};\n\nclass ParserErrorInfo : public llvm::ErrorInfo<ParserErrorInfo> {\n  ParserErrors errors;\n\npublic:\n  static char ID;\n\n  explicit ParserErrorInfo(const ErrorMessage &msg) : errors(msg) {}\n  explicit ParserErrorInfo(const std::vector<ErrorMessage> &msgs) : errors(msgs) {}\n  explicit ParserErrorInfo(const ParserErrors &errors) : errors(errors) {}\n\n  template <class... TA>\n  ParserErrorInfo(error::Error e, const codon::SrcInfo &o = codon::SrcInfo(),\n                  const TA &...args) {\n    auto msg = Emsg(e, args...);\n    errors = ParserErrors(ErrorMessage(msg, o, (int)e));\n  }\n\n  const ParserErrors &getErrors() const { return errors; }\n  ParserErrors &getErrors() { return errors; }\n\n  void log(llvm::raw_ostream &out) const override {\n    for (const auto &trace : errors) {\n      for (const auto &msg : trace.getMessages()) {\n        auto t = msg.toString();\n        out << t << \"\\n\";\n      }\n    }\n  }\n\n  std::error_code convertToErrorCode() const override {\n    return llvm::inconvertibleErrorCode();\n  }\n};\n\nclass RuntimeErrorInfo : public llvm::ErrorInfo<RuntimeErrorInfo> {\nprivate:\n  std::string output;\n  std::string type;\n  ErrorMessage message;\n  std::vector<std::string> backtrace;\n\npublic:\n  RuntimeErrorInfo(const std::string &output, const std::string &type,\n                   const std::string &msg, const std::string &file = \"\", int line = 0,\n                   int col = 0, std::vector<std::string> backtrace = {})\n      : output(output), type(type), message(msg, file, line, col),\n        backtrace(std::move(backtrace)) {}\n\n  std::string getOutput() const { return output; }\n  std::string getType() const { return type; }\n  std::string getMessage() const { return message.getMessage(); }\n  std::string getFile() const { return message.getFile(); }\n  int getLine() const { return message.getLine(); }\n  int getColumn() const { return message.getColumn(); }\n  std::vector<std::string> getBacktrace() const { return backtrace; }\n\n  void log(llvm::raw_ostream &out) const override {\n    out << type << \": \";\n    message.log(out);\n  }\n\n  std::error_code convertToErrorCode() const override {\n    return llvm::inconvertibleErrorCode();\n  }\n\n  static char ID;\n};\n\nclass PluginErrorInfo : public llvm::ErrorInfo<PluginErrorInfo> {\nprivate:\n  std::string message;\n\npublic:\n  explicit PluginErrorInfo(const std::string &message) : message(message) {}\n\n  std::string getMessage() const { return message; }\n\n  void log(llvm::raw_ostream &out) const override { out << message; }\n\n  std::error_code convertToErrorCode() const override {\n    return llvm::inconvertibleErrorCode();\n  }\n\n  static char ID;\n};\n\nclass IOErrorInfo : public llvm::ErrorInfo<IOErrorInfo> {\nprivate:\n  std::string message;\n\npublic:\n  explicit IOErrorInfo(const std::string &message) : message(message) {}\n\n  std::string getMessage() const { return message; }\n\n  void log(llvm::raw_ostream &out) const override { out << message; }\n\n  std::error_code convertToErrorCode() const override {\n    return llvm::inconvertibleErrorCode();\n  }\n\n  static char ID;\n};\n\ntemplate <class... TA> std::string Eformat(const TA &...args) { return \"\"; }\ntemplate <class... TA> std::string Eformat(const char *fmt, const TA &...args) {\n  return fmt::format(fmt::runtime(fmt), args...);\n}\n\ntemplate <class... TA> std::string Emsg(Error e, const TA &...args) {\n  switch (e) {\n  /// Validations\n  case Error::CALL_NAME_ORDER:\n    return fmt::format(\"positional argument follows keyword argument\");\n  case Error::CALL_NAME_STAR:\n    return fmt::format(\"cannot use starred expression here\");\n  case Error::CALL_ELLIPSIS:\n    return fmt::format(\"multiple ellipsis expressions\");\n  case Error::IMPORT_IDENTIFIER:\n    return fmt::format(\"expected identifier\");\n  case Error::IMPORT_FN:\n    return fmt::format(\n        \"function signatures only allowed when importing C or Python functions\");\n  case Error::IMPORT_STAR:\n    return fmt::format(\"import * only allowed at module level\");\n  case Error::FN_LLVM:\n    return fmt::format(\"return types required for LLVM and C functions\");\n  case Error::FN_LAST_KWARG:\n    return fmt::format(\"kwargs must be the last argument\");\n  case Error::FN_MULTIPLE_ARGS:\n    return fmt::format(\"multiple star arguments provided\");\n  case Error::FN_DEFAULT_STARARG:\n    return fmt::format(\"star arguments cannot have default values\");\n  case Error::FN_ARG_TWICE:\n    return fmt::format(fmt::runtime(\"duplicate argument '{}' in function definition\"),\n                       args...);\n  case Error::FN_DEFAULT:\n    return fmt::format(\n        fmt::runtime(\"non-default argument '{}' follows default argument\"), args...);\n  case Error::FN_C_DEFAULT:\n    return fmt::format(\n        fmt::runtime(\n            \"argument '{}' within C function definition cannot have default value\"),\n        args...);\n  case Error::FN_C_TYPE:\n    return fmt::format(\n        fmt::runtime(\n            \"argument '{}' within C function definition requires type annotation\"),\n        args...);\n  case Error::FN_SINGLE_DECORATOR:\n    return fmt::format(\n        fmt::runtime(\"cannot combine '@{}' with other attributes or decorators\"),\n        args...);\n  case Error::CLASS_EXTENSION:\n    return fmt::format(\"class extensions cannot define data attributes and generics or \"\n                       \"inherit other classes\");\n  case Error::CLASS_MISSING_TYPE:\n    return fmt::format(fmt::runtime(\"type required for data attribute '{}'\"), args...);\n  case Error::CLASS_ARG_TWICE:\n    return fmt::format(\n        fmt::runtime(\"duplicate data attribute '{}' in class definition\"), args...);\n  case Error::CLASS_BAD_DECORATOR:\n    return fmt::format(\"unsupported class decorator\");\n  case Error::CLASS_MULTIPLE_DECORATORS:\n    return fmt::format(fmt::runtime(\"duplicate decorator '@{}' in class definition\"),\n                       args...);\n  case Error::CLASS_SINGLE_DECORATOR:\n    return fmt::format(\n        fmt::runtime(\"cannot combine '@{}' with other attributes or decorators\"),\n        args...);\n  case Error::CLASS_CONFLICT_DECORATOR:\n    return fmt::format(fmt::runtime(\"cannot combine '@{}' with '@{}'\"), args...);\n  case Error::CLASS_NONSTATIC_DECORATOR:\n    return fmt::format(\"class decorator arguments must be compile-time static values\");\n  case Error::CLASS_BAD_DECORATOR_ARG:\n    return fmt::format(\"class decorator got unexpected argument\");\n  /// Simplification\n  case Error::ID_NOT_FOUND:\n    return fmt::format(fmt::runtime(\"name '{}' is not defined\"), args...);\n  case Error::ID_CANNOT_CAPTURE:\n    return fmt::format(fmt::runtime(\"name '{}' cannot be captured\"), args...);\n  case Error::ID_NONLOCAL:\n    return fmt::format(fmt::runtime(\"no binding for nonlocal '{}' found\"), args...);\n  case Error::ID_INVALID_BIND:\n    return fmt::format(fmt::runtime(\"cannot bind '{}' to global or nonlocal name\"),\n                       args...);\n  case Error::IMPORT_NO_MODULE:\n    return fmt::format(fmt::runtime(\"no module named '{}'\"), args...);\n  case Error::IMPORT_NO_NAME:\n    return fmt::format(fmt::runtime(\"cannot import name '{}' from '{}'\"), args...);\n  case Error::DEL_NOT_ALLOWED:\n    return fmt::format(fmt::runtime(\"name '{}' cannot be deleted\"), args...);\n  case Error::DEL_INVALID:\n    return fmt::format(fmt::runtime(\"cannot delete given expression\"), args...);\n  case Error::ASSIGN_INVALID:\n    return fmt::format(\"cannot assign to given expression\");\n  case Error::ASSIGN_LOCAL_REFERENCE:\n    return fmt::format(\n        fmt::runtime(\"local variable '{}' referenced before assignment at {}\"),\n        args...);\n  case Error::ASSIGN_MULTI_STAR:\n    return fmt::format(\"multiple starred expressions in assignment\");\n  case Error::INT_RANGE:\n    return fmt::format(fmt::runtime(\"integer '{}' cannot fit into 64-bit integer\"),\n                       args...);\n  case Error::FLOAT_RANGE:\n    return fmt::format(fmt::runtime(\"float '{}' cannot fit into 64-bit float\"),\n                       args...);\n  case Error::STR_FSTRING_BALANCE_EXTRA:\n    return fmt::format(\"expecting '}}' in f-string\");\n  case Error::STR_FSTRING_BALANCE_MISSING:\n    return fmt::format(\"single '}}' is not allowed in f-string\");\n  case Error::CALL_NO_TYPE:\n    return fmt::format(fmt::runtime(\"cannot use calls in type signatures\"), args...);\n  case Error::CALL_TUPLE_COMPREHENSION:\n    return fmt::format(\n        fmt::runtime(\n            \"tuple constructor does not accept nested or conditioned comprehensions\"),\n        args...);\n  case Error::CALL_NAMEDTUPLE:\n    return fmt::format(fmt::runtime(\"namedtuple() takes 2 static arguments\"), args...);\n  case Error::CALL_PARTIAL:\n    return fmt::format(fmt::runtime(\"partial() takes 1 or more arguments\"), args...);\n  case Error::EXPECTED_TOPLEVEL:\n    return fmt::format(fmt::runtime(\"{} must be a top-level statement\"), args...);\n  case Error::CLASS_ID_NOT_FOUND:\n    // Note that type aliases are not valid class names\n    return fmt::format(fmt::runtime(\"class name '{}' is not defined\"), args...);\n  case Error::CLASS_INVALID_BIND:\n    return fmt::format(fmt::runtime(\"cannot bind '{}' to class or function\"), args...);\n  case Error::CLASS_NO_INHERIT:\n    return fmt::format(fmt::runtime(\"{} classes cannot inherit {} classes\"), args...);\n  case Error::CLASS_NO_EXTEND:\n    return fmt::format(fmt::runtime(\"'{}' cannot be extended\"), args...);\n  case Error::CLASS_TUPLE_INHERIT:\n    return fmt::format(\"reference classes cannot inherit tuple classes\");\n  case Error::CLASS_BAD_MRO:\n    return fmt::format(\"inconsistent class hierarchy\");\n  case Error::CLASS_BAD_ATTR:\n    return fmt::format(\"unexpected expression in class definition\");\n  case Error::MATCH_MULTI_ELLIPSIS:\n    return fmt::format(\"multiple ellipses in a pattern\");\n  case Error::FN_OUTSIDE_ERROR:\n    return fmt::format(fmt::runtime(\"'{}' outside function\"), args...);\n  case Error::FN_GLOBAL_ASSIGNED:\n    return fmt::format(\n        fmt::runtime(\"name '{}' is assigned to before global declaration\"), args...);\n  case Error::FN_GLOBAL_NOT_FOUND:\n    return fmt::format(fmt::runtime(\"no binding for {} '{}' found\"), args...);\n  case Error::FN_BAD_LLVM:\n    return fmt::format(\"invalid LLVM code\");\n  case Error::FN_REALIZE_BUILTIN:\n    return fmt::format(\"builtin, exported and external functions cannot be generic\");\n  case Error::EXPECTED_LOOP:\n    return fmt::format(fmt::runtime(\"'{}' outside loop\"), args...);\n  case Error::LOOP_DECORATOR:\n    return fmt::format(\"invalid loop decorator\");\n  case Error::BAD_STATIC_TYPE:\n    return fmt::format(\"expected 'int', 'bool' or 'str'\");\n  case Error::EXPECTED_TYPE:\n    return fmt::format(fmt::runtime(\"expected {} expression\"), args...);\n  case Error::UNEXPECTED_TYPE:\n    return fmt::format(fmt::runtime(\"unexpected {} expression\"), args...);\n\n  /// Typechecking\n  case Error::UNION_TOO_BIG:\n    return fmt::format(\n        fmt::runtime(\n            \"union exceeded its maximum capacity (contains more than {} types)\"),\n        args...);\n  case Error::DOT_NO_ATTR:\n    return fmt::format(fmt::runtime(\"'{}' object has no attribute '{}'\"), args...);\n  case Error::DOT_NO_ATTR_ARGS:\n    return fmt::format(fmt::runtime(\"'{}' object has no method '{}' with arguments {}\"),\n                       args...);\n  case Error::FN_NO_ATTR_ARGS:\n    return fmt::format(fmt::runtime(\"no function '{}' with arguments {}\"), args...);\n  case Error::EXPECTED_STATIC:\n    return fmt::format(\"expected static expression\");\n  case Error::EXPECTED_STATIC_SPECIFIED:\n    return fmt::format(fmt::runtime(\"expected static {} expression\"), args...);\n  case Error::ASSIGN_UNEXPECTED_STATIC:\n    return fmt::format(\"cannot modify static expressions\");\n  case Error::ASSIGN_UNEXPECTED_FROZEN:\n    return fmt::format(\"cannot modify tuple attributes\");\n  case Error::CALL_BAD_UNPACK:\n    return fmt::format(fmt::runtime(\"argument after * must be a tuple, not '{}'\"),\n                       args...);\n  case Error::CALL_BAD_ITER:\n    return fmt::format(fmt::runtime(\"iterable must be a tuple, not '{}'\"), args...);\n  case Error::CALL_BAD_KWUNPACK:\n    return fmt::format(\n        fmt::runtime(\"argument after ** must be a named tuple, not '{}'\"), args...);\n  case Error::CALL_REPEATED_NAME:\n    return fmt::format(fmt::runtime(\"keyword argument repeated: {}\"), args...);\n  case Error::CALL_RECURSIVE_DEFAULT:\n    return fmt::format(fmt::runtime(\"argument '{}' has recursive default value\"),\n                       args...);\n  case Error::CALL_SUPERF:\n    return fmt::format(\"no superf methods found\");\n  case Error::CALL_SUPER_PARENT:\n    return fmt::format(\"no super methods found\");\n  case Error::CALL_PTR_VAR:\n    return fmt::format(\"__ptr__() only takes identifiers or tuple fields as arguments\");\n  case Error::EXPECTED_TUPLE:\n    return fmt::format(\"expected tuple type\");\n  case Error::CALL_REALIZED_FN:\n    return fmt::format(\"static.realized() only takes functions as a first argument\");\n  case Error::CALL_ARGS_MANY:\n    return fmt::format(fmt::runtime(\"{}() takes {} arguments ({} given)\"), args...);\n  case Error::CALL_ARGS_INVALID:\n    return fmt::format(fmt::runtime(\"'{}' is an invalid keyword argument for {}()\"),\n                       args...);\n  case Error::CALL_ARGS_MISSING:\n    return fmt::format(\n        fmt::runtime(\"{}() missing 1 required positional argument: '{}'\"), args...);\n  case Error::GENERICS_MISMATCH:\n    return fmt::format(fmt::runtime(\"{} takes {} generics ({} given)\"), args...);\n  case Error::EXPECTED_GENERATOR:\n    return fmt::format(\"expected iterable expression\");\n  case Error::STATIC_RANGE_BOUNDS:\n    return fmt::format(\n        fmt::runtime(\"static.range too large (expected 0..{}, got instead {})\"),\n        args...);\n  case Error::TUPLE_RANGE_BOUNDS:\n    return fmt::format(\n        fmt::runtime(\"tuple index out of range (expected 0..{}, got instead {})\"),\n        args...);\n  case Error::STATIC_DIV_ZERO:\n    return fmt::format(\"static division by zero\");\n  case Error::SLICE_STEP_ZERO:\n    return fmt::format(\"slice step cannot be zero\");\n  case Error::OP_NO_MAGIC:\n    return fmt::format(\n        fmt::runtime(\"unsupported operand type(s) for {}: '{}' and '{}'\"), args...);\n  case Error::INST_CALLABLE_STATIC:\n    return fmt::format(\"CallableTrait cannot take static types\");\n  case Error::CATCH_EXCEPTION_TYPE:\n    return fmt::format(fmt::runtime(\"'{}' does not inherit from BaseException\"),\n                       args...);\n\n  case Error::TYPE_CANNOT_REALIZE_ATTR:\n    return fmt::format(\n        fmt::runtime(\"type of attribute '{}' of object '{}' cannot be inferred\"),\n        args...);\n  case Error::TYPE_UNIFY:\n    return fmt::format(fmt::runtime(\"'{}' does not match expected type '{}'\"), args...);\n  case Error::TYPE_FAILED:\n    return fmt::format(\n        fmt::runtime(\n            \"cannot infer the complete type of an expression (inferred only '{}')\"),\n        args...);\n\n  case Error::COMPILER_NO_FILE:\n    return fmt::format(fmt::runtime(\"cannot open file '{}' for parsing\"), args...);\n  case Error::COMPILER_NO_STDLIB:\n    return fmt::format(\"cannot locate standard library\");\n  case Error::MAX_REALIZATION:\n    return fmt::format(\n        fmt::runtime(\n            \"maximum realization depth reached during the realization of '{}'\"),\n        args...);\n  case Error::CUSTOM:\n    return Eformat(args...);\n\n  default:\n    assert(false);\n  }\n}\n\ntemplate <class... TA>\nvoid E(Error e, const codon::SrcInfo &o = codon::SrcInfo(), const TA &...args) {\n  auto msg = Emsg(e, args...);\n  auto err = ParserErrors(ErrorMessage(msg, o, (int)e));\n  throw exc::ParserException(err);\n}\n\nvoid E(llvm::Error &&error);\n\n} // namespace error\n} // namespace codon\n"
  },
  {
    "path": "codon/compiler/jit.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"jit.h\"\n\n#include <sstream>\n\n#include \"codon/parser/common.h\"\n#include \"codon/parser/peg/peg.h\"\n#include \"codon/parser/visitors/doc/doc.h\"\n#include \"codon/parser/visitors/format/format.h\"\n#include \"codon/parser/visitors/scoping/scoping.h\"\n#include \"codon/parser/visitors/translate/translate.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nnamespace codon {\nnamespace jit {\nnamespace {\ntypedef int MainFunc(int, char **);\ntypedef void InputFunc();\ntypedef void *PyWrapperFunc(void *);\n\nconst std::string JIT_FILENAME = \"<jit>\";\n} // namespace\n\nJIT::JIT(const std::string &argv0, const std::string &mode,\n         const std::string &stdlibRoot)\n    : compiler(std::make_unique<Compiler>(argv0, Compiler::Mode::JIT,\n                                          /*disabledPasses=*/std::vector<std::string>{},\n                                          /*isTest=*/false,\n                                          /*pyNumerics=*/false, /*pyExtension=*/false)),\n      engine(std::make_unique<Engine>()), pydata(std::make_unique<PythonData>()),\n      mode(mode), forgetful(false) {\n  if (!stdlibRoot.empty())\n    compiler->getCache()->fs->add_search_path(stdlibRoot);\n  compiler->getLLVMVisitor()->setJIT(true);\n}\n\nvoid collectExecutableStmts(ast::Stmt *s, ast::SuiteStmt *final) {\n  if (cast<ast::FunctionStmt>(s) || cast<ast::ClassStmt>(s) ||\n      cast<ast::CommentStmt>(s))\n    return;\n  if (auto ss = ast::cast<ast::SuiteStmt>(s)) {\n    for (auto &si : *ss)\n      collectExecutableStmts(si, final);\n  } else if (s) {\n    final->addStmt(ast::clean_clone(s));\n  }\n}\n\nllvm::Error JIT::init(bool forgetful) {\n  if (forgetful) {\n    this->forgetful = true;\n    auto fs = std::make_shared<ast::ResourceFilesystem>(compiler->getArgv0(), \"\",\n                                                        /*allowExternal=*/false);\n    compiler->getCache()->fs = fs;\n  }\n\n  auto *cache = compiler->getCache();\n  auto *module = compiler->getModule();\n  auto *pm = compiler->getPassManager();\n  auto *llvisitor = compiler->getLLVMVisitor();\n\n  cache->isJit = true;\n  auto typechecked = ast::TypecheckVisitor::apply(\n      cache, cache->N<ast::SuiteStmt>(), JIT_FILENAME, {}, compiler->getEarlyDefines());\n  cache->isJit =\n      false; // we still need main(), so pause isJit first time during translation\n  ast::TranslateVisitor::apply(cache, std::move(typechecked));\n  cache->isJit = true;\n  module->setSrcInfo({JIT_FILENAME, 0, 0, 0});\n\n  pm->run(module);\n  module->accept(*llvisitor);\n  auto pair = llvisitor->takeModule(module);\n\n  if (auto err = engine->addModule({std::move(pair.first), std::move(pair.second)}))\n    return err;\n\n  auto func = engine->lookup(\"main\");\n  if (auto err = func.takeError())\n    return err;\n\n  auto *main = func->toPtr<MainFunc>();\n  (*main)(0, nullptr);\n  return llvm::Error::success();\n}\n\nllvm::Error JIT::compile(const ir::Func *input, llvm::orc::ResourceTrackerSP rt) {\n  auto *module = compiler->getModule();\n  auto *pm = compiler->getPassManager();\n  auto *llvisitor = compiler->getLLVMVisitor();\n\n  Timer t1(\"jit/ir\");\n  pm->run(module);\n  t1.log();\n\n  Timer t2(\"jit/llvm\");\n  auto pair = llvisitor->takeModule(module);\n  t2.log();\n\n  Timer t3(\"jit/engine\");\n  if (auto err = engine->addModule({std::move(pair.first), std::move(pair.second)}, rt))\n    return std::move(err);\n  t3.log();\n\n  return llvm::Error::success();\n}\n\nJITState::JITState(ast::Cache *cache, bool forgetful)\n    : cache(cache), forgetful(forgetful), bCache(*cache),\n      mainCtx(*(cache->imports[MAIN_IMPORT].ctx)),\n      stdlibCtx(*(cache->imports[STDLIB_IMPORT].ctx)), typeCtx(*(cache->typeCtx)),\n      translateCtx(*(cache->codegenCtx)) {}\n\nvoid JITState::undo() {\n  if (!forgetful)\n    undoUnusedIR();\n\n  *cache = bCache;\n  *(cache->imports[MAIN_IMPORT].ctx) = mainCtx;\n  *(cache->imports[STDLIB_IMPORT].ctx) = stdlibCtx;\n  *(cache->typeCtx) = typeCtx;\n  *(cache->codegenCtx) = translateCtx;\n\n  if (forgetful)\n    cleanUpRealizations();\n}\n\nvoid JITState::undoUnusedIR() {\n  // Clean-up unused IR nodes made before Typechecker raised an error\n  for (auto &f : cache->functions) {\n    for (auto &r : f.second.realizations) {\n      if (!(in(bCache.functions, f.first) &&\n            in(bCache.functions[f.first].realizations, r.first)) &&\n          r.second->ir) {\n        cache->module->remove(r.second->ir);\n      }\n    }\n  }\n}\n\nvoid JITState::cleanUpRealizations() {\n  // Clean-up IR nodes after single JIT input\n  // Nothing should be done here with a proper arena support.\n}\n\nllvm::Expected<ir::Func *> JIT::compile(const std::string &code,\n                                        const std::string &file, int line) {\n  auto *cache = compiler->getCache();\n  auto preamble = cache->N<ast::SuiteStmt>();\n\n  JITState state(cache, forgetful);\n\n  try {\n    auto nodeOrErr = ast::parseCode(cache, file.empty() ? JIT_FILENAME : file, code,\n                                    /*startLine=*/line);\n    if (!nodeOrErr)\n      throw exc::ParserException(nodeOrErr.takeError());\n    auto *node = *nodeOrErr;\n\n    ast::Stmt **e = &node;\n    while (auto se = ast::cast<ast::SuiteStmt>(*e)) {\n      if (se->empty())\n        break;\n      e = &se->back();\n    }\n    if (e)\n      if (auto ex = ast::cast<ast::ExprStmt>(*e)) {\n        *e = cache->N<ast::ExprStmt>(cache->N<ast::CallExpr>(\n            cache->N<ast::IdExpr>(\"_jit_display\"), clone(ex->getExpr()),\n            cache->N<ast::StringExpr>(mode)));\n      }\n    auto sctx = cache->imports[MAIN_IMPORT].ctx;\n    if (auto err = ast::ScopingVisitor::apply(sctx->cache, node, &sctx->globalShadows))\n      throw exc::ParserException(std::move(err));\n    auto tv = ast::TypecheckVisitor::apply(sctx, node, JIT_FILENAME);\n    auto typechecked = cache->N<ast::SuiteStmt>();\n    for (auto &s : *preamble)\n      typechecked->addStmt(s);\n    typechecked->addStmt(node);\n    // TODO: unroll on errors...\n\n    // add newly realized functions\n    std::vector<ast::Stmt *> v;\n    std::vector<ir::Func **> frs;\n    v.push_back(typechecked);\n    for (auto &p : cache->pendingRealizations) {\n      v.push_back(cache->functions[p.first].ast);\n      frs.push_back(&cache->functions[p.first].realizations[p.second]->ir);\n    }\n    auto func = ast::TranslateVisitor::apply(cache, cache->N<ast::SuiteStmt>(v));\n    cache->jitCell++;\n\n    return func;\n  } catch (const exc::ParserException &exc) {\n    state.undo();\n\n    return llvm::make_error<error::ParserErrorInfo>(exc.getErrors());\n  }\n}\n\nllvm::Expected<void *> JIT::address(const ir::Func *input,\n                                    llvm::orc::ResourceTrackerSP rt) {\n  if (auto err = compile(input, rt))\n    return std::move(err);\n\n  const std::string name = ir::LLVMVisitor::getNameForFunction(input);\n  auto func = engine->lookup(name);\n  if (auto err = func.takeError())\n    return std::move(err);\n\n  return (void *)func->getValue();\n}\n\nllvm::Expected<std::string> JIT::run(const ir::Func *input,\n                                     llvm::orc::ResourceTrackerSP rt) {\n  auto result = address(input, rt);\n  if (auto err = result.takeError())\n    return std::move(err);\n\n  auto *repl = (InputFunc *)result.get();\n  try {\n    (*repl)();\n  } catch (const runtime::JITError &e) {\n    return handleJITError(e);\n  }\n  return runtime::getCapturedOutput();\n}\n\nllvm::Expected<std::string> JIT::execute(const std::string &code,\n                                         const std::string &file, int line, bool debug,\n                                         llvm::orc::ResourceTrackerSP rt) {\n  if (debug)\n    fmt::print(stderr, \"[codon::jit::execute] code:\\n{}-----\\n\", code);\n\n  std::unique_ptr<JITState> state = nullptr;\n  if (forgetful)\n    state = std::make_unique<JITState>(compiler->getCache(), forgetful);\n\n  auto result = compile(code, file, line);\n  if (auto err = result.takeError())\n    return std::move(err);\n  if (auto err = compile(result.get(), rt))\n    return std::move(err);\n  auto r = run(result.get());\n\n  if (state)\n    state->undo();\n\n  return r;\n}\n\nllvm::Error JIT::handleJITError(const runtime::JITError &e) {\n  std::vector<std::string> backtrace;\n  for (auto pc : e.getBacktrace()) {\n    auto line = engine->getDebugListener()->getPrettyBacktrace(pc);\n    if (line && !line->empty())\n      backtrace.push_back(*line);\n  }\n  return llvm::make_error<error::RuntimeErrorInfo>(e.getOutput(), e.getType(), e.what(),\n                                                   e.getFile(), e.getLine(), e.getCol(),\n                                                   backtrace);\n}\n\nnamespace {\nstd::string buildKey(const std::string &name, const std::vector<std::string> &types) {\n  std::stringstream key;\n  key << name;\n  for (const auto &t : types) {\n    key << \"|\" << t;\n  }\n  return key.str();\n}\n\nstd::string buildPythonWrapper(const std::string &name, const std::string &wrapname,\n                               const std::vector<std::string> &types,\n                               const std::string &pyModule,\n                               const std::vector<std::string> &pyVars) {\n  std::stringstream wrap;\n  wrap << \"@export\\n\";\n  wrap << \"def \" << wrapname << \"(args: cobj) -> cobj:\\n\";\n  for (unsigned i = 0; i < types.size(); i++) {\n    wrap << \"    \"\n         << \"a\" << i << \" = \" << types[i] << \".__from_py__(PyTuple_GetItem(args, \" << i\n         << \"))\\n\";\n  }\n  for (unsigned i = 0; i < pyVars.size(); i++) {\n    wrap << \"    \"\n         << \"py\" << i << \" = pyobj._get_module(\\\"\" << pyModule << \"\\\")._getattr(\\\"\"\n         << pyVars[i] << \"\\\")\\n\";\n  }\n  wrap << \"    return \" << name << \"(\";\n  for (unsigned i = 0; i < types.size(); i++) {\n    if (i > 0)\n      wrap << \", \";\n    wrap << \"a\" << i;\n  }\n  for (unsigned i = 0; i < pyVars.size(); i++) {\n    if (i > 0 || types.size() > 0)\n      wrap << \", \";\n    wrap << \"py\" << i;\n  }\n  wrap << \").__to_py__()\\n\";\n\n  return wrap.str();\n}\n} // namespace\n\nJIT::PythonData::PythonData() : cobj(nullptr), cache() {}\n\nir::types::Type *JIT::PythonData::getCObjType(ir::Module *M) {\n  if (cobj)\n    return cobj;\n  cobj = M->getPointerType(M->getByteType());\n  return cobj;\n}\n\nJIT::JITResult JIT::executeSafe(const std::string &code, const std::string &file,\n                                int line, bool debug) {\n  auto result = execute(code, file, line, debug);\n  if (auto err = result.takeError()) {\n    auto errorInfo = llvm::toString(std::move(err));\n    return JITResult::error(errorInfo);\n  }\n  return JITResult::success();\n}\n\nJIT::JITResult JIT::executePython(const std::string &name,\n                                  const std::vector<std::string> &types,\n                                  const std::string &pyModule,\n                                  const std::vector<std::string> &pyVars, void *arg,\n                                  bool debug) {\n  auto key = buildKey(name, types);\n  auto &cache = pydata->cache;\n  auto it = cache.find(key);\n  PyWrapperFunc *wrap;\n\n  if (it != cache.end()) {\n    auto *wrapper = it->second;\n    const std::string name = ir::LLVMVisitor::getNameForFunction(wrapper);\n    auto func = llvm::cantFail(engine->lookup(name));\n    wrap = func.toPtr<PyWrapperFunc>();\n  } else {\n    static int idx = 0;\n    auto wrapname = \"__codon_wrapped__\" + name + \"_\" + std::to_string(idx++);\n    auto wrapper = buildPythonWrapper(name, wrapname, types, pyModule, pyVars);\n    if (debug)\n      fmt::print(stderr, \"[codon::jit::executePython] wrapper:\\n{}-----\\n\", wrapper);\n    if (auto err = compile(wrapper).takeError()) {\n      auto errorInfo = llvm::toString(std::move(err));\n      return JITResult::error(errorInfo);\n    }\n\n    auto *M = compiler->getModule();\n    auto *func = M->getOrRealizeFunc(wrapname, {pydata->getCObjType(M)});\n    seqassertn(func, \"could not access wrapper func '{}'\", wrapname);\n    cache.emplace(key, func);\n\n    auto result = address(func);\n    if (auto err = result.takeError()) {\n      auto errorInfo = llvm::toString(std::move(err));\n      return JITResult::error(errorInfo);\n    }\n    wrap = (PyWrapperFunc *)result.get();\n  }\n\n  try {\n    auto *ans = (*wrap)(arg);\n    return JITResult::success(ans);\n  } catch (const runtime::JITError &e) {\n    auto err = handleJITError(e);\n    auto errorInfo = llvm::toString(std::move(err));\n    return JITResult::error(errorInfo);\n  }\n}\n\n} // namespace jit\n} // namespace codon\n\nvoid *jit_init(char *name) {\n  auto jit = new codon::jit::JIT(std::string(name));\n  llvm::cantFail(jit->init());\n  return jit;\n}\n\nvoid jit_exit(void *jit) { delete ((codon::jit::JIT *)jit); }\n\nCJITResult jit_execute_python(void *jit, char *name, char **types, size_t types_size,\n                              char *pyModule, char **py_vars, size_t py_vars_size,\n                              void *arg, uint8_t debug) {\n  std::vector<std::string> cppTypes;\n  cppTypes.reserve(types_size);\n  for (size_t i = 0; i < types_size; i++)\n    cppTypes.emplace_back(types[i]);\n  std::vector<std::string> cppPyVars;\n  cppPyVars.reserve(py_vars_size);\n  for (size_t i = 0; i < py_vars_size; i++)\n    cppPyVars.emplace_back(py_vars[i]);\n  auto t = ((codon::jit::JIT *)jit)\n               ->executePython(std::string(name), cppTypes, std::string(pyModule),\n                               cppPyVars, arg, bool(debug));\n  void *result = t.result;\n  char *message =\n      t.message.empty() ? nullptr : strndup(t.message.c_str(), t.message.size());\n  return {result, message};\n}\n\nCJITResult jit_execute_safe(void *jit, char *code, char *file, int32_t line,\n                            uint8_t debug) {\n  auto t = ((codon::jit::JIT *)jit)\n               ->executeSafe(std::string(code), std::string(file), line, bool(debug));\n  void *result = t.result;\n  char *message =\n      t.message.empty() ? nullptr : strndup(t.message.c_str(), t.message.size());\n  return {result, message};\n}\n\nchar *get_jit_library() {\n  auto t = codon::ast::library_path();\n  return strndup(t.c_str(), t.size());\n}\n"
  },
  {
    "path": "codon/compiler/jit.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"codon/cir/llvm/llvisitor.h\"\n#include \"codon/cir/transform/manager.h\"\n#include \"codon/cir/var.h\"\n#include \"codon/compiler/compiler.h\"\n#include \"codon/compiler/engine.h\"\n#include \"codon/compiler/error.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/visitors/translate/translate.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n#include \"codon/runtime/lib.h\"\n\n#include \"codon/compiler/jit_extern.h\"\n\nnamespace codon {\nnamespace jit {\n\nclass JITState {\n  ast::Cache *cache;\n  bool forgetful;\n\n  ast::Cache bCache;\n  ast::TypeContext mainCtx;\n  ast::TypeContext stdlibCtx;\n  ast::TypeContext typeCtx;\n  ast::TranslateContext translateCtx;\n\npublic:\n  explicit JITState(ast::Cache *cache, bool forgetful = false);\n\n  void undo();\n  void undoUnusedIR();\n  void cleanUpRealizations();\n};\n\nclass JIT {\npublic:\n  struct PythonData {\n    ir::types::Type *cobj;\n    std::unordered_map<std::string, ir::Func *> cache;\n\n    PythonData();\n    ir::types::Type *getCObjType(ir::Module *M);\n  };\n\n  struct JITResult {\n    void *result;\n    std::string message;\n\n    operator bool() const { return message.empty(); }\n    static JITResult success(void *result = nullptr) { return {result, \"\"}; }\n    static JITResult error(const std::string &message) { return {nullptr, message}; }\n  };\n\nprivate:\n  std::unique_ptr<Compiler> compiler;\n  std::unique_ptr<Engine> engine;\n  std::unique_ptr<PythonData> pydata;\n  std::string mode;\n  bool forgetful = false;\n\npublic:\n  explicit JIT(const std::string &argv0, const std::string &mode = \"\",\n               const std::string &stdlibRoot = \"\");\n\n  Compiler *getCompiler() const { return compiler.get(); }\n  Engine *getEngine() const { return engine.get(); }\n\n  // General\n  llvm::Error init(bool forgetful = false);\n  llvm::Error compile(const ir::Func *input, llvm::orc::ResourceTrackerSP rt = nullptr);\n  llvm::Expected<ir::Func *> compile(const std::string &code,\n                                     const std::string &file = \"\", int line = 0);\n  llvm::Expected<void *> address(const ir::Func *input,\n                                 llvm::orc::ResourceTrackerSP rt = nullptr);\n  llvm::Expected<std::string> run(const ir::Func *input,\n                                  llvm::orc::ResourceTrackerSP rt = nullptr);\n  llvm::Expected<std::string> execute(const std::string &code,\n                                      const std::string &file = \"\", int line = 0,\n                                      bool debug = false,\n                                      llvm::orc::ResourceTrackerSP rt = nullptr);\n\n  // Python\n  llvm::Expected<void *> runPythonWrapper(const ir::Func *wrapper, void *arg);\n  llvm::Expected<ir::Func *> getWrapperFunc(const std::string &name,\n                                            const std::vector<std::string> &types);\n  JITResult executePython(const std::string &name,\n                          const std::vector<std::string> &types,\n                          const std::string &pyModule,\n                          const std::vector<std::string> &pyVars, void *arg,\n                          bool debug);\n  JITResult executeSafe(const std::string &code, const std::string &file, int line,\n                        bool debug);\n\n  // Errors\n  llvm::Error handleJITError(const runtime::JITError &e);\n};\n\n} // namespace jit\n} // namespace codon\n"
  },
  {
    "path": "codon/compiler/jit_extern.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <stddef.h>\n#include <stdint.h>\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct CJITResult {\n  void *result;\n  char *error;\n};\n\nvoid *jit_init(char *name);\nvoid jit_exit(void *jit);\n\nstruct CJITResult jit_execute_python(void *jit, char *name, char **types,\n                                     size_t types_size, char *pyModule, char **py_vars,\n                                     size_t py_vars_size, void *arg, uint8_t debug);\n\nstruct CJITResult jit_execute_safe(void *jit, char *code, char *file, int32_t line,\n                                   uint8_t debug);\n\nchar *get_jit_library();\n\n#ifdef __cplusplus\n}\n#endif\n"
  },
  {
    "path": "codon/compiler/memory_manager.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"memory_manager.h\"\n\n#include \"codon/runtime/lib.h\"\n\nnamespace codon {\n\nclass BoehmGCJITLinkMemoryManager::IPInFlightAlloc\n    : public llvm::jitlink::JITLinkMemoryManager::InFlightAlloc {\npublic:\n  IPInFlightAlloc(BoehmGCJITLinkMemoryManager &MemMgr, llvm::jitlink::LinkGraph &G,\n                  llvm::jitlink::BasicLayout BL,\n                  llvm::sys::MemoryBlock StandardSegments,\n                  llvm::sys::MemoryBlock FinalizationSegments)\n      : MemMgr(MemMgr), G(G), BL(std::move(BL)),\n        StandardSegments(std::move(StandardSegments)),\n        FinalizationSegments(std::move(FinalizationSegments)) {}\n\n  void finalize(OnFinalizedFunction OnFinalized) override {\n\n    // Apply memory protections to all segments.\n    if (auto Err = applyProtections()) {\n      OnFinalized(std::move(Err));\n      return;\n    }\n\n    // Run finalization actions.\n    auto DeallocActions = runFinalizeActions(G.allocActions());\n    if (!DeallocActions) {\n      OnFinalized(DeallocActions.takeError());\n      return;\n    }\n\n    // Release the finalize segments slab.\n    if (auto EC = llvm::sys::Memory::releaseMappedMemory(FinalizationSegments)) {\n      OnFinalized(llvm::errorCodeToError(EC));\n      return;\n    }\n\n    // Continue with finalized allocation.\n    OnFinalized(MemMgr.createFinalizedAlloc(std::move(StandardSegments),\n                                            std::move(*DeallocActions)));\n  }\n\n  void abandon(OnAbandonedFunction OnAbandoned) override {\n    llvm::Error Err = llvm::Error::success();\n    if (auto EC = llvm::sys::Memory::releaseMappedMemory(FinalizationSegments))\n      Err = llvm::joinErrors(std::move(Err), llvm::errorCodeToError(EC));\n    if (auto EC = llvm::sys::Memory::releaseMappedMemory(StandardSegments))\n      Err = llvm::joinErrors(std::move(Err), llvm::errorCodeToError(EC));\n    OnAbandoned(std::move(Err));\n  }\n\nprivate:\n  llvm::Error applyProtections() {\n    for (auto &KV : BL.segments()) {\n      const auto &AG = KV.first;\n      auto &Seg = KV.second;\n\n      auto Prot = toSysMemoryProtectionFlags(AG.getMemProt());\n\n      uint64_t SegSize =\n          llvm::alignTo(Seg.ContentSize + Seg.ZeroFillSize, MemMgr.PageSize);\n      llvm::sys::MemoryBlock MB(Seg.WorkingMem, SegSize);\n      if (auto EC = llvm::sys::Memory::protectMappedMemory(MB, Prot))\n        return llvm::errorCodeToError(EC);\n      if (Prot & llvm::sys::Memory::MF_EXEC)\n        llvm::sys::Memory::InvalidateInstructionCache(MB.base(), MB.allocatedSize());\n    }\n    return llvm::Error::success();\n  }\n\n  BoehmGCJITLinkMemoryManager &MemMgr;\n  llvm::jitlink::LinkGraph &G;\n  llvm::jitlink::BasicLayout BL;\n  llvm::sys::MemoryBlock StandardSegments;\n  llvm::sys::MemoryBlock FinalizationSegments;\n};\n\nllvm::Expected<std::unique_ptr<BoehmGCJITLinkMemoryManager>>\nBoehmGCJITLinkMemoryManager::Create() {\n  if (auto PageSize = llvm::sys::Process::getPageSize()) {\n    if (!llvm::isPowerOf2_64((uint64_t)*PageSize))\n      return llvm::make_error<llvm::StringError>(\"Page size is not a power of 2\",\n                                                 llvm::inconvertibleErrorCode());\n    return std::make_unique<BoehmGCJITLinkMemoryManager>(*PageSize);\n  } else {\n    return PageSize.takeError();\n  }\n}\n\nvoid BoehmGCJITLinkMemoryManager::allocate(const llvm::jitlink::JITLinkDylib *JD,\n                                           llvm::jitlink::LinkGraph &G,\n                                           OnAllocatedFunction OnAllocated) {\n  llvm::jitlink::BasicLayout BL(G);\n\n  /// Scan the request and calculate the group and total sizes.\n  /// Check that segment size is no larger than a page.\n  auto SegsSizes = BL.getContiguousPageBasedLayoutSizes(PageSize);\n  if (!SegsSizes) {\n    OnAllocated(SegsSizes.takeError());\n    return;\n  }\n\n  /// Check that the total size requested (including zero fill) is not larger\n  /// than a size_t.\n  if (SegsSizes->total() > std::numeric_limits<size_t>::max()) {\n    OnAllocated(llvm::make_error<llvm::jitlink::JITLinkError>(\n        \"Total requested size \" + llvm::formatv(\"{0:x}\", SegsSizes->total()) +\n        \" for graph \" + G.getName() + \" exceeds address space\"));\n    return;\n  }\n\n  // Allocate one slab for the whole thing (to make sure everything is\n  // in-range), then partition into standard and finalization blocks.\n  //\n  // FIXME: Make two separate allocations in the future to reduce\n  // fragmentation: finalization segments will usually be a single page, and\n  // standard segments are likely to be more than one page. Where multiple\n  // allocations are in-flight at once (likely) the current approach will leave\n  // a lot of single-page holes.\n  llvm::sys::MemoryBlock Slab;\n  llvm::sys::MemoryBlock StandardSegsMem;\n  llvm::sys::MemoryBlock FinalizeSegsMem;\n  {\n    const llvm::sys::Memory::ProtectionFlags ReadWrite =\n        static_cast<llvm::sys::Memory::ProtectionFlags>(llvm::sys::Memory::MF_READ |\n                                                        llvm::sys::Memory::MF_WRITE);\n\n    std::error_code EC;\n    Slab = llvm::sys::Memory::allocateMappedMemory(SegsSizes->total(), nullptr,\n                                                   ReadWrite, EC);\n\n    if (EC) {\n      OnAllocated(llvm::errorCodeToError(EC));\n      return;\n    }\n\n    // Zero-fill the whole slab up-front.\n    memset(Slab.base(), 0, Slab.allocatedSize());\n\n    StandardSegsMem = {Slab.base(), static_cast<size_t>(SegsSizes->StandardSegs)};\n    FinalizeSegsMem = {(void *)((char *)Slab.base() + SegsSizes->StandardSegs),\n                       static_cast<size_t>(SegsSizes->FinalizeSegs)};\n  }\n\n  auto NextStandardSegAddr = llvm::orc::ExecutorAddr::fromPtr(StandardSegsMem.base());\n  auto NextFinalizeSegAddr = llvm::orc::ExecutorAddr::fromPtr(FinalizeSegsMem.base());\n\n  // Build ProtMap, assign addresses.\n  for (auto &KV : BL.segments()) {\n    auto &AG = KV.first;\n    auto &Seg = KV.second;\n\n    auto &SegAddr = (AG.getMemLifetime() == llvm::orc::MemLifetime::Standard)\n                        ? NextStandardSegAddr\n                        : NextFinalizeSegAddr;\n\n    Seg.WorkingMem = SegAddr.toPtr<char *>();\n    Seg.Addr = SegAddr;\n\n    SegAddr += llvm::alignTo(Seg.ContentSize + Seg.ZeroFillSize, PageSize);\n\n    if (static_cast<int>(AG.getMemProt()) &\n        static_cast<int>(llvm::orc::MemProt::Write)) {\n      seq_gc_add_roots((void *)Seg.Addr.getValue(), (void *)SegAddr.getValue());\n    }\n  }\n\n  if (auto Err = BL.apply()) {\n    OnAllocated(std::move(Err));\n    return;\n  }\n\n  OnAllocated(std::make_unique<IPInFlightAlloc>(\n      *this, G, std::move(BL), std::move(StandardSegsMem), std::move(FinalizeSegsMem)));\n}\n\nvoid BoehmGCJITLinkMemoryManager::deallocate(std::vector<FinalizedAlloc> Allocs,\n                                             OnDeallocatedFunction OnDeallocated) {\n  std::vector<llvm::sys::MemoryBlock> StandardSegmentsList;\n  std::vector<std::vector<llvm::orc::shared::WrapperFunctionCall>> DeallocActionsList;\n\n  {\n    std::lock_guard<std::mutex> Lock(FinalizedAllocsMutex);\n    for (auto &Alloc : Allocs) {\n      auto *FA = Alloc.release().toPtr<FinalizedAllocInfo *>();\n      StandardSegmentsList.push_back(std::move(FA->StandardSegments));\n      DeallocActionsList.push_back(std::move(FA->DeallocActions));\n      FA->~FinalizedAllocInfo();\n      FinalizedAllocInfos.Deallocate(FA);\n    }\n  }\n\n  llvm::Error DeallocErr = llvm::Error::success();\n\n  while (!DeallocActionsList.empty()) {\n    auto &DeallocActions = DeallocActionsList.back();\n    auto &StandardSegments = StandardSegmentsList.back();\n\n    /// Run any deallocate calls.\n    while (!DeallocActions.empty()) {\n      if (auto Err = DeallocActions.back().runWithSPSRetErrorMerged())\n        DeallocErr = llvm::joinErrors(std::move(DeallocErr), std::move(Err));\n      DeallocActions.pop_back();\n    }\n\n    /// Release the standard segments slab.\n    if (auto EC = llvm::sys::Memory::releaseMappedMemory(StandardSegments))\n      DeallocErr = llvm::joinErrors(std::move(DeallocErr), llvm::errorCodeToError(EC));\n\n    DeallocActionsList.pop_back();\n    StandardSegmentsList.pop_back();\n  }\n\n  OnDeallocated(std::move(DeallocErr));\n}\n\nllvm::jitlink::JITLinkMemoryManager::FinalizedAlloc\nBoehmGCJITLinkMemoryManager::createFinalizedAlloc(\n    llvm::sys::MemoryBlock StandardSegments,\n    std::vector<llvm::orc::shared::WrapperFunctionCall> DeallocActions) {\n  std::lock_guard<std::mutex> Lock(FinalizedAllocsMutex);\n  auto *FA = FinalizedAllocInfos.Allocate<FinalizedAllocInfo>();\n  new (FA) FinalizedAllocInfo({std::move(StandardSegments), std::move(DeallocActions)});\n  return FinalizedAlloc(llvm::orc::ExecutorAddr::fromPtr(FA));\n}\n\n} // namespace codon\n"
  },
  {
    "path": "codon/compiler/memory_manager.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <mutex>\n#include <utility>\n#include <vector>\n\n#include \"codon/cir/llvm/llvm.h\"\n\nnamespace codon {\n\n/// Basically a copy of LLVM's jitlink::InProcessMemoryManager that registers\n/// relevant allocated sections with the GC. TODO: Avoid copying this entire\n/// class if/when there's an API to perform the registration externally.\nclass BoehmGCJITLinkMemoryManager : public llvm::jitlink::JITLinkMemoryManager {\npublic:\n  class IPInFlightAlloc;\n\n  /// Attempts to auto-detect the host page size.\n  static llvm::Expected<std::unique_ptr<BoehmGCJITLinkMemoryManager>> Create();\n\n  /// Create an instance using the given page size.\n  BoehmGCJITLinkMemoryManager(uint64_t PageSize) : PageSize(PageSize) {}\n\n  void allocate(const llvm::jitlink::JITLinkDylib *JD, llvm::jitlink::LinkGraph &G,\n                OnAllocatedFunction OnAllocated) override;\n\n  // Use overloads from base class.\n  using llvm::jitlink::JITLinkMemoryManager::allocate;\n\n  void deallocate(std::vector<FinalizedAlloc> Alloc,\n                  OnDeallocatedFunction OnDeallocated) override;\n\n  // Use overloads from base class.\n  using llvm::jitlink::JITLinkMemoryManager::deallocate;\n\nprivate:\n  // FIXME: Use an in-place array instead of a vector for DeallocActions.\n  //        There shouldn't need to be a heap alloc for this.\n  struct FinalizedAllocInfo {\n    llvm::sys::MemoryBlock StandardSegments;\n    std::vector<llvm::orc::shared::WrapperFunctionCall> DeallocActions;\n  };\n\n  FinalizedAlloc createFinalizedAlloc(\n      llvm::sys::MemoryBlock StandardSegments,\n      std::vector<llvm::orc::shared::WrapperFunctionCall> DeallocActions);\n\n  uint64_t PageSize;\n  std::mutex FinalizedAllocsMutex;\n  llvm::RecyclingAllocator<llvm::BumpPtrAllocator, FinalizedAllocInfo>\n      FinalizedAllocInfos;\n};\n\n} // namespace codon\n"
  },
  {
    "path": "codon/config/.gitignore",
    "content": "*\n*/\n!.gitignore\n"
  },
  {
    "path": "codon/dsl/dsl.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/cir/cir.h\"\n#include \"codon/cir/transform/manager.h\"\n#include \"codon/cir/transform/pass.h\"\n#include \"codon/parser/cache.h\"\n#include \"llvm/Passes/PassBuilder.h\"\n#include <functional>\n#include <string>\n#include <vector>\n\nnamespace codon {\n\n/// Base class for DSL plugins. Plugins will return an instance of\n/// a child of this class, which defines various characteristics of\n/// the DSL, like keywords and IR passes.\nclass DSL {\npublic:\n  /// General information about this plugin.\n  struct Info {\n    /// Extension name\n    std::string name;\n    /// Extension description\n    std::string description;\n    /// Extension version\n    std::string version;\n    /// Extension URL\n    std::string url;\n    /// Supported Codon versions (semver range)\n    std::string supported;\n    /// Plugin stdlib path\n    std::string stdlibPath;\n    /// Plugin dynamic library path\n    std::string dylibPath;\n    /// Linker arguments (to replace \"-l dylibPath\" if present)\n    std::vector<std::string> linkArgs;\n  };\n\n  using KeywordCallback =\n      std::function<ast::Stmt *(ast::TypecheckVisitor *, ast::CustomStmt *)>;\n\n  struct ExprKeyword {\n    std::string keyword;\n    KeywordCallback callback;\n  };\n\n  struct BlockKeyword {\n    std::string keyword;\n    KeywordCallback callback;\n    bool hasExpr;\n  };\n\n  virtual ~DSL() noexcept = default;\n\n  /// Registers this DSL's IR passes with the given pass manager.\n  /// @param pm the pass manager to add the passes to\n  /// @param debug true if compiling in debug mode\n  virtual void addIRPasses(ir::transform::PassManager *pm, bool debug) {}\n\n  /// Registers this DSL's LLVM passes with the given pass builder.\n  /// @param pb the pass builder to add the passes to\n  /// @param debug true if compiling in debug mode\n  virtual void addLLVMPasses(llvm::PassBuilder *pb, bool debug) {}\n\n  /// Returns a vector of \"expression keywords\", defined as keywords of\n  /// the form \"keyword <expr>\".\n  /// @return this DSL's expression keywords\n  virtual std::vector<ExprKeyword> getExprKeywords() { return {}; }\n\n  /// Returns a vector of \"block keywords\", defined as keywords of the\n  /// form \"keyword <expr>: <block of code>\".\n  /// @return this DSL's block keywords\n  virtual std::vector<BlockKeyword> getBlockKeywords() { return {}; }\n};\n\n} // namespace codon\n"
  },
  {
    "path": "codon/dsl/plugins.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"plugins.h\"\n\n#include <cstdlib>\n#include <semver.hpp>\n#include <toml++/toml.h>\n\n#include \"codon/parser/common.h\"\n#include \"codon/util/common.h\"\n#include \"llvm/ADT/SmallString.h\"\n#include \"llvm/Support/FileSystem.h\"\n#include \"llvm/Support/Path.h\"\n\nnamespace codon {\nnamespace {\nllvm::Expected<Plugin *> pluginError(const std::string &msg) {\n  return llvm::make_error<error::PluginErrorInfo>(msg);\n}\n\ntypedef std::unique_ptr<DSL> LoadFunc();\n} // namespace\n\nllvm::Expected<Plugin *> PluginManager::load(const std::string &path) {\n#if __APPLE__\n  const std::string libExt = \"dylib\";\n#else\n  const std::string libExt = \"so\";\n#endif\n\n  const std::string config = \"plugin.toml\";\n\n  llvm::SmallString<128> tomlPath(path);\n  llvm::sys::path::append(tomlPath, config);\n  if (!llvm::sys::fs::exists(tomlPath)) {\n    // try default install path\n    std::string s = ast::Filesystem::executable_path(argv0.c_str());\n    tomlPath = llvm::SmallString<128>(llvm::sys::path::parent_path(s));\n    llvm::sys::path::append(tomlPath, \"../lib/codon/plugins\", path, config);\n  }\n\n  toml::parse_result tml;\n  try {\n    tml = toml::parse_file(tomlPath.str());\n  } catch (const toml::parse_error &e) {\n    return pluginError(\n        fmt::format(\"[toml::parse_file(\\\"{}\\\")] {}\", tomlPath.str(), e.what()));\n  }\n  auto about = tml[\"about\"];\n  auto library = tml[\"library\"];\n\n  std::string cppLib = library[\"cpp\"].value_or(\"\");\n  std::string dylibPath;\n  if (!cppLib.empty()) {\n    llvm::SmallString<128> p = llvm::sys::path::parent_path(tomlPath);\n    llvm::sys::path::append(p, cppLib + \".\" + libExt);\n    dylibPath = p.str();\n  }\n\n  auto link = library[\"link\"];\n  std::vector<std::string> linkArgs;\n  if (auto arr = link.as_array()) {\n    arr->for_each([&linkArgs](auto &&el) {\n      std::string l = el.value_or(\"\");\n      if (!l.empty())\n        linkArgs.push_back(l);\n    });\n  } else {\n    std::string l = link.value_or(\"\");\n    if (!l.empty())\n      linkArgs.push_back(l);\n  }\n  for (auto &l : linkArgs)\n    l = fmt::format(fmt::runtime(l),\n                    fmt::arg(\"root\", llvm::sys::path::parent_path(tomlPath)));\n\n  std::string codonLib = library[\"codon\"].value_or(\"\");\n  std::string stdlibPath;\n  if (!codonLib.empty()) {\n    llvm::SmallString<128> p = llvm::sys::path::parent_path(tomlPath);\n    llvm::sys::path::append(p, codonLib);\n    stdlibPath = p.str();\n  }\n\n  DSL::Info info = {about[\"name\"].value_or(\"\"),\n                    about[\"description\"].value_or(\"\"),\n                    about[\"version\"].value_or(\"\"),\n                    about[\"url\"].value_or(\"\"),\n                    about[\"supported\"].value_or(\"\"),\n                    stdlibPath,\n                    dylibPath,\n                    linkArgs};\n\n  bool versionOk = false;\n  try {\n    versionOk = semver::range::satisfies(\n        semver::version(CODON_VERSION_MAJOR, CODON_VERSION_MINOR, CODON_VERSION_PATCH),\n        info.supported);\n  } catch (const std::invalid_argument &e) {\n    return pluginError(fmt::format(\"[semver::range::satisfies(..., \\\"{}\\\")] {}\",\n                                   info.supported, e.what()));\n  }\n  if (!versionOk)\n    return pluginError(fmt::format(\"unsupported version {} (supported: {})\",\n                                   CODON_VERSION, info.supported));\n\n  if (!dylibPath.empty()) {\n    std::string libLoadErrorMsg;\n    auto handle = llvm::sys::DynamicLibrary::getPermanentLibrary(dylibPath.c_str(),\n                                                                 &libLoadErrorMsg);\n    if (!handle.isValid())\n      return pluginError(fmt::format(\n          \"[llvm::sys::DynamicLibrary::getPermanentLibrary(\\\"{}\\\", ...)] {}\", dylibPath,\n          libLoadErrorMsg));\n\n    auto *entry = (LoadFunc *)handle.getAddressOfSymbol(\"load\");\n    if (!entry)\n      return pluginError(\n          fmt::format(\"could not find 'load' in plugin shared library: {}\", dylibPath));\n\n    auto dsl = (*entry)();\n    plugins.push_back(std::make_unique<Plugin>(std::move(dsl), info, handle));\n  } else {\n    plugins.push_back(std::make_unique<Plugin>(std::make_unique<DSL>(), info,\n                                               llvm::sys::DynamicLibrary()));\n  }\n  return plugins.back().get();\n}\n\n} // namespace codon\n"
  },
  {
    "path": "codon/dsl/plugins.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <functional>\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"codon/cir/util/iterators.h\"\n#include \"codon/compiler/error.h\"\n#include \"codon/dsl/dsl.h\"\n#include \"llvm/Support/DynamicLibrary.h\"\n\nnamespace codon {\n\n/// Plugin metadata\nstruct Plugin {\n  /// the associated DSL\n  std::unique_ptr<DSL> dsl;\n  /// plugin information\n  DSL::Info info;\n  /// library handle\n  llvm::sys::DynamicLibrary handle;\n\n  Plugin(std::unique_ptr<DSL> dsl, DSL::Info info, llvm::sys::DynamicLibrary handle)\n      : dsl(std::move(dsl)), info(std::move(info)), handle(std::move(handle)) {}\n};\n\n/// Manager for loading, applying and unloading plugins.\nclass PluginManager {\nprivate:\n  /// Codon executable location\n  std::string argv0;\n  /// vector of loaded plugins\n  std::vector<std::unique_ptr<Plugin>> plugins;\n\npublic:\n  /// Constructs a plugin manager\n  PluginManager(const std::string &argv0) : argv0(argv0) {}\n\n  /// @return iterator to the first plugin\n  auto begin() { return ir::util::raw_ptr_adaptor(plugins.begin()); }\n  /// @return iterator beyond the last plugin\n  auto end() { return ir::util::raw_ptr_adaptor(plugins.end()); }\n  /// @return const iterator to the first plugin\n  auto begin() const { return ir::util::const_raw_ptr_adaptor(plugins.begin()); }\n  /// @return const iterator beyond the last plugin\n  auto end() const { return ir::util::const_raw_ptr_adaptor(plugins.end()); }\n\n  /// Loads the plugin at the given load path.\n  /// @param path path to plugin directory containing \"plugin.toml\" file\n  /// @return plugin pointer if successful, plugin error otherwise\n  llvm::Expected<Plugin *> load(const std::string &path);\n};\n\n} // namespace codon\n"
  },
  {
    "path": "codon/parser/ast/attr.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"attr.h\"\n\nnamespace codon::ast {} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/ast/attr.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\nnamespace codon::ast {\n\nconstexpr int INDENT_SIZE = 2;\n\nstruct Attr {\n  enum {\n    Module = 201,\n    ParentClass,\n    Bindings,\n    LLVM,\n    Python,\n    Atomic,\n    Property,\n    StaticMethod,\n    Attribute,\n    C,\n    Internal,\n    HiddenFromUser,\n    ForceRealize,\n    AllowPassThrough,\n    ParentCallExpr,\n    TupleCall,\n    Validated,\n    AutoGenerated,\n    CVarArg,\n    Method,\n    Capture,\n    HasSelf,\n    IsGenerator,\n    Extend,\n    Tuple,\n    Dataclass,\n    ClassDeduce,\n    ClassNoTuple,\n    Test,\n    Overload,\n    Export,\n    Inline,\n    NoArgReorder,\n    FunctionAttributes,\n    NoExtend,\n\n    ClassMagic,\n    ExprSequenceItem,\n    ExprStarSequenceItem,\n    ExprList,\n    ExprSet,\n    ExprDict,\n    ExprPartial,\n    ExprDominated,\n    ExprStarArgument,\n    ExprKwStarArgument,\n    ExprOrderedCall,\n    ExprExternVar,\n    ExprDominatedUndefCheck,\n    ExprDominatedUsed,\n    ExprTime,\n    ExprDoNotRealize,\n    ExprNoSpecial,\n    TryPyVar\n  };\n};\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/ast/error.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"llvm/Support/Error.h\"\n#include <stdexcept>\n#include <string>\n#include <utility>\n#include <vector>\n\n/**\n * WARNING: do not include anything else in this file, especially format.h\n * peglib.h uses this file. However, it is not compatible with format.h\n * (and possibly some other includes). Their inclusion will result in a succesful\n * compilation but extremely weird behaviour and hard-to-debug crashes (it seems that\n * some parts of peglib conflict with format.h in a weird way---further investigation\n * needed).\n */\n\nnamespace codon {\nstruct SrcInfo {\n  std::string file;\n  int line;\n  int col;\n  int len;\n  int id; /// used to differentiate different instances\n\n  SrcInfo();\n  SrcInfo(std::string file, int line, int col, int len);\n  bool operator==(const SrcInfo &src) const;\n  bool operator<(const SrcInfo &src) const;\n  bool operator<=(const SrcInfo &src) const;\n};\n\nclass ErrorMessage {\nprivate:\n  std::string msg;\n  SrcInfo loc;\n  int errorCode = -1;\n\npublic:\n  explicit ErrorMessage(std::string msg, SrcInfo loc = SrcInfo(), int errorCode = -1)\n      : msg(std::move(msg)), loc(std::move(loc)), errorCode(-1) {}\n  explicit ErrorMessage(std::string msg, const std::string &file = \"\", int line = 0,\n                        int col = 0, int len = 0, int errorCode = -1)\n      : msg(std::move(msg)), loc(file, line, col, len), errorCode(-1) {}\n\n  std::string getMessage() const { return msg; }\n  std::string getFile() const { return loc.file; }\n  int getLine() const { return loc.line; }\n  int getColumn() const { return loc.col; }\n  int getLength() const { return loc.len; }\n  int getErrorCode() const { return errorCode; }\n  SrcInfo getSrcInfo() const { return loc; }\n  void setSrcInfo(const SrcInfo &s) { loc = s; }\n  bool operator==(const ErrorMessage &t) const { return msg == t.msg && loc == t.loc; }\n\n  std::string toString() const;\n\n  void log(llvm::raw_ostream &out) const { out << toString(); }\n};\n\nstruct ParserErrors {\n  struct Backtrace {\n    std::vector<ErrorMessage> trace;\n    const std::vector<ErrorMessage> &getMessages() const { return trace; }\n    auto begin() const { return trace.begin(); }\n    auto front() const { return trace.front(); }\n    auto front() { return trace.front(); }\n    auto end() const { return trace.end(); }\n    auto back() { return trace.back(); }\n    auto back() const { return trace.back(); }\n    auto size() const { return trace.size(); }\n    void addMessage(const std::string &msg, const SrcInfo &info = SrcInfo()) {\n      trace.emplace_back(msg, info);\n    }\n    bool operator==(const Backtrace &t) const { return trace == t.trace; }\n  };\n  std::vector<Backtrace> errors;\n\n  ParserErrors() = default;\n  explicit ParserErrors(const ErrorMessage &msg) : errors{Backtrace{{msg}}} {}\n  ParserErrors(const std::string &msg, const SrcInfo &info)\n      : ParserErrors(ErrorMessage{msg, info}) {}\n  explicit ParserErrors(const std::string &msg)\n      : ParserErrors(ErrorMessage{msg, SrcInfo{}}) {}\n  ParserErrors(const ParserErrors &e) = default;\n  explicit ParserErrors(const std::vector<ErrorMessage> &m) : ParserErrors() {\n    for (auto &msg : m)\n      errors.push_back(Backtrace{{msg}});\n  }\n\n  auto begin() { return errors.begin(); }\n  auto end() { return errors.end(); }\n  auto begin() const { return errors.begin(); }\n  auto end() const { return errors.end(); }\n  auto empty() const { return errors.empty(); }\n  auto size() const { return errors.size(); }\n  auto &back() { return errors.back(); }\n  const auto &back() const { return errors.back(); }\n  void append(const ParserErrors &e) {\n    for (auto &trace : e)\n      addError(trace);\n  }\n\n  Backtrace getLast() {\n    assert(!empty() && \"empty error trace\");\n    return errors.back();\n  }\n\n  /// Add an error message to the current backtrace\n  void addError(const Backtrace &trace) {\n    if (!errors.empty() && errors.back() == trace)\n      return;\n    errors.push_back({trace});\n  }\n  void addError(const std::vector<ErrorMessage> &trace) { addError(Backtrace{trace}); }\n  std::string getMessage() const {\n    if (empty())\n      return \"\";\n    return errors.front().trace.front().getMessage();\n  }\n};\n} // namespace codon\n\nnamespace codon::exc {\n\n/**\n * Parser error exception.\n * Used for parsing, transformation and type-checking errors.\n */\nclass ParserException : public std::runtime_error {\n  /// These vectors (stacks) store an error stack-trace.\n  ParserErrors errors;\n\npublic:\n  ParserException() noexcept : std::runtime_error(\"\") {}\n  explicit ParserException(const ParserErrors &errors) noexcept\n      : std::runtime_error(errors.getMessage()), errors(errors) {}\n  explicit ParserException(llvm::Error &&e) noexcept;\n\n  const ParserErrors &getErrors() const { return errors; }\n  ParserErrors &getErrors() { return errors; }\n};\n\n} // namespace codon::exc\n"
  },
  {
    "path": "codon/parser/ast/expr.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"expr.h\"\n\n#include <algorithm>\n#include <iterator>\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/match.h\"\n#include \"codon/parser/peg/peg.h\"\n#include \"codon/parser/visitors/visitor.h\"\n\n#define FASTFLOAT_ALLOWS_LEADING_PLUS\n#define FASTFLOAT_SKIP_WHITE_SPACE\n#include \"fast_float/fast_float.h\"\n\n#define ACCEPT_IMPL(T, X)                                                              \\\n  ASTNode *T::clone(bool c) const { return cache->N<T>(*this, c); }                    \\\n  void T::accept(X &visitor) { visitor.visit(this); }                                  \\\n  const char T::NodeId = 0;\n\nusing namespace codon::error;\nusing namespace codon::matcher;\n\nnamespace codon::ast {\n\nExpr::Expr() : AcceptorExtend(), type(nullptr), done(false), origExpr(nullptr) {}\nExpr::Expr(const Expr &expr, bool clean) : Expr(expr) {\n  if (clean) {\n    type = nullptr;\n    done = false;\n  }\n}\ntypes::ClassType *Expr::getClassType() const {\n  return type ? type->getClass() : nullptr;\n}\nstd::string Expr::wrapType(const std::string &sexpr) const {\n  auto is = sexpr;\n  if (done)\n    is.insert(findStar(is), \"*\");\n  return \"(\" + is +\n         (type && !done ? fmt::format(\" #:type \\\"{}\\\"\", type->debugString(2)) : \"\") +\n         \")\";\n}\n\nParam::Param(std::string name, Expr *type, Expr *defaultValue, int status)\n    : name(std::move(name)), type(type), defaultValue(defaultValue) {\n  if (status == 0 && (match(getType(), MOr(M<IdExpr>(TYPE_TYPE), M<IdExpr>(TRAIT_TYPE),\n                                           M<IndexExpr>(M<IdExpr>(TRAIT_TYPE), M_))) ||\n                      getStaticGeneric(getType()))) {\n    this->status = Generic;\n  } else {\n    this->status = (status == 0 ? Value : (status == 1 ? Generic : HiddenGeneric));\n  }\n}\nParam::Param(const SrcInfo &info, std::string name, Expr *type, Expr *defaultValue,\n             int status)\n    : Param(std::move(name), type, defaultValue, status) {\n  setSrcInfo(info);\n}\nstd::string Param::toString(int indent) const {\n  return fmt::format(\"({}{}{}{})\", name,\n                     type ? \" #:type \" + type->toString(indent) : \"\",\n                     defaultValue ? \" #:default \" + defaultValue->toString(indent) : \"\",\n                     !isValue() ? \" #:generic\" : \"\");\n}\nParam Param::clone(bool clean) const {\n  return Param(name, ast::clone(type, clean), ast::clone(defaultValue, clean), status);\n}\nstd::pair<int, std::string> Param::getNameWithStars() const {\n  int stars = 0;\n  for (; stars < name.size() && name[stars] == '*'; stars++)\n    ;\n  auto n = name.substr(stars);\n  return {stars, n};\n}\n\nNoneExpr::NoneExpr() : AcceptorExtend() {}\nNoneExpr::NoneExpr(const NoneExpr &expr, bool clean) : AcceptorExtend(expr, clean) {}\nstd::string NoneExpr::toString(int) const { return wrapType(\"none\"); }\n\nBoolExpr::BoolExpr(bool value) : AcceptorExtend(), value(value) {}\nBoolExpr::BoolExpr(const BoolExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), value(expr.value) {}\nbool BoolExpr::getValue() const { return value; }\nstd::string BoolExpr::toString(int) const {\n  return wrapType(fmt::format(\"bool {}\", static_cast<int>(value)));\n}\n\nIntExpr::IntExpr(int64_t intValue)\n    : AcceptorExtend(), value(std::to_string(intValue)), intValue(intValue) {}\nIntExpr::IntExpr(const std::string &value, std::string suffix)\n    : AcceptorExtend(), value(), suffix(std::move(suffix)) {\n  for (auto c : value)\n    if (c != '_')\n      this->value += c;\n  try {\n    if (startswith(this->value, \"0b\") || startswith(this->value, \"0B\"))\n      intValue = std::stoull(this->value.substr(2), nullptr, 2);\n    else\n      intValue = std::stoull(this->value, nullptr, 0);\n  } catch (std::out_of_range &) {\n  }\n}\nIntExpr::IntExpr(const IntExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), value(expr.value), suffix(expr.suffix),\n      intValue(expr.intValue) {}\nstd::pair<std::string, std::string> IntExpr::getRawData() const {\n  return {value, suffix};\n}\nbool IntExpr::hasStoredValue() const { return intValue.has_value(); }\nint64_t IntExpr::getValue() const {\n  seqassertn(hasStoredValue(), \"value not set\");\n  return intValue.value();\n}\nstd::string IntExpr::toString(int) const {\n  return wrapType(\n      fmt::format(\"int {}{}\", value,\n                  suffix.empty() ? \"\" : fmt::format(\" #:suffix \\\"{}\\\"\", suffix)));\n}\n\nFloatExpr::FloatExpr(double floatValue)\n    : AcceptorExtend(), value(fmt::format(\"{:g}\", floatValue)), floatValue(floatValue) {\n}\nFloatExpr::FloatExpr(const std::string &value, std::string suffix)\n    : AcceptorExtend(), value(), suffix(std::move(suffix)) {\n  this->value.reserve(value.size());\n  std::ranges::copy_if(value.begin(), value.end(), std::back_inserter(this->value),\n                       [](char c) { return c != '_'; });\n\n  double result;\n  auto r = fast_float::from_chars(this->value.data(),\n                                  this->value.data() + this->value.size(), result);\n  if (r.ec == std::errc() || r.ec == std::errc::result_out_of_range)\n    floatValue = result;\n}\nFloatExpr::FloatExpr(const FloatExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), value(expr.value), suffix(expr.suffix),\n      floatValue(expr.floatValue) {}\nstd::pair<std::string, std::string> FloatExpr::getRawData() const {\n  return {value, suffix};\n}\nbool FloatExpr::hasStoredValue() const { return floatValue.has_value(); }\ndouble FloatExpr::getValue() const {\n  seqassertn(hasStoredValue(), \"value not set\");\n  return floatValue.value();\n}\nstd::string FloatExpr::toString(int) const {\n  return wrapType(\n      fmt::format(\"float {}{}\", value,\n                  suffix.empty() ? \"\" : fmt::format(\" #:suffix \\\"{}\\\"\", suffix)));\n}\n\nStringExpr::StringExpr(std::vector<StringExpr::String> strings)\n    : AcceptorExtend(), strings(std::move(strings)) {}\nStringExpr::StringExpr(std::string value, std::string prefix)\n    : StringExpr(std::vector<StringExpr::String>{\n          StringExpr::String{std::move(value), std::move(prefix)}}) {}\nStringExpr::StringExpr(const StringExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), strings(expr.strings) {\n  for (auto &s : strings)\n    s.expr = ast::clone(s.expr);\n}\nstd::string StringExpr::toString(int) const {\n  std::vector<std::string> s;\n  for (auto &vp : strings)\n    s.push_back(fmt::format(\n        \"\\\"{}\\\"{}\", escape(vp.value),\n        vp.prefix.empty() ? \"\" : fmt::format(\" #:prefix \\\"{}\\\"\", vp.prefix)));\n  return wrapType(fmt::format(\"string ({})\", join(s)));\n}\nstd::string StringExpr::getValue() const {\n  seqassert(isSimple(), \"invalid StringExpr\");\n  return strings[0].value;\n}\nbool StringExpr::isSimple() const {\n  return strings.size() == 1 && strings[0].prefix.empty();\n}\n\nIdExpr::IdExpr(std::string value) : AcceptorExtend(), value(std::move(value)) {}\nIdExpr::IdExpr(const IdExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), value(expr.value) {}\nstd::string IdExpr::toString(int) const {\n  return !getType() ? fmt::format(\"'{}\", value) : wrapType(fmt::format(\"'{}\", value));\n}\n\nStarExpr::StarExpr(Expr *expr) : AcceptorExtend(), expr(expr) {}\nStarExpr::StarExpr(const StarExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), expr(ast::clone(expr.expr, clean)) {}\nstd::string StarExpr::toString(int indent) const {\n  return wrapType(fmt::format(\"star {}\", expr->toString(indent)));\n}\n\nKeywordStarExpr::KeywordStarExpr(Expr *expr) : AcceptorExtend(), expr(expr) {}\nKeywordStarExpr::KeywordStarExpr(const KeywordStarExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), expr(ast::clone(expr.expr, clean)) {}\nstd::string KeywordStarExpr::toString(int indent) const {\n  return wrapType(fmt::format(\"kwstar {}\", expr->toString(indent)));\n}\n\nTupleExpr::TupleExpr(std::vector<Expr *> items)\n    : AcceptorExtend(), Items(std::move(items)) {}\nTupleExpr::TupleExpr(const TupleExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), Items(ast::clone(expr.items, clean)) {}\nstd::string TupleExpr::toString(int) const {\n  return wrapType(fmt::format(\"tuple {}\", combine(items)));\n}\n\nListExpr::ListExpr(std::vector<Expr *> items)\n    : AcceptorExtend(), Items(std::move(items)) {}\nListExpr::ListExpr(const ListExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), Items(ast::clone(expr.items, clean)) {}\nstd::string ListExpr::toString(int) const {\n  return wrapType(!items.empty() ? fmt::format(\"list {}\", combine(items)) : \"list\");\n}\n\nSetExpr::SetExpr(std::vector<Expr *> items)\n    : AcceptorExtend(), Items(std::move(items)) {}\nSetExpr::SetExpr(const SetExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), Items(ast::clone(expr.items, clean)) {}\nstd::string SetExpr::toString(int) const {\n  return wrapType(!items.empty() ? fmt::format(\"set {}\", combine(items)) : \"set\");\n}\n\nDictExpr::DictExpr(std::vector<Expr *> items)\n    : AcceptorExtend(), Items(std::move(items)) {\n  for (auto *i : *this) {\n    auto t = cast<TupleExpr>(i);\n    seqassertn(t && t->size() == 2, \"dictionary items are invalid\");\n  }\n}\nDictExpr::DictExpr(const DictExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), Items(ast::clone(expr.items, clean)) {}\nstd::string DictExpr::toString(int) const {\n  return wrapType(!items.empty() ? fmt::format(\"dict {}\", combine(items)) : \"set\");\n}\n\nGeneratorExpr::GeneratorExpr(Cache *cache, GeneratorExpr::GeneratorKind kind,\n                             Expr *expr, std::vector<Stmt *> loops)\n    : AcceptorExtend(), kind(kind), loops() {\n  this->cache = cache;\n  seqassert(!loops.empty() && cast<ForStmt>(loops[0]), \"bad generator constructor\");\n  loops.push_back(cache->N<SuiteStmt>(cache->N<ExprStmt>(expr)));\n  formCompleteStmt(loops);\n}\nGeneratorExpr::GeneratorExpr(Cache *cache, Expr *key, Expr *expr,\n                             std::vector<Stmt *> loops)\n    : AcceptorExtend(), kind(GeneratorExpr::DictGenerator), loops() {\n  this->cache = cache;\n  seqassert(!loops.empty() && cast<ForStmt>(loops[0]), \"bad generator constructor\");\n  Expr *t = cache->N<TupleExpr>(std::vector<Expr *>{key, expr});\n  loops.push_back(cache->N<SuiteStmt>(cache->N<ExprStmt>(t)));\n  formCompleteStmt(loops);\n}\nGeneratorExpr::GeneratorExpr(const GeneratorExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), kind(expr.kind),\n      loops(ast::clone(expr.loops, clean)) {}\nstd::string GeneratorExpr::toString(int indent) const {\n  auto pad = indent >= 0 ? (\"\\n\" + std::string(indent + 2 * INDENT_SIZE, ' ')) : \" \";\n  std::string prefix;\n  if (kind == GeneratorKind::ListGenerator)\n    prefix = \"list-\";\n  if (kind == GeneratorKind::SetGenerator)\n    prefix = \"set-\";\n  if (kind == GeneratorKind::DictGenerator)\n    prefix = \"dict-\";\n  auto l = loops->toString(indent >= 0 ? indent + 2 * INDENT_SIZE : -1);\n  return wrapType(fmt::format(\"{}gen {}\", prefix, l));\n}\nExpr *GeneratorExpr::getFinalExpr() {\n  auto s = *(getFinalStmt());\n  if (cast<ExprStmt>(s))\n    return cast<ExprStmt>(s)->getExpr();\n  return nullptr;\n}\nint GeneratorExpr::loopCount() const {\n  int cnt = 0;\n  for (Stmt *i = loops;;) {\n    if (auto sf = cast<ForStmt>(i)) {\n      i = sf->getSuite();\n      cnt++;\n    } else if (auto si = cast<IfStmt>(i)) {\n      i = si->getIf();\n      cnt++;\n    } else if (auto ss = cast<SuiteStmt>(i)) {\n      if (ss->empty())\n        break;\n      i = ss->back();\n    } else\n      break;\n  }\n  return cnt;\n}\nvoid GeneratorExpr::setFinalExpr(Expr *expr) {\n  *(getFinalStmt()) = cache->N<ExprStmt>(expr);\n}\nvoid GeneratorExpr::setFinalStmt(Stmt *stmt) { *(getFinalStmt()) = stmt; }\nStmt *GeneratorExpr::getFinalSuite() const { return loops; }\nStmt **GeneratorExpr::getFinalStmt() {\n  for (Stmt **i = &loops;;) {\n    if (auto sf = cast<ForStmt>(*i))\n      i = reinterpret_cast<Stmt **>(&sf->suite);\n    else if (auto si = cast<IfStmt>(*i))\n      i = reinterpret_cast<Stmt **>(&si->ifSuite);\n    else if (auto ss = cast<SuiteStmt>(*i)) {\n      if (ss->empty())\n        return i;\n      i = &(ss->back());\n    } else\n      return i;\n  }\n  seqassert(false, \"bad generator\");\n  return nullptr;\n}\nvoid GeneratorExpr::formCompleteStmt(const std::vector<Stmt *> &loops) {\n  Stmt *final = nullptr;\n  for (size_t i = loops.size(); i-- > 0;) {\n    if (auto si = cast<IfStmt>(loops[i]))\n      si->ifSuite = SuiteStmt::wrap(final);\n    else if (auto sf = cast<ForStmt>(loops[i]))\n      sf->suite = SuiteStmt::wrap(final);\n    final = loops[i];\n  }\n  this->loops = loops[0];\n}\n\nIfExpr::IfExpr(Expr *cond, Expr *ifexpr, Expr *elsexpr)\n    : AcceptorExtend(), cond(cond), ifexpr(ifexpr), elsexpr(elsexpr) {}\nIfExpr::IfExpr(const IfExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), cond(ast::clone(expr.cond, clean)),\n      ifexpr(ast::clone(expr.ifexpr, clean)), elsexpr(ast::clone(expr.elsexpr, clean)) {\n}\nstd::string IfExpr::toString(int indent) const {\n  return wrapType(fmt::format(\"if-expr {} {} {}\", cond->toString(indent),\n                              ifexpr->toString(indent), elsexpr->toString(indent)));\n}\n\nUnaryExpr::UnaryExpr(std::string op, Expr *expr)\n    : AcceptorExtend(), op(std::move(op)), expr(expr) {}\nUnaryExpr::UnaryExpr(const UnaryExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), op(expr.op), expr(ast::clone(expr.expr, clean)) {}\nstd::string UnaryExpr::toString(int indent) const {\n  return wrapType(fmt::format(\"unary \\\"{}\\\" {}\", op, expr->toString(indent)));\n}\n\nBinaryExpr::BinaryExpr(Expr *lexpr, std::string op, Expr *rexpr, bool inPlace)\n    : AcceptorExtend(), op(std::move(op)), lexpr(lexpr), rexpr(rexpr),\n      inPlace(inPlace) {}\nBinaryExpr::BinaryExpr(const BinaryExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), op(expr.op), lexpr(ast::clone(expr.lexpr, clean)),\n      rexpr(ast::clone(expr.rexpr, clean)), inPlace(expr.inPlace) {}\nstd::string BinaryExpr::toString(int indent) const {\n  return wrapType(fmt::format(\"binary \\\"{}\\\" {} {}{}\", op, lexpr->toString(indent),\n                              rexpr->toString(indent), inPlace ? \" #:in-place\" : \"\"));\n}\n\nChainBinaryExpr::ChainBinaryExpr(std::vector<std::pair<std::string, Expr *>> exprs)\n    : AcceptorExtend(), exprs(std::move(exprs)) {}\nChainBinaryExpr::ChainBinaryExpr(const ChainBinaryExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean) {\n  for (auto &e : expr.exprs)\n    exprs.emplace_back(e.first, ast::clone(e.second, clean));\n}\nstd::string ChainBinaryExpr::toString(int indent) const {\n  std::vector<std::string> s;\n  for (auto &i : exprs)\n    s.push_back(fmt::format(\"({} \\\"{}\\\")\", i.first, i.second->toString(indent)));\n  return wrapType(fmt::format(\"chain {}\", join(s, \" \")));\n}\n\nPipe Pipe::clone(bool clean) const { return {op, ast::clone(expr, clean)}; }\n\nPipeExpr::PipeExpr(std::vector<Pipe> items)\n    : AcceptorExtend(), Items(std::move(items)) {\n  for (auto &i : *this) {\n    if (auto call = cast<CallExpr>(i.expr)) {\n      for (auto &a : *call)\n        if (auto el = cast<EllipsisExpr>(a.value))\n          el->mode = EllipsisExpr::PIPE;\n    }\n  }\n}\nPipeExpr::PipeExpr(const PipeExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), Items(ast::clone(expr.items, clean)),\n      inTypes(expr.inTypes) {}\nstd::string PipeExpr::toString(int indent) const {\n  std::vector<std::string> s;\n  for (auto &i : items)\n    s.push_back(fmt::format(\"({} \\\"{}\\\")\", i.expr->toString(indent), i.op));\n  return wrapType(fmt::format(\"pipe {}\", join(s, \" \")));\n}\n\nIndexExpr::IndexExpr(Expr *expr, Expr *index)\n    : AcceptorExtend(), expr(expr), index(index) {}\nIndexExpr::IndexExpr(const IndexExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), expr(ast::clone(expr.expr, clean)),\n      index(ast::clone(expr.index, clean)) {}\nstd::string IndexExpr::toString(int indent) const {\n  return wrapType(\n      fmt::format(\"index {} {}\", expr->toString(indent), index->toString(indent)));\n}\n\nCallArg CallArg::clone(bool clean) const {\n  return CallArg{name, ast::clone(value, clean)};\n}\nCallArg::CallArg(const SrcInfo &info, std::string name, Expr *value)\n    : name(std::move(name)), value(value) {\n  setSrcInfo(info);\n}\nCallArg::CallArg(std::string name, Expr *value) : name(std::move(name)), value(value) {\n  if (value)\n    setSrcInfo(value->getSrcInfo());\n}\nCallArg::CallArg(Expr *value) : CallArg(\"\", value) {}\n\nCallExpr::CallExpr(const CallExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), Items(ast::clone(expr.items, clean)),\n      expr(ast::clone(expr.expr, clean)), ordered(expr.ordered), partial(expr.partial) {\n}\nCallExpr::CallExpr(Expr *expr, std::vector<CallArg> args)\n    : AcceptorExtend(), Items(std::move(args)), expr(expr), ordered(false),\n      partial(false) {}\nCallExpr::CallExpr(Expr *expr, const std::vector<Expr *> &args)\n    : AcceptorExtend(), Items({}), expr(expr), ordered(false), partial(false) {\n  for (auto a : args)\n    if (a)\n      items.emplace_back(\"\", a);\n}\nstd::string CallExpr::toString(int indent) const {\n  std::vector<std::string> s;\n  auto pad = indent >= 0 ? (\"\\n\" + std::string(indent + 2 * INDENT_SIZE, ' ')) : \" \";\n  for (auto &i : *this) {\n    if (!i.name.empty())\n      s.emplace_back(pad + fmt::format(\"#:name '{}\", i.name));\n    s.emplace_back(pad +\n                   i.value->toString(indent >= 0 ? indent + 2 * INDENT_SIZE : -1));\n  }\n  return wrapType(fmt::format(\"call{} {}{}\", partial ? \"-partial\" : \"\",\n                              expr->toString(indent), join(s, \"\")));\n}\n\nDotExpr::DotExpr(Expr *expr, std::string member)\n    : AcceptorExtend(), expr(expr), member(std::move(member)) {}\nDotExpr::DotExpr(const DotExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), expr(ast::clone(expr.expr, clean)),\n      member(expr.member) {}\nstd::string DotExpr::toString(int indent) const {\n  return wrapType(fmt::format(\"dot {} '{}\", expr->toString(indent), member));\n}\n\nSliceExpr::SliceExpr(Expr *start, Expr *stop, Expr *step)\n    : AcceptorExtend(), start(start), stop(stop), step(step) {}\nSliceExpr::SliceExpr(const SliceExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), start(ast::clone(expr.start, clean)),\n      stop(ast::clone(expr.stop, clean)), step(ast::clone(expr.step, clean)) {}\nstd::string SliceExpr::toString(int indent) const {\n  return wrapType(fmt::format(\n      \"slice{}{}{}\", start ? fmt::format(\" #:start {}\", start->toString(indent)) : \"\",\n      stop ? fmt::format(\" #:end {}\", stop->toString(indent)) : \"\",\n      step ? fmt::format(\" #:step {}\", step->toString(indent)) : \"\"));\n}\n\nEllipsisExpr::EllipsisExpr(EllipsisType mode) : AcceptorExtend(), mode(mode) {}\nEllipsisExpr::EllipsisExpr(const EllipsisExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), mode(expr.mode) {}\nstd::string EllipsisExpr::toString(int) const {\n  return wrapType(fmt::format(\n      \"ellipsis{}\", mode == PIPE ? \" #:pipe\" : (mode == PARTIAL ? \" #:partial\" : \"\")));\n}\n\nLambdaExpr::LambdaExpr(std::vector<Param> vars, Expr *expr)\n    : AcceptorExtend(), Items(std::move(vars)), expr(expr) {}\nLambdaExpr::LambdaExpr(const LambdaExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), Items(ast::clone(expr.items, clean)),\n      expr(ast::clone(expr.expr, clean)) {}\nstd::string LambdaExpr::toString(int indent) const {\n  std::vector<std::string> as;\n  for (auto &a : items)\n    as.push_back(a.toString(indent));\n  return wrapType(fmt::format(\"lambda ({}) {}\", join(as, \" \"), expr->toString(indent)));\n}\n\nYieldExpr::YieldExpr() : AcceptorExtend() {}\nYieldExpr::YieldExpr(const YieldExpr &expr, bool clean) : AcceptorExtend(expr, clean) {}\nstd::string YieldExpr::toString(int) const { return \"yield-expr\"; }\n\nAwaitExpr::AwaitExpr(Expr *expr) : AcceptorExtend(), expr(expr), transformed(false) {}\nAwaitExpr::AwaitExpr(const AwaitExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), expr(ast::clone(expr.expr, clean)),\n      transformed(expr.transformed) {}\nstd::string AwaitExpr::toString(int indent) const {\n  return wrapType(fmt::format(\"await {}\", expr->toString(indent)));\n}\n\nAssignExpr::AssignExpr(Expr *var, Expr *expr)\n    : AcceptorExtend(), var(var), expr(expr) {}\nAssignExpr::AssignExpr(const AssignExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), var(ast::clone(expr.var, clean)),\n      expr(ast::clone(expr.expr, clean)) {}\nstd::string AssignExpr::toString(int indent) const {\n  return wrapType(\n      fmt::format(\"assign-expr '{} {}\", var->toString(indent), expr->toString(indent)));\n}\n\nRangeExpr::RangeExpr(Expr *start, Expr *stop)\n    : AcceptorExtend(), start(start), stop(stop) {}\nRangeExpr::RangeExpr(const RangeExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), start(ast::clone(expr.start, clean)),\n      stop(ast::clone(expr.stop, clean)) {}\nstd::string RangeExpr::toString(int indent) const {\n  return wrapType(\n      fmt::format(\"range {} {}\", start->toString(indent), stop->toString(indent)));\n}\n\nStmtExpr::StmtExpr(std::vector<Stmt *> stmts, Expr *expr)\n    : AcceptorExtend(), Items(std::move(stmts)), expr(expr) {}\nStmtExpr::StmtExpr(Stmt *stmt, Expr *expr) : AcceptorExtend(), Items({}), expr(expr) {\n  items.push_back(stmt);\n}\nStmtExpr::StmtExpr(Stmt *stmt, Stmt *stmt2, Expr *expr)\n    : AcceptorExtend(), Items({}), expr(expr) {\n  items.push_back(stmt);\n  items.push_back(stmt2);\n}\nStmtExpr::StmtExpr(const StmtExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), Items(ast::clone(expr.items, clean)),\n      expr(ast::clone(expr.expr, clean)) {}\nstd::string StmtExpr::toString(int indent) const {\n  auto pad = indent >= 0 ? (\"\\n\" + std::string(indent + 2 * INDENT_SIZE, ' ')) : \" \";\n  std::vector<std::string> s;\n  s.reserve(items.size());\n  for (auto &i : items)\n    s.emplace_back(pad + i->toString(indent >= 0 ? indent + 2 * INDENT_SIZE : -1));\n  return wrapType(\n      fmt::format(\"stmt-expr {} ({})\", expr->toString(indent), join(s, \"\")));\n}\n\nInstantiateExpr::InstantiateExpr(Expr *expr, std::vector<Expr *> typeParams)\n    : AcceptorExtend(), Items(std::move(typeParams)), expr(expr) {}\nInstantiateExpr::InstantiateExpr(Expr *expr, Expr *typeParam)\n    : AcceptorExtend(), Items({typeParam}), expr(expr) {}\nInstantiateExpr::InstantiateExpr(const InstantiateExpr &expr, bool clean)\n    : AcceptorExtend(expr, clean), Items(ast::clone(expr.items, clean)),\n      expr(ast::clone(expr.expr, clean)) {}\nstd::string InstantiateExpr::toString(int indent) const {\n  return wrapType(\n      fmt::format(\"instantiate {} {}\", expr->toString(indent), combine(items)));\n}\n\nbool isId(Expr *e, const std::string &s) {\n  auto ie = cast<IdExpr>(e);\n  return ie && ie->getValue() == s;\n}\n\ntypes::LiteralKind getStaticGeneric(Expr *e) {\n  IdExpr *ie = nullptr;\n  if (match(e, M<IndexExpr>(M<IdExpr>(MOr(\"Static\", \"Literal\")), MVar<IdExpr>(ie)))) {\n    return types::Type::literalFromString(ie->getValue());\n  }\n  return types::LiteralKind::Runtime;\n}\n\nconst char ASTNode::NodeId = 0;\nconst char Expr::NodeId = 0;\nACCEPT_IMPL(NoneExpr, ASTVisitor);\nACCEPT_IMPL(BoolExpr, ASTVisitor);\nACCEPT_IMPL(IntExpr, ASTVisitor);\nACCEPT_IMPL(FloatExpr, ASTVisitor);\nACCEPT_IMPL(StringExpr, ASTVisitor);\nACCEPT_IMPL(IdExpr, ASTVisitor);\nACCEPT_IMPL(StarExpr, ASTVisitor);\nACCEPT_IMPL(KeywordStarExpr, ASTVisitor);\nACCEPT_IMPL(TupleExpr, ASTVisitor);\nACCEPT_IMPL(ListExpr, ASTVisitor);\nACCEPT_IMPL(SetExpr, ASTVisitor);\nACCEPT_IMPL(DictExpr, ASTVisitor);\nACCEPT_IMPL(GeneratorExpr, ASTVisitor);\nACCEPT_IMPL(IfExpr, ASTVisitor);\nACCEPT_IMPL(UnaryExpr, ASTVisitor);\nACCEPT_IMPL(BinaryExpr, ASTVisitor);\nACCEPT_IMPL(ChainBinaryExpr, ASTVisitor);\nACCEPT_IMPL(PipeExpr, ASTVisitor);\nACCEPT_IMPL(IndexExpr, ASTVisitor);\nACCEPT_IMPL(CallExpr, ASTVisitor);\nACCEPT_IMPL(DotExpr, ASTVisitor);\nACCEPT_IMPL(SliceExpr, ASTVisitor);\nACCEPT_IMPL(EllipsisExpr, ASTVisitor);\nACCEPT_IMPL(LambdaExpr, ASTVisitor);\nACCEPT_IMPL(YieldExpr, ASTVisitor);\nACCEPT_IMPL(AwaitExpr, ASTVisitor);\nACCEPT_IMPL(AssignExpr, ASTVisitor);\nACCEPT_IMPL(RangeExpr, ASTVisitor);\nACCEPT_IMPL(StmtExpr, ASTVisitor);\nACCEPT_IMPL(InstantiateExpr, ASTVisitor);\n\n} // namespace codon::ast\n\nnamespace tser {\nvoid operator<<(codon::ast::Expr *t, BinaryArchive &a) {\n  using S = codon::PolymorphicSerializer<BinaryArchive, codon::ast::Expr>;\n  a.save(t != nullptr);\n  if (t) {\n    void *typ = const_cast<void *>(t->dynamicNodeId());\n    auto key = S::_serializers[typ];\n    a.save(key);\n    S::save(key, t, a);\n  }\n}\n\nvoid operator>>(codon::ast::Expr *&t, BinaryArchive &a) {\n  using S = codon::PolymorphicSerializer<BinaryArchive, codon::ast::Expr>;\n  bool empty = a.load<bool>();\n  if (!empty) {\n    const auto key = a.load<std::string>();\n    S::load(key, t, a);\n  } else {\n    t = nullptr;\n  }\n}\n} // namespace tser\n"
  },
  {
    "path": "codon/parser/ast/expr.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <ostream>\n#include <string>\n#include <variant>\n#include <vector>\n\n#include \"codon/parser/ast/node.h\"\n#include \"codon/parser/ast/types.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/util/serialize.h\"\n\nnamespace codon::ast {\n\n#define ACCEPT(CLASS, VISITOR, ...)                                                    \\\n  static const char NodeId;                                                            \\\n  using AcceptorExtend::clone;                                                         \\\n  using AcceptorExtend::accept;                                                        \\\n  ASTNode *clone(bool c) const override;                                               \\\n  void accept(VISITOR &visitor) override;                                              \\\n  std::string toString(int) const override;                                            \\\n  friend class TypecheckVisitor;                                                       \\\n  template <typename TE, typename TS> friend struct CallbackASTVisitor;                \\\n  friend struct ReplacingCallbackASTVisitor;                                           \\\n  inline decltype(auto) match_members() const { return std::tie(__VA_ARGS__); }        \\\n  SERIALIZE(CLASS, BASE(Expr), ##__VA_ARGS__)\n\n// Forward declarations\nstruct Stmt;\n\n/**\n * A Seq AST expression.\n * Each AST expression is intended to be instantiated as a shared_ptr.\n */\nstruct Expr : public AcceptorExtend<Expr, ASTNode> {\n  using base_type = Expr;\n\n  Expr();\n  Expr(const Expr &) = default;\n  Expr(const Expr &, bool);\n\n  /// Get a node type.\n  /// @return Type pointer or a nullptr if a type is not set.\n  types::Type *getType() const { return type.get(); }\n  void setType(const types::TypePtr &t) { type = t; }\n  types::ClassType *getClassType() const;\n  bool isDone() const { return done; }\n  void setDone() { done = true; }\n  Expr *getOrigExpr() const { return origExpr; }\n  void setOrigExpr(Expr *orig) { origExpr = orig; }\n\n  static const char NodeId;\n  SERIALIZE(Expr, BASE(ASTNode), /*type,*/ done, origExpr);\n\n  Expr *operator<<(types::Type *t);\n\nprotected:\n  /// Add a type to S-expression string.\n  std::string wrapType(const std::string &sexpr) const;\n\nprivate:\n  /// Type of the expression. nullptr by default.\n  types::TypePtr type;\n  /// Flag that indicates if all types in an expression are inferred (i.e. if a\n  /// type-checking procedure was successful).\n  bool done;\n  /// Original (pre-transformation) expression\n  Expr *origExpr;\n};\n\n/// Function signature parameter helper node (name: type = defaultValue).\nstruct Param : public codon::SrcObject {\n  std::string name;\n  Expr *type;\n  Expr *defaultValue;\n  enum {\n    Value,\n    Generic,\n    HiddenGeneric\n  } status; // 1 for normal generic, 2 for hidden generic\n\n  explicit Param(std::string name = \"\", Expr *type = nullptr,\n                 Expr *defaultValue = nullptr, int generic = 0);\n  explicit Param(const SrcInfo &info, std::string name = \"\", Expr *type = nullptr,\n                 Expr *defaultValue = nullptr, int generic = 0);\n\n  std::string getName() const { return name; }\n  Expr *getType() const { return type; }\n  Expr *getDefault() const { return defaultValue; }\n  bool isValue() const { return status == Value; }\n  bool isGeneric() const { return status == Generic; }\n  bool isHiddenGeneric() const { return status == HiddenGeneric; }\n  std::pair<int, std::string> getNameWithStars() const;\n\n  SERIALIZE(Param, name, type, defaultValue);\n  Param clone(bool) const;\n  std::string toString(int) const;\n};\n\n/// None expression.\n/// @li None\nstruct NoneExpr : public AcceptorExtend<NoneExpr, Expr> {\n  NoneExpr();\n  NoneExpr(const NoneExpr &, bool);\n\n  ACCEPT(NoneExpr, ASTVisitor);\n};\n\n/// Bool expression (value).\n/// @li True\nstruct BoolExpr : public AcceptorExtend<BoolExpr, Expr> {\n  explicit BoolExpr(bool value = false);\n  BoolExpr(const BoolExpr &, bool);\n\n  bool getValue() const;\n\n  ACCEPT(BoolExpr, ASTVisitor, value);\n\nprivate:\n  bool value;\n};\n\n/// Int expression (value.suffix).\n/// @li 12\n/// @li 13u\n/// @li 000_010b\nstruct IntExpr : public AcceptorExtend<IntExpr, Expr> {\n  explicit IntExpr(int64_t intValue = 0);\n  explicit IntExpr(const std::string &value, std::string suffix = \"\");\n  IntExpr(const IntExpr &, bool);\n\n  bool hasStoredValue() const;\n  int64_t getValue() const;\n  std::pair<std::string, std::string> getRawData() const;\n\n  ACCEPT(IntExpr, ASTVisitor, value, suffix, intValue);\n\nprivate:\n  /// Expression value is stored as a string that is parsed during typechecking.\n  std::string value;\n  /// Number suffix (e.g. \"u\" for \"123u\").\n  std::string suffix;\n  /// Parsed value and sign for \"normal\" 64-bit integers.\n  std::optional<int64_t> intValue;\n};\n\n/// Float expression (value.suffix).\n/// @li 12.1\n/// @li 13.15z\n/// @li e-12\nstruct FloatExpr : public AcceptorExtend<FloatExpr, Expr> {\n  explicit FloatExpr(double floatValue = 0.0);\n  explicit FloatExpr(const std::string &value, std::string suffix = \"\");\n  FloatExpr(const FloatExpr &, bool);\n\n  bool hasStoredValue() const;\n  double getValue() const;\n  std::pair<std::string, std::string> getRawData() const;\n\n  ACCEPT(FloatExpr, ASTVisitor, value, suffix, floatValue);\n\nprivate:\n  /// Expression value is stored as a string that is parsed during typechecking.\n  std::string value;\n  /// Number suffix (e.g. \"u\" for \"123u\").\n  std::string suffix;\n  /// Parsed value for 64-bit floats.\n  std::optional<double> floatValue;\n};\n\n/// String expression (prefix\"value\").\n/// @li s'ACGT'\n/// @li \"fff\"\nstruct StringExpr : public AcceptorExtend<StringExpr, Expr> {\n  struct FormatSpec {\n    std::string text;\n    std::string conversion;\n    std::string spec;\n\n    SERIALIZE(FormatSpec, text, conversion, spec);\n  };\n\n  // Vector of {value, prefix} strings.\n  struct String : public SrcObject {\n    std::string value;\n    std::string prefix;\n    Expr *expr;\n    FormatSpec format;\n\n    explicit String(std::string v, std::string p = \"\", Expr *e = nullptr)\n        : value(std::move(v)), prefix(std::move(p)), expr(e), format() {}\n\n    SERIALIZE(String, value, prefix, expr, format);\n  };\n\n  explicit StringExpr(std::string value = \"\", std::string prefix = \"\");\n  explicit StringExpr(std::vector<String> strings);\n  StringExpr(const StringExpr &, bool);\n\n  std::string getValue() const;\n  bool isSimple() const;\n\n  ACCEPT(StringExpr, ASTVisitor, strings);\n\nprivate:\n  std::vector<String> strings;\n\n  auto begin() { return strings.begin(); }\n  auto end() { return strings.end(); }\n\n  friend class ScopingVisitor;\n};\n\n/// Identifier expression (value).\nstruct IdExpr : public AcceptorExtend<IdExpr, Expr> {\n  explicit IdExpr(std::string value = \"\");\n  IdExpr(const IdExpr &, bool);\n\n  std::string getValue() const { return value; }\n\n  ACCEPT(IdExpr, ASTVisitor, value);\n\nprivate:\n  std::string value;\n\n  void setValue(const std::string &s) { value = s; }\n\n  friend class ScopingVisitor;\n};\n\n/// Star (unpacking) expression (*what).\n/// @li *args\nstruct StarExpr : public AcceptorExtend<StarExpr, Expr> {\n  explicit StarExpr(Expr *what = nullptr);\n  StarExpr(const StarExpr &, bool);\n\n  Expr *getExpr() const { return expr; }\n\n  ACCEPT(StarExpr, ASTVisitor, expr);\n\nprivate:\n  Expr *expr;\n};\n\n/// KeywordStar (unpacking) expression (**what).\n/// @li **kwargs\nstruct KeywordStarExpr : public AcceptorExtend<KeywordStarExpr, Expr> {\n  explicit KeywordStarExpr(Expr *what = nullptr);\n  KeywordStarExpr(const KeywordStarExpr &, bool);\n\n  Expr *getExpr() const { return expr; }\n\n  ACCEPT(KeywordStarExpr, ASTVisitor, expr);\n\nprivate:\n  Expr *expr;\n};\n\n/// Tuple expression ((items...)).\n/// @li (1, a)\nstruct TupleExpr : public AcceptorExtend<TupleExpr, Expr>, Items<Expr *> {\n  explicit TupleExpr(std::vector<Expr *> items = {});\n  TupleExpr(const TupleExpr &, bool);\n\n  ACCEPT(TupleExpr, ASTVisitor, items);\n};\n\n/// List expression ([items...]).\n/// @li [1, 2]\nstruct ListExpr : public AcceptorExtend<ListExpr, Expr>, Items<Expr *> {\n  explicit ListExpr(std::vector<Expr *> items = {});\n  ListExpr(const ListExpr &, bool);\n\n  ACCEPT(ListExpr, ASTVisitor, items);\n};\n\n/// Set expression ({items...}).\n/// @li {1, 2}\nstruct SetExpr : public AcceptorExtend<SetExpr, Expr>, Items<Expr *> {\n  explicit SetExpr(std::vector<Expr *> items = {});\n  SetExpr(const SetExpr &, bool);\n\n  ACCEPT(SetExpr, ASTVisitor, items);\n};\n\n/// Dictionary expression ({(key: value)...}).\n/// Each (key, value) pair is stored as a TupleExpr.\n/// @li {'s': 1, 't': 2}\nstruct DictExpr : public AcceptorExtend<DictExpr, Expr>, Items<Expr *> {\n  explicit DictExpr(std::vector<Expr *> items = {});\n  DictExpr(const DictExpr &, bool);\n\n  ACCEPT(DictExpr, ASTVisitor, items);\n};\n\n/// Generator or comprehension expression [(expr (loops...))].\n/// @li [i for i in j]\n/// @li (f + 1 for j in k if j for f in j)\nstruct GeneratorExpr : public AcceptorExtend<GeneratorExpr, Expr> {\n  /// Generator kind: normal generator, list comprehension, set comprehension.\n  enum GeneratorKind {\n    Generator,\n    ListGenerator,\n    SetGenerator,\n    TupleGenerator,\n    DictGenerator\n  };\n\n  GeneratorExpr() : kind(Generator), loops(nullptr) {}\n  GeneratorExpr(Cache *cache, GeneratorKind kind, Expr *expr,\n                std::vector<Stmt *> loops);\n  GeneratorExpr(Cache *cache, Expr *key, Expr *expr, std::vector<Stmt *> loops);\n  GeneratorExpr(const GeneratorExpr &, bool);\n\n  int loopCount() const;\n  Stmt *getFinalSuite() const;\n  Expr *getFinalExpr();\n\n  ACCEPT(GeneratorExpr, ASTVisitor, kind, loops);\n\nprivate:\n  GeneratorKind kind;\n  Stmt *loops;\n\n  Stmt **getFinalStmt();\n  void setFinalExpr(Expr *);\n  void setFinalStmt(Stmt *);\n  void formCompleteStmt(const std::vector<Stmt *> &);\n\n  friend class TranslateVisitor;\n};\n\n/// Conditional expression [cond if ifexpr else elsexpr].\n/// @li 1 if a else 2\nstruct IfExpr : public AcceptorExtend<IfExpr, Expr> {\n  explicit IfExpr(Expr *cond = nullptr, Expr *ifexpr = nullptr,\n                  Expr *elsexpr = nullptr);\n  IfExpr(const IfExpr &, bool);\n\n  Expr *getCond() const { return cond; }\n  Expr *getIf() const { return ifexpr; }\n  Expr *getElse() const { return elsexpr; }\n\n  ACCEPT(IfExpr, ASTVisitor, cond, ifexpr, elsexpr);\n\nprivate:\n  Expr *cond, *ifexpr, *elsexpr;\n};\n\n/// Unary expression [op expr].\n/// @li -56\nstruct UnaryExpr : public AcceptorExtend<UnaryExpr, Expr> {\n  explicit UnaryExpr(std::string op = \"\", Expr *expr = nullptr);\n  UnaryExpr(const UnaryExpr &, bool);\n\n  std::string getOp() const { return op; }\n  Expr *getExpr() const { return expr; }\n\n  ACCEPT(UnaryExpr, ASTVisitor, op, expr);\n\nprivate:\n  std::string op;\n  Expr *expr;\n};\n\n/// Binary expression [lexpr op rexpr].\n/// @li 1 + 2\n/// @li 3 or 4\nstruct BinaryExpr : public AcceptorExtend<BinaryExpr, Expr> {\n  explicit BinaryExpr(Expr *lexpr = nullptr, std::string op = \"\", Expr *rexpr = nullptr,\n                      bool inPlace = false);\n  BinaryExpr(const BinaryExpr &, bool);\n\n  std::string getOp() const { return op; }\n  void setOp(const std::string &o) { op = o; }\n  Expr *getLhs() const { return lexpr; }\n  Expr *getRhs() const { return rexpr; }\n  bool isInPlace() const { return inPlace; }\n\n  ACCEPT(BinaryExpr, ASTVisitor, op, lexpr, rexpr);\n\nprivate:\n  std::string op;\n  Expr *lexpr, *rexpr;\n\n  /// True if an expression modifies lhs in-place (e.g. a += b).\n  bool inPlace;\n};\n\n/// Chained binary expression.\n/// @li 1 <= x <= 2\nstruct ChainBinaryExpr : public AcceptorExtend<ChainBinaryExpr, Expr> {\n  explicit ChainBinaryExpr(std::vector<std::pair<std::string, Expr *>> exprs = {});\n  ChainBinaryExpr(const ChainBinaryExpr &, bool);\n\n  ACCEPT(ChainBinaryExpr, ASTVisitor, exprs);\n\nprivate:\n  std::vector<std::pair<std::string, Expr *>> exprs;\n};\n\nstruct Pipe {\n  std::string op;\n  Expr *expr;\n\n  SERIALIZE(Pipe, op, expr);\n  Pipe clone(bool) const;\n};\n\n/// Pipe expression [(op expr)...].\n/// op is either \"\" (only the first item), \"|>\" or \"||>\".\n/// @li a |> b ||> c\nstruct PipeExpr : public AcceptorExtend<PipeExpr, Expr>, Items<Pipe> {\n  explicit PipeExpr(std::vector<Pipe> items = {});\n  PipeExpr(const PipeExpr &, bool);\n\n  ACCEPT(PipeExpr, ASTVisitor, items);\n\nprivate:\n  /// Output type of a \"prefix\" pipe ending at the index position.\n  /// Example: for a |> b |> c, inTypes[1] is typeof(a |> b).\n  std::vector<types::TypePtr> inTypes;\n};\n\n/// Index expression (expr[index]).\n/// @li a[5]\nstruct IndexExpr : public AcceptorExtend<IndexExpr, Expr> {\n  explicit IndexExpr(Expr *expr = nullptr, Expr *index = nullptr);\n  IndexExpr(const IndexExpr &, bool);\n\n  Expr *getExpr() const { return expr; }\n  Expr *getIndex() const { return index; }\n\n  ACCEPT(IndexExpr, ASTVisitor, expr, index);\n\nprivate:\n  Expr *expr, *index;\n};\n\nstruct CallArg : public codon::SrcObject {\n  std::string name;\n  Expr *value;\n\n  explicit CallArg(std::string name = \"\", Expr *value = nullptr);\n  CallArg(const SrcInfo &info, std::string name, Expr *value);\n  CallArg(Expr *value);\n\n  std::string getName() const { return name; }\n  Expr *getExpr() const { return value; }\n  operator Expr *() const { return value; }\n\n  SERIALIZE(CallArg, name, value);\n  CallArg clone(bool) const;\n};\n\n/// Call expression (expr((name=value)...)).\n/// @li a(1, b=2)\nstruct CallExpr : public AcceptorExtend<CallExpr, Expr>, Items<CallArg> {\n  /// Each argument can have a name (e.g. foo(1, b=5))\n  explicit CallExpr(Expr *expr = nullptr, std::vector<CallArg> args = {});\n  /// Convenience constructors\n  CallExpr(Expr *expr, const std::vector<Expr *> &args);\n  template <typename... Ts>\n  CallExpr(Expr *expr, Expr *arg, Ts... args)\n      : CallExpr(expr, std::vector<Expr *>{arg, args...}) {}\n  CallExpr(const CallExpr &, bool);\n\n  Expr *getExpr() const { return expr; }\n  bool isOrdered() const { return ordered; }\n  bool isPartial() const { return partial; }\n\n  ACCEPT(CallExpr, ASTVisitor, expr, items, ordered, partial);\n\nprivate:\n  Expr *expr;\n  /// True if type-checker has processed and re-ordered args.\n  bool ordered;\n  /// True if the call is partial\n  bool partial = false;\n};\n\n/// Dot (access) expression (expr.member).\n/// @li a.b\nstruct DotExpr : public AcceptorExtend<DotExpr, Expr> {\n  DotExpr() : expr(nullptr), member() {}\n  DotExpr(Expr *expr, std::string member);\n  DotExpr(const DotExpr &, bool);\n\n  Expr *getExpr() const { return expr; }\n  std::string getMember() const { return member; }\n\n  ACCEPT(DotExpr, ASTVisitor, expr, member);\n\nprivate:\n  Expr *expr;\n  std::string member;\n};\n\n/// Slice expression (st:stop:step).\n/// @li 1:10:3\n/// @li s::-1\n/// @li :::\nstruct SliceExpr : public AcceptorExtend<SliceExpr, Expr> {\n  explicit SliceExpr(Expr *start = nullptr, Expr *stop = nullptr, Expr *step = nullptr);\n  SliceExpr(const SliceExpr &, bool);\n\n  Expr *getStart() const { return start; }\n  Expr *getStop() const { return stop; }\n  Expr *getStep() const { return step; }\n\n  ACCEPT(SliceExpr, ASTVisitor, start, stop, step);\n\nprivate:\n  /// Any of these can be nullptr to account for partial slices.\n  Expr *start, *stop, *step;\n};\n\n/// Ellipsis expression.\n/// @li ...\nstruct EllipsisExpr : public AcceptorExtend<EllipsisExpr, Expr> {\n  /// True if this is a target partial argument within a PipeExpr.\n  /// If true, this node will be handled differently during the type-checking stage.\n  enum EllipsisType { PIPE, PARTIAL, STANDALONE };\n\n  explicit EllipsisExpr(EllipsisType mode = STANDALONE);\n  EllipsisExpr(const EllipsisExpr &, bool);\n\n  EllipsisType getMode() const { return mode; }\n  bool isStandalone() const { return mode == STANDALONE; }\n  bool isPipe() const { return mode == PIPE; }\n  bool isPartial() const { return mode == PARTIAL; }\n\n  ACCEPT(EllipsisExpr, ASTVisitor, mode);\n\nprivate:\n  EllipsisType mode;\n\n  friend struct PipeExpr;\n};\n\n/// Lambda expression (lambda (vars)...: expr).\n/// @li lambda a, b: a + b\nstruct LambdaExpr : public AcceptorExtend<LambdaExpr, Expr>, Items<Param> {\n  explicit LambdaExpr(std::vector<Param> vars = {}, Expr *expr = nullptr);\n  LambdaExpr(const LambdaExpr &, bool);\n\n  Expr *getExpr() const { return expr; }\n\n  ACCEPT(LambdaExpr, ASTVisitor, expr, items);\n\nprivate:\n  Expr *expr;\n};\n\n/// Yield (send to generator) expression.\n/// @li (yield)\nstruct YieldExpr : public AcceptorExtend<YieldExpr, Expr> {\n  YieldExpr();\n  YieldExpr(const YieldExpr &, bool);\n\n  ACCEPT(YieldExpr, ASTVisitor);\n};\n\n/// Await expression (await expr).\n/// @li await a\nstruct AwaitExpr : public AcceptorExtend<AwaitExpr, Expr> {\n  explicit AwaitExpr(Expr *expr);\n  AwaitExpr(const AwaitExpr &, bool);\n\n  Expr *getExpr() const { return expr; }\n\n  ACCEPT(AwaitExpr, ASTVisitor, expr);\n\nprivate:\n  Expr *expr;\n\n  // True if a statement was transformed during type-checking stage\n  // (to avoid setting up __await__ multiple times).\n  bool transformed;\n};\n\n/// Assignment (walrus) expression (var := expr).\n/// @li a := 5 + 3\nstruct AssignExpr : public AcceptorExtend<AssignExpr, Expr> {\n  explicit AssignExpr(Expr *var = nullptr, Expr *expr = nullptr);\n  AssignExpr(const AssignExpr &, bool);\n\n  Expr *getVar() const { return var; }\n  Expr *getExpr() const { return expr; }\n\n  ACCEPT(AssignExpr, ASTVisitor, var, expr);\n\nprivate:\n  Expr *var, *expr;\n};\n\n/// Range expression (start ... end).\n/// Used only in match-case statements.\n/// @li 1 ... 2\nstruct RangeExpr : public AcceptorExtend<RangeExpr, Expr> {\n  explicit RangeExpr(Expr *start = nullptr, Expr *stop = nullptr);\n  RangeExpr(const RangeExpr &, bool);\n\n  Expr *getStart() const { return start; }\n  Expr *getStop() const { return stop; }\n\n  ACCEPT(RangeExpr, ASTVisitor, start, stop);\n\nprivate:\n  Expr *start, *stop;\n};\n\n/// The following nodes are created during typechecking.\n\n/// Statement expression (stmts...; expr).\n/// Statements are evaluated only if the expression is evaluated\n/// (to support short-circuiting).\n/// @li (a = 1; b = 2; a + b)\nstruct StmtExpr : public AcceptorExtend<StmtExpr, Expr>, Items<Stmt *> {\n  explicit StmtExpr(Stmt *stmt = nullptr, Expr *expr = nullptr);\n  StmtExpr(std::vector<Stmt *> stmts, Expr *expr);\n  StmtExpr(Stmt *stmt, Stmt *stmt2, Expr *expr);\n  StmtExpr(const StmtExpr &, bool);\n\n  Expr *getExpr() const { return expr; }\n\n  ACCEPT(StmtExpr, ASTVisitor, expr, items);\n\nprivate:\n  Expr *expr;\n};\n\n/// Static tuple indexing expression (expr[index]).\n/// @li (1, 2, 3)[2]\nstruct InstantiateExpr : public AcceptorExtend<InstantiateExpr, Expr>, Items<Expr *> {\n  explicit InstantiateExpr(Expr *expr = nullptr, std::vector<Expr *> typeParams = {});\n  /// Convenience constructor for a single type parameter.\n  InstantiateExpr(Expr *expr, Expr *typeParam);\n  InstantiateExpr(const InstantiateExpr &, bool);\n\n  Expr *getExpr() const { return expr; }\n\n  ACCEPT(InstantiateExpr, ASTVisitor, expr, items);\n\nprivate:\n  Expr *expr;\n};\n\n#undef ACCEPT\n\nbool isId(Expr *e, const std::string &s);\ntypes::LiteralKind getStaticGeneric(Expr *e);\n\n} // namespace codon::ast\n\ntemplate <>\nstruct fmt::formatter<codon::ast::CallArg> : fmt::formatter<std::string_view> {\n  template <typename FormatContext>\n  auto format(const codon::ast::CallArg &p, FormatContext &ctx) const\n      -> decltype(ctx.out()) {\n    return fmt::format_to(ctx.out(), \"({}{})\",\n                          p.name.empty() ? \"\" : fmt::format(\"{} = \", p.name),\n                          p.value ? p.value->toString(0) : \"-\");\n  }\n};\n\ntemplate <>\nstruct fmt::formatter<codon::ast::Param> : fmt::formatter<std::string_view> {\n  template <typename FormatContext>\n  auto format(const codon::ast::Param &p, FormatContext &ctx) const\n      -> decltype(ctx.out()) {\n    return fmt::format_to(ctx.out(), \"{}\", p.toString(0));\n  }\n};\n\nnamespace tser {\nvoid operator<<(codon::ast::Expr *t, BinaryArchive &a);\nvoid operator>>(codon::ast::Expr *&t, BinaryArchive &a);\n} // namespace tser\n"
  },
  {
    "path": "codon/parser/ast/node.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <ostream>\n#include <string>\n\n#include \"codon/cir/base.h\"\n\nnamespace codon::ast {\n\nusing ir::cast;\n\n// Forward declarations\nstruct Cache;\nstruct ASTVisitor;\n\nstruct ASTNode : public ir::Node {\n  static const char NodeId;\n  using ir::Node::Node;\n\n  /// See LLVM documentation.\n  static const void *nodeId() { return &NodeId; }\n  const void *dynamicNodeId() const override { return &NodeId; }\n  /// See LLVM documentation.\n  virtual bool isConvertible(const void *other) const override {\n    return other == nodeId() || ir::Node::isConvertible(other);\n  }\n\n  Cache *cache = nullptr;\n\n  ASTNode() = default;\n  ASTNode(const ASTNode &) = default;\n  virtual ~ASTNode() = default;\n\n  /// Convert a node to an S-expression.\n  virtual std::string toString(int) const = 0;\n  virtual std::string toString() const { return toString(-1); }\n\n  /// Deep copy a node.\n  virtual ASTNode *clone(bool clean) const = 0;\n  ASTNode *clone() const { return clone(false); }\n\n  using ir::Node::accept;\n  /// Accept an AST visitor.\n  virtual void accept(ASTVisitor &visitor) {}\n\n  /// Allow pretty-printing to C++ streams.\n  friend std::ostream &operator<<(std::ostream &out, const ASTNode &expr) {\n    return out << expr.toString();\n  }\n\n  void setAttribute(int key, std::unique_ptr<ir::Attribute> value) {\n    attributes[key] = std::move(value);\n  }\n  void setAttribute(int key, const std::string &value) {\n    attributes[key] = std::make_unique<ir::StringValueAttribute>(value);\n  }\n  void setAttribute(int key, int64_t value) {\n    attributes[key] = std::make_unique<ir::IntValueAttribute>(value);\n  }\n  void setAttribute(int key) { attributes[key] = std::make_unique<ir::Attribute>(); }\n\n  inline decltype(auto) members() {\n    int a = 0;\n    return std::tie(a);\n  }\n};\n\ntemplate <class... TA> void E(error::Error e, ASTNode *o, const TA &...args) {\n  E(e, o->getSrcInfo(), args...);\n}\ntemplate <class... TA> void E(error::Error e, const ASTNode &o, const TA &...args) {\n  E(e, o.getSrcInfo(), args...);\n}\n\ntemplate <typename Derived, typename Parent> class AcceptorExtend : public Parent {\npublic:\n  using Parent::Parent;\n\n  /// See LLVM documentation.\n  static const void *nodeId() { return &Derived::NodeId; }\n  const void *dynamicNodeId() const override { return &Derived::NodeId; }\n  /// See LLVM documentation.\n  virtual bool isConvertible(const void *other) const override {\n    return other == nodeId() || Parent::isConvertible(other);\n  }\n};\n\ntemplate <class T> struct Items {\n  explicit Items(std::vector<T> items) : items(std::move(items)) {}\n  const T &operator[](size_t i) const { return items[i]; }\n  T &operator[](size_t i) { return items[i]; }\n  auto begin() { return items.begin(); }\n  auto end() { return items.end(); }\n  auto begin() const { return items.begin(); }\n  auto end() const { return items.end(); }\n  auto size() const { return items.size(); }\n  bool empty() const { return items.empty(); }\n  const T &front() const { return items.front(); }\n  const T &back() const { return items.back(); }\n  T &front() { return items.front(); }\n  T &back() { return items.back(); }\n\nprotected:\n  std::vector<T> items;\n};\n\n} // namespace codon::ast\n\ntemplate <typename T>\nstruct fmt::formatter<T,\n                      std::enable_if_t<std::is_base_of_v<codon::ast::ASTNode, T>, char>>\n    : fmt::ostream_formatter {};\n"
  },
  {
    "path": "codon/parser/ast/stmt.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"stmt.h\"\n\n#include <memory>\n#include <string>\n#include <utility>\n#include <vector>\n\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/match.h\"\n#include \"codon/parser/visitors/visitor.h\"\n\n#define ACCEPT_IMPL(T, X)                                                              \\\n  ASTNode *T::clone(bool clean) const { return cache->N<T>(*this, clean); }            \\\n  void T::accept(X &visitor) { visitor.visit(this); }                                  \\\n  const char T::NodeId = 0;\n\nusing namespace codon::error;\nusing namespace codon::matcher;\n\nnamespace codon::ast {\n\nStmt::Stmt() : AcceptorExtend(), done(false) {}\nStmt::Stmt(const Stmt &stmt) : AcceptorExtend(stmt), done(stmt.done) {}\nStmt::Stmt(const codon::SrcInfo &s) : AcceptorExtend(), done(false) { setSrcInfo(s); }\nStmt::Stmt(const Stmt &stmt, bool clean) : AcceptorExtend(stmt), done(stmt.done) {\n  if (clean)\n    done = false;\n}\nstd::string Stmt::wrapStmt(const std::string &s) const { return s; }\n\nSuiteStmt::SuiteStmt(std::vector<Stmt *> stmts)\n    : AcceptorExtend(), Items(std::move(stmts)) {}\nSuiteStmt::SuiteStmt(const SuiteStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), Items(ast::clone(stmt.items, clean)) {}\nstd::string SuiteStmt::toString(int indent) const {\n  if (indent == -1)\n    return \"\";\n  std::string pad = indent >= 0 ? (\"\\n\" + std::string(indent + INDENT_SIZE, ' ')) : \" \";\n  std::string s;\n  for (int i = 0; i < size(); i++)\n    if (items[i]) {\n      auto is = items[i]->toString(indent >= 0 ? indent + INDENT_SIZE : -1);\n      if (items[i]->isDone())\n        is.insert(findStar(is), \"*\");\n      s += (i ? pad : \"\") + is;\n    }\n  return wrapStmt(fmt::format(\"({}suite{})\", (isDone() ? \"*\" : \"\"),\n                              (s.empty() ? s : \" \" + pad + s)));\n}\nvoid SuiteStmt::flatten() {\n  std::vector<Stmt *> ns;\n  for (auto &s : items) {\n    if (!s)\n      continue;\n    if (!cast<SuiteStmt>(s)) {\n      ns.push_back(s);\n    } else {\n      for (auto *ss : *cast<SuiteStmt>(s))\n        ns.push_back(ss);\n    }\n  }\n  items = ns;\n}\nvoid SuiteStmt::addStmt(Stmt *s) {\n  if (s) {\n    items.push_back(s);\n    done = false;\n  }\n}\nSuiteStmt *SuiteStmt::wrap(Stmt *s) {\n  if (s && !cast<SuiteStmt>(s))\n    return s->cache->NS<SuiteStmt>(s, s);\n  return static_cast<SuiteStmt *>(s);\n}\n\nBreakStmt::BreakStmt(const BreakStmt &stmt, bool clean) : AcceptorExtend(stmt, clean) {}\nstd::string BreakStmt::toString(int indent) const { return wrapStmt(\"(break)\"); }\n\nContinueStmt::ContinueStmt(const ContinueStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean) {}\nstd::string ContinueStmt::toString(int indent) const { return wrapStmt(\"(continue)\"); }\n\nExprStmt::ExprStmt(Expr *expr) : AcceptorExtend(), expr(expr) {}\nExprStmt::ExprStmt(const ExprStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), expr(ast::clone(stmt.expr, clean)) {}\nstd::string ExprStmt::toString(int indent) const {\n  return wrapStmt(fmt::format(\"(expr {})\", expr->toString(indent)));\n}\n\nAssignStmt::AssignStmt(Expr *lhs, Expr *rhs, Expr *type, UpdateMode update)\n    : AcceptorExtend(), lhs(lhs), rhs(rhs), type(type), update(update) {}\nAssignStmt::AssignStmt(const AssignStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), lhs(ast::clone(stmt.lhs, clean)),\n      rhs(ast::clone(stmt.rhs, clean)), type(ast::clone(stmt.type, clean)),\n      update(stmt.update) {}\nstd::string AssignStmt::toString(int indent) const {\n  return wrapStmt(\n      fmt::format(\"({} {}{}{})\", update != Assign ? \"update\" : \"assign\",\n                  lhs->toString(indent), rhs ? \" \" + rhs->toString(indent) : \"\",\n                  type ? fmt::format(\" #:type {}\", type->toString(indent)) : \"\"));\n}\n\nDelStmt::DelStmt(Expr *expr) : AcceptorExtend(), expr(expr) {}\nDelStmt::DelStmt(const DelStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), expr(ast::clone(stmt.expr, clean)) {}\nstd::string DelStmt::toString(int indent) const {\n  return wrapStmt(fmt::format(\"(del {})\", expr->toString(indent)));\n}\n\nPrintStmt::PrintStmt(std::vector<Expr *> items, bool noNewline)\n    : AcceptorExtend(), Items(std::move(items)), noNewline(noNewline) {}\nPrintStmt::PrintStmt(const PrintStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), Items(ast::clone(stmt.items, clean)),\n      noNewline(stmt.noNewline) {}\nstd::string PrintStmt::toString(int indent) const {\n  return wrapStmt(\n      fmt::format(\"(print {}{})\", noNewline ? \"#:inline \" : \"\", combine(items)));\n}\n\nReturnStmt::ReturnStmt(Expr *expr) : AcceptorExtend(), expr(expr) {}\nReturnStmt::ReturnStmt(const ReturnStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), expr(ast::clone(stmt.expr, clean)) {}\nstd::string ReturnStmt::toString(int indent) const {\n  return wrapStmt(expr ? fmt::format(\"(return {})\", expr->toString(indent))\n                       : \"(return)\");\n}\n\nYieldStmt::YieldStmt(Expr *expr) : AcceptorExtend(), expr(expr) {}\nYieldStmt::YieldStmt(const YieldStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), expr(ast::clone(stmt.expr, clean)) {}\nstd::string YieldStmt::toString(int indent) const {\n  return wrapStmt(expr ? fmt::format(\"(yield {})\", expr->toString(indent)) : \"(yield)\");\n}\n\nAssertStmt::AssertStmt(Expr *expr, Expr *message)\n    : AcceptorExtend(), expr(expr), message(message) {}\nAssertStmt::AssertStmt(const AssertStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), expr(ast::clone(stmt.expr, clean)),\n      message(ast::clone(stmt.message, clean)) {}\nstd::string AssertStmt::toString(int indent) const {\n  return wrapStmt(fmt::format(\"(assert {}{})\", expr->toString(indent),\n                              message ? message->toString(indent) : \"\"));\n}\n\nWhileStmt::WhileStmt(Expr *cond, Stmt *suite, Stmt *elseSuite)\n    : AcceptorExtend(), cond(cond), suite(SuiteStmt::wrap(suite)),\n      elseSuite(SuiteStmt::wrap(elseSuite)) {}\nWhileStmt::WhileStmt(const WhileStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), cond(ast::clone(stmt.cond, clean)),\n      suite(ast::clone(stmt.suite, clean)),\n      elseSuite(ast::clone(stmt.elseSuite, clean)) {}\nstd::string WhileStmt::toString(int indent) const {\n  if (indent == -1)\n    return wrapStmt(fmt::format(\"(while {})\", cond->toString(indent)));\n  std::string pad = indent > 0 ? (\"\\n\" + std::string(indent + INDENT_SIZE, ' ')) : \" \";\n  if (elseSuite && elseSuite->firstInBlock()) {\n    return wrapStmt(\n        fmt::format(\"(while-else {}{}{}{}{})\", cond->toString(indent), pad,\n                    suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1), pad,\n                    elseSuite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)));\n  } else {\n    return wrapStmt(\n        fmt::format(\"(while {}{}{})\", cond->toString(indent), pad,\n                    suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)));\n  }\n}\n\nForStmt::ForStmt(Expr *var, Expr *iter, Stmt *suite, Stmt *elseSuite, Expr *decorator,\n                 std::vector<CallArg> ompArgs, bool async)\n    : AcceptorExtend(), var(var), iter(iter), suite(SuiteStmt::wrap(suite)),\n      elseSuite(SuiteStmt::wrap(elseSuite)), decorator(decorator),\n      ompArgs(std::move(ompArgs)), async(async), wrapped(false), flat(false) {}\nForStmt::ForStmt(const ForStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), var(ast::clone(stmt.var, clean)),\n      iter(ast::clone(stmt.iter, clean)), suite(ast::clone(stmt.suite, clean)),\n      elseSuite(ast::clone(stmt.elseSuite, clean)),\n      decorator(ast::clone(stmt.decorator, clean)),\n      ompArgs(ast::clone(stmt.ompArgs, clean)), async(stmt.async),\n      wrapped(stmt.wrapped), flat(stmt.flat) {}\nstd::string ForStmt::toString(int indent) const {\n  auto vs = var->toString(indent);\n  if (indent == -1)\n    return wrapStmt(fmt::format(\"(for {} {})\", vs, iter->toString(indent)));\n\n  std::string pad = indent > 0 ? (\"\\n\" + std::string(indent + INDENT_SIZE, ' ')) : \" \";\n  std::string attr;\n  if (decorator)\n    attr += \" \" + decorator->toString(indent);\n  if (!attr.empty())\n    attr = \" #:attr\" + attr;\n  if (elseSuite && elseSuite->firstInBlock()) {\n    return wrapStmt(\n        fmt::format(\"(for-else {} {}{}{}{}{}{})\", vs, iter->toString(indent), attr, pad,\n                    suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1), pad,\n                    elseSuite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)));\n  } else {\n    return wrapStmt(\n        fmt::format(\"(for {} {}{}{}{})\", vs, iter->toString(indent), attr, pad,\n                    suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)));\n  }\n}\n\nIfStmt::IfStmt(Expr *cond, Stmt *ifSuite, Stmt *elseSuite)\n    : AcceptorExtend(), cond(cond), ifSuite(SuiteStmt::wrap(ifSuite)),\n      elseSuite(SuiteStmt::wrap(elseSuite)) {}\nIfStmt::IfStmt(const IfStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), cond(ast::clone(stmt.cond, clean)),\n      ifSuite(ast::clone(stmt.ifSuite, clean)),\n      elseSuite(ast::clone(stmt.elseSuite, clean)) {}\nstd::string IfStmt::toString(int indent) const {\n  if (indent == -1)\n    return wrapStmt(fmt::format(\"(if {})\", cond->toString(indent)));\n  std::string pad = indent > 0 ? (\"\\n\" + std::string(indent + INDENT_SIZE, ' ')) : \" \";\n  return wrapStmt(fmt::format(\n      \"(if {}{}{}{})\", cond->toString(indent), pad,\n      ifSuite->toString(indent >= 0 ? indent + INDENT_SIZE : -1),\n      elseSuite ? pad + elseSuite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)\n                : \"\"));\n}\n\nMatchCase::MatchCase(Expr *pattern, Expr *guard, Stmt *suite)\n    : pattern(pattern), guard(guard), suite(SuiteStmt::wrap(suite)) {}\nMatchCase MatchCase::clone(bool clean) const {\n  return {ast::clone(pattern, clean), ast::clone(guard, clean),\n          ast::clone(suite, clean)};\n}\n\nMatchStmt::MatchStmt(Expr *expr, std::vector<MatchCase> cases)\n    : AcceptorExtend(), Items(std::move(cases)), expr(expr) {}\nMatchStmt::MatchStmt(const MatchStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), Items(ast::clone(stmt.items, clean)),\n      expr(ast::clone(stmt.expr, clean)) {}\nstd::string MatchStmt::toString(int indent) const {\n  if (indent == -1)\n    return wrapStmt(fmt::format(\"(match {})\", expr->toString(indent)));\n  std::string pad = indent > 0 ? (\"\\n\" + std::string(indent + INDENT_SIZE, ' ')) : \" \";\n  std::string padExtra = indent > 0 ? std::string(INDENT_SIZE, ' ') : \"\";\n  std::vector<std::string> s;\n  for (auto &c : items)\n    s.push_back(fmt::format(\n        \"(case {}{}{}{})\", c.pattern->toString(indent),\n        c.guard ? \" #:guard \" + c.guard->toString(indent) : \"\", pad + padExtra,\n        c.suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1 * 2)));\n  return wrapStmt(\n      fmt::format(\"(match {}{}{})\", expr->toString(indent), pad, join(s, pad)));\n}\n\nImportStmt::ImportStmt(Expr *from, Expr *what, std::vector<Param> args, Expr *ret,\n                       std::string as, size_t dots, bool isFunction)\n    : AcceptorExtend(), from(from), what(what), as(std::move(as)), dots(dots),\n      args(std::move(args)), ret(ret), isFunction(isFunction) {}\nImportStmt::ImportStmt(const ImportStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), from(ast::clone(stmt.from, clean)),\n      what(ast::clone(stmt.what, clean)), as(stmt.as), dots(stmt.dots),\n      args(ast::clone(stmt.args, clean)), ret(ast::clone(stmt.ret, clean)),\n      isFunction(stmt.isFunction) {}\nstd::string ImportStmt::toString(int indent) const {\n  std::vector<std::string> va;\n  for (auto &a : args)\n    va.push_back(a.toString(indent));\n  return wrapStmt(\n      fmt::format(\"(import {}{}{}{}{}{})\", from ? from->toString(indent) : \"\",\n                  as.empty() ? \"\" : fmt::format(\" #:as '{}\", as),\n                  what ? fmt::format(\" #:what {}\", what->toString(indent)) : \"\",\n                  dots ? fmt::format(\" #:dots {}\", dots) : \"\",\n                  va.empty() ? \"\" : fmt::format(\" #:args ({})\", join(va)),\n                  ret ? fmt::format(\" #:ret {}\", ret->toString(indent)) : \"\"));\n}\n\nExceptStmt::ExceptStmt(const std::string &var, Expr *exc, Stmt *suite)\n    : var(var), exc(exc), suite(SuiteStmt::wrap(suite)) {}\nExceptStmt::ExceptStmt(const ExceptStmt &stmt, bool clean)\n    : AcceptorExtend(stmt), var(stmt.var), exc(ast::clone(stmt.exc, clean)),\n      suite(ast::clone(stmt.suite, clean)) {}\nstd::string ExceptStmt::toString(int indent) const {\n  std::string pad = indent > 0 ? (\"\\n\" + std::string(indent + INDENT_SIZE, ' ')) : \" \";\n  std::string padExtra = indent > 0 ? std::string(INDENT_SIZE, ' ') : \"\";\n  return wrapStmt(fmt::format(\n      \"(catch {}{}{}{})\", !var.empty() ? fmt::format(\"#:var '{}\", var) : \"\",\n      exc ? fmt::format(\" #:exc {}\", exc->toString(indent)) : \"\", pad + padExtra,\n      suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1 * 2)));\n}\n\nTryStmt::TryStmt(Stmt *suite, std::vector<ExceptStmt *> excepts, Stmt *elseSuite,\n                 Stmt *finally)\n    : AcceptorExtend(), Items(std::move(excepts)), suite(SuiteStmt::wrap(suite)),\n      elseSuite(SuiteStmt::wrap(elseSuite)), finally(SuiteStmt::wrap(finally)) {}\nTryStmt::TryStmt(const TryStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), Items(ast::clone(stmt.items, clean)),\n      suite(ast::clone(stmt.suite, clean)),\n      elseSuite(ast::clone(stmt.elseSuite, clean)),\n      finally(ast::clone(stmt.finally, clean)) {}\nstd::string TryStmt::toString(int indent) const {\n  if (indent == -1)\n    return wrapStmt(fmt::format(\"(try)\"));\n  std::string pad = indent > 0 ? (\"\\n\" + std::string(indent + INDENT_SIZE, ' ')) : \" \";\n  std::vector<std::string> s;\n  for (auto &i : items)\n    s.push_back(i->toString(indent));\n  return wrapStmt(fmt::format(\n      \"(try{}{}{}{}{})\", pad, suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1),\n      pad, join(s, pad),\n      elseSuite\n          ? fmt::format(\"{}(else {})\", pad,\n                        elseSuite->toString(indent >= 0 ? indent + INDENT_SIZE : -1))\n          : \"\",\n      finally ? fmt::format(\"{}(finally {})\", pad,\n                            finally->toString(indent >= 0 ? indent + INDENT_SIZE : -1))\n              : \"\"));\n}\n\nThrowStmt::ThrowStmt(Expr *expr, Expr *from, bool transformed)\n    : AcceptorExtend(), expr(expr), from(from), transformed(transformed) {}\nThrowStmt::ThrowStmt(const ThrowStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), expr(ast::clone(stmt.expr, clean)),\n      from(ast::clone(stmt.from, clean)), transformed(stmt.transformed) {}\nstd::string ThrowStmt::toString(int indent) const {\n  return wrapStmt(\n      fmt::format(\"(throw{}{})\", expr ? \" \" + expr->toString(indent) : \"\",\n                  from ? fmt::format(\" :from {}\", from->toString(indent)) : \"\"));\n}\n\nGlobalStmt::GlobalStmt(std::string var, bool nonLocal)\n    : AcceptorExtend(), var(std::move(var)), nonLocal(nonLocal) {}\nGlobalStmt::GlobalStmt(const GlobalStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), var(stmt.var), nonLocal(stmt.nonLocal) {}\nstd::string GlobalStmt::toString(int indent) const {\n  return wrapStmt(fmt::format(\"({} '{})\", nonLocal ? \"nonlocal\" : \"global\", var));\n}\n\nFunctionStmt::FunctionStmt(std::string name, Expr *ret, std::vector<Param> args,\n                           Stmt *suite, std::vector<Expr *> decorators, bool async)\n    : AcceptorExtend(), Items(std::move(args)), name(std::move(name)), ret(ret),\n      suite(SuiteStmt::wrap(suite)), decorators(std::move(decorators)), async(async) {}\nFunctionStmt::FunctionStmt(const FunctionStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), Items(ast::clone(stmt.items, clean)),\n      name(stmt.name), ret(ast::clone(stmt.ret, clean)),\n      suite(ast::clone(stmt.suite, clean)),\n      decorators(ast::clone(stmt.decorators, clean)), async(stmt.async) {}\nstd::string FunctionStmt::toString(int indent) const {\n  std::string pad = indent > 0 ? (\"\\n\" + std::string(indent + INDENT_SIZE, ' ')) : \" \";\n  std::vector<std::string> as;\n  for (auto &a : items)\n    as.push_back(a.toString(indent));\n  std::vector<std::string> dec;\n  for (auto &a : decorators)\n    if (a)\n      dec.push_back(fmt::format(\"(dec {})\", a->toString(indent)));\n  if (indent == -1)\n    return wrapStmt(fmt::format(\"(fn '{} ({}){})\", name, join(as, \" \"),\n                                ret ? \" #:ret \" + ret->toString(indent) : \"\"));\n  return wrapStmt(fmt::format(\n      \"(fn '{} ({}){}{}{}{})\", name, join(as, \" \"),\n      ret ? \" #:ret \" + ret->toString(indent) : \"\",\n      dec.empty() ? \"\" : fmt::format(\" (dec {})\", join(dec, \" \")), pad,\n      suite ? suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1) : \"(suite)\"));\n}\nstd::string FunctionStmt::getSignature() {\n  if (signature.empty()) {\n    std::vector<std::string> s;\n    for (auto &a : items)\n      s.push_back(a.type ? a.type->toString() : \"-\");\n    signature = join(s, \":\");\n  }\n  return signature;\n}\nsize_t FunctionStmt::getStarArgs() const {\n  size_t i = 0;\n  while (i < items.size()) {\n    if (startswith(items[i].name, \"*\") && !startswith(items[i].name, \"**\"))\n      break;\n    i++;\n  }\n  return i;\n}\nsize_t FunctionStmt::getKwStarArgs() const {\n  size_t i = 0;\n  while (i < items.size()) {\n    if (startswith(items[i].name, \"**\"))\n      break;\n    i++;\n  }\n  return i;\n}\nstd::string FunctionStmt::getDocstr() const {\n  if (auto s = suite->firstInBlock()) {\n    if (auto e = cast<ExprStmt>(s)) {\n      if (auto ss = cast<StringExpr>(e->getExpr()))\n        return ss->getValue();\n    }\n  }\n  return \"\";\n}\nbool FunctionStmt::hasFunctionAttribute(const std::string &attr) const {\n  if (auto f = getAttribute<ir::KeyValueAttribute>(Attr::FunctionAttributes)) {\n    return in(f->attributes, attr) != nullptr;\n  }\n  return false;\n}\n\n// Search expression tree for a identifier\nclass IdSearchVisitor : public CallbackASTVisitor<bool, bool> {\n  std::string what;\n  bool result;\n\npublic:\n  IdSearchVisitor(std::string what) : what(std::move(what)), result(false) {}\n  bool transform(Expr *expr) override {\n    if (result)\n      return result;\n    IdSearchVisitor v(what);\n    if (expr)\n      expr->accept(v);\n    return result = v.result;\n  }\n  bool transform(Stmt *stmt) override {\n    if (result)\n      return result;\n    IdSearchVisitor v(what);\n    if (stmt)\n      stmt->accept(v);\n    return result = v.result;\n  }\n  void visit(IdExpr *expr) override {\n    if (expr->getValue() == what)\n      result = true;\n  }\n};\n\n/// Check if a function can be called with the given arguments.\n/// See @c reorderNamedArgs for details.\nstd::unordered_set<std::string> FunctionStmt::getNonInferrableGenerics() const {\n  std::unordered_set<std::string> nonInferrableGenerics;\n  for (const auto &a : items) {\n    if (a.status == Param::Generic && !a.defaultValue) {\n      bool inferrable = false;\n      for (const auto &b : items)\n        if (b.type && IdSearchVisitor(a.name).transform(b.type)) {\n          inferrable = true;\n          break;\n        }\n      if (ret && IdSearchVisitor(a.name).transform(ret))\n        inferrable = true;\n      if (!inferrable)\n        nonInferrableGenerics.insert(a.name);\n    }\n  }\n  return nonInferrableGenerics;\n}\n\nClassStmt::ClassStmt(std::string name, std::vector<Param> args, Stmt *suite,\n                     std::vector<Expr *> decorators,\n                     const std::vector<Expr *> &baseClasses,\n                     std::vector<Expr *> staticBaseClasses)\n    : AcceptorExtend(), Items(std::move(args)), name(std::move(name)),\n      suite(SuiteStmt::wrap(suite)), decorators(std::move(decorators)),\n      staticBaseClasses(std::move(staticBaseClasses)) {\n  for (auto &b : baseClasses) {\n    Expr *e = nullptr;\n    if (match(b, M<IndexExpr>(M<IdExpr>(\"Static\"), MVar<Expr>(e)))) {\n      this->staticBaseClasses.push_back(e);\n    } else {\n      this->baseClasses.push_back(b);\n    }\n  }\n}\nClassStmt::ClassStmt(const ClassStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), Items(ast::clone(stmt.items, clean)),\n      name(stmt.name), suite(ast::clone(stmt.suite, clean)),\n      decorators(ast::clone(stmt.decorators, clean)),\n      baseClasses(ast::clone(stmt.baseClasses, clean)),\n      staticBaseClasses(ast::clone(stmt.staticBaseClasses, clean)) {}\nstd::string ClassStmt::toString(int indent) const {\n  std::string pad = indent > 0 ? (\"\\n\" + std::string(indent + INDENT_SIZE, ' ')) : \" \";\n  std::vector<std::string> bases;\n  for (auto &b : baseClasses)\n    bases.push_back(b->toString(indent));\n  for (auto &b : staticBaseClasses)\n    bases.push_back(fmt::format(\"(static {})\", b->toString(indent)));\n  std::string as;\n  for (int i = 0; i < items.size(); i++)\n    as += (i ? pad : \"\") + items[i].toString(indent);\n  std::vector<std::string> attr;\n  for (auto &a : decorators)\n    attr.push_back(fmt::format(\"(dec {})\", a->toString(indent)));\n  if (indent == -1)\n    return wrapStmt(fmt::format(\"(class '{} ({}))\", name, as));\n  return wrapStmt(fmt::format(\n      \"(class '{}{}{}{}{}{})\", name,\n      bases.empty() ? \"\" : fmt::format(\" (bases {})\", join(bases, \" \")),\n      attr.empty() ? \"\" : fmt::format(\" (attr {})\", join(attr, \" \")),\n      as.empty() ? as : pad + as, pad,\n      suite ? suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1) : \"(suite)\"));\n}\nbool ClassStmt::isRecord() const { return hasAttribute(Attr::Tuple); }\nbool ClassStmt::isClassVar(const Param &p) {\n  if (!p.type)\n    return true;\n  if (auto i = cast<IndexExpr>(p.type))\n    return isId(i->getExpr(), \"ClassVar\");\n  return false;\n}\nstd::string ClassStmt::getDocstr() const {\n  if (auto s = suite->firstInBlock()) {\n    if (auto e = cast<ExprStmt>(s)) {\n      if (auto ss = cast<StringExpr>(e->getExpr()))\n        return ss->getValue();\n    }\n  }\n  return \"\";\n}\n\nYieldFromStmt::YieldFromStmt(Expr *expr) : AcceptorExtend(), expr(std::move(expr)) {}\nYieldFromStmt::YieldFromStmt(const YieldFromStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), expr(ast::clone(stmt.expr, clean)) {}\nstd::string YieldFromStmt::toString(int indent) const {\n  return wrapStmt(fmt::format(\"(yield-from {})\", expr->toString(indent)));\n}\n\nWithStmt::WithStmt(std::vector<Expr *> items, std::vector<std::string> vars,\n                   Stmt *suite, bool isAsync)\n    : AcceptorExtend(), Items(std::move(items)), vars(std::move(vars)),\n      suite(SuiteStmt::wrap(suite)), async(isAsync) {\n  seqassert(this->items.size() == this->vars.size(), \"vector size mismatch\");\n}\nWithStmt::WithStmt(std::vector<std::pair<Expr *, Expr *>> itemVarPairs, Stmt *suite,\n                   bool isAsync)\n    : AcceptorExtend(), Items({}), suite(SuiteStmt::wrap(suite)), async(isAsync) {\n  for (auto [i, j] : itemVarPairs) {\n    items.push_back(i);\n    if (auto je = cast<IdExpr>(j)) {\n      vars.push_back(je->getValue());\n    } else {\n      vars.emplace_back();\n    }\n  }\n}\nWithStmt::WithStmt(const WithStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), Items(ast::clone(stmt.items, clean)),\n      vars(stmt.vars), suite(ast::clone(stmt.suite, clean)), async(stmt.async) {}\nstd::string WithStmt::toString(int indent) const {\n  std::string pad = indent > 0 ? (\"\\n\" + std::string(indent + INDENT_SIZE, ' ')) : \" \";\n  std::vector<std::string> as;\n  as.reserve(items.size());\n  for (int i = 0; i < items.size(); i++) {\n    as.push_back(!vars[i].empty() ? fmt::format(\"({} #:var '{})\",\n                                                items[i]->toString(indent), vars[i])\n                                  : items[i]->toString(indent));\n  }\n  if (indent == -1)\n    return wrapStmt(fmt::format(\"(with ({}))\", join(as, \" \")));\n  return wrapStmt(\n      fmt::format(\"(with ({}){}{})\", join(as, \" \"), pad,\n                  suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1)));\n}\n\nCustomStmt::CustomStmt(std::string keyword, Expr *expr, Stmt *suite)\n    : AcceptorExtend(), keyword(std::move(keyword)), expr(expr),\n      suite(SuiteStmt::wrap(suite)) {}\nCustomStmt::CustomStmt(const CustomStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), keyword(stmt.keyword),\n      expr(ast::clone(stmt.expr, clean)), suite(ast::clone(stmt.suite, clean)) {}\nstd::string CustomStmt::toString(int indent) const {\n  std::string pad = indent > 0 ? (\"\\n\" + std::string(indent + INDENT_SIZE, ' ')) : \" \";\n  return wrapStmt(fmt::format(\n      \"(custom-{} {}{}{})\", keyword,\n      expr ? fmt::format(\" #:expr {}\", expr->toString(indent)) : \"\", pad,\n      suite ? suite->toString(indent >= 0 ? indent + INDENT_SIZE : -1) : \"\"));\n}\n\nDirectiveStmt::DirectiveStmt(std::string key, std::string value)\n    : AcceptorExtend(), key(std::move(key)), value(std::move(value)) {}\nDirectiveStmt::DirectiveStmt(const DirectiveStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), key(stmt.key), value(stmt.value) {}\nstd::string DirectiveStmt::toString(int indent) const {\n  std::string pad = indent > 0 ? (\"\\n\" + std::string(indent + INDENT_SIZE, ' ')) : \" \";\n  return wrapStmt(fmt::format(\"(directive {} '{}')\", key, value));\n}\n\nAssignMemberStmt::AssignMemberStmt(Expr *lhs, std::string member, Expr *rhs, Expr *type)\n    : AcceptorExtend(), lhs(lhs), member(std::move(member)), rhs(rhs), type(type) {}\nAssignMemberStmt::AssignMemberStmt(const AssignMemberStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), lhs(ast::clone(stmt.lhs, clean)),\n      member(stmt.member), rhs(ast::clone(stmt.rhs, clean)),\n      type(ast::clone(stmt.type, clean)) {}\nstd::string AssignMemberStmt::toString(int indent) const {\n  return wrapStmt(fmt::format(\"(assign-member {} {} {})\", lhs->toString(indent), member,\n                              rhs->toString(indent)));\n}\n\nCommentStmt::CommentStmt(std::string comment)\n    : AcceptorExtend(), comment(std::move(comment)) {}\nCommentStmt::CommentStmt(const CommentStmt &stmt, bool clean)\n    : AcceptorExtend(stmt, clean), comment(stmt.comment) {}\nstd::string CommentStmt::toString(int indent) const {\n  return wrapStmt(fmt::format(\"(comment \\\"{}\\\")\", comment));\n}\n\nconst char Stmt::NodeId = 0;\nACCEPT_IMPL(SuiteStmt, ASTVisitor);\nACCEPT_IMPL(BreakStmt, ASTVisitor);\nACCEPT_IMPL(ContinueStmt, ASTVisitor);\nACCEPT_IMPL(ExprStmt, ASTVisitor);\nACCEPT_IMPL(AssignStmt, ASTVisitor);\nACCEPT_IMPL(DelStmt, ASTVisitor);\nACCEPT_IMPL(PrintStmt, ASTVisitor);\nACCEPT_IMPL(ReturnStmt, ASTVisitor);\nACCEPT_IMPL(YieldStmt, ASTVisitor);\nACCEPT_IMPL(AssertStmt, ASTVisitor);\nACCEPT_IMPL(WhileStmt, ASTVisitor);\nACCEPT_IMPL(ForStmt, ASTVisitor);\nACCEPT_IMPL(IfStmt, ASTVisitor);\nACCEPT_IMPL(MatchStmt, ASTVisitor);\nACCEPT_IMPL(ImportStmt, ASTVisitor);\nACCEPT_IMPL(ExceptStmt, ASTVisitor);\nACCEPT_IMPL(TryStmt, ASTVisitor);\nACCEPT_IMPL(ThrowStmt, ASTVisitor);\nACCEPT_IMPL(GlobalStmt, ASTVisitor);\nACCEPT_IMPL(FunctionStmt, ASTVisitor);\nACCEPT_IMPL(ClassStmt, ASTVisitor);\nACCEPT_IMPL(YieldFromStmt, ASTVisitor);\nACCEPT_IMPL(WithStmt, ASTVisitor);\nACCEPT_IMPL(CustomStmt, ASTVisitor);\nACCEPT_IMPL(DirectiveStmt, ASTVisitor);\nACCEPT_IMPL(AssignMemberStmt, ASTVisitor);\nACCEPT_IMPL(CommentStmt, ASTVisitor);\n\n} // namespace codon::ast\n\nnamespace tser {\nvoid operator<<(codon::ast::Stmt *t, BinaryArchive &a) {\n  using S = codon::PolymorphicSerializer<BinaryArchive, codon::ast::Stmt>;\n  a.save(t != nullptr);\n  if (t) {\n    auto typ = t->dynamicNodeId();\n    auto key = S::_serializers[const_cast<void *>(typ)];\n    a.save(key);\n    S::save(key, t, a);\n  }\n}\nvoid operator>>(codon::ast::Stmt *&t, BinaryArchive &a) {\n  using S = codon::PolymorphicSerializer<BinaryArchive, codon::ast::Stmt>;\n  bool empty = a.load<bool>();\n  if (!empty) {\n    std::string key = a.load<std::string>();\n    S::load(key, t, a);\n  } else {\n    t = nullptr;\n  }\n}\n} // namespace tser\n"
  },
  {
    "path": "codon/parser/ast/stmt.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <ostream>\n#include <string>\n#include <vector>\n\n#include \"codon/parser/ast/expr.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/util/serialize.h\"\n\nnamespace codon::ast {\n\n#define ACCEPT(CLASS, VISITOR, ...)                                                    \\\n  static const char NodeId;                                                            \\\n  using AcceptorExtend::clone;                                                         \\\n  using AcceptorExtend::accept;                                                        \\\n  ASTNode *clone(bool c) const override;                                               \\\n  void accept(VISITOR &visitor) override;                                              \\\n  std::string toString(int) const override;                                            \\\n  friend class TypecheckVisitor;                                                       \\\n  template <typename TE, typename TS> friend struct CallbackASTVisitor;                \\\n  friend struct ReplacingCallbackASTVisitor;                                           \\\n  inline decltype(auto) match_members() const { return std::tie(__VA_ARGS__); }        \\\n  SERIALIZE(CLASS, BASE(Stmt), ##__VA_ARGS__)\n\n// Forward declarations\nstruct ASTVisitor;\n\n/**\n * A Seq AST statement.\n * Each AST statement is intended to be instantiated as a shared_ptr.\n */\nstruct Stmt : public AcceptorExtend<Stmt, ASTNode> {\n  using base_type = Stmt;\n\n  Stmt();\n  Stmt(const Stmt &s);\n  Stmt(const Stmt &, bool);\n  explicit Stmt(const codon::SrcInfo &s);\n\n  bool isDone() const { return done; }\n  void setDone() { done = true; }\n  /// @return the first statement in a suite; if a statement is not a suite, returns the\n  /// statement itself\n  virtual Stmt *firstInBlock() { return this; }\n\n  static const char NodeId;\n  SERIALIZE(Stmt, BASE(ASTNode), done);\n\n  virtual std::string wrapStmt(const std::string &) const;\n\nprotected:\n  /// Flag that indicates if all types in a statement are inferred (i.e. if a\n  /// type-checking procedure was successful).\n  bool done;\n};\n\n/// Suite (block of statements) statement (stmt...).\n/// @li a = 5; foo(1)\nstruct SuiteStmt : public AcceptorExtend<SuiteStmt, Stmt>, Items<Stmt *> {\n  explicit SuiteStmt(std::vector<Stmt *> stmts = {});\n  /// Convenience constructor\n  template <typename... Ts>\n  explicit SuiteStmt(Stmt *stmt, Ts... stmts) : Items({stmt, stmts...}) {}\n  SuiteStmt(const SuiteStmt &, bool);\n\n  Stmt *firstInBlock() override {\n    return items.empty() ? nullptr : items[0]->firstInBlock();\n  }\n  void flatten();\n  void addStmt(Stmt *s);\n\n  static SuiteStmt *wrap(Stmt *);\n\n  ACCEPT(SuiteStmt, ASTVisitor, items);\n};\n\n/// Break statement.\n/// @li break\nstruct BreakStmt : public AcceptorExtend<BreakStmt, Stmt> {\n  BreakStmt() = default;\n  BreakStmt(const BreakStmt &, bool);\n\n  ACCEPT(BreakStmt, ASTVisitor);\n};\n\n/// Continue statement.\n/// @li continue\nstruct ContinueStmt : public AcceptorExtend<ContinueStmt, Stmt> {\n  ContinueStmt() = default;\n  ContinueStmt(const ContinueStmt &, bool);\n\n  ACCEPT(ContinueStmt, ASTVisitor);\n};\n\n/// Expression statement (expr).\n/// @li 3 + foo()\nstruct ExprStmt : public AcceptorExtend<ExprStmt, Stmt> {\n  explicit ExprStmt(Expr *expr = nullptr);\n  ExprStmt(const ExprStmt &, bool);\n\n  Expr *getExpr() const { return expr; }\n\n  ACCEPT(ExprStmt, ASTVisitor, expr);\n\nprivate:\n  Expr *expr;\n};\n\n/// Assignment statement (lhs: type = rhs).\n/// @li a = 5\n/// @li a: Optional[int] = 5\n/// @li a, b, c = 5, *z\nstruct AssignStmt : public AcceptorExtend<AssignStmt, Stmt> {\n  enum UpdateMode { Assign, Update, UpdateAtomic, ThreadLocalAssign };\n\n  AssignStmt()\n      : lhs(nullptr), rhs(nullptr), type(nullptr), update(UpdateMode::Assign) {}\n  AssignStmt(Expr *lhs, Expr *rhs, Expr *type = nullptr,\n             UpdateMode update = UpdateMode::Assign);\n  AssignStmt(const AssignStmt &, bool);\n\n  Expr *getLhs() const { return lhs; }\n  Expr *getRhs() const { return rhs; }\n  Expr *getTypeExpr() const { return type; }\n\n  void setLhs(Expr *expr) { lhs = expr; }\n  void setRhs(Expr *expr) { rhs = expr; }\n\n  bool isAssignment() const { return update == Assign; }\n  bool isUpdate() const { return update == Update; }\n  bool isAtomicUpdate() const { return update == UpdateAtomic; }\n  bool isThreadLocal() { return update == ThreadLocalAssign; }\n  void setUpdate() { update = Update; }\n  void setAtomicUpdate() { update = UpdateAtomic; }\n  void setThreadLocal() { update = ThreadLocalAssign; }\n\n  ACCEPT(AssignStmt, ASTVisitor, lhs, rhs, type, update);\n\nprivate:\n  Expr *lhs, *rhs, *type;\n  UpdateMode update;\n};\n\n/// Deletion statement (del expr).\n/// @li del a\n/// @li del a[5]\nstruct DelStmt : public AcceptorExtend<DelStmt, Stmt> {\n  explicit DelStmt(Expr *expr = nullptr);\n  DelStmt(const DelStmt &, bool);\n\n  Expr *getExpr() const { return expr; }\n\n  ACCEPT(DelStmt, ASTVisitor, expr);\n\nprivate:\n  Expr *expr;\n};\n\n/// Print statement (print expr).\n/// @li print a, b\nstruct PrintStmt : public AcceptorExtend<PrintStmt, Stmt>, Items<Expr *> {\n  explicit PrintStmt(std::vector<Expr *> items = {}, bool noNewline = false);\n  PrintStmt(const PrintStmt &, bool);\n\n  bool hasNewline() const { return !noNewline; }\n\n  ACCEPT(PrintStmt, ASTVisitor, items, noNewline);\n\nprivate:\n  /// True if there is a dangling comma after print: print a,\n  bool noNewline;\n};\n\n/// Return statement (return expr).\n/// @li return\n/// @li return a\nstruct ReturnStmt : public AcceptorExtend<ReturnStmt, Stmt> {\n  explicit ReturnStmt(Expr *expr = nullptr);\n  ReturnStmt(const ReturnStmt &, bool);\n\n  Expr *getExpr() const { return expr; }\n\n  ACCEPT(ReturnStmt, ASTVisitor, expr);\n\nprivate:\n  /// nullptr if this is an empty return/yield statements.\n  Expr *expr;\n};\n\n/// Yield statement (yield expr).\n/// @li yield\n/// @li yield a\nstruct YieldStmt : public AcceptorExtend<YieldStmt, Stmt> {\n  explicit YieldStmt(Expr *expr = nullptr);\n  YieldStmt(const YieldStmt &, bool);\n\n  Expr *getExpr() const { return expr; }\n\n  ACCEPT(YieldStmt, ASTVisitor, expr);\n\nprivate:\n  /// nullptr if this is an empty return/yield statements.\n  Expr *expr;\n};\n\n/// Assert statement (assert expr).\n/// @li assert a\n/// @li assert a, \"Message\"\nstruct AssertStmt : public AcceptorExtend<AssertStmt, Stmt> {\n  explicit AssertStmt(Expr *expr = nullptr, Expr *message = nullptr);\n  AssertStmt(const AssertStmt &, bool);\n\n  Expr *getExpr() const { return expr; }\n  Expr *getMessage() const { return message; }\n\n  ACCEPT(AssertStmt, ASTVisitor, expr, message);\n\nprivate:\n  Expr *expr;\n  /// nullptr if there is no message.\n  Expr *message;\n};\n\n/// While loop statement (while cond: suite; else: elseSuite).\n/// @li while True: print\n/// @li while True: break\n///          else: print\nstruct WhileStmt : public AcceptorExtend<WhileStmt, Stmt> {\n  WhileStmt() : cond(nullptr), suite(nullptr), elseSuite(nullptr), gotoVar() {}\n  WhileStmt(Expr *cond, Stmt *suite, Stmt *elseSuite = nullptr);\n  WhileStmt(const WhileStmt &, bool);\n\n  Expr *getCond() const { return cond; }\n  SuiteStmt *getSuite() const { return suite; }\n  SuiteStmt *getElse() const { return elseSuite; }\n\n  ACCEPT(WhileStmt, ASTVisitor, cond, suite, elseSuite, gotoVar);\n\nprivate:\n  Expr *cond;\n  SuiteStmt *suite;\n  /// nullptr if there is no else suite.\n  SuiteStmt *elseSuite;\n\n  /// Set if a while loop is used to emulate goto statement\n  /// (as `while gotoVar: ...`).\n  std::string gotoVar;\n};\n\n/// For loop statement (for var in iter: suite; else elseSuite).\n/// @li for a, b in c: print\n/// @li for i in j: break\n///          else: print\nstruct ForStmt : public AcceptorExtend<ForStmt, Stmt> {\n  ForStmt()\n      : var(nullptr), iter(nullptr), suite(nullptr), elseSuite(nullptr),\n        decorator(nullptr), ompArgs(), async(false), wrapped(false), flat(false) {}\n  ForStmt(Expr *var, Expr *iter, Stmt *suite, Stmt *elseSuite = nullptr,\n          Expr *decorator = nullptr, std::vector<CallArg> ompArgs = {},\n          bool async = false);\n  ForStmt(const ForStmt &, bool);\n\n  Expr *getVar() const { return var; }\n  Expr *getIter() const { return iter; }\n  SuiteStmt *getSuite() const { return suite; }\n  SuiteStmt *getElse() const { return elseSuite; }\n  Expr *getDecorator() const { return decorator; }\n  void setDecorator(Expr *e) { decorator = e; }\n  bool isAsync() const { return async; }\n  void setAsync() { async = true; }\n  bool isWrapped() const { return wrapped; }\n  bool isFlat() const { return flat; }\n\n  ACCEPT(ForStmt, ASTVisitor, var, iter, suite, elseSuite, decorator, ompArgs, async,\n         wrapped, flat);\n\nprivate:\n  Expr *var;\n  Expr *iter;\n  SuiteStmt *suite;\n  SuiteStmt *elseSuite;\n  Expr *decorator;\n  std::vector<CallArg> ompArgs;\n  bool async;\n\n  /// Indicates if iter was wrapped with __iter__() call.\n  bool wrapped;\n  /// True if there are no break/continue within the loop\n  bool flat;\n\n  friend struct GeneratorExpr;\n  friend class ScopingVisitor;\n};\n\n/// If block statement (if cond: suite; (elif cond: suite)...).\n/// @li if a: foo()\n/// @li if a: foo()\n///          elif b: bar()\n/// @li if a: foo()\n///          elif b: bar()\n///          else: baz()\nstruct IfStmt : public AcceptorExtend<IfStmt, Stmt> {\n  IfStmt(Expr *cond = nullptr, Stmt *ifSuite = nullptr, Stmt *elseSuite = nullptr);\n  IfStmt(const IfStmt &, bool);\n\n  Expr *getCond() const { return cond; }\n  SuiteStmt *getIf() const { return ifSuite; }\n  SuiteStmt *getElse() const { return elseSuite; }\n\n  ACCEPT(IfStmt, ASTVisitor, cond, ifSuite, elseSuite);\n\nprivate:\n  Expr *cond;\n  /// elseSuite can be nullptr (if no else is found).\n  SuiteStmt *ifSuite, *elseSuite;\n\n  friend struct GeneratorExpr;\n};\n\nstruct MatchCase {\n  MatchCase(Expr *pattern = nullptr, Expr *guard = nullptr, Stmt *suite = nullptr);\n\n  Expr *getPattern() const { return pattern; }\n  Expr *getGuard() const { return guard; }\n  SuiteStmt *getSuite() const { return suite; }\n\n  MatchCase clone(bool) const;\n  SERIALIZE(MatchCase, pattern, guard, suite);\n\nprivate:\n  Expr *pattern;\n  Expr *guard;\n  SuiteStmt *suite;\n\n  friend struct MatchStmt;\n  friend class TypecheckVisitor;\n  template <typename TE, typename TS> friend struct CallbackASTVisitor;\n  friend struct ReplacingCallbackASTVisitor;\n};\n\n/// Match statement (match what: (case pattern: case)...).\n/// @li match a:\n///          case 1: print\n///          case _: pass\nstruct MatchStmt : public AcceptorExtend<MatchStmt, Stmt>, Items<MatchCase> {\n  MatchStmt(Expr *what = nullptr, std::vector<MatchCase> cases = {});\n  MatchStmt(const MatchStmt &, bool);\n\n  Expr *getExpr() const { return expr; }\n\n  ACCEPT(MatchStmt, ASTVisitor, items, expr);\n\nprivate:\n  Expr *expr;\n};\n\n/// Import statement.\n/// This node describes various kinds of import statements:\n///  - from from import what (as as)\n///  - import what (as as)\n///  - from c import what(args...) (-> ret) (as as)\n///  - from .(dots...)from import what (as as)\n/// @li import a\n/// @li from b import a\n/// @li from ...b import a as ai\n/// @li from c import foo(int) -> int as bar\n/// @li from python.numpy import array\n/// @li from python import numpy.array(int) -> int as na\nstruct ImportStmt : public AcceptorExtend<ImportStmt, Stmt> {\n  ImportStmt(Expr *from = nullptr, Expr *what = nullptr, std::vector<Param> args = {},\n             Expr *ret = nullptr, std::string as = \"\", size_t dots = 0,\n             bool isFunction = true);\n  ImportStmt(const ImportStmt &, bool);\n\n  Expr *getFrom() const { return from; }\n  Expr *getWhat() const { return what; }\n  std::string getAs() const { return as; }\n  size_t getDots() const { return dots; }\n  Expr *getReturnType() const { return ret; }\n  const std::vector<Param> &getArgs() const { return args; }\n  bool isCVar() const { return !isFunction; }\n\n  ACCEPT(ImportStmt, ASTVisitor, from, what, as, dots, args, ret, isFunction);\n\nprivate:\n  Expr *from, *what;\n  std::string as;\n  /// Number of dots in a relative import (e.g. dots is 3 for \"from ...foo\").\n  size_t dots;\n  /// Function argument types for C imports.\n  std::vector<Param> args;\n  /// Function return type for C imports.\n  Expr *ret;\n  /// Set if this is a function C import (not variable import)\n  bool isFunction;\n};\n\nstruct ExceptStmt : public AcceptorExtend<ExceptStmt, Stmt> {\n  ExceptStmt(const std::string &var = \"\", Expr *exc = nullptr, Stmt *suite = nullptr);\n  ExceptStmt(const ExceptStmt &, bool);\n\n  std::string getVar() const { return var; }\n  Expr *getException() const { return exc; }\n  SuiteStmt *getSuite() const { return suite; }\n\n  ACCEPT(ExceptStmt, ASTVisitor, var, exc, suite);\n\nprivate:\n  /// empty string if an except is unnamed.\n  std::string var;\n  /// nullptr if there is no explicit exception type.\n  Expr *exc;\n  SuiteStmt *suite;\n\n  friend class ScopingVisitor;\n};\n\n/// Try-except statement (try: suite; (except var (as exc): suite)...; finally:\n/// finally).\n/// @li: try: a\n///           except e: pass\n///           except e as Exc: pass\n///           except: pass\n///           finally: print\nstruct TryStmt : public AcceptorExtend<TryStmt, Stmt>, Items<ExceptStmt *> {\n  TryStmt(Stmt *suite = nullptr, std::vector<ExceptStmt *> catches = {},\n          Stmt *elseSuite = nullptr, Stmt *finally = nullptr);\n  TryStmt(const TryStmt &, bool);\n\n  SuiteStmt *getSuite() const { return suite; }\n  SuiteStmt *getElse() const { return elseSuite; }\n  SuiteStmt *getFinally() const { return finally; }\n\n  ACCEPT(TryStmt, ASTVisitor, items, suite, elseSuite, finally);\n\nprivate:\n  SuiteStmt *suite;\n  /// nullptr if there is no else block.\n  SuiteStmt *elseSuite;\n  /// nullptr if there is no finally block.\n  SuiteStmt *finally;\n};\n\n/// Throw statement (raise expr).\n/// @li: raise a\nstruct ThrowStmt : public AcceptorExtend<ThrowStmt, Stmt> {\n  explicit ThrowStmt(Expr *expr = nullptr, Expr *from = nullptr,\n                     bool transformed = false);\n  ThrowStmt(const ThrowStmt &, bool);\n\n  Expr *getExpr() const { return expr; }\n  Expr *getFrom() const { return from; }\n  bool isTransformed() const { return transformed; }\n\n  ACCEPT(ThrowStmt, ASTVisitor, expr, from, transformed);\n\nprivate:\n  Expr *expr;\n  Expr *from;\n  // True if a statement was transformed during type-checking stage\n  // (to avoid setting up ExcHeader multiple times).\n  bool transformed;\n};\n\n/// Global variable statement (global var).\n/// @li: global a\nstruct GlobalStmt : public AcceptorExtend<GlobalStmt, Stmt> {\n  explicit GlobalStmt(std::string var = \"\", bool nonLocal = false);\n  GlobalStmt(const GlobalStmt &, bool);\n\n  std::string getVar() const { return var; }\n  bool isNonLocal() const { return nonLocal; }\n\n  ACCEPT(GlobalStmt, ASTVisitor, var, nonLocal);\n\nprivate:\n  std::string var;\n  bool nonLocal;\n};\n\n/// Function statement (@(attributes...) def name[funcs...](args...) -> ret: suite).\n/// @li: @decorator\n///           def foo[T=int, U: int](a, b: int = 0) -> list[T]: pass\nstruct FunctionStmt : public AcceptorExtend<FunctionStmt, Stmt>, Items<Param> {\n  FunctionStmt(std::string name = \"\", Expr *ret = nullptr, std::vector<Param> args = {},\n               Stmt *suite = nullptr, std::vector<Expr *> decorators = {},\n               bool async = false);\n  FunctionStmt(const FunctionStmt &, bool);\n\n  std::string getName() const { return name; }\n  void setName(const std::string &n) { name = n; }\n  Expr *getReturn() const { return ret; }\n  SuiteStmt *getSuite() const { return suite; }\n  void setSuite(SuiteStmt *s) { suite = s; }\n  const std::vector<Expr *> &getDecorators() const { return decorators; }\n  void setDecorators(const std::vector<Expr *> &d) { decorators = d; }\n  bool isAsync() const { return async; }\n  void setAsync() { async = true; }\n  void addParam(const Param &p) { items.push_back(p); }\n\n  /// @return a function signature that consists of generics and arguments in a\n  /// S-expression form.\n  /// @li (T U (int 0))\n  std::string getSignature();\n  size_t getStarArgs() const;\n  size_t getKwStarArgs() const;\n  std::string getDocstr() const;\n  std::unordered_set<std::string> getNonInferrableGenerics() const;\n  bool hasFunctionAttribute(const std::string &attr) const;\n\n  ACCEPT(FunctionStmt, ASTVisitor, name, items, ret, suite, decorators, async);\n\nprivate:\n  std::string name;\n  Expr *ret; /// nullptr if return type is not specified.\n  SuiteStmt *suite;\n  std::vector<Expr *> decorators;\n  bool async;\n  std::string signature;\n\n  friend struct Cache;\n};\n\n/// Class statement (@(attributes...) class name[generics...]: args... ; suite).\n/// @li: @type\n///           class F[T]:\n///              m: T\n///              def __new__() -> F[T]: ...\nstruct ClassStmt : public AcceptorExtend<ClassStmt, Stmt>, Items<Param> {\n  ClassStmt(std::string name = \"\", std::vector<Param> args = {}, Stmt *suite = nullptr,\n            std::vector<Expr *> decorators = {},\n            const std::vector<Expr *> &baseClasses = {},\n            std::vector<Expr *> staticBaseClasses = {});\n  ClassStmt(const ClassStmt &, bool);\n\n  std::string getName() const { return name; }\n  SuiteStmt *getSuite() const { return suite; }\n  const std::vector<Expr *> &getDecorators() const { return decorators; }\n  void setDecorators(const std::vector<Expr *> &d) { decorators = d; }\n  const std::vector<Expr *> &getBaseClasses() const { return baseClasses; }\n  const std::vector<Expr *> &getStaticBaseClasses() const { return staticBaseClasses; }\n\n  /// @return true if a class is a tuple-like record (e.g. has a \"@tuple\" attribute)\n  bool isRecord() const;\n  std::string getDocstr() const;\n\n  static bool isClassVar(const Param &p);\n\n  ACCEPT(ClassStmt, ASTVisitor, name, suite, items, decorators, baseClasses,\n         staticBaseClasses);\n\nprivate:\n  std::string name;\n  SuiteStmt *suite;\n  std::vector<Expr *> decorators;\n  std::vector<Expr *> baseClasses;\n  std::vector<Expr *> staticBaseClasses;\n};\n\n/// Yield-from statement (yield from expr).\n/// @li: yield from it\nstruct YieldFromStmt : public AcceptorExtend<YieldFromStmt, Stmt> {\n  explicit YieldFromStmt(Expr *expr = nullptr);\n  YieldFromStmt(const YieldFromStmt &, bool);\n\n  Expr *getExpr() const { return expr; }\n\n  ACCEPT(YieldFromStmt, ASTVisitor, expr);\n\nprivate:\n  Expr *expr;\n};\n\n/// With statement (with (item as var)...: suite).\n/// @li: with foo(), bar() as b: pass\nstruct WithStmt : public AcceptorExtend<WithStmt, Stmt>, Items<Expr *> {\n  WithStmt(std::vector<Expr *> items = {}, std::vector<std::string> vars = {},\n           Stmt *suite = nullptr, bool isAsync = false);\n  WithStmt(std::vector<std::pair<Expr *, Expr *>> items, Stmt *suite, bool isAsync);\n  WithStmt(const WithStmt &, bool);\n\n  const std::vector<std::string> &getVars() const { return vars; }\n  SuiteStmt *getSuite() const { return suite; }\n\n  bool isAsync() const { return async; }\n  void setAsync() { async = true; }\n\n  ACCEPT(WithStmt, ASTVisitor, items, vars, suite);\n\nprivate:\n  /// empty string if a corresponding item is unnamed\n  std::vector<std::string> vars;\n  SuiteStmt *suite;\n  bool async;\n};\n\n/// Custom block statement (foo: ...).\n/// @li: pt_tree: pass\nstruct CustomStmt : public AcceptorExtend<CustomStmt, Stmt> {\n  CustomStmt(std::string keyword = \"\", Expr *expr = nullptr, Stmt *suite = nullptr);\n  CustomStmt(const CustomStmt &, bool);\n\n  std::string getKeyword() const { return keyword; }\n  Expr *getExpr() const { return expr; }\n  SuiteStmt *getSuite() const { return suite; }\n\n  ACCEPT(CustomStmt, ASTVisitor, keyword, expr, suite);\n\nprivate:\n  std::string keyword;\n  Expr *expr;\n  SuiteStmt *suite;\n};\n\nstruct DirectiveStmt : public AcceptorExtend<DirectiveStmt, Stmt> {\n  DirectiveStmt(std::string key = \"\", std::string value = \"\");\n  DirectiveStmt(const DirectiveStmt &, bool);\n\n  std::string getKey() const { return key; }\n  std::string getValue() const { return value; }\n\n  ACCEPT(DirectiveStmt, ASTVisitor, key, value);\n\nprivate:\n  std::string key, value;\n};\n\n/// The following nodes are created during typechecking.\n\n/// Member assignment statement (lhs.member = rhs).\n/// @li: a.x = b\nstruct AssignMemberStmt : public AcceptorExtend<AssignMemberStmt, Stmt> {\n  AssignMemberStmt(Expr *lhs = nullptr, std::string member = \"\", Expr *rhs = nullptr,\n                   Expr *type = nullptr);\n  AssignMemberStmt(const AssignMemberStmt &, bool);\n\n  Expr *getLhs() const { return lhs; }\n  std::string getMember() const { return member; }\n  Expr *getRhs() const { return rhs; }\n  Expr *getTypeExpr() const { return type; }\n\n  ACCEPT(AssignMemberStmt, ASTVisitor, lhs, member, rhs, type);\n\nprivate:\n  Expr *lhs;\n  std::string member;\n  Expr *rhs;\n  Expr *type;\n};\n\n/// Comment statement (# comment).\n/// Currently used only for pretty-printing.\nstruct CommentStmt : public AcceptorExtend<CommentStmt, Stmt> {\n  explicit CommentStmt(std::string comment = \"\");\n  CommentStmt(const CommentStmt &, bool);\n\n  std::string getComment() const { return comment; }\n\n  ACCEPT(CommentStmt, ASTVisitor, comment);\n\nprivate:\n  std::string comment;\n};\n\n#undef ACCEPT\n\n} // namespace codon::ast\n\nnamespace tser {\nvoid operator<<(codon::ast::Stmt *t, BinaryArchive &a);\nvoid operator>>(codon::ast::Stmt *&t, BinaryArchive &a);\n} // namespace tser\n"
  },
  {
    "path": "codon/parser/ast/types/class.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"codon/parser/ast/types/class.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nnamespace codon::ast::types {\n\nstd::string ClassType::Generic::debugString(char mode) const {\n  if (!staticKind && type->getStatic() && mode != 2)\n    return type->getStatic()->getNonStaticType()->debugString(mode);\n  return type->debugString(mode);\n}\n\nstd::string ClassType::Generic::realizedName() const {\n  if (!staticKind && type->getStatic())\n    return type->getStatic()->getNonStaticType()->realizedName();\n  return type->realizedName();\n}\n\nClassType::Generic ClassType::Generic::generalize(int atLevel) const {\n  TypePtr t = nullptr;\n  if (!staticKind && type && type->getStatic())\n    t = type->getStatic()->getNonStaticType()->generalize(atLevel);\n  else if (type)\n    t = type->generalize(atLevel);\n  return {name, t, id, staticKind};\n}\n\nClassType::Generic\nClassType::Generic::instantiate(int atLevel, int *unboundCount,\n                                std::unordered_map<int, TypePtr> *cache) const {\n  TypePtr t = nullptr;\n  if (!staticKind && type && type->getStatic())\n    t = type->getStatic()->getNonStaticType()->instantiate(atLevel, unboundCount,\n                                                           cache);\n  else if (type)\n    t = type->instantiate(atLevel, unboundCount, cache);\n  return {name, t, id, staticKind};\n}\n\nClassType::ClassType(Cache *cache, std::string name, std::vector<Generic> generics,\n                     std::vector<Generic> hiddenGenerics)\n    : Type(cache), name(std::move(name)), generics(std::move(generics)),\n      hiddenGenerics(std::move(hiddenGenerics)) {}\nClassType::ClassType(const ClassType *base)\n    : Type(*base), name(base->name), generics(base->generics),\n      hiddenGenerics(base->hiddenGenerics), isTuple(base->isTuple) {}\n\nint ClassType::unify(Type *typ, Unification *us) {\n  if (auto tc = typ->getClass()) {\n    if (name == \"int\" && tc->name == \"Int\")\n      return tc->unify(this, us);\n    if (tc->name == \"int\" && name == \"Int\") {\n      auto t64 = std::make_shared<IntStaticType>(cache, 64);\n      return generics[0].type->unify(t64.get(), us);\n    }\n    if (name == \"unrealized_type\" && tc->name == name) {\n      // instantiate + unify!\n      std::unordered_map<int, types::TypePtr> genericCache;\n      auto l = generics[0].type->instantiate(0, &(cache->unboundCount), &genericCache);\n      genericCache.clear();\n      auto r =\n          tc->generics[0].type->instantiate(0, &(cache->unboundCount), &genericCache);\n      return l->unify(r.get(), us);\n    }\n\n    int s1 = 3, s = 0;\n    if (name == \"__NTuple__\" && tc->name == name) {\n      auto n1 = generics[0].getType()->getIntStatic();\n      auto n2 = tc->generics[0].getType()->getIntStatic();\n      if (n1 && n2) {\n        auto t1 = generics[1].getType()->getClass();\n        auto t2 = tc->generics[1].getType()->getClass();\n        seqassert(t1 && t2, \"bad ntuples\");\n        if (n1->value * t1->generics.size() != n2->value * t2->generics.size())\n          return -1;\n        for (size_t i = 0; i < t1->generics.size() * n1->value; i++) {\n          if ((s = t1->generics[i % t1->generics.size()].getType()->unify(\n                   t2->generics[i % t2->generics.size()].getType(), us)) == -1)\n            return -1;\n          s1 += s;\n        }\n        return s1;\n      }\n    } else if (tc->name == \"__NTuple__\") {\n      return tc->unify(this, us);\n    } else if (name == \"__NTuple__\" && tc->name == TYPE_TUPLE) {\n      auto n1 = generics[0].getType()->getIntStatic();\n      if (!n1) {\n        auto n = tc->generics.size();\n        auto tn = std::make_shared<IntStaticType>(cache, n);\n        // If we are unifying NT[N, T] and T[X, X, ...], we assume that N is number of\n        // X's\n        if (generics[0].type->unify(tn.get(), us) == -1)\n          return -1;\n\n        auto tv = TypecheckVisitor(cache->typeCtx);\n        TypePtr tt;\n        if (n) {\n          tt = tv.instantiateType(tv.generateTuple(1), {tc->generics[0].getType()});\n          for (size_t i = 1; i < tc->generics.size(); i++) {\n            if ((s = tt->getClass()->generics[0].getType()->unify(\n                     tc->generics[i].getType(), us)) == -1)\n              return -1;\n            s1 += s;\n          }\n        } else {\n          tt = tv.instantiateType(tv.generateTuple(1));\n          // tt = tv.instantiateType(tv.generateTuple(0));\n        }\n        if (generics[1].type->unify(tt.get(), us) == -1)\n          return -1;\n      } else {\n        auto t1 = generics[1].getType()->getClass();\n        seqassert(t1, \"bad ntuples\");\n        if (n1->value * t1->generics.size() != tc->generics.size())\n          return -1;\n        for (size_t i = 0; i < t1->generics.size() * n1->value; i++) {\n          if ((s = t1->generics[i % t1->generics.size()].getType()->unify(\n                   tc->generics[i].getType(), us)) == -1)\n            return -1;\n          s1 += s;\n        }\n      }\n      return s1;\n    }\n\n    // Check names.\n    if (name != tc->name) {\n      return -1;\n    }\n    // Check generics.\n    if (generics.size() != tc->generics.size())\n      return -1;\n    for (int i = 0; i < generics.size(); i++) {\n      if ((s = generics[i].type->unify(tc->generics[i].type.get(), us)) == -1) {\n        return -1;\n      }\n      s1 += s;\n    }\n    for (int i = 0; i < hiddenGenerics.size(); i++) {\n      if ((s = hiddenGenerics[i].type->unify(tc->hiddenGenerics[i].type.get(), us)) ==\n          -1) {\n        return -1;\n      }\n      s1 += s;\n    }\n    return s1;\n  } else if (auto tl = typ->getLink()) {\n    return tl->unify(this, us);\n  } else {\n    return -1;\n  }\n}\n\nTypePtr ClassType::generalize(int atLevel) const {\n  std::vector<Generic> g, hg;\n  for (auto &t : generics)\n    g.push_back(t.generalize(atLevel));\n  for (auto &t : hiddenGenerics)\n    hg.push_back(t.generalize(atLevel));\n  auto c = std::make_shared<ClassType>(cache, name, g, hg);\n  c->isTuple = isTuple;\n  c->setSrcInfo(getSrcInfo());\n  return c;\n}\n\nTypePtr ClassType::instantiate(int atLevel, int *unboundCount,\n                               std::unordered_map<int, TypePtr> *cache) const {\n  std::vector<Generic> g, hg;\n  for (auto &t : generics)\n    g.push_back(t.instantiate(atLevel, unboundCount, cache));\n  for (auto &t : hiddenGenerics)\n    hg.push_back(t.instantiate(atLevel, unboundCount, cache));\n  auto c = std::make_shared<ClassType>(this->cache, name, g, hg);\n  c->isTuple = isTuple;\n  c->setSrcInfo(getSrcInfo());\n  return c;\n}\n\nbool ClassType::hasUnbounds(bool includeGenerics) const {\n  if (name == \"unrealized_type\")\n    return false;\n  auto pred = [includeGenerics](const auto &t) {\n    return t.type && t.type->hasUnbounds(includeGenerics);\n  };\n  return std::ranges::any_of(generics.begin(), generics.end(), pred) ||\n         std::ranges::any_of(hiddenGenerics.begin(), hiddenGenerics.end(), pred);\n}\n\nstd::vector<Type *> ClassType::getUnbounds(bool includeGenerics) const {\n  std::vector<Type *> u;\n  if (name == \"unrealized_type\")\n    return u;\n  for (auto &t : generics)\n    if (t.type) {\n      auto tu = t.type->getUnbounds(includeGenerics);\n      u.insert(u.begin(), tu.begin(), tu.end());\n    }\n  for (auto &t : hiddenGenerics)\n    if (t.type) {\n      auto tu = t.type->getUnbounds(includeGenerics);\n      u.insert(u.begin(), tu.begin(), tu.end());\n    }\n  return u;\n}\n\nbool ClassType::canRealize() const {\n  if (name == \"type\") {\n    if (!hasUnbounds(false))\n      return true; // always true!\n  }\n  if (name == \"unrealized_type\")\n    return generics[0].type->getClass() != nullptr;\n  auto pred = [](auto &t) { return !t.type || t.type->canRealize(); };\n  return std::ranges::all_of(generics.begin(), generics.end(), pred) &&\n         std::ranges::all_of(hiddenGenerics.begin(), hiddenGenerics.end(), pred);\n}\n\nbool ClassType::isInstantiated() const {\n  if (name == \"unrealized_type\")\n    return generics[0].type->getClass() != nullptr;\n  auto pred = [](auto &t) { return !t.type || t.type->isInstantiated(); };\n  return std::ranges::all_of(generics.begin(), generics.end(), pred) &&\n         std::ranges::all_of(hiddenGenerics.begin(), hiddenGenerics.end(), pred);\n}\n\nstd::string ClassType::debugString(char mode) const {\n  if (name == \"NamedTuple\") {\n    if (auto ids = generics[0].type->getIntStatic()) {\n      auto id = ids->value;\n      seqassert(id >= 0 && id < cache->generatedTupleNames.size(), \"bad id: {}\", id);\n      const auto &names = cache->generatedTupleNames[id];\n      auto ts = generics[1].getType()->getClass();\n      if (names.empty())\n        return name;\n      std::vector<std::string> as;\n      for (size_t i = 0; i < names.size(); i++)\n        as.push_back(names[i] + \"=\" + ts->generics[i].debugString(mode));\n      return name + \"[\" + join(as, \",\") + \"]\";\n    } else {\n      return name + \"[\" + generics[0].type->debugString(mode) + \"]\";\n    }\n  } else if (name == \"Partial\" && generics[3].type->getClass() && mode != 2) {\n    // Name: function[full_args](instantiated_args...)\n    std::vector<std::string> as;\n    auto known = getPartialMask();\n    auto func = getPartialFunc();\n\n    std::vector<std::string> args;\n    if (auto ta = generics[1].type->getClass())\n      for (const auto &i : ta->generics)\n        args.push_back(i.debugString(mode));\n    size_t ai = 0, gi = 0;\n    for (size_t i = 0; i < known.size(); i++) {\n      if ((*func->ast)[i].isValue()) {\n        as.emplace_back(ai < args.size() ? (known[i] == ClassType::PartialFlag::Included\n                                                ? args[ai]\n                                                : (\"...\" + (mode == 0 ? \"\" : args[ai])))\n                                         : \"...\");\n        if (known[i] == ClassType::PartialFlag::Included)\n          ai++;\n      } else {\n        auto s = func->funcGenerics[gi].debugString(mode);\n        as.emplace_back((known[i] == ClassType::PartialFlag::Included\n                             ? s\n                             : (\"...\" + (mode == 0 ? \"\" : s))));\n        gi++;\n      }\n    }\n    if (!args.empty()) {\n      if (args.back() != \"Tuple\") // unused *args (by default always 0 in mask)\n        as.push_back(args.back());\n    }\n    auto ks = generics[2].type->debugString(mode);\n    if (ks.size() > 10) {                 // if **kwargs is used\n      ks = ks.substr(11, ks.size() - 12); // chop off NamedTuple[...]\n      as.push_back(ks);\n    }\n\n    auto fnname = func->ast->getName();\n    if (mode == 0) {\n      fnname = cache->rev(func->ast->getName());\n    } else {\n      fnname = func->ast->getName();\n    }\n    return fnname + \"(\" + join(as, \",\") + \")\";\n  }\n  std::vector<std::string> gs;\n  for (auto &a : generics)\n    if (!a.name.empty())\n      gs.push_back(a.debugString(mode));\n  if ((mode == 2) && !hiddenGenerics.empty()) {\n    for (auto &a : hiddenGenerics)\n      if (!a.name.empty())\n        gs.push_back(\"-\" + a.debugString(mode));\n  }\n  // Special formatting for Functions and Tuples\n  auto n = mode == 0 ? cache->rev(name) : name;\n  return n + (gs.empty() ? \"\" : (\"[\" + join(gs, \",\") + \"]\"));\n}\n\nstd::string ClassType::realizedName() const {\n  if (!_rn.empty())\n    return _rn;\n\n  std::string s;\n  if (name == \"Partial\") {\n    s = debugString(1);\n  } else {\n    std::vector<std::string> gs;\n    if (name == \"Union\" && generics[0].type->getClass()) {\n      std::set<std::string> gss;\n      for (auto &a : generics[0].type->getClass()->generics)\n        gss.insert(a.realizedName());\n      gs = {join(gss, \" | \")};\n    } else {\n      for (auto &a : generics)\n        if (!a.name.empty())\n          gs.push_back(a.realizedName());\n    }\n    s = join(gs, \",\");\n    s = name + (s.empty() ? \"\" : (\"[\" + s + \"]\"));\n  }\n\n  if (canRealize())\n    const_cast<ClassType *>(this)->_rn = s;\n\n  return s;\n}\n\nFuncType *ClassType::getPartialFunc() const {\n  seqassert(name == \"Partial\", \"not a partial\");\n  auto n = generics[3].type->getClass()->generics[0].type;\n  seqassert(n->getFunc(), \"not a partial func\");\n  return n->getFunc();\n}\n\nstd::string ClassType::getPartialMask() const {\n  seqassert(name == \"Partial\", \"not a partial\");\n  auto n = generics[0].type->getStrStatic()->value;\n  return n;\n}\n\nbool ClassType::isPartialEmpty() const {\n  auto a = generics[1].type->getClass();\n  auto ka = generics[2].type->getClass();\n  return a->generics.size() == 1 && a->generics[0].type->getClass()->generics.empty() &&\n         ka->generics[1].type->getClass()->generics.empty();\n}\n\n} // namespace codon::ast::types\n"
  },
  {
    "path": "codon/parser/ast/types/class.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"codon/parser/ast/types/type.h\"\n\nnamespace codon::ast::types {\n\nstruct FuncType;\n\n/**\n * A generic class reference type. All Seq types inherit from this class.\n */\nstruct ClassType : public Type {\n  /**\n   * A generic type declaration.\n   * Each generic is defined by its unique ID.\n   */\n  struct Generic {\n    // Generic name.\n    std::string name;\n    // Unique generic ID.\n    int id;\n    // Pointer to realized type (or generic LinkType).\n    TypePtr type;\n    // Set if this is a static generic\n    LiteralKind staticKind;\n\n    Generic(std::string name, TypePtr type, int id, LiteralKind staticKind)\n        : name(std::move(name)), id(id), type(std::move(type)), staticKind(staticKind) {\n    }\n\n    types::Type *getType() const { return type.get(); }\n    Generic generalize(int atLevel) const;\n    Generic instantiate(int atLevel, int *unboundCount,\n                        std::unordered_map<int, TypePtr> *cache) const;\n    std::string debugString(char mode) const;\n    std::string realizedName() const;\n  };\n\n  /// Canonical type name.\n  std::string name;\n  /// List of generics, if present.\n  std::vector<Generic> generics;\n\n  std::vector<Generic> hiddenGenerics;\n\n  bool isTuple = false;\n  std::string _rn;\n\n  explicit ClassType(Cache *cache, std::string name, std::vector<Generic> generics = {},\n                     std::vector<Generic> hiddenGenerics = {});\n  explicit ClassType(const ClassType *base);\n\npublic:\n  int unify(Type *typ, Unification *undo) override;\n  TypePtr generalize(int atLevel) const override;\n  TypePtr instantiate(int atLevel, int *unboundCount,\n                      std::unordered_map<int, TypePtr> *cache) const override;\n\npublic:\n  bool hasUnbounds(bool) const override;\n  std::vector<Type *> getUnbounds(bool) const override;\n  bool canRealize() const override;\n  bool isInstantiated() const override;\n  std::string debugString(char mode) const override;\n  std::string realizedName() const override;\n  ClassType *getClass() override { return this; }\n  ClassType *getPartial() override { return name == \"Partial\" ? getClass() : nullptr; }\n  bool isRecord() const { return isTuple; }\n\n  size_t size() const { return generics.size(); }\n  Type *operator[](size_t i) const { return generics[i].getType(); }\n\npublic:\n  enum PartialFlag { Missing = '0', Included, Default };\n\n  FuncType *getPartialFunc() const;\n  std::string getPartialMask() const;\n  bool isPartialEmpty() const;\n};\n\n} // namespace codon::ast::types\n\ntemplate <>\nstruct fmt::formatter<codon::ast::types::ClassType::Generic>\n    : fmt::formatter<std::string_view> {\n  template <typename FormatContext>\n  auto format(const codon::ast::types::ClassType::Generic &p, FormatContext &ctx) const\n      -> decltype(ctx.out()) {\n    return fmt::format_to(ctx.out(), \"({}{})\",\n                          p.name.empty() ? \"\" : fmt::format(\"{} = \", p.name), p.type);\n  }\n};\n"
  },
  {
    "path": "codon/parser/ast/types/function.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"codon/parser/ast/stmt.h\"\n#include \"codon/parser/ast/types/function.h\"\n#include \"codon/parser/cache.h\"\n\nnamespace codon::ast::types {\n\nFuncType::FuncType(const ClassType *baseType, FunctionStmt *ast,\n                   std::vector<Generic> funcGenerics, TypePtr funcParent)\n    : ClassType(baseType), ast(ast), funcGenerics(std::move(funcGenerics)),\n      funcParent(std::move(funcParent)) {}\n\nint FuncType::unify(Type *typ, Unification *us) {\n  if (this == typ)\n    return 0;\n  int s1 = 2, s = 0;\n  if (auto t = typ->getFunc()) {\n    // Check if names and parents match.\n    if (ast->getName() != t->ast->getName() ||\n        (static_cast<bool>(funcParent) ^ static_cast<bool>(t->funcParent)))\n      return -1;\n    if (funcParent && (s = funcParent->unify(t->funcParent.get(), us)) == -1) {\n      return -1;\n    }\n    s1 += s;\n    // Check if function generics match.\n    seqassert(funcGenerics.size() == t->funcGenerics.size(),\n              \"generic size mismatch for {}\", ast->getName());\n    for (int i = 0; i < funcGenerics.size(); i++) {\n      if ((s = funcGenerics[i].type->unify(t->funcGenerics[i].type.get(), us)) == -1)\n        return -1;\n      s1 += s;\n    }\n  }\n  s = this->ClassType::unify(typ, us);\n  return s == -1 ? s : s1 + s;\n}\n\nTypePtr FuncType::generalize(int atLevel) const {\n  std::vector<Generic> fg;\n  for (auto &t : funcGenerics)\n    fg.push_back(t.generalize(atLevel));\n  auto p = funcParent ? funcParent->generalize(atLevel) : nullptr;\n\n  auto r = std::static_pointer_cast<ClassType>(this->ClassType::generalize(atLevel));\n  auto t = std::make_shared<FuncType>(r->getClass(), ast, fg, p);\n  return t;\n}\n\nTypePtr FuncType::instantiate(int atLevel, int *unboundCount,\n                              std::unordered_map<int, TypePtr> *cache) const {\n  std::vector<Generic> fg;\n  for (auto &t : funcGenerics) {\n    fg.push_back(t.instantiate(atLevel, unboundCount, cache));\n    if (cache && fg.back().type) {\n      if (auto c = in(*cache, t.id))\n        *c = fg.back().type;\n    }\n  }\n  auto p = funcParent ? funcParent->instantiate(atLevel, unboundCount, cache) : nullptr;\n  auto r = std::static_pointer_cast<ClassType>(\n      this->ClassType::instantiate(atLevel, unboundCount, cache));\n  auto t = std::make_shared<FuncType>(r->getClass(), ast, fg, p);\n  return t;\n}\n\nbool FuncType::hasUnbounds(bool includeGenerics) const {\n  for (auto &t : funcGenerics)\n    if (t.type && t.type->hasUnbounds(includeGenerics))\n      return true;\n  if (funcParent && funcParent->hasUnbounds(includeGenerics))\n    return true;\n  for (const auto &a : *this)\n    if (a.getType()->hasUnbounds(includeGenerics))\n      return true;\n  return getRetType()->hasUnbounds(includeGenerics);\n}\n\nstd::vector<Type *> FuncType::getUnbounds(bool includeGenerics) const {\n  std::vector<Type *> u;\n  for (auto &t : funcGenerics)\n    if (t.type) {\n      auto tu = t.type->getUnbounds(includeGenerics);\n      u.insert(u.begin(), tu.begin(), tu.end());\n    }\n  if (funcParent) {\n    auto tu = funcParent->getUnbounds(includeGenerics);\n    u.insert(u.begin(), tu.begin(), tu.end());\n  }\n  // Important: return type unbounds are not important, so skip them.\n  for (const auto &a : *this) {\n    auto tu = a.getType()->getUnbounds(includeGenerics);\n    u.insert(u.begin(), tu.begin(), tu.end());\n  }\n  return u;\n}\n\nbool FuncType::canRealize() const {\n  bool allowPassThrough = ast->hasAttribute(Attr::AllowPassThrough);\n  // Important: return type does not have to be realized.\n  for (int ai = 0; ai < size(); ai++)\n    if (!(*this)[ai]->getFunc() && !(*this)[ai]->canRealize()) {\n      if (!allowPassThrough)\n        return false;\n      for (auto &u : (*this)[ai]->getUnbounds(true))\n        if (u->getLink()->kind == LinkType::Generic || !u->getLink()->passThrough)\n          return false;\n    }\n  bool generics =\n      std::ranges::all_of(funcGenerics.begin(), funcGenerics.end(),\n                          [](auto &a) { return !a.type || a.type->canRealize(); });\n  if (generics && funcParent && !funcParent->canRealize()) {\n    if (!allowPassThrough)\n      return false;\n    for (auto &u : funcParent->getUnbounds(true)) {\n      if (u->getLink()->kind == LinkType::Generic || !u->getLink()->passThrough)\n        return false;\n    }\n  }\n  return generics;\n}\n\nbool FuncType::isInstantiated() const {\n  TypePtr removed = nullptr;\n  auto retType = getRetType();\n  if (retType->getFunc() && retType->getFunc()->funcParent.get() == this) {\n    removed = retType->getFunc()->funcParent;\n    retType->getFunc()->funcParent = nullptr;\n  }\n  auto res = std::ranges::all_of(\n                 funcGenerics.begin(), funcGenerics.end(),\n                 [](auto &a) { return !a.type || a.type->isInstantiated(); }) &&\n             (!funcParent || funcParent->isInstantiated()) &&\n             this->ClassType::isInstantiated();\n  if (removed)\n    retType->getFunc()->funcParent = removed;\n  return res;\n}\n\nstd::string FuncType::debugString(char mode) const {\n  std::vector<std::string> gs;\n  for (auto &a : funcGenerics)\n    if (!a.name.empty())\n      gs.push_back(mode < 2 ? a.type->debugString(mode)\n                            : (cache->rev(a.name) + \"=\" + a.type->debugString(mode)));\n  std::string s = join(gs, \",\");\n  std::vector<std::string> as;\n  // Important: return type does not have to be realized.\n  if (mode == 2)\n    as.push_back(\"RET=\" + getRetType()->debugString(mode));\n\n  if (mode < 2 || !ast) {\n    for (const auto &a : *this) {\n      as.push_back(a.debugString(mode));\n    }\n  } else {\n    for (size_t i = 0, si = 0; i < ast->size(); i++) {\n      if ((*ast)[i].isGeneric())\n        continue;\n      as.push_back(((*ast)[i].getName() + \"=\" + (*this)[si++]->debugString(mode)));\n    }\n  }\n  std::string a = join(as, \",\");\n  s = s.empty() ? a : join(std::vector<std::string>{s, a}, \";\");\n\n  seqassert(ast, \"ast must not be null\");\n  auto fnname = ast->getName();\n  if (mode == 0) {\n    fnname = cache->rev(ast->getName());\n  }\n  if (mode == 2 && funcParent)\n    s += \";\" + funcParent->debugString(mode);\n  return fnname + (s.empty() ? \"\" : (\"[\" + s + \"]\"));\n}\n\nstd::string FuncType::realizedName() const {\n  std::vector<std::string> gs;\n  for (auto &a : funcGenerics)\n    if (!a.name.empty())\n      gs.push_back(a.realizedName());\n  std::string s = join(gs, \",\");\n  std::vector<std::string> as;\n  // Important: return type does not have to be realized.\n  for (const auto &a : *this)\n    as.push_back(a.getType()->getFunc() ? a.getType()->getFunc()->realizedName()\n                                        : a.realizedName());\n  std::string a = join(as, \",\");\n  s = s.empty() ? a : join(std::vector<std::string>{a, s}, \",\");\n  return (funcParent ? funcParent->realizedName() + \":\" : \"\") + ast->getName() +\n         (s.empty() ? \"\" : (\"[\" + s + \"]\"));\n}\n\nType *FuncType::getRetType() const { return generics[1].type.get(); }\n\nstd::string FuncType::getFuncName() const { return ast->getName(); }\n\nType *FuncType::operator[](size_t i) const {\n  return generics[0].type->getClass()->generics[i].getType();\n}\n\nstd::vector<ClassType::Generic>::iterator FuncType::begin() const {\n  return generics[0].type->getClass()->generics.begin();\n}\n\nstd::vector<ClassType::Generic>::iterator FuncType::end() const {\n  return generics[0].type->getClass()->generics.end();\n}\n\nsize_t FuncType::size() const { return generics[0].type->getClass()->generics.size(); }\n\nbool FuncType::empty() const { return generics[0].type->getClass()->generics.empty(); }\n\n} // namespace codon::ast::types\n"
  },
  {
    "path": "codon/parser/ast/types/function.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"codon/parser/ast/types/class.h\"\n#include \"codon/parser/ast/types/type.h\"\n\nnamespace codon::ast {\nstruct FunctionStmt;\n}\n\nnamespace codon::ast::types {\n\n/**\n * A generic type that represents a Codon function instantiation.\n * It inherits ClassType that realizes Function[...].\n */\nstruct FuncType : public ClassType {\n  /// Canonical AST node.\n  FunctionStmt *ast;\n  /// Function generics (e.g. T in def foo[T](...)).\n  std::vector<ClassType::Generic> funcGenerics;\n  /// Enclosing class or a function.\n  TypePtr funcParent;\n\npublic:\n  FuncType(\n      const ClassType *baseType, FunctionStmt *ast,\n      std::vector<ClassType::Generic> funcGenerics = std::vector<ClassType::Generic>(),\n      TypePtr funcParent = nullptr);\n\npublic:\n  int unify(Type *typ, Unification *undo) override;\n  TypePtr generalize(int atLevel) const override;\n  TypePtr instantiate(int atLevel, int *unboundCount,\n                      std::unordered_map<int, TypePtr> *cache) const override;\n\npublic:\n  bool hasUnbounds(bool) const override;\n  std::vector<Type *> getUnbounds(bool) const override;\n  bool canRealize() const override;\n  bool isInstantiated() const override;\n  std::string debugString(char mode) const override;\n  std::string realizedName() const override;\n\n  FuncType *getFunc() override { return this; }\n\n  Type *getRetType() const;\n  Type *getParentType() const { return funcParent.get(); }\n  std::string getFuncName() const;\n\n  Type *operator[](size_t i) const;\n  std::vector<ClassType::Generic>::iterator begin() const;\n  std::vector<ClassType::Generic>::iterator end() const;\n  size_t size() const;\n  bool empty() const;\n};\n\n} // namespace codon::ast::types\n"
  },
  {
    "path": "codon/parser/ast/types/link.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"codon/parser/ast/types/link.h\"\n#include \"codon/parser/visitors/format/format.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nnamespace codon::ast::types {\n\nLinkType::LinkType(Cache *cache, Kind kind, int id, int level, TypePtr type,\n                   LiteralKind staticKind, std::shared_ptr<Trait> trait,\n                   TypePtr defaultType, std::string genericName, bool passThrough)\n    : Type(cache), kind(kind), id(id), level(level), type(std::move(type)),\n      staticKind(staticKind), trait(std::move(trait)),\n      genericName(std::move(genericName)), defaultType(std::move(defaultType)),\n      passThrough(passThrough) {\n  seqassert((this->type && kind == Link) || (!this->type && kind == Generic) ||\n                (!this->type && kind == Unbound),\n            \"inconsistent link state\");\n}\n\nLinkType::LinkType(TypePtr type)\n    : Type(type), kind(Link), id(0), level(0), type(std::move(type)),\n      staticKind(Runtime), trait(nullptr), defaultType(nullptr), passThrough(false) {\n  seqassert(this->type, \"link to nullptr\");\n}\n\nint LinkType::unify(Type *typ, Unification *undo) {\n  if (kind == Link) {\n    // Case 1: Just follow the link\n    return type->unify(typ, undo);\n  } else {\n    // Case 3: Unbound unification\n    if (getStaticKind() != typ->getStaticKind()) {\n      if (!getStaticKind()) {\n        // other one is; move this to non-static equivalent\n        if (undo) {\n          undo->statics.push_back(shared_from_this());\n          staticKind = typ->getStaticKind();\n        }\n      } else {\n        return -1;\n      }\n    }\n    if (auto t = typ->getLink()) {\n      if (t->kind == Link)\n        return t->type->unify(this, undo);\n      if (kind != t->kind)\n        return -1;\n      // Identical unbound types get a score of 1\n      if (id == t->id)\n        return 1;\n      // Generics must have matching IDs unless we are doing non-destructive unification\n      if (kind == Generic)\n        return undo ? -1 : 1;\n      // Always merge a newer type into the older type (e.g. keep the types with\n      // lower IDs around).\n      if (id < t->id)\n        return t->unify(this, undo);\n    } else if (kind == Generic) {\n      return -1;\n    }\n    // Generics must be handled by now; only unbounds can be unified!\n    seqassertn(kind == Unbound, \"not an unbound\");\n\n    // Ensure that we do not have recursive unification! (e.g. unify ?1 with list[?1])\n    if (occurs(typ, undo))\n      return -1;\n    // Handle traits\n    if (trait && trait->unify(typ, undo) == -1)\n      return -1;\n    // ⚠️ Unification: destructive part.\n    seqassert(!type, \"type has been already unified or is in inconsistent state\");\n    if (undo) {\n      LOG_TYPECHECK(\"[unify] {} := {}\", id, typ->debugString(2));\n      // Link current type to typ and ensure that this modification is recorded in undo.\n      undo->linked.push_back(shared_from_this());\n      this->kind = Link;\n      seqassert(!typ->getLink() || typ->getLink()->kind != Unbound ||\n                    typ->getLink()->id <= id,\n                \"type unification is not consistent\");\n      this->type = typ->follow()->shared_from_this();\n\n      if (auto t = type->getLink(); t && trait && t->kind == Unbound && !t->trait) {\n        undo->traits.push_back(t->shared_from_this());\n        t->trait = trait;\n      }\n    }\n    return 0;\n  }\n}\n\nTypePtr LinkType::generalize(int atLevel) const {\n  // We need to preserve the pointers as something else might be pointing\n  // to this unbound, hence the const_casts because shared_from_ptr\n  // needs it.\n\n  if (kind == Generic) {\n    return const_cast<LinkType *>(this)->shared_from_this();\n  } else if (kind == Unbound) {\n    if (level >= atLevel) {\n      return std::make_shared<LinkType>(\n          cache, Generic, id, 0, nullptr, staticKind,\n          trait ? std::static_pointer_cast<Trait>(trait->generalize(atLevel)) : nullptr,\n          defaultType ? defaultType->generalize(atLevel) : nullptr, genericName,\n          passThrough);\n    } else {\n      return const_cast<LinkType *>(this)->shared_from_this();\n    }\n  } else {\n    seqassert(type, \"link is null\");\n    return type->generalize(atLevel);\n  }\n}\n\nTypePtr LinkType::instantiate(int atLevel, int *unboundCount,\n                              std::unordered_map<int, TypePtr> *cache) const {\n  if (kind == Generic) {\n    if (TypePtr *res = nullptr; cache && ((res = in(*cache, id))))\n      return *res;\n    auto t = std::make_shared<LinkType>(\n        this->cache, Unbound, unboundCount ? (*unboundCount)++ : id, atLevel, nullptr,\n        staticKind,\n        trait ? std::static_pointer_cast<Trait>(\n                    trait->instantiate(atLevel, unboundCount, cache))\n              : nullptr,\n        defaultType ? defaultType->instantiate(atLevel, unboundCount, cache) : nullptr,\n        genericName, passThrough);\n    if (cache)\n      (*cache)[id] = t;\n    return t;\n  } else if (kind == Unbound) {\n    // We need to preserve the pointers as something else might be pointing\n    // to this unbound, hence the const_casts because shared_from_ptr\n    // needs it.\n    return const_cast<LinkType *>(this)->shared_from_this();\n  } else {\n    seqassert(type, \"link is null\");\n    return type->instantiate(atLevel, unboundCount, cache);\n  }\n}\n\nType *LinkType::follow() {\n  if (kind == Link)\n    return type->follow();\n  else\n    return this;\n}\n\nstd::vector<Type *> LinkType::getUnbounds(bool includeGenerics) const {\n  if (kind == Unbound)\n    return {(Type *)this};\n  else if (kind == Link)\n    return type->getUnbounds(includeGenerics);\n  else if (includeGenerics)\n    return {(Type *)this};\n  return {};\n}\n\nbool LinkType::hasUnbounds(bool includeGenerics) const {\n  if (kind == Unbound)\n    return true;\n  if (includeGenerics && kind == Generic)\n    return true;\n  if (kind == Link)\n    return type->hasUnbounds(includeGenerics);\n  return false;\n}\n\nbool LinkType::canRealize() const {\n  if (kind != Link)\n    return false;\n  else\n    return type->canRealize();\n}\n\nbool LinkType::isInstantiated() const { return kind == Link && type->isInstantiated(); }\n\nstd::string LinkType::debugString(char mode) const {\n  if (kind == Unbound || kind == Generic) {\n    if (mode == 2) {\n      return (genericName.empty() ? \"\" : genericName + \":\") +\n             (kind == Unbound ? \"?\" : \"#\") + fmt::format(\"{}\", id) +\n             (trait ? \":\" + trait->debugString(mode) : \"\") +\n             (staticKind ? fmt::format(\":S{}\", static_cast<int>(staticKind)) : \"\");\n    } else if (trait) {\n      return trait->debugString(mode);\n    }\n    return (genericName.empty() ? (mode ? \"?\" : \"<unknown type>\") : genericName);\n  }\n  return type->debugString(mode);\n}\n\nstd::string LinkType::realizedName() const {\n  if (kind == Unbound)\n    return (\"#\" + genericName);\n  if (kind == Generic)\n    return (\"#\" + genericName);\n  seqassert(kind == Link, \"unexpected generic link\");\n  return type->realizedName();\n}\n\nLinkType *LinkType::getLink() { return this; }\n\nFuncType *LinkType::getFunc() { return kind == Link ? type->getFunc() : nullptr; }\n\nClassType *LinkType::getPartial() {\n  return kind == Link ? type->getPartial() : nullptr;\n}\n\nClassType *LinkType::getClass() { return kind == Link ? type->getClass() : nullptr; }\n\nStaticType *LinkType::getStatic() { return kind == Link ? type->getStatic() : nullptr; }\n\nIntStaticType *LinkType::getIntStatic() {\n  return kind == Link ? type->getIntStatic() : nullptr;\n}\n\nStrStaticType *LinkType::getStrStatic() {\n  return kind == Link ? type->getStrStatic() : nullptr;\n}\n\nBoolStaticType *LinkType::getBoolStatic() {\n  return kind == Link ? type->getBoolStatic() : nullptr;\n}\n\nUnionType *LinkType::getUnion() { return kind == Link ? type->getUnion() : nullptr; }\n\nLinkType *LinkType::getUnbound() {\n  if (kind == Unbound)\n    return this;\n  if (kind == Link)\n    return type->getUnbound();\n  return nullptr;\n}\n\nbool LinkType::occurs(Type *typ, Type::Unification *undo) {\n  if (auto tl = typ->getLink()) {\n    if (tl->kind == Unbound) {\n      if (tl->id == id)\n        return true;\n      if (tl->trait && occurs(tl->trait.get(), undo))\n        return true;\n      if (undo && tl->level > level) {\n        undo->leveled.emplace_back(tl->shared_from_this(), tl->level);\n        tl->level = level;\n      }\n      return false;\n    } else if (tl->kind == Link) {\n      return occurs(tl->type.get(), undo);\n    } else {\n      return false;\n    }\n  } else if (typ->getStatic()) {\n    return false;\n  }\n  if (const auto tc = typ->getClass()) {\n    return std::ranges::any_of(\n        tc->generics.begin(), tc->generics.end(),\n        [&](const auto &g) { return g.type && occurs(g.type.get(), undo); });\n  } else {\n    return false;\n  }\n}\n\n} // namespace codon::ast::types\n"
  },
  {
    "path": "codon/parser/ast/types/link.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"codon/parser/ast/types/traits.h\"\n#include \"codon/parser/ast/types/type.h\"\n\nnamespace codon::ast::types {\n\nstruct LinkType : public Type {\n  /// Enumeration describing the current state.\n  enum Kind { Unbound, Generic, Link } kind;\n  /// The unique identifier of an unbound or generic type.\n  int id;\n  /// The type-checking level of an unbound type.\n  int level;\n  /// The type to which LinkType points to. nullptr if unknown (unbound or generic).\n  TypePtr type;\n  /// >0 if a type is a static type (e.g. N in Int[N: int]); 0 otherwise.\n  LiteralKind staticKind;\n  /// Optional trait that unbound type requires prior to unification.\n  std::shared_ptr<Trait> trait;\n  /// The generic name of a generic type, if applicable. Used for pretty-printing.\n  std::string genericName;\n  /// Type that will be used if an unbound is not resolved.\n  TypePtr defaultType;\n  /// Set if this type can be used unrealized as function argument during function\n  /// realization.\n  bool passThrough;\n\npublic:\n  LinkType(Cache *cache, Kind kind, int id, int level = 0, TypePtr type = nullptr,\n           LiteralKind staticKind = LiteralKind::Runtime,\n           std::shared_ptr<Trait> trait = nullptr, TypePtr defaultType = nullptr,\n           std::string genericName = \"\", bool passThrough = false);\n  /// Convenience constructor for linked types.\n  explicit LinkType(TypePtr type);\n\npublic:\n  int unify(Type *typ, Unification *undo) override;\n  TypePtr generalize(int atLevel) const override;\n  TypePtr instantiate(int atLevel, int *unboundCount,\n                      std::unordered_map<int, TypePtr> *cache) const override;\n\npublic:\n  Type *follow() override;\n  bool hasUnbounds(bool) const override;\n  std::vector<Type *> getUnbounds(bool) const override;\n  bool canRealize() const override;\n  bool isInstantiated() const override;\n  std::string debugString(char mode) const override;\n  std::string realizedName() const override;\n\n  LinkType *getLink() override;\n  FuncType *getFunc() override;\n  ClassType *getPartial() override;\n  ClassType *getClass() override;\n  StaticType *getStatic() override;\n  IntStaticType *getIntStatic() override;\n  StrStaticType *getStrStatic() override;\n  BoolStaticType *getBoolStatic() override;\n  UnionType *getUnion() override;\n  LinkType *getUnbound() override;\n\nprivate:\n  /// Checks if a current (unbound) type occurs within a given type.\n  /// Needed to prevent a recursive unification (e.g. ?1 with list[?1]).\n  bool occurs(Type *typ, Type::Unification *undo);\n};\n\n} // namespace codon::ast::types\n"
  },
  {
    "path": "codon/parser/ast/types/static.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <memory>\n#include <string>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/ast/types/static.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nnamespace codon::ast::types {\n\nStaticType::StaticType(Cache *cache, const std::string &typeName)\n    : ClassType(cache, typeName) {}\n\nbool StaticType::canRealize() const { return true; }\n\nbool StaticType::isInstantiated() const { return true; }\n\nstd::string StaticType::realizedName() const { return debugString(0); }\n\nType *StaticType::getNonStaticType() const { return cache->findClass(name); }\n\n/*****************************************************************/\n\nIntStaticType::IntStaticType(Cache *cache, int64_t i)\n    : StaticType(cache, \"int\"), value(i) {}\n\nint IntStaticType::unify(Type *typ, Unification *us) {\n  if (auto t = typ->getIntStatic()) {\n    return value == t->value ? 1 : -1;\n  } else if (auto c = typ->getClass()) {\n    return ClassType::unify(c, us);\n  } else if (auto tl = typ->getLink()) {\n    return tl->unify(this, us);\n  } else {\n    return -1;\n  }\n}\n\nTypePtr IntStaticType::generalize(int atLevel) const {\n  auto c = std::make_shared<IntStaticType>(cache, value);\n  c->setSrcInfo(getSrcInfo());\n  return c;\n}\n\nTypePtr IntStaticType::instantiate(int atLevel, int *unboundCount,\n                                   std::unordered_map<int, TypePtr> *cache) const {\n  auto c = std::make_shared<IntStaticType>(this->cache, value);\n  c->setSrcInfo(getSrcInfo());\n  return c;\n}\n\nstd::string IntStaticType::debugString(char mode) const {\n  return mode < 2 ? fmt::format(\"{}\", value) : fmt::format(\"Literal[{}]\", value);\n}\n\nExpr *IntStaticType::getStaticExpr() const { return cache->N<IntExpr>(value); }\n\n/*****************************************************************/\n\nStrStaticType::StrStaticType(Cache *cache, std::string s)\n    : StaticType(cache, \"str\"), value(std::move(s)) {}\n\nint StrStaticType::unify(Type *typ, Unification *us) {\n  if (auto t = typ->getStrStatic()) {\n    return value == t->value ? 1 : -1;\n  } else if (auto c = typ->getClass()) {\n    return ClassType::unify(c, us);\n  } else if (auto tl = typ->getLink()) {\n    return tl->unify(this, us);\n  } else {\n    return -1;\n  }\n}\n\nTypePtr StrStaticType::generalize(int atLevel) const {\n  auto c = std::make_shared<StrStaticType>(cache, value);\n  c->setSrcInfo(getSrcInfo());\n  return c;\n}\n\nTypePtr StrStaticType::instantiate(int atLevel, int *unboundCount,\n                                   std::unordered_map<int, TypePtr> *cache) const {\n  auto c = std::make_shared<StrStaticType>(this->cache, value);\n  c->setSrcInfo(getSrcInfo());\n  return c;\n}\n\nstd::string StrStaticType::debugString(char mode) const {\n  return mode < 2 ? fmt::format(\"'{}'\", escape(value))\n                  : fmt::format(\"Literal['{}']\", escape(value));\n}\n\nExpr *StrStaticType::getStaticExpr() const { return cache->N<StringExpr>(value); }\n\n/*****************************************************************/\n\nBoolStaticType::BoolStaticType(Cache *cache, bool b)\n    : StaticType(cache, \"bool\"), value(b) {}\n\nint BoolStaticType::unify(Type *typ, Unification *us) {\n  if (auto t = typ->getBoolStatic()) {\n    return value == t->value ? 1 : -1;\n  } else if (auto c = typ->getClass()) {\n    return ClassType::unify(c, us);\n  } else if (auto tl = typ->getLink()) {\n    return tl->unify(this, us);\n  } else {\n    return -1;\n  }\n}\n\nTypePtr BoolStaticType::generalize(int atLevel) const {\n  auto c = std::make_shared<BoolStaticType>(cache, value);\n  c->setSrcInfo(getSrcInfo());\n  return c;\n}\n\nTypePtr BoolStaticType::instantiate(int atLevel, int *unboundCount,\n                                    std::unordered_map<int, TypePtr> *cache) const {\n  auto c = std::make_shared<BoolStaticType>(this->cache, value);\n  c->setSrcInfo(getSrcInfo());\n  return c;\n}\n\nstd::string BoolStaticType::debugString(char mode) const {\n  return mode < 2 ? (value ? \"1\" : \"0\")\n                  : fmt::format(\"Literal[{}]\", value ? \"True\" : \"False\");\n}\n\nExpr *BoolStaticType::getStaticExpr() const { return cache->N<BoolExpr>(value); }\n\n} // namespace codon::ast::types\n"
  },
  {
    "path": "codon/parser/ast/types/static.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n\n#include \"codon/parser/ast/types/class.h\"\n\nnamespace codon::ast::types {\n\nstruct StaticType : public ClassType {\n  explicit StaticType(Cache *, const std::string &);\n\npublic:\n  bool canRealize() const override;\n  bool isInstantiated() const override;\n  std::string realizedName() const override;\n  virtual Expr *getStaticExpr() const = 0;\n  virtual LiteralKind getStaticKind() = 0;\n  virtual Type *getNonStaticType() const;\n  StaticType *getStatic() override { return this; }\n};\n\nstruct IntStaticType : public StaticType {\n  int64_t value;\n\n  explicit IntStaticType(Cache *cache, int64_t);\n\n  int unify(Type *typ, Unification *undo) override;\n  TypePtr generalize(int atLevel) const override;\n  TypePtr instantiate(int atLevel, int *unboundCount,\n                      std::unordered_map<int, TypePtr> *cache) const override;\n\n  std::string debugString(char mode) const override;\n  Expr *getStaticExpr() const override;\n  LiteralKind getStaticKind() override { return LiteralKind::Int; }\n\n  IntStaticType *getIntStatic() override { return this; }\n};\n\nstruct StrStaticType : public StaticType {\n  std::string value;\n\n  explicit StrStaticType(Cache *cache, std::string);\n\n  int unify(Type *typ, Unification *undo) override;\n  TypePtr generalize(int atLevel) const override;\n  TypePtr instantiate(int atLevel, int *unboundCount,\n                      std::unordered_map<int, TypePtr> *cache) const override;\n\n  std::string debugString(char mode) const override;\n  Expr *getStaticExpr() const override;\n  LiteralKind getStaticKind() override { return LiteralKind::String; }\n\n  StrStaticType *getStrStatic() override { return this; }\n};\n\nstruct BoolStaticType : public StaticType {\n  bool value;\n\n  explicit BoolStaticType(Cache *cache, bool);\n\n  int unify(Type *typ, Unification *undo) override;\n  TypePtr generalize(int atLevel) const override;\n  TypePtr instantiate(int atLevel, int *unboundCount,\n                      std::unordered_map<int, TypePtr> *cache) const override;\n\n  std::string debugString(char mode) const override;\n  Expr *getStaticExpr() const override;\n  LiteralKind getStaticKind() override { return LiteralKind::Bool; }\n\n  BoolStaticType *getBoolStatic() override { return this; }\n};\n\nusing StaticTypePtr = std::shared_ptr<StaticType>;\nusing IntStaticTypePtr = std::shared_ptr<IntStaticType>;\nusing StrStaticTypePtr = std::shared_ptr<StrStaticType>;\n\n} // namespace codon::ast::types\n"
  },
  {
    "path": "codon/parser/ast/types/traits.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nnamespace codon::ast::types {\n\nTrait::Trait(const std::shared_ptr<Type> &type) : Type(type) {}\n\nTrait::Trait(Cache *cache) : Type(cache) {}\n\nbool Trait::canRealize() const { return false; }\n\nbool Trait::isInstantiated() const { return false; }\n\nstd::string Trait::realizedName() const { return \"\"; }\n\nCallableTrait::CallableTrait(Cache *cache, std::vector<TypePtr> args)\n    : Trait(cache), args(std::move(args)) {}\n\nint CallableTrait::unify(Type *typ, Unification *us) {\n  /// TODO: one day merge with the CallExpr's logic...\n  if (auto tr = typ->getClass()) {\n    TypePtr ft = nullptr;\n    if (typ->is(\"TypeWrap\")) {\n      TypecheckVisitor tv(cache->typeCtx);\n      ft = tv.instantiateType(\n          tv.findMethod(typ->getClass(), \"__call_no_self__\").front(), typ->getClass());\n      tr = ft->getClass();\n    }\n\n    if (tr->name == \"NoneType\")\n      return 1;\n    if (tr->name != \"Function\" && !tr->getPartial())\n      return -1;\n    if (!tr->isRecord())\n      return -1;\n    if (args.empty())\n      return 1;\n\n    std::string known;\n    TypePtr func = nullptr; // trFun can point to it\n    auto trFun = tr;\n    if (auto pt = tr->getPartial()) {\n      int ic = 0;\n      std::unordered_map<int, TypePtr> c;\n      func = pt->getPartialFunc()->instantiate(0, &ic, &c);\n      trFun = func->getClass();\n      known = pt->getPartialMask();\n\n      auto knownArgTypes = pt->generics[1].type->getClass();\n      for (size_t i = 0, j = 0, k = 0; i < known.size(); i++)\n        if ((*func->getFunc()->ast)[i].isGeneric()) {\n          j++;\n        } else if (known[i] == ClassType::PartialFlag::Included) {\n          if ((*func->getFunc())[i - j]->unify(knownArgTypes->generics[k].type.get(),\n                                               us) == -1)\n            return -1;\n          k++;\n        }\n    } else {\n      known = std::string(tr->generics[0].type->getClass()->generics.size(),\n                          ClassType::PartialFlag::Missing);\n    }\n\n    auto inArgs = args[0]->getClass();\n    auto trInArgs = trFun->generics[0].type->getClass();\n    auto trAst = trFun->getFunc() ? trFun->getFunc()->ast : nullptr;\n    size_t star = 0, kwStar = trInArgs->generics.size();\n    size_t total = 0;\n    if (trAst) {\n      star = trAst->getStarArgs();\n      kwStar = trAst->getKwStarArgs();\n      for (size_t fi = 0; fi < trAst->size(); fi++) {\n        if (fi < star && !(*trAst)[fi].isValue())\n          star--;\n        if (fi < kwStar && !(*trAst)[fi].isValue())\n          kwStar--;\n      }\n      if (kwStar < trAst->size() && star >= trInArgs->generics.size())\n        star -= 1;\n      size_t preStar = 0;\n      for (size_t fi = 0; fi < trAst->size(); fi++) {\n        if (fi != kwStar && known[fi] != ClassType::PartialFlag::Included &&\n            (*trAst)[fi].isValue() && !startswith((*trAst)[fi].getName(), \"$\")) {\n          total++;\n          if (fi < star)\n            preStar++;\n        }\n      }\n      if (preStar < total) {\n        if (inArgs->generics.size() < preStar)\n          return -1;\n      } else if (inArgs->generics.size() != total) {\n        return -1;\n      }\n    } else {\n      total = star = trInArgs->generics.size();\n      if (inArgs->generics.size() != total)\n        return -1;\n    }\n    size_t i = 0;\n    for (size_t fi = 0; i < inArgs->generics.size() && fi < star; fi++) {\n      if (known[fi] != ClassType::PartialFlag::Included && trAst &&\n          (*trAst)[fi].isValue() && !startswith((*trAst)[fi].getName(), \"$\")) {\n        if (inArgs->generics[i++].type->unify(trInArgs->generics[fi].type.get(), us) ==\n            -1)\n          return -1;\n      }\n    }\n    auto tv = TypecheckVisitor(cache->typeCtx);\n    if (auto pf = trFun->getFunc()) {\n      // Make sure to set types of *args/**kwargs so that the function that\n      // is being unified with CallableTrait[] can be realized\n      if (star < trInArgs->generics.size() - (kwStar < trInArgs->generics.size())) {\n        std::vector<Type *> starArgTypes;\n        if (auto tp = tr->getPartial()) {\n          auto ts = tp->generics[1].type->getClass();\n          seqassert(ts && !ts->generics.empty() &&\n                        ts->generics[ts->generics.size() - 1].type->getClass(),\n                    \"bad partial *args/**kwargs\");\n          for (auto &tt :\n               ts->generics[ts->generics.size() - 1].type->getClass()->generics)\n            starArgTypes.push_back(tt.getType());\n        }\n        for (; i < inArgs->generics.size(); i++)\n          starArgTypes.push_back(inArgs->generics[i].getType());\n        if ((*pf->ast)[star].getType()) {\n          // if we have *args: type, use those types\n          auto starTyp =\n              tv.extractType(tv.transform(clone((*pf->ast)[star].getType())));\n          for (auto &t : starArgTypes)\n            t = starTyp;\n        }\n\n        auto tn =\n            tv.instantiateType(tv.generateTuple(starArgTypes.size()), starArgTypes);\n        if (tn->unify(trInArgs->generics[star].type.get(), us) == -1)\n          return -1;\n      }\n      if (kwStar < trInArgs->generics.size()) {\n        auto tt = tv.generateTuple(0);\n        size_t id = 0;\n        if (auto tp = tr->getPartial()) {\n          auto ts = tp->generics[2].type->getClass();\n          seqassert(ts && ts->is(\"NamedTuple\"), \"bad partial *args/**kwargs\");\n          id = ts->generics[0].type->getIntStatic()->value;\n          tt = ts->generics[1].getType()->getClass();\n        }\n        auto tid = std::make_shared<IntStaticType>(cache, id);\n        auto kt = tv.instantiateType(tv.getStdLibType(\"NamedTuple\"), {tid.get(), tt});\n        if (kt->unify(trInArgs->generics[kwStar].type.get(), us) == -1)\n          return -1;\n      }\n\n      if (us && pf->canRealize()) {\n        // Realize if possible to allow deduction of return type\n        auto rf = tv.realize(pf);\n        pf->unify(rf, us);\n      }\n      if (args[1]->unify(pf->getRetType(), us) == -1)\n        return -1;\n    }\n    return 1;\n  } else if (auto tl = typ->getLink()) {\n    if (tl->kind == LinkType::Link)\n      return unify(tl->type.get(), us);\n    if (tl->kind == LinkType::Unbound) {\n      if (tl->trait) {\n        auto tt = dynamic_cast<CallableTrait *>(tl->trait.get());\n        if (!tt || tt->args.size() != args.size())\n          return -1;\n        for (int i = 0; i < args.size(); i++)\n          if (args[i]->unify(tt->args[i].get(), us) == -1)\n            return -1;\n      }\n      return 1;\n    }\n  }\n  return -1;\n}\n\nTypePtr CallableTrait::generalize(int atLevel) const {\n  auto g = args;\n  for (auto &t : g)\n    t = t ? t->generalize(atLevel) : nullptr;\n  auto c = std::make_shared<CallableTrait>(cache, g);\n  c->setSrcInfo(getSrcInfo());\n  return c;\n}\n\nTypePtr CallableTrait::instantiate(int atLevel, int *unboundCount,\n                                   std::unordered_map<int, TypePtr> *cache) const {\n  auto g = args;\n  for (auto &t : g)\n    t = t ? t->instantiate(atLevel, unboundCount, cache) : nullptr;\n  auto c = std::make_shared<CallableTrait>(this->cache, g);\n  c->setSrcInfo(getSrcInfo());\n  return c;\n}\n\nstd::string CallableTrait::debugString(char mode) const {\n  auto s = args[0]->debugString(mode);\n  return fmt::format(\"CallableTrait[{},{}]\", startswith(s, \"Tuple\") ? s.substr(5) : s,\n                     args[1]->debugString(mode));\n}\n\nTypeTrait::TypeTrait(TypePtr typ) : Trait(typ), type(std::move(typ)) {}\n\nint TypeTrait::unify(Type *typ, Unification *us) {\n  if (typ->getClass()) {\n    // does not make sense otherwise and results in infinite cycles\n    return typ->unify(type.get(), us);\n  }\n  if (typ->getUnbound())\n    return 0;\n  return -1;\n}\n\nTypePtr TypeTrait::generalize(int atLevel) const {\n  auto c = std::make_shared<TypeTrait>(type->generalize(atLevel));\n  c->setSrcInfo(getSrcInfo());\n  return c;\n}\n\nTypePtr TypeTrait::instantiate(int atLevel, int *unboundCount,\n                               std::unordered_map<int, TypePtr> *cache) const {\n  auto c = std::make_shared<TypeTrait>(type->instantiate(atLevel, unboundCount, cache));\n  c->setSrcInfo(getSrcInfo());\n  return c;\n}\n\nstd::string TypeTrait::debugString(char mode) const {\n  return fmt::format(\"Trait[{}]\", type->getClass() ? type->getClass()->name : \"-\");\n}\n\n} // namespace codon::ast::types\n"
  },
  {
    "path": "codon/parser/ast/types/traits.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"codon/parser/ast/types/type.h\"\n\nnamespace codon::ast::types {\n\nstruct Trait : public Type {\n  bool canRealize() const override;\n  bool isInstantiated() const override;\n  std::string realizedName() const override;\n\nprotected:\n  explicit Trait(const std::shared_ptr<Type> &);\n  explicit Trait(Cache *);\n};\n\nstruct CallableTrait : public Trait {\n  std::vector<TypePtr> args; // tuple with arg types, ret type\n\npublic:\n  explicit CallableTrait(Cache *cache, std::vector<TypePtr> args);\n  int unify(Type *typ, Unification *undo) override;\n  TypePtr generalize(int atLevel) const override;\n  TypePtr instantiate(int atLevel, int *unboundCount,\n                      std::unordered_map<int, TypePtr> *cache) const override;\n  std::string debugString(char mode) const override;\n};\n\nstruct TypeTrait : public Trait {\n  TypePtr type;\n\npublic:\n  explicit TypeTrait(TypePtr type);\n  int unify(Type *typ, Unification *undo) override;\n  TypePtr generalize(int atLevel) const override;\n  TypePtr instantiate(int atLevel, int *unboundCount,\n                      std::unordered_map<int, TypePtr> *cache) const override;\n  std::string debugString(char mode) const override;\n};\n\n} // namespace codon::ast::types\n"
  },
  {
    "path": "codon/parser/ast/types/type.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"codon/parser/ast/types/type.h\"\n#include \"codon/parser/visitors/format/format.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nnamespace codon::ast::types {\n\n/// Undo a destructive unification.\nvoid Type::Unification::undo() {\n  for (size_t i = linked.size(); i-- > 0;) {\n    linked[i]->getLink()->kind = LinkType::Unbound;\n    linked[i]->getLink()->type = nullptr;\n  }\n  for (size_t i = leveled.size(); i-- > 0;) {\n    seqassertn(leveled[i].first->getLink()->kind == LinkType::Unbound,\n               \"not unbound [{}]\", leveled[i].first->getSrcInfo());\n    leveled[i].first->getLink()->level = leveled[i].second;\n  }\n  for (auto &t : traits)\n    t->getLink()->trait = nullptr;\n  for (auto &t : statics)\n    t->getLink()->staticKind = LiteralKind::Runtime;\n}\n\nType::Type(const std::shared_ptr<Type> &typ) : cache(typ->cache) {\n  setSrcInfo(typ->getSrcInfo());\n}\n\nType::Type(Cache *cache, const SrcInfo &info) : cache(cache) { setSrcInfo(info); }\n\nType *Type::follow() { return this; }\n\nbool Type::hasUnbounds(bool) const { return false; }\n\nstd::vector<Type *> Type::getUnbounds(bool) const { return {}; }\n\nstd::string Type::toString() const { return debugString(2); }\n\nstd::string Type::prettyString() const { return debugString(0); }\n\nbool Type::is(const std::string &s) { return getClass() && getClass()->name == s; }\n\nLiteralKind Type::getStaticKind() {\n  if (auto s = getStatic())\n    return s->getStaticKind();\n  if (auto l = follow()->getLink())\n    return l->staticKind;\n  return LiteralKind::Runtime;\n}\n\nLiteralKind Type::literalFromString(const std::string &s) {\n  if (s == \"int\")\n    return LiteralKind::Int;\n  if (s == \"str\")\n    return LiteralKind::String;\n  if (s == \"bool\")\n    return LiteralKind::Bool;\n  return LiteralKind::Runtime;\n}\n\nstd::string Type::stringFromLiteral(LiteralKind k) {\n  if (k == LiteralKind::Int)\n    return \"int\";\n  if (k == LiteralKind::String)\n    return \"str\";\n  if (k == LiteralKind::Bool)\n    return \"bool\";\n  return \"\";\n}\n\nType *Type::operator<<(Type *t) {\n  seqassert(t, \"rhs is nullptr\");\n  types::Type::Unification undo;\n  if (unify(t, &undo) >= 0) {\n    return this;\n  } else {\n    undo.undo();\n    return nullptr;\n  }\n}\n\n} // namespace codon::ast::types\n"
  },
  {
    "path": "codon/parser/ast/types/type.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"codon/parser/common.h\"\n\nnamespace codon::ast {\nstruct Cache;\nstruct Expr;\nstruct TypeContext;\n} // namespace codon::ast\n\nnamespace codon::ast::types {\n\n/// Forward declarations\nstruct ClassType;\nstruct FuncType;\nstruct LinkType;\nstruct StaticType;\nstruct IntStaticType;\nstruct StrStaticType;\nstruct BoolStaticType;\nstruct UnionType;\n\nenum LiteralKind { Runtime, Int, String, Bool };\n\n/**\n * An abstract type class that describes methods needed for the type inference.\n * (Hindley-Milner's Algorithm W inference; see\n * https://github.com/tomprimozic/type-systems).\n *\n * Type instances are mutable and each type is intended to be instantiated and\n * manipulated as a shared_ptr.\n */\nstruct Type : public codon::SrcObject, public std::enable_shared_from_this<Type> {\n  /// A structure that keeps the list of unification steps that can be undone later.\n  /// Needed because the unify() is destructive.\n  struct Unification {\n    /// List of unbound types that have been changed.\n    std::vector<std::shared_ptr<Type>> linked;\n    /// List of unbound types whose level has been changed.\n    std::vector<std::pair<std::shared_ptr<Type>, int>> leveled;\n    /// List of assigned traits.\n    std::vector<std::shared_ptr<Type>> traits;\n    /// List of unbound types whose static status has been changed.\n    std::vector<std::shared_ptr<Type>> statics;\n\n  public:\n    /// Undo the unification step.\n    void undo();\n  };\n\npublic:\n  /// Unifies a given type with the current type.\n  /// @param typ A given type.\n  /// @param undo A reference to Unification structure to track the unification steps\n  ///             and allow later undoing of the unification procedure.\n  /// @return Unification score: -1 for failure, anything >= 0 for success.\n  ///         Higher score translates to a \"better\" unification.\n  /// ⚠️ Destructive operation if undo is not null!\n  ///    (both the current and a given type are modified).\n  virtual int unify(Type *typ, Unification *undo) = 0;\n  /// Generalize all unbound types whose level is below the provided level.\n  /// This method replaces all unbound types with a generic types (e.g. ?1 -> T1).\n  /// Note that the generalized type keeps the unbound type's ID.\n  virtual std::shared_ptr<Type> generalize(int atLevel) const = 0;\n  /// Instantiate all generic types. Inverse of generalize(): it replaces all\n  /// generic types with new unbound types (e.g. T1 -> ?1234).\n  /// Note that the instantiated type has a distinct and unique ID.\n  /// @param atLevel Level of the instantiation.\n  /// @param unboundCount A reference of the unbound counter to ensure that no two\n  ///                     unbound types share the same ID.\n  /// @param cache A reference to a lookup table to ensure that all instances of a\n  ///              generic point to the same unbound type (e.g. dict[T, list[T]] should\n  ///              be instantiated as dict[?1, list[?1]]).\n  virtual std::shared_ptr<Type>\n  instantiate(int atLevel, int *unboundCount,\n              std::unordered_map<int, std::shared_ptr<Type>> *cache) const = 0;\n\npublic:\n  /// Get the final type (follow through all LinkType links).\n  /// For example, for (a->b->c->d) it returns d.\n  virtual Type *follow();\n  /// Check if type has unbound/generic types.\n  virtual bool hasUnbounds(bool includeGenerics) const;\n  /// Obtain the list of internal unbound types.\n  virtual std::vector<Type *> getUnbounds(bool includeGenerics) const;\n  /// True if a type is realizable.\n  virtual bool canRealize() const = 0;\n  /// True if a type is completely instantiated (has no unbounds or generics).\n  virtual bool isInstantiated() const = 0;\n  /// Debug print facility.\n  std::string toString() const;\n  /// Pretty-print facility.\n  std::string prettyString() const;\n  /// Pretty-print facility. mode is [0: pretty, 1: llvm, 2: debug]\n  virtual std::string debugString(char mode) const = 0;\n  /// Print the realization string.\n  /// Similar to toString, but does not print the data unnecessary for realization\n  /// (e.g. the function return type).\n  virtual std::string realizedName() const = 0;\n  LiteralKind getStaticKind();\n\n  /// Convenience virtual functions to avoid unnecessary casts.\n  virtual FuncType *getFunc() { return nullptr; }\n  virtual ClassType *getPartial() { return nullptr; }\n  virtual ClassType *getClass() { return nullptr; }\n  virtual LinkType *getLink() { return nullptr; }\n  virtual LinkType *getUnbound() { return nullptr; }\n  virtual StaticType *getStatic() { return nullptr; }\n  virtual IntStaticType *getIntStatic() { return nullptr; }\n  virtual StrStaticType *getStrStatic() { return nullptr; }\n  virtual BoolStaticType *getBoolStatic() { return nullptr; }\n  virtual UnionType *getUnion() { return nullptr; }\n\n  virtual bool is(const std::string &s);\n\n  Type *operator<<(Type *t);\n\n  static LiteralKind literalFromString(const std::string &s);\n  static std::string stringFromLiteral(LiteralKind k);\n\nprotected:\n  Cache *cache;\n  explicit Type(const std::shared_ptr<Type> &);\n  explicit Type(Cache *, const SrcInfo & = SrcInfo());\n};\nusing TypePtr = std::shared_ptr<Type>;\n\n} // namespace codon::ast::types\n\ntemplate <typename T>\nstruct fmt::formatter<\n    T, std::enable_if_t<std::is_base_of_v<codon::ast::types::Type, T>, char>>\n    : fmt::formatter<std::string_view> {\n  char presentation = 'b';\n\n  constexpr auto parse(const format_parse_context &ctx) -> decltype(ctx.begin()) {\n    auto it = ctx.begin();\n    if (const auto end = ctx.end();\n        it != end && (*it == 'a' || *it == 'b' || *it == 'c'))\n      presentation = *it++;\n    return it;\n  }\n\n  template <typename FormatContext>\n  auto format(const T &p, FormatContext &ctx) const -> decltype(ctx.out()) {\n    if (presentation == 'a')\n      return fmt::format_to(ctx.out(), \"{}\", p.debugString(0));\n    else if (presentation == 'b')\n      return fmt::format_to(ctx.out(), \"{}\", p.debugString(1));\n    else\n      return fmt::format_to(ctx.out(), \"{}\", p.debugString(2));\n  }\n};\n"
  },
  {
    "path": "codon/parser/ast/types/union.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\n#include <ranges>\n\nnamespace codon::ast::types {\n\nUnionType::UnionType(Cache *cache) : ClassType(cache, \"Union\") {\n  isTuple = true;\n  for (size_t i = 0; i < MAX_UNION; i++)\n    pendingTypes.emplace_back(\n        std::make_shared<LinkType>(cache, LinkType::Generic, i, 0, nullptr));\n}\n\nUnionType::UnionType(Cache *cache, const std::vector<ClassType::Generic> &generics,\n                     const std::vector<TypePtr> &pendingTypes)\n    : ClassType(cache, \"Union\", generics), pendingTypes(pendingTypes) {\n  isTuple = true;\n}\n\nint UnionType::unify(Type *typ, Unification *us) {\n  if (typ->getUnion()) {\n    auto tr = typ->getUnion();\n    if (!isSealed() && !tr->isSealed()) {\n      for (size_t i = 0; i < pendingTypes.size(); i++)\n        if (pendingTypes[i]->unify(tr->pendingTypes[i].get(), us) == -1)\n          return -1;\n      return ClassType::unify(typ, us);\n    } else if (!isSealed()) {\n      return tr->unify(this, us);\n    } else if (!tr->isSealed()) {\n      if (tr->pendingTypes[0]->getLink() &&\n          tr->pendingTypes[0]->getLink()->kind == LinkType::Unbound)\n        return ClassType::unify(tr, us);\n      return -1;\n    }\n    // Do not hard-unify if we have unbounds\n    if (!canRealize() || !tr->canRealize())\n      return 0;\n\n    auto u1 = getRealizationTypes();\n    auto u2 = tr->getRealizationTypes();\n    if (u1.size() != u2.size())\n      return -1;\n    int s1 = 2, s = 0;\n    for (size_t i = 0; i < u1.size(); i++) {\n      if ((s = u1[i]->unify(u2[i], us)) == -1)\n        return -1;\n      s1 += s;\n    }\n    return s1;\n  } else if (auto tl = typ->getLink()) {\n    return tl->unify(this, us);\n  }\n  return -1;\n}\n\nTypePtr UnionType::generalize(int atLevel) const {\n  auto r = ClassType::generalize(atLevel);\n  auto p = pendingTypes;\n  for (auto &t : p)\n    t = t->generalize(atLevel);\n  auto t = std::make_shared<UnionType>(cache, r->getClass()->generics, p);\n  t->setSrcInfo(getSrcInfo());\n  return t;\n}\n\nTypePtr UnionType::instantiate(int atLevel, int *unboundCount,\n                               std::unordered_map<int, TypePtr> *cache) const {\n  auto r = ClassType::instantiate(atLevel, unboundCount, cache);\n  auto p = pendingTypes;\n  for (auto &t : p)\n    t = t->instantiate(atLevel, unboundCount, cache);\n  auto t = std::make_shared<UnionType>(this->cache, r->getClass()->generics, p);\n  t->setSrcInfo(getSrcInfo());\n  return t;\n}\n\nstd::string UnionType::debugString(char mode) const {\n  if (mode == 2)\n    return this->ClassType::debugString(mode);\n  if (!generics[0].type->getClass())\n    return this->ClassType::debugString(mode);\n\n  std::set<std::string> gss;\n  for (auto &a : generics[0].type->getClass()->generics)\n    gss.insert(a.debugString(mode));\n  std::string s = join(gss, \" | \");\n  return (name + (s.empty() ? \"\" : (\"[\" + s + \"]\")));\n}\n\nbool UnionType::canRealize() const { return isSealed() && ClassType::canRealize(); }\n\nstd::string UnionType::realizedName() const {\n  seqassert(canRealize(), \"cannot realize {}\", debugString(2));\n  return ClassType::realizedName();\n}\n\nbool UnionType::addType(Type *typ) {\n  seqassert(!isSealed(), \"union already sealed\");\n  if (this == typ)\n    return true;\n  if (auto tu = typ->getUnion()) {\n    if (tu->isSealed()) {\n      for (auto &t : tu->generics[0].type->getClass()->generics)\n        if (!addType(t.type.get()))\n          return false;\n    } else {\n      for (auto &t : tu->pendingTypes) {\n        if (t->getLink() && t->getLink()->kind == LinkType::Unbound)\n          break;\n        else if (!addType(t.get()))\n          return false;\n      }\n    }\n    return true;\n  } else {\n    // Find first pending generic to which we can attach this!\n    Unification us;\n    for (auto &t : pendingTypes)\n      if (auto l = t->getLink()) {\n        if (l->kind == LinkType::Unbound) {\n          t->unify(typ, &us);\n          return true;\n        }\n      }\n    return false;\n  }\n}\n\nbool UnionType::isSealed() const { return generics[0].type->getClass() != nullptr; }\n\nvoid UnionType::seal() {\n  seqassert(!isSealed(), \"union already sealed\");\n  auto tv = TypecheckVisitor(cache->typeCtx);\n\n  size_t i;\n  for (i = 0; i < pendingTypes.size(); i++)\n    if (pendingTypes[i]->getLink() &&\n        pendingTypes[i]->getLink()->kind == LinkType::Unbound)\n      break;\n  std::vector<Type *> typeSet;\n  typeSet.reserve(i);\n  for (size_t j = 0; j < i; j++)\n    typeSet.emplace_back(pendingTypes[j].get());\n  auto t = tv.instantiateType(tv.generateTuple(typeSet.size()), typeSet);\n  Unification us;\n  generics[0].type->unify(t.get(), &us);\n}\n\nstd::vector<Type *> UnionType::getRealizationTypes() const {\n  seqassert(canRealize(), \"cannot realize {}\", debugString(2));\n  std::map<std::string, Type *> unionTypes;\n  for (auto &u : generics[0].type->getClass()->generics)\n    unionTypes[u.type->realizedName()] = u.type.get();\n  std::vector<Type *> r;\n  r.reserve(unionTypes.size());\n  for (auto &t : unionTypes | std::views::values)\n    r.emplace_back(t);\n  return r;\n}\n\n} // namespace codon::ast::types\n"
  },
  {
    "path": "codon/parser/ast/types/union.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <vector>\n\n#include \"codon/parser/ast/types/class.h\"\n\nnamespace codon::ast::types {\n\nstruct UnionType : public ClassType {\n  static constexpr int MAX_UNION = 256;\n\n  std::vector<TypePtr> pendingTypes;\n\n  explicit UnionType(Cache *cache);\n  UnionType(Cache *, const std::vector<ClassType::Generic> &,\n            const std::vector<TypePtr> &);\n\npublic:\n  int unify(Type *typ, Unification *undo) override;\n  TypePtr generalize(int atLevel) const override;\n  TypePtr instantiate(int atLevel, int *unboundCount,\n                      std::unordered_map<int, TypePtr> *cache) const override;\n\npublic:\n  bool canRealize() const override;\n  std::string debugString(char mode) const override;\n  std::string realizedName() const override;\n  bool isSealed() const;\n\n  UnionType *getUnion() override { return this; }\n\n  bool addType(Type *);\n  void seal();\n  std::vector<Type *> getRealizationTypes() const;\n};\n\n} // namespace codon::ast::types\n"
  },
  {
    "path": "codon/parser/ast/types.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include \"codon/parser/ast/types/class.h\"\n#include \"codon/parser/ast/types/function.h\"\n#include \"codon/parser/ast/types/link.h\"\n#include \"codon/parser/ast/types/static.h\"\n#include \"codon/parser/ast/types/traits.h\"\n#include \"codon/parser/ast/types/type.h\"\n#include \"codon/parser/ast/types/union.h\"\n"
  },
  {
    "path": "codon/parser/ast.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <ostream>\n#include <string>\n\n#include \"codon/cir/attribute.h\"\n#include \"codon/cir/base.h\"\n#include \"codon/parser/ast/attr.h\"\n#include \"codon/parser/ast/error.h\"\n#include \"codon/parser/ast/expr.h\"\n#include \"codon/parser/ast/node.h\"\n#include \"codon/parser/ast/stmt.h\"\n#include \"codon/parser/ast/types.h\"\n"
  },
  {
    "path": "codon/parser/cache.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"cache.h\"\n\n#include <chrono>\n#include <ranges>\n#include <string>\n#include <vector>\n\n#include \"codon/cir/pyextension.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/peg/peg.h\"\n#include \"codon/parser/visitors/translate/translate.h\"\n#include \"codon/parser/visitors/typecheck/ctx.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nnamespace codon::ast {\n\nconst std::string VAR_ARGV = getMangledVar(\"\", \"__argv__\");\nconst std::string FN_OPTIONAL_UNWRAP =\n    getMangledFunc(\"std.internal.types.optional\", \"unwrap\");\n\nCache::Cache(std::string argv0, const std::shared_ptr<IFilesystem> &fs) : fs(fs) {\n  if (!this->fs) {\n    this->fs = std::make_shared<Filesystem>(argv0);\n  }\n  this->_nodes = new std::vector<std::unique_ptr<ast::ASTNode>>();\n  typeCtx = std::make_shared<TypeContext>(this, \".root\");\n}\n\nstd::string Cache::getTemporaryVar(const std::string &prefix, char sigil) {\n  auto n = fmt::format(\"{}{}_{}\", sigil ? fmt::format(\"{}_\", sigil) : \"\", prefix,\n                       ++varCount);\n  return n;\n}\n\nstd::string Cache::rev(const std::string &s) {\n  auto i = reverseIdentifierLookup.find(s);\n  if (i != reverseIdentifierLookup.end())\n    return i->second;\n  seqassertn(false, \"'{}' has no non-canonical name\", s);\n  return \"\";\n}\n\nSrcInfo Cache::generateSrcInfo() {\n  return {FILE_GENERATED, generatedSrcInfoCount, generatedSrcInfoCount++, 0};\n}\n\nstd::string Cache::getContent(const SrcInfo &info) {\n  auto i = imports.find(info.file);\n  if (i == imports.end())\n    return \"\";\n  int line = info.line - 1;\n  if (line < 0 || line >= i->second.content.size())\n    return \"\";\n  auto s = i->second.content[line];\n  int col = info.col - 1;\n  if (col < 0 || col >= s.size())\n    return \"\";\n  int len = info.len;\n  return s.substr(col, len);\n}\n\nCache::Class *Cache::getClass(const types::ClassType *type) {\n  auto name = type->name;\n  return in(classes, name);\n}\n\nstd::string Cache::getMethod(types::ClassType *typ, const std::string &member) {\n  if (auto cls = getClass(typ)) {\n    if (auto t = in(cls->methods, member))\n      return *t;\n  }\n  seqassertn(false, \"cannot find '{}' in '{}'\", member, typ->name);\n  return \"\";\n}\n\ntypes::ClassType *Cache::findClass(const std::string &name) const {\n  auto f = typeCtx->find(name);\n  if (f && f->isType())\n    return f->getType()->getClass()->generics[0].getType()->getClass();\n  return nullptr;\n}\n\ntypes::FuncType *Cache::findFunction(const std::string &name) const {\n  auto f = typeCtx->find(name);\n  if (f && f->type && f->isFunc())\n    return f->type->getFunc();\n  f = typeCtx->find(name + \":0\");\n  if (f && f->type && f->isFunc())\n    return f->type->getFunc();\n  return nullptr;\n}\n\ntypes::FuncType *Cache::findMethod(types::ClassType *typ, const std::string &member,\n                                   const std::vector<types::Type *> &args) const {\n  auto f = TypecheckVisitor(typeCtx).findBestMethod(typ, member, args);\n  return f;\n}\n\nir::types::Type *Cache::realizeType(types::ClassType *type,\n                                    const std::vector<types::TypePtr> &generics) {\n  auto tv = TypecheckVisitor(typeCtx);\n  if (auto rtv = tv.realize(tv.instantiateType(type, castVectorPtr(generics)))) {\n    return classes[rtv->getClass()->name]\n        .realizations[rtv->getClass()->realizedName()]\n        ->ir;\n  }\n  return nullptr;\n}\n\nir::Func *Cache::realizeFunction(types::FuncType *type,\n                                 const std::vector<types::TypePtr> &args,\n                                 const std::vector<types::TypePtr> &generics,\n                                 types::ClassType *parentClass) {\n  auto tv = TypecheckVisitor(typeCtx);\n  auto t = tv.instantiateType(type, parentClass);\n  if (args.size() != t->size() + 1)\n    return nullptr;\n  types::Type::Unification undo;\n  if (t->getRetType()->unify(args[0].get(), &undo) < 0) {\n    undo.undo();\n    return nullptr;\n  }\n  for (int gi = 1; gi < args.size(); gi++) {\n    undo = types::Type::Unification();\n    if ((*t)[gi - 1]->unify(args[gi].get(), &undo) < 0) {\n      undo.undo();\n      return nullptr;\n    }\n  }\n  if (!generics.empty()) {\n    if (generics.size() != t->funcGenerics.size())\n      return nullptr;\n    for (int gi = 0; gi < generics.size(); gi++) {\n      undo = types::Type::Unification();\n      if (t->funcGenerics[gi].type->unify(generics[gi].get(), &undo) < 0) {\n        undo.undo();\n        return nullptr;\n      }\n    }\n  }\n  ir::Func *f = nullptr;\n  if (auto rtv = tv.realize(t.get())) {\n    auto pr = pendingRealizations; // copy it as it might be modified\n    for (const auto &key : pr | std::views::keys)\n      TranslateVisitor(codegenCtx).translateStmts(clone(functions[key].ast));\n    f = functions[rtv->getFunc()->ast->getName()].realizations[rtv->realizedName()]->ir;\n  }\n  return f;\n}\n\nir::types::Type *Cache::makeTuple(const std::vector<types::TypePtr> &types) {\n  auto tv = TypecheckVisitor(typeCtx);\n  auto t = tv.instantiateType(tv.generateTuple(types.size()), castVectorPtr(types));\n  return realizeType(t->getClass(), types);\n}\n\nir::types::Type *Cache::makeFunction(const std::vector<types::TypePtr> &types) {\n  auto tv = TypecheckVisitor(typeCtx);\n  seqassertn(!types.empty(), \"types must have at least one argument\");\n\n  std::vector<types::Type *> tt;\n  for (size_t i = 1; i < types.size(); i++)\n    tt.emplace_back(types[i].get());\n  const auto &ret = types[0];\n  auto argType = tv.instantiateType(tv.generateTuple(types.size() - 1), tt);\n  auto ft = realizeType(tv.getStdLibType(\"Function\")->getClass(), {argType, ret});\n  return ft;\n}\n\nir::types::Type *Cache::makeUnion(const std::vector<types::TypePtr> &types) {\n  auto tv = TypecheckVisitor(typeCtx);\n  auto argType =\n      tv.instantiateType(tv.generateTuple(types.size()), castVectorPtr(types));\n  return realizeType(tv.getStdLibType(\"Union\")->getClass(), {argType});\n}\n\nsize_t Cache::getRealizationId(types::ClassType *type) {\n  auto cv = TypecheckVisitor(typeCtx).getClassRealization(type);\n  return cv->id;\n}\n\nstd::vector<size_t> Cache::getBaseRealizationIds(types::ClassType *type) {\n  auto r = TypecheckVisitor(typeCtx).getClassRealization(type);\n  std::vector<size_t> baseIds;\n  for (const auto &t : r->bases) {\n    baseIds.push_back(getRealizationId(t.get()));\n  }\n  return baseIds;\n}\n\nstd::vector<size_t> Cache::getChildRealizationIds(types::ClassType *type) {\n  auto cv = TypecheckVisitor(typeCtx).getClassRealization(type);\n  auto parentId = cv->id;\n  std::vector<size_t> childIds;\n  for (const auto &[_, c] : classes) {\n    for (const auto &[_, r] : c.realizations) {\n      for (const auto &t : r->bases) {\n        if (getRealizationId(t.get()) == parentId) {\n          childIds.push_back(r->id);\n          break;\n        }\n      }\n    }\n  }\n  return childIds;\n}\n\nstd::vector<ir::SeriesFlow *> Cache::parseCode(const std::string &code) {\n  auto nodeOrErr = ast::parseCode(this, \"<internal>\", code, /*startLine=*/0);\n  if (!nodeOrErr)\n    throw exc::ParserException(nodeOrErr.takeError());\n  auto sctx = imports[MAIN_IMPORT].ctx;\n  auto node = ast::TypecheckVisitor::apply(sctx, *nodeOrErr);\n  auto old = codegenCtx->series;\n  codegenCtx->series.clear();\n  ast::TranslateVisitor(codegenCtx).initializeGlobals();\n  ast::TranslateVisitor(codegenCtx).translateStmts(node);\n  std::swap(old, codegenCtx->series);\n  return old;\n}\n\nstd::vector<std::shared_ptr<types::ClassType>>\nCache::mergeC3(std::vector<std::vector<types::TypePtr>> &seqs) {\n  // Reference: https://www.python.org/download/releases/2.3/mro/\n  std::vector<std::shared_ptr<types::ClassType>> result;\n  for (size_t i = 0;; i++) {\n    bool found = false;\n    std::shared_ptr<types::ClassType> cand = nullptr;\n    for (auto &seq : seqs) {\n      if (seq.empty())\n        continue;\n      found = true;\n      bool nothead = false;\n      for (auto &s : seqs)\n        if (!s.empty()) {\n          bool in = false;\n          for (size_t j = 1; j < s.size(); j++) {\n            if ((in |= (seq[0]->is(s[j]->getClass()->name))))\n              break;\n          }\n          if (in) {\n            nothead = true;\n            break;\n          }\n        }\n      if (!nothead) {\n        cand = std::dynamic_pointer_cast<types::ClassType>(seq[0]);\n        break;\n      }\n    }\n    if (!found)\n      return result;\n    if (!cand)\n      return {};\n    result.push_back(cand);\n    for (auto &s : seqs)\n      if (!s.empty() && cand->is(s[0]->getClass()->name)) {\n        s.erase(s.begin());\n      }\n  }\n  return result;\n}\n\n/**\n * Generate Python bindings for Cython-like access.\n */\nvoid Cache::populatePythonModule() {\n  using namespace ast;\n\n  const std::string CYTHON_ITER = \"_PyWrap.IterWrap\";\n\n  if (!pythonExt)\n    return;\n  if (!pyModule)\n    pyModule = std::make_shared<ir::PyModule>();\n\n  LOG_USER(\"[py] ====== module generation =======\");\n  auto tv = TypecheckVisitor(typeCtx);\n  auto clss = classes; // needs copy as below fns can mutate this\n  for (const auto &cn : clss | std::views::keys) {\n    auto py = tv.cythonizeClass(cn);\n    if (!py.name.empty())\n      pyModule->types.push_back(py);\n  }\n\n  // Handle __iternext__ wrappers\n  for (const auto &cn : classes[CYTHON_ITER].realizations | std::views::keys) {\n    auto py = tv.cythonizeIterator(cn);\n    pyModule->types.push_back(py);\n  }\n\n  auto fns = functions; // needs copy as below fns can mutate this\n  for (const auto &fn : fns | std::views::keys) {\n    auto py = tv.cythonizeFunction(fn);\n    if (!py.name.empty())\n      pyModule->functions.push_back(py);\n  }\n\n  // Handle pending realizations!\n  auto pr = pendingRealizations; // copy it as it might be modified\n  for (const auto &key : pr | std::views::keys)\n    TranslateVisitor(codegenCtx).translateStmts(clone(functions[key].ast));\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/cache.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <map>\n#include <ostream>\n#include <set>\n#include <string>\n#include <vector>\n\n#include \"codon/cir/cir.h\"\n#include \"codon/cir/pyextension.h\"\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/ctx.h\"\n\n#define FILE_GENERATED \"<generated>\"\n\n#define MODULE_MAIN \"__main__\"\n#define STDLIB_INTERNAL_MODULE \"internal\"\n\n#define MAIN_IMPORT \"\"\n#define STDLIB_IMPORT \":stdlib:\"\n\n#define TYPE_CALLABLE \"Callable\"\n#define TYPE_FUNCTION \"Function\"\n#define TYPE_OPTIONAL \"Optional\"\n#define TYPE_TUPLE \"Tuple\"\n#define TYPE_TYPE \"type\"\n\n#define TRAIT_TYPE \"TypeTrait\"\n#define TRAIT_CALLABLE \"CallableTrait\"\n\n#define FN_DISPATCH_SUFFIX \":dispatch\"\n#define FN_SETTER_SUFFIX \":set_\"\n\n#define VAR_CLASS_TOPLEVEL \":toplevel\"\n#define VAR_USED_SUFFIX \":used\"\n\n#define MAX_ERRORS 5\n#define MAX_TUPLE 2048\n#define MAX_INT_WIDTH 10000\n#define MAX_REALIZATION_DEPTH 200\n#define MAX_STATIC_ITER 1024\n\nnamespace codon {\nclass Compiler;\n}\n\nnamespace codon::ast {\n\nextern const std::string VAR_ARGV;\nextern const std::string FN_OPTIONAL_UNWRAP;\n\n/// Forward declarations\nstruct TypeContext;\nstruct TranslateContext;\n\n/**\n * Cache encapsulation that holds data structures shared across various transformation\n * stages (AST transformation, type checking etc.). The subsequent stages (e.g. type\n * checking) assumes that previous stages populated this structure correctly.\n * Implemented to avoid a bunch of global objects.\n */\nstruct Cache {\n  /// Filesystem object used for accessing files.\n  std::shared_ptr<IFilesystem> fs;\n  /// Stores a count for each identifier (name) seen in the code.\n  /// Used to generate unique identifier for each name in the code (e.g. Foo -> Foo.2).\n  std::unordered_map<std::string, int> identifierCount;\n  /// Maps a unique identifier back to the original name in the code\n  /// (e.g. Foo.2 -> Foo).\n  std::unordered_map<std::string, std::string> reverseIdentifierLookup;\n  /// Number of code-generated source code positions. Used to generate the next unique\n  /// source-code position information.\n  int generatedSrcInfoCount = 0;\n  /// Number of unbound variables so far. Used to generate the next unique unbound\n  /// identifier.\n  int unboundCount = 256;\n  /// Number of auto-generated variables so far. Used to generate the next unique\n  /// variable name in getTemporaryVar() below.\n  int varCount = 0;\n  /// Scope counter. Each conditional block gets a new scope ID.\n  int blockCount = 1;\n\n  /// Holds module import data.\n  struct Module {\n    /// Relative module name (e.g., `foo.bar`)\n    std::string name;\n    /// Absolute filename of an import.\n    std::string filename;\n    /// Import typechecking context.\n    std::shared_ptr<TypeContext> ctx;\n    /// Unique import variable for checking already loaded imports.\n    std::string importVar;\n    /// File content (line:col indexable)\n    std::vector<std::string> content;\n    /// Set if loaded at toplevel\n    bool loadedAtToplevel = true;\n\n    void update(const std::string &name, const std::string &filename,\n                const std::shared_ptr<TypeContext> &ctx) {\n      this->name = name;\n      this->filename = filename;\n      this->ctx = ctx;\n    }\n  };\n\n  /// Compiler\n  Compiler *compiler = nullptr;\n  /// IR module.\n  ir::Module *module = nullptr;\n\n  /// Table of imported files that maps an absolute filename to an Import structure.\n  /// By convention, the key of the Codon's standard library is \":stdlib:\",\n  /// and the main module is \"\".\n  std::unordered_map<std::string, Module> imports;\n\n  /// Set of unique (canonical) global identifiers for marking such variables as global\n  /// in code-generation step and in JIT.\n  std::map<std::string, ir::Var *> globals;\n\n  /// Stores class data for each class (type) in the source code.\n  struct Class {\n    /// Module information\n    std::string module;\n\n    /// Generic (unrealized) class template AST.\n    ClassStmt *ast = nullptr;\n    /// Non-simplified AST. Used for base class instantiation.\n    ClassStmt *originalAst = nullptr;\n\n    /// Class method lookup table. Each non-canonical name points\n    /// to a root function name of a corresponding method.\n    std::unordered_map<std::string, std::string> methods;\n\n    /// A class field (member).\n    struct ClassField {\n      /// Field name.\n      std::string name;\n      /// A corresponding generic field type.\n      types::TypePtr type;\n      /// Base class name (if available)\n      std::string baseClass;\n      Expr *typeExpr;\n\n      ClassField(const std::string &name, const types::TypePtr &type,\n                 const std::string &baseClass, Expr *typeExpr = nullptr)\n          : name(name), type(type), baseClass(baseClass), typeExpr(typeExpr) {}\n      types::Type *getType() const { return type.get(); }\n    };\n    /// A list of class' ClassField instances. List is needed (instead of map) because\n    /// the order of the fields matters.\n    std::vector<ClassField> fields;\n\n    /// Dictionary of class variables: a name maps to a canonical name.\n    std::unordered_map<std::string, std::string> classVars;\n\n    /// A class realization.\n    struct ClassRealization {\n      /// Realized class type.\n      std::shared_ptr<types::ClassType> type;\n      /// A list of field names and realization's realized field types.\n      std::vector<std::pair<std::string, types::TypePtr>> fields;\n      /// IR type pointer.\n      codon::ir::types::Type *ir = nullptr;\n      // Bases (in MRO order)\n      std::vector<std::shared_ptr<types::ClassType>> bases;\n\n      /// Realization vtable (for each base class).\n      /// Maps {base, function signature} to {thunk realization, thunk ID}.\n      /// Base can be the realization itself.\n      /// Order is important so map is used instead of unordered_map.\n      std::map<std::pair<std::string, std::string>, std::shared_ptr<types::FuncType>>\n          vtable;\n      /// Realization ID\n      size_t id = 0;\n\n      types::ClassType *getType() const { return type.get(); }\n    };\n    /// Realization lookup table that maps a realized class name to the corresponding\n    /// ClassRealization instance.\n    std::unordered_map<std::string, std::shared_ptr<ClassRealization>> realizations;\n\n    /// Set if a class is polymorphic and has RTTI.\n    bool rtti = false;\n    /// List of virtual method names\n    std::unordered_set<std::string> virtuals;\n    /// MRO\n    std::vector<std::shared_ptr<types::ClassType>> mro;\n\n    /// List of statically inherited classes.\n    std::vector<std::string> staticParentClasses;\n\n    int jitCell = 0;\n\n    bool hasRTTI() const { return rtti; }\n  };\n  /// Class lookup table that maps a canonical class identifier to the corresponding\n  /// Class instance.\n  std::unordered_map<std::string, Class> classes;\n  size_t classRealizationCnt = 0;\n\n  Class *getClass(const types::ClassType *);\n\n  std::map<std::pair<std::string, std::string>, size_t> thunkIds;\n\n  struct Function {\n    /// Module information\n    std::string module;\n    std::string rootName;\n    /// Generic (unrealized) function template AST.\n    FunctionStmt *ast;\n    /// Unrealized function type.\n    std::shared_ptr<types::FuncType> type;\n\n    /// Non-simplified AST.\n    FunctionStmt *origAst = nullptr;\n    bool isToplevel = false;\n\n    /// A function realization.\n    struct FunctionRealization {\n      /// Realized function type.\n      std::shared_ptr<types::FuncType> type;\n      /// Realized function AST (stored here for later realization in code generations\n      /// stage).\n      FunctionStmt *ast;\n      /// IR function pointer.\n      ir::Func *ir;\n      /// Resolved captures\n      std::vector<std::string> captures;\n\n      types::FuncType *getType() const { return type.get(); }\n    };\n    /// Realization lookup table that maps a realized function name to the corresponding\n    /// FunctionRealization instance.\n    std::unordered_map<std::string, std::shared_ptr<FunctionRealization>> realizations =\n        {};\n    std::set<std::string> captures = {};\n\n    types::FuncType *getType() const { return type.get(); }\n  };\n  /// Function lookup table that maps a canonical function identifier to the\n  /// corresponding Function instance.\n  std::unordered_map<std::string, Function> functions;\n\n  /// Maps a \"root\" name of each function to the list of names of the function\n  /// overloads (canonical names).\n  std::unordered_map<std::string, std::vector<std::string>> overloads;\n\n  /// Pointer to the later contexts needed for IR API access.\n  std::shared_ptr<TypeContext> typeCtx = nullptr;\n  std::shared_ptr<TranslateContext> codegenCtx = nullptr;\n  /// Set of function realizations that are to be translated to IR.\n  std::set<std::pair<std::string, std::string>> pendingRealizations;\n\n  /// Custom operators\n  std::unordered_map<std::string,\n                     std::pair<bool, std::function<Stmt *(ast::TypecheckVisitor *,\n                                                          ast::CustomStmt *)>>>\n      customBlockStmts;\n  std::unordered_map<std::string,\n                     std::function<Stmt *(ast::TypecheckVisitor *, ast::CustomStmt *)>>\n      customExprStmts;\n\n  /// Set if the Codon is running in JIT mode.\n  bool isJit = false;\n  int jitCell = 0;\n\n  std::unordered_set<size_t> generatedTuples;\n  std::unordered_map<std::string, int> generatedKwTuples;\n  std::vector<std::vector<std::string>> generatedTupleNames = {{}};\n  ParserErrors errors;\n\n  /// Set if Codon operates in Python compatibility mode (e.g., with Python numerics)\n  bool pythonCompat = false;\n  /// Set if Codon operates in Python extension mode\n  bool pythonExt = false;\n\npublic:\n  explicit Cache(std::string argv0 = \"\",\n                 const std::shared_ptr<IFilesystem> &fs = nullptr);\n\n  /// Return a uniquely named temporary variable of a format\n  /// \"{sigil}_{prefix}{counter}\". A sigil should be a non-lexable symbol.\n  std::string getTemporaryVar(const std::string &prefix = \"\", char sigil = '%');\n  /// Get the non-canonical version of a canonical name.\n  std::string rev(const std::string &s);\n\n  /// Generate a unique SrcInfo for internally generated AST nodes.\n  SrcInfo generateSrcInfo();\n  /// Get file contents at the given location.\n  std::string getContent(const SrcInfo &info);\n\n  /// Realization API.\n\n  /// Find a class with a given canonical name and return a matching types::Type pointer\n  /// or a nullptr if a class is not found.\n  /// Returns an _uninstantiated_ type.\n  types::ClassType *findClass(const std::string &name) const;\n  /// Find a function with a given canonical name and return a matching types::Type\n  /// pointer or a nullptr if a function is not found.\n  /// Returns an _uninstantiated_ type.\n  types::FuncType *findFunction(const std::string &name) const;\n  /// Find the canonical name of a class method.\n  std::string getMethod(types::ClassType *typ, const std::string &member);\n  /// Find the class method in a given class type that best matches the given arguments.\n  /// Returns an _uninstantiated_ type.\n  types::FuncType *findMethod(types::ClassType *typ, const std::string &member,\n                              const std::vector<types::Type *> &args) const;\n\n  /// Given a class type and the matching generic vector, instantiate the type and\n  /// realize it.\n  ir::types::Type *realizeType(types::ClassType *type,\n                               const std::vector<types::TypePtr> &generics = {});\n  /// Given a function type and function arguments, instantiate the type and\n  /// realize it. The first argument is the function return type.\n  /// You can also pass function generics if a function has one (e.g. T in def\n  /// foo[T](...)). If a generic is used as an argument, it will be auto-deduced. Pass\n  /// only if a generic cannot be deduced from the provided args.\n  ir::Func *realizeFunction(types::FuncType *type,\n                            const std::vector<types::TypePtr> &args,\n                            const std::vector<types::TypePtr> &generics = {},\n                            types::ClassType *parentClass = nullptr);\n\n  ir::types::Type *makeTuple(const std::vector<types::TypePtr> &types);\n  ir::types::Type *makeFunction(const std::vector<types::TypePtr> &types);\n  ir::types::Type *makeUnion(const std::vector<types::TypePtr> &types);\n\n  size_t getRealizationId(types::ClassType *type);\n  std::vector<size_t> getBaseRealizationIds(types::ClassType *type);\n  std::vector<size_t> getChildRealizationIds(types::ClassType *type);\n\n  std::vector<ir::SeriesFlow *> parseCode(const std::string &code);\n\n  static std::vector<std::shared_ptr<types::ClassType>>\n  mergeC3(std::vector<std::vector<types::TypePtr>> &);\n\n  std::shared_ptr<ir::PyModule> pyModule = nullptr;\n  void populatePythonModule();\n\nprivate:\n  std::vector<std::unique_ptr<ast::ASTNode>> *_nodes;\n\npublic:\n  /// Convenience method that constructs a node with the visitor's source location.\n  template <typename Tn, typename... Ts> Tn *N(Ts &&...args) {\n    _nodes->emplace_back(std::make_unique<Tn>(std::forward<Ts>(args)...));\n    Tn *t = static_cast<Tn *>(_nodes->back().get());\n    t->cache = this;\n    return t;\n  }\n  template <typename Tn, typename... Ts> Tn *NS(const ASTNode *srcInfo, Ts &&...args) {\n    _nodes->emplace_back(std::make_unique<Tn>(std::forward<Ts>(args)...));\n    Tn *t = static_cast<Tn *>(_nodes->back().get());\n    t->cache = this;\n    t->setSrcInfo(srcInfo->getSrcInfo());\n    return t;\n  }\n\n  std::unordered_map<std::string, double> _timings;\n  struct CTimer {\n    Cache *c;\n    Timer t;\n    std::string name;\n    CTimer(Cache *c, std::string n) : c(c), t(Timer(\"\")), name(std::move(n)) {}\n    double elapsed() const { return t.elapsed(); }\n    ~CTimer() {\n      c->_timings[name] += t.elapsed();\n      t.logged = true;\n    }\n  };\n\n  template <typename T>\n  std::vector<T *> castVectorPtr(std::vector<std::shared_ptr<T>> v) {\n    std::vector<T *> r;\n    r.reserve(v.size());\n    for (const auto &i : v)\n      r.emplace_back(i.get());\n    return r;\n  }\n};\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/common.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"common.h\"\n\n#include <cinttypes>\n#include <climits>\n#include <filesystem>\n#include <string>\n#include <vector>\n\n#include \"codon/compiler/compiler.h\"\n#include \"codon/parser/cache.h\"\n#include \"llvm/Support/FileSystem.h\"\n#include \"llvm/Support/Path.h\"\n#include <fmt/format.h>\n\n#include <cmrc/cmrc.hpp>\nCMRC_DECLARE(codon);\n\nnamespace codon::ast {\n\nIFilesystem::path_t IFilesystem::canonical(const path_t &path) const {\n  return std::filesystem::weakly_canonical(path);\n}\n\nvoid IFilesystem::add_search_path(const std::string &p) {\n  auto path = path_t(p);\n  if (exists(path)) {\n    search_paths.emplace_back(canonical(path));\n  }\n}\n\nstd::vector<IFilesystem::path_t> IFilesystem::get_stdlib_paths() const {\n  return search_paths;\n}\n\nImportFile IFilesystem::get_root(const path_t &sp) const {\n  bool isStdLib = false;\n  std::string s = sp;\n  std::string root;\n  for (auto &p : get_stdlib_paths())\n    if (startswith(s, p)) {\n      root = p;\n      isStdLib = true;\n      break;\n    }\n  auto module0 = get_module0().parent_path();\n  if (!isStdLib && !module0.empty() && startswith(s, module0))\n    root = module0;\n  std::string ext = \".codon\";\n  if (!((root.empty() || startswith(s, root)) && endswith(s, ext)))\n    ext = \".py\";\n  seqassertn((root.empty() || startswith(s, root)) && endswith(s, ext),\n             \"bad path substitution: {}, {}\", s, root);\n  auto module = s.substr(root.size() + 1, s.size() - root.size() - ext.size() - 1);\n  std::ranges::replace(module, '/', '.');\n  return ImportFile{(!isStdLib && root == module0) ? ImportFile::PACKAGE\n                                                   : ImportFile::STDLIB,\n                    s, module};\n}\n\nFilesystem::Filesystem(const std::string &argv0, const std::string &module0)\n    : argv0(argv0), module0(module0) {\n  if (auto p = getenv(\"CODON_PATH\")) {\n    add_search_path(p);\n  }\n  if (!argv0.empty()) {\n    auto root = executable_path(argv0.c_str()).parent_path();\n    for (auto loci : {\"../lib/codon/stdlib\", \"../stdlib\", \"stdlib\"}) {\n      add_search_path(root / loci);\n    }\n    for (auto loci : {\"../lib/codon/plugins\", \"../plugins\"}) {\n      add_search_path(root / loci);\n    }\n  }\n}\n\nstd::vector<std::string> Filesystem::read_lines(const path_t &path) const {\n  std::vector<std::string> lines;\n  if (path == \"-\") {\n    for (std::string line; getline(std::cin, line);) {\n      lines.push_back(line);\n    }\n  } else {\n    std::ifstream fin(path);\n    if (!fin)\n      E(error::Error::COMPILER_NO_FILE, SrcInfo(), path);\n    for (std::string line; getline(fin, line);) {\n      lines.push_back(line);\n    }\n    fin.close();\n  }\n  return lines;\n}\n\nvoid Filesystem::set_module0(const std::string &s) { module0 = canonical(path_t(s)); }\n\nIFilesystem::path_t Filesystem::get_module0() const {\n  return module0.empty() ? IFilesystem::path_t() : canonical(module0);\n}\n\nIFilesystem::path_t Filesystem::executable_path(const char *argv0) {\n  auto exc = llvm::sys::fs::getMainExecutable(\n      argv0, reinterpret_cast<void *>(executable_path));\n  return path_t(exc);\n}\n\nbool Filesystem::exists(const IFilesystem::path_t &path) const {\n  return std::filesystem::exists(path);\n}\n\nResourceFilesystem::ResourceFilesystem(const std::string &argv0,\n                                       const std::string &module0, bool allowExternal)\n    : Filesystem(argv0, module0), allowExternal(allowExternal) {\n  search_paths = {\"/stdlib\"};\n}\n\nstd::vector<std::string> ResourceFilesystem::read_lines(const path_t &path) const {\n  auto fs = cmrc::codon::get_filesystem();\n\n  if (!fs.exists(path) && allowExternal)\n    return Filesystem::read_lines(path);\n\n  std::vector<std::string> lines;\n  if (path == \"-\") {\n    E(error::Error::COMPILER_NO_FILE, SrcInfo(), \"<stdin>\");\n  } else {\n    try {\n      auto fd = fs.open(path);\n      auto contents = std::string(fd.begin(), fd.end());\n      lines = split(contents, '\\n');\n    } catch (std::system_error &) {\n      E(error::Error::COMPILER_NO_FILE, SrcInfo(), path);\n    }\n  }\n  return lines;\n}\n\nbool ResourceFilesystem::exists(const path_t &path) const {\n  auto fs = cmrc::codon::get_filesystem();\n  if (fs.exists(path))\n    return true;\n  if (allowExternal)\n    return Filesystem::exists(path);\n  return false;\n}\n\n/// String and collection utilities\n\nstd::vector<std::string> split(const std::string &s, char delim) {\n  std::vector<std::string> items;\n  std::string item;\n  std::istringstream iss(s);\n  while (std::getline(iss, item, delim))\n    items.push_back(item);\n  return items;\n}\n// clang-format off\nstd::string escape(const std::string &str) {\n  std::string r;\n  r.reserve(str.size());\n  for (unsigned char c : str) {\n    switch (c) {\n    case '\\a': r += \"\\\\a\"; break;\n    case '\\b': r += \"\\\\b\"; break;\n    case '\\f': r += \"\\\\f\"; break;\n    case '\\n': r += \"\\\\n\"; break;\n    case '\\r': r += \"\\\\r\"; break;\n    case '\\t': r += \"\\\\t\"; break;\n    case '\\v': r += \"\\\\v\"; break;\n    case '\\'': r += \"\\\\'\"; break;\n    case '\\\\': r += \"\\\\\\\\\"; break;\n    default:\n      if (c < 32 || c >= 127)\n        r += fmt::format(\"\\\\x{:x}\", c);\n      else\n        r += c;\n    }\n  }\n  return r;\n}\nstd::string unescape(const std::string &str) {\n  std::string r;\n  r.reserve(str.size());\n  for (int i = 0; i < str.size(); i++) {\n    if (str[i] == '\\\\' && i + 1 < str.size())\n      switch(str[i + 1]) {\n      case 'a': r += '\\a'; i++; break;\n      case 'b': r += '\\b'; i++; break;\n      case 'f': r += '\\f'; i++; break;\n      case 'n': r += '\\n'; i++; break;\n      case 'r': r += '\\r'; i++; break;\n      case 't': r += '\\t'; i++; break;\n      case 'v': r += '\\v'; i++; break;\n      case '\"': r += '\\\"'; i++; break;\n      case '\\'': r += '\\''; i++; break;\n      case '\\\\': r += '\\\\'; i++; break;\n      case 'x': {\n        if (i + 3 > str.size())\n          throw std::invalid_argument(\"invalid \\\\x code\");\n        size_t pos = 0;\n        auto code = std::stoi(str.substr(i + 2, 2), &pos, 16);\n        r += static_cast<char>(code);\n        i += pos + 1;\n        break;\n      }\n      default:\n        if (str[i + 1] >= '0' && str[i + 1] <= '7') {\n          size_t pos = 0;\n          auto code = std::stoi(str.substr(i + 1, 3), &pos, 8);\n          r += static_cast<char>(code);\n          i += pos;\n        } else {\n          r += str[i];\n        }\n      }\n    else\n      r += str[i];\n  }\n  return r;\n}\n// clang-format on\nstd::string escapeFStringBraces(const std::string &str, int start, int len) {\n  std::string t;\n  t.reserve(len);\n  for (int i = start; i < start + len; i++)\n    if (str[i] == '{')\n      t += \"{{\";\n    else if (str[i] == '}')\n      t += \"}}\";\n    else\n      t += str[i];\n  return t;\n}\nint findStar(const std::string &s) {\n  int i = 0;\n  for (; i < s.size(); i++) {\n    if (s[i] == '(')\n      return i + 1;\n    if (!isspace(s[i]))\n      return i;\n  }\n  return i;\n}\nbool in(const std::string &m, char item) {\n  auto f = m.find(item);\n  return f != std::string::npos;\n}\nbool in(const std::string &m, const std::string &item) {\n  auto f = m.find(item);\n  return f != std::string::npos;\n}\nsize_t startswith(const std::string &str, const std::string &prefix) {\n  if (prefix.empty())\n    return true;\n  return (str.size() >= prefix.size() && str.substr(0, prefix.size()) == prefix)\n             ? prefix.size()\n             : 0;\n}\nsize_t endswith(const std::string &str, const std::string &suffix) {\n  if (suffix.empty())\n    return true;\n  return (str.size() >= suffix.size() &&\n          str.substr(str.size() - suffix.size()) == suffix)\n             ? suffix.size()\n             : 0;\n}\nvoid ltrim(std::string &str) {\n  str.erase(str.begin(), std::ranges::find_if(\n                             str, [](unsigned char ch) { return !std::isspace(ch); }));\n}\nvoid rtrim(std::string &str) {\n  /// https://stackoverflow.com/questions/216823/whats-the-best-way-to-trim-stdstring\n  str.erase(std::find_if(str.rbegin(), str.rend(),\n                         [](unsigned char ch) { return !std::isspace(ch); })\n                .base(),\n            str.end());\n}\nbool isdigit(const std::string &str) { return std::ranges::all_of(str, ::isdigit); }\n\n// Adapted from https://github.com/gpakosz/whereami/blob/master/src/whereami.c (MIT)\n#ifdef __APPLE__\n#include <dlfcn.h>\n#include <mach-o/dyld.h>\n#endif\nstd::string library_path() {\n  std::string result;\n#ifdef __APPLE__\n  char buffer[PATH_MAX];\n  for (;;) {\n    Dl_info info;\n    if (dladdr(__builtin_extract_return_addr(__builtin_return_address(0)), &info)) {\n      char *resolved = realpath(info.dli_fname, buffer);\n      if (!resolved)\n        break;\n      result = std::string(resolved);\n    }\n    break;\n  }\n#else\n  for (int r = 0; r < 5; r++) {\n    FILE *maps = fopen(\"/proc/self/maps\", \"r\");\n    if (!maps)\n      break;\n\n    for (;;) {\n      char buffer[PATH_MAX < 1024 ? 1024 : PATH_MAX];\n      uint64_t low, high;\n      char perms[5];\n      uint64_t offset;\n      uint32_t major, minor;\n      char path[PATH_MAX];\n      uint32_t inode;\n\n      if (!fgets(buffer, sizeof(buffer), maps))\n        break;\n\n      if (sscanf(buffer, \"%\" PRIx64 \"-%\" PRIx64 \" %s %\" PRIx64 \" %x:%x %u %s\\n\", &low,\n                 &high, perms, &offset, &major, &minor, &inode, path) == 8) {\n        uint64_t addr =\n            (uintptr_t)(__builtin_extract_return_addr(__builtin_return_address(0)));\n        if (low <= addr && addr <= high) {\n          char *resolved = realpath(path, buffer);\n          if (resolved)\n            result = std::string(resolved);\n          break;\n        }\n      }\n    }\n    fclose(maps);\n    if (!result.empty())\n      break;\n  }\n#endif\n\n  return result;\n}\n\nstd::string Filesystem::get_absolute_path(const std::string &path) {\n  char *c = realpath(path.c_str(), nullptr);\n  if (!c)\n    return path;\n  std::string result(c);\n  free(c);\n  return result;\n}\n\nstd::shared_ptr<ImportFile> getImportFile(Cache *cache, const std::string &what,\n                                          const std::string &relativeTo,\n                                          bool forceStdlib) {\n  auto fs = cache->fs.get();\n  std::vector<std::string> paths;\n  auto parentRelativeTo = IFilesystem::path_t(relativeTo).parent_path();\n  if (what != \"<jit>\") {\n    if (!forceStdlib) {\n      auto path = parentRelativeTo / what;\n      path.replace_extension(\"codon\");\n      if (fs->exists(path))\n        paths.emplace_back(fs->canonical(path));\n\n      path = parentRelativeTo / what / \"__init__.codon\";\n      if (fs->exists(path))\n        paths.emplace_back(fs->canonical(path));\n\n      path = parentRelativeTo / what;\n      path.replace_extension(\"py\");\n      if (fs->exists(path))\n        paths.emplace_back(fs->canonical(path));\n\n      path = parentRelativeTo / what / \"__init__.py\";\n      if (fs->exists(path))\n        paths.emplace_back(fs->canonical(path));\n    }\n  }\n\n  auto checkPlugin = [&paths, &fs, &cache](const std::filesystem::path &path,\n                                           const std::string &what) {\n    if (fs->exists(path / what / \"plugin.toml\") &&\n        fs->exists(path / what / \"stdlib\" / what / \"__init__.codon\")) {\n      bool failed = false;\n      if (cache->compiler && !cache->compiler->isPluginLoaded(path / what)) {\n        LOG_REALIZE(\"Loading plugin {}\", path / what);\n        llvm::handleAllErrors(cache->compiler->load(path / what),\n                              [&failed](const codon::error::PluginErrorInfo &e) {\n                                codon::compilationError(e.getMessage(), /*file=*/\"\",\n                                                        /*line=*/0, /*col=*/0,\n                                                        /*len=*/0,\n                                                        /*errorCode=*/-1,\n                                                        /*terminate=*/false);\n                                failed = true;\n                              });\n      }\n      if (!failed)\n        paths.emplace_back(\n            fs->canonical(path / what / \"stdlib\" / what / \"__init__.codon\"));\n    }\n  };\n\n  if (paths.empty()) {\n    // Load a plugin maybe\n    checkPlugin(parentRelativeTo, what);\n  }\n  for (auto &p : fs->get_stdlib_paths()) {\n    auto path = p / what;\n    path.replace_extension(\"codon\");\n    if (fs->exists(path))\n      paths.emplace_back(fs->canonical(path));\n\n    path = p / what / \"__init__.codon\";\n    if (fs->exists(path))\n      paths.emplace_back(fs->canonical(path));\n\n    // Load a plugin maybe\n    checkPlugin(p, what);\n  }\n\n  if (paths.empty())\n    return nullptr;\n  return std::make_shared<ImportFile>(fs->get_root(paths[0]));\n}\n\nstd::string getMangledClass(const std::string &module, const std::string &cls,\n                            size_t id) {\n  if (module == \"std.internal.core\")\n    return cls;\n  std::string num;\n  if (!in(cls, '.'))\n    num = \".\" + std::to_string(id);\n  return (module.empty() ? \"\" : (module + \".\")) + cls + num;\n}\n\nstd::string getMangledFunc(const std::string &module, const std::string &fn,\n                           size_t overload, size_t id) {\n  if (module == \"std.internal.core\")\n    return fn + \":\" + std::to_string(overload);\n  std::string num;\n  if (!in(fn, '.'))\n    num = \".\" + std::to_string(id);\n  return (module.empty() ? \"\" : (module + \".\")) + fn + num + \":\" +\n         std::to_string(overload);\n}\n\nstd::string getMangledMethod(const std::string &module, const std::string &cls,\n                             const std::string &method, size_t overload, size_t id) {\n  if (module == \"std.internal.core\")\n    return cls + \".\" + method + \":\" + std::to_string(overload);\n  std::string num;\n  if (!in(cls, '.'))\n    num = \".\" + std::to_string(id);\n  return (module.empty() ? \"\" : (module + \".\")) + cls + num + \".\" + method + \":\" +\n         std::to_string(overload);\n}\n\nstd::string getMangledVar(const std::string &module, const std::string &var,\n                          size_t id) {\n  std::string num;\n  if (!in(var, '.'))\n    num = \".\" + std::to_string(id);\n  return (module.empty() ? \"\" : (module + \".\")) + var + num;\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/common.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <chrono>\n#include <filesystem>\n#include <map>\n#include <memory>\n#include <set>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"codon/util/common.h\"\n\nnamespace codon {\nnamespace ir {\nstruct Attribute;\n}\n\nnamespace ast {\n\nstruct Cache;\n\n/// String and collection utilities\n\n/// Split a delimiter-separated string into a vector of strings.\nstd::vector<std::string> split(const std::string &str, char delim);\n/// Escape a C string (replace \\n with \\\\n etc.).\nstd::string escape(const std::string &str);\n/// Unescape a C string (replace \\\\n with \\n etc.).\nstd::string unescape(const std::string &str);\n/// Escape an F-string braces (replace { and } with {{ and }}).\nstd::string escapeFStringBraces(const std::string &str, int start, int len);\nint findStar(const std::string &s);\n/// True if a string str starts with a prefix.\nsize_t startswith(const std::string &str, const std::string &prefix);\n/// True if a string str ends with a suffix.\nsize_t endswith(const std::string &str, const std::string &suffix);\n/// Trims whitespace at the beginning of the string.\nvoid ltrim(std::string &str);\n/// Trims whitespace at the end of the string.\nvoid rtrim(std::string &str);\n/// True if a string only contains digits.\nbool isdigit(const std::string &str);\n/// Combine items separated by a delimiter into a string.\n/// Combine items separated by a delimiter into a string.\ntemplate <typename T> std::string join(const T &items, const std::string &delim = \" \") {\n  std::string s;\n  bool first = true;\n  for (const auto &i : items) {\n    if (!first)\n      s += delim;\n    s += i;\n    first = false;\n  }\n  return s;\n}\ntemplate <typename T>\nstd::string join(const T &items, const std::string &delim, size_t start,\n                 size_t end = (1ull << 31)) {\n  std::string s;\n  if (end > items.size())\n    end = items.size();\n  for (int i = start; i < end; i++)\n    s += (i > start ? delim : \"\") + items[i];\n  return s;\n}\n/// Combine items separated by a delimiter into a string.\ntemplate <typename T>\nstd::string combine(const std::vector<T> &items, const std::string &delim = \" \",\n                    const int indent = -1) {\n  std::string s;\n  for (int i = 0; i < items.size(); i++)\n    if (items[i])\n      s += (i ? delim : \"\") + items[i]->toString(indent);\n  return s;\n}\ntemplate <typename T>\nstd::string combine2(const std::vector<T> &items, const std::string &delim = \",\",\n                     int start = 0, int end = -1) {\n  std::string s;\n  if (end == -1)\n    end = items.size();\n  for (int i = start; i < end; i++)\n    s += (i ? delim : \"\") + fmt::format(\"{}\", items[i]);\n  return s;\n}\n/// @return True if an item is found in a vector vec.\ntemplate <typename T, typename U>\nconst T *in(const std::vector<T> &vec, const U &item, size_t start = 0) {\n  auto f = std::find(vec.begin() + start, vec.end(), item);\n  return f != vec.end() ? &(*f) : nullptr;\n}\n/// @return True if an item is found in a set s.\ntemplate <typename T, typename U> const T *in(const std::set<T> &s, const U &item) {\n  auto f = s.find(item);\n  return f != s.end() ? &(*f) : nullptr;\n}\n/// @return True if an item is found in an unordered_set s.\ntemplate <typename T, typename U>\nconst T *in(const std::unordered_set<T> &s, const U &item) {\n  auto f = s.find(item);\n  return f != s.end() ? &(*f) : nullptr;\n}\n/// @return True if an item is found in a map m.\ntemplate <typename K, typename V, typename U>\nconst V *in(const std::map<K, V> &m, const U &item) {\n  auto f = m.find(item);\n  return f != m.end() ? &(f->second) : nullptr;\n}\n/// @return True if an item is found in an unordered_map m.\ntemplate <typename K, typename V, typename U>\nconst V *in(const std::unordered_map<K, V> &m, const U &item) {\n  auto f = m.find(item);\n  return f != m.end() ? &(f->second) : nullptr;\n}\n/// @return True if an item is found in an unordered_map m.\ntemplate <typename K, typename V, typename U>\nV *in(std::unordered_map<K, V> &m, const U &item) {\n  auto f = m.find(item);\n  return f != m.end() ? &(f->second) : nullptr;\n}\n/// @return True if an item is found in an string m.\nbool in(const std::string &m, const std::string &item);\nbool in(const std::string &m, char item);\n\n/// AST utilities\n\ntemplate <typename T> T clone(const T &t, bool clean = false) { return t.clone(clean); }\n\ntemplate <typename T> std::remove_const_t<T> *clone(T *t, bool clean = false) {\n  return t ? static_cast<std::remove_const_t<T> *>(t->clone(clean)) : nullptr;\n}\n\ntemplate <typename T> std::remove_const_t<T> *clean_clone(T *t) {\n  return clone(t, true);\n}\n\n/// Clones a vector of cloneable pointer objects.\ntemplate <typename T>\nstd::vector<std::remove_const_t<T>> clone(const std::vector<T> &t, bool clean = false) {\n  std::vector<std::remove_const_t<T>> v;\n  for (auto &i : t)\n    v.push_back(clone(i, clean));\n  return v;\n}\n\n/// Path utilities\n\n/// Detect an absolute path of the current libcodonc.\n/// @return Absolute executable path or argv0 if one cannot be found.\nstd::string library_path();\n\nstruct ImportFile {\n  enum Status { STDLIB, PACKAGE };\n  Status status;\n  /// Absolute path of an import.\n  std::string path;\n  /// Module name (e.g. foo.bar.baz).\n  std::string module;\n};\n\nclass IFilesystem {\npublic:\n  using path_t = std::filesystem::path;\n\nprotected:\n  std::vector<path_t> search_paths;\n\npublic:\n  virtual ~IFilesystem() {};\n\n  virtual std::vector<std::string> read_lines(const path_t &path) const = 0;\n  virtual bool exists(const path_t &path) const = 0;\n  virtual std::vector<path_t> get_stdlib_paths() const;\n  virtual path_t canonical(const path_t &path) const;\n  ImportFile get_root(const path_t &s) const;\n\n  virtual path_t get_module0() const { return \"\"; }\n  virtual void set_module0(const std::string &) {}\n  virtual void add_search_path(const std::string &p);\n};\n\nclass Filesystem : public IFilesystem {\npublic:\n  using IFilesystem::path_t;\n\nprivate:\n  path_t argv0, module0;\n  std::vector<path_t> extraPaths;\n\npublic:\n  Filesystem(const std::string &argv0, const std::string &module0 = \"\");\n  std::vector<std::string> read_lines(const path_t &path) const override;\n  bool exists(const path_t &path) const override;\n\n  path_t get_module0() const override;\n  void set_module0(const std::string &s) override;\n\npublic:\n  /// Detect an absolute path of the current executable (whose argv0 is known).\n  /// @return Absolute executable path or argv0 if one cannot be found.\n  static path_t executable_path(const char *argv0);\n\n  /// @return The absolute canonical path of a given path.\n  static std::string get_absolute_path(const std::string &path);\n};\n\nclass ResourceFilesystem : public Filesystem {\n  bool allowExternal;\n\npublic:\n  ResourceFilesystem(const std::string &argv0, const std::string &module0 = \"\",\n                     bool allowExternal = true);\n  std::vector<std::string> read_lines(const path_t &path) const override;\n  bool exists(const path_t &path) const override;\n};\n\n/// Find an import file what given an executable path (argv0) either in the standard\n/// library or relative to a file relativeTo. Set forceStdlib for searching only the\n/// standard library.\nstd::shared_ptr<ImportFile> getImportFile(Cache *cache, const std::string &what,\n                                          const std::string &relativeTo,\n                                          bool forceStdlib = false);\n\ntemplate <typename T> class SetInScope {\n  T *t;\n  T origVal;\n\npublic:\n  SetInScope(T *t, const T &val) : t(t), origVal(*t) { *t = val; }\n  ~SetInScope() { *t = origVal; }\n};\n\nstd::string getMangledClass(const std::string &module, const std::string &cls,\n                            size_t id = 0);\n\nstd::string getMangledFunc(const std::string &module, const std::string &fn,\n                           size_t overload = 0, size_t id = 0);\n\nstd::string getMangledMethod(const std::string &module, const std::string &cls,\n                             const std::string &method, size_t overload = 0,\n                             size_t id = 0);\n\nstd::string getMangledVar(const std::string &module, const std::string &var,\n                          size_t id = 0);\n\n} // namespace ast\n} // namespace codon\n"
  },
  {
    "path": "codon/parser/ctx.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <deque>\n#include <list>\n#include <memory>\n#include <stack>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"codon/parser/ast.h\"\n\nnamespace codon::ast {\n\n/**\n * A variable table (transformation context).\n * Base class that holds a list of existing identifiers and their block hierarchy.\n * @tparam T Variable type.\n */\ntemplate <typename T> class Context : public std::enable_shared_from_this<Context<T>> {\npublic:\n  using Item = std::shared_ptr<T>;\n\nprotected:\n  using Map = std::unordered_map<std::string, std::list<Item>>;\n  /// Maps a identifier to a stack of objects that share the same identifier.\n  /// Each object is represented by a nesting level and a pointer to that object.\n  /// Top of the stack is the current block; the bottom is the outer-most block.\n  /// Stack is represented as std::deque to allow iteration and access to the outer-most\n  /// block.\n  Map map;\n  /// Stack of blocks and their corresponding identifiers. Top of the stack is the\n  /// current block.\n  std::deque<std::list<std::string>> stack;\n\nprivate:\n  /// Set of current context flags.\n  std::unordered_set<std::string> flags;\n  /// The absolute path of the current module.\n  std::string filename;\n  /// SrcInfo stack used for obtaining source information of the current expression.\n  std::vector<ASTNode *> nodeStack;\n\npublic:\n  explicit Context(std::string filename) : filename(std::move(filename)) {\n    /// Add a top-level block to the stack.\n    stack.push_front(std::list<std::string>());\n  }\n  virtual ~Context() = default;\n\n  /// Add an object to the top of the stack.\n  virtual void add(const std::string &name, const Item &var) {\n    seqassertn(!name.empty(), \"adding an empty identifier\");\n    map[name].push_front(var);\n    stack.front().push_back(name);\n  }\n  /// Remove the top-most object with a given identifier.\n  void remove(const std::string &name) {\n    removeFromMap(name);\n    for (auto &s : stack) {\n      auto i = std::ranges::find(s, name);\n      if (i != s.end()) {\n        s.erase(i);\n        return;\n      }\n    }\n  }\n  /// Return a top-most object with a given identifier or nullptr if it does not exist.\n  virtual Item find(const std::string &name) const {\n    auto it = map.find(name);\n    return it != map.end() ? it->second.front() : nullptr;\n  }\n  /// Return all objects that share a common identifier or nullptr if it does not exist.\n  virtual std::list<Item> *find_all(const std::string &name) {\n    auto it = map.find(name);\n    return it != map.end() ? &(it->second) : nullptr;\n  }\n  /// Add a new block (i.e. adds a stack level).\n  virtual void addBlock() { stack.push_front(std::list<std::string>()); }\n  /// Remove the top-most block and all variables it holds.\n  virtual void popBlock() {\n    for (auto &name : stack.front())\n      removeFromMap(name);\n    stack.pop_front();\n  }\n\n  void removeFromTopStack(const std::string &name) {\n    auto it = std::ranges::find(stack.front(), name);\n    if (it != stack.front().end())\n      stack.front().erase(it);\n  }\n\n  /// The absolute path of a current module.\n  std::string getFilename() const { return filename; }\n  /// Sets the absolute path of a current module.\n  void setFilename(std::string file) { filename = std::move(file); }\n\n  /// Convenience functions to allow range-based for loops over a context.\n  typename Map::iterator begin() { return map.begin(); }\n  typename Map::iterator end() { return map.end(); }\n\n  /// Pretty-prints the current context state.\n  virtual void dump() {}\n\nprotected:\n  /// Remove an identifier from the map only.\n  virtual void removeFromMap(const std::string &name) {\n    auto i = map.find(name);\n    if (i == map.end())\n      return;\n    seqassertn(i->second.size(), \"identifier {} not found in the map\", name);\n    i->second.pop_front();\n    if (!i->second.size())\n      map.erase(name);\n  }\n\npublic:\n  /* SrcInfo helpers */\n  void pushNode(ASTNode *n) { nodeStack.emplace_back(n); }\n  void popNode() { nodeStack.pop_back(); }\n  ASTNode *getLastNode() const { return nodeStack.back(); }\n  ASTNode *getParentNode() const {\n    assert(nodeStack.size() > 1);\n    return nodeStack[nodeStack.size() - 2];\n  }\n  SrcInfo getSrcInfo() const { return nodeStack.back()->getSrcInfo(); }\n  size_t getStackSize() const { return stack.size(); }\n};\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/match.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"codon/parser/match.h\"\n\nnamespace codon::matcher {\n\nmatch_zero_or_more_t MAny() { return match_zero_or_more_t(); }\n\nmatch_startswith_t MStarts(std::string s) { return match_startswith_t{std::move(s)}; }\n\nmatch_endswith_t MEnds(std::string s) { return match_endswith_t{std::move(s)}; }\n\nmatch_contains_t MContains(std::string s) { return match_contains_t{std::move(s)}; }\n\ntemplate <> bool match(const char *c, const char *d) {\n  return std::string(c) == std::string(d);\n}\n\ntemplate <> bool match(const char *c, std::string d) { return std::string(c) == d; }\n\ntemplate <> bool match(std::string c, const char *d) { return std::string(d) == c; }\n\ntemplate <> bool match(double &a, double b) {\n  return std::abs(a - b) < __FLT_EPSILON__;\n}\n\ntemplate <> bool match(std::string s, match_startswith_t m) {\n  return m.s.size() <= s.size() && s.substr(0, m.s.size()) == m.s;\n}\n\ntemplate <> bool match(std::string s, match_endswith_t m) {\n  return m.s.size() <= s.size() && s.substr(s.size() - m.s.size(), m.s.size()) == m.s;\n}\n\ntemplate <> bool match(std::string s, match_contains_t m) {\n  return s.find(m.s) != std::string::npos;\n}\n\ntemplate <> bool match(const char *s, match_startswith_t m) {\n  return match(std::string(s), m);\n}\n\ntemplate <> bool match(const char *s, match_endswith_t m) {\n  return match(std::string(s), m);\n}\n\ntemplate <> bool match(const char *s, match_contains_t m) {\n  return match(std::string(s), m);\n}\n\n} // namespace codon::matcher\n"
  },
  {
    "path": "codon/parser/match.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <string>\n\n#include \"codon/cir/base.h\"\n\nnamespace codon::matcher {\n\ntemplate <typename T, typename... MA> struct match_t {\n  std::tuple<MA...> args;\n  std::function<void(T &)> fn;\n  match_t(MA... args, std::function<void(T &)> fn)\n      : args(std::tuple<MA...>(args...)), fn(fn) {}\n};\n\ntemplate <typename... MA> struct match_or_t {\n  std::tuple<MA...> args;\n  match_or_t(MA... args) : args(std::tuple<MA...>(args...)) {}\n};\n\nstruct match_ignore_t {};\n\nstruct match_zero_or_more_t {};\n\nstruct match_startswith_t {\n  std::string s;\n};\n\nstruct match_endswith_t {\n  std::string s;\n};\n\nstruct match_contains_t {\n  std::string s;\n};\n\ntemplate <typename T, typename... TA> match_t<T, TA...> M(TA... args) {\n  return match_t<T, TA...>(args..., nullptr);\n}\n\ntemplate <typename T, typename... TA>\nmatch_t<T, TA...> MCall(TA... args, std::function<void(T &)> fn) {\n  return match_t<T, TA...>(args..., fn);\n}\n\ntemplate <typename T, typename... TA> match_t<T, TA...> MVar(TA... args, T &tp) {\n  return match_t<T, TA...>(args..., [&tp](T &t) { tp = t; });\n}\n\ntemplate <typename T, typename... TA> match_t<T, TA...> MVar(TA... args, T *&tp) {\n  return match_t<T, TA...>(args..., [&tp](T &t) { tp = &t; });\n}\n\ntemplate <typename... TA> match_or_t<TA...> MOr(TA... args) {\n  return match_or_t<TA...>(args...);\n}\n\nmatch_zero_or_more_t MAny();\n\nmatch_startswith_t MStarts(std::string s);\n\nmatch_endswith_t MEnds(std::string s);\n\nmatch_contains_t MContains(std::string s);\n\n//////////////////////////////////////////////////////////////////////////////\n\ntemplate <class T, class M> bool match(T t, M m) {\n  if constexpr (std::is_same_v<T, M>)\n    return t == m;\n  return false;\n}\n\ntemplate <class T> bool match(T &t, match_ignore_t) { return true; }\n\ntemplate <class T> bool match(T &t, match_zero_or_more_t) { return true; }\n\ntemplate <> bool match(const char *c, const char *d);\n\ntemplate <> bool match(const char *c, std::string d);\n\ntemplate <> bool match(std::string c, const char *d);\n\ntemplate <> bool match(double &a, double b);\n\ntemplate <> bool match(std::string s, match_startswith_t m);\n\ntemplate <> bool match(std::string s, match_endswith_t m);\n\ntemplate <> bool match(std::string s, match_contains_t m);\n\ntemplate <> bool match(const char *s, match_startswith_t m);\n\ntemplate <> bool match(const char *s, match_endswith_t m);\n\ntemplate <> bool match(const char *s, match_contains_t m);\n\ntemplate <int i, typename T, typename TM> bool match_help(T &t, TM m) {\n  if constexpr (i == std::tuple_size_v<decltype(m.args)>) {\n    return i == std::tuple_size_v<decltype(t.match_members())>;\n  } else if constexpr (i < std::tuple_size_v<decltype(m.args)>) {\n    if constexpr (std::is_same_v<std::remove_reference_t<decltype(std::get<i>(m.args))>,\n                                 match_zero_or_more_t>) {\n      return true;\n    }\n    return match(std::get<i>(t.match_members()), std::get<i>(m.args)) &&\n           match_help<i + 1>(t, m);\n  } else {\n    return false;\n  }\n}\n\ntemplate <int i, typename T, typename... TO>\nbool match_or_help(T &t, match_or_t<TO...> m) {\n  if constexpr (i >= 0 && i < std::tuple_size_v<decltype(m.args)>) {\n    return match(t, std::get<i>(m.args)) || match_or_help<i + 1>(t, m);\n  } else {\n    return false;\n  }\n}\n\ntemplate <typename TM, typename... TA> bool match(TM &t, match_or_t<TA...> m) {\n  return match_or_help<0, TM, TA...>(t, m);\n}\n\ntemplate <typename TM, typename... TA> bool match(TM *t, match_or_t<TA...> m) {\n  return match_or_help<0, TM *, TA...>(t, m);\n}\n\ntemplate <typename T, typename TM, typename... TA>\nbool match(T &t, match_t<TM, TA...> m) {\n  if constexpr (std::is_pointer_v<T>) {\n    TM *tm = ir::cast<TM>(t);\n    if (!tm)\n      return false;\n    if constexpr (sizeof...(TA) == 0) {\n      if (m.fn)\n        m.fn(*tm);\n      return true;\n    } else {\n      auto r = match_help<0>(*tm, m);\n      if (r && m.fn)\n        m.fn(*tm);\n      return r;\n    }\n  } else {\n    if constexpr (!std::is_same_v<T, TM>)\n      return false;\n    if constexpr (sizeof...(TA) == 0) {\n      if (m.fn)\n        m.fn(t);\n      return true;\n    } else {\n      auto r = match_help<0>(t, m);\n      if (r && m.fn)\n        m.fn(t);\n      return r;\n    }\n  }\n}\n\ntemplate <typename T, typename TM, typename... TA>\nbool match(T *t, match_t<TM, TA...> m) {\n  return match<T *, TM, TA...>(t, m);\n}\n\n} // namespace codon::matcher\n\n#define M_ matcher::match_ignore_t()\n"
  },
  {
    "path": "codon/parser/peg/grammar.peg",
    "content": "# Copyright (C) 2022 Exaloop Inc. <https://exaloop.io>\n# Codon PEG grammar\n# Adopted from Python 3's PEG grammar (https://docs.python.org/3/reference/grammar.html)\n\n# TODO: nice docstrs\n\nPREAMBLE {\n  #include \"codon/parser/peg/rules.h\"\n  #include <any>\n  using namespace std;\n  using namespace codon::ast;\n\n  #define V0 VS[0]\n  #define V1 VS[1]\n  #define V2 VS[2]\n  #define ac std::any_cast\n  #define ac_expr std::any_cast<Expr*>\n  #define ac_stmt std::any_cast<Stmt*>\n  #define SemVals peg::SemanticValues\n\n  #define aste(T, s, ...) setSI<Expr>(CTX.cache->N<T ## Expr>(__VA_ARGS__), s)\n  #define asts(T, s, ...) setSI<Stmt>(CTX.cache->N<T ## Stmt>(__VA_ARGS__), s)\n\n  template<typename T>\n  T *setSI(ASTNode *n, const codon::SrcInfo &s) {\n    n->setSrcInfo(s);\n    return (T*)n;\n  }\n\n  /// @return vector c transformed by the function f.\n  template <typename T, typename F> auto vmap(const std::vector<T> &c, F &&f) {\n    using VT = std::decay_t<decltype(std::forward<F>(f)(*std::begin(c)))>;\n    std::vector<VT> ret;\n    std::transform(std::begin(c), std::end(c), std::inserter(ret, std::end(ret)), f);\n    return ret;\n  }\n  template<typename F> auto vmap(const peg::SemanticValues &c, F &&f) {\n    return vmap(static_cast<const std::vector<std::any>&>(c), f);\n  }\n  Expr *chain(const codon::ast::ParseContext &CTX, peg::SemanticValues &VS, const codon::SrcInfo &LOC) {\n    Expr *b = ac_expr(V0);\n    for (int i = 1; i < VS.size(); i++)\n      b = aste(Binary, LOC, b, VS.token_to_string(i - 1), ac_expr(VS[i]));\n    return b;\n  }\n  Expr *wrap_tuple(const codon::ast::ParseContext &CTX, peg::SemanticValues &VS, const codon::SrcInfo &LOC) {\n    if (VS.size() == 1 && VS.tokens.empty())\n      return ac_expr(V0);\n    return aste(Tuple, LOC, VS.transform<Expr*>());\n  }\n}\n\nprogram <- (statements (_ EOL)* / (_ EOL)*) !. {\n  if (VS.empty())\n    return asts(Suite, LOC);\n  return ac_stmt(V0);\n}\n\nfstring <- fstring_prefix _ fstring_tail? _ !. {\n  StringExpr::FormatSpec fs {\"\", \"\", \"\"};\n  auto [e, t] = ac<std::pair<Expr *, string>>(V0);\n  fs.text = t;\n  if (VS.size() > 1) {\n    auto [c, s] = ac<std::pair<string, string>>(V1);\n    fs.conversion = c;\n    fs.spec = s;\n  }\n  return make_pair(e, fs);\n}\nfstring_prefix <- star_expressions _ '='? {\n  auto text = VS.sv();\n  return make_pair(ac_expr(V0),\n                   string(!VS.sv().empty() && VS.sv().back() == '=' ? VS.sv() : \"\"));\n}\nfstring_tail <-\n  / fstring_conversion fstring_spec? {\n    return make_pair(ac<string>(V0), VS.size() > 1 ? ac<string>(V1) : \"\");\n  }\n  / fstring_spec { return make_pair(std::string(), ac<string>(V0)); }\nfstring_conversion <- \"!\" (\"s\" / \"r\" / \"a\") { return string(VS.sv().substr(1)); }\nfstring_spec <- ':' format_spec { return ac<string>(V0); }\n\n# Macros\nlist(c, e)  <- e (_ c _ e)*\ntlist(c, e) <- e (_ c _ e)* (_ <c>)?\n\nstatements <- ((_ EOL)* statement)+ {\n  auto s = asts(Suite, LOC, VS.transform<Stmt*>());\n  cast<SuiteStmt>(s)->flatten();\n  return s;\n}\nstatement <- SAMEDENT compound_stmt / SAMEDENT simple_stmt\nsimple_stmt <- tlist(';', small_stmt) _ EOL {\n  auto s = asts(Suite, LOC, VS.transform<Stmt*>());\n  cast<SuiteStmt>(s)->flatten();\n  return s;\n}\nsmall_stmt <-\n  / directive\n  / assignment\n  / 'pass' &(SPACE / ';' / EOL) { return any(asts(Suite, LOC)); }\n  / 'break' &(SPACE / ';' / EOL) { return any(asts(Break, LOC)); }\n  / 'continue' &(SPACE / ';' / EOL) { return any(asts(Continue, LOC)); }\n  / global_stmt\n  / nonlocal_stmt\n  / yield_stmt &(SPACE / ';' / EOL)\n  / assert_stmt\n  / del_stmt\n  / return_stmt &(SPACE / ';' / EOL)\n  / raise_stmt &(SPACE / ';' / EOL)\n  / print_stmt\n  / import_stmt\n  / expressions &(_ ';' / _ EOL)  { return any(asts(Expr, LOC, ac_expr(V0))); }\n  / custom_small_stmt\n\nassignment <-\n  / id _ ':' _ expression (_ '=' _ star_expressions)? {\n    return asts(Assign, LOC,\n      ac_expr(V0), VS.size() > 2 ? ac_expr(V2) : nullptr, ac_expr(V1)\n    );\n  }\n  / primary _ ':' _ expression (_ '=' _ star_expressions)? {\n    return asts(Assign, LOC,\n      ac_expr(V0),\n      VS.size() > 2 ? ac_expr(V2) : nullptr,\n      ac_expr(V1)\n    );\n  }\n  / (star_targets _ (!'==' '=') _)+ star_expressions !(_ '=') {\n    vector<Stmt*> stmts;\n    for (int i = int(VS.size()) - 2; i >= 0; i--) {\n      auto a = asts(Assign, LOC, ac_expr(VS[i]), ac_expr(VS[i + 1]));\n      stmts.push_back(a);\n    }\n    return asts(Suite, LOC, std::move(stmts));\n  }\n  / star_expression _ augassign '=' ^ _ star_expressions {\n    auto a = asts(Assign, LOC, ac_expr(V0), aste(Binary, LOC, clone(ac_expr(V0)), ac<string>(V1), ac_expr(V2), true));\n    return a;\n  }\naugassign <- <\n  '+' / '-' / '**' / '*' / '@' / '//' / '/' / '%' / '&' / '|' / '^' / '<<' / '>>'\n> {\n  return VS.token_to_string();\n}\nglobal_stmt <- 'global' SPACE tlist(',', NAME) {\n  return asts(Suite, LOC,\n    vmap(VS, [&](const any &i) { return asts(Global, LOC, ac<string>(i), false); })\n  );\n}\nnonlocal_stmt <- 'nonlocal' SPACE tlist(',', NAME) {\n  return asts(Suite, LOC,\n    vmap(VS, [&](const any &i) { return asts(Global, LOC, ac<string>(i), true); })\n  );\n}\nyield_stmt <-\n  / 'yield' SPACE 'from' SPACE expression { return asts(YieldFrom, LOC, ac_expr(V0)); }\n  / 'yield' (SPACE expressions)? {\n    return asts(Yield, LOC, !VS.empty() ? ac_expr(V0) : nullptr);\n  }\nassert_stmt <- 'assert' SPACE expression (_ ',' _ expression)? {\n  return asts(Assert, LOC, ac_expr(V0), VS.size() > 1 ? ac_expr(V1) : nullptr);\n}\n\n# TODO: do targets as in Python\ndel_stmt <- 'del' SPACE tlist(',', expression) {\n  return asts(Suite, LOC,\n    vmap(VS, [&](const any &i) { return asts(Del, LOC, ac_expr(i)); })\n  );\n}\nreturn_stmt <- 'return' (SPACE expressions)? {\n  return asts(Return, LOC, !VS.empty() ? ac_expr(V0) : nullptr);\n}\nraise_stmt <-\n  / 'raise' SPACE expression (SPACE 'from' SPACE expression)? {\n    return asts(Throw, LOC, ac_expr(V0), VS.size() > 1 ? ac_expr(V1) : nullptr);\n  }\n  / 'raise' { return asts(Throw, LOC, nullptr); }\nprint_stmt <-\n  / 'print' SPACE star_expression (_ ',' _ star_expression)* (_ <','>)? {\n    return asts(Print, LOC, VS.transform<Expr*>(), !VS.tokens.empty());\n  }\n  / 'print' _ &EOL { return asts(Print, LOC, vector<Expr*>{}, false); }\nimport_stmt <- import_name / import_from\nimport_name <- 'import' SPACE list(',', as_name) {\n  return asts(Suite, LOC,\n    vmap(VS.transform<pair<Expr*, string>>(), [&](const pair<Expr*, string> &i) {\n      return asts(Import, LOC, i.first, nullptr, vector<Param>{}, nullptr, i.second);\n    })\n  );\n}\nas_name <- dot_name (SPACE 'as' SPACE NAME)? {\n  return pair(ac_expr(V0), VS.size() > 1 ? ac<string>(V1) : \"\");\n}\nimport_from <-\n  / 'from' SPACE (_ <'.'>)* (_ dot_name)? SPACE 'import' SPACE '*' {\n    return asts(Import, LOC,\n      VS.size() == 1 ? ac_expr(V0) : nullptr, aste(Id, LOC, \"*\"), vector<Param>{},\n      nullptr, \"\", int(VS.tokens.size())\n    );\n  }\n  / 'from' SPACE (_ <'.'>)* (_ dot_name)? SPACE 'import' SPACE\n    (from_as_parens / from_as_items) {\n    auto f = VS.size() == 2 ? ac_expr(V0) : nullptr;\n    return asts(Suite, LOC,\n      vmap(\n        ac<SemVals>(VS.size() == 2 ? V1 : V0),\n        [&](const any &i) {\n          auto p = ac<pair<any, string>>(i);\n          auto t = ac<tuple<Expr*, vector<Param>, Expr*, bool>>(p.first);\n          return asts(Import, LOC,\n            f, get<0>(t), std::move(get<1>(t)), get<2>(t), p.second, int(VS.tokens.size()), get<3>(t)\n          );\n        }\n      )\n    );\n  }\nfrom_as_parens <- '(' _ tlist(',', from_as) _ ')' { return VS; }\nfrom_as_items <- list(',', from_as) { return VS; }\nfrom_as <- from_id (SPACE 'as' SPACE NAME)? {\n  return pair(V0, VS.size() > 1 ? ac<string>(V1) : \"\");\n}\nfrom_id <-\n  / dot_name _ ':' _ expression {\n    return tuple(ac_expr(V0), vector<Param>(), ac_expr(V1), false);\n  }\n  / dot_name _ from_params (_ '->' _ expression)? {\n    return tuple(\n      ac_expr(V0),\n      ac<SemVals>(V1).transform<Param>(),\n      VS.size() > 2 ? ac_expr(V2) : aste(Id, LOC, \"NoneType\"),\n      true\n    );\n  }\n  / dot_name { return tuple(ac_expr(V0), vector<Param>{}, (Expr*)nullptr, true); }\ndot_name <- id (_ '.' _ NAME)* {\n  if (VS.size() == 1)\n    return ac_expr(V0);\n  auto dot = aste(Dot, LOC, ac_expr(V0), ac<string>(V1));\n  for (int i = 2; i < VS.size(); i++)\n    dot = aste(Dot, LOC, dot, ac<string>(VS[i]));\n  return dot;\n}\nfrom_params <- '(' _ tlist(',', from_param)? _ ')' { return VS; }\nfrom_param <- expression { return Param(LOC, \"\", ac_expr(V0), nullptr); }\n#TODO expand import logic / param { return ac<Param>(V0); }\n\nsuite <- (simple_stmt / (_ EOL)+ &INDENT statements (_ EOL)* &DEDENT) {\n  return ac_stmt(V0);\n}\ncompound_stmt <-\n  / function\n  / if_stmt\n  / class\n  / with_stmt_async\n  / for\n  / try_stmt\n  / while_stmt\n  / match_stmt\n  / custom_stmt\nif_stmt <- ('if' SPACE named_expression _ ':' _ suite)\n           (SAMEDENT 'elif' SPACE named_expression _ ':' _ suite)*\n           (SAMEDENT 'else' _ ':' _ suite)? {\n  Stmt *lastElse = VS.size() % 2 == 0 ? nullptr : SuiteStmt::wrap(ac_stmt(VS.back()));\n  for (size_t i = VS.size() - bool(lastElse); i-- > 0; ) {\n    lastElse = asts(If, LOC, ac_expr(VS[i - 1]), SuiteStmt::wrap(ac_stmt(VS[i])), SuiteStmt::wrap(lastElse));\n    i--;\n  }\n  return lastElse;\n}\nwhile_stmt <- ('while' SPACE named_expression _ ':' _ suite)\n              (SAMEDENT 'else' (SPACE 'not' SPACE 'break')*  _ ':' _ suite)?  {\n  return asts(While, LOC,\n    ac_expr(V0), ac_stmt(V1), VS.size() > 2 ? ac_stmt(V2) : nullptr\n  );\n}\nfor <- decorator? for_stmt_async {\n  if (VS.size() > 1) {\n    auto s = (ForStmt*)(ac_stmt(V1));\n    s->setDecorator(ac_expr(V0));\n    return (Stmt*)s;\n  }\n  return ac_stmt(V0);\n}\nfor_stmt_async <-\n  / 'async' SPACE for_stmt {\n    auto s = (ForStmt*)(ac_stmt(V0));\n    s->setAsync();\n    return make_any<Stmt*>(s);\n  }\n  / for_stmt { return V0; }\nfor_stmt <- ('for' SPACE star_targets)\n            (SPACE 'in' SPACE star_expressions _ ':' _ suite)\n            (SAMEDENT 'else' (SPACE 'not' SPACE 'break')* _ ':' _ suite)? {\n  return asts(For, LOC,\n    ac_expr(V0), ac_expr(V1), ac_stmt(V2), VS.size() > 3 ? ac_stmt(VS[3]) : nullptr\n  );\n}\nwith_stmt_async <-\n  / 'async' SPACE with_stmt {\n    auto s = (WithStmt*)(ac_stmt(V0));\n    s->setAsync();\n    return make_any<Stmt*>(s);\n  }\n  / with_stmt { return V0; }\nwith_stmt <- 'with' SPACE (with_parens_item / with_item) _ ':' _ suite {\n  return asts(With, LOC,\n    ac<SemVals>(V0).transform<pair<Expr*, Expr*>>(), ac_stmt(V1), false\n  );\n}\nwith_parens_item <- '(' _ tlist(',', as_item) _ ')' { return VS; }\nwith_item <- list(',', as_item) { return VS; }\nas_item <-\n  / expression SPACE 'as' SPACE id &(_ (',' / ')' / ':'))  {\n    return pair(ac_expr(V0), ac_expr(V1));\n  }\n  / expression { return pair(ac_expr(V0), (Expr*)nullptr); }\n# TODO: else block?\ntry_stmt <-\n  / ('try' _ ':' _ suite) excepts else_finally? {\n    std::pair<Stmt*, Stmt*> ef {nullptr, nullptr};\n    if (VS.size() > 2) ef = ac<std::pair<Stmt *, Stmt *>>(V2);\n    return asts(Try, LOC,\n      ac_stmt(V0),\n      ac<SemVals>(V1).transform<ExceptStmt*>(),\n      ef.first, ef.second\n    );\n  }\n  / ('try' _ ':' _ suite) (SAMEDENT 'finally' _ ':' _ suite)? {\n    return asts(Try, LOC,\n      ac_stmt(V0), vector<ExceptStmt*>{}, nullptr,\n      VS.size() > 1 ? ac_stmt(V1) : nullptr\n    );\n  }\nelse_finally <-\n  / SAMEDENT 'else' _ ':' _ suite\n    SAMEDENT 'finally' _ ':' _ suite { return std::pair<Stmt *, Stmt *>(ac_stmt(V0), ac_stmt(V1)); }\n  / SAMEDENT 'else' _ ':' _ suite { return std::pair<Stmt *, Stmt *>(ac_stmt(V0), nullptr); }\n  / SAMEDENT 'finally' _ ':' _ suite { return std::pair<Stmt *, Stmt *>(nullptr, ac_stmt(V0)); }\nexcepts <- (SAMEDENT except_block)+ { return VS; }\nexcept_block <-\n  / 'except' SPACE expression (SPACE 'as' SPACE NAME)? _ ':' _ suite {\n    if (VS.size() == 3)\n      return setSI<ExceptStmt>(CTX.cache->N<ExceptStmt>(ac<string>(V1), ac_expr(V0), ac_stmt(V2)), LOC);\n    else\n      return setSI<ExceptStmt>(CTX.cache->N<ExceptStmt>(\"\", ac_expr(V0), ac_stmt(V1)), LOC);\n  }\n  / 'except' _ ':' _ suite { return setSI<ExceptStmt>(CTX.cache->N<ExceptStmt>(\"\", nullptr, ac_stmt(V0)), LOC); }\nfunction <-\n  / extern_decorators function_def_async (_ EOL)+ &INDENT extern (_ EOL)* &DEDENT {\n    auto fn = (FunctionStmt*)(ac_stmt(V1));\n    fn->setDecorators(ac<vector<Expr*>>(V0));\n    fn->setSuite(SuiteStmt::wrap(asts(Expr, LOC, aste(String, LOC, ac<string>(V2)))));\n    return (Stmt*)fn;\n  }\n  / decorators? function_def_async _ suite {\n    auto fn = (FunctionStmt*)(ac_stmt(VS.size() > 2 ? V1 : V0));\n    if (VS.size() > 2)\n      fn->setDecorators(ac<vector<Expr*>>(V0));\n    fn->setSuite(SuiteStmt::wrap(ac_stmt(VS.size() > 2 ? V2 : V1)));\n    return (Stmt*)fn;\n  }\nextern <- (empty_line* EXTERNDENT (!EOL .)* EOL empty_line*)+ {\n  return string(VS.sv());\n}\n~empty_line <- [ \\t]* EOL\nfunction_def_async <-\n  / 'async' SPACE function_def {\n    auto fn = (FunctionStmt*)(ac_stmt(V0));\n    fn->setAsync();\n    return make_any<Stmt*>(fn);\n  }\n  / function_def { return V0; }\nfunction_def <-\n  / 'def' SPACE NAME _ generics _ params (_ '->' _ expression)? _ ':' {\n    auto params = ac<SemVals>(V2).transform<Param>();\n    for (auto &p: ac<vector<Param>>(V1))\n      params.push_back(p);\n    return asts(Function, LOC,\n      ac<string>(V0),\n      VS.size() == 4 ? ac_expr(VS[3]) : nullptr,\n      params,\n      nullptr\n    );\n  }\n  / 'def' SPACE NAME _ params (_ '->' _ expression)? _ ':' {\n    return asts(Function, LOC,\n      ac<string>(V0),\n      VS.size() == 3 ? ac_expr(VS[2]) : nullptr,\n      ac<SemVals>(V1).transform<Param>(),\n      nullptr\n    );\n  }\nparams <- '(' _ tlist(',', param)? _ ')' { return VS; }\nparam <-\n  / param_name _ ':' _ expression (_ '=' _ expression)? {\n    return Param(LOC, ac<string>(V0), ac_expr(V1), VS.size() > 2 ? ac_expr(V2) : nullptr);\n  }\n  / param_name (_ '=' _ expression)? {\n    return Param(LOC, ac<string>(V0), nullptr, VS.size() > 1 ? ac_expr(V1) : nullptr);\n  }\nparam_name <- <'**' / '*'>? _ NAME {\n  return (!VS.tokens.empty() ? VS.token_to_string() : \"\") + ac<string>(V0);\n}\ngenerics <- '[' _ tlist(',', param) _ ']' {\n  vector<Param> params;\n  for (auto &p: VS) {\n    auto v = ac<Param>(p);\n    v.status = Param::Generic;\n    if (!v.type) v.type = aste(Id, LOC, \"type\");\n    params.push_back(v);\n  }\n  return params;\n}\ndecorators <- decorator+ {\n  return VS.transform<Expr*>();\n}\ndecorator <- ('@' _ !(('llvm' / 'python') _ EOL) named_expression _ EOL SAMEDENT) {\n  return ac_expr(V0);\n}\nextern_decorators <-\n  / decorators? ('@' _ <'llvm'/'python'> _ EOL SAMEDENT) decorators? {\n    vector<Expr*> vs{aste(Id, LOC, VS.token_to_string())};\n    for (auto &v: VS) {\n      auto nv = ac<vector<Expr*>>(v);\n      vs.insert(vs.end(), nv.begin(), nv.end());\n    }\n    return vs;\n  }\nclass <- decorators? class_def {\n  if (VS.size() == 2) {\n    auto fn = ac_stmt(V1);\n    cast<ClassStmt>(fn)->setDecorators(ac<vector<Expr*>>(V0));\n    return fn;\n  }\n  return ac_stmt(V0);\n}\nbase_class_args <- '(' _ tlist(',', expression)? _ ')' {\n  return VS.transform<Expr*>();\n}\nclass_args <-\n  / generics _ base_class_args { return make_pair(ac<vector<Param>>(V0), ac<vector<Expr*>>(V1)); }\n  / generics { return make_pair(ac<vector<Param>>(V0), vector<Expr*>{}); }\n  / base_class_args { return make_pair(vector<Param>{}, ac<vector<Expr*>>(V0)); }\nclass_def <- 'class' SPACE NAME _ class_args? _ ':' _ suite {\n  vector<Param> generics;\n  vector<Expr*> baseClasses;\n  if (VS.size() == 3)\n    std::tie(generics, baseClasses) = ac<pair<vector<Param>, vector<Expr*>>>(V1);\n  vector<Param> args;\n  auto suite = (SuiteStmt*)(asts(Suite, LOC));\n  auto s = cast<SuiteStmt>(ac_stmt(VS.size() == 3 ? V2 : V1));\n  seqassertn(s, \"not a suite\");\n  for (auto *i: *s) {\n    if (auto a = cast<AssignStmt>(i))\n      if (auto ei = cast<IdExpr>(a->getLhs())) {\n        args.push_back(Param(a->getSrcInfo(), ei->getValue(), a->getTypeExpr(), a->getRhs()));\n        continue;\n      }\n    suite->addStmt(i);\n  }\n  suite->flatten();\n  for (auto &p: generics)\n    args.push_back(p);\n  return asts(Class, LOC,\n    ac<string>(V0), std::move(args), suite, vector<Expr*>{}, baseClasses\n  );\n}\nmatch_stmt <- 'match' SPACE expression _ ':' (_ EOL)+\n              &INDENT (SAMEDENT case)+ (_ EOL)* &DEDENT {\n  return asts(Match, LOC, ac_expr(V0), VS.transform<MatchCase>(1));\n}\ncase <-\n  / 'case' SPACE expression SPACE 'if' SPACE pipe _ ':' _ suite {\n    return MatchCase{ac_expr(V0), ac_expr(V1), ac_stmt(V2)};\n  }\n  / 'case' SPACE expression _ ':' _ suite {\n    return MatchCase{ac_expr(V0), nullptr, ac_stmt(V1)};\n  }\ncustom_stmt <-\n  / NAME SPACE expression _ ':' _ suite {\n    return asts(Custom, LOC, ac<string>(V0), ac_expr(V1), ac_stmt(V2));\n  }\n  / NAME _ ':' _ suite {\n    return asts(Custom, LOC, ac<string>(V0), nullptr, ac_stmt(V2));\n  }\ncustom_stmt__PREDICATE {\n  auto kwd = ac<string>(V0);\n  return CTX.hasCustomStmtKeyword(kwd, VS.choice() == 0); // ignore it\n}\n\ncustom_small_stmt <- NAME SPACE expressions {\n  return any(asts(Custom, LOC, ac<string>(V0), ac_expr(V1), nullptr));\n}\ncustom_small_stmt__PREDICATE {\n  auto kwd = ac<string>(V0);\n  return CTX.hasCustomExprStmt(kwd); // ignore it\n}\n\ndirective <- '##' _ 'codon:' _ NAME _ '=' _ (INT / NAME) {\n  return asts(Directive, LOC, ac<string>(V0), ac<string>(V1));\n}\n\n\n########################################################################################\n# (2) Expressions\n########################################################################################\n\nexpressions <- tlist(',', expression) { return wrap_tuple(CTX, VS, LOC); }\nexpression <-\n  / lambdef { return ac_expr(V0); }\n  / disjunction SPACE? 'if' SPACE? disjunction SPACE? 'else' SPACE? expression {\n    return aste(If, LOC, ac_expr(V1), ac_expr(V0), ac_expr(V2));\n  }\n  / pipe { return ac_expr(V0); }\nlambdef <-\n  / 'lambda' SPACE lparams _ ':' _ expression {\n    return aste(Lambda, LOC,\n      ac<SemVals>(V0).transform<Param>(), ac_expr(V1)\n    );\n  }\n  / 'lambda' _ ':' _ expression {\n    return aste(Lambda, LOC, vector<Param>{}, ac_expr(V0));\n  }\nlparams <- tlist(',', lparam)? { return VS; }\nlparam <- param_name (_ '=' _ expression)? {\n    return Param(LOC, ac<string>(V0), nullptr, VS.size() > 1 ? ac_expr(V1) : nullptr);\n  }\npipe <-\n  / disjunction (_ <'|>' / '||>'> _ disjunction)+ {\n    vector<Pipe> v;\n    for (int i = 0; i < VS.size(); i++)\n      v.push_back(Pipe{i ? VS.token_to_string(i - 1) : \"\", ac_expr(VS[i])});\n    return aste(Pipe, LOC, std::move(v));\n  }\n  / disjunction { return ac_expr(V0); }\ndisjunction <-\n  / conjunction (SPACE? 'or' SPACE? conjunction)+ {\n    auto b = aste(Binary, LOC, ac_expr(V0), \"||\", ac_expr(V1));\n    for (int i = 2; i < VS.size(); i++)\n      b = aste(Binary, LOC, b, \"||\", ac_expr(VS[i]));\n    return b;\n  }\n  / conjunction { return ac_expr(V0); }\nconjunction <-\n  / inversion (SPACE? 'and' SPACE? inversion)+ {\n    auto b = aste(Binary, LOC, ac_expr(V0), \"&&\", ac_expr(V1));\n    for (int i = 2; i < VS.size(); i++)\n      b = aste(Binary, LOC, b, \"&&\", ac_expr(VS[i]));\n    return b;\n  }\n  / inversion { return ac_expr(V0); }\ninversion <-\n  / 'not' SPACE inversion { return aste(Unary, LOC, \"!\", ac_expr(V0)); }\n  / comparison { return ac_expr(V0); }\ncomparison <- bitwise_or compare_op_bitwise_or* {\n  if (VS.size() == 1) {\n    return ac_expr(V0);\n  } else if (VS.size() == 2) {\n    auto p = ac<pair<string, Expr*>>(V1);\n    return aste(Binary, LOC, ac_expr(V0), p.first, p.second);\n  } else {\n    vector<pair<string, Expr*>> v{pair(string(), ac_expr(V0))};\n    auto vp = VS.transform<pair<string, Expr*>>(1);\n    v.insert(v.end(), vp.begin(), vp.end());\n    return aste(ChainBinary, LOC, std::move(v));\n  }\n}\ncompare_op_bitwise_or <-\n  / SPACE 'not' SPACE 'in' SPACE bitwise_or {\n    return pair(string(\"not in\"), ac_expr(V0));\n  }\n  / SPACE 'is' SPACE 'not' SPACE bitwise_or {\n    return pair(string(\"is not\"), ac_expr(V0));\n  }\n  / SPACE <'in' / 'is'> SPACE bitwise_or {\n    return pair(VS.token_to_string(), ac_expr(V0));\n  }\n  / _ <'==' / '!=' / '<=' / '<' / '>=' / '>'> _ bitwise_or {\n    return pair(VS.token_to_string(), ac_expr(V0));\n  }\nbitwise_or  <- bitwise_xor (_ <'|'> _ bitwise_xor)* { return chain(CTX, VS, LOC); }\nbitwise_xor <- bitwise_and (_ <'^'> _ bitwise_and)* { return chain(CTX, VS, LOC); }\nbitwise_and <- shift_expr  (_ <'&'> _ shift_expr )* { return chain(CTX, VS, LOC); }\nshift_expr <- sum  (_ <'<<' / '>>'> _ sum )* { return chain(CTX, VS, LOC); }\nsum        <- term (_ <'+' / '-'>   _ term)* { return chain(CTX, VS, LOC); }\nterm <- factor (_ <'*' / '//' / '/' / '%' / '@'> _ factor)* { return chain(CTX, VS, LOC); }\nfactor <-\n  / <'+' / '-' / '~'> _ factor {\n    return aste(Unary, LOC, VS.token_to_string(), ac_expr(V0));\n  }\n  / power { return ac_expr(V0); }\npower <-\n  / await_primary _ <'**'> _ factor {\n    return aste(Binary, LOC, ac_expr(V0), \"**\", ac_expr(V1));\n  }\n  / await_primary { return ac_expr(V0); }\nawait_primary <-\n  / 'await' SPACE primary { return aste(Await, LOC, ac_expr(V0)); }\n  / primary { return ac_expr(V0); }\nprimary <- atom (_ primary_tail)* {\n  auto e = ac<Expr*>(V0);\n  for (int i = 1; i < VS.size(); i++) {\n    auto p = ac<pair<int, any>>(VS[i]);\n    if (p.first == 0) {\n      e = aste(Dot, LOC, e, ac<string>(p.second));\n    } else if (p.first == 1) {\n      e = aste(Call, LOC, e, ac_expr(p.second));\n    } else if (p.first == 2) {\n      e = aste(Call, LOC, e, ac<vector<CallArg>>(p.second));\n    } else {\n      e = aste(Index, LOC, e, ac_expr(p.second));\n    }\n  }\n  return e;\n}\nprimary_tail <-\n  / '.' _ NAME { return pair(0, V0); }\n  / genexp { return pair(1, V0); }\n  / arguments { return pair(2, VS.size() ? V0 : any(vector<CallArg>{})); }\n  / slices { return pair(3, V0); }\nslices <- '[' _ tlist(',', slice) _ ']' { return wrap_tuple(CTX, VS, LOC); }\nslice <-\n  / slice_part _ ':' _ slice_part (_ ':' _ slice_part)? {\n    return aste(Slice, LOC,\n      ac_expr(V0), ac_expr(V1), VS.size() > 2 ? ac_expr(V2) : nullptr\n    );\n  }\n  / expression { return ac_expr(V0); }\nslice_part <- expression? { return VS.size() ? V0 : make_any<Expr*>(nullptr); }\natom <-\n  / STRING (SPACE STRING)* {\n    auto e = aste(String, LOC, VS.transform<StringExpr::String>());\n    return e;\n  }\n  / id { return ac_expr(V0); }\n  / 'True' { return aste(Bool, LOC, true); }\n  / 'False' { return aste(Bool, LOC, false);}\n  / 'None' { return aste(None, LOC); }\n  / INT _ '...' _ INT {\n    return aste(Range, LOC,\n      aste(Int, LOC, ac<string>(V0)), aste(Int, LOC, ac<string>(V1))\n    );\n  }\n  / FLOAT NAME? {\n    return aste(Float, LOC, ac<string>(V0), VS.size() > 1 ? ac<string>(V1) : \"\");\n  }\n  / INT NAME? {\n    return aste(Int, LOC, ac<string>(V0), VS.size() > 1 ? ac<string>(V1) : \"\");\n  }\n  / parentheses { return ac_expr(V0); }\n  / '...' { return aste(Ellipsis, LOC); }\nparentheses <- (\n  tuple / yield / named / genexp / listexpr / listcomp / dict / set / dictcomp / setcomp\n)\ntuple <-\n  / '(' _ ')' { return aste(Tuple, LOC, VS.transform<Expr*>()); }\n  / '(' _ tlist(',', star_named_expression) _ ')' { return wrap_tuple(CTX, VS, LOC); }\nyield <- '(' _ 'yield' _ ')' { return aste(Yield, LOC); }\nnamed <- '(' _ named_expression _ ')'\ngenexp <- '(' _ named_expression SPACE for_if_clauses _ ')' {\n  return aste(Generator,\n    LOC, CTX.cache, GeneratorExpr::Generator, ac_expr(V0), ac<std::vector<Stmt*>>(V1)\n  );\n}\nlistexpr <- '[' _ tlist(',', star_named_expression)? _ ']' {\n  return aste(List, LOC, VS.transform<Expr*>());\n}\nlistcomp <- '[' _ named_expression SPACE for_if_clauses _ ']' {\n  return aste(Generator,\n    LOC, CTX.cache, GeneratorExpr::ListGenerator, ac_expr(V0), ac<std::vector<Stmt*>>(V1)\n  );\n}\nset <- '{' _ tlist(',', star_named_expression) _ '}' {\n  return aste(Set, LOC, VS.transform<Expr*>());\n}\nsetcomp <- '{' _ named_expression SPACE for_if_clauses _ '}' {\n  return aste(Generator,\n    LOC, CTX.cache, GeneratorExpr::SetGenerator, ac_expr(V0), ac<std::vector<Stmt*>>(V1)\n  );\n}\ndict <- '{' _ tlist(',', double_starred_kvpair)? _ '}' {\n  return aste(Dict, LOC, VS.transform<Expr*>());\n}\ndictcomp <- '{' _ kvpair SPACE for_if_clauses _ '}' {\n  auto p = ac<Expr*>(V0);\n  return aste(Generator,\n    LOC, CTX.cache, (*cast<TupleExpr>(p))[0], (*cast<TupleExpr>(p))[1], ac<std::vector<Stmt*>>(V1)\n  );\n}\ndouble_starred_kvpair <-\n  / '**' _ bitwise_or {\n    return aste(KeywordStar, LOC, ac_expr(V0));\n  }\n  / kvpair { return ac<Expr*>(V0); }\nkvpair <- expression _ ':' _ expression {\n  return aste(Tuple, LOC, std::vector<Expr*>{ac_expr(V0), ac_expr(V1)});\n}\nfor_if_clauses <- for_if_clause_async (SPACE for_if_clause_async)* {\n  std::vector<Stmt*> v = ac<std::vector<Stmt*>>(V0);\n  auto tail = VS.transform<std::vector<Stmt*>>(1);\n  for (auto &t: tail)\n    v.insert(v.end(), t.begin(), t.end());\n  return v;\n}\nfor_if_clause_async <-\n  / 'async' SPACE for_if_clause {\n    auto s = ac<std::vector<Stmt*>>(V0);\n    if (!s.empty() && cast<ForStmt>(s.front()))\n      cast<ForStmt>(s.front())->setAsync();\n    return make_any<std::vector<Stmt*>>(s);\n  }\n  / for_if_clause { return V0; }\nfor_if_clause <-\n  / 'for' SPACE star_targets SPACE 'in' SPACE disjunction\n                 (SPACE? 'if' SPACE? disjunction)* {\n    std::vector<Stmt*> v{asts(For, LOC, ac_expr(V0), ac_expr(V1), nullptr)};\n    auto tail = VS.transform<Expr*>(2);\n    for (auto &t: tail)\n      v.push_back(asts(If, LOC, t, nullptr));\n    return v;\n  }\n\nstar_targets <- tlist(',', star_target) { return wrap_tuple(CTX, VS, LOC); }\nstar_target <-\n  / '*' _ !'*' star_target { return aste(Star, LOC, ac_expr(V0)); }\n  / star_parens { return ac_expr(V0); }\n  / primary { return ac_expr(V0); }\nstar_parens <-\n  / '(' _ tlist(',', star_target) _ ')' { return wrap_tuple(CTX, VS, LOC); }\n  / '[' _ tlist(',', star_target) _ ']' { return wrap_tuple(CTX, VS, LOC); }\n\nstar_expressions <- tlist(',', star_expression) { return wrap_tuple(CTX, VS, LOC); }\nstar_expression <-\n  / '*' _ bitwise_or { return aste(Star, LOC, ac_expr(V0)); }\n  / expression { return ac_expr(V0); }\nstar_named_expression <-\n  / '*' _ bitwise_or { return aste(Star, LOC, ac_expr(V0)); }\n  / named_expression { return ac_expr(V0); }\nnamed_expression <-\n  / NAME _ ':=' _ ^ expression {\n    return aste(Assign, LOC, aste(Id, LOC, ac<string>(V0)), ac_expr(V1));\n  }\n  / expression !(_ ':=') { return ac_expr(V0); }\narguments <- '(' _ tlist(',', args)? _ ')' {\n  vector<CallArg> result;\n  for (auto &v: VS)\n    for (auto &i: ac<vector<CallArg>>(v))\n      result.push_back(i);\n  if (EllipsisExpr *e = nullptr; !result.empty() &&\n      result.back().getName().empty() &&\n      ((e = cast<EllipsisExpr>(result.back().getExpr())))) {\n    auto en = CTX.cache->N<EllipsisExpr>(EllipsisExpr::PARTIAL);\n    en->setSrcInfo(e->getSrcInfo());\n    result.back() = CallArg{\"\", en};\n  }\n  return result;\n}\nargs <- (simple_args (_ ',' _ kwargs)? / kwargs) {\n  auto args = ac<vector<CallArg>>(V0);\n  if (VS.size() > 1) {\n    auto v = ac<vector<CallArg>>(V1);\n    args.insert(args.end(), v.begin(), v.end());\n  }\n  return args;\n}\nsimple_args <- list(',', (starred_expression / named_expression !(_ '='))) {\n  return vmap(VS, [](auto &i) { return CallArg(ac_expr(i)); });\n}\nstarred_expression <- '*' _ expression { return aste(Star, LOC, ac_expr(V0)); }\nkwargs <-\n  / list(',', kwarg_or_starred) _ ',' _ list(',', kwarg_or_double_starred) {\n    return VS.transform<CallArg>();\n  }\n  / list(',', kwarg_or_starred) { return VS.transform<CallArg>(); }\n  / list(',', kwarg_or_double_starred) { return VS.transform<CallArg>(); }\nkwarg_or_starred <-\n  / NAME _ '=' _ expression { return CallArg(LOC, ac<string>(V0), ac_expr(V1)); }\n  / starred_expression { return CallArg(ac_expr(V0)); }\nkwarg_or_double_starred <-\n  / NAME _ '=' _ expression { return CallArg(LOC, ac<string>(V0), ac_expr(V1)); }\n  / '**' _ expression {\n    return CallArg(aste(KeywordStar, LOC, ac_expr(V0)));\n  }\nid <- NAME { return aste(Id, LOC, ac<string>(V0)); }\nINT <- (BININT / HEXINT / DECINT) { return string(VS.sv()); }\nBININT <- <'0' [bB] [0-1] ('_'* [0-1])*>\nHEXINT <- <'0' [xX] [0-9a-fA-F] ('_'? [0-9a-fA-F])*>\nDECINT <- <[0-9] ('_'? [0-9])*>\nFLOAT <- (EXPFLOAT / PTFLOAT) { return string(VS.sv()); }\nPTFLOAT <- DECINT? '.' DECINT / DECINT '.'\nEXPFLOAT <- (PTFLOAT / DECINT) [eE] <'+' / '-'>? DECINT\nNAME <-\n  / keyword [a-zA-Z_0-9]+ { return string(VS.sv()); }\n  / !keyword <[a-zA-Z_] [a-zA-Z_0-9]*> { return VS.token_to_string(); }\nSTRING <- <NAME? STR> {\n  auto p = StringExpr::String(\n    ac<string>(VS.size() > 1 ? V1 : V0),\n    VS.size() > 1 ? ac<string>(V0) : \"\"\n  );\n  if (p.prefix != \"r\" && p.prefix != \"R\") {\n    p.value = unescape(p.value);\n  } else {\n    p.prefix = \"\";\n  }\n  return p;\n}\nSTRING__PREDICATE {\n  auto p = StringExpr::String(\n    ac<string>(VS.size() > 1 ? V1 : V0),\n    VS.size() > 1 ? ac<string>(V0) : \"\"\n  );\n  if (p.prefix != \"r\" && p.prefix != \"R\")\n    try {\n      p.value = unescape(p.value);\n    } catch (std::invalid_argument &e) {\n      MSG = \"invalid code in a string\";\n      return false;\n    } catch (std::out_of_range &) {\n      MSG = \"invalid code in a string\";\n      return false;\n    }\n  return true;\n}\nSTR <- <\n  '\"\"\"' (!'\"\"\"' CHAR)*       '\"\"\"'   /  '\\'\\'\\'' (!'\\'\\'\\'' CHAR)*     '\\'\\'\\'' /\n  '\"'   (!('\"' / EOL) CHAR)* '\"'     /  '\\''     (!('\\'' / EOL) CHAR)* '\\''\n> {\n  string s;\n  s.reserve(VS.size());\n  for (auto &v: VS)\n    s.append(ac<string>(v));\n  return s;\n}\nCHAR <- ('\\\\' . / .) { return string(VS.sv()); }\n~COMMENT <- !directive <'#' (!EOL .)*>\n~INDENT__NOPACKRAT <- <[ \\t]*> { CTX.indent.push(VS.sv().size()); }\nINDENT__PREDICATE {\n  if (!(CTX.indent.empty() && VS.sv().size()) &&\n      !(!CTX.indent.empty() && VS.sv().size() > CTX.indent.top())) {\n    MSG = \"unexpected indentation\";\n    return false;\n  }\n  return true;\n}\n~SAMEDENT__NOPACKRAT <- <[ \\t]*> {}\nSAMEDENT__PREDICATE {\n  return !(!CTX.indent.size() && VS.sv().size()) &&\n         !(CTX.indent.size() && VS.sv().size() != CTX.indent.top());\n}\n~DEDENT__NOPACKRAT <- <[ \\t]*> { CTX.indent.pop(); }\nDEDENT__PREDICATE {\n  if (!(CTX.indent.size() && VS.sv().size() < CTX.indent.top())) {\n    MSG = \"unexpected dedent\";\n    return false;\n  }\n  return true;\n}\n~EXTERNDENT__NOPACKRAT <- <[ \\t]*> {}\nEXTERNDENT__PREDICATE {\n  return !(!CTX.indent.size() && VS.sv().size()) &&\n         !(CTX.indent.size() && VS.sv().size() < CTX.indent.top());\n}\n~EOL <- <[\\r][\\n] / [\\r\\n]>\n~SPACE <- ([ \\t]+ / COMMENT / NLP EOL) SPACE?\n~_ <- SPACE?\n\n~keyword <- <\n  'False' / 'else' / 'import' / 'pass' / 'None' / 'break' / 'except' / 'in' / 'raise' /\n  'True' / 'class' / 'finally' / 'is' / 'return' / 'and' / 'continue' / 'for' / 'as'  /\n  'lambda' / 'try' / 'def' / 'from' / 'while' / 'assert' / 'del' / 'global' / 'not' /\n  'with' / 'elif' / 'if' / 'or' / 'yield' / 'async' / 'await'\n>\n\n# https://docs.python.org/3/library/string.html#formatspec\nformat_spec <- ([^{}] [<>=^] / [<>=^])? [+- ]? 'z'? '#'? '0'? [0-9]* [_,]* ('.' [0-9]+)? [bcdeEfFgGnosxX%]? {\n  return string(VS.sv());\n}\n"
  },
  {
    "path": "codon/parser/peg/openmp.peg",
    "content": "# Copyright (C) 2022 Exaloop Inc. <https://exaloop.io>\n# OpenMP PEG grammar\n\nPREAMBLE {\n  #include \"codon/parser/peg/rules.h\"\n  #include <any>\n  using namespace std;\n  using namespace codon::ast;\n\n  #define V0 VS[0]\n  #define V1 VS[1]\n  #define ac std::any_cast\n\n  template<typename T>\n  T *setSI(ASTNode *n, const codon::SrcInfo &s) {\n    n->setSrcInfo(s);\n    return (T*)n;\n  }\n  #define ast(T, s, ...) setSI<Expr>(CTX.cache->N<T ## Expr>(__VA_ARGS__), s)\n}\n\npragma <- \"omp\"? _ \"parallel\"? _ (clause _)* {\n  std::vector<CallArg> v;\n  for (auto &i: VS) {\n    auto vi = ac<std::vector<CallArg>>(i);\n    v.insert(v.end(), vi.begin(), vi.end());\n  }\n  return v;\n}\nclause <-\n  / \"schedule\" _ \"(\" _ schedule_kind (_ \",\" _ int)? _ \")\" {\n    // CTX;\n    std::vector<CallArg> v{CallArg{\"schedule\", ast(String, LOC, ac<string>(V0))}};\n    if (VS.size() > 1)\n      v.push_back(CallArg{\"chunk_size\", ast(Int, LOC, ac<int>(V1))});\n    return v;\n  }\n  / \"num_threads\" _ \"(\" _ int _ \")\" {\n    return std::vector<CallArg>{CallArg{\"num_threads\", ast(Int, LOC, ac<int>(V0))}};\n  }\n  / \"ordered\" {\n    return std::vector<CallArg>{CallArg{\"ordered\", ast(Bool, LOC, true)}};\n  }\n  / \"collapse\" {\n    return std::vector<CallArg>{CallArg{\"collapse\", ast(Int, LOC, ac<int>(V0))}};\n  }\n  / \"gpu\" {\n    return std::vector<CallArg>{CallArg{\"gpu\", ast(Bool, LOC, true)}};\n  }\nschedule_kind <- (\"static\" / \"dynamic\" / \"guided\" / \"auto\" / \"runtime\") {\n  return VS.token_to_string();\n}\nint <- [1-9] [0-9]* {\n  return stoi(VS.token_to_string());\n}\n# ident <- [a-zA-Z_] [a-zA-Z_0-9]* {\n#   return ast<ast::IdExpr>(VS.token_to_string());\n# }\n~SPACE <- [ \\t]+\n~_ <- SPACE*\n"
  },
  {
    "path": "codon/parser/peg/peg.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"peg.h\"\n\n#include <any>\n#include <memory>\n#include <peglib.h>\n#include <string>\n#include <vector>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/peg/rules.h\"\n#include \"codon/parser/visitors/format/format.h\"\n\n#include <ranges>\n\ndouble totalPeg = 0.0;\n\nnamespace codon::ast {\n\nstatic std::shared_ptr<peg::Grammar> grammar(nullptr);\nstatic std::shared_ptr<peg::Grammar> ompGrammar(nullptr);\n\nstd::shared_ptr<peg::Grammar> initParser() {\n  auto g = std::make_shared<peg::Grammar>();\n  init_codon_rules(*g);\n  init_codon_actions(*g);\n  ~(*g)[\"NLP\"] <=\n      peg::usr([](const char *s, size_t n, peg::SemanticValues &, std::any &dt) {\n        auto e = (n >= 1 && s[0] == '\\\\' ? 1 : -1);\n        if (std::any_cast<ParseContext &>(dt).parens && e == -1)\n          e = 0;\n        return e;\n      });\n  for (auto &val : *g | std::views::values) {\n    auto v = peg::LinkReferences(*g, val.params);\n    val.accept(v);\n  }\n  (*g)[\"program\"].enablePackratParsing = true;\n  (*g)[\"fstring\"].enablePackratParsing = true;\n  for (auto &rule : std::vector<std::string>{\n           \"arguments\", \"slices\", \"genexp\", \"parentheses\", \"star_parens\", \"generics\",\n           \"with_parens_item\", \"params\", \"from_as_parens\", \"from_params\"}) {\n    (*g)[rule].enter = [](const peg::Context &, const char *, size_t, std::any &dt) {\n      std::any_cast<ParseContext &>(dt).parens++;\n    };\n    (*g)[rule.c_str()].leave = [](const peg::Context &, const char *, size_t, size_t,\n                                  std::any &, std::any &dt) {\n      std::any_cast<ParseContext &>(dt).parens--;\n    };\n  }\n  return g;\n}\n\ntemplate <typename T>\nllvm::Expected<T> parseCode(Cache *cache, const std::string &file,\n                            const std::string &code, int line_offset, int col_offset,\n                            const std::string &rule) {\n  Timer t(\"\");\n  t.logged = true;\n  // Initialize\n  if (!grammar)\n    grammar = initParser();\n\n  std::vector<ErrorMessage> errors;\n  auto log = [&](size_t line, size_t col, const std::string &msg, const std::string &) {\n    size_t ed = msg.size();\n    if (startswith(msg, \"syntax error, unexpected\")) {\n      auto i = msg.find(\", expecting\");\n      if (i != std::string::npos)\n        ed = i;\n    }\n    errors.emplace_back(msg.substr(0, ed), file, line, col);\n  };\n  T result;\n  auto ctx = std::make_any<ParseContext>(cache, 0, line_offset, col_offset);\n  auto r = (*grammar)[rule].parse_and_get_value(code.c_str(), code.size(), ctx, result,\n                                                file.c_str(), log);\n  auto ret = r.ret && r.len == code.size();\n  if (!ret)\n    r.error_info.output_log(log, code.c_str(), code.size());\n  totalPeg += t.elapsed();\n\n  if (!errors.empty())\n    return llvm::make_error<error::ParserErrorInfo>(errors);\n  return result;\n}\n\nllvm::Expected<Stmt *> parseCode(Cache *cache, const std::string &file,\n                                 const std::string &code, int line_offset) {\n  return parseCode<Stmt *>(cache, file, code + \"\\n\", line_offset, 0, \"program\");\n}\n\nllvm::Expected<std::pair<Expr *, StringExpr::FormatSpec>>\nparseExpr(Cache *cache, const std::string &code, const codon::SrcInfo &offset) {\n  auto newCode = code;\n  ltrim(newCode);\n  rtrim(newCode);\n  return parseCode<std::pair<Expr *, StringExpr::FormatSpec>>(\n      cache, offset.file, newCode, offset.line, offset.col, \"fstring\");\n}\n\nllvm::Expected<Stmt *> parseFile(Cache *cache, const std::string &file) {\n  auto lines = cache->fs->read_lines(file);\n  cache->imports[file].content = lines;\n  std::string code = join(lines, \"\\n\");\n  auto result = parseCode(cache, file, code);\n  // /* For debugging purposes: */ LOG(\"peg/{} :=  {}\", file, result);\n  return result;\n}\n\nstd::shared_ptr<peg::Grammar> initOpenMPParser() {\n  auto g = std::make_shared<peg::Grammar>();\n  init_omp_rules(*g);\n  init_omp_actions(*g);\n  for (auto &val : *g | std::views::values) {\n    auto v = peg::LinkReferences(*g, val.params);\n    val.accept(v);\n  }\n  (*g)[\"pragma\"].enablePackratParsing = true;\n  return g;\n}\n\nllvm::Expected<std::vector<CallArg>> parseOpenMP(Cache *cache, const std::string &code,\n                                                 const codon::SrcInfo &loc) {\n  if (!ompGrammar)\n    ompGrammar = initOpenMPParser();\n\n  std::vector<ErrorMessage> errors;\n  auto log = [&](size_t line, size_t col, const std::string &msg, const std::string &) {\n    errors.emplace_back(fmt::format(\"openmp: {}\", msg), loc.file, loc.line, loc.col);\n  };\n  std::vector<CallArg> result;\n  auto ctx = std::make_any<ParseContext>(cache, 0, 0, 0);\n  auto r = (*ompGrammar)[\"pragma\"].parse_and_get_value(code.c_str(), code.size(), ctx,\n                                                       result, \"\", log);\n  auto ret = r.ret && r.len == code.size();\n  if (!ret)\n    r.error_info.output_log(log, code.c_str(), code.size());\n  if (!errors.empty())\n    return llvm::make_error<error::ParserErrorInfo>(errors);\n  return result;\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/peg/peg.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <string>\n#include <vector>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/util/common.h\"\n\nnamespace codon::ast {\n\n/// Parse a Seq code block with the appropriate file and position offsets.\nllvm::Expected<Stmt *> parseCode(Cache *cache, const std::string &file,\n                                 const std::string &code, int line_offset = 0);\n/// Parse a Seq code expression.\n/// @return pair of Expr * and a format specification\n/// (empty if not available).\nllvm::Expected<std::pair<Expr *, StringExpr::FormatSpec>>\nparseExpr(Cache *cache, const std::string &code, const codon::SrcInfo &offset);\n/// Parse a Seq file.\nllvm::Expected<Stmt *> parseFile(Cache *cache, const std::string &file);\n\n/// Parse a OpenMP clause.\nllvm::Expected<std::vector<CallArg>> parseOpenMP(Cache *cache, const std::string &code,\n                                                 const codon::SrcInfo &loc);\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/peg/rules.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <peglib.h>\n#include <string>\n\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/common.h\"\n\nnamespace codon::ast {\n\nstruct ParseContext {\n  Cache *cache;\n  std::stack<int> indent;\n  int parens;\n  int line_offset, col_offset;\n  ParseContext(Cache *cache, int parens = 0, int line_offset = 0, int col_offset = 0)\n      : cache(cache), parens(parens), line_offset(line_offset), col_offset(col_offset) {\n  }\n\n  bool hasCustomStmtKeyword(const std::string &kwd, bool hasExpr) const {\n    auto i = cache->customBlockStmts.find(kwd);\n    if (i != cache->customBlockStmts.end())\n      return i->second.first == hasExpr;\n    return false;\n  }\n  bool hasCustomExprStmt(const std::string &kwd) const {\n    return in(cache->customExprStmts, kwd);\n  }\n};\n\n} // namespace codon::ast\n\nvoid init_codon_rules(peg::Grammar &);\nvoid init_codon_actions(peg::Grammar &);\nvoid init_omp_rules(peg::Grammar &);\nvoid init_omp_actions(peg::Grammar &);\n"
  },
  {
    "path": "codon/parser/visitors/doc/doc.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"doc.h\"\n\n#include <memory>\n#include <string>\n#include <vector>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/match.h\"\n#include \"codon/parser/peg/peg.h\"\n#include \"codon/parser/visitors/format/format.h\"\n#include \"codon/parser/visitors/scoping/scoping.h\"\n\nnamespace codon::ast {\n\nusing namespace error;\nusing namespace matcher;\n\n// clang-format off\nstd::string json_escape(const std::string &str) {\n  std::string r;\n  r.reserve(str.size());\n  for (unsigned char c : str) {\n    switch (c) {\n    case '\\b': r += \"\\\\b\"; break;\n    case '\\f': r += \"\\\\f\"; break;\n    case '\\n': r += \"\\\\n\"; break;\n    case '\\r': r += \"\\\\r\"; break;\n    case '\\t': r += \"\\\\t\"; break;\n    case '\\\\': r += \"\\\\\\\\\"; break;\n    case '\"': r += \"\\\\\\\"\"; break;\n    default: r += c;\n    }\n  }\n  return r;\n}\n// clang-format on\n\njson::json() : list(false) {}\njson::json(const std::string &s) : list(false) { values[s] = nullptr; }\njson::json(const std::string &s, const std::string &v) : list(false) {\n  values[s] = std::make_shared<json>(v);\n}\njson::json(const std::vector<std::shared_ptr<json>> &vs) : list(true) {\n  for (int i = 0; i < vs.size(); i++)\n    values[std::to_string(i)] = vs[i];\n}\njson::json(const std::vector<std::string> &vs) : list(true) {\n  for (int i = 0; i < vs.size(); i++)\n    values[std::to_string(i)] = std::make_shared<json>(vs[i]);\n}\njson::json(const std::unordered_map<std::string, std::string> &vs) : list(false) {\n  for (auto &v : vs)\n    values[v.first] = std::make_shared<json>(v.second);\n}\n\nstd::string json::toString() {\n  std::vector<std::string> s;\n  if (values.empty()) {\n    return \"{}\";\n  } else if (values.size() == 1 && !values.begin()->second) {\n    return fmt::format(\"\\\"{}\\\"\", json_escape(values.begin()->first));\n  } else if (list) {\n    for (int i = 0; i < values.size(); i++)\n      s.push_back(values[std::to_string(i)]->toString());\n    return fmt::format(\"[ {} ]\", join(s, \", \"));\n  } else {\n    for (auto &v : values)\n      s.push_back(\n          fmt::format(\"\\\"{}\\\": {}\", json_escape(v.first), v.second->toString()));\n    return fmt::format(\"{{ {} }}\", join(s, \", \"));\n  }\n}\n\nstd::shared_ptr<json> json::get(const std::string &s) {\n  auto i = values.find(s);\n  seqassertn(i != values.end(), \"cannot find {}\", s);\n  return i->second;\n}\n\nstd::shared_ptr<json> json::set(const std::string &s, const std::string &value) {\n  return values[s] = std::make_shared<json>(value);\n}\nstd::shared_ptr<json> json::set(const std::string &s,\n                                const std::shared_ptr<json> &value) {\n  return values[s] = value;\n}\n\nstd::shared_ptr<json> DocVisitor::apply(const std::string &argv0,\n                                        const std::vector<std::string> &files) {\n  auto shared = std::make_shared<DocShared>();\n  shared->argv0 = argv0;\n  auto cache = std::make_unique<ast::Cache>(argv0);\n  shared->cache = cache.get();\n  shared->modules[\"\"] = std::make_shared<DocContext>(shared);\n  shared->j = std::make_shared<json>();\n\n  auto stdlib = getImportFile(cache.get(), STDLIB_INTERNAL_MODULE, \"\", true);\n  auto astOrErr = ast::parseFile(shared->cache, stdlib->path);\n  if (!astOrErr)\n    throw exc::ParserException(astOrErr.takeError());\n  auto coreOrErr =\n      ast::parseCode(shared->cache, stdlib->path, \"from internal.core import *\");\n  if (!coreOrErr)\n    throw exc::ParserException(coreOrErr.takeError());\n  shared->modules[\"\"]->setFilename(stdlib->path);\n  shared->modules[\"\"]->add(\"__py_numerics__\", std::make_shared<int>(shared->itemID++));\n  shared->modules[\"\"]->add(\"__py_extension__\", std::make_shared<int>(shared->itemID++));\n  shared->modules[\"\"]->add(\"__debug__\", std::make_shared<int>(shared->itemID++));\n  shared->modules[\"\"]->add(\"__apple__\", std::make_shared<int>(shared->itemID++));\n\n  auto j = std::make_shared<json>(std::unordered_map<std::string, std::string>{\n      {\"name\", \"type\"}, {\"kind\", \"class\"}, {\"type\", \"type\"}});\n  j->set(\"generics\", std::make_shared<json>(std::vector<std::string>{\"T\"}));\n  shared->modules[\"\"]->add(\"type\", std::make_shared<int>(shared->itemID));\n  shared->j->set(std::to_string(shared->itemID++), j);\n  j = std::make_shared<json>(std::unordered_map<std::string, std::string>{\n      {\"name\", \"Literal\"}, {\"kind\", \"class\"}, {\"type\", \"type\"}});\n  j->set(\"generics\", std::make_shared<json>(std::vector<std::string>{\"T\"}));\n  shared->modules[\"\"]->add(\"Literal\", std::make_shared<int>(shared->itemID));\n  shared->j->set(std::to_string(shared->itemID++), j);\n  DocVisitor(shared->modules[\"\"]).transformModule(*coreOrErr);\n  DocVisitor(shared->modules[\"\"]).transformModule(*astOrErr);\n\n  auto ctx = std::make_shared<DocContext>(shared);\n  for (auto &f : files) {\n    auto path = std::string(cache->fs->canonical(f));\n    ctx->setFilename(path);\n    // LOG(\"-> parsing {}\", path);\n    auto fAstOrErr = ast::parseFile(shared->cache, path);\n    if (!fAstOrErr)\n      throw exc::ParserException(fAstOrErr.takeError());\n    DocVisitor(ctx).transformModule(*fAstOrErr);\n  }\n\n  shared->cache = nullptr;\n  return shared->j;\n}\n\nstd::shared_ptr<int> DocContext::find(const std::string &s) const {\n  auto i = Context<int>::find(s);\n  if (!i && this != shared->modules[\"\"].get())\n    return shared->modules[\"\"]->find(s);\n  return i;\n}\n\nstd::string getDocstr(Stmt *s) {\n  if (auto se = cast<ExprStmt>(s))\n    if (auto e = cast<StringExpr>(se->getExpr()))\n      return e->getValue();\n  return \"\";\n}\n\nstd::vector<Stmt *> DocVisitor::flatten(Stmt *stmt, std::string *docstr, bool deep) {\n  std::vector<Stmt *> stmts;\n  if (auto s = cast<SuiteStmt>(stmt)) {\n    for (int i = 0; i < (deep ? s->size() : 1); i++) {\n      for (auto &x : flatten((*s)[i], i ? nullptr : docstr, deep))\n        stmts.push_back(std::move(x));\n    }\n  } else {\n    if (docstr)\n      *docstr = getDocstr(stmt);\n    stmts.push_back(std::move(stmt));\n  }\n  return stmts;\n}\n\nstd::shared_ptr<json> DocVisitor::transform(Expr *expr) {\n  if (!expr)\n    return std::make_shared<json>();\n  DocVisitor v(ctx);\n  v.setSrcInfo(expr->getSrcInfo());\n  v.resultExpr = std::make_shared<json>();\n  expr->accept(v);\n  return v.resultExpr;\n}\n\nstd::string DocVisitor::transform(Stmt *stmt) {\n  if (!stmt)\n    return \"\";\n  DocVisitor v(ctx);\n  v.setSrcInfo(stmt->getSrcInfo());\n  stmt->accept(v);\n  return v.resultStmt;\n}\n\nvoid DocVisitor::transformModule(Stmt *stmt) {\n  if (auto err = ScopingVisitor::apply(ctx->shared->cache, stmt))\n    throw exc::ParserException(std::move(err));\n\n  std::vector<std::string> children;\n  std::string docstr;\n\n  auto flat = flatten(std::move(stmt), &docstr);\n  for (int i = 0; i < flat.size(); i++) {\n    auto &s = flat[i];\n    auto id = transform(s);\n    if (id.empty())\n      continue;\n    if (i < (flat.size() - 1) && cast<AssignStmt>(s)) {\n      auto ds = getDocstr(flat[i + 1]);\n      if (!ds.empty())\n        ctx->shared->j->get(id)->set(\"doc\", ds);\n    }\n    children.push_back(id);\n  }\n\n  auto id = std::to_string(ctx->shared->itemID++);\n  auto ja = ctx->shared->j->set(\n      id, std::make_shared<json>(std::unordered_map<std::string, std::string>{\n              {\"kind\", \"module\"}, {\"path\", ctx->getFilename()}}));\n  ja->set(\"children\", std::make_shared<json>(children));\n  if (!docstr.empty())\n    ja->set(\"doc\", docstr);\n}\n\nvoid DocVisitor::visit(IntExpr *expr) {\n  auto [value, _] = expr->getRawData();\n  resultExpr = std::make_shared<json>(value);\n}\n\nvoid DocVisitor::visit(IdExpr *expr) {\n  auto i = ctx->find(expr->getValue());\n  if (!i)\n    E(Error::CUSTOM, expr->getSrcInfo(), \"unknown identifier {}\", expr->getValue());\n  resultExpr = std::make_shared<json>(*i ? std::to_string(*i) : expr->getValue());\n}\n\nvoid DocVisitor::visit(IndexExpr *expr) {\n  auto tr = [&](Expr *e) {\n    if (match(e, MOr(M<IntExpr>(), M<BoolExpr>(), M<StringExpr>())))\n      return std::make_shared<json>(FormatVisitor::apply(e));\n    else\n      return transform(e);\n  };\n  std::vector<std::shared_ptr<json>> v;\n  v.push_back(transform(expr->getExpr()));\n  if (auto tp = cast<TupleExpr>(expr->getIndex())) {\n    if (auto l = cast<ListExpr>((*tp)[0])) {\n      for (auto *e : *l)\n        v.push_back(tr(e));\n      v.push_back(tr((*tp)[1]));\n    } else\n      for (auto *e : *tp) {\n        v.push_back(tr(e));\n      }\n  } else {\n    v.push_back(tr(expr->getIndex()));\n  }\n  resultExpr = std::make_shared<json>(v);\n}\n\nbool isValidName(const std::string &s) {\n  if (s.empty())\n    return false;\n  if (s.size() > 4 && s.substr(0, 2) == \"__\" && s.substr(s.size() - 2) == \"__\")\n    return true;\n  return s[0] != '_';\n}\n\nvoid DocVisitor::visit(FunctionStmt *stmt) {\n  int id = ctx->shared->itemID++;\n  ctx->add(stmt->getName(), std::make_shared<int>(id));\n  auto j = std::make_shared<json>(std::unordered_map<std::string, std::string>{\n      {\"kind\", \"function\"}, {\"name\", stmt->getName()}});\n  j->set(\"pos\", jsonify(stmt->getSrcInfo()));\n\n  std::vector<std::shared_ptr<json>> args;\n  std::vector<std::string> generics;\n  for (auto &a : *stmt)\n    if (!a.isValue()) {\n      ctx->add(a.name, std::make_shared<int>(0));\n      generics.push_back(a.name);\n      a.status = Param::Generic;\n    }\n  for (auto &a : *stmt) {\n    auto jj = std::make_shared<json>();\n    jj->set(\"name\", a.name);\n    if (a.type) {\n      auto tt = transform(a.type);\n      if (tt->values.empty()) {\n        LOG(\"{}: warning: cannot resolve argument {}\", a.type->getSrcInfo(),\n            FormatVisitor::apply(a.type));\n        jj->set(\"type\", FormatVisitor::apply(a.type));\n      } else {\n        jj->set(\"type\", tt);\n      }\n    }\n    if (a.defaultValue) {\n      jj->set(\"default\", FormatVisitor::apply(a.defaultValue));\n    }\n    args.push_back(jj);\n  }\n  j->set(\"generics\", std::make_shared<json>(generics));\n  bool isLLVM = false;\n  std::vector<std::string> attrs;\n  for (const auto &d : stmt->getDecorators())\n    if (auto e = cast<IdExpr>(d)) {\n      attrs.push_back(e->getValue());\n      isLLVM |= (e->getValue() == \"llvm\");\n    }\n  if (stmt->hasAttribute(Attr::Property))\n    attrs.push_back(\"property\");\n  if (stmt->hasAttribute(Attr::Attribute))\n    attrs.push_back(\"__attribute__\");\n  if (stmt->hasAttribute(Attr::Python))\n    attrs.push_back(\"python\");\n  if (stmt->hasAttribute(Attr::LLVM))\n    attrs.push_back(\"llvm\");\n  if (stmt->hasAttribute(Attr::Internal))\n    attrs.push_back(\"__internal__\");\n  if (stmt->hasAttribute(Attr::HiddenFromUser))\n    attrs.push_back(\"__hidden__\");\n  if (stmt->hasAttribute(Attr::Atomic))\n    attrs.push_back(\"atomic\");\n  if (stmt->hasAttribute(Attr::StaticMethod))\n    attrs.push_back(\"staticmethod\");\n  if (stmt->hasAttribute(Attr::C))\n    attrs.push_back(\"C\");\n  if (!attrs.empty())\n    j->set(\"attrs\", std::make_shared<json>(attrs));\n  if (stmt->getReturn())\n    j->set(\"return\", transform(stmt->getReturn()));\n  j->set(\"args\", std::make_shared<json>(args));\n  std::string docstr;\n  flatten(stmt->getSuite(), &docstr);\n  for (auto &g : generics)\n    ctx->remove(g);\n  if (!docstr.empty() && !isLLVM)\n    j->set(\"doc\", docstr);\n  ctx->shared->j->set(std::to_string(id), j);\n  resultStmt = std::to_string(id);\n}\n\nvoid DocVisitor::visit(ClassStmt *stmt) {\n  std::vector<std::string> generics;\n\n  bool isRecord = stmt->isRecord();\n  auto j = std::make_shared<json>(std::unordered_map<std::string, std::string>{\n      {\"name\", stmt->getName()},\n      {\"kind\", \"class\"},\n      {\"type\", isRecord ? \"type\" : \"class\"}});\n  int id = ctx->shared->itemID++;\n\n  bool isExtend = false;\n  for (auto &d : stmt->getDecorators())\n    if (auto e = cast<IdExpr>(d))\n      isExtend |= (e->getValue() == \"extend\");\n\n  if (isExtend) {\n    j->set(\"type\", \"extension\");\n    auto i = ctx->find(stmt->getName());\n    j->set(\"parent\", std::to_string(*i));\n    generics = ctx->shared->generics[*i];\n  } else {\n    ctx->add(stmt->getName(), std::make_shared<int>(id));\n  }\n\n  std::vector<std::shared_ptr<json>> args;\n  for (const auto &a : *stmt)\n    if (!a.isValue()) {\n      generics.push_back(a.name);\n    }\n  ctx->shared->generics[id] = generics;\n  for (auto &g : generics)\n    ctx->add(g, std::make_shared<int>(0));\n  for (const auto &a : *stmt) {\n    auto ja = std::make_shared<json>();\n    ja->set(\"name\", a.name);\n    if (a.type) {\n      auto tt = transform(a.type);\n      if (tt->values.empty()) {\n        LOG(\"{}: warning: cannot resolve argument {}\", a.type->getSrcInfo(),\n            FormatVisitor::apply(a.type));\n        ja->set(\"type\", FormatVisitor::apply(a.type));\n      } else {\n        ja->set(\"type\", tt);\n      }\n    }\n    if (a.defaultValue)\n      ja->set(\"default\", FormatVisitor::apply(a.defaultValue));\n    args.push_back(ja);\n  }\n  j->set(\"generics\", std::make_shared<json>(generics));\n  j->set(\"args\", std::make_shared<json>(args));\n  j->set(\"pos\", jsonify(stmt->getSrcInfo()));\n\n  std::string docstr;\n  std::vector<std::string> members;\n  for (auto &f : flatten(stmt->getSuite(), &docstr)) {\n    if (auto ff = cast<FunctionStmt>(f)) {\n      auto i = transform(f);\n      if (i != \"\")\n        members.push_back(i);\n      if (isValidName(ff->getName()))\n        ctx->remove(ff->getName());\n    }\n  }\n  for (auto &g : generics)\n    ctx->remove(g);\n  j->set(\"members\", std::make_shared<json>(members));\n  if (!docstr.empty())\n    j->set(\"doc\", docstr);\n  ctx->shared->j->set(std::to_string(id), j);\n  resultStmt = std::to_string(id);\n}\n\nstd::shared_ptr<json> DocVisitor::jsonify(const codon::SrcInfo &s) {\n  return std::make_shared<json>(\n      std::vector<std::string>{std::to_string(s.line), std::to_string(s.len)});\n}\n\nvoid DocVisitor::visit(ImportStmt *stmt) {\n  if (match(stmt->getFrom(), M<IdExpr>(MOr(\"C\", \"python\")))) {\n    int id = ctx->shared->itemID++;\n    std::string name, lib;\n    if (auto i = cast<IdExpr>(stmt->getWhat()))\n      name = i->getValue();\n    else if (auto d = cast<DotExpr>(stmt->getWhat()))\n      name = d->getMember(), lib = FormatVisitor::apply(d->getExpr());\n    else\n      seqassert(false, \"invalid C import statement\");\n    ctx->add(name, std::make_shared<int>(id));\n    name = stmt->getAs().empty() ? name : stmt->getAs();\n\n    auto dict = std::unordered_map<std::string, std::string>{{\"name\", name},\n                                                             {\"kind\", \"function\"}};\n    if (stmt->getFrom() && cast<IdExpr>(stmt->getFrom()))\n      dict[\"extern\"] = cast<IdExpr>(stmt->getFrom())->getValue();\n    auto j = std::make_shared<json>(dict);\n    j->set(\"pos\", jsonify(stmt->getSrcInfo()));\n    std::vector<std::shared_ptr<json>> args;\n    if (stmt->getReturnType())\n      j->set(\"return\", transform(stmt->getReturnType()));\n    for (const auto &a : stmt->getArgs()) {\n      auto ja = std::make_shared<json>();\n      ja->set(\"name\", a.name);\n      ja->set(\"type\", transform(a.type));\n      args.push_back(ja);\n    }\n    j->set(\"dylib\", lib);\n    j->set(\"args\", std::make_shared<json>(args));\n    ctx->shared->j->set(std::to_string(id), j);\n    resultStmt = std::to_string(id);\n    return;\n  }\n\n  std::vector<std::string> dirs; // Path components\n  Expr *e = stmt->getFrom();\n  while (cast<DotExpr>(e)) {\n    while (auto d = cast<DotExpr>(e)) {\n      dirs.push_back(d->getMember());\n      e = d->getExpr();\n    }\n    if (!cast<IdExpr>(e) || !stmt->getArgs().empty() || stmt->getReturnType() ||\n        (stmt->getWhat() && !cast<IdExpr>(stmt->getWhat())))\n      E(Error::CUSTOM, stmt->getSrcInfo(), \"invalid import statement\");\n  }\n  if (auto ee = cast<IdExpr>(e)) {\n    if (!stmt->getArgs().empty() || stmt->getReturnType() ||\n        (stmt->getWhat() && !cast<IdExpr>(stmt->getWhat())))\n      E(Error::CUSTOM, stmt->getSrcInfo(), \"invalid import statement\");\n    // We have an empty stmt->from in \"from .. import\".\n    if (!ee->getValue().empty())\n      dirs.push_back(ee->getValue());\n  }\n  // Handle dots (e.g. .. in from ..m import x).\n  for (size_t i = 1; i < stmt->getDots(); i++)\n    dirs.emplace_back(\"..\");\n  std::string path;\n  for (int i = static_cast<int>(dirs.size()) - 1; i >= 0; i--)\n    path += dirs[i] + (i ? \"/\" : \"\");\n  // Fetch the import!\n  auto file = getImportFile(ctx->shared->cache, path, ctx->getFilename());\n  if (!file)\n    E(Error::CUSTOM, stmt->getSrcInfo(), \"cannot locate import '{}'\", path);\n\n  auto ictx = ctx;\n  auto it = ctx->shared->modules.find(file->path);\n  if (it == ctx->shared->modules.end()) {\n    ctx->shared->modules[file->path] = ictx = std::make_shared<DocContext>(ctx->shared);\n    ictx->setFilename(file->path);\n    // LOG(\"=> parsing {}\", file->path);\n    auto tmpOrErr = parseFile(ctx->shared->cache, file->path);\n    if (!tmpOrErr)\n      throw exc::ParserException(tmpOrErr.takeError());\n    DocVisitor(ictx).transformModule(*tmpOrErr);\n  } else {\n    ictx = it->second;\n  }\n\n  if (!stmt->getWhat()) {\n    // TODO: implement this corner case\n    for (auto &i : dirs)\n      if (!ctx->find(i))\n        ctx->add(i, std::make_shared<int>(ctx->shared->itemID++));\n  } else if (isId(stmt->getWhat(), \"*\")) {\n    for (auto &i : *ictx)\n      ctx->add(i.first, i.second.front());\n  } else {\n    auto i = cast<IdExpr>(stmt->getWhat());\n    if (auto c = ictx->find(i->getValue()))\n      ctx->add(stmt->getAs().empty() ? i->getValue() : stmt->getAs(), c);\n    else\n      E(Error::CUSTOM, stmt->getSrcInfo(), \"symbol '{}' not found in {}\", i->getValue(),\n        file->path);\n  }\n}\n\nvoid DocVisitor::visit(AssignStmt *stmt) {\n  auto e = cast<IdExpr>(stmt->getLhs());\n  if (!e)\n    return;\n  int id = ctx->shared->itemID++;\n  ctx->add(e->getValue(), std::make_shared<int>(id));\n  auto j = std::make_shared<json>(std::unordered_map<std::string, std::string>{\n      {\"name\", e->getValue()}, {\"kind\", \"variable\"}});\n  if (stmt->getTypeExpr())\n    j->set(\"type\", transform(stmt->getTypeExpr()));\n  if (stmt->getRhs())\n    j->set(\"value\", FormatVisitor::apply(stmt->getRhs()));\n  j->set(\"pos\", jsonify(stmt->getSrcInfo()));\n  ctx->shared->j->set(std::to_string(id), j);\n  resultStmt = std::to_string(id);\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/doc/doc.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/ctx.h\"\n#include \"codon/parser/visitors/visitor.h\"\n\nnamespace codon::ast {\n\nstruct json {\n  // values={str -> null} -> string value\n  // values={i -> json} -> list (if list=true)\n  // values={...} -> dictionary\n  std::unordered_map<std::string, std::shared_ptr<json>> values;\n  bool list;\n\n  json();\n  json(const std::string &s);\n  json(const std::string &s, const std::string &v);\n  json(const std::vector<std::shared_ptr<json>> &vs);\n  json(const std::vector<std::string> &vs);\n  json(const std::unordered_map<std::string, std::string> &vs);\n  std::string toString();\n  std::shared_ptr<json> get(const std::string &s);\n  std::shared_ptr<json> set(const std::string &s, const std::string &value);\n  std::shared_ptr<json> set(const std::string &s, const std::shared_ptr<json> &value);\n};\n\nstruct DocContext;\nstruct DocShared {\n  int itemID = 1;\n  std::shared_ptr<json> j;\n  std::unordered_map<std::string, std::shared_ptr<DocContext>> modules;\n  std::string argv0;\n  Cache *cache = nullptr;\n  std::unordered_map<int, std::vector<std::string>> generics;\n  DocShared() {}\n};\n\nstruct DocContext : public Context<int> {\n  std::shared_ptr<DocShared> shared;\n  explicit DocContext(std::shared_ptr<DocShared> shared)\n      : Context<int>(\"\"), shared(std::move(shared)) {}\n  std::shared_ptr<int> find(const std::string &s) const override;\n};\n\nstruct DocVisitor : public CallbackASTVisitor<std::shared_ptr<json>, std::string> {\n  std::shared_ptr<DocContext> ctx;\n  std::shared_ptr<json> resultExpr;\n  std::string resultStmt;\n\npublic:\n  explicit DocVisitor(std::shared_ptr<DocContext> ctx) : ctx(std::move(ctx)) {}\n  static std::shared_ptr<json> apply(const std::string &argv0,\n                                     const std::vector<std::string> &files);\n\n  std::shared_ptr<json> transform(Expr *e) override;\n  std::string transform(Stmt *e) override;\n\n  void transformModule(Stmt *stmt);\n  static std::shared_ptr<json> jsonify(const codon::SrcInfo &s);\n  static std::vector<Stmt *> flatten(Stmt *stmt, std::string *docstr = nullptr,\n                                     bool deep = true);\n\npublic:\n  void visit(IntExpr *) override;\n  void visit(IdExpr *) override;\n  void visit(IndexExpr *) override;\n  void visit(FunctionStmt *) override;\n  void visit(ClassStmt *) override;\n  void visit(AssignStmt *) override;\n  void visit(ImportStmt *) override;\n};\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/format/format.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <string>\n#include <vector>\n\n#include \"codon/parser/visitors/format/format.h\"\n\nnamespace codon {\nnamespace ast {\n\nstd::string FormatVisitor::anchor_root(const std::string &s) {\n  return fmt::format(\"<a class=\\\".root\\\" name=\\\"{}\\\">{}</a>\", s, s);\n}\n\nstd::string FormatVisitor::anchor(const std::string &s) {\n  return fmt::format(\"<a class=\\\".anchor\\\" href=\\\"#{}\\\">{}</a>\", s, s);\n}\n\nFormatVisitor::FormatVisitor(bool html, Cache *cache)\n    : renderType(false), renderHTML(html), indent(0), cache(cache) {\n  if (renderHTML) {\n    header = \"<html><head><link rel=stylesheet href=\\\"../code.css\\\"/></head>\\n<body>\";\n    header += \"<div class=code>\\n\";\n    footer = \"\\n</div></body></html>\";\n    nl = \"<br/>\";\n    typeStart = \"<ast-type>\";\n    typeEnd = \"</ast-type>\";\n    nodeStart = \"\";\n    nodeEnd = \"\";\n    stmtStart = \"<ast-stmt>\";\n    stmtEnd = \"</ast-stmt>\";\n    exprStart = \"<ast-expr>\";\n    exprEnd = \"</ast-expr>\";\n    commentStart = \"<ast-comment>\";\n    commentEnd = \"</ast-comment>\";\n    literalStart = \"<ast-expr class=lit>\";\n    literalEnd = \"</ast-expr>\";\n    keywordStart = \"<ast-keyword>\";\n    keywordEnd = \"</ast-keyword>\";\n    space = \"&nbsp;\";\n    renderType = true;\n  } else {\n    space = \" \";\n  }\n}\n\nstd::string FormatVisitor::transform(Expr *expr) {\n  FormatVisitor v(renderHTML, cache);\n  if (expr)\n    expr->accept(v);\n  return v.result;\n}\n\nstd::string FormatVisitor::transform(Stmt *stmt) { return transform(stmt, 0); }\n\nstd::string FormatVisitor::transform(Stmt *stmt, int indent) {\n  FormatVisitor v(renderHTML, cache);\n  v.indent = this->indent + indent;\n  if (stmt)\n    stmt->accept(v);\n  if (v.result.empty())\n    return \"\";\n  return fmt::format(\"{}{}{}{}{}\", stmtStart, cast<SuiteStmt>(stmt) ? \"\" : pad(indent),\n                     v.result, stmtEnd, newline());\n}\n\nstd::string FormatVisitor::pad(int indent) const {\n  std::string s;\n  for (int i = 0; i < (this->indent + indent) * 2; i++)\n    s += space;\n  return s;\n}\n\nstd::string FormatVisitor::newline() const { return nl + \"\\n\"; }\n\nstd::string FormatVisitor::keyword(const std::string &s) const {\n  return fmt::format(\"{}{}{}\", keywordStart, s, keywordEnd);\n}\n\nstd::string FormatVisitor::literal(const std::string &s) const {\n  return fmt::format(\"{}{}{}\", literalStart, s, literalEnd);\n}\n\n/*************************************************************************************/\n\nvoid FormatVisitor::visit(NoneExpr *expr) { result = renderExpr(expr, \"None\"); }\n\nvoid FormatVisitor::visit(BoolExpr *expr) {\n  result = renderExpr(expr, \"{}\", literal(expr->getValue() ? \"True\" : \"False\"));\n}\n\nvoid FormatVisitor::visit(IntExpr *expr) {\n  auto [value, suffix] = expr->getRawData();\n  result = renderExpr(expr, \"{}{}\", literal(value), suffix);\n}\n\nvoid FormatVisitor::visit(FloatExpr *expr) {\n  auto [value, suffix] = expr->getRawData();\n  result = renderExpr(expr, \"{}{}\", literal(value), suffix);\n}\n\nvoid FormatVisitor::visit(StringExpr *expr) {\n  result =\n      renderExpr(expr, \"{}\", literal(fmt::format(\"\\\"{}\\\"\", escape(expr->getValue()))));\n}\n\nvoid FormatVisitor::visit(IdExpr *expr) {\n  result = renderExpr(expr, \"{}\",\n                      expr->getType() && expr->getType()->getFunc()\n                          ? anchor(expr->getValue())\n                          : expr->getValue());\n}\n\nvoid FormatVisitor::visit(StarExpr *expr) {\n  result = renderExpr(expr, \"*{}\", transform(expr->getExpr()));\n}\n\nvoid FormatVisitor::visit(KeywordStarExpr *expr) {\n  result = renderExpr(expr, \"**{}\", transform(expr->getExpr()));\n}\n\nvoid FormatVisitor::visit(TupleExpr *expr) {\n  result = renderExpr(expr, \"({})\", transformItems(*expr));\n}\n\nvoid FormatVisitor::visit(ListExpr *expr) {\n  result = renderExpr(expr, \"[{}]\", transformItems(*expr));\n}\n\nvoid FormatVisitor::visit(InstantiateExpr *expr) {\n  result =\n      renderExpr(expr, \"{}⟦{}⟧\", transform(expr->getExpr()), transformItems(*expr));\n}\n\nvoid FormatVisitor::visit(SetExpr *expr) {\n  result = renderExpr(expr, \"{{{}}}\", transformItems(*expr));\n}\n\nvoid FormatVisitor::visit(DictExpr *expr) {\n  std::vector<std::string> s;\n  for (auto *i : *expr) {\n    auto t = cast<TupleExpr>(i);\n    s.push_back(fmt::format(\"{}: {}\", transform((*t)[0]), transform((*t)[1])));\n  }\n  result = renderExpr(expr, \"{{{}}}\", join(s, \", \"));\n}\n\nvoid FormatVisitor::visit(GeneratorExpr *expr) {\n  // seqassert(false, \"not implemented\");\n  result = \"GENERATOR_IMPL\";\n}\n\nvoid FormatVisitor::visit(IfExpr *expr) {\n  result = renderExpr(expr, \"({} {} {} {} {})\", transform(expr->getIf()), keyword(\"if\"),\n                      transform(expr->getCond()), keyword(\"else\"),\n                      transform(expr->getElse()));\n}\n\nvoid FormatVisitor::visit(UnaryExpr *expr) {\n  result = renderExpr(expr, \"{}{}\", expr->getOp(), transform(expr->getExpr()));\n}\n\nvoid FormatVisitor::visit(BinaryExpr *expr) {\n  result = renderExpr(expr, \"({} {} {})\", transform(expr->getLhs()), expr->getOp(),\n                      transform(expr->getRhs()));\n}\n\nvoid FormatVisitor::visit(PipeExpr *expr) {\n  std::vector<std::string> items;\n  for (const auto &l : *expr) {\n    if (!items.size())\n      items.push_back(transform(l.expr));\n    else\n      items.push_back(l.op + \" \" + transform(l.expr));\n  }\n  result = renderExpr(expr, \"({})\", join(items, \" \"));\n}\n\nvoid FormatVisitor::visit(IndexExpr *expr) {\n  result = renderExpr(expr, \"{}[{}]\", transform(expr->getExpr()),\n                      transform(expr->getIndex()));\n}\n\nvoid FormatVisitor::visit(CallExpr *expr) {\n  std::vector<std::string> args;\n  for (auto &i : *expr) {\n    if (i.name == \"\")\n      args.push_back(transform(i.value));\n    else\n      args.push_back(fmt::format(\"{}={}\", i.name, transform(i.value)));\n  }\n  result = renderExpr(expr, \"{}({})\", transform(expr->getExpr()), join(args, \", \"));\n}\n\nvoid FormatVisitor::visit(DotExpr *expr) {\n  result = renderExpr(expr, \"{}.{}\", transform(expr->getExpr()), expr->getMember());\n}\n\nvoid FormatVisitor::visit(SliceExpr *expr) {\n  std::string s;\n  if (expr->getStart())\n    s += transform(expr->getStart());\n  s += \":\";\n  if (expr->getStop())\n    s += transform(expr->getStop());\n  s += \":\";\n  if (expr->getStep())\n    s += transform(expr->getStep());\n  result = renderExpr(expr, \"{}\", s);\n}\n\nvoid FormatVisitor::visit(EllipsisExpr *expr) { result = renderExpr(expr, \"...\"); }\n\nvoid FormatVisitor::visit(LambdaExpr *expr) {\n  std::vector<std::string> s;\n  for (const auto &v : *expr)\n    s.emplace_back(v.getName());\n  result = renderExpr(expr, \"{} {}: {}\", keyword(\"lambda\"), join(s, \", \"),\n                      transform(expr->getExpr()));\n}\n\nvoid FormatVisitor::visit(YieldExpr *expr) {\n  result = renderExpr(expr, \"{}\", \"(\" + keyword(\"yield\") + \")\");\n}\n\nvoid FormatVisitor::visit(AwaitExpr *expr) {\n  result = fmt::format(\"{} {}\", keyword(\"await\"), transform(expr->getExpr()));\n}\n\nvoid FormatVisitor::visit(StmtExpr *expr) {\n  std::string s;\n  for (auto *i : *expr)\n    s += fmt::format(\"{}{}\", pad(2), transform(i, 2));\n  result = renderExpr(expr, \"《{}{}{}{}{}》\", newline(), s, newline(), pad(2),\n                      transform(expr->getExpr()));\n}\n\nvoid FormatVisitor::visit(AssignExpr *expr) {\n  result = renderExpr(expr, \"({} := {})\", transform(expr->getVar()),\n                      transform(expr->getExpr()));\n}\n\nvoid FormatVisitor::visit(SuiteStmt *stmt) {\n  for (auto *s : *stmt)\n    result += transform(s);\n}\n\nvoid FormatVisitor::visit(BreakStmt *stmt) { result = keyword(\"break\"); }\n\nvoid FormatVisitor::visit(ContinueStmt *stmt) { result = keyword(\"continue\"); }\n\nvoid FormatVisitor::visit(ExprStmt *stmt) { result = transform(stmt->getExpr()); }\n\nvoid FormatVisitor::visit(AssignStmt *stmt) {\n  if (stmt->getTypeExpr()) {\n    result = fmt::format(\"{}: {} = {}\", transform(stmt->getLhs()),\n                         transform(stmt->getTypeExpr()), transform(stmt->getRhs()));\n  } else {\n    result =\n        fmt::format(\"{} = {}\", transform(stmt->getLhs()), transform(stmt->getRhs()));\n  }\n}\n\nvoid FormatVisitor::visit(AssignMemberStmt *stmt) {\n  result = fmt::format(\"{}.{} = {}\", transform(stmt->getLhs()), stmt->getMember(),\n                       transform(stmt->getRhs()));\n}\n\nvoid FormatVisitor::visit(DelStmt *stmt) {\n  result = fmt::format(\"{} {}\", keyword(\"del\"), transform(stmt->getExpr()));\n}\n\nvoid FormatVisitor::visit(PrintStmt *stmt) {\n  result = fmt::format(\"{} {}\", keyword(\"print\"), transformItems(*stmt));\n}\n\nvoid FormatVisitor::visit(ReturnStmt *stmt) {\n  result = fmt::format(\"{}{}\", keyword(\"return\"),\n                       stmt->getExpr() ? \" \" + transform(stmt->getExpr()) : \"\");\n}\n\nvoid FormatVisitor::visit(YieldStmt *stmt) {\n  result = fmt::format(\"{}{}\", keyword(\"yield\"),\n                       stmt->getExpr() ? \" \" + transform(stmt->getExpr()) : \"\");\n}\n\nvoid FormatVisitor::visit(AssertStmt *stmt) {\n  result = fmt::format(\"{} {}\", keyword(\"assert\"), transform(stmt->getExpr()));\n}\n\nvoid FormatVisitor::visit(WhileStmt *stmt) {\n  result = fmt::format(\"{} {}:{}{}\", keyword(\"while\"), transform(stmt->getCond()),\n                       newline(), transform(stmt->getSuite(), 1));\n}\n\nvoid FormatVisitor::visit(ForStmt *stmt) {\n  result = fmt::format(\"{} {} {} {}:{}{}\", keyword(\"for\"), transform(stmt->getVar()),\n                       keyword(\"in\"), transform(stmt->getIter()), newline(),\n                       transform(stmt->getSuite(), 1));\n}\n\nvoid FormatVisitor::visit(IfStmt *stmt) {\n  result =\n      fmt::format(\"{} {}:{}{}{}\", keyword(\"if\"), transform(stmt->getCond()), newline(),\n                  transform(stmt->getIf(), 1),\n                  stmt->getElse() ? fmt::format(\"{}:{}{}\", keyword(\"else\"), newline(),\n                                                transform(stmt->getElse(), 1))\n                                  : \"\");\n}\n\nvoid FormatVisitor::visit(MatchStmt *stmt) {\n  std::string s;\n  for (const auto &c : *stmt)\n    s += fmt::format(\n        \"{}{}{}{}:{}{}\", pad(1), keyword(\"case\"), transform(c.getPattern()),\n        c.getGuard() ? \" \" + (keyword(\"case\") + \" \" + transform(c.getGuard())) : \"\",\n        newline(), transform(c.getSuite(), 2));\n  result = fmt::format(\"{} {}:{}{}\", keyword(\"match\"), transform(stmt->getExpr()),\n                       newline(), s);\n}\n\nvoid FormatVisitor::visit(ImportStmt *stmt) {\n  auto as =\n      stmt->getAs().empty() ? \"\" : fmt::format(\" {} {} \", keyword(\"as\"), stmt->getAs());\n  if (!stmt->getWhat())\n    result += fmt::format(\"{} {}{}\", keyword(\"import\"), transform(stmt->getFrom()), as);\n  else\n    result += fmt::format(\"{} {} {} {}{}\", keyword(\"from\"), transform(stmt->getFrom()),\n                          keyword(\"import\"), transform(stmt->getWhat()), as);\n}\n\nvoid FormatVisitor::visit(TryStmt *stmt) {\n  std::vector<std::string> catches;\n  for (auto *c : *stmt) {\n    catches.push_back(fmt::format(\n        \"{} {}{}:{}{}\", keyword(\"except\"), transform(c->getException()),\n        c->getVar() == \"\" ? \"\" : fmt::format(\"{} {}\", keyword(\"as\"), c->getVar()),\n        newline(), transform(c->getSuite(), 1)));\n  }\n  result = fmt::format(\"{}:{}{}{}{}\", keyword(\"try\"), newline(),\n                       transform(stmt->getSuite(), 1), join(catches, \"\"),\n                       stmt->getFinally()\n                           ? fmt::format(\"{}:{}{}\", keyword(\"finally\"), newline(),\n                                         transform(stmt->getFinally(), 1))\n                           : \"\");\n}\n\nvoid FormatVisitor::visit(GlobalStmt *stmt) {\n  result = fmt::format(\"{} {}\", keyword(\"global\"), stmt->getVar());\n}\n\nvoid FormatVisitor::visit(ThrowStmt *stmt) {\n  result = fmt::format(\"{} {}{}\", keyword(\"raise\"), transform(stmt->getExpr()),\n                       stmt->getFrom() ? fmt::format(\" {} {}\", keyword(\"from\"),\n                                                     transform(stmt->getFrom()))\n                                       : \"\");\n}\n\nvoid FormatVisitor::visit(FunctionStmt *fstmt) {\n  if (cache) {\n    if (in(cache->functions, fstmt->getName())) {\n      if (!cache->functions[fstmt->getName()].realizations.empty()) {\n        result += fmt::format(\"<details><summary># {}</summary>\",\n                              fmt::format(\"{} {}\", keyword(\"def\"), fstmt->getName()));\n        for (auto &val :\n             cache->functions[fstmt->getName()].realizations | std::views::values) {\n          auto fa = val->ast;\n          auto ft = val->type;\n          std::vector<std::string> attrs;\n          for (const auto &a : fa->getDecorators())\n            attrs.push_back(fmt::format(\"@{}\", transform(a)));\n          if (auto a = fa->getAttribute<ir::StringValueAttribute>(Attr::Module))\n            if (!a->value.empty())\n              attrs.push_back(fmt::format(\"@module:{}\", a->value));\n          if (auto a = fa->getAttribute<ir::StringValueAttribute>(Attr::ParentClass))\n            if (!a->value.empty())\n              attrs.push_back(fmt::format(\"@parent:{}\", a->value));\n          std::vector<std::string> args;\n          for (size_t i = 0, j = 0; i < fa->size(); i++) {\n            auto &a = (*fa)[i];\n            if (a.isValue()) {\n              args.push_back(fmt::format(\n                  \"{}: {}{}\", a.getName(), anchor((*ft)[j++]->realizedName()),\n                  a.getDefault() ? fmt::format(\"={}\", transform(a.getDefault())) : \"\"));\n            }\n          }\n          auto body = transform(fa->getSuite(), 1);\n          auto name = fmt::format(\"{}\", anchor_root(fa->getName()));\n          result += fmt::format(\n              \"{}{}{}{} {}({}){}:{}{}\", newline(), pad(),\n              attrs.size() ? join(attrs, newline() + pad()) + newline() + pad() : \"\",\n              keyword(\"def\"), anchor_root(name), join(args, \", \"),\n              fmt::format(\" -> {}\", anchor(ft->getRetType()->realizedName())),\n              newline(), body.empty() ? fmt::format(\"{}\", keyword(\"pass\")) : body);\n        }\n        result += \"</details>\";\n      }\n      return;\n    }\n  }\n}\n\nvoid FormatVisitor::visit(ClassStmt *stmt) {\n  if (cache) {\n    if (auto cls = in(cache->classes, stmt->getName())) {\n      if (!cls->realizations.empty()) {\n        result = fmt::format(\n            \"<details><summary># {}</summary>\",\n            fmt::format(\"{} {} {}\", keyword(\"class\"), stmt->getName(),\n                        stmt->hasAttribute(Attr::Extend) ? \" +@extend\" : \"\"));\n        for (auto &real : cls->realizations) {\n          std::vector<std::string> args;\n          auto l = real.second->type->is(TYPE_TUPLE)\n                       ? real.second->type->generics.size()\n                       : real.second->fields.size();\n          for (size_t i = 0; i < l; i++) {\n            const auto &[n, t] = real.second->fields[i];\n            auto name = fmt::format(\"{}{}: {}{}\", exprStart, n,\n                                    anchor(t->realizedName()), exprEnd);\n            args.push_back(name);\n          }\n          result += fmt::format(\"{}{}{}{} {}\", newline(), pad(),\n                                (stmt->hasAttribute(Attr::Tuple)\n                                     ? fmt::format(\"@tuple{}{}\", newline(), pad())\n                                     : \"\"),\n                                keyword(\"class\"), anchor_root(real.first));\n          if (!args.empty())\n            result += fmt::format(\":{}{}{}\", newline(), pad(indent + 1),\n                                  join(args, newline() + pad(indent + 1)));\n        }\n        result += \"</details>\";\n      }\n    }\n  }\n  // if (stmt->suite)\n  //   result += transform(stmt->suite);\n}\n\nvoid FormatVisitor::visit(YieldFromStmt *stmt) {\n  result = fmt::format(\"{} {}\", keyword(\"yield from\"), transform(stmt->getExpr()));\n}\n\nvoid FormatVisitor::visit(WithStmt *stmt) {}\n\nvoid FormatVisitor::visit(CommentStmt *stmt) {\n  result = fmt::format(\"{}# {}{}\", commentStart, stmt->getComment(), commentEnd);\n}\n\nvoid FormatVisitor::visit(DirectiveStmt *stmt) {}\n\n} // namespace ast\n} // namespace codon\n"
  },
  {
    "path": "codon/parser/visitors/format/format.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <ostream>\n#include <string>\n#include <vector>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/visitors/visitor.h\"\n\nnamespace codon {\nnamespace ast {\n\nclass FormatVisitor : public CallbackASTVisitor<std::string, std::string> {\n  std::string result;\n  std::string space;\n  bool renderType, renderHTML;\n  int indent;\n\n  std::string header, footer, nl;\n  std::string typeStart, typeEnd;\n  std::string nodeStart, nodeEnd;\n  std::string stmtStart, stmtEnd;\n  std::string exprStart, exprEnd;\n  std::string commentStart, commentEnd;\n  std::string keywordStart, keywordEnd;\n  std::string literalStart, literalEnd;\n\n  Cache *cache;\n\nprivate:\n  template <typename T, typename... Ts>\n  std::string renderExpr(T &&t, const char *fmt, Ts &&...args) {\n    std::string s = t->getType()\n                        ? fmt::format(\"{}{}{}\", typeStart,\n                                      anchor(t->getType()->realizedName()), typeEnd)\n                        : \"\";\n    return fmt::format(\"{}{}{}{}{}{}\", exprStart, nodeStart,\n                       fmt::format(fmt::runtime(fmt), args...), nodeEnd, s, exprEnd);\n  }\n  template <typename... Ts> std::string renderComment(const char *fmt, Ts &&...args) {\n    return fmt::format(\"{}{}{}\", commentStart, fmt::format(fmt::runtime(fmt), args...),\n                       commentEnd);\n  }\n  std::string pad(int indent = 0) const;\n  std::string newline() const;\n  std::string keyword(const std::string &s) const;\n  std::string literal(const std::string &s) const;\n  static std::string anchor_root(const std::string &s);\n  static std::string anchor(const std::string &s);\n\npublic:\n  FormatVisitor(bool html, Cache *cache = nullptr);\n  std::string transform(Expr *e) override;\n  std::string transform(Stmt *stmt) override;\n  std::string transform(Stmt *stmt, int indent);\n\n  template <typename T>\n  static std::string apply(const T &stmt, Cache *cache = nullptr, bool html = false,\n                           bool init = false) {\n    auto t = FormatVisitor(html, cache);\n    return fmt::format(\"{}{}{}\", t.header, t.transform(stmt), t.footer);\n  }\n\n  void defaultVisit(Expr *e) override { seqassertn(false, \"cannot format {}\", *e); }\n  void defaultVisit(Stmt *e) override { seqassertn(false, \"cannot format {}\", *e); }\n\npublic:\n  void visit(NoneExpr *) override;\n  void visit(BoolExpr *) override;\n  void visit(IntExpr *) override;\n  void visit(FloatExpr *) override;\n  void visit(StringExpr *) override;\n  void visit(IdExpr *) override;\n  void visit(StarExpr *) override;\n  void visit(KeywordStarExpr *) override;\n  void visit(TupleExpr *) override;\n  void visit(ListExpr *) override;\n  void visit(SetExpr *) override;\n  void visit(DictExpr *) override;\n  void visit(GeneratorExpr *) override;\n  void visit(InstantiateExpr *expr) override;\n  void visit(IfExpr *) override;\n  void visit(UnaryExpr *) override;\n  void visit(BinaryExpr *) override;\n  void visit(PipeExpr *) override;\n  void visit(IndexExpr *) override;\n  void visit(CallExpr *) override;\n  void visit(DotExpr *) override;\n  void visit(SliceExpr *) override;\n  void visit(EllipsisExpr *) override;\n  void visit(LambdaExpr *) override;\n  void visit(YieldExpr *) override;\n  void visit(AwaitExpr *) override;\n  void visit(StmtExpr *expr) override;\n  void visit(AssignExpr *expr) override;\n\n  void visit(SuiteStmt *) override;\n  void visit(BreakStmt *) override;\n  void visit(ContinueStmt *) override;\n  void visit(ExprStmt *) override;\n  void visit(AssignStmt *) override;\n  void visit(AssignMemberStmt *) override;\n  void visit(DelStmt *) override;\n  void visit(PrintStmt *) override;\n  void visit(ReturnStmt *) override;\n  void visit(YieldStmt *) override;\n  void visit(AssertStmt *) override;\n  void visit(WhileStmt *) override;\n  void visit(ForStmt *) override;\n  void visit(IfStmt *) override;\n  void visit(MatchStmt *) override;\n  void visit(ImportStmt *) override;\n  void visit(TryStmt *) override;\n  void visit(GlobalStmt *) override;\n  void visit(ThrowStmt *) override;\n  void visit(FunctionStmt *) override;\n  void visit(ClassStmt *) override;\n  void visit(YieldFromStmt *) override;\n  void visit(WithStmt *) override;\n  void visit(CommentStmt *) override;\n  void visit(DirectiveStmt *) override;\n\npublic:\n  friend std::ostream &operator<<(std::ostream &out, const FormatVisitor &c) {\n    return out << c.result;\n  }\n\n  using CallbackASTVisitor<std::string, std::string>::transform;\n  template <typename T> std::string transformItems(const T &ts) {\n    std::vector<std::string> r;\n    for (auto &e : ts)\n      r.push_back(transform(e));\n    return fmt::format(\"{}\", join(r, \", \"));\n  }\n};\n\n} // namespace ast\n} // namespace codon\n\ntemplate <>\nstruct fmt::formatter<codon::ast::FormatVisitor> : fmt::ostream_formatter {};\n"
  },
  {
    "path": "codon/parser/visitors/scoping/scoping.cpp",
    "content": "// Copyright (C) 2022-2023 Exaloop Inc. <https://exaloop.io>\n\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/match.h\"\n#include \"codon/parser/peg/peg.h\"\n#include \"codon/parser/visitors/scoping/scoping.h\"\n\n#define CHECK(x)                                                                       \\\n  {                                                                                    \\\n    if (!(x))                                                                          \\\n      return;                                                                          \\\n  }\n#define STOP_ERROR(...)                                                                \\\n  do {                                                                                 \\\n    addError(__VA_ARGS__);                                                             \\\n    return;                                                                            \\\n  } while (0)\n\nusing namespace codon::error;\nusing namespace codon::matcher;\n\nnamespace codon::ast {\n\nllvm::Error\nScopingVisitor::apply(Cache *cache, Stmt *s,\n                      std::unordered_map<std::string, size_t> *globalShadows) {\n  auto c = std::make_shared<ScopingVisitor::Context>();\n  c->cache = cache;\n  c->functionScope = nullptr;\n  ScopingVisitor v;\n  v.ctx = c;\n\n  ConditionalBlock cb(c.get(), s, 0);\n  if (!v.transform(s))\n    return llvm::make_error<ParserErrorInfo>(v.errors);\n  if (v.hasErrors())\n    return llvm::make_error<ParserErrorInfo>(v.errors);\n  v.processChildCaptures();\n\n  /// Count number of shadowed names to know which names change or not later on\n  if (globalShadows) {\n    for (auto &[u, v] : c->map) {\n      size_t i = 0;\n      for (auto &ii : v)\n        if (!ii.ignore)\n          i++;\n      if (i > 1)\n        (*globalShadows)[u] = i;\n    }\n  }\n\n  // LOG(\"-> {}\", s->toString(2));\n  return llvm::Error::success();\n}\n\nbool ScopingVisitor::transform(Expr *expr) {\n  if (!canContinue())\n    return false;\n\n  ScopingVisitor v(*this);\n  if (expr) {\n    v.setSrcInfo(expr->getSrcInfo());\n    expr->accept(v);\n    if (v.hasErrors())\n      errors.append(v.errors);\n    if (!canContinue())\n      return false;\n  }\n  return true;\n}\n\nbool ScopingVisitor::transform(Stmt *stmt) {\n  if (!canContinue())\n    return false;\n\n  ScopingVisitor v(*this);\n  if (stmt) {\n    v.setSrcInfo(stmt->getSrcInfo());\n    stmt->setAttribute(Attr::ExprTime, ++ctx->time);\n    stmt->accept(v);\n    if (v.hasErrors())\n      errors.append(v.errors);\n    if (!canContinue())\n      return false;\n  }\n  return true;\n}\n\nbool ScopingVisitor::transformScope(Expr *e) {\n  if (e) {\n    ConditionalBlock c(ctx.get(), nullptr);\n    return transform(e);\n  }\n  return true;\n}\n\nbool ScopingVisitor::transformScope(Stmt *s) {\n  if (s) {\n    ConditionalBlock c(ctx.get(), s);\n    return transform(s);\n  }\n  return true;\n}\n\nbool ScopingVisitor::transformAdding(Expr *e, ASTNode *root) {\n  if (cast<IndexExpr>(e)) {\n    return transform(e);\n  } else if (auto de = cast<DotExpr>(e)) {\n    if (!transform(e))\n      return false;\n    if (!ctx->classDeduce.first.empty() &&\n        match(de->getExpr(), M<IdExpr>(ctx->classDeduce.first)))\n      ctx->classDeduce.second.insert(de->getMember());\n    return true;\n  } else if (cast<ListExpr>(e) || cast<TupleExpr>(e) || cast<IdExpr>(e)) {\n    SetInScope s1(&(ctx->adding), true);\n    SetInScope s2(&(ctx->root), root);\n    return transform(e);\n  } else {\n    seqassert(e, \"bad call to transformAdding\");\n    addError(Error::ASSIGN_INVALID, e);\n    return false;\n  }\n}\n\nvoid ScopingVisitor::visit(IdExpr *expr) {\n  if (ctx->adding)\n    ctx->root = expr;\n  if (ctx->adding && ctx->tempScope)\n    ctx->renames.back()[expr->getValue()] =\n        ctx->cache->getTemporaryVar(expr->getValue());\n  for (size_t i = ctx->renames.size(); i-- > 0;)\n    if (auto v = in(ctx->renames[i], expr->getValue())) {\n      expr->setValue(*v);\n      break;\n    }\n  if (visitName(expr->getValue(), ctx->adding, ctx->root, expr->getSrcInfo()))\n    expr->setAttribute(Attr::ExprDominatedUndefCheck);\n}\n\nvoid ScopingVisitor::visit(DotExpr *expr) {\n  SetInScope s(&(ctx->adding), false); // to handle a.x, y = b\n  CallbackASTVisitor<bool, bool>::visit(expr);\n}\n\nvoid ScopingVisitor::visit(IndexExpr *expr) {\n  SetInScope s(&(ctx->adding), false); // to handle a[x], y = b\n  CallbackASTVisitor<bool, bool>::visit(expr);\n}\n\nvoid ScopingVisitor::visit(StringExpr *expr) {\n  std::vector<StringExpr::String> exprs;\n  for (auto &p : *expr) {\n    if (p.prefix == \"f\" || p.prefix == \"F\") {\n      /// Transform an F-string\n      auto fstr = unpackFString(p.value);\n      if (!canContinue())\n        return;\n      for (auto pf : fstr) {\n        if (pf.prefix.empty() && !exprs.empty() && exprs.back().prefix.empty()) {\n          exprs.back().value += pf.value;\n        } else {\n          exprs.emplace_back(pf);\n        }\n      }\n    } else if (!p.prefix.empty()) {\n      exprs.emplace_back(p);\n    } else if (!exprs.empty() && exprs.back().prefix.empty()) {\n      exprs.back().value += p.value;\n    } else {\n      exprs.emplace_back(p);\n    }\n  }\n  expr->strings = exprs;\n}\n\n/// Split a Python-like f-string into a list:\n///   `f\"foo {x+1} bar\"` -> `(\"foo \", str(x+1), \" bar\")\n/// Supports \"{x=}\" specifier (that prints the raw expression as well):\n///   `f\"{x+1=}\"` -> `(\"x+1=\", str(x+1))`\nstd::vector<StringExpr::String>\nScopingVisitor::unpackFString(const std::string &value) {\n  // Strings to be concatenated\n  std::vector<StringExpr::String> items;\n  int braceCount = 0, braceStart = 0;\n  for (int i = 0; i < value.size(); i++) {\n    if (value[i] == '{') {\n      if (braceStart < i)\n        items.emplace_back(value.substr(braceStart, i - braceStart));\n      if (!braceCount)\n        braceStart = i + 1;\n      braceCount++;\n    } else if (value[i] == '}') {\n      braceCount--;\n      if (!braceCount) {\n        std::string code = value.substr(braceStart, i - braceStart);\n\n        auto offset = getSrcInfo();\n        offset.col += i;\n        items.emplace_back(code, \"#f\");\n        items.back().setSrcInfo(offset);\n\n        auto val = parseExpr(ctx->cache, code, offset);\n        if (!val) {\n          addError(val.takeError());\n        } else {\n          items.back().expr = val->first;\n          if (!transform(items.back().expr))\n            return items;\n          items.back().format = val->second;\n        }\n      }\n      braceStart = i + 1;\n    }\n  }\n  if (braceCount > 0)\n    addError(Error::STR_FSTRING_BALANCE_EXTRA, getSrcInfo());\n  else if (braceCount < 0)\n    addError(Error::STR_FSTRING_BALANCE_MISSING, getSrcInfo());\n  if (braceStart != value.size())\n    items.emplace_back(value.substr(braceStart, value.size() - braceStart));\n  return items;\n}\n\nvoid ScopingVisitor::visit(GeneratorExpr *expr) {\n  SetInScope s(&(ctx->tempScope), true);\n  ctx->renames.emplace_back();\n  CHECK(transform(expr->getFinalSuite()));\n  ctx->renames.pop_back();\n}\n\nvoid ScopingVisitor::visit(IfExpr *expr) {\n  CHECK(transform(expr->getCond()));\n  CHECK(transformScope(expr->getIf()));\n  CHECK(transformScope(expr->getElse()));\n}\n\nvoid ScopingVisitor::visit(BinaryExpr *expr) {\n  CHECK(transform(expr->getLhs()));\n  if (expr->getOp() == \"&&\" || expr->getOp() == \"||\") {\n    CHECK(transformScope(expr->getRhs()));\n  } else {\n    CHECK(transform(expr->getRhs()));\n  }\n}\n\nvoid ScopingVisitor::visit(AssignExpr *expr) {\n  seqassert(cast<IdExpr>(expr->getVar()),\n            \"only simple assignment expression are supported\");\n\n  SetInScope s(&(ctx->tempScope), false);\n  CHECK(transform(expr->getExpr()));\n  CHECK(transformAdding(expr->getVar(), expr));\n}\n\nvoid ScopingVisitor::visit(LambdaExpr *expr) {\n  auto c = std::make_shared<ScopingVisitor::Context>();\n  c->cache = ctx->cache;\n  FunctionStmt f(\"lambda\", nullptr, {}, nullptr);\n  c->functionScope = &f;\n  c->renames = ctx->renames;\n  ScopingVisitor v;\n  c->scope.emplace_back(0, nullptr);\n  v.ctx = c;\n  for (const auto &a : *expr) {\n    auto [_, n] = a.getNameWithStars();\n    v.visitName(n, true, expr, a.getSrcInfo());\n    if (a.defaultValue)\n      CHECK(transform(a.defaultValue));\n  }\n  c->scope.pop_back();\n\n  SuiteStmt s;\n  c->scope.emplace_back(0, &s);\n  v.transform(expr->getExpr());\n  v.processChildCaptures();\n  c->scope.pop_back();\n  if (v.hasErrors())\n    errors.append(v.errors);\n  if (!canContinue())\n    return;\n\n  auto b = std::make_unique<BindingsAttribute>();\n  b->captures = c->captures;\n  for (const auto &n : c->captures)\n    ctx->childCaptures.insert(n);\n  for (auto &[u, v] : c->map)\n    b->bindings[u] = {u, v.size()};\n  expr->setAttribute(Attr::Bindings, std::move(b));\n}\n\nvoid ScopingVisitor::visit(AssignStmt *stmt) {\n  CHECK(transform(stmt->getRhs()));\n  CHECK(transform(stmt->getTypeExpr()));\n  CHECK(transformAdding(stmt->getLhs(), stmt));\n}\n\nvoid ScopingVisitor::visit(IfStmt *stmt) {\n  CHECK(transform(stmt->getCond()));\n  CHECK(transformScope(stmt->getIf()));\n  CHECK(transformScope(stmt->getElse()));\n}\n\nvoid ScopingVisitor::visit(MatchStmt *stmt) {\n  CHECK(transform(stmt->getExpr()));\n  for (auto &m : *stmt) {\n    CHECK(transform(m.getPattern()));\n    CHECK(transform(m.getGuard()));\n    CHECK(transformScope(m.getSuite()));\n  }\n}\n\nvoid ScopingVisitor::visit(WhileStmt *stmt) {\n  std::unordered_set<std::string> seen;\n  {\n    ConditionalBlock c(ctx.get(), stmt->getSuite());\n    ctx->scope.back().seenVars = std::make_unique<std::unordered_set<std::string>>();\n    CHECK(transform(stmt->getCond()));\n    CHECK(transform(stmt->getSuite()));\n    seen = *(ctx->scope.back().seenVars);\n  }\n  for (auto &var : seen) {\n    findDominatingBinding(var);\n  }\n\n  CHECK(transformScope(stmt->getElse()));\n}\n\nvoid ScopingVisitor::visit(ForStmt *stmt) {\n  CHECK(transform(stmt->getIter()));\n  CHECK(transform(stmt->getDecorator()));\n  for (auto &a : stmt->ompArgs)\n    CHECK(transform(a.value));\n\n  std::unordered_set<std::string> seen, seenDef;\n  {\n    ConditionalBlock c(ctx.get(), stmt->getSuite());\n\n    ctx->scope.back().seenVars = std::make_unique<std::unordered_set<std::string>>();\n    CHECK(transformAdding(stmt->getVar(), stmt));\n    seenDef = *(ctx->scope.back().seenVars);\n\n    ctx->scope.back().seenVars = std::make_unique<std::unordered_set<std::string>>();\n    CHECK(transform(stmt->getSuite()));\n    seen = *(ctx->scope.back().seenVars);\n  }\n  for (auto &var : seen)\n    if (!in(seenDef, var))\n      findDominatingBinding(var);\n\n  CHECK(transformScope(stmt->getElse()));\n}\n\nvoid ScopingVisitor::visit(ImportStmt *stmt) {\n  // Validate\n  if (stmt->getFrom()) {\n    Expr *e = stmt->getFrom();\n    while (auto d = cast<DotExpr>(e))\n      e = d->getExpr();\n    if (!isId(stmt->getFrom(), \"C\") && !isId(stmt->getFrom(), \"python\")) {\n      if (!cast<IdExpr>(e))\n        STOP_ERROR(Error::IMPORT_IDENTIFIER, e);\n      if (!stmt->getArgs().empty())\n        STOP_ERROR(Error::IMPORT_FN, stmt->getArgs().front().getSrcInfo());\n      if (stmt->getReturnType())\n        STOP_ERROR(Error::IMPORT_FN, stmt->getReturnType());\n      if (stmt->getWhat() && !cast<IdExpr>(stmt->getWhat()))\n        STOP_ERROR(Error::IMPORT_IDENTIFIER, stmt->getWhat());\n    }\n    if (stmt->isCVar() && !stmt->getArgs().empty())\n      STOP_ERROR(Error::IMPORT_FN, stmt->getArgs().front().getSrcInfo());\n  }\n  if (ctx->functionScope && stmt->getWhat() && isId(stmt->getWhat(), \"*\"))\n    STOP_ERROR(error::Error::IMPORT_STAR, stmt);\n\n  // dylib C imports\n  if (stmt->getFrom() && isId(stmt->getFrom(), \"C\") && cast<DotExpr>(stmt->getWhat()))\n    CHECK(transform(cast<DotExpr>(stmt->getWhat())->getExpr()));\n\n  if (stmt->getAs().empty()) {\n    if (stmt->getWhat()) {\n      if (!match(stmt->getWhat(), M<IdExpr>(\"*\")))\n        CHECK(transformAdding(stmt->getWhat(), stmt));\n    } else {\n      CHECK(transformAdding(stmt->getFrom(), stmt));\n    }\n  } else {\n    visitName(stmt->getAs(), true, stmt, stmt->getSrcInfo());\n  }\n  for (const auto &a : stmt->getArgs()) {\n    CHECK(transform(a.type));\n    CHECK(transform(a.defaultValue));\n  }\n  CHECK(transform(stmt->getReturnType()));\n}\n\nvoid ScopingVisitor::visit(TryStmt *stmt) {\n  CHECK(transformScope(stmt->getSuite()));\n  for (auto *a : *stmt) {\n    CHECK(transform(a->getException()));\n    ConditionalBlock c(ctx.get(), a->getSuite());\n    if (!a->getVar().empty()) {\n      auto newName = ctx->cache->getTemporaryVar(a->getVar());\n      ctx->renames.push_back({{a->getVar(), newName}});\n      a->var = newName;\n      visitName(a->getVar(), true, a, a->getException()->getSrcInfo());\n    }\n    CHECK(transform(a->getSuite()));\n    if (!a->getVar().empty())\n      ctx->renames.pop_back();\n  }\n  CHECK(transformScope(stmt->getElse()));\n  CHECK(transform(stmt->getFinally()));\n}\n\nvoid ScopingVisitor::visit(DelStmt *stmt) { CHECK(transform(stmt->getExpr())); }\n\n/// Process `global` statements.\nvoid ScopingVisitor::visit(GlobalStmt *stmt) {\n  if (!ctx->functionScope)\n    STOP_ERROR(Error::FN_OUTSIDE_ERROR, stmt,\n               stmt->isNonLocal() ? \"nonlocal\" : \"global\");\n  if (in(ctx->map, stmt->getVar()) || in(ctx->captures, stmt->getVar()))\n    STOP_ERROR(Error::FN_GLOBAL_ASSIGNED, stmt, stmt->getVar());\n\n  visitName(stmt->getVar(), true, stmt, stmt->getSrcInfo());\n  // No shadowing od global/nonlocal allowed\n  findDominatingBinding(stmt->getVar(), /* alowShadow= */ false);\n  ctx->captures[stmt->getVar()] = stmt->isNonLocal()\n                                      ? BindingsAttribute::CaptureType::Nonlocal\n                                      : BindingsAttribute::CaptureType::Global;\n}\n\nvoid ScopingVisitor::visit(YieldStmt *stmt) {\n  if (ctx->functionScope)\n    ctx->functionScope->setAttribute(Attr::IsGenerator);\n  CHECK(transform(stmt->getExpr()));\n}\n\nvoid ScopingVisitor::visit(YieldExpr *expr) {\n  if (ctx->functionScope)\n    ctx->functionScope->setAttribute(Attr::IsGenerator);\n}\n\nvoid ScopingVisitor::visit(FunctionStmt *stmt) {\n  // Validate\n  std::vector<Expr *> newDecorators;\n  for (auto &d : stmt->getDecorators()) {\n    if (isId(d, \"__attribute__\")) {\n      stmt->setAttribute(Attr::Attribute);\n    } else if (isId(d, \"llvm\")) {\n      stmt->setAttribute(Attr::LLVM);\n    } else if (isId(d, \"python\")) {\n      if (stmt->getDecorators().size() != 1)\n        STOP_ERROR(Error::FN_SINGLE_DECORATOR, stmt->getDecorators()[1], \"python\");\n      stmt->setAttribute(Attr::Python);\n    } else if (isId(d, \"__internal__\")) {\n      stmt->setAttribute(Attr::Internal);\n    } else if (isId(d, \"__hidden__\")) {\n      stmt->setAttribute(Attr::HiddenFromUser);\n    } else if (isId(d, \"atomic\")) {\n      stmt->setAttribute(Attr::Atomic);\n    } else if (isId(d, \"property\")) {\n      stmt->setAttribute(Attr::Property);\n    } else if (isId(d, \"staticmethod\")) {\n      stmt->setAttribute(Attr::StaticMethod);\n    } else if (isId(d, \"__force__\")) {\n      stmt->setAttribute(Attr::ForceRealize);\n    } else if (isId(d, \"C\")) {\n      stmt->setAttribute(Attr::C);\n    } else {\n      newDecorators.emplace_back(d);\n    }\n  }\n  if (stmt->hasAttribute(Attr::C)) {\n    for (auto &a : *stmt) {\n      if (a.getName().size() > 1 && a.getName()[0] == '*' && a.getName()[1] != '*')\n        stmt->setAttribute(Attr::CVarArg);\n    }\n  }\n  if (!stmt->empty() && !stmt->front().getType() && stmt->front().getName() == \"self\") {\n    stmt->setAttribute(Attr::HasSelf);\n  }\n  stmt->setDecorators(newDecorators);\n  if (!stmt->getReturn() &&\n      (stmt->hasAttribute(Attr::LLVM) || stmt->hasAttribute(Attr::C)))\n    STOP_ERROR(Error::FN_LLVM, getSrcInfo());\n  // Set attributes\n  std::unordered_set<std::string> seenArgs;\n  bool defaultsStarted = false, hasStarArg = false, hasKwArg = false;\n  for (size_t ia = 0; ia < stmt->size(); ia++) {\n    auto &a = (*stmt)[ia];\n    auto [stars, n] = a.getNameWithStars();\n    if (stars == 2) {\n      if (hasKwArg)\n        STOP_ERROR(Error::FN_MULTIPLE_ARGS, a.getSrcInfo());\n      if (a.getDefault())\n        STOP_ERROR(Error::FN_DEFAULT_STARARG, a.getDefault());\n      if (ia != stmt->size() - 1)\n        STOP_ERROR(Error::FN_LAST_KWARG, a.getSrcInfo());\n\n      hasKwArg = true;\n    } else if (stars == 1) {\n      if (hasStarArg)\n        STOP_ERROR(Error::FN_MULTIPLE_ARGS, a.getSrcInfo());\n      if (a.getDefault())\n        STOP_ERROR(Error::FN_DEFAULT_STARARG, a.getDefault());\n      hasStarArg = true;\n    }\n    if (in(seenArgs, n))\n      STOP_ERROR(Error::FN_ARG_TWICE, a.getSrcInfo(), n);\n    seenArgs.insert(n);\n    if (!a.getDefault() && defaultsStarted && !stars && a.isValue())\n      STOP_ERROR(Error::FN_DEFAULT, a.getSrcInfo(), n);\n    defaultsStarted |= static_cast<bool>(a.getDefault());\n    if (stmt->hasAttribute(Attr::C)) {\n      if (a.getDefault())\n        STOP_ERROR(Error::FN_C_DEFAULT, a.getDefault(), n);\n      if (stars != 1 && !a.getType())\n        STOP_ERROR(Error::FN_C_TYPE, a.getSrcInfo(), n);\n    }\n  }\n\n  bool isOverload = false;\n  for (auto &d : stmt->getDecorators())\n    if (isId(d, \"overload\")) {\n      isOverload = true;\n    }\n  if (!isOverload)\n    visitName(stmt->getName(), true, stmt, stmt->getSrcInfo());\n\n  auto c = std::make_shared<ScopingVisitor::Context>();\n  c->cache = ctx->cache;\n  c->functionScope = stmt;\n  if (ctx->inClass && !stmt->empty())\n    c->classDeduce = {stmt->front().getName(), {}};\n  c->renames = ctx->renames;\n  ScopingVisitor v;\n  c->scope.emplace_back(0);\n  v.ctx = c;\n  v.visitName(stmt->getName(), true, stmt, stmt->getSrcInfo());\n  for (const auto &a : *stmt) {\n    auto [_, n] = a.getNameWithStars();\n    v.visitName(n, true, stmt, a.getSrcInfo());\n    if (a.defaultValue)\n      CHECK(transform(a.defaultValue));\n  }\n  c->scope.pop_back();\n\n  c->scope.emplace_back(0, stmt->getSuite());\n  v.transform(stmt->getSuite());\n  v.processChildCaptures();\n  c->scope.pop_back();\n  if (v.hasErrors())\n    errors.append(v.errors);\n  if (!canContinue())\n    return;\n\n  auto b = std::make_unique<BindingsAttribute>();\n  b->captures = c->captures;\n  for (const auto &n : c->captures) {\n    ctx->childCaptures.insert(n);\n  }\n  // Recursive capture if used\n  if (c->map[stmt->getName()].size() == 1 && in(c->firstSeen, stmt->getName()))\n    b->captures[stmt->getName()] = BindingsAttribute::Read;\n  for (auto &[u, v] : c->map) {\n    b->bindings[u] = {u, v.size()};\n    auto cp = in(c->childCaptures, u);\n    if (!cp)\n      cp = in(c->captures, u);\n    if (cp && *cp == BindingsAttribute::Nonlocal) {\n      b->bindings[u].isNonlocal = true;\n      ctx->childCaptures[u] = BindingsAttribute::Nonlocal;\n    }\n  }\n  stmt->setAttribute(Attr::Bindings, std::move(b));\n\n  if (!c->classDeduce.second.empty()) {\n    auto s = std::make_unique<ir::StringListAttribute>();\n    for (const auto &n : c->classDeduce.second)\n      s->values.push_back(n);\n    stmt->setAttribute(Attr::ClassDeduce, std::move(s));\n  }\n}\n\nvoid ScopingVisitor::visit(WithStmt *stmt) {\n  ConditionalBlock c(ctx.get(), stmt->getSuite());\n  for (size_t i = 0; i < stmt->size(); i++) {\n    CHECK(transform((*stmt)[i]));\n    if (!stmt->getVars()[i].empty())\n      visitName(stmt->getVars()[i], true, stmt, stmt->getSrcInfo());\n  }\n  CHECK(transform(stmt->getSuite()));\n}\n\nvoid ScopingVisitor::visit(ClassStmt *stmt) {\n  // @tuple(init=, repr=, eq=, order=, hash=, pickle=, container=, python=, add=,\n  // internal=...)\n  // @dataclass(...)\n  // @extend\n\n  std::map<std::string, bool> tupleMagics = {\n      {\"new\", true},           {\"repr\", false},    {\"hash\", false},\n      {\"eq\", false},           {\"ne\", false},      {\"lt\", false},\n      {\"le\", false},           {\"gt\", false},      {\"ge\", false},\n      {\"pickle\", true},        {\"unpickle\", true}, {\"to_py\", false},\n      {\"from_py\", false},      {\"iter\", false},    {\"getitem\", false},\n      {\"len\", false},          {\"to_gpu\", false},  {\"from_gpu\", false},\n      {\"from_gpu_new\", false}, {\"tuplesize\", true}};\n\n  for (auto &d : stmt->getDecorators()) {\n    if (isId(d, \"__notuple__\")) {\n      stmt->setAttribute(Attr::ClassNoTuple);\n    } else if (isId(d, \"__noextend__\")) {\n      stmt->setAttribute(Attr::NoExtend);\n    } else if (isId(d, \"dataclass\")) {\n      stmt->setAttribute(Attr::Dataclass);\n    } else if (auto c = cast<CallExpr>(d)) {\n      if (isId(c->getExpr(), \"tuple\")) {\n        stmt->setAttribute(Attr::Tuple);\n        for (auto &val : tupleMagics | std::views::values)\n          val = true;\n      } else if (!isId(c->getExpr(), \"dataclass\")) {\n        STOP_ERROR(Error::CLASS_BAD_DECORATOR, c->getExpr());\n      } else if (stmt->hasAttribute(Attr::Tuple)) {\n        STOP_ERROR(Error::CLASS_CONFLICT_DECORATOR, c, \"dataclass\", \"tuple\");\n      }\n      for (const auto &a : *c) {\n        auto b = cast<BoolExpr>(a);\n        if (!b)\n          STOP_ERROR(Error::CLASS_NONSTATIC_DECORATOR, a.getSrcInfo());\n        char val = static_cast<char>(b->getValue());\n        if (a.getName() == \"init\") {\n          tupleMagics[\"new\"] = val;\n        } else if (a.getName() == \"repr\") {\n          tupleMagics[\"repr\"] = val;\n        } else if (a.getName() == \"eq\") {\n          tupleMagics[\"eq\"] = tupleMagics[\"ne\"] = val;\n        } else if (a.getName() == \"order\") {\n          tupleMagics[\"lt\"] = tupleMagics[\"le\"] = tupleMagics[\"gt\"] =\n              tupleMagics[\"ge\"] = val;\n        } else if (a.getName() == \"hash\") {\n          tupleMagics[\"hash\"] = val;\n        } else if (a.getName() == \"pickle\") {\n          tupleMagics[\"pickle\"] = tupleMagics[\"unpickle\"] = val;\n        } else if (a.getName() == \"python\") {\n          tupleMagics[\"to_py\"] = tupleMagics[\"from_py\"] = val;\n        } else if (a.getName() == \"gpu\") {\n          tupleMagics[\"to_gpu\"] = tupleMagics[\"from_gpu\"] =\n              tupleMagics[\"from_gpu_new\"] = val;\n        } else if (a.getName() == \"container\") {\n          tupleMagics[\"iter\"] = tupleMagics[\"getitem\"] = val;\n        } else {\n          STOP_ERROR(Error::CLASS_BAD_DECORATOR_ARG, a.getSrcInfo());\n        }\n      }\n    } else if (isId(d, \"tuple\")) {\n      if (stmt->hasAttribute(Attr::Tuple))\n        STOP_ERROR(Error::CLASS_MULTIPLE_DECORATORS, d, \"tuple\");\n      stmt->setAttribute(Attr::Tuple);\n      for (auto &val : tupleMagics | std::views::values) {\n        val = true;\n      }\n    } else if (isId(d, \"extend\")) {\n      stmt->setAttribute(Attr::Extend);\n      if (stmt->getDecorators().size() != 1) {\n        STOP_ERROR(\n            Error::CLASS_SINGLE_DECORATOR,\n            stmt->getDecorators()[stmt->getDecorators().front() == d]->getSrcInfo(),\n            \"extend\");\n      }\n    } else if (isId(d, \"__internal__\")) {\n      stmt->setAttribute(Attr::Internal);\n    } else {\n      STOP_ERROR(Error::CLASS_BAD_DECORATOR, d);\n    }\n  }\n  if (!stmt->hasAttribute(Attr::Tuple) && !stmt->hasAttribute(Attr::Internal) &&\n      !stmt->hasAttribute(Attr::Dataclass) && stmt->getStaticBaseClasses().empty() &&\n      stmt->size() == 0) {\n    stmt->setAttribute(Attr::ClassDeduce);\n  }\n  if (!stmt->hasAttribute(Attr::Tuple)) {\n    tupleMagics[\"init\"] = tupleMagics[\"new\"];\n    tupleMagics[\"new\"] = tupleMagics[\"raw\"] = true;\n    tupleMagics[\"len\"] = false;\n    tupleMagics[\"repr_default\"] = true;\n  }\n  tupleMagics[\"dict\"] = true;\n  // Internal classes do not get any auto-generated members.\n  std::vector<std::string> magics;\n  if (!stmt->hasAttribute(Attr::Internal)) {\n    for (auto &m : tupleMagics)\n      if (m.second) {\n        if (m.first == \"new\")\n          magics.insert(magics.begin(), m.first);\n        else\n          magics.push_back(m.first);\n      }\n  }\n  stmt->setAttribute(Attr::ClassMagic,\n                     std::make_unique<ir::StringListAttribute>(magics));\n  std::unordered_set<std::string> seen;\n  if (stmt->hasAttribute(Attr::Extend) && !stmt->empty())\n    STOP_ERROR(Error::CLASS_EXTENSION, stmt->front().getSrcInfo());\n  if (stmt->hasAttribute(Attr::Extend) &&\n      !(stmt->getBaseClasses().empty() && stmt->getStaticBaseClasses().empty())) {\n    STOP_ERROR(Error::CLASS_EXTENSION, stmt->getBaseClasses().empty()\n                                           ? stmt->getStaticBaseClasses().front()\n                                           : stmt->getBaseClasses().front());\n  }\n  for (auto &a : *stmt) {\n    if (!a.getType() && !a.getDefault())\n      STOP_ERROR(Error::CLASS_MISSING_TYPE, a.getSrcInfo(), a.getName());\n    if (in(seen, a.getName()))\n      STOP_ERROR(Error::CLASS_ARG_TWICE, a.getSrcInfo(), a.getName());\n    seen.insert(a.getName());\n  }\n\n  if (stmt->hasAttribute(Attr::Extend))\n    visitName(stmt->getName());\n  else\n    visitName(stmt->getName(), true, stmt, stmt->getSrcInfo());\n\n  auto c = std::make_shared<ScopingVisitor::Context>();\n  c->cache = ctx->cache;\n  c->renames = ctx->renames;\n  ScopingVisitor v;\n  c->scope.emplace_back(0);\n  c->inClass = true;\n  v.ctx = c;\n  for (const auto &a : *stmt) {\n    v.transform(a.type);\n    v.transform(a.defaultValue);\n  }\n  v.transform(stmt->getSuite());\n  c->scope.pop_back();\n  if (v.hasErrors())\n    errors.append(v.errors);\n  if (!canContinue())\n    return;\n\n  for (auto &d : stmt->getBaseClasses())\n    CHECK(transform(d));\n  for (auto &d : stmt->getStaticBaseClasses())\n    CHECK(transform(d));\n}\n\nvoid ScopingVisitor::processChildCaptures() {\n  for (auto &n : ctx->childCaptures) {\n    if (auto i = in(ctx->map, n.first)) {\n      if (i->back().binding && cast<ClassStmt>(i->back().binding))\n        continue;\n    }\n    if (!findDominatingBinding(n.first)) {\n      ctx->captures.insert(n); // propagate!\n    }\n  }\n}\n\nvoid ScopingVisitor::switchToUpdate(ASTNode *binding, const std::string &name,\n                                    bool gotUsedVar) {\n  if (binding && binding->hasAttribute(Attr::Bindings)) {\n    binding->getAttribute<BindingsAttribute>(Attr::Bindings)->bindings.erase(name);\n  }\n  if (binding) {\n    if (!gotUsedVar && binding->hasAttribute(Attr::ExprDominatedUsed))\n      binding->eraseAttribute(Attr::ExprDominatedUsed);\n    binding->setAttribute(gotUsedVar ? Attr::ExprDominatedUsed : Attr::ExprDominated);\n  }\n  if (cast<FunctionStmt>(binding))\n    STOP_ERROR(error::Error::ID_INVALID_BIND, binding, name);\n  else if (cast<ClassStmt>(binding))\n    STOP_ERROR(error::Error::ID_INVALID_BIND, binding, name);\n}\n\nbool ScopingVisitor::visitName(const std::string &name, bool adding, ASTNode *root,\n                               const SrcInfo &src) {\n  if (adding && ctx->inClass)\n    return false;\n  if (adding) {\n    if (auto p = in(ctx->captures, name)) {\n      if (*p == BindingsAttribute::CaptureType::Read) {\n        addError(error::Error::ASSIGN_LOCAL_REFERENCE, ctx->firstSeen[name], name, src);\n        return false;\n      } else if (root) { // global, nonlocal\n        switchToUpdate(root, name, false);\n      }\n    } else {\n      if (auto i = in(ctx->childCaptures, name)) {\n        if (*i != BindingsAttribute::CaptureType::Global && ctx->functionScope) {\n          auto newScope = std::vector<int>{ctx->scope[0].id};\n          seqassert(ctx->scope.front().suite, \"invalid suite\");\n          if (!ctx->scope.front().suite->hasAttribute(Attr::Bindings))\n            ctx->scope.front().suite->setAttribute(\n                Attr::Bindings, std::make_unique<BindingsAttribute>());\n          ctx->scope.front()\n              .suite->getAttribute<BindingsAttribute>(Attr::Bindings)\n              ->bindings[name] = {name, 0,\n                                  *i == BindingsAttribute::CaptureType::Nonlocal};\n          auto newItem = ScopingVisitor::Context::Item(src, newScope, nullptr);\n          ctx->map[name].push_back(newItem);\n        }\n      }\n      ctx->map[name].emplace_front(src, ctx->getScope(), root);\n    }\n  } else {\n    if (!in(ctx->firstSeen, name))\n      ctx->firstSeen[name] = src;\n    if (!in(ctx->map, name)) {\n      ctx->captures[name] = BindingsAttribute::CaptureType::Read;\n    }\n  }\n  if (auto val = findDominatingBinding(name)) {\n    // Track loop variables to dominate them later. Example:\n    // x = 1\n    // while True:\n    //   if x > 10: break\n    //   x = x + 1  # x must be dominated after the loop to ensure that it gets updated\n    auto scope = ctx->getScope();\n    for (size_t li = ctx->scope.size(); li-- > 0;) {\n      if (ctx->scope[li].seenVars) {\n        bool inside = val->scope.size() >= scope.size() &&\n                      val->scope[scope.size() - 1] == scope.back();\n        if (!inside)\n          ctx->scope[li].seenVars->insert(name);\n        else\n          break;\n      }\n      scope.pop_back();\n    }\n\n    // Variable binding check for variables that are defined within conditional blocks\n    if (!val->accessChecked.empty()) {\n      bool checked = false;\n      for (size_t ai = val->accessChecked.size(); ai-- > 0;) {\n        auto &a = val->accessChecked[ai];\n        if (a.size() <= ctx->scope.size() &&\n            a[a.size() - 1] == ctx->scope[a.size() - 1].id) {\n          checked = true;\n          break;\n        }\n      }\n      if (!checked) {\n        seqassert(!adding, \"bad branch\");\n        if (!(val->binding && val->binding->hasAttribute(Attr::Bindings))) {\n          // If the expression is not conditional, we can just do the check once\n          val->accessChecked.push_back(ctx->getScope());\n        }\n        return true;\n      }\n    }\n  }\n  return false;\n}\n\n/// Get an item from the context. Perform domination analysis for accessing items\n/// defined in the conditional blocks (i.e., Python scoping).\nScopingVisitor::Context::Item *\nScopingVisitor::findDominatingBinding(const std::string &name, bool allowShadow) {\n  auto it = in(ctx->map, name);\n  if (!it || it->empty())\n    return nullptr;\n  auto lastGood = it->begin();\n  while (lastGood != it->end() && lastGood->ignore)\n    ++lastGood;\n  int commonScope = static_cast<int>(ctx->scope.size());\n  // Iterate through all bindings with the given name and find the closest binding that\n  // dominates the current scope.\n  for (auto i = it->begin(); i != it->end(); ++i) {\n    if (i->ignore)\n      continue;\n\n    bool completeDomination = i->scope.size() <= ctx->scope.size() &&\n                              i->scope.back() == ctx->scope[i->scope.size() - 1].id;\n    if (completeDomination) {\n      commonScope = i->scope.size();\n      lastGood = i;\n      break;\n    } else {\n      seqassert(i->scope[0] == 0, \"bad scoping\");\n      seqassert(ctx->scope[0].id == 0, \"bad scoping\");\n      // Find the longest block prefix between the binding and the current common scope.\n      commonScope = std::min(commonScope, static_cast<int>(i->scope.size()));\n      while (commonScope > 0 &&\n             i->scope[commonScope - 1] != ctx->scope[commonScope - 1].id)\n        commonScope--;\n      // if (commonScope < int(ctx->scope.size()) && commonScope != p)\n      //   break;\n      lastGood = i;\n    }\n  }\n  seqassert(lastGood != it->end(), \"corrupted scoping ({})\", name);\n  if (!allowShadow) { // go to the end\n    lastGood = it->end();\n    --lastGood;\n    int p = std::min(commonScope, static_cast<int>(lastGood->scope.size()));\n    while (p >= 0 && lastGood->scope[p - 1] != ctx->scope[p - 1].id)\n      p--;\n    commonScope = p;\n  }\n\n  bool gotUsedVar = false;\n  if (lastGood->scope.size() != commonScope) {\n    // The current scope is potentially reachable by multiple bindings that are\n    // not dominated by a common binding. Create such binding in the scope that\n    // dominates (covers) all of them.\n    auto scope = ctx->getScope();\n    auto newScope = std::vector<int>(scope.begin(), scope.begin() + commonScope);\n\n    // Make sure to prepend a binding declaration: `var` and `var__used__ = False`\n    // to the dominating scope.\n    for (size_t si = commonScope; si-- > 0; si--) {\n      if (!ctx->scope[si].suite)\n        continue;\n      if (!ctx->scope[si].suite->hasAttribute(Attr::Bindings))\n        ctx->scope[si].suite->setAttribute(Attr::Bindings,\n                                           std::make_unique<BindingsAttribute>());\n      ctx->scope[si]\n          .suite->getAttribute<BindingsAttribute>(Attr::Bindings)\n          ->bindings[name] = {name, 1};\n      auto newItem = ScopingVisitor::Context::Item(\n          getSrcInfo(), newScope, ctx->scope[si].suite, {lastGood->scope});\n      lastGood = it->insert(++lastGood, newItem);\n      gotUsedVar = true;\n      break;\n    }\n  } else if (lastGood->binding && lastGood->binding->hasAttribute(Attr::Bindings)) {\n    gotUsedVar = lastGood->binding->getAttribute<BindingsAttribute>(Attr::Bindings)\n                     ->bindings[name]\n                     .count > 0;\n  }\n  // Remove all bindings after the dominant binding.\n  for (auto i = it->begin(); i != it->end(); ++i) {\n    if (i == lastGood)\n      break;\n    switchToUpdate(i->binding, name, gotUsedVar);\n    i->scope = lastGood->scope;\n    i->ignore = true;\n  }\n  if (!gotUsedVar && lastGood->binding &&\n      lastGood->binding->hasAttribute(Attr::Bindings))\n    lastGood->binding->getAttribute<BindingsAttribute>(Attr::Bindings)\n        ->bindings[name] = {name, 0};\n  return &(*lastGood);\n}\n\nauto format_as(BindingsAttribute::CaptureType c) {\n  return c == BindingsAttribute::Read ? \"RD\"\n                                      : (c == BindingsAttribute::Global ? \"GL\" : \"NL\");\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/scoping/scoping.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/visitors/typecheck/ctx.h\"\n#include \"codon/parser/visitors/visitor.h\"\n\nnamespace codon::ast {\n\nstruct BindingsAttribute : public ir::Attribute {\n  static const int AttributeID = 190;\n\n  enum CaptureType { Read, Global, Nonlocal };\n  std::unordered_map<std::string, CaptureType> captures;\n\n  struct Binding {\n    std::string name;\n    size_t count;\n    bool isNonlocal = false;\n  };\n  std::unordered_map<std::string, Binding> bindings;\n\n  std::unordered_map<std::string, std::string> localRenames;\n\n  std::unique_ptr<Attribute> clone() const override {\n    auto p = std::make_unique<BindingsAttribute>();\n    p->captures = captures;\n    p->bindings = bindings;\n    p->localRenames = localRenames;\n    return p;\n  }\n\nprivate:\n  std::ostream &doFormat(std::ostream &os) const override { return os << \"Bindings\"; }\n};\n\nauto format_as(BindingsAttribute::CaptureType c);\n\nclass ScopingVisitor : public CallbackASTVisitor<bool, bool> {\n  struct Context {\n    /// A pointer to the shared cache.\n    Cache *cache;\n\n    /// Holds the information about current scope.\n    /// A scope is defined as a stack of conditional blocks\n    /// (i.e., blocks that might not get executed during the runtime).\n    /// Used mainly to support Python's variable scoping rules.\n    struct ScopeBlock {\n      int id;\n      // Associated SuiteStmt\n      Stmt *suite;\n      /// List of variables \"seen\" before their assignment within a loop.\n      /// Used to dominate variables that are updated within a loop.\n      std::unique_ptr<std::unordered_set<std::string>> seenVars = nullptr;\n      ScopeBlock(int id, Stmt *s = nullptr) : id(id), suite(s), seenVars(nullptr) {}\n    };\n    /// Current hierarchy of conditional blocks.\n    std::vector<ScopeBlock> scope;\n    std::vector<int> getScope() const {\n      std::vector<int> result;\n      result.reserve(scope.size());\n      for (const auto &b : scope)\n        result.emplace_back(b.id);\n      return result;\n    }\n\n    struct Item : public codon::SrcObject {\n      std::vector<int> scope;\n      ASTNode *binding = nullptr;\n      bool ignore = false;\n\n      /// List of scopes where the identifier is accessible\n      /// without __used__ check\n      std::vector<std::vector<int>> accessChecked;\n      Item(const codon::SrcInfo &src, std::vector<int> scope,\n           ASTNode *binding = nullptr, std::vector<std::vector<int>> accessChecked = {})\n          : scope(std::move(scope)), binding(std::move(binding)), ignore(false),\n            accessChecked(std::move(accessChecked)) {\n        setSrcInfo(src);\n      }\n    };\n    std::unordered_map<std::string, std::list<Item>> map;\n\n    std::unordered_map<std::string, BindingsAttribute::CaptureType> captures;\n    std::unordered_map<std::string, BindingsAttribute::CaptureType>\n        childCaptures; // for functions!\n    std::map<std::string, SrcInfo> firstSeen;\n    std::pair<std::string, std::unordered_set<std::string>> classDeduce;\n\n    bool adding = false;\n    ASTNode *root = nullptr;\n    FunctionStmt *functionScope = nullptr;\n    bool inClass = false;\n    // bool isConditional = false;\n\n    std::vector<std::unordered_map<std::string, std::string>> renames = {{}};\n    bool tempScope = false;\n\n    // Time to track positions of assignments and references to them.\n    int64_t time = 0;\n  };\n  std::shared_ptr<Context> ctx = nullptr;\n\n  struct ConditionalBlock {\n    Context *ctx;\n    ConditionalBlock(Context *ctx, Stmt *s, int id = -1) : ctx(ctx) {\n      if (s)\n        seqassertn(cast<SuiteStmt>(s), \"not a suite\");\n      ctx->scope.emplace_back(id == -1 ? ctx->cache->blockCount++ : id, s);\n    }\n    ~ConditionalBlock() {\n      seqassertn(!ctx->scope.empty() &&\n                     (ctx->scope.back().id == 0 || ctx->scope.size() > 1),\n                 \"empty scope\");\n      ctx->scope.pop_back();\n    }\n  };\n\npublic:\n  ParserErrors errors;\n  bool hasErrors() const { return !errors.empty(); }\n  bool canContinue() const { return errors.size() <= MAX_ERRORS; }\n\n  template <class... TA>\n  void addError(error::Error e, const SrcInfo &o, const TA &...args) {\n    auto msg = ErrorMessage(error::Emsg(e, args...), o.file, o.line, o.col, o.len,\n                            static_cast<int>(e));\n    errors.addError({msg});\n  }\n  template <class... TA> void addError(error::Error e, ASTNode *o, const TA &...args) {\n    this->addError(e, o->getSrcInfo(), args...);\n  }\n  void addError(llvm::Error &&e) {\n    llvm::handleAllErrors(std::move(e), [this](const error::ParserErrorInfo &e) {\n      this->errors.append(e.getErrors());\n    });\n  }\n\n  static llvm::Error apply(Cache *, Stmt *s,\n                           std::unordered_map<std::string, size_t> * = nullptr);\n  bool transform(Expr *expr) override;\n  bool transform(Stmt *stmt) override;\n\n  // Can error!\n  bool visitName(const std::string &name, bool = false, ASTNode * = nullptr,\n                 const SrcInfo & = SrcInfo());\n  bool transformAdding(Expr *e, ASTNode *);\n  bool transformScope(Expr *);\n  bool transformScope(Stmt *);\n\n  void visit(StringExpr *) override;\n  void visit(IdExpr *) override;\n  void visit(DotExpr *) override;\n  void visit(IndexExpr *) override;\n  void visit(GeneratorExpr *) override;\n  void visit(IfExpr *) override;\n  void visit(BinaryExpr *) override;\n  void visit(LambdaExpr *) override;\n  void visit(YieldExpr *) override;\n  void visit(AssignExpr *) override;\n\n  void visit(AssignStmt *) override;\n  void visit(DelStmt *) override;\n  void visit(YieldStmt *) override;\n  void visit(WhileStmt *) override;\n  void visit(ForStmt *) override;\n  void visit(IfStmt *) override;\n  void visit(MatchStmt *) override;\n  void visit(ImportStmt *) override;\n  void visit(TryStmt *) override;\n  void visit(GlobalStmt *) override;\n  void visit(FunctionStmt *) override;\n  void visit(ClassStmt *) override;\n  void visit(WithStmt *) override;\n\n  Context::Item *findDominatingBinding(const std::string &, bool = true);\n  void processChildCaptures();\n  void switchToUpdate(ASTNode *binding, const std::string &, bool);\n\n  std::vector<StringExpr::String> unpackFString(const std::string &value);\n\n  template <typename Tn, typename... Ts> Tn *N(Ts &&...args) {\n    Tn *t = ctx->cache->N<Tn>(std::forward<Ts>(args)...);\n    t->setSrcInfo(getSrcInfo());\n    return t;\n  }\n};\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/translate/translate.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"translate.h\"\n\n#include <memory>\n#include <sstream>\n#include <string>\n#include <vector>\n\n#include \"codon/cir/transform/parallel/schedule.h\"\n#include \"codon/cir/util/cloning.h\"\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/visitors/translate/translate_ctx.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nusing codon::ir::cast;\nusing codon::ir::transform::parallel::OMPSched;\n\nnamespace codon::ast {\n\nTranslateVisitor::TranslateVisitor(std::shared_ptr<TranslateContext> ctx)\n    : ctx(std::move(ctx)), result(nullptr) {}\n\nir::Func *TranslateVisitor::apply(Cache *cache, Stmt *stmts) {\n  ir::BodiedFunc *main = nullptr;\n  if (cache->isJit) {\n    auto fnName = fmt::format(\"_jit_{}\", cache->jitCell);\n    main = cache->module->Nr<ir::BodiedFunc>(fnName);\n    main->setSrcInfo({\"<jit>\", 0, 0, 0});\n    main->setGlobal();\n    auto irType = cache->module->unsafeGetFuncType(\n        fnName, cache->classes[\"NoneType\"].realizations[\"NoneType\"]->ir, {}, false);\n    main->realize(irType, {});\n    main->setJIT();\n  } else {\n    main = cast<ir::BodiedFunc>(cache->module->getMainFunc());\n    auto path = cache->fs->get_module0();\n    main->setSrcInfo({path, 0, 0, 0});\n  }\n\n  auto block = cache->module->Nr<ir::SeriesFlow>(\"body\");\n  main->setBody(block);\n\n  if (!cache->codegenCtx)\n    cache->codegenCtx = std::make_shared<TranslateContext>(cache);\n  cache->codegenCtx->bases = {main};\n  cache->codegenCtx->series = {block};\n\n  cache->populatePythonModule();\n  TranslateVisitor(cache->codegenCtx).translateStmts(stmts);\n  return main;\n}\n\nvoid TranslateVisitor::translateStmts(Stmt *stmts) const {\n  initializeGlobals();\n  TranslateVisitor(ctx->cache->codegenCtx).transform(stmts);\n  for (auto &f : ctx->cache->functions | std::views::values)\n    TranslateVisitor(ctx->cache->codegenCtx).transform(f.ast);\n}\n\nvoid TranslateVisitor::initializeGlobals() const {\n  for (auto &[name, ir] : ctx->cache->globals)\n    if (!ir) {\n      ir::types::Type *vt = nullptr;\n      if (auto t = ctx->cache->typeCtx->forceFind(name)->getType()) {\n        if (!t->isInstantiated() || (t->is(TYPE_TYPE)) || t->getFunc())\n          continue;\n        vt = getType(t);\n      }\n      ir = name == VAR_ARGV ? ctx->cache->codegenCtx->getModule()->getArgVar()\n                            : ctx->cache->codegenCtx->getModule()->N<ir::Var>(\n                                  SrcInfo(), vt, true, false, false, name);\n      ctx->cache->codegenCtx->add(TranslateItem::Var, name, ir);\n    }\n}\n\n/************************************************************************************/\n\nir::Value *TranslateVisitor::transform(Expr *expr) {\n  TranslateVisitor v(ctx);\n  v.setSrcInfo(expr->getSrcInfo());\n\n  types::ClassType *p = nullptr;\n  if (expr->hasAttribute(Attr::ExprList) || expr->hasAttribute(Attr::ExprSet) ||\n      expr->hasAttribute(Attr::ExprDict) || expr->hasAttribute(Attr::ExprPartial)) {\n    ctx->seqItems.emplace_back();\n  }\n  if (expr->hasAttribute(Attr::ExprPartial)) {\n    p = expr->getType()->getPartial();\n  }\n\n  expr->accept(v);\n  ir::Value *ir = v.result;\n\n  if (expr->hasAttribute(Attr::ExprList) || expr->hasAttribute(Attr::ExprSet)) {\n    std::vector<ir::LiteralElement> le;\n    for (auto &pl : ctx->seqItems.back()) {\n      seqassert(pl.first == Attr::ExprSequenceItem ||\n                    pl.first == Attr::ExprStarSequenceItem,\n                \"invalid list/set element\");\n      le.push_back(\n          ir::LiteralElement{pl.second, pl.first == Attr::ExprStarSequenceItem});\n    }\n    if (expr->hasAttribute(Attr::ExprList))\n      ir->setAttribute(std::make_unique<ir::ListLiteralAttribute>(le));\n    else\n      ir->setAttribute(std::make_unique<ir::SetLiteralAttribute>(le));\n    ctx->seqItems.pop_back();\n  }\n  if (expr->hasAttribute(Attr::ExprDict)) {\n    std::vector<ir::DictLiteralAttribute::KeyValuePair> dla;\n    for (int pi = 0; pi < ctx->seqItems.back().size(); pi++) {\n      auto &pl = ctx->seqItems.back()[pi];\n      if (pl.first == Attr::ExprStarSequenceItem) {\n        dla.push_back({pl.second, nullptr});\n      } else {\n        seqassert(pl.first == Attr::ExprSequenceItem &&\n                      pi + 1 < ctx->seqItems.back().size() &&\n                      ctx->seqItems.back()[pi + 1].first == Attr::ExprSequenceItem,\n                  \"invalid dict element\");\n        dla.push_back({pl.second, ctx->seqItems.back()[pi + 1].second});\n        pi++;\n      }\n    }\n    ir->setAttribute(std::make_unique<ir::DictLiteralAttribute>(dla));\n    ctx->seqItems.pop_back();\n  }\n  if (expr->hasAttribute(Attr::ExprPartial)) {\n    std::vector<ir::Value *> vals;\n    seqassert(p, \"invalid partial element\");\n    int j = 0;\n    auto known = p->getPartialMask();\n    auto func = p->getPartialFunc();\n    for (int i = 0; i < known.size(); i++) {\n      if (known[i] == types::ClassType::PartialFlag::Included &&\n          (*func->ast)[i].isValue()) {\n        seqassert(j < ctx->seqItems.back().size() &&\n                      ctx->seqItems.back()[j].first == Attr::ExprSequenceItem,\n                  \"invalid partial element\");\n        vals.push_back(ctx->seqItems.back()[j++].second);\n      } else if ((*func->ast)[i].isValue()) {\n        vals.push_back({nullptr});\n      }\n    }\n    ir->setAttribute(\n        std::make_unique<ir::PartialFunctionAttribute>(func->ast->getName(), vals));\n    ctx->seqItems.pop_back();\n  }\n  if (expr->hasAttribute(Attr::ExprSequenceItem)) {\n    ctx->seqItems.back().emplace_back(Attr::ExprSequenceItem, ir);\n  }\n  if (expr->hasAttribute(Attr::ExprStarSequenceItem)) {\n    ctx->seqItems.back().emplace_back(Attr::ExprStarSequenceItem, ir);\n  }\n\n  return ir;\n}\n\nvoid TranslateVisitor::defaultVisit(Expr *n) {\n  seqassert(false, \"invalid node {}\", n->toString());\n}\n\nvoid TranslateVisitor::visit(NoneExpr *expr) {\n  auto f = expr->getType()->realizedName() + \":\" +\n           getMangledMethod(\"std.internal.core\", TYPE_OPTIONAL, \"__new__\");\n  auto val = ctx->find(f);\n  seqassert(val, \"cannot find '{}'\", f);\n  result = make<ir::CallInstr>(expr, make<ir::VarValue>(expr, val->getFunc()),\n                               std::vector<ir::Value *>{});\n}\n\nvoid TranslateVisitor::visit(BoolExpr *expr) {\n  result = make<ir::BoolConst>(expr, expr->getValue(), getType(expr->getType()));\n}\n\nvoid TranslateVisitor::visit(IntExpr *expr) {\n  result = make<ir::IntConst>(expr, expr->getValue(), getType(expr->getType()));\n}\n\nvoid TranslateVisitor::visit(FloatExpr *expr) {\n  result = make<ir::FloatConst>(expr, expr->getValue(), getType(expr->getType()));\n}\n\nvoid TranslateVisitor::visit(StringExpr *expr) {\n  result = make<ir::StringConst>(expr, expr->getValue(), getType(expr->getType()));\n}\n\nvoid TranslateVisitor::visit(IdExpr *expr) {\n  auto val = ctx->find(expr->getValue());\n  if (!val) {\n    // ctx->find(expr->getValue());\n    seqassert(val, \"cannot find '{}'\", expr->getValue());\n  }\n  if (expr->getValue() == getMangledVar(\"\", \"__vtable_size__\")) {\n    // LOG(\"[] __vtable_size__={}\", ctx->cache->classRealizationCnt + 2);\n    result = make<ir::IntConst>(expr, ctx->cache->classRealizationCnt + 2,\n                                getType(expr->getType()));\n  } else if (auto *v = val->getVar()) {\n    result = make<ir::VarValue>(expr, v);\n  } else if (auto *f = val->getFunc()) {\n    result = make<ir::VarValue>(expr, f);\n  } else {\n    // Just use NoneType which is {} (same as type)\n    auto ntval =\n        ctx->find(getMangledMethod(\"std.internal.core\", \"NoneType\", \"__new__\"));\n    seqassert(ntval, \"cannot find '{}'\", \"NoneType.__new__\");\n    result = make<ir::CallInstr>(expr, make<ir::VarValue>(expr, ntval->getFunc()),\n                                 std::vector<ir::Value *>{});\n  }\n}\n\nvoid TranslateVisitor::visit(IfExpr *expr) {\n  auto cond = transform(expr->getCond());\n  auto ifexpr = transform(expr->getIf());\n  auto elsexpr = transform(expr->getElse());\n  result = make<ir::TernaryInstr>(expr, cond, ifexpr, elsexpr);\n}\n\n// Search expression tree for an identifier\nclass IdVisitor : public CallbackASTVisitor<bool, bool> {\npublic:\n  std::unordered_set<std::string> ids;\n\n  bool transform(Expr *expr) override {\n    IdVisitor v;\n    if (expr)\n      expr->accept(v);\n    ids.insert(v.ids.begin(), v.ids.end());\n    return true;\n  }\n  bool transform(Stmt *stmt) override {\n    IdVisitor v;\n    if (stmt)\n      stmt->accept(v);\n    ids.insert(v.ids.begin(), v.ids.end());\n    return true;\n  }\n  void visit(IdExpr *expr) override { ids.insert(expr->getValue()); }\n};\n\nvoid TranslateVisitor::visit(GeneratorExpr *expr) {\n  auto name = ctx->cache->imports[MAIN_IMPORT].ctx->generateCanonicalName(\"_generator\");\n  ir::Func *fn = ctx->cache->module->Nr<ir::BodiedFunc>(name);\n  fn->setGlobal();\n  fn->setGenerator();\n  std::vector<std::string> names;\n  std::vector<codon::ir::types::Type *> types;\n  std::vector<ir::Value *> items;\n\n  IdVisitor v;\n  expr->accept(v);\n  for (auto &i : v.ids) {\n    auto val = ctx->find(i);\n    if (val && !val->getFunc() && !val->getType() && !val->getVar()->isGlobal()) {\n      types.push_back(val->getVar()->getType());\n      names.push_back(i);\n      items.emplace_back(make<ir::VarValue>(expr, val->getVar()));\n    }\n  }\n  auto irType = ctx->cache->module->unsafeGetFuncType(\n      name, ctx->forceFind(expr->getType()->realizedName())->getType(), types, false);\n  fn->realize(irType, names);\n\n  ctx->addBlock();\n  for (auto &n : names)\n    ctx->add(TranslateItem::Var, n, fn->getArgVar(n));\n  auto body = make<ir::SeriesFlow>(expr, \"body\");\n  ctx->bases.push_back(cast<ir::BodiedFunc>(fn));\n  ctx->addSeries(body);\n\n  expr->setFinalStmt(ctx->cache->N<YieldStmt>(expr->getFinalExpr()));\n  auto e = expr->getFinalSuite();\n  transform(e);\n  ctx->popSeries();\n  ctx->bases.pop_back();\n  cast<ir::BodiedFunc>(fn)->setBody(body);\n  ctx->popBlock();\n  result = make<ir::CallInstr>(expr, make<ir::VarValue>(expr, fn), std::move(items));\n}\n\nvoid TranslateVisitor::visit(CallExpr *expr) {\n  auto ei = cast<IdExpr>(expr->getExpr());\n  if (ei && ei->getValue() == getMangledFunc(\"std.internal.core\", \"__ptr__\")) {\n    auto head = expr->begin()->getExpr();\n    ir::FlowInstr *pre = cast<ir::FlowInstr>(transform(head));\n    while (auto sexp = cast<StmtExpr>(head))\n      head = sexp->getExpr();\n\n    std::vector<std::string> members;\n    while (auto id = cast<DotExpr>(head)) {\n      members.emplace_back(id->getMember());\n      head = id->getExpr();\n    }\n    std::ranges::reverse(members);\n    auto id = cast<IdExpr>(head);\n    seqassert(id, \"expected IdExpr, got {}\", *((*expr)[0].value));\n    auto key = id->getValue();\n    auto val = ctx->find(key);\n    seqassert(val && val->getVar(), \"{} is not a variable\", key);\n\n    auto pv = make<ir::PointerValue>(expr, val->getVar(), members);\n    if (pre) {\n      pre->setValue(pv);\n      result = pre;\n    } else {\n      result = pv;\n    }\n    return;\n  } else if (ei && ei->getValue() ==\n                       getMangledMethod(\"std.internal.core\", \"__array__\", \"__new__\")) {\n    auto fnt = expr->getExpr()->getType()->getFunc();\n    auto sz = fnt->funcGenerics[0].type->getIntStatic()->value;\n    auto typ = fnt->funcParent->getClass()->generics[0].getType();\n\n    auto *arrayType = ctx->getModule()->unsafeGetArrayType(getType(typ));\n    arrayType->setAstType(expr->getType()->shared_from_this());\n    result = make<ir::StackAllocInstr>(expr, arrayType, sz);\n    return;\n  } else if (ei && startswith(ei->getValue(),\n                              getMangledMethod(\"std.internal.core\", \"Generator\",\n                                               \"_yield_in_no_suspend\"))) {\n    result = make<ir::YieldInInstr>(expr, getType(expr->getType()), false);\n    return;\n  }\n\n  auto ft = expr->getExpr()->getType()->getFunc();\n  seqassert(ft, \"not calling function\");\n  auto callee = transform(expr->getExpr());\n  bool isVariadic = ft->ast->hasAttribute(Attr::CVarArg);\n  std::vector<ir::Value *> items;\n  size_t i = 0;\n  for (auto &a : *expr) {\n    seqassert(!cast<EllipsisExpr>(a.value), \"ellipsis not elided\");\n    if (i + 1 == expr->size() && isVariadic) {\n      auto call = cast<CallExpr>(a.value);\n      seqassert(call, \"expected *args tuple: '{}'\", call->toString(0));\n      for (auto &arg : *call)\n        items.emplace_back(transform(arg.value));\n    } else {\n      items.emplace_back(transform(a.value));\n    }\n    i++;\n  }\n  result = make<ir::CallInstr>(expr, callee, std::move(items));\n}\n\nvoid TranslateVisitor::visit(DotExpr *expr) {\n  if (expr->getMember() == \"__atomic__\" || expr->getMember() == \"__elemsize__\" ||\n      expr->getMember() == \"__contents_atomic__\") {\n    auto ei = cast<IdExpr>(expr->getExpr());\n    seqassert(ei, \"expected IdExpr, got {}\", *(expr->getExpr()));\n    auto t = TypecheckVisitor(ctx->cache->typeCtx).extractType(ei->getType());\n    auto type = ctx->find(t->realizedName())->getType();\n    seqassert(type, \"{} is not a type\", ei->getValue());\n    result = make<ir::TypePropertyInstr>(\n        expr, type,\n        expr->getMember() == \"__atomic__\"\n            ? ir::TypePropertyInstr::Property::IS_ATOMIC\n            : (expr->getMember() == \"__contents_atomic__\"\n                   ? ir::TypePropertyInstr::Property::IS_CONTENT_ATOMIC\n                   : ir::TypePropertyInstr::Property::SIZEOF));\n  } else {\n    result =\n        make<ir::ExtractInstr>(expr, transform(expr->getExpr()), expr->getMember());\n  }\n}\n\nvoid TranslateVisitor::visit(YieldExpr *expr) {\n  result = make<ir::YieldInInstr>(expr, getType(expr->getType()));\n}\n\nvoid TranslateVisitor::visit(PipeExpr *expr) {\n  auto isGen = [](const ir::Value *v) -> bool {\n    auto *type = v->getType();\n    if (ir::isA<ir::types::GeneratorType>(type))\n      return true;\n    else if (auto *fn = cast<ir::types::FuncType>(type)) {\n      return ir::isA<ir::types::GeneratorType>(fn->getReturnType());\n    }\n    return false;\n  };\n\n  std::vector<ir::PipelineFlow::Stage> stages;\n  auto *firstStage = transform((*expr)[0].expr);\n  auto firstIsGen = isGen(firstStage);\n  stages.emplace_back(firstStage, std::vector<ir::Value *>(), firstIsGen, false);\n\n  // Pipeline without generators (just function call sugar)\n  auto simplePipeline = !firstIsGen;\n  for (auto i = 1; i < expr->size(); i++) {\n    auto call = cast<CallExpr>((*expr)[i].expr);\n    seqassert(call, \"{} is not a call\", *((*expr)[i].expr));\n\n    auto fn = transform(call->getExpr());\n    if (i + 1 != expr->size())\n      simplePipeline &= !isGen(fn);\n\n    std::vector<ir::Value *> args;\n    args.reserve(call->size());\n    for (auto &a : *call)\n      args.emplace_back(cast<EllipsisExpr>(a.value) ? nullptr : transform(a.value));\n    stages.emplace_back(fn, args, isGen(fn), false);\n  }\n\n  if (simplePipeline) {\n    // Transform a |> b |> c to c(b(a))\n    ir::util::CloneVisitor cv(ctx->getModule());\n    result = cv.clone(stages[0].getCallee());\n    for (auto i = 1; i < stages.size(); ++i) {\n      std::vector<ir::Value *> newArgs;\n      for (auto arg : stages[i])\n        newArgs.push_back(arg ? cv.clone(arg) : result);\n      result = make<ir::CallInstr>(expr, cv.clone(stages[i].getCallee()), newArgs);\n    }\n  } else {\n    for (int i = 0; i < expr->size(); i++)\n      if ((*expr)[i].op == \"||>\")\n        stages[i].setParallel();\n    // This is a statement in IR.\n    ctx->getSeries()->push_back(make<ir::PipelineFlow>(expr, stages));\n  }\n}\n\nvoid TranslateVisitor::visit(AwaitExpr *expr) {\n  result =\n      make<ir::AwaitInstr>(expr, transform(expr->getExpr()), getType(expr->getType()),\n                           expr->getExpr()->getType()->is(\n                               getMangledClass(\"std.internal.core\", \"Generator\")));\n}\n\nvoid TranslateVisitor::visit(StmtExpr *expr) {\n  auto *bodySeries = make<ir::SeriesFlow>(expr, \"body\");\n  ctx->addSeries(bodySeries);\n  for (auto &s : *expr)\n    transform(s);\n  ctx->popSeries();\n  result = make<ir::FlowInstr>(expr, bodySeries, transform(expr->getExpr()));\n}\n\n/************************************************************************************/\n\nir::Value *TranslateVisitor::transform(Stmt *stmt) {\n  TranslateVisitor v(ctx);\n  v.setSrcInfo(stmt->getSrcInfo());\n  stmt->accept(v);\n  if (v.result)\n    ctx->getSeries()->push_back(v.result);\n  return v.result;\n}\n\nvoid TranslateVisitor::defaultVisit(Stmt *n) {\n  seqassert(false, \"invalid node {}\", n->toString());\n}\n\nvoid TranslateVisitor::visit(SuiteStmt *stmt) {\n  for (auto *s : *stmt)\n    transform(s);\n}\n\nvoid TranslateVisitor::visit(BreakStmt *stmt) { result = make<ir::BreakInstr>(stmt); }\n\nvoid TranslateVisitor::visit(ContinueStmt *stmt) {\n  result = make<ir::ContinueInstr>(stmt);\n}\n\nvoid TranslateVisitor::visit(ExprStmt *stmt) {\n  IdExpr *ei = nullptr;\n  auto ce = cast<CallExpr>(stmt->getExpr());\n  if (ce && ((ei = cast<IdExpr>(ce->getExpr()))) &&\n      ei->getValue() ==\n          getMangledMethod(\"std.internal.core\", \"Generator\", \"_yield_final\")) {\n    result = make<ir::YieldInstr>(stmt, transform((*ce)[0].value), true);\n    ctx->getBase()->setGenerator();\n  } else {\n    result = transform(stmt->getExpr());\n  }\n}\n\nvoid TranslateVisitor::visit(AssignStmt *stmt) {\n  if (stmt->getLhs() && cast<IdExpr>(stmt->getLhs()) &&\n      cast<IdExpr>(stmt->getLhs())->getValue() == VAR_ARGV)\n    return;\n\n  auto lei = cast<IdExpr>(stmt->getLhs());\n  seqassert(lei, \"expected IdExpr, got {}\", *stmt);\n  auto var = lei->getValue();\n\n  auto isGlobal = in(ctx->cache->globals, var);\n  ir::Var *v = nullptr;\n\n  if (stmt->isUpdate()) {\n    auto val = ctx->find(lei->getValue());\n    seqassert(val && val->getVar(), \"{} is not a variable\", lei->getValue());\n    v = val->getVar();\n\n    if (!v->getType()) {\n      v->setSrcInfo(stmt->getSrcInfo());\n      v->setType(getType(stmt->getRhs()->getType()));\n    }\n    result = make<ir::AssignInstr>(stmt, v, transform(stmt->getRhs()));\n    return;\n  }\n\n  if (!stmt->getLhs()->getType()->isInstantiated() ||\n      (stmt->getLhs()->getType()->is(TYPE_TYPE)) ||\n      stmt->getLhs()->getType()->getFunc()) {\n    if (!cast<IdExpr>(stmt->getRhs())) {\n      // Side effect\n      result = transform(stmt->getRhs());\n    }\n    return; // type aliases/fn aliases etc\n  }\n\n  if (isGlobal) {\n    seqassert(ctx->find(var) && ctx->find(var)->getVar(), \"cannot find global '{}'\",\n              var);\n    v = ctx->find(var)->getVar();\n    v->setSrcInfo(stmt->getSrcInfo());\n    v->setType(getType((stmt->getRhs() ? stmt->getRhs() : stmt->getLhs())->getType()));\n  } else {\n    v = make<ir::Var>(\n        stmt, getType((stmt->getRhs() ? stmt->getRhs() : stmt->getLhs())->getType()),\n        false, false, false, var);\n    ctx->getBase()->push_back(v);\n    ctx->add(TranslateItem::Var, var, v);\n  }\n  // Check if it is thread-local\n  if (stmt->isThreadLocal()) {\n    v->setThreadLocal();\n    v->setGlobal();\n  }\n  // Check if it is a C variable\n  if (stmt->getLhs()->hasAttribute(Attr::ExprExternVar)) {\n    v->setExternal();\n    v->setName(ctx->cache->rev(var));\n    v->setGlobal();\n    return;\n  }\n\n  if (stmt->getRhs()) {\n    result = make<ir::AssignInstr>(stmt, v, transform(stmt->getRhs()));\n  }\n}\n\nvoid TranslateVisitor::visit(AssignMemberStmt *stmt) {\n  result = make<ir::InsertInstr>(stmt, transform(stmt->getLhs()), stmt->getMember(),\n                                 transform(stmt->getRhs()));\n}\n\nvoid TranslateVisitor::visit(ReturnStmt *stmt) {\n  result = make<ir::ReturnInstr>(stmt, stmt->getExpr() ? transform(stmt->getExpr())\n                                                       : nullptr);\n}\n\nvoid TranslateVisitor::visit(YieldStmt *stmt) {\n  result = make<ir::YieldInstr>(stmt,\n                                stmt->getExpr() ? transform(stmt->getExpr()) : nullptr);\n  ctx->getBase()->setGenerator();\n}\n\nvoid TranslateVisitor::visit(WhileStmt *stmt) {\n  auto loop = make<ir::WhileFlow>(stmt, transform(stmt->getCond()),\n                                  make<ir::SeriesFlow>(stmt, \"body\"));\n  ctx->addSeries(cast<ir::SeriesFlow>(loop->getBody()));\n  transform(stmt->getSuite());\n  ctx->popSeries();\n  result = loop;\n}\n\nvoid TranslateVisitor::visit(ForStmt *stmt) {\n  std::unique_ptr<OMPSched> os = nullptr;\n  if (stmt->getDecorator()) {\n    auto c = cast<CallExpr>(stmt->getDecorator());\n    seqassert(c, \"for par is not a call: {}\", *(stmt->getDecorator()));\n    auto fc = c->getExpr()->getType()->getFunc();\n    seqassert(fc && fc->ast->getName() == getMangledFunc(\"std.openmp\", \"for_par\"),\n              \"for par is not a function\");\n    auto schedule = fc->funcGenerics[0].type->getStrStatic()->value;\n    bool ordered = fc->funcGenerics[1].type->getBoolStatic()->value;\n    auto threads = transform((*c)[0].value);\n    auto chunk = transform((*c)[1].value);\n    auto collapse = fc->funcGenerics[2].type->getIntStatic()->value;\n    bool gpu = fc->funcGenerics[3].type->getBoolStatic()->value;\n    os = std::make_unique<OMPSched>(schedule, threads, chunk, ordered, collapse, gpu);\n  }\n\n  seqassert(cast<IdExpr>(stmt->getVar()), \"expected IdExpr, got {}\", *(stmt->getVar()));\n  auto varName = cast<IdExpr>(stmt->getVar())->getValue();\n  ir::Var *var = nullptr;\n  if (!ctx->find(varName) || !stmt->getVar()->hasAttribute(Attr::ExprDominated)) {\n    var = make<ir::Var>(stmt, getType(stmt->getVar()->getType()), false, false, false,\n                        varName);\n  } else {\n    var = ctx->find(varName)->getVar();\n  }\n  ctx->getBase()->push_back(var);\n  auto bodySeries = make<ir::SeriesFlow>(stmt, \"body\");\n\n  auto loop = make<ir::ForFlow>(stmt, transform(stmt->getIter()), bodySeries, var);\n  if (stmt->isAsync())\n    loop->setAsync();\n  if (os)\n    loop->setSchedule(std::move(os));\n  ctx->add(TranslateItem::Var, varName, var);\n  ctx->addSeries(cast<ir::SeriesFlow>(loop->getBody()));\n  transform(stmt->getSuite());\n  ctx->popSeries();\n  result = loop;\n}\n\nvoid TranslateVisitor::visit(IfStmt *stmt) {\n  auto cond = transform(stmt->getCond());\n  auto trueSeries = make<ir::SeriesFlow>(stmt, \"ifstmt_true\");\n  ctx->addSeries(trueSeries);\n  transform(stmt->getIf());\n  ctx->popSeries();\n\n  ir::SeriesFlow *falseSeries = nullptr;\n  if (stmt->getElse()) {\n    falseSeries = make<ir::SeriesFlow>(stmt, \"ifstmt_false\");\n    ctx->addSeries(falseSeries);\n    transform(stmt->getElse());\n    ctx->popSeries();\n  }\n  result = make<ir::IfFlow>(stmt, cond, trueSeries, falseSeries);\n}\n\nvoid TranslateVisitor::visit(TryStmt *stmt) {\n  auto *bodySeries = make<ir::SeriesFlow>(stmt, \"body\");\n  ctx->addSeries(bodySeries);\n  transform(stmt->getSuite());\n  ctx->popSeries();\n\n  ir::SeriesFlow *finallySeries = make<ir::SeriesFlow>(stmt, \"finally\");\n  if (stmt->getFinally()) {\n    ctx->addSeries(finallySeries);\n    transform(stmt->getFinally());\n    ctx->popSeries();\n  }\n\n  ir::SeriesFlow *elseSeries = nullptr;\n  if (stmt->getElse()) {\n    elseSeries = make<ir::SeriesFlow>(stmt, \"else\");\n    ctx->addSeries(elseSeries);\n    transform(stmt->getElse());\n    ctx->popSeries();\n  }\n\n  auto *tc = make<ir::TryCatchFlow>(stmt, bodySeries, finallySeries, elseSeries);\n  for (auto *c : *stmt) {\n    auto *catchBody = make<ir::SeriesFlow>(stmt, \"catch\");\n    auto *excType = c->getException()\n                        ? getType(TypecheckVisitor(ctx->cache->typeCtx)\n                                      .extractType(c->getException()->getType()))\n                        : nullptr;\n    ir::Var *catchVar = nullptr;\n    if (!c->getVar().empty()) {\n      if (!ctx->find(c->getVar()) || !c->hasAttribute(Attr::ExprDominated)) {\n        catchVar = make<ir::Var>(stmt, excType, false, false, false, c->getVar());\n      } else {\n        catchVar = ctx->find(c->getVar())->getVar();\n      }\n      ctx->add(TranslateItem::Var, c->getVar(), catchVar);\n      ctx->getBase()->push_back(catchVar);\n    }\n    ctx->addSeries(catchBody);\n    transform(c->getSuite());\n    ctx->popSeries();\n    tc->push_back(ir::TryCatchFlow::Catch(catchBody, excType, catchVar));\n  }\n  result = tc;\n}\n\nvoid TranslateVisitor::visit(ThrowStmt *stmt) {\n  result = make<ir::ThrowInstr>(stmt,\n                                stmt->getExpr() ? transform(stmt->getExpr()) : nullptr);\n}\n\nvoid TranslateVisitor::visit(FunctionStmt *stmt) {\n  // Process all realizations.\n  transformFunctionRealizations(stmt->getName(), stmt->hasAttribute(Attr::LLVM));\n}\n\nvoid TranslateVisitor::visit(ClassStmt *stmt) {\n  // Nothing to see here, as all type handles are already generated.\n  // Methods will be handled by FunctionStmt visitor.\n}\n\n/************************************************************************************/\n\ncodon::ir::types::Type *TranslateVisitor::getType(types::Type *t) const {\n  seqassert(t && t->getClass(), \"not a class: {}\", t ? t->debugString(2) : \"-\");\n  std::string name = t->getClass()->ClassType::realizedName();\n  auto i = ctx->find(name);\n  seqassert(i, \"type {} not realized: {}\", t->debugString(2), name);\n  seqassert(i->getType(), \"type {} not IR-realized: {}\", t->debugString(2), name);\n  return i->getType();\n}\n\nvoid TranslateVisitor::transformFunctionRealizations(const std::string &name,\n                                                     bool isLLVM) {\n  for (auto &real : ctx->cache->functions[name].realizations) {\n    if (!in(ctx->cache->pendingRealizations, make_pair(name, real.first)))\n      continue;\n    ctx->cache->pendingRealizations.erase(make_pair(name, real.first));\n\n    LOG_TYPECHECK(\"[translate] generating fn {}\", real.first);\n    real.second->ir->setSrcInfo(getSrcInfo());\n    const auto &ast = real.second->ast;\n    seqassert(ast, \"AST not set for {}\", real.first);\n    if (!isLLVM)\n      transformFunction(real.second->type.get(), ast, real.second->ir);\n    else\n      transformLLVMFunction(real.second->type.get(), ast, real.second->ir);\n  }\n}\n\nvoid TranslateVisitor::transformFunction(const types::FuncType *type, FunctionStmt *ast,\n                                         ir::Func *func) {\n  std::vector<std::string> names;\n  std::vector<int> indices;\n  for (int i = 0, j = 0; i < ast->size(); i++)\n    if ((*ast)[i].isValue()) {\n      if (!(*type)[j]->getFunc()) {\n        names.push_back(ctx->cache->rev((*ast)[i].name));\n        indices.push_back(i);\n      }\n      j++;\n    }\n  if (ast->hasAttribute(Attr::CVarArg)) {\n    names.pop_back();\n    indices.pop_back();\n  }\n  // TODO: refactor IR attribute API\n  std::unordered_map<std::string, std::string> attr;\n  if (ast->hasAttribute(Attr::FunctionAttributes))\n    attr =\n        ast->getAttribute<ir::KeyValueAttribute>(Attr::FunctionAttributes)->attributes;\n  attr[\".module\"] = ast->getAttribute<ir::StringValueAttribute>(Attr::Module)->value;\n  func->setAttribute(std::make_unique<ir::KeyValueAttribute>(attr));\n  for (int i = 0; i < names.size(); i++)\n    func->getArgVar(names[i])->setSrcInfo((*ast)[indices[i]].getSrcInfo());\n  // func->setUnmangledName(ctx->cache->reverseIdentifierLookup[type->ast->name]);\n  if (!ast->hasAttribute(Attr::C) && !ast->hasAttribute(Attr::Internal)) {\n    ctx->addBlock();\n    for (auto i = 0; i < names.size(); i++)\n      ctx->add(TranslateItem::Var, (*ast)[indices[i]].name, func->getArgVar(names[i]));\n    auto body = make<ir::SeriesFlow>(ast, \"body\");\n    ctx->bases.push_back(cast<ir::BodiedFunc>(func));\n    ctx->addSeries(body);\n    transform(ast->getSuite());\n    ctx->popSeries();\n    ctx->bases.pop_back();\n    cast<ir::BodiedFunc>(func)->setBody(body);\n    ctx->popBlock();\n  }\n  if (ast->isAsync())\n    func->setAsync();\n}\n\nvoid TranslateVisitor::transformLLVMFunction(types::FuncType *type, FunctionStmt *ast,\n                                             ir::Func *func) const {\n  std::vector<std::string> names;\n  std::vector<int> indices;\n  for (int i = 0, j = 1; i < ast->size(); i++)\n    if ((*ast)[i].isValue()) {\n      names.push_back(ctx->cache->reverseIdentifierLookup[(*ast)[i].name]);\n      indices.push_back(i);\n      j++;\n    }\n  auto f = cast<ir::LLVMFunc>(func);\n  seqassert(f, \"not a function\");\n  std::unordered_map<std::string, std::string> attr;\n  if (ast->hasAttribute(Attr::FunctionAttributes))\n    attr =\n        ast->getAttribute<ir::KeyValueAttribute>(Attr::FunctionAttributes)->attributes;\n  attr[\".module\"] = ast->getAttribute<ir::StringValueAttribute>(Attr::Module)->value;\n  func->setAttribute(std::make_unique<ir::KeyValueAttribute>(attr));\n  for (int i = 0; i < names.size(); i++)\n    func->getArgVar(names[i])->setSrcInfo((*ast)[indices[i]].getSrcInfo());\n\n  seqassert(\n      ast->getSuite()->firstInBlock() &&\n          cast<ExprStmt>(ast->getSuite()->firstInBlock()) &&\n          cast<StringExpr>(cast<ExprStmt>(ast->getSuite()->firstInBlock())->getExpr()),\n      \"LLVM function does not begin with a string\");\n  std::istringstream sin(\n      cast<StringExpr>(cast<ExprStmt>(ast->getSuite()->firstInBlock())->getExpr())\n          ->getValue());\n  std::vector<ir::types::Generic> literals;\n  auto ss = cast<SuiteStmt>(ast->getSuite());\n  for (int i = 1; i < ss->size(); i++) {\n    if (auto sti = cast<ExprStmt>((*ss)[i])->getExpr()->getType()->getIntStatic()) {\n      literals.emplace_back(sti->value);\n    } else if (auto sts =\n                   cast<ExprStmt>((*ss)[i])->getExpr()->getType()->getStrStatic()) {\n      literals.emplace_back(sts->value);\n    } else {\n      seqassert(cast<ExprStmt>((*ss)[i])->getExpr()->getType(),\n                \"invalid LLVM type argument: {}\", (*ss)[i]->toString(0));\n      literals.emplace_back(\n          getType(TypecheckVisitor(ctx->cache->typeCtx)\n                      .extractType(cast<ExprStmt>((*ss)[i])->getExpr()->getType())));\n    }\n  }\n  bool isDeclare = true;\n  std::string declare;\n  std::vector<std::string> lines;\n  for (std::string l; getline(sin, l);) {\n    std::string lp = l;\n    ltrim(lp);\n    rtrim(lp);\n    // Extract declares and constants.\n    if (isDeclare && !startswith(lp, \"declare \") && !startswith(lp, \"@\")) {\n      bool isConst = lp.find(\"private constant\") != std::string::npos;\n      if (!isConst) {\n        isDeclare = false;\n        if (!lp.empty() && lp.back() != ':')\n          lines.emplace_back(\"entry:\");\n      }\n    }\n    if (isDeclare)\n      declare += lp + \"\\n\";\n    else\n      lines.emplace_back(l);\n  }\n  f->setLLVMBody(join(lines, \"\\n\"));\n  f->setLLVMDeclarations(declare);\n  f->setLLVMLiterals(literals);\n  // func->setUnmangledName(ctx->cache->reverseIdentifierLookup[type->ast->name]);\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/translate/translate.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <string>\n#include <unordered_map>\n\n#include \"codon/cir/cir.h\"\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/visitors/translate/translate_ctx.h\"\n#include \"codon/parser/visitors/visitor.h\"\n\nnamespace codon::ast {\n\nclass TranslateVisitor : public CallbackASTVisitor<ir::Value *, ir::Value *> {\n  std::shared_ptr<TranslateContext> ctx;\n  ir::Value *result;\n\npublic:\n  explicit TranslateVisitor(std::shared_ptr<TranslateContext> ctx);\n  static codon::ir::Func *apply(Cache *cache, Stmt *stmts);\n  void translateStmts(Stmt *stmts) const;\n\n  ir::Value *transform(Expr *expr) override;\n  ir::Value *transform(Stmt *stmt) override;\n\n  void initializeGlobals() const;\n\nprivate:\n  void defaultVisit(Expr *expr) override;\n  void defaultVisit(Stmt *expr) override;\n\npublic:\n  void visit(NoneExpr *) override;\n  void visit(BoolExpr *) override;\n  void visit(IntExpr *) override;\n  void visit(FloatExpr *) override;\n  void visit(StringExpr *) override;\n  void visit(IdExpr *) override;\n  void visit(IfExpr *) override;\n  void visit(GeneratorExpr *) override;\n  void visit(CallExpr *) override;\n  void visit(DotExpr *) override;\n  void visit(YieldExpr *) override;\n  void visit(StmtExpr *) override;\n  void visit(PipeExpr *) override;\n  void visit(AwaitExpr *) override;\n\n  void visit(SuiteStmt *) override;\n  void visit(BreakStmt *) override;\n  void visit(ContinueStmt *) override;\n  void visit(ExprStmt *) override;\n  void visit(AssignStmt *) override;\n  void visit(AssignMemberStmt *) override;\n  void visit(ReturnStmt *) override;\n  void visit(YieldStmt *) override;\n  void visit(WhileStmt *) override;\n  void visit(ForStmt *) override;\n  void visit(IfStmt *) override;\n  void visit(TryStmt *) override;\n  void visit(ThrowStmt *) override;\n  void visit(FunctionStmt *) override;\n  void visit(ClassStmt *) override;\n  void visit(CommentStmt *) override {}\n  void visit(DirectiveStmt *) override {}\n\nprivate:\n  ir::types::Type *getType(types::Type *t) const;\n\n  void transformFunctionRealizations(const std::string &name, bool isLLVM);\n  void transformFunction(const types::FuncType *type, FunctionStmt *ast,\n                         ir::Func *func);\n  void transformLLVMFunction(types::FuncType *type, FunctionStmt *ast,\n                             ir::Func *func) const;\n\n  template <typename ValueType, typename... Args> ValueType *make(Args &&...args) {\n    auto *ret = ctx->getModule()->N<ValueType>(std::forward<Args>(args)...);\n    return ret;\n  }\n};\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/translate/translate_ctx.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"translate_ctx.h\"\n\n#include <memory>\n#include <vector>\n\n#include \"codon/parser/common.h\"\n#include \"codon/parser/ctx.h\"\n#include \"codon/parser/visitors/translate/translate.h\"\n#include \"codon/parser/visitors/typecheck/ctx.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nnamespace codon::ast {\n\nTranslateContext::TranslateContext(Cache *cache)\n    : Context<TranslateItem>(\"\"), cache(cache) {}\n\nstd::shared_ptr<TranslateItem> TranslateContext::find(const std::string &name) const {\n  if (auto t = Context<TranslateItem>::find(name))\n    return t;\n  std::shared_ptr<TranslateItem> ret = nullptr;\n  auto tt = cache->typeCtx->find(name);\n  if (tt && tt->isType() && tt->type->canRealize()) {\n    auto t = tt->getType();\n    if (name != t->realizedName()) // type prefix\n      t = TypecheckVisitor(cache->typeCtx).extractType(t);\n    auto n = t->getClass()->name;\n    if (!in(cache->classes, n) || !in(cache->classes[n].realizations, name))\n      return nullptr;\n    ret = std::make_shared<TranslateItem>(TranslateItem::Type, bases[0]);\n    ret->handle.type = cache->classes[n].realizations[name]->ir;\n  } else if (tt && tt->type->getFunc() && tt->type->canRealize()) {\n    ret = std::make_shared<TranslateItem>(TranslateItem::Func, bases[0]);\n    seqassertn(\n        in(cache->functions, tt->type->getFunc()->ast->getName()) &&\n            in(cache->functions[tt->type->getFunc()->ast->getName()].realizations,\n               name),\n        \"cannot find function realization {}\", name);\n    ret->handle.func =\n        cache->functions[tt->type->getFunc()->ast->getName()].realizations[name]->ir;\n  }\n  return ret;\n}\n\nstd::shared_ptr<TranslateItem>\nTranslateContext::forceFind(const std::string &name) const {\n  auto i = find(name);\n  seqassertn(i, \"cannot find '{}'\", name);\n  return i;\n}\n\nstd::shared_ptr<TranslateItem>\nTranslateContext::add(TranslateItem::Kind kind, const std::string &name, void *type) {\n  auto it = std::make_shared<TranslateItem>(kind, getBase());\n  if (kind == TranslateItem::Var)\n    it->handle.var = static_cast<ir::Var *>(type);\n  else if (kind == TranslateItem::Func)\n    it->handle.func = static_cast<ir::Func *>(type);\n  else\n    it->handle.type = static_cast<ir::types::Type *>(type);\n  add(name, it);\n  return it;\n}\n\nvoid TranslateContext::addSeries(codon::ir::SeriesFlow *s) { series.push_back(s); }\nvoid TranslateContext::popSeries() { series.pop_back(); }\n\ncodon::ir::Module *TranslateContext::getModule() const {\n  return dynamic_cast<codon::ir::Module *>(bases[0]->getModule());\n}\ncodon::ir::BodiedFunc *TranslateContext::getBase() const { return bases.back(); }\ncodon::ir::SeriesFlow *TranslateContext::getSeries() const { return series.back(); }\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/translate/translate_ctx.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#include \"codon/cir/cir.h\"\n#include \"codon/cir/types/types.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/ctx.h\"\n\nnamespace codon::ast {\n\n/**\n * IR context object description.\n * This represents an identifier that can be either a function, a class (type), or a\n * variable.\n */\nstruct TranslateItem {\n  enum Kind { Func, Type, Var } kind;\n  /// IR handle.\n  union {\n    codon::ir::Var *var;\n    codon::ir::Func *func;\n    codon::ir::types::Type *type;\n  } handle;\n  /// Base function pointer.\n  codon::ir::BodiedFunc *base;\n\n  TranslateItem(Kind k, codon::ir::BodiedFunc *base)\n      : kind(k), handle{nullptr}, base(base) {}\n  const codon::ir::BodiedFunc *getBase() const { return base; }\n  codon::ir::Func *getFunc() const { return kind == Func ? handle.func : nullptr; }\n  codon::ir::types::Type *getType() const {\n    return kind == Type ? handle.type : nullptr;\n  }\n  codon::ir::Var *getVar() const { return kind == Var ? handle.var : nullptr; }\n};\n\n/**\n * A variable table (context) for the IR translation stage.\n */\nstruct TranslateContext : public Context<TranslateItem> {\n  /// A pointer to the shared cache.\n  Cache *cache;\n  /// Stack of function bases.\n  std::vector<codon::ir::BodiedFunc *> bases;\n  /// Stack of IR series (blocks).\n  std::vector<codon::ir::SeriesFlow *> series;\n  /// Stack of sequence items for attribute initialization.\n  std::vector<std::vector<std::pair<int, ir::Value *>>> seqItems;\n\npublic:\n  TranslateContext(Cache *cache);\n\n  using Context<TranslateItem>::add;\n  /// Convenience method for adding an object to the context.\n  std::shared_ptr<TranslateItem> add(TranslateItem::Kind kind, const std::string &name,\n                                     void *type);\n  std::shared_ptr<TranslateItem> find(const std::string &name) const override;\n  std::shared_ptr<TranslateItem> forceFind(const std::string &name) const;\n\n  /// Convenience method for adding a series.\n  void addSeries(codon::ir::SeriesFlow *s);\n  void popSeries();\n\npublic:\n  codon::ir::Module *getModule() const;\n  codon::ir::BodiedFunc *getBase() const;\n  codon::ir::SeriesFlow *getSeries() const;\n};\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/access.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <string>\n#include <tuple>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/match.h\"\n#include \"codon/parser/visitors/scoping/scoping.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nusing namespace codon::error;\nusing namespace codon::matcher;\n\nnamespace codon::ast {\n\nusing namespace types;\n\n/// Typecheck identifiers.\n/// If an identifier is a static variable, evaluate it and replace it with its value\n///   (e.g., change `N` to `IntExpr(16)`).\n/// If the identifier of a generic is fully qualified, use its qualified name\n///   (e.g., replace `Ptr` with `Ptr[byte]`).\nvoid TypecheckVisitor::visit(IdExpr *expr) {\n  auto val = ctx->find(expr->getValue(), getTime());\n  if (!val) {\n    E(Error::ID_NOT_FOUND, expr, expr->getValue());\n  }\n\n  // If this is an overload, use the dispatch function\n  if (isUnbound(expr) && hasOverloads(val->getName())) {\n    val = ctx->forceFind(getDispatch(val->getName())->getFuncName());\n  }\n\n  // If we are accessing an outside variable, capture it or raise an error\n  checkCapture(val);\n\n  // Replace the variable with its canonical name\n  expr->value = val->getName();\n\n  // Set up type\n  unify(expr->getType(), instantiateType(val->getType()));\n  if (auto f = expr->getType()->getFunc()) {\n    expr->value = f->getFuncName(); // resolve overloads\n  }\n\n  // Realize a type or a function if possible and replace the identifier with\n  // a qualified identifier or a static expression (e.g., `foo` -> `foo[int]`)\n  if (expr->getType()->canRealize()) {\n    if (auto s = expr->getType()->getStatic()) {\n      resultExpr = transform(s->getStaticExpr());\n      return;\n    }\n    if (!val->isVar()) {\n      if (!(expr->hasAttribute(Attr::ExprDoNotRealize) && expr->getType()->getFunc())) {\n        if (auto r = realize(expr->getType())) {\n          expr->value = r->realizedName();\n          expr->setDone();\n        }\n      }\n    } else {\n      realize(expr->getType());\n      expr->setDone();\n    }\n  }\n\n  // If this identifier needs __used__ checks (see @c ScopeVisitor), add them\n  if (expr->hasAttribute(Attr::ExprDominatedUndefCheck)) {\n    auto controlVar =\n        fmt::format(\"{}{}\", getUnmangledName(val->canonicalName), VAR_USED_SUFFIX);\n    if (ctx->find(controlVar, getTime())) {\n      auto checkStmt = N<ExprStmt>(N<CallExpr>(\n          N<DotExpr>(N<IdExpr>(\"__internal__\"), \"undef\"), N<IdExpr>(controlVar),\n          N<StringExpr>(getUnmangledName(val->canonicalName))));\n      expr->eraseAttribute(Attr::ExprDominatedUndefCheck);\n      resultExpr = transform(N<StmtExpr>(checkStmt, expr));\n    }\n  }\n}\n\n/// Transform a dot expression. Select the best method overload if possible.\n/// @example\n///   `obj.__class__`   -> `type(obj)`\n///   `cls.__name__`    -> `\"class\"` (same for functions)\n///   `obj.method`      -> `cls.method(obj, ...)` or\n///                        `cls.method(obj)` if method has `@property` attribute\n///   `obj.member`      -> see @c getClassMember\n/// @return nullptr if no transformation was made\nvoid TypecheckVisitor::visit(DotExpr *expr) {\n  // Check if this is being called from CallExpr (e.g., foo.bar(...))\n  // and mark it as such (to inline useless partial expression)\n  auto *parentCall = cast<CallExpr>(ctx->getParentNode());\n  if (parentCall && !parentCall->hasAttribute(Attr::ParentCallExpr))\n    parentCall = nullptr;\n\n  // Flatten imports:\n  //   `a.b.c`      -> canonical name of `c` in `a.b` if `a.b` is an import\n  //   `a.B.c`      -> canonical name of `c` in class `a.B`\n  //   `python.foo` -> internal.python._get_identifier(\"foo\")\n  std::vector<std::string> chain;\n  Expr *head = expr;\n  for (; cast<DotExpr>(head); head = cast<DotExpr>(head)->getExpr())\n    chain.push_back(cast<DotExpr>(head)->getMember());\n  Expr *final = expr;\n  if (auto id = cast<IdExpr>(head)) {\n    // Case: a.bar.baz\n    chain.push_back(id->getValue());\n    std::ranges::reverse(chain);\n    auto [pos, val] = getImport(chain);\n    if (!val) {\n      // Python capture\n      seqassert(ctx->getBase()->pyCaptures, \"unexpected py capture\");\n      ctx->getBase()->pyCaptures->insert(chain[0]);\n      final = N<IndexExpr>(N<IdExpr>(\"__pyenv__\"), N<StringExpr>(chain[0]));\n    } else if (val->getModule() == \"std.python\" &&\n               ctx->getModule() != val->getModule()) {\n      // Import from python (e.g., pyobj.foo)\n      final = transform(N<CallExpr>(\n          N<DotExpr>(N<DotExpr>(N<IdExpr>(\"internal\"), \"python\"), \"_get_identifier\"),\n          N<StringExpr>(chain[pos++])));\n    } else if (val->getModule() == ctx->getModule() && pos == 1) {\n      final = transform(N<IdExpr>(chain[0]), true);\n    } else {\n      final = N<IdExpr>(val->canonicalName);\n    }\n    while (pos < chain.size())\n      final = N<DotExpr>(final, chain[pos++]);\n  }\n  if (auto dot = cast<DotExpr>(final)) {\n    expr->expr = dot->getExpr();\n    expr->member = dot->getMember();\n  } else {\n    resultExpr = transform(final);\n    return;\n  }\n\n  // Special case: obj.__class__\n  if (expr->getMember() == \"__class__\") {\n    /// TODO: prevent cls.__class__ and type(cls)\n    resultExpr = transform(N<CallExpr>(N<IdExpr>(TYPE_TYPE), expr->getExpr()));\n    return;\n  }\n  expr->expr = transform(expr->getExpr());\n\n  bool hasSide = hasSideEffect(expr->getExpr());\n  auto wrapSide = [&](Expr *e) -> Expr * {\n    if (hasSide) {\n      return transform(N<StmtExpr>(N<ExprStmt>(expr->getExpr()), e));\n    }\n    return transform(e);\n  };\n\n  // Special case: fn.__name__\n  // Should go before cls.__name__ to allow printing generic functions\n  // NOTE: NO SIDE EFFECTS! (because of generic function support!)\n  if (extractType(expr->getExpr())->getFunc() && expr->getMember() == \"__name__\") {\n    resultExpr = N<StringExpr>(extractType(expr->getExpr())->prettyString());\n    return;\n  }\n  if (expr->getExpr()->getType()->getPartial() && expr->getMember() == \"__name__\") {\n    resultExpr = wrapSide(\n        N<StringExpr>(expr->getExpr()->getType()->getPartial()->prettyString()));\n    return;\n  }\n  // Special case: fn.__llvm_name__ or obj.__llvm_name__\n  if (expr->getMember() == \"__llvm_name__\") {\n    if (realize(expr->getExpr()->getType()))\n      resultExpr = wrapSide(N<StringExpr>(expr->getExpr()->getType()->realizedName()));\n    return;\n  }\n  // Special case: cls.__name__\n  if (isTypeExpr(expr->getExpr()) && expr->getMember() == \"__name__\") {\n    if (realize(expr->getExpr()->getType()))\n      resultExpr =\n          wrapSide(N<StringExpr>(extractType(expr->getExpr())->prettyString()));\n    return;\n  }\n  // Special case: cls.__mro__\n  if (isTypeExpr(expr->getExpr()) && expr->getMember() == \"__mro__\") {\n    if (realize(expr->getExpr()->getType())) {\n      auto t = extractType(expr->getExpr())->getClass();\n      auto bases = getRTTISuperTypes(t);\n      std::vector<Expr *> items;\n      for (size_t i = 1; i < bases.size(); i++)\n        items.push_back(N<IdExpr>(bases[i]->realizedName()));\n      resultExpr = wrapSide(N<TupleExpr>(items));\n    }\n    return;\n  }\n  if (isTypeExpr(expr->getExpr()) && expr->getMember() == \"__repr__\") {\n    resultExpr = transform(N<CallExpr>(\n        N<IdExpr>(getMangledFunc(\"std.internal.types.type\", \"__type_repr__\")),\n        expr->getExpr(), N<EllipsisExpr>(EllipsisExpr::PARTIAL)));\n    return;\n  }\n  // Special case: expr.__is_static__\n  if (expr->getMember() == \"__is_static__\") {\n    if (expr->getExpr()->isDone())\n      resultExpr = wrapSide(\n          N<BoolExpr>(static_cast<bool>(expr->getExpr()->getType()->getStatic())));\n    return;\n  }\n  // Special case: cls.__id__\n  if (isTypeExpr(expr->getExpr()) && expr->getMember() == \"__id__\") {\n    if (auto c = realize(extractType(expr->getExpr())))\n      resultExpr = wrapSide(N<IntExpr>(getClassRealization(c)->id));\n    return;\n  }\n\n  // Ensure that the type is known (otherwise wait until it becomes known)\n  auto typ = extractClassType(expr->getExpr());\n  if (!typ)\n    return;\n\n  // Check if this is a method or member access\n  while (true) {\n    auto methods = findMethod(typ, expr->getMember());\n    if (methods.empty())\n      resultExpr = getClassMember(expr);\n\n    // If the expression changed during the @c getClassMember (e.g., optional unwrap),\n    // keep going further to be able to select the appropriate method or member\n    auto oldExpr = expr->getExpr();\n    if (!resultExpr && expr->getExpr() != oldExpr) {\n      typ = extractClassType(expr->getExpr());\n      if (!typ)\n        return; // delay typechecking\n      continue;\n    }\n\n    if (!methods.empty()) {\n      // If a method is ambiguous use dispatch\n      auto bestMethod = methods.size() > 1 ? getDispatch(getRootName(methods.front()))\n                                           : methods.front();\n      Expr *e = N<IdExpr>(bestMethod->getFuncName());\n      e->setType(instantiateType(bestMethod, typ));\n\n      if (isTypeExpr(expr->getExpr())) {\n        // Static access: `cls.method`\n        unify(expr->getType(), e->getType());\n        resultExpr = wrapSide(e);\n        return;\n      }\n\n      auto vt = expr->getExpr()->getType();\n      if (vt->is(\"Super\"))\n        vt = extractClassGeneric(vt);\n      auto cls = getClass(vt);\n      bool isVirtual =\n          cls && cls->rtti &&\n          static_cast<bool>(\n              in(cls->virtuals, getUnmangledName(bestMethod->ast->getName()))) &&\n          !isDispatch(bestMethod) &&\n          !bestMethod->ast->hasAttribute(Attr::StaticMethod) &&\n          !bestMethod->ast->hasAttribute(Attr::Property);\n\n      if (parentCall && !bestMethod->ast->hasAttribute(Attr::StaticMethod) &&\n          !bestMethod->ast->hasAttribute(Attr::Property) && !isVirtual) {\n        // Instance access: `obj.method` from the call\n        // Modify the call to push `self` to the front of the argument list.\n        // Avoids creating partial functions.\n        parentCall->items.insert(parentCall->items.begin(), expr->getExpr());\n        unify(expr->getType(), e->getType());\n        resultExpr = transform(e);\n      } else {\n        if (isVirtual) {\n          Expr *id = nullptr, *slf = nullptr;\n          if (expr->getExpr()->getType()->is(\"Super\")) {\n            id = N<DotExpr>(N<DotExpr>(expr->getExpr(), \"__T__\"), \"__id__\");\n            slf = N<DotExpr>(expr->getExpr(), \"__obj__\");\n          } else {\n            id = N<IntExpr>(0);\n            slf = expr->getExpr();\n          }\n          resultExpr = transform(\n              N<CallExpr>(N<IdExpr>(getMangledMethod(\"std.internal.core\", \"RTTIType\",\n                                                     \"_thunk_dispatch\")),\n                          std::vector<CallArg>{\n                              CallArg{\"slf\", slf}, CallArg{\"cls_id\", id},\n                              CallArg{\"F\", N<IdExpr>(bestMethod->ast->name)},\n                              CallArg{\"\", N<EllipsisExpr>(EllipsisExpr::PARTIAL)}}));\n          return;\n        }\n\n        // Instance access: `obj.method`\n        // Transform y.method to a partial call `type(y).method(y, ...)`\n        std::vector<Expr *> methodArgs;\n        // Do not add self if a method is marked with @staticmethod\n        if (!bestMethod->ast->hasAttribute(Attr::StaticMethod)) {\n          methodArgs.emplace_back(expr->getExpr());\n        }\n        // If a method is marked with @property, just call it directly\n        if (!bestMethod->ast->hasAttribute(Attr::Property)) {\n          methodArgs.emplace_back(N<EllipsisExpr>(EllipsisExpr::PARTIAL));\n        }\n        resultExpr = transform(N<CallExpr>(e, methodArgs));\n      }\n    }\n    break;\n  }\n}\n\n/// Access identifiers outside of the current function/class scope.\n/// Either use them as-is (globals), capture them if allowed (nonlocals),\n/// or raise an error.\nvoid TypecheckVisitor::checkCapture(const TypeContext::Item &val) const {\n  if (!ctx->isOuter(val))\n    return;\n  if ((val->isType() && !val->isGeneric()) || val->isFunc())\n    return;\n\n  // Ensure that outer variables can be captured (i.e., do not cross no-capture\n  // boundary). Example:\n  // def foo():\n  //   x = 1\n  //   class T:      # <- boundary (classes cannot capture locals)\n  //     t: int = x  # x cannot be accessed\n  //     def bar():  # <- another boundary\n  //                 # (class methods cannot capture locals except class generics)\n  //       print(x)  # x cannot be accessed\n  bool crossCaptureBoundary = false;\n  bool localGeneric = val->isGeneric() && val->getBaseName() == ctx->getBaseName();\n  bool parentClassGeneric =\n      val->isGeneric() && !ctx->getBase()->isType() &&\n      (ctx->bases.size() > 1 && ctx->bases[ctx->bases.size() - 2].isType() &&\n       ctx->bases[ctx->bases.size() - 2].name == val->getBaseName());\n  auto i = ctx->bases.size();\n  while (i-- > 0) {\n    if (ctx->bases[i].name == val->getBaseName())\n      break;\n    if (!localGeneric && !parentClassGeneric)\n      crossCaptureBoundary = true;\n  }\n\n  // Mark methods (class functions that access class generics)\n  if (parentClassGeneric)\n    ctx->getBase()->func->setAttribute(Attr::Method);\n\n  // Ignore generics\n  if (parentClassGeneric || localGeneric)\n    return;\n\n  // Case: a global variable that has not been marked with `global` statement\n  if (val->isVar() && val->getBaseName().empty() && val->scope.size() == 1) {\n    registerGlobal(val->getName());\n    return;\n  }\n\n  // Not in module; probably some capture\n  if (val->getModule() != ctx->getModule())\n    return;\n\n  // Check if a real variable (not a static) is defined outside the current scope\n  if (crossCaptureBoundary)\n    E(Error::ID_CANNOT_CAPTURE, getSrcInfo(), getUserFacingName(val->getName()));\n\n  // Case: a nonlocal variable that has not been marked with `nonlocal` statement\n  //       and capturing is *not* enabled\n  E(Error::ID_NONLOCAL, getSrcInfo(), getUserFacingName(val->getName()));\n}\n\n/// Check if a chain (a.b.c.d...) contains an import or a class prefix.\nstd::pair<size_t, TypeContext::Item>\nTypecheckVisitor::getImport(const std::vector<std::string> &chain) {\n  size_t importEnd = 0;\n  std::string importName;\n\n  // 1. Find the longest prefix that corresponds to the existing import\n  //    (e.g., `a.b.c.d` -> `a.b.c` if there is `import a.b.c`)\n  TypeContext::Item val = nullptr, importVal = nullptr;\n  for (auto i = chain.size(); i-- > 0;) {\n    auto name = join(chain, \"/\", 0, i + 1);\n    val = ctx->find(name, getTime());\n    if (val && val->type->is(\"Import\") && startswith(val->getName(), \"%_import_\")) {\n      importName = getStrLiteral(val->type.get());\n      importEnd = i + 1;\n      importVal = val;\n      break;\n    }\n  }\n\n  // Case: the whole chain is import itself\n  if (importEnd == chain.size())\n    return {importEnd, val};\n\n  // Find the longest prefix that corresponds to the existing class\n  // (e.g., `a.b.c` -> `a.b` if there is `class a: class b:`)\n  std::string itemName;\n  size_t itemEnd = 0;\n  auto ictx = importName.empty() ? ctx : getImport(importName)->ctx;\n  for (auto i = chain.size(); i-- > importEnd;) {\n    if (ictx->getModule() == \"std.python\" && importEnd < chain.size()) {\n      // Special case: importing from Python.\n      // Fake TypecheckItem that indicates std.python access\n      val = std::make_shared<TypecheckItem>(\n          \"\", \"\", ictx->getModule(), TypecheckVisitor(ictx).instantiateUnbound());\n      return {importEnd, val};\n    } else {\n      auto key = join(chain, \".\", importEnd, i + 1);\n      // check only globals for imports!\n      val = ictx->find(key, 0, importName.empty() ? nullptr : \"\");\n      if (val && i + 1 != chain.size() && val->getType()->is(\"Import\") &&\n          startswith(val->getName(), \"%_import_\")) {\n        importName = getStrLiteral(val->getType());\n        importEnd = i + 1;\n        importVal = val;\n        ictx = getImport(importName)->ctx;\n        i = chain.size();\n        continue;\n      }\n      bool isOverload = val && val->isFunc() && hasOverloads(val->canonicalName);\n      if (isOverload && importEnd == i) { // top-level overload\n        itemName = val->canonicalName, itemEnd = i + 1;\n        break;\n      }\n      // Class member\n      if (val && !isOverload &&\n          (importName.empty() || val->isType() || !val->isConditional())) {\n        itemName = val->canonicalName, itemEnd = i + 1;\n        break;\n      }\n      // Resolve the identifier from the import\n      if (auto imp = ctx->find(\"Import\")) {\n        auto t = extractClassType(imp->getType());\n        if (findMember(t, key))\n          return {importEnd, importVal};\n        if (!findMethod(t, key).empty())\n          return {importEnd, importVal};\n      }\n    }\n  }\n  if (itemName.empty() && importName.empty()) {\n    if (ctx->getBase()->pyCaptures)\n      return {1, nullptr};\n    E(Error::IMPORT_NO_MODULE, getSrcInfo(), chain[importEnd]);\n  } else if (itemName.empty()) {\n    auto import = getImport(importName);\n    if (!ctx->isStdlibLoading && endswith(importName, \"__init__.codon\")) {\n      // Special case: subimport is not yet loaded\n      //   (e.g., import a; a.b.x where a.b is a module as well)\n      if (auto file = getImportFile(ctx->cache, chain[importEnd], importName)) {\n        // Auto-load support\n        Stmt *s = N<SuiteStmt>(N<ImportStmt>(N<IdExpr>(chain[importEnd]), nullptr));\n        if (auto err = ScopingVisitor::apply(ctx->cache, s))\n          throw exc::ParserException(std::move(err));\n        s = TypecheckVisitor(import->ctx, preamble).transform(s);\n        prependStmts->push_back(s);\n        return getImport(chain);\n      }\n    }\n    E(Error::IMPORT_NO_NAME, getSrcInfo(), chain[importEnd], import->name);\n  }\n  importEnd = itemEnd;\n  return {importEnd, val};\n}\n\n/// Find or generate an overload dispatch function for a given overload.\n///  Dispatch functions ensure that a function call is being routed to the correct\n///  overload\n/// even when dealing with partial functions and decorators.\n/// @example\n/// This is how dispatch looks like:\n///   ```def foo:dispatch(*args, **kwargs):\n///        return foo(*args, **kwargs)```\ntypes::FuncType *TypecheckVisitor::getDispatch(const std::string &fn) {\n  auto &overloads = ctx->cache->overloads[fn];\n\n  // Single overload: just return it\n  if (overloads.size() == 1)\n    return ctx->forceFind(overloads.front())->type->getFunc();\n\n  // Check if dispatch exists\n  for (auto &m : overloads)\n    if (isDispatch(getFunction(m)->ast))\n      return getFunction(m)->getType();\n\n  // Dispatch does not exist. Generate it\n  auto name = fmt::format(\"{}{}\", fn, FN_DISPATCH_SUFFIX);\n  Expr *root; // Root function name used for calling\n  auto ofn = getFunction(overloads[0]);\n  auto aa = ofn->ast->getAttribute<ir::StringValueAttribute>(Attr::ParentClass);\n  if (aa)\n    root = N<DotExpr>(N<IdExpr>(aa->value), getUnmangledName(fn));\n  else\n    root = N<IdExpr>(fn);\n  root = N<CallExpr>(root, N<StarExpr>(N<IdExpr>(\"args\")),\n                     N<KeywordStarExpr>(N<IdExpr>(\"kwargs\")));\n  auto nar = ctx->generateCanonicalName(\"args\");\n  auto nkw = ctx->generateCanonicalName(\"kwargs\");\n  auto ast = N<FunctionStmt>(name, nullptr,\n                             std::vector<Param>{Param(\"*\" + nar), Param(\"**\" + nkw)},\n                             N<SuiteStmt>(N<ReturnStmt>(root)));\n  ast->setAttribute(Attr::AutoGenerated);\n  ast->setAttribute(Attr::Module, ctx->moduleName.path);\n  if (aa)\n    ast->setAttribute(Attr::ParentClass, aa->value);\n  ctx->cache->reverseIdentifierLookup[name] = getUnmangledName(fn);\n\n  auto baseType = getFuncTypeBase(2);\n  auto typ = std::make_shared<FuncType>(baseType.get(), ast);\n  /// Make sure that parent is set so that the parent type can be passed to the inner\n  /// call\n  ///   (e.g., A[B].foo -> A.foo.dispatch() { A[B].foo() })\n  typ->funcParent = ofn->type->funcParent;\n  typ = std::static_pointer_cast<FuncType>(typ->generalize(ctx->typecheckLevel - 1));\n  ctx->addFunc(name, name, typ);\n\n  overloads.insert(overloads.begin(), name);\n  ctx->cache->functions[name] = Cache::Function{\"\", fn, ast, typ};\n  ast->setDone();\n  return typ.get(); // stored in Cache::Function, hence not destroyed\n}\n\n/// Find a class member.\n/// @example\n///   `obj.GENERIC`     -> `GENERIC` (IdExpr with generic/static value)\n///   `optional.member` -> `unwrap(optional).member`\n///   `pyobj.member`    -> `pyobj._getattr(\"member\")`\nExpr *TypecheckVisitor::getClassMember(DotExpr *expr) {\n  auto typ = extractClassType(expr->getExpr());\n  seqassert(typ, \"not a class\");\n\n  // Case: object member access (`obj.member`)\n  if (!isTypeExpr(expr->getExpr())) {\n    if (auto member = findMember(typ, expr->getMember())) {\n      unify(expr->getType(), instantiateType(member->getType(), typ));\n      if (!expr->getType()->canRealize() && member->typeExpr) {\n        unify(expr->getType(), extractType(withClassGenerics(typ, [&]() {\n                return transform(clean_clone(member->typeExpr));\n              })));\n      }\n      if (expr->getExpr()->isDone() && realize(expr->getType()))\n        expr->setDone();\n      return nullptr;\n    }\n  }\n\n  bool hasSide = hasSideEffect(expr->getExpr());\n  auto wrapSide = [&](Expr *e) -> Expr * {\n    if (hasSide)\n      return transform(N<StmtExpr>(N<ExprStmt>(expr->getExpr()), e));\n    return transform(e);\n  };\n\n  // Case: class variable (`Cls.var`)\n  if (auto cls = getClass(typ))\n    if (auto var = in(cls->classVars, expr->getMember())) {\n      return wrapSide(N<IdExpr>(*var));\n    }\n\n  // Case: special members\n  std::unordered_map<std::string, std::string> specialMembers{\n      {\"__elemsize__\", \"int\"}, {\"__atomic__\", \"bool\"}, {\"__contents_atomic__\", \"bool\"}};\n  if (auto mtyp = in(specialMembers, expr->getMember())) {\n    unify(expr->getType(), getStdLibType(*mtyp));\n    if (expr->getExpr()->isDone() && realize(expr->getType()))\n      expr->setDone();\n    return nullptr;\n  }\n  if (expr->getMember() == \"__name__\" && isTypeExpr(expr->getExpr())) {\n    unify(expr->getType(), getStdLibType(\"str\"));\n    if (expr->getExpr()->isDone() && realize(expr->getType()))\n      expr->setDone();\n    return nullptr;\n  }\n\n  // Case: object generic access (`obj.T`)\n  ClassType::Generic *generic = nullptr;\n  for (auto &g : typ->generics)\n    if (expr->getMember() == getUnmangledName(g.name)) {\n      generic = &g;\n      break;\n    }\n  if (generic) {\n    if (generic->staticKind) {\n      unify(expr->getType(), generic->getType());\n      if (realize(expr->getType())) {\n        return wrapSide(generic->type->getStatic()->getStaticExpr());\n      }\n    } else {\n      unify(expr->getType(), instantiateTypeVar(generic->getType()));\n      if (realize(expr->getType()))\n        return wrapSide(N<IdExpr>(generic->getType()->realizedName()));\n    }\n    return nullptr;\n  }\n\n  // Case: transform `optional.member` to `unwrap(optional).member`\n  if (typ->is(TYPE_OPTIONAL)) {\n    expr->expr = transform(N<CallExpr>(N<IdExpr>(FN_OPTIONAL_UNWRAP), expr->getExpr()));\n    return nullptr;\n  }\n\n  // Case: transform `pyobj.member` to `pyobj._getattr(\"member\")`\n  if (typ->is(\"pyobj\")) {\n    return transform(N<CallExpr>(N<DotExpr>(expr->getExpr(), \"_getattr\"),\n                                 N<StringExpr>(expr->getMember())));\n  }\n\n  // Case: transform `union.m` to `Union._member(union, \"m\", ...)`\n  if (typ->getUnion()) {\n    if (!typ->canRealize())\n      return nullptr; // delay!\n    return transform(N<CallExpr>(\n        N<DotExpr>(N<IdExpr>(\"Union\"), \"_member\"),\n        std::vector<CallArg>{CallArg{\"union\", expr->getExpr()},\n                             CallArg{\"member\", N<StringExpr>(expr->getMember())}}));\n  }\n\n  // Case: __getattr__ support. Ensure that only Literal[str] arguments are accepted.\n  auto u = instantiateUnbound();\n  u->staticKind = LiteralKind::String;\n  if (auto m = findBestMethod(typ, \"__getattr__\", {typ, u.get()})) {\n    if (m->funcGenerics.size() == 1 &&\n        extractFuncGeneric(m)->getStaticKind() == LiteralKind::String) {\n      return transform(N<CallExpr>(N<DotExpr>(expr->getExpr(), \"__getattr__\"),\n                                   N<StringExpr>(expr->getMember())));\n    }\n  }\n\n  // For debugging purposes:\n  findMethod(typ, expr->getMember());\n  E(Error::DOT_NO_ATTR, expr, typ->prettyString(), expr->getMember());\n  return nullptr;\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/assign.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <string>\n#include <tuple>\n\n#include \"codon/cir/attribute.h\"\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/match.h\"\n#include \"codon/parser/visitors/scoping/scoping.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nusing namespace codon::error;\nusing namespace codon::matcher;\n\nnamespace codon::ast {\n\nusing namespace types;\n\n/// Transform walrus (assignment) expression.\n/// @example\n///   `(expr := var)` -> `var = expr; var`\nvoid TypecheckVisitor::visit(AssignExpr *expr) {\n  auto a = N<AssignStmt>(clone(expr->getVar()), expr->getExpr());\n  a->cloneAttributesFrom(expr);\n  resultExpr = transform(N<StmtExpr>(a, expr->getVar()));\n}\n\n/// Transform assignments. Handle dominated assignments, forward declarations, static\n/// assignments and type/function aliases.\n/// See @c transformAssignment and @c unpackAssignments for more details.\n/// See @c wrapExpr for more examples.\nvoid TypecheckVisitor::visit(AssignStmt *stmt) {\n  if (cast<TupleExpr>(stmt->lhs) || cast<ListExpr>(stmt->lhs)) {\n    resultStmt = transform(unpackAssignment(stmt->lhs, stmt->rhs));\n    return;\n  }\n\n  bool mustUpdate = stmt->isUpdate() || stmt->isAtomicUpdate();\n  mustUpdate |= stmt->getLhs()->hasAttribute(Attr::ExprDominated);\n  mustUpdate |= stmt->getLhs()->hasAttribute(Attr::ExprDominatedUsed);\n  if (cast<BinaryExpr>(stmt->getRhs()) &&\n      cast<BinaryExpr>(stmt->getRhs())->isInPlace()) {\n    // Update case: a += b\n    seqassert(!stmt->getTypeExpr(), \"invalid AssignStmt {}\", stmt->toString(0));\n    mustUpdate = true;\n  }\n\n  resultStmt = transformAssignment(stmt, mustUpdate);\n  if (stmt->getLhs()->hasAttribute(Attr::ExprDominatedUsed)) {\n    // If this is dominated, set __used__ if needed\n    stmt->getLhs()->eraseAttribute(Attr::ExprDominatedUsed);\n    auto e = cast<IdExpr>(stmt->getLhs());\n    seqassert(e, \"dominated bad assignment\");\n    resultStmt = transform(N<SuiteStmt>(\n        resultStmt,\n        N<AssignStmt>(N<IdExpr>(fmt::format(\"{}{}\", getUnmangledName(e->getValue()),\n                                            VAR_USED_SUFFIX)),\n                      N<BoolExpr>(true), nullptr, AssignStmt::UpdateMode::Update)));\n  }\n}\n\n/// Transform deletions.\n/// @example\n///   `del a`    -> `a = type(a)()` and remove `a` from the context\n///   `del a[x]` -> `a.__delitem__(x)`\nvoid TypecheckVisitor::visit(DelStmt *stmt) {\n  if (auto idx = cast<IndexExpr>(stmt->getExpr())) {\n    resultStmt = N<ExprStmt>(transform(\n        N<CallExpr>(N<DotExpr>(idx->getExpr(), \"__delitem__\"), idx->getIndex())));\n  } else if (auto ei = cast<IdExpr>(stmt->getExpr())) {\n    // Assign `a` to `type(a)()` to mark it for deletion\n    resultStmt = transform(N<AssignStmt>(\n        stmt->getExpr(),\n        N<CallExpr>(N<CallExpr>(N<IdExpr>(TYPE_TYPE), clone(stmt->getExpr()))), nullptr,\n        AssignStmt::Update));\n\n    // Allow deletion *only* if the binding is dominated\n    auto val = ctx->find(ei->getValue());\n    if (!val)\n      E(Error::ID_NOT_FOUND, ei, ei->getValue());\n    if (ctx->getScope() != val->scope)\n      E(Error::DEL_NOT_ALLOWED, ei, ei->getValue());\n    ctx->remove(ei->getValue());\n    ctx->remove(getUnmangledName(ei->getValue()));\n  } else {\n    E(Error::DEL_INVALID, stmt);\n  }\n}\n\n/// Unpack an assignment expression `lhs = rhs` into a list of simple assignment\n/// expressions (e.g., `a = b`, `a.x = b`, or `a[x] = b`).\n/// Handle Python unpacking rules.\n/// @example\n///   `(a, b) = c`     -> `a = c[0]; b = c[1]`\n///   `a, b = c`       -> `a = c[0]; b = c[1]`\n///   `[a, *x, b] = c` -> `a = c[0]; x = c[1:-1]; b = c[-1]`.\n/// Non-trivial right-hand expressions are first stored in a temporary variable.\n/// @example\n///   `a, b = c, d + foo()` -> `assign = (c, d + foo); a = assign[0]; b = assign[1]`.\n/// Each assignment is unpacked recursively to allow cases like `a, (b, c) = d`.\nStmt *TypecheckVisitor::unpackAssignment(Expr *lhs, Expr *rhs) {\n  std::vector<Expr *> leftSide;\n  if (auto et = cast<TupleExpr>(lhs)) {\n    // Case: (a, b) = ...\n    for (auto *i : *et)\n      leftSide.push_back(i);\n  } else if (auto el = cast<ListExpr>(lhs)) {\n    // Case: [a, b] = ...\n    for (auto *i : *el)\n      leftSide.push_back(i);\n  } else {\n    return N<AssignStmt>(lhs, rhs);\n  }\n\n  // Prepare the right-side expression\n  auto oldSrcInfo = getSrcInfo();\n  setSrcInfo(rhs->getSrcInfo());\n  auto *block = N<SuiteStmt>();\n  if (!cast<IdExpr>(rhs)) {\n    // Store any non-trivial right-side expression into a variable\n    auto var = getTemporaryVar(\"assign\");\n    auto newRhs = N<IdExpr>(var);\n    block->addStmt(N<AssignStmt>(newRhs, ast::clone(rhs)));\n    rhs = newRhs;\n  }\n\n  // Process assignments until the fist StarExpr (if any)\n  size_t st = 0;\n  for (; st < leftSide.size(); st++) {\n    if (cast<StarExpr>(leftSide[st]))\n      break;\n    // Transformation: `leftSide_st = rhs[st]` where `st` is static integer\n    auto rightSide = N<IndexExpr>(ast::clone(rhs), N<IntExpr>(st));\n    // Recursively process the assignment because of cases like `(a, (b, c)) = d)`\n    auto ns = unpackAssignment(leftSide[st], rightSide);\n    block->addStmt(ns);\n  }\n  // Process StarExpr (if any) and the assignments that follow it\n  if (st < leftSide.size() && cast<StarExpr>(leftSide[st])) {\n    // StarExpr becomes SliceExpr (e.g., `b` in `(a, *b, c) = d` becomes\n    // `list(d[1:-2])`)\n    Expr *rightSide = N<CallExpr>(\n        N<IdExpr>(getMangledMethod(\"std.internal.types.array\", \"List\", \"as_list\")),\n        N<IndexExpr>(\n            ast::clone(rhs),\n            N<SliceExpr>(N<IntExpr>(st),\n                         // this slice is either [st:] or [st:-lhs_len + st + 1]\n                         leftSide.size() == st + 1\n                             ? nullptr\n                             : N<IntExpr>(-leftSide.size() + st + 1),\n                         nullptr)));\n    auto ns = unpackAssignment(cast<StarExpr>(leftSide[st])->getExpr(), rightSide);\n    block->addStmt(ns);\n    st += 1;\n    // Process remaining assignments. They will use negative indices (-1, -2 etc.)\n    // because we do not know how big is StarExpr\n    for (; st < leftSide.size(); st++) {\n      if (cast<StarExpr>(leftSide[st]))\n        E(Error::ASSIGN_MULTI_STAR, leftSide[st]->getSrcInfo());\n      rightSide = N<IndexExpr>(ast::clone(rhs),\n                               N<IntExpr>(-static_cast<int>(leftSide.size() - st)));\n      auto next = unpackAssignment(leftSide[st], rightSide);\n      block->addStmt(next);\n    }\n  }\n  setSrcInfo(oldSrcInfo);\n  return block;\n}\n\n/// Transform simple assignments.\n/// @example\n///   `a[x] = b`    -> `a.__setitem__(x, b)`\n///   `a.x = b`     -> @c AssignMemberStmt\n///   `a: type` = b -> @c AssignStmt\n///   `a = b`       -> @c AssignStmt or @c UpdateStmt (see below)\nStmt *TypecheckVisitor::transformAssignment(AssignStmt *stmt, bool mustExist) {\n  if (auto idx = cast<IndexExpr>(stmt->getLhs())) {\n    // Case: a[x] = b\n    seqassert(!stmt->type, \"unexpected type annotation\");\n    if (auto b = cast<BinaryExpr>(stmt->getRhs())) {\n      // Case: a[x] += b (inplace operator)\n      if (mustExist && b->isInPlace() && !cast<IdExpr>(b->getRhs())) {\n        auto var = getTemporaryVar(\"assign\");\n        return transform(N<SuiteStmt>(\n            N<AssignStmt>(N<IdExpr>(var), idx->getIndex()),\n            N<ExprStmt>(N<CallExpr>(\n                N<DotExpr>(idx->getExpr(), \"__setitem__\"), N<IdExpr>(var),\n                N<BinaryExpr>(N<IndexExpr>(clone(idx->getExpr()), N<IdExpr>(var)),\n                              b->getOp(), b->getRhs(), true)))));\n      }\n    }\n    return transform(N<ExprStmt>(N<CallExpr>(N<DotExpr>(idx->getExpr(), \"__setitem__\"),\n                                             idx->getIndex(), stmt->getRhs())));\n  }\n\n  if (auto dot = cast<DotExpr>(stmt->getLhs())) {\n    // Case: a.x = b\n    dot->expr = transform(dot->getExpr(), true);\n    return transform(N<AssignMemberStmt>(\n        dot->getExpr(), dot->member, transform(stmt->getRhs()), stmt->getTypeExpr()));\n  }\n\n  // Case: a (: t) = b\n  auto e = cast<IdExpr>(stmt->getLhs());\n  if (!e) {\n    E(Error::ASSIGN_INVALID, stmt->getLhs());\n    return nullptr;\n  }\n\n  if (ctx->inFunction() && stmt->getRhs() && !mustExist) {\n    if (auto b =\n            ctx->getBase()->func->getAttribute<BindingsAttribute>(Attr::Bindings)) {\n      const auto &bd = b->bindings[e->getValue()];\n      if (bd.isNonlocal) {\n        if (stmt->getTypeExpr())\n          stmt->type = N<IndexExpr>(N<IdExpr>(\"Capsule\"), stmt->getTypeExpr());\n        else\n          stmt->type = N<IdExpr>(\"Capsule\");\n      }\n    }\n  }\n\n  bool isThreadLocal = false;\n  auto typeExpr = transformType(stmt->getTypeExpr());\n  if (typeExpr &&\n      extractType(typeExpr)->is(getMangledClass(\"std.threading\", \"ThreadLocal\"))) {\n    isThreadLocal = true;\n    if (auto ti = cast<IndexExpr>(stmt->getTypeExpr())) {\n      typeExpr = transformType(ti->getIndex());\n    } else {\n      typeExpr = nullptr;\n    }\n  }\n\n  // Make sure that existing values that cannot be shadowed are only updated\n  // mustExist |= val && !ctx->isOuter(val);\n  if (mustExist) {\n    auto val = ctx->find(e->getValue(), getTime());\n    if (!val)\n      E(Error::ASSIGN_LOCAL_REFERENCE, e, e->getValue(), e->getSrcInfo());\n\n    auto s = N<AssignStmt>(stmt->getLhs(), stmt->getRhs(), typeExpr);\n    if (!ctx->getBase()->isType() && ctx->getBase()->func->hasAttribute(Attr::Atomic))\n      s->setAtomicUpdate();\n    else\n      s->setUpdate();\n    if (auto u = transformUpdate(s))\n      return u;\n    else\n      return s; // delay\n  }\n\n  stmt->rhs = transform(stmt->getRhs(), true);\n  stmt->type = typeExpr;\n\n  // Generate new canonical variable name for this assignment and add it to the context\n  auto canonical = ctx->generateCanonicalName(e->getValue());\n  auto assign =\n      N<AssignStmt>(N<IdExpr>(canonical), stmt->getRhs(), stmt->getTypeExpr());\n  assign->getLhs()->cloneAttributesFrom(stmt->getLhs());\n  assign->getLhs()->setType(stmt->getLhs()->getType()\n                                ? stmt->getLhs()->getType()->shared_from_this()\n                                : instantiateUnbound(assign->getLhs()->getSrcInfo()));\n  if (isThreadLocal)\n    assign->setThreadLocal();\n  if (!stmt->getRhs() && !stmt->getTypeExpr() && ctx->find(\"NoneType\")) {\n    // All declarations that are not handled are to be marked with NoneType later on\n    // (useful for dangling declarations that are not initialized afterwards due to\n    //  static check)\n    assign->getLhs()->getType()->getLink()->defaultType =\n        getStdLibType(\"NoneType\")->shared_from_this();\n    ctx->getBase()->pendingDefaults[1].insert(\n        assign->getLhs()->getType()->shared_from_this());\n  }\n  if (stmt->getTypeExpr()) {\n    auto t = extractType(stmt->getTypeExpr());\n    unify(assign->getLhs()->getType(),\n          instantiateType(stmt->getTypeExpr()->getSrcInfo(), t));\n  }\n  auto val = std::make_shared<TypecheckItem>(\n      canonical, ctx->getBaseName(), ctx->getModule(),\n      assign->getLhs()->getType()->shared_from_this(), ctx->getScope());\n  val->time = getTime();\n  val->setSrcInfo(getSrcInfo());\n  ctx->add(e->getValue(), val);\n  ctx->addAlwaysVisible(val);\n\n  if (assign->getRhs()) { // not a declaration!\n    // Check if we can wrap the expression (e.g., `a: float = 3` -> `a = float(3)`)\n    if (wrapExpr(&assign->rhs, assign->getLhs()->getType())) {\n      unify(assign->getLhs()->getType(), assign->getRhs()->getType());\n    }\n\n    // Generalize non-variable types. That way we can support cases like:\n    // `a = foo(x, ...); a(1); a('s')`\n    if (!val->isVar()) {\n      val->type = val->type->generalize(ctx->typecheckLevel - 1);\n      // See capture_function_partial_proper_realize test\n      assign->getLhs()->setType(val->type);\n      assign->getRhs()->setType(val->type);\n    }\n  }\n\n  // Mark declarations or generalized type/functions as done\n  if ((!assign->getRhs() || assign->getRhs()->isDone()) &&\n      assign->getLhs()->getType()->canRealize()) {\n    if (auto r = realize(assign->getLhs()->getType())) {\n      // overwrite types to remove dangling unbounds with some partials...\n      assign->getLhs()->setType(r->shared_from_this());\n      if (assign->getRhs())\n        assign->getRhs()->setType(r->shared_from_this());\n      assign->setDone();\n    }\n  } else if (assign->getRhs() && !val->isVar() && !val->type->hasUnbounds(false)) {\n    assign->setDone();\n  }\n\n  // Register all toplevel variables as global in JIT mode\n  //   OR if they are in imported module (not toplevel)\n  bool isGlobal = (ctx->cache->isJit && val->isGlobal() && !val->isGeneric()) ||\n                  (canonical == VAR_ARGV) ||\n                  (val->isGlobal() && val->getModule() != \"\");\n  if (isGlobal && val->isVar()) {\n    registerGlobal(canonical);\n    if (ctx->cache->isJit) {\n      getImport(STDLIB_IMPORT)->ctx->addToplevel(getUnmangledName(val->getName()), val);\n    }\n  }\n\n  return assign;\n}\n\n/// Transform binding updates. Special handling is done for atomic or in-place\n/// statements (e.g., `a += b`).\n/// See @c transformInplaceUpdate and @c wrapExpr for details.\nStmt *TypecheckVisitor::transformUpdate(AssignStmt *stmt) {\n  stmt->lhs = transform(stmt->getLhs());\n\n  // Check inplace updates\n  auto [inPlace, inPlaceStmt] = transformInplaceUpdate(stmt);\n  if (inPlace) {\n    return inPlaceStmt;\n  }\n\n  stmt->rhs = transform(stmt->getRhs());\n  stmt->type = transformType(stmt->getTypeExpr());\n  if (stmt->getTypeExpr()) {\n    unify(stmt->getLhs()->getType(), instantiateType(stmt->getTypeExpr()->getSrcInfo(),\n                                                     extractType(stmt->getTypeExpr())));\n  }\n\n  // Case: wrap expressions if needed (e.g. floats or optionals)\n  if (wrapExpr(&stmt->rhs, stmt->getLhs()->getType()))\n    unify(stmt->getRhs()->getType(), stmt->getLhs()->getType());\n  if (stmt->getRhs()->isDone() && realize(stmt->getLhs()->getType()))\n    stmt->setDone();\n\n  return nullptr;\n}\n\n/// Typecheck instance member assignments (e.g., `a.b = c`) and handle optional\n/// instances. Disallow tuple updates.\n/// @example\n///   `opt.foo = bar` -> `unwrap(opt).foo = wrap(bar)`\n/// See @c wrapExpr for more examples.\nvoid TypecheckVisitor::visit(AssignMemberStmt *stmt) {\n  stmt->lhs = transform(stmt->getLhs());\n\n  if (auto lhsClass = extractClassType(stmt->getLhs())) {\n    auto member = findMember(lhsClass, stmt->getMember());\n    if (!member) {\n      // Case: property setters\n      auto setters = findMethod(\n          lhsClass, fmt::format(\"{}{}\", FN_SETTER_SUFFIX, stmt->getMember()));\n      if (!setters.empty()) {\n        resultStmt =\n            transform(N<ExprStmt>(N<CallExpr>(N<IdExpr>(setters.front()->getFuncName()),\n                                              stmt->getLhs(), stmt->getRhs())));\n        return;\n      }\n      // Case: class variables\n      if (auto cls = getClass(lhsClass))\n        if (auto var = in(cls->classVars, stmt->getMember())) {\n          auto a = N<AssignStmt>(N<IdExpr>(*var), transform(stmt->getRhs()));\n          a->setUpdate();\n          resultStmt = transform(a);\n          return;\n        }\n    }\n    if (!member && lhsClass->is(TYPE_OPTIONAL)) {\n      // Unwrap optional and look up there\n      resultStmt = transform(N<AssignMemberStmt>(\n          N<CallExpr>(N<IdExpr>(FN_OPTIONAL_UNWRAP), stmt->getLhs()), stmt->getMember(),\n          stmt->getRhs()));\n      return;\n    }\n    // Case: __setattr__ support. Ensure that only Literal[str] arguments are accepted.\n    if (!member) {\n      auto u = instantiateUnbound(), v = instantiateUnbound();\n      u->staticKind = LiteralKind::String;\n      if (auto m =\n              findBestMethod(lhsClass, \"__setattr__\", {lhsClass, u.get(), v.get()})) {\n        if (m->funcGenerics.size() >= 1 &&\n            extractFuncGeneric(m)->getStaticKind() == LiteralKind::String) {\n          resultStmt = transform(N<ExprStmt>(\n              N<CallExpr>(N<DotExpr>(stmt->getLhs(), \"__setattr__\"),\n                          N<StringExpr>(stmt->getMember()), stmt->getRhs())));\n          return;\n        }\n      }\n    }\n\n    if (!member) {\n      E(Error::DOT_NO_ATTR, stmt->getLhs(), lhsClass->prettyString(),\n        stmt->getMember());\n      return;\n    }\n    if (lhsClass->isRecord()) // prevent tuple member assignment\n      E(Error::ASSIGN_UNEXPECTED_FROZEN, stmt->getLhs());\n\n    stmt->rhs = transform(stmt->getRhs());\n    stmt->type = transformType(stmt->getTypeExpr());\n    if (stmt->getTypeExpr()) {\n      unify(stmt->getRhs()->getType(),\n            instantiateType(stmt->getTypeExpr()->getSrcInfo(),\n                            extractType(stmt->getTypeExpr())));\n    }\n\n    auto ftyp =\n        instantiateType(stmt->getLhs()->getSrcInfo(), member->getType(), lhsClass);\n    if (!ftyp->canRealize() && member->typeExpr) {\n      unify(ftyp.get(), extractType(withClassGenerics(lhsClass, [&]() {\n              return transform(clean_clone(member->typeExpr));\n            })));\n    }\n    if (!wrapExpr(&stmt->rhs, ftyp.get()))\n      return;\n    unify(stmt->getRhs()->getType(), ftyp.get());\n\n    if (stmt->getRhs()->isDone())\n      stmt->setDone();\n  }\n}\n\n/// Transform in-place and atomic updates.\n/// @example\n///   `a += b` -> `a.__iadd__(a, b)` if `__iadd__` exists\n///   Capsule operations:\n///   `a = b` -> a.val[0] = b\n///   `a += b` -> a.val[0] += b\n///   Atomic operations (when the needed magics are available):\n///   `a = b`         -> `type(a).__atomic_xchg__(__ptr__(a), b)`\n///   `a += b`        -> `type(a).__atomic_add__(__ptr__(a), b)`\n///   `a = min(a, b)` -> `type(a).__atomic_min__(__ptr__(a), b)` (same for `max`)\n/// @return a tuple indicating whether (1) the update statement can be replaced with an\n///         expression, and (2) the replacement expression.\nstd::pair<bool, Stmt *> TypecheckVisitor::transformInplaceUpdate(AssignStmt *stmt) {\n  // Case: capsule operations\n  if (stmt->getLhs()->getType()->is(\"Capsule\")) {\n    return {true,\n            transform(N<AssignStmt>(\n                N<IndexExpr>(N<CallExpr>(N<IdExpr>(getMangledMethod(\"std.internal.core\",\n                                                                    \"Capsule\", \"_ptr\")),\n                                         stmt->getLhs()),\n                             N<IntExpr>(0)),\n                stmt->getRhs()))};\n  }\n\n  // Case: in-place updates (e.g., `a += b`).\n  // They are stored as `Update(a, Binary(a + b, inPlace=true))`\n  auto bin = cast<BinaryExpr>(stmt->getRhs());\n  if (bin && bin->isInPlace()) {\n    bin->lexpr = transform(bin->getLhs());\n    bin->rexpr = transform(bin->getRhs());\n\n    if (!stmt->getRhs()->getType())\n      stmt->getRhs()->setType(instantiateUnbound());\n    if (bin->getLhs()->getClassType() && bin->getRhs()->getClassType()) {\n      if (auto transformed = transformBinaryInplaceMagic(bin, stmt->isAtomicUpdate())) {\n        unify(stmt->getRhs()->getType(), transformed->getType());\n        return {true, transform(N<ExprStmt>(transformed))};\n      } else if (!stmt->isAtomicUpdate()) {\n        // If atomic, call normal magic and then use __atomic_xchg__ below\n        return {false, nullptr};\n      }\n    } else { // Delay\n      unify(stmt->getLhs()->getType(),\n            unify(stmt->getRhs()->getType(), instantiateUnbound()));\n      return {true, nullptr};\n    }\n  }\n\n  // Case: atomic min/max operations.\n  // Note: check only `a = min(a, b)`; does NOT check `a = min(b, a)`\n  auto lhsClass = extractClassType(stmt->getLhs());\n  auto call = cast<CallExpr>(stmt->getRhs());\n  auto lei = cast<IdExpr>(stmt->getLhs());\n  auto cei = call ? cast<IdExpr>(call->getExpr()) : nullptr;\n  if (stmt->isAtomicUpdate() && call && lei && cei &&\n      (cei->getValue() == \"min\" || cei->getValue() == \"max\") && call->size() == 2) {\n    call->front().value = transform(call->front());\n    if (cast<IdExpr>(call->front()) &&\n        cast<IdExpr>(call->front())->getValue() == lei->getValue()) {\n      // `type(a).__atomic_min__(__ptr__(a), b)`\n      auto ptrTyp = instantiateType(stmt->getLhs()->getSrcInfo(), getStdLibType(\"Ptr\"),\n                                    std::vector<types::Type *>{lhsClass});\n      (*call)[1].value = transform((*call)[1]);\n      auto rhsTyp = extractClassType((*call)[1].value);\n      if (auto method =\n              findBestMethod(lhsClass, fmt::format(\"__atomic_{}__\", cei->getValue()),\n                             {ptrTyp.get(), rhsTyp})) {\n        return {true,\n                transform(N<ExprStmt>(N<CallExpr>(\n                    N<IdExpr>(method->getFuncName()),\n                    N<CallExpr>(N<IdExpr>(\"__ptr__\"), stmt->getLhs()), (*call)[1])))};\n      }\n    }\n  }\n\n  // Case: atomic assignments\n  if (stmt->isAtomicUpdate() && lhsClass) {\n    // `type(a).__atomic_xchg__(__ptr__(a), b)`\n    stmt->rhs = transform(stmt->getRhs());\n    if (auto rhsClass = stmt->getRhs()->getClassType()) {\n      auto ptrType = instantiateType(stmt->getLhs()->getSrcInfo(), getStdLibType(\"Ptr\"),\n                                     std::vector<types::Type *>{lhsClass});\n      if (auto m =\n              findBestMethod(lhsClass, \"__atomic_xchg__\", {ptrType.get(), rhsClass})) {\n        return {true, transform(N<ExprStmt>(\n                          N<CallExpr>(N<IdExpr>(m->getFuncName()),\n                                      N<CallExpr>(N<IdExpr>(\"__ptr__\"), stmt->getLhs()),\n                                      stmt->getRhs())))};\n      }\n    }\n  }\n\n  return {false, nullptr};\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/basic.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/peg/peg.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nusing namespace codon::error;\n\nnamespace codon::ast {\n\nusing namespace types;\n\n/// Set type to `Optional[?]`\nvoid TypecheckVisitor::visit(NoneExpr *expr) {\n  unify(expr->getType(), instantiateType(getStdLibType(TYPE_OPTIONAL)));\n  if (realize(expr->getType())) {\n    // Realize the appropriate `Optional.__new__` for the translation stage\n    auto f =\n        ctx->forceFind(getMangledMethod(\"std.internal.core\", TYPE_OPTIONAL, \"__new__\"))\n            ->getType();\n    auto t = realize(instantiateType(f, extractClassType(expr)));\n    expr->setDone();\n  }\n}\n\n/// Set type to `bool`\nvoid TypecheckVisitor::visit(BoolExpr *expr) {\n  unify(expr->getType(), instantiateStatic(expr->getValue()));\n  expr->setDone();\n}\n\n/// Set type to `int`\nvoid TypecheckVisitor::visit(IntExpr *expr) { resultExpr = transformInt(expr); }\n\n/// Set type to `float`\nvoid TypecheckVisitor::visit(FloatExpr *expr) { resultExpr = transformFloat(expr); }\n\n/// Set type to `str`. Concatinate strings in list and apply appropriate transformations\n///   (e.g., `str` wrap).\nvoid TypecheckVisitor::visit(StringExpr *expr) {\n  if (expr->isSimple()) {\n    unify(expr->getType(), instantiateStatic(expr->getValue()));\n    expr->setDone();\n  } else {\n    std::vector<Expr *> items;\n    for (auto &p : *expr) {\n      if (p.expr) {\n        if (!p.format.conversion.empty()) {\n          switch (p.format.conversion[0]) {\n          case 'r':\n            p.expr = N<CallExpr>(N<IdExpr>(\"repr\"), p.expr);\n            break;\n          case 's':\n            p.expr = N<CallExpr>(N<IdExpr>(\"str\"), p.expr);\n            break;\n          case 'a':\n            p.expr = N<CallExpr>(N<IdExpr>(\"ascii\"), p.expr);\n            break;\n          default:\n            // TODO: error?\n            break;\n          }\n        }\n        if (!p.format.spec.empty()) {\n          p.expr = N<CallExpr>(N<DotExpr>(p.expr, \"__format__\"),\n                               N<StringExpr>(p.format.spec));\n        }\n        p.expr = N<CallExpr>(N<IdExpr>(\"str\"), p.expr);\n        if (!p.format.text.empty()) {\n          p.expr = N<CallExpr>(N<DotExpr>(N<IdExpr>(\"str\"), \"cat\"),\n                               N<StringExpr>(p.format.text), p.expr);\n        }\n        items.emplace_back(p.expr);\n      } else if (!p.prefix.empty()) {\n        /// Custom prefix strings:\n        /// call `str.__prefsix_[prefix]__(str, [static length of str])`\n        items.emplace_back(N<CallExpr>(\n            N<DotExpr>(N<IdExpr>(\"str\"), fmt::format(\"__prefix_{}__\", p.prefix)),\n            N<StringExpr>(p.value), N<IntExpr>(p.value.size())));\n      } else {\n        items.emplace_back(N<StringExpr>(p.value));\n      }\n    }\n    if (items.size() == 1)\n      resultExpr = transform(items.front());\n    else\n      resultExpr = transform(N<CallExpr>(N<DotExpr>(N<IdExpr>(\"str\"), \"cat\"), items));\n  }\n}\n\n/// Parse various integer representations depending on the integer suffix.\n/// @example\n///   `123u`   -> `UInt[64](123)`\n///   `123i56` -> `Int[56](123)`\n///   `123pf`  -> `int.__suffix_pf__(123)`\nExpr *TypecheckVisitor::transformInt(IntExpr *expr) {\n  auto [value, suffix] = expr->getRawData();\n  Expr *holder = nullptr;\n  if (!expr->hasStoredValue()) {\n    holder = N<StringExpr>(value);\n    if (suffix.empty())\n      suffix = \"i64\";\n  } else {\n    holder = N<IntExpr>(expr->getValue());\n  }\n\n  /// Handle fixed-width integers: suffixValue is a pointer to NN if the suffix\n  /// is `uNNN` or `iNNN`.\n  std::unique_ptr<int16_t> suffixValue = nullptr;\n  if (suffix.size() > 1 && (suffix[0] == 'u' || suffix[0] == 'i') &&\n      isdigit(suffix.substr(1))) {\n    try {\n      suffixValue = std::make_unique<int16_t>(std::stoi(suffix.substr(1)));\n    } catch (...) {\n    }\n    if (suffixValue && *suffixValue > MAX_INT_WIDTH)\n      suffixValue = nullptr;\n  }\n\n  if (suffix.empty()) {\n    // A normal integer (int64_t)\n    unify(expr->getType(), instantiateStatic(expr->getValue()));\n    expr->setDone();\n    return nullptr;\n  } else if (suffix == \"u\") {\n    // Unsigned integer: call `UInt[64](value)`\n    return transform(\n        N<CallExpr>(N<IndexExpr>(N<IdExpr>(\"UInt\"), N<IntExpr>(64)), holder));\n  } else if (suffixValue) {\n    // Fixed-width numbers (with `uNNN` and `iNNN` suffixes):\n    // call `UInt[NNN](value)` or `Int[NNN](value)`\n    return transform(\n        N<CallExpr>(N<IndexExpr>(N<IdExpr>(suffix[0] == 'u' ? \"UInt\" : \"Int\"),\n                                 N<IntExpr>(*suffixValue)),\n                    holder));\n  } else {\n    // Custom suffix: call `int.__suffix_[suffix]__(value)`\n    return transform(N<CallExpr>(\n        N<DotExpr>(N<IdExpr>(\"int\"), fmt::format(\"__suffix_{}__\", suffix)), holder));\n  }\n}\n\n/// Parse various float representations depending on the suffix.\n/// @example\n///   `123.4pf` -> `float.__suffix_pf__(123.4)`\nExpr *TypecheckVisitor::transformFloat(FloatExpr *expr) {\n  auto [value, suffix] = expr->getRawData();\n\n  Expr *holder = nullptr;\n  if (!expr->hasStoredValue()) {\n    holder = N<StringExpr>(value);\n  } else {\n    holder = N<FloatExpr>(expr->getValue());\n  }\n\n  if (suffix.empty() && expr->hasStoredValue()) {\n    // A normal float (double)\n    unify(expr->getType(), getStdLibType(\"float\"));\n    expr->setDone();\n    return nullptr;\n  } else if (suffix.empty()) {\n    return transform(N<CallExpr>(N<DotExpr>(N<IdExpr>(\"float\"), \"__new__\"), holder));\n  } else {\n    // Custom suffix: call `float.__suffix_[suffix]__(value)`\n    return transform(N<CallExpr>(\n        N<DotExpr>(N<IdExpr>(\"float\"), fmt::format(\"__suffix_{}__\", suffix)), holder));\n  }\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/call.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <string>\n#include <tuple>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/match.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nusing namespace codon::error;\nnamespace codon::ast {\n\nusing namespace types;\nusing namespace matcher;\n\n/// Transform print statement.\n/// @example\n///   `print a, b` -> `print(a, b)`\n///   `print a, b,` -> `print(a, b, end=' ')`\nvoid TypecheckVisitor::visit(PrintStmt *stmt) {\n  std::vector<CallArg> args;\n  args.reserve(stmt->size());\n  for (auto &i : *stmt)\n    args.emplace_back(i);\n  if (!stmt->hasNewline())\n    args.emplace_back(\"end\", N<StringExpr>(\" \"));\n  resultStmt = transform(N<ExprStmt>(N<CallExpr>(N<IdExpr>(\"print\"), args)));\n}\n\n/// Just ensure that this expression is not independent of CallExpr where it is handled.\nvoid TypecheckVisitor::visit(StarExpr *expr) {\n  E(Error::UNEXPECTED_TYPE, expr, \"star\");\n}\n\n/// Just ensure that this expression is not independent of CallExpr where it is handled.\nvoid TypecheckVisitor::visit(KeywordStarExpr *expr) {\n  E(Error::UNEXPECTED_TYPE, expr, \"kwstar\");\n}\n\n/// Typechecks an ellipsis. Ellipses are typically replaced during the typechecking; the\n/// only remaining ellipses are those that belong to PipeExprs.\nvoid TypecheckVisitor::visit(EllipsisExpr *expr) {\n  if (expr->isPipe() && realize(expr->getType())) {\n    expr->setDone();\n  } else if (expr->isStandalone()) {\n    resultExpr = transform(N<CallExpr>(N<IdExpr>(\"ellipsis\")));\n    unify(expr->getType(), resultExpr->getType());\n  }\n}\n\n/// Typecheck a call expression. This is the most complex expression to typecheck.\n/// @example\n///   `fn(1, 2, x=3, y=4)` -> `func(a=1, x=3, args=(2,), kwargs=KwArgs(y=4), T=int)`\n///   `fn(arg1, ...)`      -> `(_v = Partial.N10(arg1); _v)`\n/// See @c transformCallArgs , @c getCalleeFn , @c callReorderArguments ,\n///     @c typecheckCallArgs , @c transformSpecialCall and @c wrapExpr for more details.\nvoid TypecheckVisitor::visit(CallExpr *expr) {\n  if (ctx->simpleTypes)\n    E(Error::CALL_NO_TYPE, expr);\n  if (match(expr->getExpr(), M<IdExpr>(\"tuple\")) && expr->size() == 1) {\n    expr->setAttribute(Attr::TupleCall);\n  }\n\n  validateCall(expr);\n\n  // Check if this call is partial call\n  PartialCallData part;\n  if (!expr->empty()) {\n    if (auto el = cast<EllipsisExpr>(expr->back().getExpr()); el && el->isPartial()) {\n      part.isPartial = true;\n    }\n  }\n\n  // Do not allow realization here (function will be realized later);\n  // used to prevent early realization of compile_error\n  expr->setAttribute(Attr::ParentCallExpr);\n  if (part.isPartial)\n    expr->getExpr()->setAttribute(Attr::ExprDoNotRealize);\n  expr->expr = transform(expr->getExpr());\n  expr->eraseAttribute(Attr::ParentCallExpr);\n  if (isUnbound(expr->getExpr()))\n    return; // delay\n\n  auto [calleeFn, newExpr] = getCalleeFn(expr, part);\n  // Transform `tuple(i for i in tup)` into a GeneratorExpr\n  // that will be handled during the type checking.\n  if (!calleeFn && expr->hasAttribute(Attr::TupleCall)) {\n    if (cast<GeneratorExpr>(expr->begin()->getExpr())) {\n      auto g = cast<GeneratorExpr>(expr->begin()->getExpr());\n      if (!g || g->kind != GeneratorExpr::Generator || g->loopCount() != 1)\n        E(Error::CALL_TUPLE_COMPREHENSION, expr->begin()->getExpr());\n      g->kind = GeneratorExpr::TupleGenerator;\n      resultExpr = transform(g);\n      return;\n    } else {\n      resultExpr = transformTupleFn(expr);\n      return;\n    }\n  } else if ((resultExpr = newExpr)) {\n    return;\n  } else if (!calleeFn) {\n    return;\n  }\n\n  if (!withClassGenerics(\n          calleeFn.get(), [&]() { return transformCallArgs(expr); }, true, true))\n    return;\n  // Early dispatch modifier\n  if (isDispatch(calleeFn.get())) {\n    if (startswith(calleeFn->getFuncName(), \"Tuple.__new__\")) {\n      generateTuple(expr->size());\n    }\n    std::unique_ptr<std::vector<FuncType *>> m = nullptr;\n    auto id = cast<IdExpr>(getHeadExpr(expr->getExpr()));\n    if (id && part.var.empty()) {\n      // Case: function overloads (IdExpr)\n      // Make sure to ignore partial constructs (they are also StmtExpr(IdExpr, ...))\n      std::vector<types::FuncType *> methods;\n      auto key = id->getValue();\n      if (isDispatch(key))\n        key = key.substr(0, key.size() - std::string(FN_DISPATCH_SUFFIX).size());\n      for (auto &ovs : getOverloads(key)) {\n        if (!isDispatch(ovs))\n          methods.push_back(getFunction(ovs)->getType());\n      }\n      std::ranges::reverse(methods);\n      m = std::make_unique<std::vector<FuncType *>>(findMatchingMethods(\n          calleeFn->funcParent ? calleeFn->funcParent->getClass() : nullptr, methods,\n          expr->items, expr->getExpr()->getType()->getPartial()));\n    }\n    // partials have dangling ellipsis that messes up with the unbound check below\n    bool doDispatch = !m || m->empty() || part.isPartial;\n    if (!doDispatch && m && m->size() > 1) {\n      for (auto &a : *expr) {\n        if (isUnbound(a.getExpr()))\n          return; // typecheck this later once we know the argument\n      }\n    }\n    if (!doDispatch) {\n      calleeFn = instantiateType(m->front(), calleeFn->funcParent\n                                                 ? calleeFn->funcParent->getClass()\n                                                 : nullptr);\n      auto e = N<IdExpr>(calleeFn->getFuncName());\n      e->setType(calleeFn);\n      if (cast<IdExpr>(expr->getExpr())) {\n        expr->expr = e;\n      } else if (cast<StmtExpr>(expr->getExpr())) {\n        // Side effect...\n        for (auto *se = cast<StmtExpr>(expr->getExpr());;) {\n          if (auto ne = cast<StmtExpr>(se->getExpr())) {\n            se = ne;\n          } else {\n            se->expr = e;\n            break;\n          }\n        }\n      } else {\n        expr->expr = N<StmtExpr>(N<ExprStmt>(expr->getExpr()), e);\n      }\n      expr->getExpr()->setType(calleeFn);\n    } else if (m && m->empty()) {\n      std::vector<std::string> a;\n      for (auto &t : *expr)\n        a.emplace_back(fmt::format(\"{}\", t.getExpr()->getType()->getStatic()\n                                             ? t.getExpr()->getClassType()->name\n                                             : t.getExpr()->getType()->prettyString()));\n      auto argsNice = fmt::format(\"({})\", join(a, \", \"));\n      auto name = getUnmangledName(calleeFn->getFuncName());\n      if (auto a =\n              calleeFn->ast->getAttribute<ir::StringValueAttribute>(Attr::ParentClass))\n        name = fmt::format(\"{}.{}\", getUserFacingName(a->value), name);\n      E(Error::FN_NO_ATTR_ARGS, expr, name, argsNice);\n    }\n  }\n\n  // Handle named and default arguments\n  if ((resultExpr = callReorderArguments(calleeFn.get(), expr, part)))\n    return;\n\n  // Handle special calls\n  if (!part.isPartial) {\n    auto [isSpecial, specialExpr] = transformSpecialCall(expr);\n    if (isSpecial) {\n      resultExpr = specialExpr;\n      return;\n    }\n  }\n\n  // Typecheck arguments with the function signature\n  bool done = typecheckCallArgs(calleeFn.get(), expr->items, part);\n  if (!part.isPartial && calleeFn->canRealize()) {\n    // Previous unifications can qualify existing identifiers.\n    // Transform again to get the full identifier\n    expr->expr = transform(expr->expr);\n  }\n  done &= expr->expr->isDone();\n\n  // Emit the final call\n  if (part.isPartial) {\n    // Case: partial call. `calleeFn(args...)` -> `Partial(args..., fn, mask)`\n    std::vector<Expr *> newArgs;\n    for (auto &r : *expr)\n      if (!cast<EllipsisExpr>(r.getExpr())) {\n        newArgs.push_back(r.getExpr());\n        newArgs.back()->setAttribute(Attr::ExprSequenceItem);\n      }\n    newArgs.push_back(part.args);\n    auto partialCall = generatePartialCall(part.known, calleeFn->getFunc(),\n                                           N<TupleExpr>(newArgs), part.kwArgs);\n    std::string var = getTemporaryVar(\"part\");\n    Expr *call = nullptr;\n    if (!part.var.empty()) {\n      // Callee is already a partial call\n      auto stmts = cast<StmtExpr>(expr->expr)->items;\n      stmts.push_back(N<AssignStmt>(N<IdExpr>(var), partialCall));\n      call = N<StmtExpr>(stmts, N<IdExpr>(var));\n    } else {\n      // New partial call: `(part = Partial(stored_args...); part)`\n      call = N<StmtExpr>(N<AssignStmt>(N<IdExpr>(var), partialCall), N<IdExpr>(var));\n    }\n    call->setAttribute(Attr::ExprPartial);\n    resultExpr = transform(call);\n  } else {\n    // Case: normal function call\n    unify(expr->getType(), calleeFn->getRetType());\n    if (done)\n      expr->setDone();\n  }\n}\n\nvoid TypecheckVisitor::validateCall(CallExpr *expr) {\n  if (expr->hasAttribute(Attr::Validated))\n    return;\n  bool namesStarted = false, foundEllipsis = false;\n  for (auto &a : *expr) {\n    if (a.name.empty() && namesStarted &&\n        !(cast<KeywordStarExpr>(a.value) || cast<EllipsisExpr>(a.value)))\n      E(Error::CALL_NAME_ORDER, a.value);\n    if (!a.name.empty() && (cast<StarExpr>(a.value) || cast<KeywordStarExpr>(a.value)))\n      E(Error::CALL_NAME_STAR, a.value);\n    if (cast<EllipsisExpr>(a.value) && foundEllipsis)\n      E(Error::CALL_ELLIPSIS, a.value);\n    foundEllipsis |= static_cast<bool>(cast<EllipsisExpr>(a.value));\n    namesStarted |= !a.name.empty();\n  }\n  expr->setAttribute(Attr::Validated);\n}\n\n/// Transform call arguments. Expand *args and **kwargs to the list of @c CallArg\n/// objects.\n/// @return false if expansion could not be completed; true otherwise\nbool TypecheckVisitor::transformCallArgs(CallExpr *expr) {\n  for (auto ai = 0; ai < expr->size();) {\n    if (auto star = cast<StarExpr>((*expr)[ai].getExpr())) {\n      // Case: *args expansion\n      star->expr = transform(star->getExpr());\n      auto typ = star->getExpr()->getClassType();\n      while (typ && typ->is(TYPE_OPTIONAL)) {\n        star->expr =\n            transform(N<CallExpr>(N<IdExpr>(FN_OPTIONAL_UNWRAP), star->getExpr()));\n        typ = star->getExpr()->getClassType();\n      }\n      if (!typ) // Process later\n        return false;\n      if (!typ->isRecord())\n        E(Error::CALL_BAD_UNPACK, (*expr)[ai], typ->prettyString());\n      auto fields = getClassFields(typ);\n\n      Expr *head = star->getExpr(), *lead = nullptr;\n      if (hasSideEffect(head)) {\n        auto var = getTemporaryVar(\"star\");\n        lead = N<AssignExpr>(N<IdExpr>(var), head);\n        head = N<IdExpr>(var);\n      }\n      for (size_t i = 0; i < fields.size(); i++, ai++) {\n        expr->items.insert(\n            expr->items.begin() + ai,\n            CallArg{\"\", transform(N<DotExpr>(clone(lead && i == 0 ? lead : head),\n                                             fields[i].name))});\n      }\n      expr->items.erase(expr->items.begin() + ai);\n    } else if (const auto kwstar = cast<KeywordStarExpr>((*expr)[ai].getExpr())) {\n      // Case: **kwargs expansion\n      kwstar->expr = transform(kwstar->getExpr());\n      auto typ = kwstar->getExpr()->getClassType();\n      while (typ && typ->is(TYPE_OPTIONAL)) {\n        kwstar->expr =\n            transform(N<CallExpr>(N<IdExpr>(FN_OPTIONAL_UNWRAP), kwstar->getExpr()));\n        typ = kwstar->getExpr()->getClassType();\n      }\n      if (!typ)\n        return false;\n      Expr *head = kwstar->getExpr(), *lead = nullptr;\n      if (hasSideEffect(head)) {\n        auto var = getTemporaryVar(\"star\");\n        lead = N<AssignExpr>(N<IdExpr>(var), head);\n        head = N<IdExpr>(var);\n      }\n      if (typ->is(\"NamedTuple\")) {\n        auto id = getIntLiteral(typ);\n        seqassert(id >= 0 && id < ctx->cache->generatedTupleNames.size(), \"bad id: {}\",\n                  id);\n        auto names = ctx->cache->generatedTupleNames[id];\n        for (size_t i = 0; i < names.size(); i++, ai++) {\n          expr->items.insert(\n              expr->items.begin() + ai,\n              CallArg{names[i],\n                      transform(N<DotExpr>(\n                          N<DotExpr>(clone(lead && i == 0 ? lead : head), \"args\"),\n                          fmt::format(\"item{}\", i + 1)))});\n        }\n        expr->items.erase(expr->items.begin() + ai);\n      } else if (typ->isRecord()) {\n        auto fields = getClassFields(typ);\n        for (size_t i = 0; i < fields.size(); i++, ai++) {\n          expr->items.insert(\n              expr->items.begin() + ai,\n              CallArg{fields[i].name,\n                      transform(N<DotExpr>(clone(lead && i == 0 ? lead : head),\n                                           fields[i].name))});\n        }\n        expr->items.erase(expr->items.begin() + ai);\n      } else {\n        E(Error::CALL_BAD_KWUNPACK, (*expr)[ai], typ->prettyString());\n      }\n    } else {\n      // Case: normal argument (no expansion)\n      (*expr)[ai].value = transform((*expr)[ai].getExpr());\n      ai++;\n    }\n  }\n\n  // Check if some argument names are reused after the expansion\n  std::set<std::string> seen;\n  for (auto &a : *expr)\n    if (!a.name.empty()) {\n      if (in(seen, a.name))\n        E(Error::CALL_REPEATED_NAME, a, a.name);\n      seen.insert(a.name);\n    }\n\n  return true;\n}\n\n/// Extract the @c FuncType that represents the function to be called by the callee.\n/// Also handle special callees: constructors and partial functions.\n/// @return a pair with the callee's @c FuncType and the replacement expression\n///         (when needed; otherwise nullptr).\nstd::pair<std::shared_ptr<FuncType>, Expr *>\nTypecheckVisitor::getCalleeFn(CallExpr *expr, PartialCallData &part) {\n  auto callee = expr->getExpr()->getClassType();\n  if (!callee) {\n    // Case: unknown callee, wait until it becomes known\n    return {nullptr, nullptr};\n  }\n\n  if (expr->hasAttribute(Attr::TupleCall) &&\n      (extractType(expr->getExpr())->is(TYPE_TUPLE) ||\n       (callee->getFunc() &&\n        startswith(callee->getFunc()->ast->name, \"std.internal.static.tuple.\"))))\n    return {nullptr, nullptr};\n\n  if (isTypeExpr(expr->getExpr())) {\n    auto typ = expr->getExpr()->getClassType();\n    if (!isId(expr->getExpr(), TYPE_TYPE))\n      typ = extractClassGeneric(typ)->getClass();\n    if (!typ)\n      return {nullptr, nullptr};\n    auto clsName = typ->name;\n    if (typ->isRecord()) {\n      if (expr->hasAttribute(Attr::TupleCall)) {\n        expr->eraseAttribute(Attr::TupleCall);\n      }\n      // Case: tuple constructor. Transform to: `T.__new__(args)`\n      auto e =\n          transform(N<CallExpr>(N<DotExpr>(expr->getExpr(), \"__new__\"), expr->items));\n      return {nullptr, e};\n    }\n\n    // Case: reference type constructor. Transform to\n    // `ctr = T.__new__(); v.__init__(args)`\n    Expr *var = N<IdExpr>(getTemporaryVar(\"ctr\"));\n    auto newInit =\n        N<AssignStmt>(clone(var), N<CallExpr>(N<DotExpr>(expr->getExpr(), \"__new__\")));\n    auto e = N<StmtExpr>(N<SuiteStmt>(newInit), clone(var));\n    auto init =\n        N<ExprStmt>(N<CallExpr>(N<DotExpr>(clone(var), \"__init__\"), expr->items));\n    e->items.emplace_back(init);\n    return {nullptr, transform(e)};\n  }\n\n  if (auto partType = callee->getPartial()) {\n    auto mask = partType->getPartialMask();\n    auto genFn = partType->getPartialFunc()->generalize(0);\n    auto calleeFn =\n        std::static_pointer_cast<types::FuncType>(instantiateType(genFn.get()));\n    if (!partType->isPartialEmpty() || std::ranges::any_of(mask, [](char c) {\n          return c != ClassType::PartialFlag::Missing;\n        })) {\n      // Case: calling partial object `p`. Transform roughly to\n      // `part = callee; partial_fn(*part.args, args...)`\n      Expr *var = N<IdExpr>(part.var = getTemporaryVar(\"partcall\"));\n      expr->expr = transform(N<StmtExpr>(N<AssignStmt>(clone(var), expr->getExpr()),\n                                         N<IdExpr>(calleeFn->getFuncName())));\n      part.known = mask;\n    } else {\n      expr->expr = transform(N<IdExpr>(calleeFn->getFuncName()));\n    }\n    seqassert(expr->getExpr()->getType()->getFunc(), \"not a function: {}\",\n              *(expr->getExpr()->getType()));\n    unify(expr->getExpr()->getType(), calleeFn);\n\n    // Unify partial generics with types known thus far\n    auto knownArgTypes = extractClassGeneric(partType, 1)->getClass();\n    for (size_t i = 0, j = 0, k = 0; i < mask.size(); i++)\n      if ((*calleeFn->ast)[i].isGeneric()) {\n        j++;\n      } else if (mask[i] == ClassType::PartialFlag::Included) {\n        unify(extractFuncArgType(calleeFn.get(), i - j),\n              extractClassGeneric(knownArgTypes, k));\n        k++;\n      } else if (mask[i] == ClassType::PartialFlag::Default) {\n        k++;\n      }\n    return {calleeFn, nullptr};\n  } else if (!callee->getFunc()) {\n    // Case: callee is not a function. Try __call__ method instead\n    return {nullptr, transform(N<CallExpr>(N<DotExpr>(expr->getExpr(), \"__call__\"),\n                                           expr->items))};\n  } else {\n    return {std::static_pointer_cast<types::FuncType>(\n                callee->getFunc()->shared_from_this()),\n            nullptr};\n  }\n}\n\n/// Reorder the call arguments to match the signature order. Ensure that every @c\n/// CallArg has a set name. Form *args/**kwargs tuples if needed, and use partial\n/// and default values where needed.\n/// @example\n///   `foo(1, 2, baz=3, baf=4)` -> `foo(a=1, baz=2, args=(3, ), kwargs=KwArgs(baf=4))`\nExpr *TypecheckVisitor::callReorderArguments(FuncType *calleeFn, CallExpr *expr,\n                                             PartialCallData &part) {\n  if (calleeFn->ast->hasAttribute(Attr::NoArgReorder))\n    return nullptr;\n\n  bool inOrder = true;\n  std::vector<std::pair<size_t, Expr **>> ordered;\n\n  std::vector<CallArg> args;    // stores ordered and processed arguments\n  std::vector<Expr *> typeArgs; // stores type and static arguments (e.g., `T: type`)\n  int64_t starIdx = -1;         // for *args\n  std::vector<Expr *> starArgs;\n  int64_t kwStarIdx = -1; // for **kwargs\n  std::vector<std::string> kwStarNames;\n  std::vector<Expr *> kwStarArgs;\n  auto newMask = std::string(calleeFn->ast->size(), ClassType::PartialFlag::Included);\n\n  // Extract pi-th partial argument from a partial object\n  auto getPartialArg = [&](size_t pi) {\n    auto id = transform(N<DotExpr>(N<IdExpr>(part.var), \"args\"));\n    // Manually call @c transformStaticTupleIndex to avoid spurious InstantiateExpr\n    auto ex = transformStaticTupleIndex(id->getClassType(), id, N<IntExpr>(pi));\n    seqassert(ex.first && ex.second, \"partial indexing failed: {}\", *(id->getType()));\n    return ex.second;\n  };\n\n  auto addReordered = [&](size_t i) -> bool {\n    Expr **e = &((*expr)[i].value);\n    if (hasSideEffect(*e)) {\n      if (!ordered.empty() && i < ordered.back().first)\n        inOrder = false;\n      ordered.emplace_back(i, e);\n      return true;\n    }\n    return false;\n  };\n\n  // Handle reordered arguments (see @c reorderNamedArgs for details)\n  bool partial = false;\n  auto reorderFn = [&](int starArgIndex, int kwstarArgIndex,\n                       const std::vector<std::vector<int>> &slots, bool _partial) {\n    partial = _partial;\n    return withClassGenerics(\n        calleeFn,\n        [&]() {\n          for (size_t si = 0, pi = 0, gi = 0; si < slots.size(); si++) {\n            // Get the argument name to be used later\n            auto [_, rn] = (*calleeFn->ast)[si].getNameWithStars();\n            auto realName = getUnmangledName(rn);\n\n            if ((*calleeFn->ast)[si].isGeneric()) {\n              // Case: generic arguments. Populate typeArgs\n              if (startswith(realName, \"$\")) {\n                if (slots[si].empty()) {\n                  if (!part.known.empty() &&\n                      part.known[si] == ClassType::PartialFlag::Included) {\n                    auto t = N<IdExpr>(realName);\n                    t->setType(\n                        calleeFn->funcGenerics[gi].getType()->shared_from_this());\n                    typeArgs.emplace_back(t);\n                  } else {\n                    typeArgs.emplace_back(transform(N<IdExpr>(realName.substr(1))));\n                  }\n                } else {\n                  typeArgs.emplace_back((*expr)[slots[si][0]].getExpr());\n                  if (addReordered(slots[si][0]))\n                    inOrder = false; // type arguments always need preprocessing\n                }\n                newMask[si] = ClassType::PartialFlag::Included;\n              } else if (slots[si].empty()) {\n                typeArgs.push_back(nullptr);\n                newMask[si] = ClassType::PartialFlag::Missing;\n              } else {\n                typeArgs.push_back((*expr)[slots[si][0]].getExpr());\n                newMask[si] = ClassType::PartialFlag::Included;\n                if (addReordered(slots[si][0]))\n                  inOrder = false; // type arguments always need preprocessing\n              }\n              gi++;\n            } else if (si == starArgIndex &&\n                       !(slots[si].size() == 1 &&\n                         (*expr)[slots[si][0]].getExpr()->hasAttribute(\n                             Attr::ExprStarArgument))) {\n              // Case: *args. Build the tuple that holds them all\n              if (!part.known.empty()) {\n                starArgs.push_back(N<StarExpr>(getPartialArg(-1)));\n              }\n              for (auto &e : slots[si]) {\n                starArgs.push_back((*expr)[e].getExpr());\n                addReordered(e);\n              }\n              starIdx = static_cast<int>(args.size());\n              args.emplace_back(realName, nullptr);\n              if (partial)\n                newMask[si] = ClassType::PartialFlag::Missing;\n            } else if (si == kwstarArgIndex &&\n                       !(slots[si].size() == 1 &&\n                         (*expr)[slots[si][0]].getExpr()->hasAttribute(\n                             Attr::ExprKwStarArgument))) {\n              // Case: **kwargs. Build the named tuple that holds them all\n              std::unordered_set<std::string> newNames;\n              for (auto &e : slots[si]) // kwargs names can be overriden later\n                newNames.insert((*expr)[e].getName());\n              if (!part.known.empty()) {\n                auto e = transform(N<DotExpr>(N<IdExpr>(part.var), \"kwargs\"));\n                for (auto &[n, ne] : extractNamedTuple(e)) {\n                  if (!in(newNames, n)) {\n                    newNames.insert(n);\n                    kwStarNames.emplace_back(n);\n                    kwStarArgs.emplace_back(transform(ne));\n                  }\n                }\n              }\n              for (auto &e : slots[si]) {\n                kwStarNames.emplace_back((*expr)[e].getName());\n                kwStarArgs.emplace_back((*expr)[e].getExpr());\n                addReordered(e);\n              }\n\n              kwStarIdx = static_cast<int>(args.size());\n              args.emplace_back(realName, nullptr);\n              if (partial)\n                newMask[si] = ClassType::PartialFlag::Missing;\n            } else if (slots[si].empty()) {\n              // Case: no arguments provided.\n              if (!part.known.empty() &&\n                  part.known[si] == ClassType::PartialFlag::Included) {\n                // Case 1: Argument captured by partial\n                args.emplace_back(realName, getPartialArg(pi));\n                pi++;\n              } else if (startswith(realName, \"$\")) {\n                // Case 3: Local name capture\n                bool added = false;\n                if (partial) {\n                  if (auto val = ctx->find(realName.substr(1))) {\n                    if (val->isFunc() && val->getType()->getFunc()->ast->getName() ==\n                                             calleeFn->ast->getName()) {\n                      // Special case: fn(fn=fn)\n                      // Delay this one.\n                      args.emplace_back(\n                          realName, transform(N<EllipsisExpr>(EllipsisExpr::PARTIAL)));\n                      newMask[si] = ClassType::PartialFlag::Missing;\n                      added = true;\n                    }\n                  }\n                }\n                if (!added)\n                  args.emplace_back(realName, transform(N<IdExpr>(realName.substr(1))));\n              } else if ((*calleeFn->ast)[si].getDefault()) {\n                // Case 4: default is present\n                if (auto ai = cast<IdExpr>((*calleeFn->ast)[si].getDefault())) {\n                  // Case 4a: non-values (Ids / .default names)\n                  if (!part.known.empty() &&\n                      part.known[si] == ClassType::PartialFlag::Default) {\n                    // Case 4a/1: Default already captured by partial.\n                    args.emplace_back(realName, getPartialArg(pi));\n                    pi++;\n                  } else {\n                    // TODO: check if the value is toplevel to avoid capturing it\n                    auto e = transform(N<IdExpr>(ai->getValue()));\n                    seqassert(e->getType()->getLink(), \"not a link type\");\n                    args.emplace_back(realName, e);\n                  }\n                  if (partial)\n                    newMask[si] = ClassType::PartialFlag::Default;\n                } else if (!partial) {\n                  // Case 4b: values / non-Id defaults (None, etc.)\n                  if (cast<NoneExpr>((*calleeFn->ast)[si].getDefault()) &&\n                      !(*calleeFn->ast)[si].type) {\n                    args.emplace_back(\n                        realName, transform(N<CallExpr>(N<InstantiateExpr>(\n                                      N<IdExpr>(\"Optional\"), N<IdExpr>(\"NoneType\")))));\n                  } else {\n                    args.emplace_back(\n                        realName,\n                        transform(clean_clone((*calleeFn->ast)[si].getDefault())));\n                  }\n                } else {\n                  args.emplace_back(realName,\n                                    transform(N<EllipsisExpr>(EllipsisExpr::PARTIAL)));\n                  newMask[si] = ClassType::PartialFlag::Missing;\n                }\n              } else if (partial) {\n                // Case 5: this is partial call. Just add ... for missing arguments\n                args.emplace_back(realName,\n                                  transform(N<EllipsisExpr>(EllipsisExpr::PARTIAL)));\n                newMask[si] = ClassType::PartialFlag::Missing;\n              } else {\n                seqassert(expr, \"cannot happen\");\n              }\n            } else {\n              // Case: argument provided\n              seqassert(slots[si].size() == 1, \"call transformation failed\");\n\n              args.emplace_back(realName, (*expr)[slots[si][0]].getExpr());\n              addReordered(slots[si][0]);\n            }\n          }\n          return 0;\n        },\n        true);\n  };\n\n  // Reorder arguments if needed\n  part.args = part.kwArgs = nullptr; // Stores partial *args/**kwargs expression\n  if (expr->hasAttribute(Attr::ExprOrderedCall)) {\n    args = expr->items;\n  } else {\n    reorderNamedArgs(\n        calleeFn, expr->items, reorderFn,\n        [&](error::Error e, const SrcInfo &o, const std::string &errorMsg) {\n          E(Error::CUSTOM, o, errorMsg.c_str());\n          return -1;\n        },\n        part.known);\n  }\n\n  // Do reordering\n  if (!inOrder) {\n    std::vector<Stmt *> prepends;\n    std::ranges::sort(ordered,\n                      [](const auto &a, const auto &b) { return a.first < b.first; });\n    for (auto &eptr : ordered | std::views::values) {\n      auto name = getTemporaryVar(\"call\");\n      auto front = transform(\n          N<AssignStmt>(N<IdExpr>(name), *eptr, getParamType((*eptr)->getType())));\n      auto swap = transform(N<IdExpr>(name));\n      *eptr = swap;\n      prepends.emplace_back(front);\n    }\n    return transform(N<StmtExpr>(prepends, expr));\n  }\n\n  // Handle *args\n  if (starIdx != -1) {\n    Expr *se = N<TupleExpr>(starArgs);\n    se->setAttribute(Attr::ExprStarArgument);\n    if (!match(expr->getExpr(), M<IdExpr>(\"hasattr\")))\n      se = transform(se);\n    if (partial) {\n      part.args = se;\n      args[starIdx].value = transform(N<EllipsisExpr>(EllipsisExpr::PARTIAL));\n    } else {\n      args[starIdx].value = se;\n    }\n  }\n\n  // Handle **kwargs\n  if (kwStarIdx != -1) {\n    auto kwid = generateKwId(kwStarNames);\n    auto kwe = transform(N<CallExpr>(N<IdExpr>(\"NamedTuple\"), N<TupleExpr>(kwStarArgs),\n                                     N<IntExpr>(kwid)));\n    kwe->setAttribute(Attr::ExprKwStarArgument);\n    if (partial) {\n      part.kwArgs = kwe;\n      args[kwStarIdx] = transform(N<EllipsisExpr>(EllipsisExpr::PARTIAL));\n    } else {\n      args[kwStarIdx] = kwe;\n    }\n  }\n\n  // Populate partial data\n  if (part.args != nullptr)\n    part.args->setAttribute(Attr::ExprSequenceItem);\n  if (part.kwArgs != nullptr)\n    part.kwArgs->setAttribute(Attr::ExprSequenceItem);\n  if (part.isPartial) {\n    expr->items.pop_back();\n    if (!part.args)\n      part.args = transform(N<TupleExpr>()); // use ()\n    if (!part.kwArgs)\n      part.kwArgs = transform(N<CallExpr>(N<IdExpr>(\"NamedTuple\"))); // use NamedTuple()\n  }\n\n  // Unify function type generics with the provided generics\n  seqassert((expr->hasAttribute(Attr::ExprOrderedCall) && typeArgs.empty()) ||\n                (!expr->hasAttribute(Attr::ExprOrderedCall) &&\n                 typeArgs.size() == calleeFn->funcGenerics.size()),\n            \"bad vector sizes\");\n  if (!calleeFn->funcGenerics.empty()) {\n    auto niGenerics = calleeFn->ast->getNonInferrableGenerics();\n    for (size_t si = 0; !expr->hasAttribute(Attr::ExprOrderedCall) &&\n                        si < calleeFn->funcGenerics.size();\n         si++) {\n      const auto &gen = calleeFn->funcGenerics[si];\n      if (typeArgs[si]) {\n        auto typ = extractType(typeArgs[si]);\n        if (gen.staticKind && !typ->getStaticKind()) {\n          E(Error::EXPECTED_STATIC, typeArgs[si]);\n        }\n        unify(typ, gen.getType());\n      } else {\n        if (isUnbound(gen.getType()) && !(*calleeFn->ast)[si].getDefault() &&\n            !partial && in(niGenerics, gen.name)) {\n          E(Error::CUSTOM, getSrcInfo(), \"generic '{}' not provided\",\n            getUnmangledName(gen.name));\n        }\n      }\n    }\n  }\n\n  expr->items = args;\n  expr->setAttribute(Attr::ExprOrderedCall);\n  part.known = newMask;\n  return nullptr;\n}\n\n/// Unify the call arguments' types with the function declaration signatures.\n/// Also apply argument transformations to ensure the type compatibility and handle\n/// default generics.\n/// @example\n///   `foo(1, 2)` -> `foo(1, Optional(2), T=int)`\nbool TypecheckVisitor::typecheckCallArgs(FuncType *calleeFn, std::vector<CallArg> &args,\n                                         const PartialCallData &partial) {\n  bool wrappingDone = true;         // tracks whether all arguments are wrapped\n  std::vector<Type *> replacements; // list of replacement arguments\n\n  withClassGenerics(\n      calleeFn,\n      [&]() {\n        for (size_t i = 0, si = 0; i < calleeFn->ast->size(); i++) {\n          if ((*calleeFn->ast)[i].isGeneric())\n            continue;\n\n          if (startswith((*calleeFn->ast)[i].getName(), \"*\") &&\n              (*calleeFn->ast)[i].getType()) {\n            // Special case: `*args: type` and `**kwargs: type`\n            if (auto callExpr = cast<CallExpr>(args[si].getExpr())) {\n              auto typ = extractType(transform(clone((*calleeFn->ast)[i].getType())));\n              if (startswith((*calleeFn->ast)[i].getName(), \"**\"))\n                callExpr = cast<CallExpr>(callExpr->front().getExpr());\n              for (auto &ca : *callExpr) {\n                if (wrapExpr(&ca.value, typ, calleeFn)) {\n                  unify(ca.getExpr()->getType(), typ);\n                } else {\n                  wrappingDone = false;\n                }\n              }\n              auto name = callExpr->getClassType()->name;\n              auto tup = transform(N<CallExpr>(N<IdExpr>(name), callExpr->items));\n              if (startswith((*calleeFn->ast)[i].getName(), \"**\")) {\n                args[si].value = transform(N<CallExpr>(\n                    N<DotExpr>(N<IdExpr>(\"NamedTuple\"), \"__new__\"), tup,\n                    N<IntExpr>(extractClassGeneric(args[si].getExpr()->getType())\n                                   ->getIntStatic()\n                                   ->value)));\n              } else {\n                args[si].value = tup;\n              }\n            }\n            replacements.push_back(args[si].getExpr()->getType());\n            // else this is empty and is a partial call; leave it for later\n          } else if (partial.isPartial && !partial.known.empty() &&\n                     partial.known[si] == ClassType::PartialFlag::Default) {\n            // Defaults should not be unified (yet)!\n            replacements.push_back(extractFuncArgType(calleeFn, si));\n          } else {\n            if (wrapExpr(&args[si].value, extractFuncArgType(calleeFn, si), calleeFn)) {\n              unify(args[si].getExpr()->getType(), extractFuncArgType(calleeFn, si));\n            } else {\n              wrappingDone = false;\n            }\n            replacements.push_back(!extractFuncArgType(calleeFn, si)->getClass()\n                                       ? args[si].getExpr()->getType()\n                                       : extractFuncArgType(calleeFn, si));\n          }\n          si++;\n        }\n        return true;\n      },\n      true);\n\n  // Realize arguments\n  bool done = true;\n  for (auto &a : args) {\n    // Previous unifications can qualify existing identifiers.\n    // Transform again to get the full identifier\n    if (realize(a.getExpr()->getType()))\n      a.value = transform(a.getExpr());\n    done &= a.getExpr()->isDone();\n  }\n\n  // Handle default generics\n  if (!partial.isPartial)\n    for (size_t i = 0, j = 0; wrappingDone && i < calleeFn->ast->size(); i++)\n      if ((*calleeFn->ast)[i].isGeneric()) {\n        if ((*calleeFn->ast)[i].getDefault() &&\n            isUnbound(extractFuncGeneric(calleeFn, j))) {\n          auto def = extractType(withClassGenerics(\n              calleeFn,\n              [&]() {\n                return transform(clean_clone((*calleeFn->ast)[i].getDefault()));\n              },\n              true));\n          unify(extractFuncGeneric(calleeFn, j), def);\n        }\n        j++;\n      }\n\n  // Replace the arguments\n  for (size_t si = 0; si < replacements.size(); si++) {\n    if (replacements[si]) {\n      extractClassGeneric(calleeFn)->getClass()->generics[si].type =\n          replacements[si]->shared_from_this();\n    }\n  }\n  extractClassGeneric(calleeFn)->getClass()->_rn = \"\";\n  calleeFn->getClass()->_rn = \"\"; /// TODO: TERRIBLE!\n\n  return done;\n}\n\n/// Transform and typecheck the following special call expressions:\n///   `superf(fn)`\n///   `super()`\n///   `__ptr__(var)`\n///   `__array__[int](sz)`\n///   `isinstance(obj, type)`\n///   `static.len(tup)`\n///   `hasattr(obj, \"attr\")`\n///   `getattr(obj, \"attr\")`\n///   `type(obj)`\n///   `compile_err(\"msg\")`\n/// See below for more details.\nstd::pair<bool, Expr *> TypecheckVisitor::transformSpecialCall(CallExpr *expr) {\n  if (expr->hasAttribute(Attr::ExprNoSpecial))\n    return {false, nullptr};\n\n  auto ei = cast<IdExpr>(expr->getExpr());\n  if (!ei)\n    return {false, nullptr};\n  auto isF = [](const IdExpr *val, const std::string &module, const std::string &cls,\n                const std::string &name = \"\") {\n    if (name.empty())\n      return val->getValue() == getMangledFunc(module, cls);\n    else\n      return val->getValue() == getMangledMethod(module, cls, name);\n  };\n  if (isF(ei, \"std.internal.core\", \"superf\")) {\n    return {true, transformSuperF(expr)};\n  } else if (isF(ei, \"std.internal.core\", \"super\")) {\n    return {true, transformSuper()};\n  } else if (isF(ei, \"std.internal.core\", \"__ptr__\")) {\n    return {true, transformPtr(expr)};\n  } else if (isF(ei, \"std.internal.core\", \"__array__\", \"__new__\")) {\n    return {true, transformArray(expr)};\n  } else if (isF(ei, \"std.internal.core\", \"isinstance\")) { // static\n    return {true, transformIsInstance(expr)};\n  } else if (isF(ei, \"std.internal.static\", \"len\")) { // static\n    return {true, transformStaticLen(expr)};\n  } else if (isF(ei, \"std.internal.core\", \"hasattr\")) { // static\n    return {true, transformHasAttr(expr)};\n  } else if (isF(ei, \"std.internal.core\", \"getattr\")) {\n    return {true, transformGetAttr(expr)};\n  } else if (isF(ei, \"std.internal.core\", \"setattr\")) {\n    return {true, transformSetAttr(expr)};\n  } else if (isF(ei, \"std.internal.core\", \"type\", \"__new__\")) {\n    return {true, transformTypeFn(expr)};\n  } else if (isF(ei, \"std.internal.core\", \"compile_error\")) {\n    return {true, transformCompileError(expr)};\n  } else if (isF(ei, \"std.internal.static\", \"print\")) {\n    return {false, transformStaticPrintFn(expr)};\n  } else if (isF(ei, \"std.collections\", \"namedtuple\")) {\n    return {true, transformNamedTuple(expr)};\n  } else if (isF(ei, \"std.functools\", \"partial\")) {\n    return {true, transformFunctoolsPartial(expr)};\n  } else if (isF(ei, \"std.internal.static\", \"has_rtti\")) { // static\n    return {true, transformHasRttiFn(expr)};\n  } else if (isF(ei, \"std.internal.static\", \"function\", \"realized\")) {\n    return {true, transformRealizedFn(expr)};\n  } else if (isF(ei, \"std.internal.static\", \"function\", \"can_call\")) { // static\n    return {true, transformStaticFnCanCall(expr)};\n  } else if (isF(ei, \"std.internal.static\", \"function\", \"has_type\")) { // static\n    return {true, transformStaticFnArgHasType(expr)};\n  } else if (isF(ei, \"std.internal.static\", \"function\", \"get_type\")) {\n    return {true, transformStaticFnArgGetType(expr)};\n  } else if (isF(ei, \"std.internal.static\", \"function\", \"args\")) {\n    return {true, transformStaticFnArgs(expr)};\n  } else if (isF(ei, \"std.internal.static\", \"function\", \"has_default\")) { // static\n    return {true, transformStaticFnHasDefault(expr)};\n  } else if (isF(ei, \"std.internal.static\", \"function\", \"get_default\")) {\n    return {true, transformStaticFnGetDefault(expr)};\n  } else if (isF(ei, \"std.internal.static\", \"function\", \"wrap_args\")) {\n    return {true, transformStaticFnWrapCallArgs(expr)};\n  } else if (isF(ei, \"std.internal.static\", \"vars\")) {\n    return {true, transformStaticVars(expr)};\n  } else if (isF(ei, \"std.internal.static\", \"tuple_type\")) {\n    return {true, transformStaticTupleType(expr)};\n  } else if (isF(ei, \"std.internal.static\", \"format\")) { // static\n    return {true, transformStaticFormat(expr)};\n  } else if (isF(ei, \"std.internal.static\", \"int_to_string\")) { // static\n    return {true, transformStaticIntToStr(expr)};\n  } else {\n    return {false, nullptr};\n  }\n}\n\n/// Get the list that describes the inheritance hierarchy of a given type.\n/// The first type in the list is the most recently inherited type.\nstd::vector<TypePtr> TypecheckVisitor::getStaticSuperTypes(ClassType *cls) {\n  std::vector<TypePtr> result;\n  if (!cls)\n    return result;\n\n  result.push_back(cls->shared_from_this());\n  auto c = getClass(cls);\n  auto fields = getClassFields(cls);\n  for (auto &name : c->staticParentClasses) {\n    auto parentTyp = instantiateType(extractClassType(name), cls);\n    auto parentFields = getClassFields(parentTyp->getClass());\n    for (auto &field : fields) {\n      for (auto &parentField : parentFields)\n        if (field.name == parentField.name) {\n          auto t = instantiateType(field.getType(), cls);\n          unify(t.get(), instantiateType(parentField.getType(), parentTyp->getClass()));\n          break;\n        }\n    }\n    for (auto &t : getStaticSuperTypes(parentTyp->getClass()))\n      result.push_back(t);\n  }\n  return result;\n}\n\n/// Get the list that describes the inheritance hierarchy of a given type.\n/// The first type in the list is the most recently inherited type.\nstd::vector<TypePtr> TypecheckVisitor::getRTTISuperTypes(ClassType *cls) {\n  std::vector<TypePtr> result;\n  if (!cls)\n    return result;\n\n  auto c = getClass(cls);\n  const auto &mro = c->mro;\n  for (const auto &umt : c->mro) {\n    auto mt = instantiateType(umt.get(), cls);\n    realize(mt.get()); // ensure that parent types are realized\n    result.push_back(mt);\n  }\n  return result;\n}\n\n/// Return a partial type call `Partial(args, kwargs, fn, mask)` for a given function\n/// and a mask.\n/// @param mask a 0-1 vector whose size matches the number of function arguments.\n///             1 indicates that the argument has been provided and is cached within\n///             the partial object.\nExpr *TypecheckVisitor::generatePartialCall(const std::string &mask,\n                                            types::FuncType *fn, Expr *args,\n                                            Expr *kwargs) {\n  if (!args)\n    args = N<TupleExpr>(std::vector<Expr *>{N<TupleExpr>()});\n  if (!kwargs)\n    kwargs = N<CallExpr>(N<IdExpr>(\"NamedTuple\"));\n\n  auto efn = N<IdExpr>(fn->getFuncName());\n  efn->setType(instantiateType(getStdLibType(\"unrealized_type\"),\n                               std::vector<types::Type *>{fn->getFunc()}));\n  efn->setDone();\n  Expr *call = N<CallExpr>(\n      N<IdExpr>(\"Partial\"),\n      std::vector<CallArg>{CallArg{\"args\", args}, CallArg{\"kwargs\", kwargs},\n                           CallArg{\"M\", N<StringExpr>(mask)}, CallArg{\"F\", efn}});\n  return call;\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/class.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <string>\n#include <tuple>\n\n#include \"codon/cir/attribute.h\"\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/match.h\"\n#include \"codon/parser/visitors/format/format.h\"\n#include \"codon/parser/visitors/scoping/scoping.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\nusing namespace codon::error;\n\nnamespace codon::ast {\n\nusing namespace types;\nusing namespace matcher;\n\n/// Parse a class (type) declaration and add a (generic) type to the context.\nvoid TypecheckVisitor::visit(ClassStmt *stmt) {\n  // Get root name\n  std::string name = stmt->getName();\n\n  // Generate/find class' canonical name (unique ID) and AST\n  std::string canonicalName;\n  std::vector<Param> &argsToParse = stmt->items;\n\n  // classItem will be added later when the scope is different\n  auto classItem = std::make_shared<TypecheckItem>(\"\", \"\", ctx->getModule(), nullptr,\n                                                   ctx->getScope());\n  classItem->setSrcInfo(stmt->getSrcInfo());\n  std::shared_ptr<TypecheckItem> timedItem = nullptr;\n  types::ClassType *typ = nullptr;\n  if (!stmt->hasAttribute(Attr::Extend)) {\n    classItem->canonicalName = canonicalName =\n        ctx->generateCanonicalName(name, !stmt->hasAttribute(Attr::Internal),\n                                   /* noSuffix*/ stmt->hasAttribute(Attr::Internal));\n\n    if (canonicalName == \"Union\")\n      classItem->type = std::make_shared<types::UnionType>(ctx->cache);\n    else\n      classItem->type = std::make_shared<types::ClassType>(ctx->cache, canonicalName);\n    if (stmt->isRecord())\n      classItem->type->getClass()->isTuple = true;\n    classItem->type->setSrcInfo(stmt->getSrcInfo());\n\n    typ = classItem->getType()->getClass();\n    if (canonicalName != TYPE_TYPE)\n      classItem->type = instantiateTypeVar(classItem->getType());\n\n    timedItem = std::make_shared<TypecheckItem>(*classItem);\n    // timedItem->time = getTime();\n\n    // Reference types are added to the context here.\n    // Tuple types are added after class contents are parsed to prevent\n    // recursive record types (note: these are allowed for reference types)\n    if (!stmt->hasAttribute(Attr::Tuple)) {\n      ctx->add(name, timedItem);\n      ctx->addAlwaysVisible(classItem);\n    }\n  } else {\n    // Find the canonical name and AST of the class that is to be extended\n    if (!ctx->isGlobal() || ctx->isConditional())\n      E(Error::EXPECTED_TOPLEVEL, getSrcInfo(), \"class extension\");\n    auto val = ctx->find(name, getTime());\n    if (!val || !val->isType())\n      E(Error::CLASS_ID_NOT_FOUND, getSrcInfo(), name);\n    typ = val->getName() == TYPE_TYPE ? val->getType()->getClass()\n                                      : extractClassType(val->getType());\n    if (getClass(typ)->ast->hasAttribute(Attr::NoExtend))\n      if (!ctx->isStdlibLoading && !stmt->hasAttribute(Attr::AutoGenerated))\n        E(Error::CLASS_NO_EXTEND, getSrcInfo(), name);\n    canonicalName = typ->name;\n    argsToParse = getClass(typ)->ast->items;\n  }\n  auto &cls = ctx->cache->classes[canonicalName];\n\n  std::vector<Stmt *> clsStmts; // Will be filled later!\n  std::vector<Stmt *> varStmts; // Will be filled later!\n  std::vector<Stmt *> fnStmts;  // Will be filled later!\n  std::vector<TypeContext::Item> addLater;\n  try {\n    // Add the class base\n    TypeContext::BaseGuard br(ctx.get(), canonicalName);\n    ctx->getBase()->type = typ->shared_from_this();\n\n    // Parse and add class generics\n    std::vector<Param> args;\n    if (stmt->hasAttribute(Attr::Extend)) {\n      for (auto &a : argsToParse) {\n        if (!a.isGeneric())\n          continue;\n        auto val = ctx->forceFind(a.name);\n        val->type->getLink()->kind = LinkType::Unbound;\n        ctx->add(getUnmangledName(val->canonicalName), val);\n        args.emplace_back(val->canonicalName, nullptr, nullptr, a.status);\n      }\n    } else {\n      if (stmt->hasAttribute(Attr::ClassDeduce)) {\n        if (!autoDeduceMembers(stmt, argsToParse))\n          stmt->eraseAttribute(Attr::ClassDeduce);\n      }\n\n      // Add all generics before parent classes, fields and methods\n      for (auto &a : argsToParse) {\n        if (!a.isGeneric())\n          continue;\n\n        auto varName = ctx->generateCanonicalName(a.getName());\n        auto generic = instantiateUnbound();\n        auto typId = generic->id;\n        generic->getLink()->genericName = varName;\n        auto defType = transformType(clone(a.getDefault()));\n        if (defType)\n          generic->defaultType = extractType(defType)->shared_from_this();\n        if (auto st = getStaticGeneric(a.getType())) {\n          if (st == LiteralKind::Runtime)\n            a.type = transform(a.getType()); // trigger error\n          generic->staticKind = st;\n          auto val = ctx->addVar(a.getName(), varName, generic);\n          val->generic = true;\n        } else {\n          if (cast<IndexExpr>(a.getType())) { // Parse TraitVar\n            a.type = transform(a.getType());\n            auto ti = cast<InstantiateExpr>(a.getType());\n            seqassert(ti && isId(ti->getExpr(), TRAIT_TYPE),\n                      \"not a TypeTrait instantiation: {}\", *(a.getType()));\n            auto l = extractType(ti->front());\n            if (l->getLink() && l->getLink()->trait)\n              generic->getLink()->trait = l->getLink()->trait;\n            else\n              generic->getLink()->trait =\n                  std::make_shared<types::TypeTrait>(l->shared_from_this());\n          }\n          ctx->addType(a.getName(), varName, generic)->generic = true;\n        }\n        typ->generics.emplace_back(varName, generic->generalize(ctx->typecheckLevel),\n                                   typId, generic->staticKind);\n        args.emplace_back(varName, a.getType(), defType, a.status);\n      }\n    }\n\n    // Form class type node (e.g. `Foo`, or `Foo[T, U]` for generic classes)\n    Expr *transformedTypeAst = nullptr;\n    if (!stmt->hasAttribute(Attr::Extend)) {\n      transformedTypeAst = N<IdExpr>(canonicalName);\n      for (auto &a : args) {\n        if (a.isGeneric()) {\n          if (!cast<InstantiateExpr>(transformedTypeAst)) {\n            transformedTypeAst =\n                N<InstantiateExpr>(N<IdExpr>(canonicalName), std::vector<Expr *>{});\n          }\n          cast<InstantiateExpr>(transformedTypeAst)\n              ->items.push_back(transform(N<IdExpr>(a.getName()), true));\n        }\n      }\n    }\n\n    // Collect classes (and their fields) that are to be statically inherited\n    std::vector<TypePtr> staticBaseASTs;\n    if (!stmt->hasAttribute(Attr::Extend)) {\n      // Handle static inheritance\n      staticBaseASTs = parseBaseClasses(stmt->staticBaseClasses, args, stmt,\n                                        canonicalName, nullptr, typ);\n      // Handle RTTI inheritance\n      parseBaseClasses(stmt->baseClasses, args, stmt, canonicalName, transformedTypeAst,\n                       typ);\n    }\n\n    // A ClassStmt will be separated into class variable assignments, method-free\n    // ClassStmts (that include nested classes) and method FunctionStmts\n    transformNestedClasses(stmt, clsStmts, varStmts, fnStmts);\n\n    // Collect class fields\n    for (auto &a : argsToParse) {\n      if (a.isValue()) {\n        if (ClassStmt::isClassVar(a)) {\n          // Handle class variables. Transform them later to allow self-references\n          auto varName = fmt::format(\"{}.{}\", canonicalName, a.getName());\n          auto h = transform(N<AssignStmt>(N<IdExpr>(varName), nullptr, nullptr));\n          preamble->addStmt(h);\n          auto val = ctx->forceFind(varName);\n          val->baseName = \"\";\n          val->scope = {0};\n          registerGlobal(val->canonicalName);\n          if (a.getDefault()) {\n            auto assign = N<AssignStmt>(\n                N<IdExpr>(varName), transform(a.getDefault()),\n                a.getType() ? cast<IndexExpr>(a.getType())->getIndex() : nullptr);\n            assign->setUpdate();\n            varStmts.push_back(assign);\n          }\n          cls.classVars[a.getName()] = varName;\n          ctx->add(a.getName(), val);\n        } else if (!stmt->hasAttribute(Attr::Extend)) {\n          std::string varName = a.getName();\n          auto ta = transformType(clean_clone(a.getType()), true);\n          args.emplace_back(varName, ta, transform(clone(a.getDefault()), true));\n          cls.fields.emplace_back(varName, nullptr, canonicalName);\n        }\n      }\n    }\n\n    // ASTs for member arguments to be used for populating magic methods\n    std::vector<Param> memberArgs;\n    for (auto &a : args) {\n      if (a.isValue())\n        memberArgs.emplace_back(clone(a));\n    }\n\n    // Handle class members\n    if (!stmt->hasAttribute(Attr::Extend)) {\n      ctx->typecheckLevel++; // to avoid unifying generics early\n      if (canonicalName == TYPE_TUPLE) {\n        // Special tuple handling!\n        for (auto aj = 0; aj < MAX_TUPLE; aj++) {\n          auto genName = fmt::format(\"T{}\", aj + 1);\n          auto genCanName = ctx->generateCanonicalName(genName);\n          auto generic = instantiateUnbound();\n          generic->getLink()->genericName = genName;\n          Expr *te = N<IdExpr>(genCanName);\n          cls.fields.emplace_back(fmt::format(\"item{}\", aj + 1),\n                                  generic->generalize(ctx->typecheckLevel), \"\", te);\n        }\n      } else {\n        for (auto ai = 0, aj = 0; ai < args.size(); ai++) {\n          if (args[ai].isValue() && !ClassStmt::isClassVar(args[ai])) {\n            cls.fields[aj].typeExpr = clean_clone(args[ai].getType());\n            cls.fields[aj].type =\n                extractType(args[ai].getType())->generalize(ctx->typecheckLevel - 1);\n            cls.fields[aj].type->setSrcInfo(args[ai].getType()->getSrcInfo());\n            aj++;\n          }\n        }\n      }\n      ctx->typecheckLevel--;\n    }\n\n    // Parse class members (arguments) and methods\n    if (!stmt->hasAttribute(Attr::Extend)) {\n      // Now that we are done with arguments, add record type to the context\n      if (stmt->hasAttribute(Attr::Tuple)) {\n        ctx->add(name, timedItem);\n        ctx->addAlwaysVisible(classItem);\n      }\n      // Create a cached AST.\n      stmt->setAttribute(Attr::Module, ctx->moduleName.status == ImportFile::STDLIB\n                                           ? STDLIB_IMPORT\n                                           : ctx->moduleName.path);\n      cls.ast = N<ClassStmt>(canonicalName, args, N<SuiteStmt>());\n      cls.ast->cloneAttributesFrom(stmt);\n      cls.ast->baseClasses = stmt->baseClasses;\n      for (auto &b : staticBaseASTs)\n        cls.staticParentClasses.emplace_back(b->getClass()->name);\n      cls.module = ctx->moduleName.path;\n      cls.jitCell = ctx->cache->jitCell;\n\n      // Codegen default magic methods\n      // __new__ must be the first\n      if (auto aa = stmt->getAttribute<ir::StringListAttribute>(Attr::ClassMagic))\n        for (const auto &m : aa->values) {\n          fnStmts.push_back(transform(codegenMagic(m, transformedTypeAst, memberArgs,\n                                                   stmt->hasAttribute(Attr::Tuple))));\n        }\n      // Add inherited methods\n      for (auto &base : staticBaseASTs) {\n        for (auto &val : getClass(base->getClass())->methods | std::views::values)\n          for (auto &mf : getOverloads(val)) {\n            const auto &fp = getFunction(mf);\n            auto f = fp->origAst;\n            if (f && !f->hasAttribute(Attr::AutoGenerated)) {\n              fnStmts.push_back(\n                  cast<FunctionStmt>(withClassGenerics(base->getClass(), [&]() {\n                    // since functions can come from other modules\n                    // make sure to transform them in their respective module\n                    // however make sure to add/pop generics :/\n                    if (!ctx->isStdlibLoading && fp->module != ctx->moduleName.path) {\n                      auto ictx = getImport(fp->module)->ctx;\n                      TypeContext::BaseGuard _(ictx.get(), canonicalName);\n                      ictx->getBase()->type = typ->shared_from_this();\n                      auto tv = TypecheckVisitor(ictx);\n                      auto e = tv.withClassGenerics(\n                          typ, [&]() { return tv.transform(clean_clone(f)); }, false,\n                          false,\n                          /*instantiate*/ true);\n                      return e;\n                    } else {\n                      return transform(clean_clone(f));\n                    }\n                  })));\n            }\n          }\n      }\n    }\n\n    // Add class methods\n    for (const auto &sp : getClassMethods(stmt->getSuite())) {\n      if (auto fp = cast<FunctionStmt>(sp)) {\n        for (auto *&dc : fp->decorators) {\n          // Handle @setter setters\n          if (match(dc, M<DotExpr>(M<IdExpr>(fp->getName()), \"setter\")) &&\n              fp->size() == 2) {\n            fp->name = fmt::format(\"{}{}\", FN_SETTER_SUFFIX, fp->getName());\n            dc = nullptr;\n            break;\n          }\n        }\n        // All tuple methods are marked with AutoGenerated for later convenience\n        // (e.g. heterogenous tuple processing).\n        if (canonicalName == TYPE_TUPLE)\n          sp->setAttribute(Attr::AutoGenerated);\n        fnStmts.emplace_back(transform(sp));\n      }\n    }\n\n    // After popping context block, record types and nested classes will disappear.\n    // Store their references and re-add them to the context after popping\n    addLater.reserve(clsStmts.size() + 1);\n    for (auto &c : clsStmts)\n      addLater.emplace_back(ctx->find(cast<ClassStmt>(c)->getName()));\n    if (stmt->hasAttribute(Attr::Tuple))\n      addLater.emplace_back(ctx->forceFind(name));\n\n    // Mark functions as virtual:\n    auto banned = std::set<std::string>{\"__init__\", \"__new__\", \"__raw__\",\n                                        \"__tuplesize__\", \"__repr_default__\"};\n    for (const auto &method : cls.methods | std::views::keys) {\n      for (size_t mi = 1; mi < cls.mro.size(); mi++) {\n        // ... in the current class\n        auto b = cls.mro[mi]->name;\n        if (in(getClass(b)->methods, method) && !in(banned, method)) {\n          cls.virtuals.insert(method);\n        }\n      }\n      for (auto &v : cls.virtuals) {\n        for (size_t mi = 1; mi < cls.mro.size(); mi++) {\n          // ... and in parent classes\n          auto b = cls.mro[mi]->name;\n          getClass(b)->virtuals.insert(v);\n        }\n      }\n    }\n\n    // Generalize generics and remove them from the context\n    for (const auto &g : args)\n      if (!g.isValue()) {\n        auto generic = ctx->forceFind(g.name)->type;\n        if (g.status == Param::Generic) {\n          // Generalize generics. Hidden generics are linked to the class generics so\n          // ignore them\n          seqassert(generic && generic->getLink() &&\n                        generic->getLink()->kind != types::LinkType::Link,\n                    \"generic has been unified\");\n          generic->getLink()->kind = LinkType::Generic;\n        }\n        ctx->remove(g.name);\n      }\n\n    // Debug information\n    if (!startswith(canonicalName, \"Tuple\") && false) {\n      LOG_REALIZE(\"[class] {} -> {:c} / {}\", canonicalName, *typ, cls.fields.size());\n      for (auto &m : cls.fields)\n        LOG_REALIZE(\"       - member: {}: {:c}\", m.name, *(m.type));\n      for (auto &m : cls.methods)\n        LOG_REALIZE(\"       - method: {}: {}\", m.first, m.second);\n      for (auto &m : cls.mro)\n        LOG_REALIZE(\"       - mro: {:c}\", *m);\n    }\n  } catch (const exc::ParserException &) {\n    if (!stmt->hasAttribute(Attr::Tuple))\n      ctx->remove(name);\n    ctx->cache->classes.erase(name);\n    throw;\n  }\n  for (auto &i : addLater)\n    ctx->add(getUnmangledName(i->canonicalName), i);\n\n  // Extensions are not needed as the cache is already populated\n  if (!stmt->hasAttribute(Attr::Extend)) {\n    auto c = cls.ast;\n    seqassert(c, \"not a class AST for {}\", canonicalName);\n    c->setDone();\n    clsStmts.push_back(c);\n  }\n\n  clsStmts.insert(clsStmts.end(), fnStmts.begin(), fnStmts.end());\n  for (auto &a : varStmts) {\n    // Transform class variables here to allow self-references\n    clsStmts.push_back(transform(a));\n  }\n  resultStmt = N<SuiteStmt>(clsStmts);\n}\n\n/// Parse statically inherited classes.\n/// Returns a list of their ASTs. Also updates the class fields.\n/// @param args Class fields that are to be updated with base classes' fields.\n/// @param typeAst Transformed AST for base class type (e.g., `A[T]`).\n///                Only set when dealing with dynamic polymorphism.\nstd::vector<TypePtr> TypecheckVisitor::parseBaseClasses(\n    std::vector<Expr *> &baseClasses, std::vector<Param> &args, const Stmt *attr,\n    const std::string &canonicalName, const Expr *typeAst, types::ClassType *typ) {\n  std::vector<TypePtr> asts;\n\n  // TODO)) fix MRO it to work with generic classes (maybe replacements? IDK...)\n  std::vector<std::vector<TypePtr>> mro{{typ->shared_from_this()}};\n  for (auto &cls : baseClasses) {\n    std::vector<Expr *> subs;\n\n    // Get the base class and generic replacements (e.g., if there is Bar[T],\n    // Bar in Foo(Bar[int]) will have `T = int`)\n    cls = transformType(cls, true);\n    if (!cls->getClassType())\n      E(Error::CLASS_ID_NOT_FOUND, getSrcInfo(), FormatVisitor::apply(cls));\n\n    auto clsTyp = extractClassType(cls);\n    asts.push_back(clsTyp->shared_from_this());\n    auto cachedCls = getClass(clsTyp);\n    if (!cachedCls->ast)\n      E(Error::CLASS_NO_INHERIT, getSrcInfo(), \"nested\", \"surrounding\");\n    std::vector<TypePtr> rootMro;\n    for (auto &t : cachedCls->mro)\n      rootMro.push_back(instantiateType(t.get(), clsTyp));\n    mro.push_back(rootMro);\n\n    // Sanity checks\n    if (attr->hasAttribute(Attr::Tuple) && typeAst)\n      E(Error::CLASS_NO_INHERIT, getSrcInfo(), \"tuple\", \"other\");\n    if (!attr->hasAttribute(Attr::Tuple) && cachedCls->ast->hasAttribute(Attr::Tuple))\n      E(Error::CLASS_TUPLE_INHERIT, getSrcInfo());\n    if (cachedCls->ast->hasAttribute(Attr::Internal))\n      E(Error::CLASS_NO_INHERIT, getSrcInfo(), \"internal\", \"other\");\n\n    // Mark parent classes as polymorphic as well.\n    if (typeAst && !cachedCls->hasRTTI()) {\n      if (ctx->cache->isJit && cachedCls->jitCell != ctx->cache->jitCell)\n        E(Error::CUSTOM, cls,\n          \"cannot inherit from a non-RTTI class defined in previous cell '{}' \"\n          \"in JIT mode\",\n          getUnmangledName(clsTyp->name));\n      cachedCls->rtti = true;\n    }\n\n    // Add hidden generics\n    addClassGenerics(clsTyp);\n    for (auto &g : clsTyp->generics) {\n      g.type = g.getType()->generalize(ctx->typecheckLevel);\n      typ->hiddenGenerics.push_back(g);\n    }\n    for (auto &g : clsTyp->hiddenGenerics) {\n      g.type = g.getType()->generalize(ctx->typecheckLevel);\n      typ->hiddenGenerics.push_back(g);\n    }\n\n    // Add class variables\n    for (auto &[varName, varCanonicalName] : cachedCls->classVars) {\n      // Handle class variables. Transform them later to allow self-references\n      auto newName = fmt::format(\"{}.{}\", canonicalName, varName);\n      auto newCanonicalName = ctx->generateCanonicalName(newName);\n      getClass(typ)->classVars[varName] = varCanonicalName;\n      ctx->add(newName, ctx->forceFind(varCanonicalName));\n      ctx->add(newCanonicalName, ctx->forceFind(varCanonicalName));\n    }\n  }\n  // Add normal fields\n  auto cls = getClass(canonicalName);\n  for (auto &clsTyp : asts) {\n    withClassGenerics(clsTyp->getClass(), [&]() {\n      int ai = 0;\n      auto ast = getClass(clsTyp->getClass())->ast;\n      for (auto &a : *ast) {\n        auto acls = getClass(ast->name);\n        if (a.isValue() && !ClassStmt::isClassVar(a)) {\n          auto name = a.getName();\n          int i = 0;\n          for (auto &aa : args)\n            i += aa.getName() == a.getName() ||\n                 startswith(aa.getName(), a.getName() + \"#\");\n          if (i)\n            name = fmt::format(\"{}#{}\", name, i);\n          seqassert(acls->fields[ai].name == a.getName(), \"bad class fields: {} vs {}\",\n                    acls->fields[ai].name, a.getName());\n          args.emplace_back(name, transformType(clean_clone(a.getType()), true),\n                            transform(clean_clone(a.getDefault())));\n          cls->fields.emplace_back(\n              name, extractType(args.back().getType())->shared_from_this(),\n              acls->fields[ai].baseClass);\n          ai++;\n        }\n      }\n      return true;\n    });\n  }\n  if (typeAst) {\n    if (!asts.empty() ||\n        typ->name == getMangledClass(\"std.internal.builtin\", \"object\")) {\n      mro.push_back(asts);\n      cls->rtti = true;\n    }\n    cls->mro = Cache::mergeC3(mro);\n    if (cls->mro.empty()) {\n      E(Error::CLASS_BAD_MRO, getSrcInfo());\n    }\n  }\n  return asts;\n}\n\n/// Find the first __init__ with self parameter and use it to deduce class members.\n/// Each deduced member will be treated as generic.\n/// @example\n///   ```@deduce\n///      class Foo:\n///        def __init__(self):\n///          self.x, self.y = 1, 2```\n///   will result in\n///   ```class Foo[T1, T2]:\n///        x: T1\n///        y: T2```\n/// @return the transformed init and the pointer to the original function.\nbool TypecheckVisitor::autoDeduceMembers(ClassStmt *stmt, std::vector<Param> &args) {\n  std::set<std::string> members;\n  for (const auto &sp : getClassMethods(stmt->suite))\n    if (auto f = cast<FunctionStmt>(sp)) {\n      if (f->name == \"__init__\")\n        if (const auto b =\n                f->getAttribute<ir::StringListAttribute>(Attr::ClassDeduce)) {\n          for (const auto &m : b->values)\n            members.insert(m);\n        }\n    }\n  if (!members.empty()) {\n    // log(\"auto-deducing {}: {}\", stmt->name, members);\n    if (auto aa = stmt->getAttribute<ir::StringListAttribute>(Attr::ClassMagic))\n      std::erase(aa->values, \"init\");\n    for (auto m : members) {\n      auto genericName = fmt::format(\"T_{}\", m);\n      args.emplace_back(genericName, N<IdExpr>(TYPE_TYPE), N<IdExpr>(\"NoneType\"),\n                        Param::Generic);\n      args.emplace_back(m, N<IdExpr>(genericName));\n    }\n    return true;\n  }\n  return false;\n}\n\n/// Return a list of all statements within a given class suite.\n/// Checks each suite recursively, and assumes that each statement is either\n/// a function, a class or a docstring.\nstd::vector<Stmt *> TypecheckVisitor::getClassMethods(Stmt *s) {\n  std::vector<Stmt *> v;\n  if (!s)\n    return v;\n  if (auto sp = cast<SuiteStmt>(s)) {\n    for (auto *ss : *sp)\n      for (auto *u : getClassMethods(ss))\n        v.push_back(u);\n  } else if (cast<FunctionStmt>(s) || cast<ClassStmt>(s)) {\n    v.push_back(s);\n  } else if (!match(s, M<ExprStmt>(M<StringExpr>()))) {\n    E(Error::CLASS_BAD_ATTR, s);\n  }\n  return v;\n}\n\n/// Extract nested classes and transform them before the main class.\nvoid TypecheckVisitor::transformNestedClasses(const ClassStmt *stmt,\n                                              std::vector<Stmt *> &clsStmts,\n                                              std::vector<Stmt *> &varStmts,\n                                              std::vector<Stmt *> &fnStmts) {\n  for (const auto &sp : getClassMethods(stmt->suite))\n    if (auto cp = cast<ClassStmt>(sp)) {\n      auto origName = cp->getName();\n      // If class B is nested within A, it's name is always A.B, never B itself.\n      // Ensure that parent class name is appended\n      auto parentName = stmt->getName();\n      cp->name = fmt::format(\"{}.{}\", parentName, origName);\n      auto tsp = transform(cp);\n      if (auto tss = cast<SuiteStmt>(tsp)) {\n        std::string name;\n        for (auto &s : *tss)\n          if (auto c = cast<ClassStmt>(s)) {\n            clsStmts.push_back(s);\n            name = c->getName();\n          } else if (cast<AssignStmt>(s)) {\n            varStmts.push_back(s);\n          } else {\n            fnStmts.push_back(s);\n          }\n        ctx->add(origName, ctx->forceFind(name));\n      }\n    }\n}\n\n/// Generate a magic method `__op__` for each magic `op`\n/// described by @param typExpr and its arguments.\n/// Currently, generate:\n/// @li Constructors: __new__, __init__\n/// @li Utilities: __raw__, __hash__, __repr__, __tuplesize__, __add__, __mul__, __len__\n/// @li Iteration: __iter__, __getitem__, __len__, __contains__\n/// @li Comparisons: __eq__, __ne__, __lt__, __le__, __gt__, __ge__\n/// @li Pickling: __pickle__, __unpickle__\n/// @li Python: __to_py__, __from_py__\n/// @li GPU: __to_gpu__, __from_gpu__, __from_gpu_new__\nStmt *TypecheckVisitor::codegenMagic(const std::string &op, Expr *typExpr,\n                                     const std::vector<Param> &allArgs, bool isRecord) {\n#define I(s) N<IdExpr>(s)\n#define NS(x) N<DotExpr>(N<IdExpr>(\"__magic__\"), (x))\n  seqassert(typExpr, \"typExpr is null\");\n  Expr *ret = nullptr;\n  std::vector<Param> fargs;\n  std::vector<Stmt *> stmts;\n  std::vector<int> attrs{Attr::AutoGenerated};\n\n  std::vector<Param> args;\n  args.reserve(allArgs.size());\n  for (auto &a : allArgs)\n    args.push_back(clone(a));\n\n  if (op == \"new\") {\n    ret = clone(typExpr);\n    if (isRecord) {\n      // Tuples: def __new__() -> T (internal)\n      for (auto &a : args)\n        fargs.emplace_back(a.getName(), clone(a.getType()), clone(a.getDefault()));\n      attrs.push_back(Attr::Internal);\n    } else {\n      // Classes: def __new__() -> T\n      stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), clone(typExpr))));\n    }\n  } else if (op == \"init\") {\n    // Classes: def __init__(self: T, a1: T1, ..., aN: TN) -> None:\n    //            self.aI = aI ...\n    ret = I(\"NoneType\");\n    fargs.emplace_back(\"self\", clone(typExpr));\n    for (auto &a : args) {\n      fargs.emplace_back(a.getName(), clean_clone(a.getType()), clone(a.getDefault()));\n      stmts.push_back(\n          N<AssignStmt>(N<DotExpr>(I(\"self\"), a.getName()), I(a.getName())));\n    }\n  } else if (op == \"raw\" || op == \"dict\") {\n    // Classes: def __raw__(self: T)\n    fargs.emplace_back(\"self\", clone(typExpr));\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), I(\"self\"))));\n  } else if (op == \"tuplesize\") {\n    // def __tuplesize__() -> int\n    ret = I(\"int\");\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op))));\n  } else if (op == \"getitem\") {\n    // Tuples: def __getitem__(self: T, index: int)\n    fargs.emplace_back(\"self\", clone(typExpr));\n    fargs.emplace_back(\"index\", I(\"int\"));\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), I(\"self\"), I(\"index\"))));\n  } else if (op == \"iter\") {\n    // Tuples: def __iter__(self: T)\n    fargs.emplace_back(\"self\", clone(typExpr));\n    stmts.emplace_back(N<YieldFromStmt>(N<CallExpr>(NS(op), I(\"self\"))));\n  } else if (op == \"contains\") {\n    // Tuples: def __contains__(self: T, what) -> bool\n    fargs.emplace_back(\"self\", clone(typExpr));\n    fargs.emplace_back(\"what\", nullptr);\n    ret = I(\"bool\");\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), I(\"self\"), I(\"what\"))));\n  } else if (op == \"eq\" || op == \"ne\" || op == \"lt\" || op == \"le\" || op == \"gt\" ||\n             op == \"ge\") {\n    // def __op__(self: T, obj: T) -> bool\n    fargs.emplace_back(\"self\", clone(typExpr));\n    fargs.emplace_back(\"obj\", clone(typExpr));\n    ret = I(\"bool\");\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), I(\"self\"), I(\"obj\"))));\n  } else if (op == \"hash\" || op == \"len\") {\n    // def __hash__(self: T) -> int\n    fargs.emplace_back(\"self\", clone(typExpr));\n    ret = I(\"int\");\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), I(\"self\"))));\n  } else if (op == \"pickle\") {\n    // def __pickle__(self: T, dest: Ptr[byte])\n    fargs.emplace_back(\"self\", clone(typExpr));\n    fargs.emplace_back(\"dest\", N<IndexExpr>(I(\"Ptr\"), I(\"byte\")));\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), I(\"self\"), I(\"dest\"))));\n  } else if (op == \"unpickle\" || op == \"from_py\") {\n    // def __unpickle__(src: Ptr[byte]) -> T\n    fargs.emplace_back(\"src\", N<IndexExpr>(I(\"Ptr\"), I(\"byte\")));\n    ret = clone(typExpr);\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), I(\"src\"), clone(typExpr))));\n  } else if (op == \"to_py\") {\n    // def __to_py__(self: T) -> Ptr[byte]\n    fargs.emplace_back(\"self\", clone(typExpr));\n    ret = N<IndexExpr>(I(\"Ptr\"), I(\"byte\"));\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), I(\"self\"))));\n  } else if (op == \"to_gpu\") {\n    // def __to_gpu__(self: T, cache) -> T\n    fargs.emplace_back(\"self\", clone(typExpr));\n    fargs.emplace_back(\"cache\");\n    ret = clone(typExpr);\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), I(\"self\"), I(\"cache\"))));\n  } else if (op == \"from_gpu\") {\n    // def __from_gpu__(self: T, other: T)\n    fargs.emplace_back(\"self\", clone(typExpr));\n    fargs.emplace_back(\"other\", clone(typExpr));\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), I(\"self\"), I(\"other\"))));\n  } else if (op == \"from_gpu_new\") {\n    // def __from_gpu_new__(other: T) -> T\n    fargs.emplace_back(\"other\", clone(typExpr));\n    ret = clone(typExpr);\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), I(\"other\"))));\n  } else if (op == \"repr\") {\n    // def __repr__(self: T) -> str\n    fargs.emplace_back(\"self\", clone(typExpr));\n    ret = I(\"str\");\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), I(\"self\"))));\n  } else if (op == \"repr_default\") {\n    // def __repr_default__(self: T) -> str\n    fargs.emplace_back(\"self\", clone(typExpr));\n    ret = I(\"str\");\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), I(\"self\"))));\n  } else if (op == \"add\") {\n    // def __add__(self, obj)\n    fargs.emplace_back(\"self\", clone(typExpr));\n    fargs.emplace_back(\"obj\", nullptr);\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), I(\"self\"), I(\"obj\"))));\n  } else if (op == \"mul\") {\n    // def __mul__(self, i: Literal[int])\n    fargs.emplace_back(\"self\", clone(typExpr));\n    fargs.emplace_back(\"i\", N<IndexExpr>(I(\"Literal\"), I(\"int\")));\n    stmts.emplace_back(N<ReturnStmt>(N<CallExpr>(NS(op), I(\"self\"), I(\"i\"))));\n  } else {\n    seqassert(false, \"invalid magic {}\", op);\n  }\n#undef I\n#undef NS\n  auto t =\n      NC<FunctionStmt>(fmt::format(\"__{}__\", op), ret, fargs, NC<SuiteStmt>(stmts));\n  for (auto &a : attrs)\n    t->setAttribute(a);\n  t->setSrcInfo(ctx->cache->generateSrcInfo());\n  return t;\n}\n\nint TypecheckVisitor::generateKwId(const std::vector<std::string> &names) const {\n  auto key = join(names, \";\");\n  std::string suffix;\n  if (!names.empty()) {\n    // Each set of names generates different tuple (i.e., `KwArgs[foo, bar]` is not the\n    // same as `KwArgs[bar, baz]`). Cache the names and use an integer for each name\n    // combination.\n    if (!in(ctx->cache->generatedKwTuples, key)) {\n      ctx->cache->generatedTupleNames.push_back(names);\n      ctx->cache->generatedKwTuples[key] =\n          static_cast<int>(ctx->cache->generatedKwTuples.size()) + 1;\n    }\n    return ctx->cache->generatedKwTuples[key];\n  } else {\n    return 0;\n  }\n}\n\ntypes::ClassType *TypecheckVisitor::generateTuple(size_t n, bool generateNew) {\n  if (n > MAX_TUPLE)\n    E(Error::CUSTOM, getSrcInfo(), \"tuple too large ({})\", n);\n\n  auto key = fmt::format(\"{}.{}\", TYPE_TUPLE, n);\n  auto val = getImport(STDLIB_IMPORT)->ctx->find(key);\n  if (!val) {\n    auto t = std::make_shared<types::ClassType>(ctx->cache, TYPE_TUPLE);\n    t->isTuple = true;\n    auto cls = getClass(t.get());\n    seqassert(n <= cls->fields.size(), \"tuple too large\");\n    for (size_t i = 0; i < n; i++) {\n      const auto &f = cls->fields[i];\n      auto gt = f.getType()->getLink();\n      t->generics.emplace_back(cast<IdExpr>(f.typeExpr)->getValue(), f.type, gt->id,\n                               LiteralKind::Runtime);\n    }\n    val = getImport(STDLIB_IMPORT)->ctx->addType(key, key, t);\n  }\n  auto t = val->getType()->getClass();\n  if (generateNew && !in(ctx->cache->generatedTuples, n)) {\n    ctx->cache->generatedTuples.insert(n);\n    std::vector<Param> newFnArgs;\n    std::vector<Expr *> typeArgs;\n    for (size_t i = 0; i < n; i++) {\n      newFnArgs.emplace_back(fmt::format(\"item{}\", i + 1),\n                             N<IdExpr>(fmt::format(\"T{}\", i + 1)));\n      typeArgs.emplace_back(N<IdExpr>(fmt::format(\"T{}\", i + 1)));\n    }\n    for (size_t i = 0; i < n; i++) {\n      newFnArgs.emplace_back(fmt::format(\"T{}\", i + 1), N<IdExpr>(TYPE_TYPE));\n    }\n    Stmt *fn = N<FunctionStmt>(\n        \"__new__\", N<IndexExpr>(N<IdExpr>(TYPE_TUPLE), N<TupleExpr>(typeArgs)),\n        newFnArgs, nullptr);\n    fn->setAttribute(Attr::Internal);\n    Stmt *ext = N<ClassStmt>(TYPE_TUPLE, std::vector<Param>{}, fn);\n    ext->setAttribute(Attr::Extend);\n    ext->setAttribute(Attr::AutoGenerated);\n    ext = N<SuiteStmt>(ext);\n\n    llvm::cantFail(ScopingVisitor::apply(ctx->cache, ext));\n    auto rctx = getImport(STDLIB_IMPORT)->ctx;\n    auto oldBases = rctx->bases;\n    rctx->bases.clear();\n    rctx->bases.push_back(oldBases[0]);\n    ext = TypecheckVisitor::apply(rctx, ext);\n    rctx->bases = oldBases;\n    preamble->addStmt(ext);\n  }\n  return t;\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/collections.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nusing namespace codon::error;\n\nnamespace codon::ast {\n\nusing namespace types;\n\n/// Transform tuples.\n/// @example\n///   `(a1, ..., aN)` -> `Tuple.__new__(a1, ..., aN)`\nvoid TypecheckVisitor::visit(TupleExpr *expr) {\n  resultExpr =\n      transform(N<CallExpr>(N<DotExpr>(N<IdExpr>(TYPE_TUPLE), \"__new__\"), expr->items));\n}\n\n/// Transform a list `[a1, ..., aN]` to the corresponding statement expression.\n/// See @c transformComprehension\nvoid TypecheckVisitor::visit(ListExpr *expr) {\n  expr->setType(instantiateUnbound());\n  auto name = getStdLibType(\"List\")->name;\n  if ((resultExpr = transformComprehension(name, \"append\", expr->items))) {\n    resultExpr->setAttribute(Attr::ExprList);\n  }\n}\n\n/// Transform a set `{a1, ..., aN}` to the corresponding statement expression.\n/// See @c transformComprehension\nvoid TypecheckVisitor::visit(SetExpr *expr) {\n  expr->setType(instantiateUnbound());\n  auto name = getStdLibType(\"Set\")->name;\n  if ((resultExpr = transformComprehension(name, \"add\", expr->items))) {\n    resultExpr->setAttribute(Attr::ExprSet);\n  }\n}\n\n/// Transform a dictionary `{k1: v1, ..., kN: vN}` to a corresponding statement\n/// expression. See @c transformComprehension\nvoid TypecheckVisitor::visit(DictExpr *expr) {\n  expr->setType(instantiateUnbound());\n  auto name = getStdLibType(\"Dict\")->name;\n  if ((resultExpr = transformComprehension(name, \"__setitem__\", expr->items))) {\n    resultExpr->setAttribute(Attr::ExprDict);\n  }\n}\n\n/// Transform a tuple generator expression.\n/// @example\n///   `tuple(expr for i in tuple_generator)` -> `Tuple.N.__new__(expr...)`\nvoid TypecheckVisitor::visit(GeneratorExpr *expr) {\n  // List comprehension optimization:\n  // Use `iter.__len__()` when creating list if there is a single for loop\n  // without any if conditions in the comprehension\n  bool canOptimize =\n      expr->kind == GeneratorExpr::ListGenerator && expr->loopCount() == 1;\n  if (canOptimize) {\n    auto iter = transform(clone(cast<ForStmt>(expr->getFinalSuite())->getIter()));\n    auto ce = cast<CallExpr>(iter);\n    if (IdExpr *id = nullptr; ce && ((id = cast<IdExpr>(ce->getExpr())))) {\n      // Turn off this optimization for static items\n      canOptimize &= !startswith(id->getValue(), \"std.internal.static\");\n    }\n  }\n\n  Expr *var = N<IdExpr>(getTemporaryVar(\"gen\"));\n  if (expr->kind == GeneratorExpr::ListGenerator) {\n    // List comprehensions\n    expr->setFinalExpr(\n        N<CallExpr>(N<DotExpr>(clone(var), \"append\"), expr->getFinalExpr()));\n    auto suite = expr->getFinalSuite();\n    auto noOptStmt =\n        N<SuiteStmt>(N<AssignStmt>(clone(var), N<CallExpr>(N<IdExpr>(\"List\"))), suite);\n\n    if (canOptimize) {\n      auto optimizeVar = getTemporaryVar(\"i\");\n      auto origIter = cast<ForStmt>(expr->getFinalSuite())->getIter();\n\n      auto optStmt = clone(noOptStmt);\n      cast<ForStmt>((*cast<SuiteStmt>(optStmt))[1])->iter = N<IdExpr>(optimizeVar);\n      optStmt = N<SuiteStmt>(\n          N<AssignStmt>(N<IdExpr>(optimizeVar), clone(origIter)),\n          N<AssignStmt>(\n              clone(var),\n              N<CallExpr>(N<IdExpr>(\"List\"),\n                          N<CallExpr>(N<DotExpr>(N<IdExpr>(optimizeVar), \"__len__\")))),\n          (*cast<SuiteStmt>(optStmt))[1]);\n      resultExpr = N<IfExpr>(\n          N<CallExpr>(N<IdExpr>(\"hasattr\"), clone(origIter), N<StringExpr>(\"__len__\")),\n          N<StmtExpr>(optStmt, clone(var)), N<StmtExpr>(noOptStmt, var));\n    } else {\n      resultExpr = N<StmtExpr>(noOptStmt, var);\n    }\n    resultExpr = transform(resultExpr);\n  } else if (expr->kind == GeneratorExpr::SetGenerator) {\n    // Set comprehensions\n    auto head = N<AssignStmt>(clone(var), N<CallExpr>(N<IdExpr>(\"Set\")));\n    expr->setFinalExpr(\n        N<CallExpr>(N<DotExpr>(clone(var), \"add\"), expr->getFinalExpr()));\n    auto suite = expr->getFinalSuite();\n    resultExpr = transform(N<StmtExpr>(N<SuiteStmt>(head, suite), var));\n  } else if (expr->kind == GeneratorExpr::DictGenerator) {\n    // Dictionary comprehensions\n    auto head = N<AssignStmt>(clone(var), N<CallExpr>(N<IdExpr>(\"Dict\")));\n    expr->setFinalExpr(N<CallExpr>(N<DotExpr>(clone(var), \"__setitem__\"),\n                                   N<StarExpr>(expr->getFinalExpr())));\n    auto suite = expr->getFinalSuite();\n    resultExpr = transform(N<StmtExpr>(N<SuiteStmt>(head, suite), var));\n  } else if (expr->kind == GeneratorExpr::TupleGenerator) {\n    seqassert(expr->loopCount() == 1, \"invalid tuple generator\");\n    auto gen = transform(cast<ForStmt>(expr->getFinalSuite())->getIter());\n    if (!gen->getType()->canRealize())\n      return; // Wait until the iterator can be realized\n\n    auto block = N<SuiteStmt>();\n    // `tuple = tuple_generator`\n    auto tupleVar = getTemporaryVar(\"tuple\");\n    block->addStmt(N<AssignStmt>(N<IdExpr>(tupleVar), gen));\n\n    auto forStmt = clone(cast<ForStmt>(expr->getFinalSuite()));\n    auto finalExpr = expr->getFinalExpr();\n    auto [ok, delay, preamble, staticItems] = transformStaticLoopCall(\n        cast<ForStmt>(expr->getFinalSuite())->getVar(), &forStmt->suite, gen,\n        [&](Stmt *wrap) { return N<StmtExpr>(clone(wrap), clone(finalExpr)); }, true);\n    if (!ok)\n      E(Error::CALL_BAD_ITER, gen, gen->getType()->prettyString());\n    if (delay)\n      return;\n\n    std::vector<Expr *> tupleItems;\n    for (auto &i : staticItems)\n      tupleItems.push_back(cast<Expr>(i));\n    if (preamble)\n      block->addStmt(preamble);\n    resultExpr = transform(N<StmtExpr>(block, N<TupleExpr>(tupleItems)));\n  } else {\n    expr->loops =\n        transform(expr->getFinalSuite()); // assume: internal data will be changed\n    if (!expr->getFinalExpr()) {\n      // Case such as (0 for _ in static.range(2))\n      // TODO: make this better.\n      E(Error::CUSTOM, expr,\n        \"generator cannot be compiled. If using static tuple generator, use tuple(...) \"\n        \"instead.\");\n    }\n    unify(expr->getType(), instantiateType(getStdLibType(\"Generator\"),\n                                           {expr->getFinalExpr()->getType()}));\n    if (realize(expr->getType()))\n      expr->setDone();\n  }\n}\n\n/// Transform a collection of type `type` to a statement expression:\n///   `[a1, ..., aN]` -> `cont = [type](); (cont.[fn](a1); ...); cont`\n/// Any star-expression within the collection will be expanded:\n///   `[a, *b]` -> `cont.[fn](a); for i in b: cont.[fn](i)`.\n/// @example\n///   `[a, *b, c]`  -> ```cont = List(3)\n///                       cont.append(a)\n///                       for i in b: cont.append(i)\n///                       cont.append(c)```\n///   `{a, *b, c}`  -> ```cont = Set()\n///                       cont.add(a)\n///                       for i in b: cont.add(i)\n///                       cont.add(c)```\n///   `{a: 1, **d}` -> ```cont = Dict()\n///                       cont.__setitem__((a, 1))\n///                       for i in b.items(): cont.__setitem__((i[0], i[i]))```\nExpr *TypecheckVisitor::transformComprehension(const std::string &type,\n                                               const std::string &fn,\n                                               std::vector<Expr *> &items) {\n  // Deduce the super type of the collection--- in other words, the least common\n  // ancestor of all types in the collection. For example, `type([1, 1.2]) == type([1.2,\n  // 1]) == float` because float is an \"ancestor\" of int.\n  // TODO: use wrapExpr...\n  auto superTyp = [&](ClassType *collectionCls, ClassType *ti) -> TypePtr {\n    if (!collectionCls)\n      return ti->shared_from_this();\n    if (collectionCls->is(\"int\") && ti->is(\"float\")) {\n      // Rule: int derives from float\n      return ti->shared_from_this();\n    } else if (collectionCls->name != TYPE_OPTIONAL && ti->name == TYPE_OPTIONAL) {\n      // Rule: T derives from Optional[T]\n      return instantiateType(getStdLibType(\"Optional\"),\n                             std::vector<types::Type *>{collectionCls});\n    } else if (collectionCls->name == TYPE_OPTIONAL && ti->name != TYPE_OPTIONAL) {\n      return instantiateType(getStdLibType(\"Optional\"), std::vector<types::Type *>{ti});\n    } else if (!collectionCls->is(\"pyobj\") && ti->is(\"pyobj\")) {\n      // Rule: anything derives from pyobj\n      return ti->shared_from_this();\n    } else if (collectionCls->name != ti->name) {\n      // Rule: subclass derives from superclass\n      const auto &mros = getClass(collectionCls)->mro;\n      for (size_t i = 1; i < mros.size(); i++) {\n        auto t = instantiateType(mros[i].get(), collectionCls);\n        if (t->unify(ti, nullptr) >= 0) {\n          return ti->shared_from_this();\n        }\n      }\n    }\n    return nullptr;\n  };\n\n  TypePtr collectionTyp = instantiateUnbound();\n  bool done = true;\n  bool isDict = type == getStdLibType(\"Dict\")->name;\n  for (auto &i : items) {\n    ClassType *typ = nullptr;\n    if (!isDict && cast<StarExpr>(i)) {\n      auto star = cast<StarExpr>(i);\n      star->expr = transform(N<CallExpr>(N<DotExpr>(star->getExpr(), \"__iter__\")));\n      if (star->getExpr()->getType()->is(\"Generator\"))\n        typ = extractClassGeneric(star->getExpr()->getType())->getClass();\n    } else if (isDict && cast<KeywordStarExpr>(i)) {\n      auto star = cast<KeywordStarExpr>(i);\n      star->expr = transform(N<CallExpr>(N<DotExpr>(star->getExpr(), \"items\")));\n      if (star->getExpr()->getType()->is(\"Generator\"))\n        typ = extractClassGeneric(star->getExpr()->getType())->getClass();\n    } else {\n      i = transform(i);\n      typ = i->getClassType();\n    }\n    if (!typ) {\n      done = false;\n      continue;\n    }\n    if (!collectionTyp->getClass()) {\n      unify(collectionTyp.get(), typ);\n    } else if (!isDict) {\n      if (auto t = superTyp(collectionTyp->getClass(), typ))\n        collectionTyp = t;\n    } else {\n      auto tt = unify(typ, instantiateType(generateTuple(2)))->getClass();\n      seqassert(collectionTyp->getClass() &&\n                    collectionTyp->getClass()->generics.size() == 2 &&\n                    tt->generics.size() == 2,\n                \"bad dict\");\n      std::vector<types::TypePtr> nt;\n      for (int di = 0; di < 2; di++) {\n        nt.push_back(extractClassGeneric(collectionTyp.get(), di)->shared_from_this());\n        if (!nt[di]->getClass())\n          unify(nt[di].get(), extractClassGeneric(tt, di));\n        else if (auto dt = superTyp(nt[di]->getClass(),\n                                    extractClassGeneric(tt, di)->getClass()))\n          nt[di] = dt;\n      }\n      collectionTyp =\n          instantiateType(generateTuple(nt.size()), ctx->cache->castVectorPtr(nt));\n    }\n  }\n  if (!done)\n    return nullptr;\n  std::vector<Stmt *> stmts;\n  Expr *var = N<IdExpr>(getTemporaryVar(\"cont\"));\n\n  std::vector<Expr *> constructorArgs{};\n  if (type == getStdLibType(\"List\")->name && !items.empty()) {\n    // Optimization: pre-allocate the list with the exact number of elements\n    constructorArgs.push_back(N<IntExpr>(items.size()));\n  }\n  auto t = N<IdExpr>(type);\n  auto ta = instantiateType(getStdLibType(type));\n  if (isDict && collectionTyp->getClass()) {\n    seqassert(collectionTyp->getClass()->isRecord(), \"bad dict\");\n    std::vector<types::Type *> nt;\n    for (auto &g : collectionTyp->getClass()->generics)\n      nt.push_back(g.getType());\n    ta = instantiateType(getStdLibType(type), nt);\n  } else if (!isDict) {\n    ta = instantiateType(getStdLibType(type), {collectionTyp.get()});\n  }\n  t->setType(instantiateTypeVar(ta.get()));\n  stmts.push_back(N<AssignStmt>(clone(var), N<CallExpr>(t, constructorArgs)));\n  for (const auto &it : items) {\n    if (!isDict && cast<StarExpr>(it)) {\n      // Unpack star-expression by iterating over it\n      // `*star` -> `for i in star: cont.[fn](i)`\n      auto star = cast<StarExpr>(it);\n      Expr *forVar = N<IdExpr>(getTemporaryVar(\"i\"));\n      star->getExpr()->setAttribute(Attr::ExprStarSequenceItem);\n      stmts.push_back(N<ForStmt>(\n          clone(forVar), star->getExpr(),\n          N<ExprStmt>(N<CallExpr>(N<DotExpr>(clone(var), fn), clone(forVar)))));\n    } else if (isDict && cast<KeywordStarExpr>(it)) {\n      // Expand kwstar-expression by iterating over it: see the example above\n      auto star = cast<KeywordStarExpr>(it);\n      Expr *forVar = N<IdExpr>(getTemporaryVar(\"it\"));\n      star->getExpr()->setAttribute(Attr::ExprStarSequenceItem);\n      stmts.push_back(N<ForStmt>(\n          clone(forVar), star->getExpr(),\n          N<ExprStmt>(N<CallExpr>(N<DotExpr>(clone(var), fn),\n                                  N<IndexExpr>(clone(forVar), N<IntExpr>(0)),\n                                  N<IndexExpr>(clone(forVar), N<IntExpr>(1))))));\n    } else {\n      it->setAttribute(Attr::ExprSequenceItem);\n      if (isDict) {\n        Expr *head = it, *lead = nullptr;\n        if (hasSideEffect(head)) {\n          auto var = getTemporaryVar(\"star\");\n          lead = N<AssignExpr>(N<IdExpr>(var), head);\n          head = N<IdExpr>(var);\n        } else {\n          lead = clone(head);\n        }\n        lead->setAttribute(Attr::ExprSequenceItem);\n        head->setAttribute(Attr::ExprSequenceItem);\n        stmts.push_back(N<ExprStmt>(N<CallExpr>(N<DotExpr>(clone(var), fn),\n                                                N<IndexExpr>(lead, N<IntExpr>(0)),\n                                                N<IndexExpr>(head, N<IntExpr>(1)))));\n      } else {\n        it->setAttribute(Attr::ExprSequenceItem);\n        stmts.push_back(N<ExprStmt>(N<CallExpr>(N<DotExpr>(clone(var), fn), it)));\n      }\n    }\n  }\n  return transform(N<StmtExpr>(stmts, var));\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/cond.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\nusing namespace codon::error;\n\nnamespace codon::ast {\n\nusing namespace types;\n\n/// Call `ready` and `notReady` depending whether the provided static expression can be\n/// evaluated or not.\ntemplate <typename TT, typename TF>\nauto evaluateStaticCondition(Expr *cond, TT ready, TF notReady) {\n  seqassertn(cond->getType()->getStaticKind(), \"not a static condition\");\n  if (cond->getType()->canRealize()) {\n    bool isTrue = false;\n    if (auto as = cond->getType()->getStrStatic())\n      isTrue = !as->value.empty();\n    else if (auto ai = cond->getType()->getIntStatic())\n      isTrue = ai->value;\n    else if (auto ab = cond->getType()->getBoolStatic())\n      isTrue = ab->value;\n    return ready(isTrue);\n  } else {\n    return notReady();\n  }\n}\n\n/// Only allowed in @c MatchStmt\nvoid TypecheckVisitor::visit(RangeExpr *expr) {\n  E(Error::UNEXPECTED_TYPE, expr, \"range\");\n}\n\n/// Typecheck if expressions. Evaluate static if blocks if possible.\n/// Also wrap conditional expressions to match each other. See @c wrapExpr for more\n/// details.\nvoid TypecheckVisitor::visit(IfExpr *expr) {\n  auto oldExpectedType = getStdLibType(\"bool\")->shared_from_this();\n  std::swap(ctx->expectedType, oldExpectedType);\n  expr->cond = transform(expr->getCond());\n  std::swap(ctx->expectedType, oldExpectedType);\n\n  // Static if evaluation\n  if (expr->getCond()->getType()->getStaticKind()) {\n    resultExpr = evaluateStaticCondition(\n        expr->getCond(),\n        [&](bool isTrue) {\n          LOG_TYPECHECK(\"[static::cond] {}: {}\", getSrcInfo(), isTrue);\n          return transform(isTrue ? expr->getIf() : expr->getElse());\n        },\n        [&]() -> Expr * { return nullptr; });\n    if (resultExpr)\n      unify(expr->getType(), resultExpr->getType());\n    else if (expr->getType()->getUnbound())\n      expr->getType()->getUnbound()->staticKind = LiteralKind::Int; // determine later!\n    return;\n  }\n\n  expr->ifexpr = transform(expr->getIf());\n  expr->elsexpr = transform(expr->getElse());\n\n  wrapExpr(&expr->cond, getStdLibType(\"bool\"));\n  // Add wrappers and unify both sides\n  if (expr->getIf()->getType()->getStatic())\n    expr->getIf()->setType(\n        expr->getIf()->getType()->getStatic()->getNonStaticType()->shared_from_this());\n  if (expr->getElse()->getType()->getStatic())\n    expr->getElse()->setType(expr->getElse()\n                                 ->getType()\n                                 ->getStatic()\n                                 ->getNonStaticType()\n                                 ->shared_from_this());\n  wrapExpr(&expr->elsexpr, expr->getIf()->getType(), nullptr, /*allowUnwrap*/ false);\n  wrapExpr(&expr->ifexpr, expr->getElse()->getType(), nullptr, /*allowUnwrap*/ false);\n\n  unify(expr->getType(), expr->getIf()->getType());\n  unify(expr->getType(), expr->getElse()->getType());\n  if (expr->getCond()->isDone() && expr->getIf()->isDone() && expr->getElse()->isDone())\n    expr->setDone();\n}\n\n/// Typecheck if statements. Evaluate static if blocks if possible.\n/// See @c wrapExpr for more details.\nvoid TypecheckVisitor::visit(IfStmt *stmt) {\n  auto oldExpectedType = getStdLibType(\"bool\")->shared_from_this();\n  std::swap(ctx->expectedType, oldExpectedType);\n  stmt->cond = transform(stmt->getCond());\n  std::swap(ctx->expectedType, oldExpectedType);\n\n  // Static if evaluation\n  if (stmt->getCond()->getType()->getStaticKind()) {\n    resultStmt = evaluateStaticCondition(\n        stmt->getCond(),\n        [&](bool isTrue) {\n          LOG_TYPECHECK(\"[static::cond] {}: {}\", getSrcInfo(), isTrue);\n          auto t = transform(isTrue ? stmt->getIf() : stmt->getElse());\n          return t ? t : transform(N<SuiteStmt>());\n        },\n        [&]() -> Stmt * { return nullptr; });\n    return;\n  }\n\n  wrapExpr(&stmt->cond, getStdLibType(\"bool\"));\n  ctx->blockLevel++;\n  stmt->ifSuite = SuiteStmt::wrap(transform(stmt->getIf()));\n  stmt->elseSuite = SuiteStmt::wrap(transform(stmt->getElse()));\n  ctx->blockLevel--;\n\n  if (stmt->cond->isDone() && (!stmt->getIf() || stmt->getIf()->isDone()) &&\n      (!stmt->getElse() || stmt->getElse()->isDone()))\n    stmt->setDone();\n}\n\n/// Simplify match statement by transforming it into a series of conditional statements.\n/// @example\n///   ```match e:\n///        case pattern1: ...\n///        case pattern2 if guard: ...\n///        ...``` ->\n///   ```_match = e\n///      while True:  # used to simulate goto statement with break\n///        [pattern1 transformation]: (...; break)\n///        [pattern2 transformation]: if guard: (...; break)\n///        ...\n///        break  # exit the loop no matter what```\n/// The first pattern that matches the given expression will be used; other patterns\n/// will not be used (i.e., there is no fall-through). See @c transformPattern for\n/// pattern transformations\nvoid TypecheckVisitor::visit(MatchStmt *stmt) {\n  auto var = getTemporaryVar(\"match\");\n  auto result = N<SuiteStmt>();\n  result->addStmt(transform(N<AssignStmt>(N<IdExpr>(var), clone(stmt->getExpr()))));\n  for (auto &c : *stmt) {\n    Stmt *suite = N<SuiteStmt>(c.getSuite(), N<BreakStmt>());\n    if (c.getGuard())\n      suite = N<IfStmt>(c.getGuard(), suite);\n    result->addStmt(transformPattern(N<IdExpr>(var), c.getPattern(), suite));\n  }\n  // Make sure to break even if there is no case _ to prevent infinite loop\n  result->addStmt(N<BreakStmt>());\n  resultStmt = transform(N<WhileStmt>(N<BoolExpr>(true), result));\n}\n\n/// Transform a match pattern into a series of if statements.\n/// @example\n///   `case True`          -> `if isinstance(var, \"bool\"): if var == True`\n///   `case 1`             -> `if isinstance(var, \"int\"): if var == 1`\n///   `case 1...3`         -> ```if isinstance(var, \"int\"):\n///                                if var >= 1: if var <= 3```\n///   `case (1, pat)`      -> ```if isinstance(var, \"Tuple\"): if static.len(var) == 2:\n///                                 if match(var[0], 1): if match(var[1], pat)```\n///   `case [1, ..., pat]` -> ```if isinstance(var, \"List\"): if len(var) >= 2:\n///                                 if match(var[0], 1): if match(var[-1], pat)```\n///   `case 1 or pat`      -> `if match(var, 1): if match(var, pat)`\n///                           (note: pattern suite is cloned for each `or`)\n///   `case (x := pat)`    -> `(x := var; if match(var, pat))`\n///   `case x`             -> `(x := var)`\n///                           (only when `x` is not '_')\n///   `case expr`          -> `if hasattr(typeof(var), \"__match__\"): if\n///   var.__match__(foo())`\n///                           (any expression that does not fit above patterns)\nStmt *TypecheckVisitor::transformPattern(Expr *var, Expr *pattern, Stmt *suite) {\n  // Convenience function to generate `isinstance(e, typ)` calls\n  auto isinstance = [&](Expr *e, const std::string &typ) -> Expr * {\n    return N<CallExpr>(N<IdExpr>(\"isinstance\"), clone(e), N<IdExpr>(typ));\n  };\n  // Convenience function to find the index of an ellipsis within a list pattern\n  auto findEllipsis = [&](const std::vector<Expr *> &items) {\n    size_t i = items.size();\n    for (auto it = 0; it < items.size(); it++)\n      if (cast<EllipsisExpr>(items[it])) {\n        if (i != items.size())\n          E(Error::MATCH_MULTI_ELLIPSIS, items[it], \"multiple ellipses in pattern\");\n        i = it;\n      }\n    return i;\n  };\n\n  // See the above examples for transformation details\n  if (cast<IntExpr>(pattern) || cast<BoolExpr>(pattern)) {\n    // Bool and int patterns\n    return N<IfStmt>(isinstance(var, cast<BoolExpr>(pattern) ? \"bool\" : \"int\"),\n                     N<IfStmt>(N<BinaryExpr>(var, \"==\", pattern), suite));\n  } else if (auto er = cast<RangeExpr>(pattern)) {\n    // Range pattern\n    return N<IfStmt>(\n        isinstance(var, \"int\"),\n        N<IfStmt>(N<BinaryExpr>(var, \">=\", er->start),\n                  N<IfStmt>(N<BinaryExpr>(clone(var), \"<=\", er->stop), suite)));\n  } else if (auto et = cast<TupleExpr>(pattern)) {\n    // Tuple pattern\n    for (auto it = et->items.size(); it-- > 0;) {\n      suite =\n          transformPattern(N<IndexExpr>(clone(var), N<IntExpr>(it)), (*et)[it], suite);\n    }\n    return N<IfStmt>(\n        isinstance(var, \"Tuple\"),\n        N<IfStmt>(N<BinaryExpr>(\n                      N<CallExpr>(\n                          N<IdExpr>(getMangledFunc(\"std.internal.static\", \"len\")), var),\n                      \"==\", N<IntExpr>(et->size())),\n                  suite));\n  } else if (auto el = cast<ListExpr>(pattern)) {\n    // List pattern\n    size_t ellipsis = findEllipsis(el->items), sz = el->size();\n    std::string op;\n    if (ellipsis == el->size()) {\n      op = \"==\";\n    } else {\n      op = \">=\", sz -= 1;\n    }\n    for (auto it = el->size(); it-- > ellipsis + 1;) {\n      suite = transformPattern(N<IndexExpr>(clone(var), N<IntExpr>(it - el->size())),\n                               (*el)[it], suite);\n    }\n    for (auto it = ellipsis; it-- > 0;) {\n      suite =\n          transformPattern(N<IndexExpr>(clone(var), N<IntExpr>(it)), (*el)[it], suite);\n    }\n    return N<IfStmt>(\n        isinstance(var, \"List\"),\n        N<IfStmt>(N<BinaryExpr>(N<CallExpr>(N<IdExpr>(\"len\"), var), op, N<IntExpr>(sz)),\n                  suite));\n  } else if (auto eb = cast<BinaryExpr>(pattern)) {\n    // Or pattern\n    if (eb->op == \"|\" || eb->op == \"||\") {\n      return N<SuiteStmt>(transformPattern(clone(var), eb->lexpr, clone(suite)),\n                          transformPattern(var, eb->rexpr, suite));\n    }\n  } else if (auto ei = cast<IdExpr>(pattern)) {\n    // Wildcard pattern\n    if (ei->value != \"_\") {\n      return N<SuiteStmt>(N<AssignStmt>(pattern, var), suite);\n    } else {\n      return suite;\n    }\n  } else if (auto ea = cast<AssignExpr>(pattern)) {\n    // Bound pattern\n    seqassert(cast<IdExpr>(ea->getVar()),\n              \"only simple assignment expressions are supported\");\n    return N<SuiteStmt>(N<AssignStmt>(ea->getVar(), clone(var)),\n                        transformPattern(var, ea->getExpr(), suite));\n  }\n  pattern = transform(pattern); // transform to check for pattern errors\n  if (cast<EllipsisExpr>(pattern))\n    pattern = N<CallExpr>(N<IdExpr>(\"ellipsis\"));\n  // Fallback (`__match__`) pattern\n  auto p =\n      N<IfStmt>(N<CallExpr>(N<IdExpr>(\"hasattr\"), clone(var),\n                            N<StringExpr>(\"__match__\"), clone(pattern)),\n                N<IfStmt>(N<CallExpr>(N<DotExpr>(var, \"__match__\"), pattern), suite));\n  return p;\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/ctx.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"ctx.h\"\n\n#include <map>\n#include <memory>\n#include <unordered_map>\n#include <vector>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/visitors/format/format.h\"\n#include \"codon/parser/visitors/scoping/scoping.h\"\n\nusing namespace codon::error;\n\nnamespace codon::ast {\n\nTypecheckItem::TypecheckItem(std::string canonicalName, std::string baseName,\n                             std::string moduleName, types::TypePtr type,\n                             std::vector<int> scope)\n    : canonicalName(std::move(canonicalName)), baseName(std::move(baseName)),\n      moduleName(std::move(moduleName)), type(std::move(type)),\n      scope(std::move(scope)) {}\n\nTypeContext::TypeContext(Cache *cache, std::string filename)\n    : Context<TypecheckItem>(std::move(filename)), cache(cache) {\n  bases.emplace_back();\n  scope.emplace_back(0);\n  auto e = cache->N<NoneExpr>();\n  e->setSrcInfo(cache->generateSrcInfo());\n  pushNode(e); // Always have srcInfo() around\n}\n\nvoid TypeContext::add(const std::string &name, const TypeContext::Item &var) {\n  seqassert(!var->scope.empty(), \"bad scope for '{}'\", name);\n  Context<TypecheckItem>::add(name, var);\n}\n\nvoid TypeContext::removeFromMap(const std::string &name) {\n  Context<TypecheckItem>::removeFromMap(name);\n}\n\nTypeContext::Item TypeContext::addVar(const std::string &name,\n                                      const std::string &canonicalName,\n                                      const types::TypePtr &type, int64_t time,\n                                      const SrcInfo &srcInfo) {\n  seqassert(!canonicalName.empty(), \"empty canonical name for '{}'\", name);\n  // seqassert(type->getLink(), \"bad var\");\n  auto t = std::make_shared<TypecheckItem>(canonicalName, getBaseName(), getModule(),\n                                           type, getScope());\n  t->setSrcInfo(srcInfo);\n  t->time = time;\n  add(name, t);\n  addAlwaysVisible(t);\n  return t;\n}\n\nTypeContext::Item TypeContext::addType(const std::string &name,\n                                       const std::string &canonicalName,\n                                       const types::TypePtr &type,\n                                       const SrcInfo &srcInfo) {\n  seqassert(!canonicalName.empty(), \"empty canonical name for '{}'\", name);\n  // seqassert(type->getClass(), \"bad type\");\n  auto t = std::make_shared<TypecheckItem>(canonicalName, getBaseName(), getModule(),\n                                           type, getScope());\n  t->setSrcInfo(srcInfo);\n  add(name, t);\n  addAlwaysVisible(t);\n  return t;\n}\n\nTypeContext::Item TypeContext::addFunc(const std::string &name,\n                                       const std::string &canonicalName,\n                                       const types::TypePtr &type,\n                                       const SrcInfo &srcInfo) {\n  seqassert(!canonicalName.empty(), \"empty canonical name for '{}'\", name);\n  seqassert(type->getFunc(), \"bad func\");\n  auto t = std::make_shared<TypecheckItem>(canonicalName, getBaseName(), getModule(),\n                                           type, getScope());\n  t->setSrcInfo(srcInfo);\n  add(name, t);\n  addAlwaysVisible(t);\n  return t;\n}\n\nTypeContext::Item TypeContext::addAlwaysVisible(const TypeContext::Item &item,\n                                                bool pop) {\n  add(item->canonicalName, item);\n  if (pop)\n    stack.front().pop_back(); // do not remove it later!\n  if (!cache->typeCtx->Context<TypecheckItem>::find(item->canonicalName)) {\n    cache->typeCtx->add(item->canonicalName, item);\n    if (pop)\n      cache->typeCtx->stack.front().pop_back(); // do not remove it later!\n\n    // Realizations etc.\n    if (!in(cache->reverseIdentifierLookup, item->canonicalName))\n      cache->reverseIdentifierLookup[item->canonicalName] = item->canonicalName;\n  }\n  return item;\n}\n\nTypeContext::Item TypeContext::find(const std::string &name, int64_t time,\n                                    const char *inBase) const {\n  auto it = map.find(name);\n  bool isMangled = in(name, \".\");\n  std::string base = inBase ? inBase : getBaseName();\n  if (it != map.end()) {\n    for (auto &i : it->second) {\n      if (!isMangled && !startswith(base, i->getBaseName())) {\n        continue; // avoid middle realizations\n      }\n      if (isMangled || i->getBaseName() != base || !time ||\n          i->getModule() != getModule()) {\n        return i;\n      } else {\n        if (i->getTime() <= time)\n          return i;\n      }\n    }\n  }\n\n  // Item is not found in the current module. Time to look in the standard library!\n  // Note: the standard library items cannot be dominated.\n  TypeContext::Item t = nullptr;\n  auto stdlib = cache->imports[STDLIB_IMPORT].ctx;\n  if (stdlib.get() != this)\n    t = stdlib->Context<TypecheckItem>::find(name);\n\n  // Maybe we are looking for a canonical identifier?\n  if (!t && cache->typeCtx.get() != this)\n    t = cache->typeCtx->Context<TypecheckItem>::find(name);\n\n  return t;\n}\n\nTypeContext::Item TypeContext::forceFind(const std::string &name) const {\n  auto f = find(name);\n  seqassert(f, \"cannot find '{}'\", name);\n  return f;\n}\n\n/// Getters and setters\n\nstd::string TypeContext::getBaseName() const { return bases.back().name; }\n\nstd::string TypeContext::getModule() const {\n  std::string base = moduleName.status == ImportFile::STDLIB ? \"std.\" : \"\";\n  base += moduleName.module;\n  if (auto sz = startswith(base, \"__main__\"))\n    base = base.substr(sz);\n  return base;\n}\n\nstd::string TypeContext::getModulePath() const { return moduleName.path; }\n\nvoid TypeContext::dump() { dump(0); }\n\nstd::string TypeContext::generateCanonicalName(const std::string &name,\n                                               bool includeBase, bool noSuffix) const {\n  std::string newName = name;\n  if (name.find('.') != std::string::npos)\n    return name;\n  includeBase &= !(!name.empty() && name[0] == '%');\n  if (includeBase) {\n    std::string base = getBaseName();\n    if (base.empty())\n      base = getModule();\n    if (base == \"std.internal.core\") {\n      noSuffix = true;\n      base = \"\";\n    }\n    newName = (base.empty() ? \"\" : (base + \".\")) + newName;\n  }\n  auto num = cache->identifierCount[newName]++;\n  if (!noSuffix)\n    newName = fmt::format(\"{}.{}\", newName, num);\n  if (name != newName)\n    cache->identifierCount[newName]++;\n  cache->reverseIdentifierLookup[newName] = name;\n  return newName;\n}\n\nbool TypeContext::isGlobal() const { return bases.size() == 1; }\n\nbool TypeContext::isConditional() const { return scope.size() > 1; }\n\nTypeContext::Base *TypeContext::getBase() {\n  return bases.empty() ? nullptr : &(bases.back());\n}\n\nbool TypeContext::inFunction() const { return !isGlobal() && !bases.back().isType(); }\n\nbool TypeContext::inClass() const { return !isGlobal() && bases.back().isType(); }\n\nbool TypeContext::isOuter(const Item &val) const {\n  return getBaseName() != val->getBaseName() || getModule() != val->getModule();\n}\n\nTypeContext::Base *TypeContext::getClassBase() {\n  if (bases.size() >= 2 && bases[bases.size() - 2].isType())\n    return &(bases[bases.size() - 2]);\n  return nullptr;\n}\n\nsize_t TypeContext::getRealizationDepth() const { return bases.size(); }\n\nstd::string TypeContext::getRealizationStackName() const {\n  if (bases.empty())\n    return \"\";\n  std::vector<std::string> s;\n  for (auto &b : bases)\n    if (b.type)\n      s.push_back(b.type->realizedName());\n  return join(s, \":\");\n}\n\nvoid TypeContext::dump(int pad) {\n  auto ordered =\n      std::map<std::string, decltype(map)::mapped_type>(map.begin(), map.end());\n  LOG(\"current module: {} ({})\", moduleName.module, moduleName.path);\n  LOG(\"current base:   {} / {}\", getRealizationStackName(), getBase()->name);\n  for (auto &i : ordered) {\n    std::string s;\n    auto t = i.second.front();\n    LOG(\"{}{:.<25}\", std::string(static_cast<size_t>(pad) * 2, ' '), i.first);\n    LOG(\"   ... kind:      {}\", t->isType() * 100 + t->isFunc() * 10 + t->isVar());\n    LOG(\"   ... canonical: {}\", t->canonicalName);\n    LOG(\"   ... base:      {}\", t->baseName);\n    LOG(\"   ... module:    {}\", t->moduleName);\n    LOG(\"   ... type:      {}\", t->type ? t->type->debugString(2) : \"<null>\");\n    LOG(\"   ... scope:     {}\", t->scope);\n    LOG(\"   ... gnrc/sttc: {} / {}\", t->generic, static_cast<int>(t->getStaticKind()));\n  }\n}\n\nstd::string TypeContext::debugInfo() {\n  return fmt::format(\"[{}:i{}@{}]\", getBase()->name, getBase()->iteration,\n                     getSrcInfo());\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/ctx.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <memory>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/ctx.h\"\n\nnamespace codon::ast {\n\nclass TypecheckVisitor;\n\n/**\n * Typecheck context identifier.\n * Can be either a function, a class (type), or a variable.\n */\nstruct TypecheckItem : public SrcObject {\n  /// Unique identifier (canonical name)\n  std::string canonicalName;\n  /// Base name (e.g., foo.bar.baz)\n  std::string baseName;\n  /// Full module name\n  std::string moduleName;\n  /// Type\n  types::TypePtr type = nullptr;\n\n  /// Full base scope information\n  std::vector<int> scope = {0};\n  /// Specifies at which time the name was added to the context.\n  /// Used to prevent using later definitions early (can happen in\n  /// advanced type checking iterations).\n  int64_t time = 0;\n\n  /// Set if an identifier is a class or a function generic\n  bool generic = false;\n\n  TypecheckItem(std::string, std::string, std::string, types::TypePtr,\n                std::vector<int> = {0});\n\n  /* Convenience getters */\n  std::string getBaseName() const { return baseName; }\n  std::string getModule() const { return moduleName; }\n  bool isVar() const { return !generic && !isFunc() && !isType(); }\n  bool isFunc() const { return type->getFunc() != nullptr; }\n  bool isType() const { return type->is(TYPE_TYPE); }\n\n  bool isGlobal() const { return scope.size() == 1 && baseName.empty(); }\n  /// True if an identifier is within a conditional block\n  /// (i.e., a block that might not be executed during the runtime)\n  bool isConditional() const { return scope.size() > 1; }\n  bool isGeneric() const { return generic; }\n  types::LiteralKind getStaticKind() const { return type->getStaticKind(); }\n\n  types::Type *getType() const { return type.get(); }\n  std::string getName() const { return canonicalName; }\n\n  int64_t getTime() const { return time; }\n};\n\n/** Context class that tracks identifiers during the typechecking. **/\nstruct TypeContext : public Context<TypecheckItem> {\n  /// A pointer to the shared cache.\n  Cache *cache;\n\n  /// Holds the information about current scope.\n  /// A scope is defined as a stack of conditional blocks\n  /// (i.e., blocks that might not get executed during the runtime).\n  /// Used mainly to support Python's variable scoping rules.\n  struct ScopeBlock {\n    int id;\n    std::unordered_map<std::string, std::pair<std::string, bool>> replacements;\n    /// List of statements that are to be prepended to a block\n    /// after its transformation.\n    std::vector<Stmt *> stmts;\n    ScopeBlock(int id) : id(id) {}\n  };\n  /// Current hierarchy of conditional blocks.\n  std::vector<ScopeBlock> scope;\n  std::vector<int> getScope() const {\n    std::vector<int> result;\n    result.reserve(scope.size());\n    for (const auto &b : scope)\n      result.emplace_back(b.id);\n    return result;\n  }\n\n  /// Holds the information about current base.\n  /// A base is defined as a function or a class block.\n  struct Base {\n    /// Canonical name of a function or a class that owns this base.\n    std::string name;\n    /// Function type\n    types::TypePtr type;\n    /// The return type of currently realized function\n    types::TypePtr returnType;\n    /// Typechecking iteration\n    int iteration = 0;\n    /// Only set for functions.\n    FunctionStmt *func = nullptr;\n    Stmt *suite = nullptr;\n    /// Index of the parent base\n    int parent = 0;\n\n    struct {\n      /// Set if the base is class base and if class is marked with @deduce.\n      /// Stores the list of class fields in the order of traversal.\n      std::shared_ptr<std::vector<std::string>> deducedMembers = nullptr;\n      /// Canonical name of `self` parameter that is used to deduce class fields\n      /// (e.g., self in self.foo).\n      std::string selfName;\n    } deduce;\n\n    /// Map of captured identifiers (i.e., identifiers not defined in a function).\n    /// Captured (canonical) identifiers are mapped to the new canonical names\n    /// (representing the canonical function argument names that are appended to the\n    /// function after processing) and their types (indicating if they are a type, a\n    /// static or a variable).\n    // std::unordered_set<std::string> captures;\n\n    /// Map of identifiers that are to be fetched from Python.\n    std::unordered_set<std::string> *pyCaptures = nullptr;\n\n    // /// Scope that defines the base.\n    // std::vector<int> scope;\n\n    /// A stack of nested loops enclosing the current statement used for transforming\n    /// \"break\" statement in loop-else constructs. Each loop is defined by a \"break\"\n    /// variable created while parsing a loop-else construct. If a loop has no else\n    /// block, the corresponding loop variable is empty.\n    struct Loop {\n      std::string breakVar;\n      /// False if a loop has continue/break statement. Used for flattening static\n      /// loops.\n      bool flat = true;\n      Loop(const std::string &breakVar) : breakVar(breakVar) {}\n    };\n    std::vector<Loop> loops;\n\n    std::map<int, std::set<types::TypePtr>> pendingDefaults;\n\n  public:\n    Loop *getLoop() { return loops.empty() ? nullptr : &(loops.back()); }\n    bool isType() const { return func == nullptr; }\n  };\n  /// Current base stack (the last enclosing base is the last base in the stack).\n  std::vector<Base> bases;\n\n  struct BaseGuard {\n    TypeContext *holder;\n    BaseGuard(TypeContext *holder, const std::string &name) : holder(holder) {\n      holder->bases.emplace_back();\n      holder->bases.back().name = name;\n      holder->addBlock();\n    }\n    ~BaseGuard() {\n      holder->bases.pop_back();\n      holder->popBlock();\n    }\n  };\n\n  /// Current module. The default module is named `__main__`.\n  ImportFile moduleName = {ImportFile::PACKAGE, \"\", \"\"};\n  /// Set if the standard library is currently being loaded.\n  bool isStdlibLoading = false;\n\n  /// The current type-checking level (for type instantiation and generalization).\n  int typecheckLevel = 0;\n  int changedNodes = 0;\n\n  /// Number of nested realizations. Used to prevent infinite instantiations.\n  int realizationDepth = 0;\n\n  /// Number of nested blocks (0 for toplevel)\n  int blockLevel = 0;\n  /// True if an early return is found (anything afterwards won't be typechecked)\n  bool returnEarly = false;\n  /// Stack of static loop control variables (used to emulate goto statements).\n  std::vector<std::string> staticLoops = {};\n\n  /// Current statement time.\n  int64_t time = 0;\n\n  /// @brief  Type to be expected upon completed typechecking.\n  types::TypePtr expectedType = nullptr;\n\n  bool autoPython = false;\n  /// True if no side-effects allowed\n  bool simpleTypes = false;\n\n  std::unordered_map<std::string, size_t> globalShadows;\n\n  std::unordered_map<std::string, double> _itime;\n\npublic:\n  explicit TypeContext(Cache *cache, std::string filename = \"\");\n\n  void add(const std::string &name, const Item &var) override;\n  /// Convenience method for adding an object to the context.\n  Item addVar(const std::string &name, const std::string &canonicalName,\n              const types::TypePtr &type, int64_t time = 0,\n              const SrcInfo &srcInfo = SrcInfo());\n  Item addType(const std::string &name, const std::string &canonicalName,\n               const types::TypePtr &type, const SrcInfo &srcInfo = SrcInfo());\n  Item addFunc(const std::string &name, const std::string &canonicalName,\n               const types::TypePtr &type, const SrcInfo &srcInfo = SrcInfo());\n  /// Add the item to the standard library module, thus ensuring its visibility from all\n  /// modules.\n  Item addAlwaysVisible(const Item &item, bool = false);\n\n  /// Get an item from the context before given srcInfo. If the item does not exist,\n  /// nullptr is returned.\n  Item find(const std::string &name, int64_t time = 0, const char * = nullptr) const;\n  /// Get an item that exists in the context. If the item does not exist, assertion is\n  /// raised.\n  Item forceFind(const std::string &name) const;\n\n  /// Return a canonical name of the current base.\n  /// An empty string represents the toplevel base.\n  std::string getBaseName() const;\n  /// Return the current module.\n  std::string getModule() const;\n  /// Return the current module path.\n  std::string getModulePath() const;\n  /// Pretty-print the current context state.\n  void dump() override;\n\n  /// Generate a unique identifier (name) for a given string.\n  std::string generateCanonicalName(const std::string &name, bool includeBase = false,\n                                    bool noSuffix = false) const;\n  /// True if we are at the toplevel.\n  bool isGlobal() const;\n  /// True if we are within a conditional block.\n  bool isConditional() const;\n  /// Get the current base.\n  Base *getBase();\n  /// True if the current base is function.\n  bool inFunction() const;\n  /// True if the current base is class.\n  bool inClass() const;\n  /// True if an item is defined outside of the current base or a module.\n  bool isOuter(const Item &val) const;\n  /// Get the enclosing class base (or nullptr if such does not exist).\n  Base *getClassBase();\n\n  /// Convenience method for adding an object to the context.\n  std::shared_ptr<TypecheckItem>\n  addToplevel(const std::string &name, const std::shared_ptr<TypecheckItem> &item) {\n    map[name].push_front(item);\n    return item;\n  }\n\npublic:\n  /// Get the current realization depth (i.e., the number of nested realizations).\n  size_t getRealizationDepth() const;\n  /// Get the name of the current realization stack (e.g., `fn1:fn2:...`).\n  std::string getRealizationStackName() const;\n\nprivate:\n  /// Pretty-print the current context state.\n  void dump(int pad);\n  /// Pretty-print the current realization context.\n  std::string debugInfo();\n\nprotected:\n  void removeFromMap(const std::string &name) override;\n};\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/error.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/match.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nnamespace codon::ast {\n\nusing namespace types;\nusing namespace matcher;\nusing namespace error;\n\n/// Transform asserts.\n/// @example\n///   `assert foo()` ->\n///   `if not foo(): raise __internal__.seq_assert([file], [line], \"\")`\n///   `assert foo(), msg` ->\n///   `if not foo(): raise __internal__.seq_assert([file], [line], str(msg))`\n/// Use `seq_assert_test` instead of `seq_assert` and do not raise anything during unit\n/// testing (i.e., when the enclosing function is marked with `@test`).\nvoid TypecheckVisitor::visit(AssertStmt *stmt) {\n  Expr *msg = N<StringExpr>(\"\");\n  if (stmt->getMessage())\n    msg = N<CallExpr>(N<IdExpr>(\"str\"), stmt->getMessage());\n  auto test = ctx->inFunction() && ctx->getBase()->func->hasAttribute(Attr::Test);\n  auto ex = N<CallExpr>(\n      N<DotExpr>(N<IdExpr>(\"__internal__\"), test ? \"seq_assert_test\" : \"seq_assert\"),\n      N<StringExpr>(stmt->getSrcInfo().file), N<IntExpr>(stmt->getSrcInfo().line), msg);\n  auto cond = N<UnaryExpr>(\"!\", stmt->getExpr());\n  if (test) {\n    resultStmt = transform(N<IfStmt>(cond, N<ExprStmt>(ex)));\n  } else {\n    resultStmt = transform(N<IfStmt>(cond, N<ThrowStmt>(ex)));\n  }\n}\n\n/// Typecheck try-except statements. Handle Python exceptions separately.\n/// @example\n///   ```try: ...\n///      except python.Error as e: ...\n///      except PyExc as f: ...\n///      except ValueError as g: ...\n///   ``` -> ```\n///      try: ...\n///      except ValueError as g: ...                   # ValueError\n///      except PyExc as exc:\n///        while True:\n///          if isinstance(exc.pytype, python.Error):  # python.Error\n///            e = exc.pytype; ...; break\n///          f = exc; ...; break                       # PyExc\n///          raise```\nvoid TypecheckVisitor::visit(TryStmt *stmt) {\n  ctx->blockLevel++;\n  stmt->suite = SuiteStmt::wrap(transform(stmt->getSuite()));\n  ctx->blockLevel--;\n\n  std::vector<ExceptStmt *> catches;\n  auto pyCatchStmt = N<WhileStmt>(N<BoolExpr>(true), N<SuiteStmt>());\n\n  auto done = stmt->getSuite()->isDone();\n  for (auto &c : *stmt) {\n    TypeContext::Item val = nullptr;\n    if (!c->getVar().empty()) {\n      if (!c->hasAttribute(Attr::ExprDominated) &&\n          !c->hasAttribute(Attr::ExprDominatedUsed)) {\n        val = ctx->addVar(getUnmangledName(c->getVar()),\n                          ctx->generateCanonicalName(c->getVar()), instantiateUnbound(),\n                          getTime());\n      } else if (c->hasAttribute(Attr::ExprDominatedUsed)) {\n        val = ctx->forceFind(c->getVar());\n        c->eraseAttribute(Attr::ExprDominatedUsed);\n        c->setAttribute(Attr::ExprDominated);\n        c->suite = N<SuiteStmt>(\n            N<AssignStmt>(N<IdExpr>(fmt::format(\"{}{}\", getUnmangledName(c->getVar()),\n                                                VAR_USED_SUFFIX)),\n                          N<BoolExpr>(true), nullptr, AssignStmt::UpdateMode::Update),\n            c->getSuite());\n      } else {\n        val = ctx->forceFind(c->getVar());\n      }\n      c->var = val->canonicalName;\n    }\n    c->exc = transform(c->getException());\n    if (c->getException() && extractClassType(c->getException())->is(\"pyobj\")) {\n      // Transform python.Error exceptions\n\n      if (!stmt->hasAttribute(Attr::TryPyVar))\n        stmt->setAttribute(Attr::TryPyVar, getTemporaryVar(\"pyexc\"));\n      auto pyVar = stmt->getAttribute<ir::StringValueAttribute>(Attr::TryPyVar)->value;\n\n      if (!c->var.empty()) {\n        c->suite = N<SuiteStmt>(\n            N<AssignStmt>(N<IdExpr>(c->var), N<DotExpr>(N<IdExpr>(pyVar), \"pytype\")),\n            c->getSuite());\n      }\n      c->suite = SuiteStmt::wrap(N<IfStmt>(\n          N<CallExpr>(N<IdExpr>(\"isinstance\"), N<DotExpr>(N<IdExpr>(pyVar), \"pytype\"),\n                      c->getException()),\n          N<SuiteStmt>(c->getSuite(), N<BreakStmt>()), nullptr));\n      cast<SuiteStmt>(pyCatchStmt->getSuite())->addStmt(c->getSuite());\n    } else if (c->getException() &&\n               extractClassType(c->getException())\n                   ->is(getMangledClass(\"std.internal.python\", \"PyError\"))) {\n      // Transform PyExc exceptions\n      if (!stmt->hasAttribute(Attr::TryPyVar))\n        stmt->setAttribute(Attr::TryPyVar, getTemporaryVar(\"pyexc\"));\n      auto pyVar = stmt->getAttribute<ir::StringValueAttribute>(Attr::TryPyVar)->value;\n\n      if (!c->var.empty()) {\n        c->suite = N<SuiteStmt>(N<AssignStmt>(N<IdExpr>(c->var), N<IdExpr>(pyVar)),\n                                c->getSuite());\n      }\n      c->suite = N<SuiteStmt>(c->getSuite(), N<BreakStmt>());\n      cast<SuiteStmt>(pyCatchStmt->getSuite())->addStmt(c->getSuite());\n    } else {\n      // Handle all other exceptions\n      c->exc = transformType(c->getException());\n\n      if (c->getException()) {\n        auto t = extractClassType(c->getException());\n        bool exceptionOK = false;\n        for (auto &p : getRTTISuperTypes(t))\n          if (p->is(getMangledClass(\"std.internal.types.error\", \"BaseException\"))) {\n            exceptionOK = true;\n            break;\n          }\n        if (!exceptionOK)\n          E(Error::CATCH_EXCEPTION_TYPE, c->getException(), t->prettyString());\n        if (val)\n          unify(val->getType(), extractType(c->getException()));\n      }\n      ctx->blockLevel++;\n      c->suite = SuiteStmt::wrap(transform(c->getSuite()));\n      ctx->blockLevel--;\n      done &= (!c->getException() || c->getException()->isDone()) &&\n              c->getSuite()->isDone();\n      catches.push_back(c);\n    }\n  }\n  if (!cast<SuiteStmt>(pyCatchStmt->getSuite())->empty()) {\n    // Process PyError catches\n    auto pyVar = stmt->getAttribute<ir::StringValueAttribute>(Attr::TryPyVar)->value;\n    auto exc = N<IdExpr>(getMangledClass(\"std.internal.python\", \"PyError\"));\n    cast<SuiteStmt>(pyCatchStmt->getSuite())->addStmt(N<ThrowStmt>(nullptr));\n    auto c = N<ExceptStmt>(pyVar, transformType(exc), pyCatchStmt);\n\n    auto val = ctx->addVar(\n        pyVar, pyVar, extractType(c->getException())->shared_from_this(), getTime());\n    ctx->blockLevel++;\n    c->suite = SuiteStmt::wrap(transform(c->getSuite()));\n    ctx->blockLevel--;\n    done &= (!c->exc || c->exc->isDone()) && c->getSuite()->isDone();\n    catches.push_back(c);\n  }\n  stmt->items = catches;\n\n  if (stmt->getElse()) {\n    ctx->blockLevel++;\n    stmt->elseSuite = SuiteStmt::wrap(transform(stmt->getElse()));\n    ctx->blockLevel--;\n    done &= stmt->getElse()->isDone();\n  }\n\n  if (stmt->getFinally()) {\n    ctx->blockLevel++;\n    stmt->finally = SuiteStmt::wrap(transform(stmt->getFinally()));\n    ctx->blockLevel--;\n    done &= stmt->getFinally()->isDone();\n  }\n\n  if (done)\n    stmt->setDone();\n}\n\n/// Transform `raise` statements.\n/// @example\n///   `raise exc` -> ```raise BaseException.set_header(exc, \"fn\", \"file\", line, col)```\nvoid TypecheckVisitor::visit(ThrowStmt *stmt) {\n  if (!stmt->expr) {\n    stmt->setDone();\n    return;\n  }\n\n  stmt->expr = transform(stmt->getExpr());\n  if (!match(stmt->getExpr(),\n             M<CallExpr>(M<IdExpr>(getMangledMethod(\"std.internal.types.error\",\n                                                    \"BaseException\", \"_set_header\")),\n                         M_))) {\n    stmt->expr = transform(N<CallExpr>(\n        N<IdExpr>(getMangledMethod(\"std.internal.types.error\", \"BaseException\",\n                                   \"_set_header\")),\n        stmt->getExpr(), N<StringExpr>(ctx->getBase()->name),\n        N<StringExpr>(stmt->getSrcInfo().file), N<IntExpr>(stmt->getSrcInfo().line),\n        N<IntExpr>(stmt->getSrcInfo().col),\n        stmt->getFrom()\n            ? N<CallExpr>(N<DotExpr>(N<IdExpr>(\"Super\"), \"_super\"), stmt->getFrom(),\n                          N<IdExpr>(getMangledClass(\"std.internal.types.error\",\n                                                    \"BaseException\")))\n            : N<CallExpr>(N<IdExpr>(\"NoneType\"))));\n  }\n  if (stmt->getExpr()->isDone())\n    stmt->setDone();\n}\n\n/// Transform with statements.\n/// @example\n///   `with foo(), bar() as a: ...` ->\n///   ```tmp = foo()\n///      tmp.__enter__()\n///      try:\n///        a = bar()\n///        a.__enter__()\n///        try:\n///          ...\n///        finally:\n///          a.__exit__()\n///      finally:\n///        tmp.__exit__()```\nvoid TypecheckVisitor::visit(WithStmt *stmt) {\n  seqassert(!stmt->empty(), \"stmt->items is empty\");\n\n  bool isAsync = stmt->isAsync();\n\n  std::vector<Stmt *> content;\n  for (auto i = stmt->items.size(); i-- > 0;) {\n    std::string var = stmt->vars[i].empty() ? getTemporaryVar(\"with\") : stmt->vars[i];\n    auto as = N<AssignStmt>(N<IdExpr>(var), (*stmt)[i], nullptr,\n                            (*stmt)[i]->hasAttribute(Attr::ExprDominated)\n                                ? AssignStmt::UpdateMode::Update\n                                : AssignStmt::UpdateMode::Assign);\n\n    Expr *enter =\n        N<CallExpr>(N<DotExpr>(N<IdExpr>(var), isAsync ? \"__aenter__\" : \"__enter__\"));\n    Expr *exit =\n        N<CallExpr>(N<DotExpr>(N<IdExpr>(var), isAsync ? \"__aexit__\" : \"__exit__\"));\n    if (isAsync) {\n      enter = N<AwaitExpr>(enter);\n      exit = N<AwaitExpr>(exit);\n    }\n    content = std::vector<Stmt *>{\n        as, N<ExprStmt>(enter),\n        N<TryStmt>(!content.empty() ? N<SuiteStmt>(content) : clone(stmt->getSuite()),\n                   std::vector<ExceptStmt *>{}, nullptr,\n                   N<SuiteStmt>(N<ExprStmt>(exit)))};\n  }\n  resultStmt = transform(N<SuiteStmt>(content));\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/function.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <algorithm>\n#include <string>\n#include <tuple>\n\n#include \"codon/cir/attribute.h\"\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/match.h\"\n#include \"codon/parser/peg/peg.h\"\n#include \"codon/parser/visitors/scoping/scoping.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nusing namespace codon::error;\n\nnamespace codon::ast {\n\nusing namespace types;\nusing namespace matcher;\n\n/// Unify the function return type with `Generator[?]`.\n/// The unbound type will be deduced from return/yield statements.\nvoid TypecheckVisitor::visit(LambdaExpr *expr) {\n  std::vector<Param> params;\n  std::string name = getTemporaryVar(\"lambda\");\n  params.reserve(expr->size());\n  for (auto &s : *expr)\n    params.emplace_back(s);\n  Stmt *f = N<FunctionStmt>(name, nullptr, params,\n                            N<SuiteStmt>(N<ReturnStmt>(expr->getExpr())));\n\n  /// TODO: just copy BindingsAttribute from expr instead?\n  if (auto err = ScopingVisitor::apply(ctx->cache, N<SuiteStmt>(f)))\n    throw exc::ParserException(std::move(err));\n  f->setAttribute(Attr::ExprTime, getTime()); // to handle captures properly\n  f = transform(f);\n  if (auto a = expr->getAttribute(Attr::Bindings))\n    f->setAttribute(Attr::Bindings, a->clone());\n  prependStmts->push_back(f);\n  resultExpr = transform(N<IdExpr>(name));\n}\n\n/// Unify the function return type with `Generator[?]`.\n/// The unbound type will be deduced from return/yield statements.\nvoid TypecheckVisitor::visit(YieldExpr *expr) {\n  if (!ctx->inFunction())\n    E(Error::FN_OUTSIDE_ERROR, expr, \"yield\");\n\n  unify(ctx->getBase()->returnType.get(),\n        instantiateType(getStdLibType(\"Generator\"), {expr->getType()}));\n  if (realize(expr->getType()))\n    expr->setDone();\n}\n\n/// Typecheck await statements.\nvoid TypecheckVisitor::visit(AwaitExpr *expr) {\n  if (!ctx->inFunction())\n    E(Error::FN_OUTSIDE_ERROR, expr, \"await\");\n  auto isAsync = ctx->getBase()->func->isAsync();\n  if (!isAsync)\n    E(Error::FN_OUTSIDE_ERROR, expr, \"await\");\n\n  expr->expr = transform(expr->getExpr());\n  if (!expr->transformed) {\n    if (auto c = expr->getExpr()->getType()->getClass()) {\n      bool isCoroutine = c->is(getMangledClass(\"std.internal.core\", \"Coroutine\")) ||\n                         c->is(getMangledClass(\"std.asyncio\", \"Future\")) ||\n                         c->is(getMangledClass(\"std.asyncio\", \"Task\"));\n      if (!isCoroutine) {\n        if (!findMethod(c, \"__await__\").empty()) {\n          auto e = transform(N<CallExpr>(N<DotExpr>(expr->getExpr(), \"__await__\")));\n          isCoroutine =\n              e->getType()->is(getMangledClass(\"std.internal.core\", \"Coroutine\")) ||\n              e->getType()->is(getMangledClass(\"std.asyncio\", \"Future\")) ||\n              e->getType()->is(getMangledClass(\"std.asyncio\", \"Task\")) ||\n              e->getType()->is(getMangledClass(\"std.internal.core\", \"Generator\"));\n          if (!isCoroutine) {\n            E(Error::EXPECTED_TYPE, expr, \"awaitable\");\n          } else {\n            expr->expr = e;\n            expr->transformed = true;\n          }\n        } else {\n          E(Error::EXPECTED_TYPE, expr, \"awaitable\");\n        }\n      }\n    }\n  }\n\n  if (expr->getExpr()->getType()->getClass())\n    unify(expr->getType(), extractClassGeneric(expr->getExpr()->getType()));\n\n  if (expr->getExpr()->isDone())\n    expr->setDone();\n}\n\n/// Typecheck return statements. Empty return is transformed to `return NoneType()`.\n/// Also partialize functions if they are being returned.\n/// See @c wrapExpr for more details.\nvoid TypecheckVisitor::visit(ReturnStmt *stmt) {\n  if (stmt->hasAttribute(Attr::Internal)) {\n    stmt->expr = transform(N<CallExpr>(\n        N<IdExpr>(getMangledMethod(\"std.internal.core\", \"NoneType\", \"__new__\"))));\n    stmt->setDone();\n    return;\n  }\n\n  if (!ctx->inFunction())\n    E(Error::FN_OUTSIDE_ERROR, stmt, \"return\");\n  auto isAsync = ctx->getBase()->func->isAsync();\n\n  if (!stmt->expr && ctx->getBase()->func->hasAttribute(Attr::IsGenerator)) {\n    stmt->setDone();\n  } else {\n    if (ctx->getBase()->func->hasAttribute(Attr::IsGenerator))\n      E(Error::CUSTOM, stmt, \"returning values from generators not yet supported\");\n    if (!stmt->expr)\n      stmt->expr = N<CallExpr>(N<IdExpr>(\"NoneType\"));\n    stmt->expr = transform(stmt->getExpr());\n\n    // Wrap expression to match the return type\n    if (!ctx->getBase()->returnType->getUnbound())\n      if (!wrapExpr(&stmt->expr, ctx->getBase()->returnType.get())) {\n        return;\n      }\n\n    // Special case: partialize functions if we are returning them\n    if (stmt->getExpr()->getType()->getFunc() &&\n        !(ctx->getBase()->returnType->getClass() &&\n          ctx->getBase()->returnType->is(\"Function\"))) {\n      stmt->expr = transform(\n          N<CallExpr>(N<IdExpr>(stmt->getExpr()->getType()->getFunc()->ast->getName()),\n                      N<EllipsisExpr>(EllipsisExpr::PARTIAL)));\n    }\n\n    if (!ctx->getBase()->returnType->getStaticKind() &&\n        stmt->getExpr()->getType()->getStatic())\n      stmt->getExpr()->setType(stmt->getExpr()\n                                   ->getType()\n                                   ->getStatic()\n                                   ->getNonStaticType()\n                                   ->shared_from_this());\n\n    if (isAsync) {\n      unify(ctx->getBase()->returnType.get(),\n            instantiateType(getStdLibType(\"Coroutine\"), {stmt->getExpr()->getType()}));\n    } else {\n      unify(ctx->getBase()->returnType.get(), stmt->getExpr()->getType());\n    }\n  }\n\n  // If we are not within conditional block, ignore later statements in this function.\n  // Useful with static if statements.\n  if (!ctx->blockLevel)\n    ctx->returnEarly = true;\n\n  if (!stmt->getExpr() || stmt->getExpr()->isDone())\n    stmt->setDone();\n}\n\n/// Typecheck yield statements. Empty yields assume `NoneType`.\nvoid TypecheckVisitor::visit(YieldStmt *stmt) {\n  if (!ctx->inFunction())\n    E(Error::FN_OUTSIDE_ERROR, stmt, \"yield\");\n  auto isAsync = ctx->getBase()->func->isAsync();\n\n  stmt->expr =\n      transform(stmt->getExpr() ? stmt->getExpr() : N<CallExpr>(N<IdExpr>(\"NoneType\")));\n  unify(ctx->getBase()->returnType.get(),\n        instantiateType(getStdLibType(!isAsync ? \"Generator\" : \"AsyncGenerator\"),\n                        {stmt->getExpr()->getType()}));\n\n  if (stmt->getExpr()->isDone())\n    stmt->setDone();\n}\n\n/// Transform `yield from` statements.\n/// @example\n///   `yield from a` -> `for var in a: yield var`\nvoid TypecheckVisitor::visit(YieldFromStmt *stmt) {\n  auto var = getTemporaryVar(\"yield\");\n  resultStmt = transform(\n      N<ForStmt>(N<IdExpr>(var), stmt->getExpr(), N<YieldStmt>(N<IdExpr>(var))));\n}\n\n/// Process `global` statements. Remove them upon completion.\nvoid TypecheckVisitor::visit(GlobalStmt *stmt) { resultStmt = N<SuiteStmt>(); }\n\n/// Parse a function stub and create a corresponding generic function type.\n/// Also realize built-ins and extern C functions.\nvoid TypecheckVisitor::visit(FunctionStmt *stmt) {\n  if (stmt->hasAttribute(Attr::Python)) {\n    // Handle Python block\n    resultStmt =\n        transformPythonDefinition(stmt->getName(), stmt->items, stmt->getReturn(),\n                                  stmt->getSuite()->firstInBlock());\n    return;\n  }\n  auto origStmt = clean_clone(stmt);\n\n  // Parse attributes\n  std::vector<std::string> attributes;\n  bool hasDecorators = false;\n  for (auto i = stmt->decorators.size(); i-- > 0;) {\n    if (!stmt->decorators[i])\n      continue;\n    auto [isAttr, attrName, attrRealizedName] = getDecorator(stmt->decorators[i]);\n    if (!attrName.empty()) {\n      if (attrName == getMangledFunc(\"std.internal.attributes\", \"test\"))\n        stmt->setAttribute(Attr::Test);\n      else if (attrName == getMangledFunc(\"std.internal.attributes\", \"export\"))\n        stmt->setAttribute(Attr::Export);\n      else if (attrName == getMangledFunc(\"std.internal.attributes\", \"inline\"))\n        stmt->setAttribute(Attr::Inline);\n      else if (attrName == getMangledFunc(\"std.internal.attributes\", \"no_arg_reorder\"))\n        stmt->setAttribute(Attr::NoArgReorder);\n      else if (attrName == getMangledFunc(\"std.internal.core\", \"overload\"))\n        stmt->setAttribute(Attr::Overload);\n\n      if (!stmt->hasAttribute(Attr::FunctionAttributes))\n        stmt->setAttribute(Attr::FunctionAttributes,\n                           std::make_unique<ir::KeyValueAttribute>());\n\n      std::string key = attrRealizedName;\n      stmt->getAttribute<ir::KeyValueAttribute>(Attr::FunctionAttributes)\n          ->attributes[attrName] = key;\n\n      const auto &attrFn = getFunction(attrName);\n      if (attrFn && attrFn->ast) {\n        if (attrFn->ast->hasAttribute(Attr::Export))\n          stmt->setAttribute(Attr::Export);\n        if (attrFn->ast->hasAttribute(Attr::Inline))\n          stmt->setAttribute(Attr::Inline);\n        if (attrFn->ast->hasAttribute(Attr::NoArgReorder))\n          stmt->setAttribute(Attr::NoArgReorder);\n        if (attrFn->ast->hasAttribute(Attr::ForceRealize))\n          stmt->setAttribute(Attr::ForceRealize);\n        if (attrFn->ast->hasAttribute(Attr::FunctionAttributes)) {\n          for (const auto &[k, v] :\n               attrFn->ast\n                   ->getAttribute<ir::KeyValueAttribute>(Attr::FunctionAttributes)\n                   ->attributes)\n            stmt->getAttribute<ir::KeyValueAttribute>(Attr::FunctionAttributes)\n                ->attributes[k] = v;\n        }\n      }\n      if (isAttr)\n        stmt->decorators[i] = nullptr; // remove it from further consideration\n    }\n    if (!isAttr) {\n      hasDecorators = true;\n    }\n  }\n\n  bool isClassMember = ctx->inClass();\n  if (stmt->hasAttribute(Attr::ForceRealize) && (!ctx->isGlobal() || isClassMember))\n    E(Error::EXPECTED_TOPLEVEL, getSrcInfo(), \"builtin function\");\n\n  // All overloads share the same canonical name except for the number at the\n  // end (e.g., `foo.1:0`, `foo.1:1` etc.)\n  std::string rootName;\n  if (isClassMember) {\n    // Case 1: method overload\n    if (auto n = in(getClass(ctx->getBase()->name)->methods, stmt->getName()))\n      rootName = *n;\n\n    // TODO: handle static inherits and auto-generated cases\n    // if (!rootName.empty() && stmt->hasAttribute(Attr::Overload)) {\n    //   compilationWarning(\n    //       fmt::format(\"function '{}' should be marked with @overload\",\n    //       stmt->getName()), getSrcInfo().file, getSrcInfo().line);\n    // }\n    if (rootName.empty() && stmt->hasAttribute(Attr::Overload)) {\n      compilationWarning(fmt::format(\"function '{}' marked with unnecessary @overload\",\n                                     stmt->getName()),\n                         getSrcInfo().file, getSrcInfo().line);\n    }\n  } else if (stmt->hasAttribute(Attr::Overload)) {\n    // Case 2: function overload\n    if (auto c = ctx->find(stmt->getName(), getTime())) {\n      if (c->isFunc() && c->getModule() == ctx->getModule() &&\n          c->getBaseName() == ctx->getBaseName()) {\n        rootName = c->canonicalName;\n      }\n    }\n  }\n  if (rootName.empty())\n    rootName = ctx->generateCanonicalName(stmt->getName(), true, isClassMember);\n  // Append overload number to the name\n  auto canonicalName = rootName;\n  if (!in(ctx->cache->overloads, rootName))\n    ctx->cache->overloads.insert({rootName, {}});\n  canonicalName += fmt::format(\":{}\", getOverloads(rootName).size());\n  ctx->cache->reverseIdentifierLookup[canonicalName] = stmt->getName();\n\n  if (isClassMember) {\n    // Set the enclosing class name\n    stmt->setAttribute(Attr::ParentClass, ctx->getBase()->name);\n    // Add the method to the class' method list\n    getClass(ctx->getBase()->name)->methods[stmt->getName()] = rootName;\n  }\n\n  // Handle captures. Add additional argument to the function for every capture.\n  // Make sure to account for **kwargs if present\n  if (auto b = stmt->getAttribute<BindingsAttribute>(Attr::Bindings)) {\n    size_t insertSize = stmt->size();\n    if (!stmt->empty() && startswith(stmt->back().name, \"**\"))\n      insertSize--;\n    for (auto &[c, t] : b->captures) {\n      std::string cc = \"$\" + c;\n      if (auto v = ctx->find(c, getTime())) {\n        if (t != BindingsAttribute::CaptureType::Global && !v->isGlobal()) {\n          bool parentClassGeneric =\n              ctx->bases.back().isType() && ctx->bases.back().name == v->getBaseName();\n          if (v->isGeneric() && parentClassGeneric) {\n            stmt->setAttribute(Attr::Method);\n          }\n          if (!v->isGeneric() || (v->getStaticKind() && !parentClassGeneric)) {\n            if (!v->isFunc()) {\n              if (v->isType()) {\n                stmt->items.insert(stmt->items.begin() + insertSize++,\n                                   Param(cc, N<IdExpr>(TYPE_TYPE)));\n              } else if (auto si = v->getStaticKind()) {\n                stmt->items.insert(\n                    stmt->items.begin() + insertSize++,\n                    Param(cc, N<IndexExpr>(N<IdExpr>(\"Literal\"),\n                                           N<IdExpr>(Type::stringFromLiteral(si)))));\n              } else {\n                stmt->items.insert(stmt->items.begin() + insertSize++, Param(cc));\n              }\n            } else {\n              // Local function is captured. Just note its canonical name and add it to\n              // the context during realization.\n              b->localRenames[c] = v->getName();\n            }\n          }\n          continue;\n        }\n      }\n      if ((c == stmt->getName() && hasDecorators) /* decorated recursive fns */ ||\n          in(ctx->globalShadows, c)) {\n        // log(\"-> {} / {}\", stmt->getName(), c);\n        stmt->items.insert(stmt->items.begin() + insertSize++, Param(cc));\n      }\n    }\n  }\n\n  std::vector<Param> args;\n  Stmt *suite = nullptr;\n  Expr *ret = nullptr;\n  std::vector<ClassType::Generic> explicits;\n  std::shared_ptr<types::ClassType> baseType = nullptr;\n  bool isGlobal = ctx->isGlobal();\n  {\n    // Set up the base\n    TypeContext::BaseGuard br(ctx.get(), canonicalName);\n    ctx->getBase()->func = stmt;\n\n    // Parse arguments and add them to the context\n    for (auto &a : *stmt) {\n      auto [stars, varName] = a.getNameWithStars();\n      auto name = ctx->generateCanonicalName(varName);\n\n      // Mark as method if the first argument is self\n      if (isClassMember && stmt->hasAttribute(Attr::HasSelf) && a.getName() == \"self\")\n        stmt->setAttribute(Attr::Method);\n\n      // Handle default values\n      auto defaultValue = a.getDefault();\n      if (match(defaultValue, MOr(M<NoneExpr>(), M<IntExpr>(), M<BoolExpr>(),\n                                  M<FloatExpr>(), M<IdExpr>(), M<StringExpr>()))) {\n        // Special case: all simple types and Nones are handled at call site\n        // (as they are not mutable).\n        if (match(defaultValue, M<NoneExpr>())) {\n          if (match(a.getType(), M<IdExpr>(MOr(TYPE_TYPE, TRAIT_TYPE)))) {\n            // Special case: `arg: type = None` -> `arg: type = NoneType`\n            defaultValue = N<IdExpr>(\"NoneType\");\n          } else {\n            ; // Do nothing. NoneExpr will be handled later (we don't want it\n              // to be converted to Optional call yet.)\n          }\n        } else {\n          defaultValue = transform(defaultValue);\n        }\n      } else if (defaultValue) {\n        if (!a.isValue()) {\n          // Special case: generic defaults are evaluated as-is!\n          defaultValue = transform(defaultValue);\n        } else {\n          auto defName = fmt::format(\".default.{}.{}\", canonicalName, a.getName());\n          auto nctx = std::make_shared<TypeContext>(ctx->cache);\n          *nctx = *ctx;\n          nctx->bases.pop_back();\n          if (isClassMember) // class variable; go to the global context!\n            nctx->bases.erase(nctx->bases.begin() + 1, nctx->bases.end());\n          auto tv = TypecheckVisitor(nctx);\n          auto as = N<AssignStmt>(N<IdExpr>(defName), defaultValue,\n                                  a.isValue() ? nullptr : a.getType());\n          if (isClassMember) {\n            preamble->addStmt(\n                tv.transform(N<AssignStmt>(N<IdExpr>(defName), nullptr, nullptr)));\n            registerGlobal(defName);\n            as->setUpdate();\n          } else if (isGlobal) {\n            registerGlobal(defName);\n          }\n          auto das = tv.transform(as);\n          prependStmts->push_back(das);\n          // Default unbounds must be allowed to pass through\n          // to support cases such as `a = []`\n          auto f = ctx->forceFind(defName);\n          for (auto &u : f->getType()->getUnbounds(false)) {\n            // log(\"pass-through: {} / {}\", stmt->getName(), u->debugString(2));\n            u->getUnbound()->passThrough = true;\n            stmt->setAttribute(Attr::AllowPassThrough);\n          }\n          defaultValue = tv.transform(N<IdExpr>(defName));\n        }\n      }\n      args.emplace_back(std::string(stars, '*') + name, a.getType(), defaultValue,\n                        a.status);\n\n      // Add generics to the context\n      if (!a.isValue()) {\n        // Generic and static types\n        auto generic = instantiateUnbound();\n        auto typId = generic->getLink()->id;\n        generic->genericName = varName;\n        auto defType = transform(clone(a.getDefault()));\n        if (auto st = getStaticGeneric(a.getType())) {\n          auto val = ctx->addVar(varName, name, generic);\n          val->generic = true;\n          generic->staticKind = st;\n          if (defType)\n            generic->defaultType = extractType(defType)->shared_from_this();\n        } else {\n          if (match(a.getType(), M<InstantiateExpr>(M<IdExpr>(TRAIT_TYPE), M_))) {\n            // Parse TraitVar\n            auto l = transformType(cast<InstantiateExpr>(a.getType())->front(), true)\n                         ->getType();\n            if (l->getLink() && l->getLink()->trait)\n              generic->getLink()->trait = l->getLink()->trait;\n            else\n              generic->getLink()->trait =\n                  std::make_shared<types::TypeTrait>(l->shared_from_this());\n          }\n          auto val = ctx->addType(varName, name, generic);\n          val->generic = true;\n          if (defType)\n            generic->defaultType = extractType(defType)->shared_from_this();\n        }\n        auto g = generic->generalize(ctx->typecheckLevel);\n        if (startswith(varName, \"$\"))\n          varName = varName.substr(1);\n        explicits.emplace_back(name, g, typId, g->getStaticKind());\n      }\n    }\n\n    // Prepare list of all generic types\n    ClassType *parentClass = nullptr;\n    if (isClassMember && stmt->hasAttribute(Attr::Method)) {\n      // Get class generics (e.g., T for `class Cls[T]: def foo:`)\n      auto aa = stmt->getAttribute<ir::StringValueAttribute>(Attr::ParentClass);\n      parentClass = extractClassType(aa->value);\n    }\n    // Add function generics\n    std::vector<TypePtr> generics;\n    generics.reserve(explicits.size());\n    for (const auto &i : explicits)\n      generics.emplace_back(extractType(i.name)->shared_from_this());\n\n    // Handle function arguments\n    // Base type: `Function[[args,...], ret]`\n    baseType = getFuncTypeBase(stmt->size() - explicits.size());\n    ctx->typecheckLevel++;\n\n    // Parse arguments to the context. Needs to be done after adding generics\n    // to support cases like `foo(a: T, T: type)`\n    for (auto &a : args) {\n      a.type = transformType(a.getType(), true);\n    }\n\n    // Unify base type generics with argument types. Add non-generic arguments to the\n    // context. Delayed to prevent cases like `def foo(a, b=a)`\n    auto argType = extractClassGeneric(baseType.get())->getClass();\n    for (int ai = 0, aj = 0; ai < stmt->size(); ai++) {\n      if (!(*stmt)[ai].isValue())\n        continue;\n      auto [_, canName] = (*stmt)[ai].getNameWithStars();\n      if (!(*stmt)[ai].getType()) {\n        if (parentClass && ai == 0 && (*stmt)[ai].getName() == \"self\") {\n          // Special case: self in methods\n          auto *st = unify(extractClassGeneric(argType, aj), parentClass);\n          if (getClass(parentClass->name)->ast->hasAttribute(Attr::ClassDeduce) &&\n              stmt->hasAttribute(Attr::ClassDeduce) && stmt->getName() == \"__init__\") {\n            for (auto &u : st->getUnbounds(true)) {\n              stmt->setAttribute(Attr::AllowPassThrough);\n              // log(\"pass-through: {}.__init__ / {}\", parentClass->name,\n              //     u->debugString(2));\n              u->getLink()->passThrough = true;\n            }\n          }\n        } else {\n          generics.push_back(extractClassGeneric(argType, aj)->shared_from_this());\n        }\n      } else if (startswith((*stmt)[ai].getName(), \"*\")) {\n        // Special case: `*args: type` and `**kwargs: type`. Do not add this type to the\n        // signature (as the real type is `Tuple[type, ...]`); it will be used during\n        // call typechecking\n        generics.push_back(extractClassGeneric(argType, aj)->shared_from_this());\n      } else {\n        unify(extractClassGeneric(argType, aj),\n              extractType(transformType((*stmt)[ai].getType(), true)));\n      }\n      aj++;\n    }\n\n    // Parse the return type\n    ret = transformType(stmt->getReturn(), true);\n    auto retType = extractClassGeneric(baseType.get(), 1);\n    if (ret) {\n      // Fix for functions returning Literal types\n      if (auto st = getStaticGeneric(ret))\n        baseType->generics[1].staticKind = st;\n\n      unify(retType, extractType(ret));\n      if (isId(ret, \"Union\"))\n        extractClassGeneric(retType)->getUnbound()->kind = LinkType::Generic;\n    } else {\n      generics.push_back(unify(retType, instantiateUnbound())->shared_from_this());\n    }\n    ctx->typecheckLevel--;\n\n    // Generalize generics and remove them from the context\n    for (const auto &g : generics) {\n      for (auto &u : g->getUnbounds(false))\n        if (u->getUnbound()) {\n          u->getUnbound()->kind = LinkType::Generic;\n        }\n    }\n\n    // Parse function body\n    if (!stmt->hasAttribute(Attr::Internal) && !stmt->hasAttribute(Attr::C)) {\n      if (stmt->hasAttribute(Attr::LLVM)) {\n        suite = transformLLVMDefinition(stmt->getSuite()->firstInBlock());\n      } else if (stmt->hasAttribute(Attr::C)) {\n        // Do nothing\n      } else {\n        suite = clone(stmt->getSuite());\n      }\n    }\n  }\n  stmt->setAttribute(Attr::Module, ctx->moduleName.path);\n\n  // Make function AST and cache it for later realization\n  auto f = N<FunctionStmt>(canonicalName, ret, args, suite, std::vector<Expr *>{},\n                           stmt->isAsync());\n  f->cloneAttributesFrom(stmt);\n  auto &fn = ctx->cache->functions[canonicalName] =\n      Cache::Function{ctx->getModulePath(),\n                      rootName,\n                      f,\n                      nullptr,\n                      origStmt,\n                      ctx->getModule().empty() && ctx->isGlobal()};\n  f->setDone();\n  auto aa = stmt->getAttribute<ir::StringValueAttribute>(Attr::ParentClass);\n  auto parentClass = aa ? extractClassType(aa->value) : nullptr;\n\n  // Construct the type\n  auto funcTyp = std::make_shared<types::FuncType>(baseType.get(), fn.ast, explicits);\n  funcTyp->setSrcInfo(getSrcInfo());\n  if (isClassMember && stmt->hasAttribute(Attr::Method)) {\n    funcTyp->funcParent = parentClass->shared_from_this();\n  }\n  funcTyp = std::static_pointer_cast<types::FuncType>(\n      funcTyp->generalize(ctx->typecheckLevel));\n  fn.type = funcTyp;\n\n  auto &overloads = ctx->cache->overloads[rootName];\n  if (rootName == \"Tuple.__new__\") {\n    overloads.insert(std::ranges::upper_bound(\n                         overloads, canonicalName,\n                         [&](const auto &a, const auto &b) {\n                           return getFunction(a)->getType()->funcGenerics.size() <\n                                  getFunction(b)->getType()->funcGenerics.size();\n                         }),\n                     canonicalName);\n  } else {\n    overloads.push_back(canonicalName);\n  }\n\n  auto val = ctx->addFunc(stmt->name, rootName, funcTyp);\n  // val->time = getTime();\n  ctx->addFunc(canonicalName, canonicalName, funcTyp);\n  if (stmt->hasAttribute(Attr::Overload) || isClassMember) {\n    ctx->remove(stmt->name); // first overload will handle it!\n  }\n\n  // Special method handling\n  if (isClassMember) {\n    auto m = getClassMethod(parentClass, getUnmangledName(canonicalName));\n    bool found = false;\n    for (auto &i : getOverloads(m))\n      if (i == canonicalName) {\n        getFunction(i)->type = funcTyp;\n        found = true;\n        break;\n      }\n    seqassert(found, \"cannot find matching class method for {}\", canonicalName);\n  } else {\n    // Hack so that we can later use same helpers for class overloads\n    getClass(VAR_CLASS_TOPLEVEL)->methods[stmt->getName()] = rootName;\n  }\n\n  // Ensure that functions with @C, @force_realize, and @export attributes can be\n  // realized\n  if (stmt->hasAttribute(Attr::ForceRealize) || stmt->hasAttribute(Attr::Export) ||\n      (stmt->hasAttribute(Attr::C) && !stmt->hasAttribute(Attr::CVarArg))) {\n    if (!funcTyp->canRealize())\n      E(Error::FN_REALIZE_BUILTIN, stmt);\n  }\n\n  // Expression to be used if function binding is modified by captures or decorators\n  Expr *finalExpr = nullptr;\n  // Parse remaining decorators\n  for (auto i = stmt->decorators.size(); i-- > 0;) {\n    if (stmt->decorators[i]) {\n      // Replace each decorator with `decorator(finalExpr)` in the reverse order\n      finalExpr = N<CallExpr>(stmt->decorators[i],\n                              finalExpr ? finalExpr : N<IdExpr>(canonicalName));\n    }\n  }\n  if (finalExpr) {\n    auto a = N<AssignStmt>(N<IdExpr>(stmt->getName()), finalExpr);\n    if (isClassMember) { // class method decorator\n      auto nctx = std::make_shared<TypeContext>(ctx->cache);\n      *nctx = *ctx;\n      nctx->bases.pop_back();\n      nctx->bases.erase(nctx->bases.begin() + 1, nctx->bases.end()); // global context\n      auto tv = TypecheckVisitor(nctx);\n\n      auto defName = ctx->generateCanonicalName(stmt->getName());\n      preamble->addStmt(\n          tv.transform(N<AssignStmt>(N<IdExpr>(defName), nullptr, nullptr)));\n      registerGlobal(defName);\n      a->setUpdate();\n\n      cast<IdExpr>(a->getLhs())->value = defName;\n      std::vector<CallArg> args;\n      for (auto arg : *stmt) {\n        if (startswith(arg.name, \"**\"))\n          args.push_back(N<KeywordStarExpr>(N<IdExpr>(arg.name)));\n        else if (startswith(arg.name, \"*\"))\n          args.push_back(N<StarExpr>(N<IdExpr>(arg.name)));\n        else\n          args.push_back(N<IdExpr>(arg.name));\n      }\n      Stmt *newFunc = N<FunctionStmt>(\n          stmt->getName(), clone(stmt->getReturn()), clone(stmt->items),\n          N<SuiteStmt>(N<ReturnStmt>(N<CallExpr>(N<IdExpr>(defName), args))),\n          std::vector<Expr *>{}, stmt->isAsync());\n      newFunc = transform(newFunc);\n      resultStmt = N<SuiteStmt>(f, N<SuiteStmt>(transform(a), newFunc));\n    } else {\n      resultStmt = N<SuiteStmt>(f, transform(a));\n    }\n  } else {\n    resultStmt = f;\n  }\n}\n\n/// Transform Python code blocks.\n/// @example\n///   ```@python\n///      def foo(x: int, y) -> int:\n///        [code]\n///   ``` -> ```\n///      pyobj._exec(\"def foo(x, y): [code]\")\n///      from python import __main__.foo(int, _) -> int\n///   ```\nStmt *TypecheckVisitor::transformPythonDefinition(const std::string &name,\n                                                  const std::vector<Param> &args,\n                                                  Expr *ret, Stmt *codeStmt) {\n  seqassert(codeStmt && cast<ExprStmt>(codeStmt) &&\n                cast<StringExpr>(cast<ExprStmt>(codeStmt)->getExpr()),\n            \"invalid Python definition\");\n\n  auto code = cast<StringExpr>(cast<ExprStmt>(codeStmt)->getExpr())->getValue();\n  std::vector<std::string> pyargs;\n  pyargs.reserve(args.size());\n  for (const auto &a : args)\n    pyargs.emplace_back(a.getName());\n  code = fmt::format(\"def {}({}):\\n{}\\n\", name, join(pyargs, \", \"), code);\n  return transform(N<SuiteStmt>(\n      N<ExprStmt>(\n          N<CallExpr>(N<DotExpr>(N<IdExpr>(\"pyobj\"), \"_exec\"), N<StringExpr>(code))),\n      N<ImportStmt>(N<IdExpr>(\"python\"), N<DotExpr>(N<IdExpr>(\"__main__\"), name),\n                    clone(args), ret ? clone(ret) : N<IdExpr>(\"pyobj\"))));\n}\n\n/// Transform LLVM functions.\n/// @example\n///   ```@llvm\n///      def foo(x: int) -> float:\n///        [code]\n///   ``` -> ```\n///      def foo(x: int) -> float:\n///        StringExpr(\"[code]\")\n///        SuiteStmt(referenced_types)\n///   ```\n/// As LLVM code can reference types and static expressions in `{=expr}` blocks,\n/// all block expression will be stored in the `referenced_types` suite.\n/// \"[code]\" is transformed accordingly: each `{=expr}` block will\n/// be replaced with `{}` so that @c fmt::format can fill the gaps.\n/// Note that any brace (`{` or `}`) that is not part of a block is\n/// escaped (e.g. `{` -> `{{` and `}` -> `}}`) so that @c fmt::format can process them.\nStmt *TypecheckVisitor::transformLLVMDefinition(Stmt *codeStmt) {\n  StringExpr *codeExpr;\n  auto m = match(codeStmt, M<ExprStmt>(MVar<StringExpr>(codeExpr)));\n  seqassert(m, \"invalid LLVM definition\");\n  auto code = codeExpr->getValue();\n  /// Remove docstring (if any)\n  size_t start = 0;\n  while (start < code.size() && std::isspace(code[start]))\n    start++;\n  if (startswith(code.substr(start), \"\\\"\\\"\\\"\")) {\n    start += 3;\n    bool found = false;\n    while (start < code.size() - 2) {\n      if (code[start] == '\"' && code[start + 1] == '\"' && code[start + 2] == '\"') {\n        found = true;\n        start += 3;\n        break;\n      }\n      start++;\n    }\n    if (found) {\n      code = code.substr(start);\n    }\n  }\n\n  std::vector<Stmt *> items;\n  std::string finalCode;\n  items.push_back(nullptr);\n\n  // Parse LLVM code and look for expression blocks that start with `{=`\n  int braceCount = 0, braceStart = 0;\n  for (int i = 0; i < code.size(); i++) {\n    if (i < code.size() - 1 && code[i] == '\\\\' && code[i + 1] == '\\n') {\n      code[i] = code[i + 1] = ' ';\n    }\n    if (i < code.size() - 1 && code[i] == '{' && code[i + 1] == '=') {\n      if (braceStart <= i)\n        finalCode += escapeFStringBraces(code, braceStart, i - braceStart) + '{';\n      if (!braceCount) {\n        braceStart = i + 2;\n        braceCount++;\n      } else {\n        E(Error::FN_BAD_LLVM, getSrcInfo());\n      }\n    } else if (braceCount && code[i] == '}') {\n      braceCount--;\n      std::string exprCode = code.substr(braceStart, i - braceStart);\n      auto offset = getSrcInfo();\n      offset.col += i;\n      auto exprOrErr = parseExpr(ctx->cache, exprCode, offset);\n      if (!exprOrErr)\n        throw exc::ParserException(exprOrErr.takeError());\n      auto expr = exprOrErr->first;\n      items.push_back(N<ExprStmt>(expr));\n      braceStart = i + 1;\n      finalCode += '}';\n    }\n  }\n  if (braceCount)\n    E(Error::FN_BAD_LLVM, getSrcInfo());\n  if (braceStart != code.size())\n    finalCode += escapeFStringBraces(code, braceStart,\n                                     static_cast<int>(code.size()) - braceStart);\n  items[0] = N<ExprStmt>(N<StringExpr>(finalCode));\n  return N<SuiteStmt>(items);\n}\n\n/// Fetch a decorator canonical name. The first pair member indicates if a decorator is\n/// actually an attribute (a function with `@__attribute__`).\nstd::tuple<bool, std::string, std::string> TypecheckVisitor::getDecorator(Expr *e) {\n  auto dt = transform(clone(e));\n  dt = getHeadExpr(dt);\n  if (auto id = cast<IdExpr>(cast<CallExpr>(dt) ? cast<CallExpr>(dt)->getExpr() : dt)) {\n    auto ci = ctx->find(id->getValue(), getTime());\n    if (ci && ci->isFunc()) {\n      auto fn = ci->getType()->getFunc()->ast->getName();\n      auto f = getFunction(fn);\n      if (!f) {\n        if (auto o = in(ctx->cache->overloads, fn)) {\n          if (o->size() == 1)\n            f = getFunction(o->front());\n        }\n      }\n      // Special case: Id to Call\n      if (f->ast->hasAttribute(Attr::Attribute) && cast<IdExpr>(dt))\n        dt = transform(N<CallExpr>(dt));\n      if (f)\n        return {f->ast->hasAttribute(Attr::Attribute), fn,\n                dt->isDone() ? id->getValue() : \"\"};\n    }\n  }\n  return {false, \"\", \"\"};\n}\n\n/// Generate and return `Function[Tuple[args...], ret]` type\nstd::shared_ptr<ClassType> TypecheckVisitor::getFuncTypeBase(size_t nargs) {\n  auto baseType = instantiateType(getStdLibType(\"Function\"));\n  unify(extractClassGeneric(baseType->getClass()),\n        instantiateType(generateTuple(nargs, false)));\n  return std::static_pointer_cast<types::ClassType>(baseType);\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/import.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <memory>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/match.h\"\n#include \"codon/parser/peg/peg.h\"\n#include \"codon/parser/visitors/format/format.h\"\n#include \"codon/parser/visitors/scoping/scoping.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nusing namespace codon::error;\nusing namespace codon::matcher;\n\nnamespace codon::ast {\n\n/// Import and parse a new module into its own context.\n/// Also handle special imports ( see @c transformSpecialImport ).\n/// To simulate Python's dynamic import logic and import stuff only once,\n/// each import statement is guarded as follows:\n///   if not _import_N_done:\n///     _import_N()\n///     _import_N_done = True\n/// See @c transformNewImport and below for more details.\nvoid TypecheckVisitor::visit(ImportStmt *stmt) {\n  seqassert(!ctx->inClass(), \"imports within a class\");\n  if ((resultStmt = transformSpecialImport(stmt)))\n    return;\n\n  // Fetch the import\n  auto components = getImportPath(stmt->getFrom(), stmt->getDots());\n  auto path = combine2(components, \"/\");\n  auto file = getImportFile(ctx->cache, path, ctx->getFilename());\n  if (!file) {\n    if (stmt->getDots() == 0 && ctx->autoPython) {\n      auto newStr = FormatVisitor::apply(stmt->getFrom());\n      if (stmt->getWhat())\n        newStr += \".\" + FormatVisitor::apply(stmt->getWhat());\n      compilationWarning(fmt::format(\"importing '{}' from Python\", newStr),\n                         stmt->getSrcInfo().file, stmt->getSrcInfo().line,\n                         stmt->getSrcInfo().col);\n      auto exprOrErr = parseExpr(ctx->cache, newStr, stmt->getFrom()->getSrcInfo());\n      if (!exprOrErr)\n        throw exc::ParserException(exprOrErr.takeError());\n      resultStmt = transform(N<ImportStmt>(N<IdExpr>(\"python\"), exprOrErr->first,\n                                           stmt->getArgs(), stmt->getReturnType(),\n                                           stmt->getAs()));\n      return;\n    }\n\n    std::string s(stmt->getDots(), '.');\n    for (auto &c : components) {\n      if (c == \"..\") {\n        continue;\n      } else if (!s.empty() && s.back() != '.') {\n        s += \".\" + c;\n      } else {\n        s += c;\n      }\n    }\n    bool allDot = true;\n    for (auto cp : s)\n      if (cp != '.') {\n        allDot = false;\n        break;\n      }\n    if (allDot && match(stmt->getWhat(), M<IdExpr>()))\n      s = cast<IdExpr>(stmt->getWhat())->getValue();\n    E(Error::IMPORT_NO_MODULE, stmt, s);\n  }\n\n  // If the file has not been seen before, load it into cache\n  bool handled = true;\n  if (!in(ctx->cache->imports, file->path)) {\n    resultStmt = transformNewImport(*file);\n    if (!resultStmt)\n      handled = false; // we need an import\n  }\n\n  const auto &import = getImport(file->path);\n  std::string importVar = import->importVar;\n  if (!import->loadedAtToplevel)\n    handled = false;\n\n  // Construct `if _import_done.__invert__(): (_import(); _import_done = True)`.\n  // Do not do this during the standard library loading (we assume that standard library\n  // imports are \"clean\" and do not need guards). Note that the importVar is empty if\n  // the import has been loaded during the standard library loading.\n  if (!handled) {\n    resultStmt = N<ExprStmt>(\n        N<CallExpr>(N<IdExpr>(getMangledFunc(\"\", fmt::format(\"{}_call\", importVar)))));\n    LOG_TYPECHECK(\"[import] loading {}\", importVar);\n  }\n\n  // Import requested identifiers from the import's scope to the current scope\n  if (!stmt->getWhat()) {\n    // Case: import foo\n    auto name = stmt->as.empty() ? path : stmt->getAs();\n    auto e = ctx->forceFind(importVar);\n    ctx->add(name, e);\n  } else if (cast<IdExpr>(stmt->getWhat()) &&\n             cast<IdExpr>(stmt->getWhat())->getValue() == \"*\") {\n    // Case: from foo import *\n    seqassert(stmt->getAs().empty(), \"renamed star-import\");\n    // Just copy all symbols from import's context here.\n    for (auto &[i, ival] : *(import->ctx)) {\n      if ((!startswith(i, \"_\") || (ctx->isStdlibLoading && startswith(i, \"__\")))) {\n        // Ignore all identifiers that start with `_` but not those that start with\n        // `__` while the standard library is being loaded\n        auto c = ival.front();\n        if (c->isConditional() && i.find('.') == std::string::npos)\n          c = import->ctx->find(i);\n        // Imports should ignore noShadow property\n        ctx->add(i, c);\n      }\n    }\n  } else {\n    // Case 3: from foo import bar\n    auto i = cast<IdExpr>(stmt->getWhat());\n    seqassert(i, \"not a valid import what expression\");\n    auto c = import->ctx->find(i->getValue());\n    // Make sure that we are importing an existing global symbol\n    if (!c)\n      E(Error::IMPORT_NO_NAME, i, i->getValue(), file->module);\n    if (c->isConditional())\n      c = import->ctx->find(i->getValue());\n    // Imports should ignore noShadow property\n    ctx->add(stmt->getAs().empty() ? i->getValue() : stmt->getAs(), c);\n  }\n  resultStmt = transform(!resultStmt ? N<SuiteStmt>() : resultStmt); // erase it\n}\n\n/// Transform special `from C` and `from python` imports.\n/// See @c transformCImport, @c transformCDLLImport and @c transformPythonImport\nStmt *TypecheckVisitor::transformSpecialImport(const ImportStmt *stmt) {\n  if (auto fi = cast<IdExpr>(stmt->getFrom())) {\n    if (fi->getValue() == \"C\") {\n      auto wi = cast<IdExpr>(stmt->getWhat());\n      if (wi && !stmt->isCVar()) {\n        // C function imports\n        return transformCImport(wi->getValue(), stmt->getArgs(), stmt->getReturnType(),\n                                stmt->getAs());\n      } else if (wi) {\n        // C variable imports\n        return transformCVarImport(wi->getValue(), stmt->getReturnType(),\n                                   stmt->getAs());\n      } else if (auto de = cast<DotExpr>(stmt->getWhat())) {\n        // dylib C imports\n        return transformCDLLImport(de->getExpr(), de->getMember(), stmt->getArgs(),\n                                   stmt->getReturnType(), stmt->getAs(),\n                                   !stmt->isCVar());\n      }\n    } else if (fi->getValue() == \"python\" && stmt->getWhat()) {\n      // Python imports\n      return transformPythonImport(stmt->getWhat(), stmt->getArgs(),\n                                   stmt->getReturnType(), stmt->getAs());\n    }\n  }\n  return nullptr;\n}\n\n/// Transform Dot(Dot(a, b), c...) into \"{a, b, c, ...}\".\n/// Useful for getting import paths.\nstd::vector<std::string> TypecheckVisitor::getImportPath(Expr *from,\n                                                         size_t dots) const {\n  std::vector<std::string> components; // Path components\n  if (from) {\n    for (; cast<DotExpr>(from); from = cast<DotExpr>(from)->getExpr())\n      components.push_back(cast<DotExpr>(from)->getMember());\n    seqassert(cast<IdExpr>(from), \"invalid import statement\");\n    components.push_back(cast<IdExpr>(from)->getValue());\n  }\n\n  // Handle dots (i.e., `..` in `from ..m import x`)\n  for (size_t i = 1; i < dots; i++)\n    components.emplace_back(\"..\");\n  std::ranges::reverse(components);\n  return components;\n}\n\n/// Transform a C function import.\n/// @example\n///   `from C import foo(int) -> float as f` ->\n///   ```@.c\n///      def foo(a1: int) -> float:\n///        pass\n///      f = foo # if altName is provided```\n/// No return type implies void return type. *args is treated as C VAR_ARGS.\nStmt *TypecheckVisitor::transformCImport(const std::string &name,\n                                         const std::vector<Param> &args, Expr *ret,\n                                         const std::string &altName) {\n  std::vector<Param> fnArgs;\n  bool hasVarArgs = false;\n  for (size_t ai = 0; ai < args.size(); ai++) {\n    seqassert(args[ai].getName().empty(), \"unexpected argument name\");\n    seqassert(!args[ai].getDefault(), \"unexpected default argument\");\n    seqassert(args[ai].getType(), \"missing type\");\n    if (cast<EllipsisExpr>(args[ai].getType()) && ai + 1 == args.size()) {\n      // C VAR_ARGS support\n      hasVarArgs = true;\n      fnArgs.emplace_back(\"*args\", nullptr, nullptr);\n    } else {\n      fnArgs.emplace_back(args[ai].getName().empty() ? fmt::format(\"a{}\", ai)\n                                                     : args[ai].getName(),\n                          clone(args[ai].getType()), nullptr);\n    }\n  }\n  auto _ = ctx->generateCanonicalName(name); // avoid canonicalName == name\n  Stmt *f =\n      N<FunctionStmt>(name, ret ? clone(ret) : N<IdExpr>(\"NoneType\"), fnArgs, nullptr);\n  f->setAttribute(Attr::C);\n  if (hasVarArgs)\n    f->setAttribute(Attr::CVarArg);\n  f = transform(f); // Already in the preamble\n  if (!altName.empty()) {\n    auto v = ctx->find(altName);\n    auto val = ctx->forceFind(name);\n    ctx->add(altName, val);\n    ctx->remove(name);\n  }\n  return f;\n}\n\n/// Transform a C variable import.\n/// @example\n///   `from C import foo: int as f` ->\n///   ```f: int = \"foo\"```\nStmt *TypecheckVisitor::transformCVarImport(const std::string &name, Expr *type,\n                                            const std::string &altName) {\n  auto canonical = ctx->generateCanonicalName(name);\n  auto typ = transformType(clone(type));\n  auto val = ctx->addVar(\n      altName.empty() ? name : altName, canonical,\n      std::make_shared<types::LinkType>(extractClassType(typ)->shared_from_this()),\n      getTime());\n  auto s = N<AssignStmt>(N<IdExpr>(canonical), nullptr, typ);\n  s->lhs->setAttribute(Attr::ExprExternVar);\n  s->lhs->setType(val->type);\n  s->lhs->setDone();\n  s->setDone();\n  return s;\n}\n\n/// Transform a dynamic C import.\n/// @example\n///   `from C import lib.foo(int) -> float as f` ->\n///   `f = _dlsym(lib, \"foo\", Fn=Function[[int], float]); f`\n/// No return type implies void return type.\nStmt *TypecheckVisitor::transformCDLLImport(Expr *dylib, const std::string &name,\n                                            const std::vector<Param> &args, Expr *ret,\n                                            const std::string &altName,\n                                            bool isFunction) {\n  Expr *type = nullptr;\n  if (isFunction) {\n    std::vector<Expr *> fnArgs{N<ListExpr>(), ret ? clone(ret) : N<IdExpr>(\"NoneType\")};\n    for (const auto &a : args) {\n      seqassert(a.getName().empty(), \"unexpected argument name\");\n      seqassert(!a.getDefault(), \"unexpected default argument\");\n      seqassert(a.getType(), \"missing type\");\n      cast<ListExpr>(fnArgs[0])->items.emplace_back(clone(a.getType()));\n    }\n    type = N<IndexExpr>(N<IdExpr>(\"Function\"), N<TupleExpr>(fnArgs));\n  } else {\n    type = clone(ret);\n  }\n\n  Expr *c = clone(dylib);\n  return transform(N<AssignStmt>(\n      N<IdExpr>(altName.empty() ? name : altName),\n      N<CallExpr>(N<IdExpr>(\"_dlsym\"),\n                  std::vector<CallArg>{CallArg(c), CallArg(N<StringExpr>(name)),\n                                       CallArg{\"Fn\", type}})));\n}\n\n/// Transform a Python module and function imports.\n/// @example\n///   `from python import module as f` -> `f = pyobj._import(\"module\")`\n///   `from python import lib.foo(int) -> float as f` ->\n///   ```def f(a0: int) -> float:\n///        f = pyobj._import(\"lib\")._getattr(\"foo\")\n///        return float.__from_py__(f(a0))```\n/// If a return type is nullptr, the function just returns f (raw pyobj).\nStmt *TypecheckVisitor::transformPythonImport(Expr *what,\n                                              const std::vector<Param> &args, Expr *ret,\n                                              const std::string &altName) {\n  // Get a module name (e.g., os.path)\n  auto components = getImportPath(what);\n\n  if (!ret && args.empty()) {\n    // Simple import: `from python import foo.bar` -> `bar = pyobj._import(\"foo.bar\")`\n    return transform(\n        N<AssignStmt>(N<IdExpr>(altName.empty() ? components.back() : altName),\n                      N<CallExpr>(N<DotExpr>(N<IdExpr>(\"pyobj\"), \"_import\"),\n                                  N<StringExpr>(combine2(components, \".\")))));\n  }\n\n  // Python function import:\n  // `from python import foo.bar(int) -> float` ->\n  // ```def bar(a1: int) -> float:\n  //      f = pyobj._import(\"foo\")._getattr(\"bar\")\n  //      return float.__from_py__(f(a1))```\n\n  // f = pyobj._import(\"foo\")._getattr(\"bar\")\n  auto call = N<AssignStmt>(\n      N<IdExpr>(\"f\"),\n      N<CallExpr>(N<DotExpr>(N<CallExpr>(N<DotExpr>(N<IdExpr>(\"pyobj\"), \"_import\"),\n                                         N<StringExpr>(combine2(\n                                             components, \".\", 0,\n                                             static_cast<int>(components.size()) - 1))),\n                             \"_getattr\"),\n                  N<StringExpr>(components.back())));\n  // f(a1, ...)\n  std::vector<Param> params;\n  std::vector<Expr *> callArgs;\n  for (int i = 0; i < args.size(); i++) {\n    params.emplace_back(fmt::format(\"a{}\", i), clone(args[i].getType()), nullptr);\n    callArgs.emplace_back(N<IdExpr>(fmt::format(\"a{}\", i)));\n  }\n  // `return ret.__from_py__(f(a1, ...))`\n  auto retType = (ret && !cast<NoneExpr>(ret)) ? clone(ret) : N<IdExpr>(\"NoneType\");\n  auto retExpr = N<CallExpr>(N<DotExpr>(clone(retType), \"__from_py__\"),\n                             N<DotExpr>(N<CallExpr>(N<IdExpr>(\"f\"), callArgs), \"p\"));\n  auto retStmt = N<ReturnStmt>(retExpr);\n  // Create a function\n  return transform(N<FunctionStmt>(altName.empty() ? components.back() : altName,\n                                   retType, params, N<SuiteStmt>(call, retStmt)));\n}\n\n/// Import a new file into its own context and wrap its top-level statements into a\n/// function to support Python-like runtime import loading.\n/// @example\n///   ```_import_[I]_done = False\n///      def _import_[I]():\n///        global [imported global variables]...\n///        __name__ = [I]\n///        [imported top-level statements]```\nStmt *TypecheckVisitor::transformNewImport(const ImportFile &file) {\n  // Use a clean context to parse a new file\n  auto moduleID = file.module;\n  std::ranges::replace(moduleID, '.', '_');\n  auto ictx = std::make_shared<TypeContext>(ctx->cache, file.path);\n  ictx->isStdlibLoading = ctx->isStdlibLoading;\n  ictx->moduleName = file;\n  auto &import = ctx->cache->imports[file.path];\n  import.update(file.module, file.path, ictx);\n  import.loadedAtToplevel =\n      getImport(ctx->moduleName.path)->loadedAtToplevel &&\n      (ctx->isStdlibLoading || (ctx->isGlobal() && ctx->scope.size() == 1));\n  auto importVar = import.importVar =\n      getTemporaryVar(fmt::format(\"import_{}\", moduleID));\n  LOG_REALIZE(\"[import] initializing {} (location: {}, toplevel: {})\", importVar,\n              file.path, import.loadedAtToplevel);\n\n  // __name__ = [import name]\n  Stmt *n = nullptr;\n  if (file.module != \"internal.core\") {\n    // str is not defined when loading internal.core; __name__ is not needed anyway\n    n = N<SuiteStmt>(\n        N<AssignStmt>(N<IdExpr>(\"__name__\"), N<StringExpr>(ictx->moduleName.module)),\n        N<AssignStmt>(N<IdExpr>(\"__file__\"), N<StringExpr>(ictx->moduleName.path)));\n    ctx->addBlock();\n    preamble->addStmt(transform(\n        N<AssignStmt>(N<IdExpr>(importVar),\n                      N<CallExpr>(N<IdExpr>(\"Import.__new__\"), N<BoolExpr>(false),\n                                  N<StringExpr>(file.path), N<StringExpr>(file.module)),\n                      N<IdExpr>(\"Import\"))));\n    auto val = ctx->forceFind(importVar);\n    ctx->popBlock();\n    val->scope = {0};\n    val->baseName = \"\";\n    val->moduleName = MODULE_MAIN;\n    val->time = 0;\n    getImport(STDLIB_IMPORT)->ctx->addToplevel(importVar, val);\n    registerGlobal(val->getName());\n  }\n  auto nodeOrErr = parseFile(ctx->cache, file.path);\n  if (!nodeOrErr)\n    throw exc::ParserException(nodeOrErr.takeError());\n  n = N<SuiteStmt>(n, *nodeOrErr);\n  auto tv = TypecheckVisitor(ictx, preamble);\n  if (auto err = ScopingVisitor::apply(ctx->cache, n, &ictx->globalShadows))\n    throw exc::ParserException(std::move(err));\n\n  if (!ctx->cache->errors.empty())\n    throw exc::ParserException(ctx->cache->errors);\n  // Add comment to the top of import for easier dump inspection\n  auto comment =\n      N<CommentStmt>(fmt::format(\"import: {} at {}\", file.module, file.path));\n  auto suite = N<SuiteStmt>(comment, n);\n\n  if (ctx->isStdlibLoading) {\n    // When loading the standard library, imports are not wrapped.\n    // We assume that the standard library has no recursive imports and that all\n    // statements are executed before the user-provided code.\n    return tv.transform(suite);\n  } else {\n    // Generate import identifier\n    auto stmts = N<SuiteStmt>();\n    auto ret = N<ReturnStmt>();\n    ret->setAttribute(Attr::Internal); // do not trigger toplevel ReturnStmt error\n    stmts->addStmt(N<IfStmt>(N<DotExpr>(N<IdExpr>(importVar), \"loaded\"), ret));\n    stmts->addStmt(N<ExprStmt>(\n        N<CallExpr>(N<IdExpr>(\"Import._set_loaded\"),\n                    N<CallExpr>(N<IdExpr>(\"__ptr__\"), N<IdExpr>(importVar)))));\n    stmts->addStmt(suite);\n\n    // Wrap all imported top-level statements into a function.\n    auto fnName = fmt::format(\"{}_call\", importVar);\n    Stmt *fn =\n        N<FunctionStmt>(fnName, N<IdExpr>(\"NoneType\"), std::vector<Param>{}, stmts);\n    fn = tv.transform(fn);\n    tv.realize(ictx->forceFind(fnName)->getType());\n    preamble->addStmt(fn);\n    // LOG_USER(\"[import] done importing {}\", file.module);\n  }\n  return nullptr;\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/infer.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <limits>\n#include <map>\n#include <memory>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include \"codon/cir/attribute.h\"\n#include \"codon/cir/types/types.h\"\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/visitors/scoping/scoping.h\"\n#include \"codon/parser/visitors/translate/translate.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nusing namespace codon::error;\n\nconstexpr int MAX_TYPECHECK_ITER = 1000;\n\nnamespace codon::ast {\n\nusing namespace types;\n\n/// Unify types a (passed by reference) and b.\n/// Destructive operation as it modifies both a and b. If types cannot be unified, raise\n/// an error.\n/// @param a Type (by reference)\n/// @param b Type\n/// @return a\nType *TypecheckVisitor::unify(Type *a, Type *b) const {\n  seqassert(a, \"lhs is nullptr\");\n  if (!((*a) << b)) {\n    types::Type::Unification undo;\n    a->unify(b, &undo);\n    // log(\"[unify] {} {}\", a->debugString(2), b->debugString(2));\n    // log(\"[unify] {} {}\", a->debugString(1), b->debugString(1));\n    E(Error::TYPE_UNIFY, getSrcInfo(), a->prettyString(), b->prettyString());\n    return nullptr;\n  }\n  return a;\n}\n\n/// Infer all types within a Stmt *. Implements the LTS-DI typechecking.\n/// @param isToplevel set if typechecking the program toplevel.\nStmt *TypecheckVisitor::inferTypes(Stmt *result, bool isToplevel) {\n  if (!result)\n    return nullptr;\n\n  for (ctx->getBase()->iteration = 1;; ctx->getBase()->iteration++) {\n    LOG_TYPECHECK(\"[iter] {} :: {}\", ctx->getBase()->name, ctx->getBase()->iteration);\n    if (ctx->getBase()->iteration >= MAX_TYPECHECK_ITER) {\n      // log(\"-> {}\", result->toString(2));\n      ParserErrors errors;\n      errors.addError(\n          {ErrorMessage{fmt::format(\"cannot typecheck '{}' in reasonable time\",\n                                    ctx->getBase()->name.empty()\n                                        ? \"toplevel\"\n                                        : getUnmangledName(ctx->getBase()->name)),\n                        result->getSrcInfo()}});\n      for (auto &error : findTypecheckErrors(result))\n        errors.addError(error);\n      throw exc::ParserException(errors);\n    }\n\n    // Keep iterating until:\n    //   (1) success: the statement is marked as done; or\n    //   (2) failure: no expression or statements were marked as done during an\n    //                iteration (i.e., changedNodes is zero)\n    ctx->typecheckLevel++;\n    auto changedNodes = ctx->changedNodes;\n    ctx->changedNodes = 0;\n    auto returnEarly = ctx->returnEarly;\n    ctx->returnEarly = false;\n    auto tv = TypecheckVisitor(ctx, preamble);\n    // if (preamble) {\n    //   auto pt = tv.transform(preamble);\n    //   preamble = cast<SuiteStmt>(pt);\n    // }\n    result = tv.transform(result);\n    std::swap(ctx->changedNodes, changedNodes);\n    std::swap(ctx->returnEarly, returnEarly);\n    ctx->typecheckLevel--;\n\n    if (ctx->getBase()->iteration == 1 && isToplevel) {\n      // Realize all @force_realize functions\n      for (auto &f : ctx->cache->functions) {\n        auto ast = f.second.ast;\n        if (f.second.type && f.second.realizations.empty() &&\n            (ast->hasAttribute(Attr::ForceRealize) || ast->hasAttribute(Attr::Export) ||\n             (ast->hasAttribute(Attr::C) && !ast->hasAttribute(Attr::CVarArg)))) {\n          seqassert(f.second.type->canRealize(), \"cannot realize {}\", f.first);\n          LOG_REALIZE(\"[force_realize] {}\", f.second.getType()->debugString(2));\n          realize(instantiateType(f.second.getType()));\n          seqassert(!f.second.realizations.empty(), \"cannot realize {}\", f.first);\n        }\n      }\n    }\n\n    if (result->isDone()) {\n      // Special union case: if union cannot be inferred return type is Union[NoneType]\n      if (auto tr = ctx->getBase()->returnType) {\n        if (auto tu = tr->getUnion()) {\n          if (!tu->isSealed()) {\n            if (tu->pendingTypes[0]->getLink() &&\n                tu->pendingTypes[0]->getLink()->kind == LinkType::Unbound) {\n              auto r = tu->addType(getStdLibType(\"NoneType\"));\n              seqassert(r, \"cannot add type to union {}\", tu->debugString(2));\n              tu->seal();\n            }\n          }\n        }\n      }\n      break;\n    } else if (changedNodes) {\n      continue;\n    } else {\n      // Special case: nothing was changed, however there are unbound types that have\n      // default values (e.g., generics with default values). Unify those types with\n      // their default values and then run another round to see if anything changed.\n      bool anotherRound = false;\n      // Special case: return type might have default as well (e.g., Union)\n      if (auto t = ctx->getBase()->returnType) {\n        ctx->getBase()->pendingDefaults[0].insert(t);\n      }\n      // First unify \"explicit\" generics (whose default type is explicit),\n      // then \"implicit\" ones (whose default type is compiler generated,\n      // e.g. compiler-generated variable placeholders with default NoneType)\n      for (auto &unbounds : ctx->getBase()->pendingDefaults | std::views::values) {\n        if (!unbounds.empty()) {\n          for (const auto &unbound : unbounds) {\n            if (auto tu = unbound->getUnion()) {\n              // Seal all dynamic unions after the iteration is over\n              if (!tu->isSealed()) {\n                tu->seal();\n                anotherRound = true;\n              }\n            } else if (auto u = unbound->getLink()) {\n              types::Type::Unification undo;\n              if (u->defaultType) {\n                if (u->defaultType->getClass()) { // type[...]\n                  if (u->unify(extractClassType(u->defaultType.get()), &undo) >= 0) {\n                    anotherRound = true;\n                  }\n                } else { // generic\n                  if (u->unify(u->defaultType.get(), &undo) >= 0) {\n                    anotherRound = true;\n                  }\n                }\n              }\n            }\n          }\n          unbounds.clear();\n          if (anotherRound)\n            break;\n        }\n      }\n      if (anotherRound)\n        continue;\n      // Nothing helps. Return nullptr.\n      return nullptr;\n    }\n  }\n\n  return result;\n}\n\n/// Realize a type and create IR type stub. If type is a function type, also realize the\n/// underlying function and generate IR function stub.\n/// @return realized type or nullptr if the type cannot be realized\ntypes::Type *TypecheckVisitor::realize(types::Type *typ) {\n  if (!typ || !typ->canRealize()) {\n    return nullptr;\n  }\n\n  try {\n    if (auto f = typ->getFunc()) {\n      // Cache::CTimer t(ctx->cache, f->realizedName());\n      if (auto ret = realizeFunc(f)) {\n        // Realize Function[..] type as well\n        auto t = std::make_shared<ClassType>(ret->getClass());\n        realizeType(t.get());\n        // Needed for return type unification\n        unify(f->getRetType(), extractClassGeneric(ret, 1));\n        return ret;\n      }\n    } else if (auto c = typ->getClass()) {\n      auto t = realizeType(c);\n      return t;\n    }\n  } catch (exc::ParserException &exc) {\n    seqassert(!exc.getErrors().empty(), \"empty error trace\");\n    auto &bt = exc.getErrors().back();\n    if (bt.front().getErrorCode() == Error::MAX_REALIZATION)\n      throw;\n    if (auto f = typ->getFunc()) {\n      if (f->ast->hasAttribute(Attr::HiddenFromUser)) {\n        bt.back().setSrcInfo(getSrcInfo());\n      } else {\n        std::vector<std::string> args;\n        for (size_t i = 0, ai = 0, gi = 0; i < f->ast->size(); i++) {\n          auto [ns, n] = (*f->ast)[i].getNameWithStars();\n          args.push_back(fmt::format(\n              \"{}{}: {}\", std::string(ns, '*'), getUserFacingName(n),\n              (*f->ast)[i].isGeneric() ? extractFuncGeneric(f, gi++)->prettyString()\n                                       : extractFuncArgType(f, ai++)->prettyString()));\n        }\n        auto name = f->ast->name;\n        std::string name_args;\n        if (startswith(name, \"%_import_\")) {\n          for (auto &i : ctx->cache->imports | std::views::values)\n            if (getMangledFunc(\"\", i.importVar + \"_call\") == name) {\n              name = i.name;\n              break;\n            }\n          name = fmt::format(\"<import {}>\", name);\n        } else {\n          name = getUserFacingName(f->ast->getName());\n          name_args = fmt::format(\"({})\", join(args, \", \"));\n        }\n        bt.addMessage(fmt::format(\"during the realization of {}{}\", name, name_args),\n                      getSrcInfo());\n      }\n    } else {\n      bt.addMessage(fmt::format(\"during the realization of {}\", typ->prettyString()),\n                    getSrcInfo());\n    }\n    throw;\n  }\n  return nullptr;\n}\n\n/// Realize a type and create IR type stub.\n/// @return realized type or nullptr if the type cannot be realized\ntypes::Type *TypecheckVisitor::realizeType(types::ClassType *type) {\n  if (!type || !type->canRealize())\n    return nullptr;\n  // Check if the type fields are all initialized\n  // (sometimes that's not the case: e.g., `class X: x: List[X]`)\n\n  // generalize generics to ensure that they do not get unified later!\n  if (type->is(\"unrealized_type\"))\n    type->generics[0].type = extractClassGeneric(type)->generalize(0);\n\n  if (type->is(\"__NTuple__\")) {\n    auto n = std::max(static_cast<int64_t>(0), getIntLiteral(type));\n    auto tt = extractClassGeneric(type, 1)->getClass();\n    std::vector<ClassType::Generic> generics;\n    auto t = instantiateType(generateTuple(n * tt->generics.size()));\n    for (size_t i = 0, j = 0; i < n; i++)\n      for (const auto &ttg : tt->generics) {\n        unify(t->generics[j].getType(), ttg.getType());\n        generics.push_back(t->generics[j]);\n        j++;\n      }\n    type->name = TYPE_TUPLE;\n    type->generics = generics;\n    type->_rn = \"\";\n  }\n\n  // Check if the type was already realized\n  auto rn = type->ClassType::realizedName();\n  auto cls = getClass(type);\n  if (auto r = in(cls->realizations, rn)) {\n    return (*r)->type->getClass();\n  }\n\n  auto realized = type->getClass();\n  auto fields = getClassFields(realized);\n  if (!cls->ast)\n    return nullptr; // not yet done!\n  auto fTypes = getClassFieldTypes(realized);\n  for (auto &field : fTypes) {\n    if (!field)\n      return nullptr;\n  }\n\n  if (auto s = type->getStatic())\n    realized =\n        s->getNonStaticType()->getClass(); // do not cache static but its root type!\n\n  // Realize generics\n  if (!type->is(\"unrealized_type\"))\n    for (auto &e : realized->generics) {\n      if (!realize(e.getType()))\n        return nullptr;\n      if (e.type->getFunc() && !e.type->getFunc()->getRetType()->canRealize())\n        return nullptr;\n    }\n\n  // Realizations should always be visible, so add them to the toplevel\n  rn = type->ClassType::realizedName();\n  auto rt = std::static_pointer_cast<ClassType>(realized->generalize(0));\n  auto val = std::make_shared<TypecheckItem>(rn, \"\", ctx->getModule(), rt);\n  if (!val->type->is(TYPE_TYPE))\n    val->type = instantiateTypeVar(realized);\n  ctx->addAlwaysVisible(val, true);\n  auto realization = getClass(realized)->realizations[rn] =\n      std::make_shared<Cache::Class::ClassRealization>();\n  realization->type = rt;\n  realization->id = ++ctx->cache->classRealizationCnt;\n\n  const auto &mros = getClass(realized)->mro;\n  for (size_t i = 1; i < mros.size(); i++) {\n    auto mt = instantiateType(mros[i].get(), realized);\n    seqassert(mt->canRealize(), \"cannot realize {}\", mt->debugString(2));\n    realization->bases.push_back(mt);\n  }\n\n  // Create LLVM stub\n  auto lt = makeIRType(realized);\n\n  // Realize fields\n  std::vector<ir::types::Type *> typeArgs;   // needed for IR\n  std::vector<std::string> names;            // needed for IR\n  std::map<std::string, SrcInfo> memberInfo; // needed for IR\n  for (size_t i = 0; i < fTypes.size(); i++) {\n    if (!realize(fTypes[i].get())) {\n      // realize(fTypes[i].get());\n      E(Error::TYPE_CANNOT_REALIZE_ATTR, getSrcInfo(), fields[i].name,\n        realized->prettyString());\n    }\n    // LOG_REALIZE(\"- member: {} -> {}: {}\", field.name, field.type, fTypes[i]);\n    realization->fields.emplace_back(fields[i].name, fTypes[i]);\n    names.emplace_back(fields[i].name);\n    typeArgs.emplace_back(makeIRType(fTypes[i]->getClass()));\n    memberInfo[fields[i].name] = fTypes[i]->getSrcInfo();\n  }\n\n  // Set IR attributes\n  if (!names.empty()) {\n    if (auto *ir = cast<ir::types::RefType>(lt)) {\n      ir->getContents()->realize(typeArgs, names);\n      ir->setAttribute(std::make_unique<ir::MemberAttribute>(memberInfo));\n      ir->getContents()->setAttribute(\n          std::make_unique<ir::MemberAttribute>(memberInfo));\n    }\n  }\n\n  return rt.get();\n}\n\ntypes::Type *TypecheckVisitor::realizeFunc(types::FuncType *type, bool force) {\n  auto module = type->ast->getAttribute<ir::StringValueAttribute>(Attr::Module)->value;\n  auto &realizations = getFunction(type->getFuncName())->realizations;\n  auto imp = getImport(module);\n  if (auto r = in(realizations, type->realizedName())) {\n    if (!force) {\n      return (*r)->getType();\n    }\n  }\n\n  // auto *_t = new Cache::CTimer(ctx->cache, ctx->getRealizationStackName() + \":\" +\n  //                                              type->realizedName());\n\n  auto oldCtx = this->ctx;\n  this->ctx = imp->ctx;\n  if (ctx->getRealizationDepth() > MAX_REALIZATION_DEPTH) {\n    E(Error::MAX_REALIZATION, getSrcInfo(), getUserFacingName(type->getFuncName()));\n  }\n\n  bool isImport = isImportFn(type->getFuncName());\n  if (!isImport) {\n    getLogger().level++;\n    ctx->addBlock();\n    ctx->typecheckLevel++;\n    ctx->bases.push_back({type->getFuncName(), type->getFunc()->shared_from_this(),\n                          type->getRetType()->shared_from_this()});\n    for (size_t t = ctx->bases.size() - 1; t-- > 0;) {\n      if (startswith(ctx->getBaseName(), ctx->bases[t].name)) {\n        ctx->getBase()->parent = static_cast<int>(t);\n        break;\n      }\n    }\n    // LOG(\"[realize] F {} -> {} : base {} ; depth = {} ; ctx-base: {}; ret = {}; \"\n    //     \"parent = {}\",\n    //     type->getFuncName(), type->realizedName(), ctx->getRealizationStackName(),\n    //     ctx->getRealizationDepth(), ctx->getBaseName(),\n    //     ctx->getBase()->returnType->debugString(2),\n    //     ctx->bases[ctx->getBase()->parent].name);\n  }\n\n  // Types might change after realization, fix it\n  for (auto &t : *type)\n    realizeType(t.getType()->getClass());\n\n  // Clone the generic AST that is to be realized\n  auto ast = clean_clone(type->ast);\n  if (auto s = generateSpecialAst(type))\n    ast->suite = s;\n  addClassGenerics(type, true);\n  ctx->getBase()->func = ast;\n\n  // Internal functions have no AST that can be realized\n  bool hasAst = ast->getSuite() && !ast->hasAttribute(Attr::Internal);\n  // Add function arguments\n  if (auto b = ast->getAttribute<BindingsAttribute>(Attr::Bindings)) {\n    for (auto &[c, t] : b->captures) {\n      if (t == BindingsAttribute::CaptureType::Global) {\n        auto cp = ctx->find(c);\n        if (!cp)\n          E(Error::ID_NOT_FOUND, getSrcInfo(), c);\n        if (!cp->isGlobal())\n          E(Error::FN_GLOBAL_NOT_FOUND, getSrcInfo(), \"global\", c);\n      }\n    }\n    for (const auto [name, canonical] : b->localRenames) {\n      auto val = ctx->forceFind(canonical);\n      ctx->add(name, val);\n    }\n  }\n  for (size_t i = 0, j = 0, gi = 0; hasAst && i < ast->size(); i++) {\n    auto [_, varName] = (*ast)[i].getNameWithStars();\n    auto un = getUnmangledName(varName);\n    if ((*ast)[i].isValue()) {\n      TypePtr at = extractFuncArgType(type, j++)->shared_from_this();\n      bool isStatic = ast && getStaticGeneric((*ast)[i].getType());\n      if (!isStatic && at && at->getStatic())\n        at = at->getStatic()->getNonStaticType()->shared_from_this();\n\n      if (startswith(un, \"$\"))\n        un = un.substr(1);\n      if (at->is(\"TypeWrap\")) {\n        ctx->addType(un, varName, instantiateTypeVar(extractClassGeneric(at.get())));\n      } else {\n        ctx->addVar(un, varName, std::make_shared<LinkType>(at));\n      }\n    } else {\n      if (startswith(un, \"$\")) {\n        un = un.substr(1);\n        auto g = type->funcGenerics[gi];\n        auto t = g.type;\n        if (!g.staticKind && !t->is(TYPE_TYPE))\n          t = instantiateTypeVar(t.get());\n        auto v = ctx->addType(un, varName, t);\n        v->generic = true;\n      }\n      gi++;\n    }\n  }\n\n  // Populate realization table in advance to support recursive realizations\n  auto key = type->realizedName(); // note: the key might change later\n  ir::Func *oldIR = nullptr;       // Get it if it was already made (force mode)\n  if (auto i = in(realizations, key))\n    oldIR = (*i)->ir;\n  auto r = realizations[key] = std::make_shared<Cache::Function::FunctionRealization>();\n  r->type = std::static_pointer_cast<FuncType>(type->shared_from_this());\n  r->ir = oldIR;\n  if (auto b = ast->getAttribute<BindingsAttribute>(Attr::Bindings))\n    for (const auto &c : b->captures | std::views::keys) {\n      auto h = ctx->find(c);\n      r->captures.push_back(h ? h->canonicalName : \"\");\n    }\n\n  // Realizations should always be visible, so add them to the toplevel\n  auto val = std::make_shared<TypecheckItem>(key, \"\", ctx->getModule(),\n                                             type->shared_from_this());\n  ctx->addAlwaysVisible(val, true);\n\n  ctx->getBase()->suite = ast->getSuite();\n  if (hasAst) {\n    auto oldBlockLevel = ctx->blockLevel;\n    ctx->blockLevel = 0;\n    auto ret = inferTypes(ctx->getBase()->suite);\n    ctx->blockLevel = oldBlockLevel;\n\n    if (!ret) {\n      realizations.erase(key);\n      ParserErrors errors;\n      if (!startswith(ast->name, \"%_lambda\")) {\n        // Lambda typecheck failures are \"ignored\" as they are treated as statements,\n        // not functions.\n        // TODO: generalize this further.\n        errors = findTypecheckErrors(ctx->getBase()->suite);\n      }\n      if (!isImport) {\n        ctx->bases.pop_back();\n        ctx->popBlock();\n        ctx->typecheckLevel--;\n        getLogger().level--;\n      }\n      if (!errors.empty()) {\n        throw exc::ParserException(errors);\n      }\n      this->ctx = oldCtx;\n      return nullptr; // inference must be delayed\n    } else {\n      ctx->getBase()->suite = ret;\n    }\n    // Use NoneType as the return type when the return type is not specified and\n    // function has no return statement\n    if (!ast->getReturn() && isUnbound(type->getRetType())) {\n      auto rt = getStdLibType(\"NoneType\")->shared_from_this();\n      if (ast->isAsync())\n        rt = instantiateType(getStdLibType(\"Coroutine\"), {rt.get()});\n      unify(type->getRetType(), rt.get());\n    }\n  }\n  // Realize the return type\n  auto ret = realize(type->getRetType());\n  if (type->hasUnbounds(/*includeGenerics*/ false)) {\n    // log(\"cannot realize {}; undoing...\", type->debugString(2));\n    realizations.erase(key);\n    ctx->bases.pop_back();\n    ctx->popBlock();\n    ctx->typecheckLevel--;\n    getLogger().level--;\n    return nullptr;\n  }\n  seqassert(ret, \"cannot realize return type '{}'\", *(type->getRetType()));\n\n  std::vector<Param> args;\n  for (auto &i : *ast) {\n    auto [_, varName] = i.getNameWithStars();\n    args.emplace_back(varName, nullptr, nullptr, i.status);\n  }\n  r->ast =\n      N<FunctionStmt>(r->type->realizedName(), nullptr, args, ctx->getBase()->suite,\n                      std::vector<Expr *>{}, ast->isAsync());\n  r->ast->setSrcInfo(ast->getSrcInfo());\n  r->ast->cloneAttributesFrom(ast);\n\n  auto newType = std::static_pointer_cast<types::FuncType>(type->generalize(0));\n  auto newKey = newType->realizedName();\n\n  if (!in(ctx->cache->pendingRealizations, make_pair(newType->getFuncName(), newKey))) {\n    realizations[newKey] = r;\n  } else {\n    realizations[key] = realizations[newKey];\n  }\n  if (force)\n    realizations[newKey]->ast = r->ast;\n  r->type = newType;\n  if (!r->ir)\n    r->ir = makeIRFunction(r);\n  val = std::make_shared<TypecheckItem>(newKey, \"\", ctx->getModule(), r->type);\n  ctx->addAlwaysVisible(val, true);\n  if (!isImport) {\n    ctx->bases.pop_back();\n    ctx->popBlock();\n    ctx->typecheckLevel--;\n    getLogger().level--;\n  }\n  this->ctx = oldCtx;\n\n  LOG_REALIZE(\"[func] {}\", r->getType()->debugString(2));\n\n  return r->getType();\n}\n\n/// Make IR node for a realized type.\nir::types::Type *TypecheckVisitor::makeIRType(types::ClassType *t) {\n  // Realize if not, and return cached value if it exists\n  auto realizedName = t->ClassType::realizedName();\n  auto cls = ctx->cache->getClass(t);\n  if (!in(cls->realizations, realizedName)) {\n    t = realize(t->getClass())->getClass();\n    realizedName = t->ClassType::realizedName();\n    cls = ctx->cache->getClass(t);\n  }\n  if (auto l = cls->realizations[realizedName]->ir) {\n    if (cls->rtti)\n      cast<ir::types::RefType>(l)->setPolymorphic();\n    return l;\n  }\n\n  auto forceFindIRType = [&](Type *tt) {\n    auto ttc = tt->getClass();\n    auto rn = ttc->ClassType::realizedName();\n    auto ttcls = ctx->cache->getClass(ttc);\n    seqassert(ttc && in(ttcls->realizations, rn), \"{} not realized\", *tt);\n    auto l = ttcls->realizations[rn]->ir;\n    seqassert(l, \"no LLVM type for {}\", *tt);\n    return l;\n  };\n\n  // Prepare generics and statics\n  std::vector<ir::types::Type *> types;\n  std::vector<types::StaticType *> statics;\n  if (t->is(\"unrealized_type\"))\n    types.push_back(nullptr);\n  else\n    for (auto &m : t->generics) {\n      if (auto s = m.type->getStatic())\n        statics.push_back(s);\n      else\n        types.push_back(forceFindIRType(m.getType()));\n    }\n\n  // Get the IR type\n  auto *module = ctx->cache->module;\n  ir::types::Type *handle = nullptr;\n\n  if (t->name == \"bool\") {\n    handle = module->getBoolType();\n  } else if (t->name == \"byte\") {\n    handle = module->getByteType();\n  } else if (t->name == \"int\") {\n    handle = module->getIntType();\n  } else if (t->name == \"float\") {\n    handle = module->getFloatType();\n  } else if (t->name == \"float32\") {\n    handle = module->getFloat32Type();\n  } else if (t->name == \"float16\") {\n    handle = module->getFloat16Type();\n  } else if (t->name == \"bfloat16\") {\n    handle = module->getBFloat16Type();\n  } else if (t->name == \"float128\") {\n    handle = module->getFloat128Type();\n  } else if (t->name == \"str\") {\n    handle = module->getStringType();\n  } else if (t->name == \"Int\" || t->name == \"UInt\") {\n    handle =\n        module->Nr<ir::types::IntNType>(getIntLiteral(statics[0]), t->name == \"Int\");\n  } else if (t->name == \"Ptr\") {\n    seqassert(types.size() == 1, \"bad generics/statics\");\n    handle = module->unsafeGetPointerType(types[0]);\n  } else if (t->name == \"Generator\" || t->name == \"AsyncGenerator\") {\n    seqassert(types.size() == 1, \"bad generics/statics\");\n    handle = module->unsafeGetGeneratorType(types[0]);\n  } else if (t->name == \"Coroutine\") {\n    seqassert(types.size() == 1, \"bad generics/statics\");\n    handle = module->unsafeGetGeneratorType(types[0]);\n  } else if (t->name == TYPE_OPTIONAL) {\n    seqassert(types.size() == 1, \"bad generics/statics\");\n    handle = module->unsafeGetOptionalType(types[0]);\n  } else if (t->name == \"NoneType\") {\n    seqassert(types.empty() && statics.empty(), \"bad generics/statics\");\n    auto record =\n        cast<ir::types::RecordType>(module->unsafeGetMemberedType(realizedName));\n    record->realize({}, {});\n    handle = record;\n  } else if (t->name == \"Union\") {\n    seqassert(!types.empty(), \"bad union\");\n    auto unionTypes = t->getUnion()->getRealizationTypes();\n    std::vector<ir::types::Type *> unionVec;\n    unionVec.reserve(unionTypes.size());\n    for (auto &u : unionTypes)\n      unionVec.emplace_back(forceFindIRType(u));\n    handle = module->unsafeGetUnionType(unionVec);\n  } else if (t->name == \"Function\") {\n    types.clear();\n    for (auto &m : extractClassGeneric(t)->getClass()->generics)\n      types.push_back(forceFindIRType(m.getType()));\n    auto ret = forceFindIRType(extractClassGeneric(t, 1));\n    handle = module->unsafeGetFuncType(realizedName, ret, types);\n  } else if (t->name == getMangledClass(\"std.simd\", \"Vec\")) {\n    seqassert(types.size() == 1 && !statics.empty(), \"bad generics/statics\");\n    handle = module->unsafeGetVectorType(getIntLiteral(statics[0]), types[0]);\n  } else {\n    // Type arguments will be populated afterwards to avoid infinite loop with recursive\n    // reference types (e.g., `class X: x: Optional[X]`)\n    if (t->isRecord()) {\n      std::vector<ir::types::Type *> typeArgs;   // needed for IR\n      std::vector<std::string> names;            // needed for IR\n      std::map<std::string, SrcInfo> memberInfo; // needed for IR\n\n      seqassert(!t->is(\"__NTuple__\"), \"ntuple not inlined\");\n      auto ft = getClassFieldTypes(t->getClass());\n      const auto &fields = cls->fields;\n      for (size_t i = 0; i < ft.size(); i++) {\n        if (!realize(ft[i].get())) {\n          E(Error::TYPE_CANNOT_REALIZE_ATTR, getSrcInfo(), fields[i].name,\n            t->prettyString());\n        }\n        names.emplace_back(fields[i].name);\n        typeArgs.emplace_back(makeIRType(ft[i]->getClass()));\n        memberInfo[fields[i].name] = ft[i]->getSrcInfo();\n      }\n      auto record =\n          cast<ir::types::RecordType>(module->unsafeGetMemberedType(realizedName));\n      record->realize(typeArgs, names);\n      handle = record;\n      handle->setAttribute(\n          std::make_unique<ir::MemberAttribute>(std::move(memberInfo)));\n    } else {\n      handle = module->unsafeGetMemberedType(realizedName, !t->isRecord());\n      if (cls->rtti)\n        cast<ir::types::RefType>(handle)->setPolymorphic();\n    }\n  }\n  handle->setSrcInfo(t->getSrcInfo());\n  handle->setAstType(\n      std::const_pointer_cast<codon::ast::types::Type>(t->shared_from_this()));\n  return cls->realizations[realizedName]->ir = handle;\n}\n\n/// Make IR node for a realized function.\nir::Func *TypecheckVisitor::makeIRFunction(\n    const std::shared_ptr<Cache::Function::FunctionRealization> &r) {\n  ir::Func *fn = nullptr;\n  auto irm = ctx->cache->module;\n  // Create and store a function IR node and a realized AST for IR passes\n  if (r->ast->hasAttribute(Attr::Internal)) {\n    // e.g., __new__, Ptr.__new__, etc.\n    fn = irm->Nr<ir::InternalFunc>(r->type->ast->name);\n  } else if (r->ast->hasAttribute(Attr::LLVM)) {\n    fn = irm->Nr<ir::LLVMFunc>(r->type->realizedName());\n  } else if (r->ast->hasAttribute(Attr::C)) {\n    fn = irm->Nr<ir::ExternalFunc>(r->type->realizedName());\n  } else {\n    fn = irm->Nr<ir::BodiedFunc>(r->type->realizedName());\n  }\n  fn->setUnmangledName(ctx->cache->reverseIdentifierLookup[r->type->ast->name]);\n  auto parent = r->type->funcParent;\n  if (auto aa = r->ast->getAttribute<ir::StringValueAttribute>(Attr::ParentClass)) {\n    if (!aa->value.empty() && !r->ast->hasAttribute(Attr::Method)) {\n      // Hack for non-generic methods\n      parent = ctx->find(aa->value)->type;\n    }\n  }\n  if (parent && parent->isInstantiated() && parent->canRealize()) {\n    parent = extractClassType(parent.get())->shared_from_this();\n    realize(parent.get());\n    fn->setParentType(makeIRType(parent->getClass()));\n  }\n  fn->setGlobal();\n  // Mark this realization as pending (i.e., realized but not translated)\n  ctx->cache->pendingRealizations.insert({r->type->ast->name, r->type->realizedName()});\n\n  seqassert(!r->type ||\n                r->ast->size() == r->type->size() + r->type->funcGenerics.size(),\n            \"type/AST argument mismatch\");\n\n  // Populate the IR node\n  std::vector<std::string> names;\n  std::vector<codon::ir::types::Type *> types;\n  for (size_t i = 0, j = 0; i < r->ast->size(); i++) {\n    if ((*r->ast)[i].isValue()) {\n      if (!extractFuncArgType(r->getType(), j)->getFunc()) {\n        types.push_back(makeIRType(extractFuncArgType(r->getType(), j)->getClass()));\n        names.push_back(ctx->cache->reverseIdentifierLookup[(*r->ast)[i].getName()]);\n      }\n      j++;\n    }\n  }\n  if (r->ast->hasAttribute(Attr::CVarArg)) {\n    types.pop_back();\n    names.pop_back();\n  }\n  auto irType = irm->unsafeGetFuncType(r->type->realizedName(),\n                                       makeIRType(r->type->getRetType()->getClass()),\n                                       types, r->ast->hasAttribute(Attr::CVarArg));\n  irType->setAstType(r->type->shared_from_this());\n  fn->realize(irType, names);\n  return fn;\n}\n\nir::Func *TypecheckVisitor::realizeIRFunc(types::FuncType *fn,\n                                          const std::vector<types::TypePtr> &generics) {\n  // TODO: used by cytonization. Probably needs refactoring.\n  auto fnType = instantiateType(fn);\n  types::Type::Unification u;\n  for (size_t i = 0; i < generics.size(); i++)\n    fnType->getFunc()->funcGenerics[i].type->unify(generics[i].get(), &u);\n  if (!realize(fnType.get()))\n    return nullptr;\n\n  auto pr = ctx->cache->pendingRealizations; // copy it as it might be modified\n  for (const auto &key : pr | std::views::keys)\n    TranslateVisitor(ctx->cache->codegenCtx)\n        .translateStmts(clone(getFunction(key)->ast));\n  return getFunction(fn->ast->getName())->realizations[fnType->realizedName()]->ir;\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/loops.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <string>\n#include <tuple>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/match.h\"\n#include \"codon/parser/peg/peg.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nusing namespace codon::error;\nnamespace codon::ast {\n\nusing namespace types;\nusing namespace matcher;\n\n/// Ensure that `break` is in a loop.\n/// Transform if a loop break variable is available\n/// (e.g., a break within loop-else block).\n/// @example\n///   `break` -> `no_break = False; break`\n\nvoid TypecheckVisitor::visit(BreakStmt *stmt) {\n  if (!ctx->getBase()->getLoop())\n    E(Error::EXPECTED_LOOP, stmt, \"break\");\n  ctx->getBase()->getLoop()->flat = false;\n  if (!ctx->getBase()->getLoop()->breakVar.empty()) {\n    resultStmt =\n        N<SuiteStmt>(transform(N<AssignStmt>(\n                         N<IdExpr>(ctx->getBase()->getLoop()->breakVar),\n                         N<BoolExpr>(false), nullptr, AssignStmt::UpdateMode::Update)),\n                     N<BreakStmt>());\n  } else {\n    stmt->setDone();\n    if (!ctx->staticLoops.back().empty()) {\n      auto a = N<AssignStmt>(N<IdExpr>(ctx->staticLoops.back()), N<BoolExpr>(false));\n      a->setUpdate();\n      resultStmt = transform(N<SuiteStmt>(a, stmt));\n    }\n  }\n}\n\n/// Ensure that `continue` is in a loop\nvoid TypecheckVisitor::visit(ContinueStmt *stmt) {\n  if (!ctx->getBase()->getLoop())\n    E(Error::EXPECTED_LOOP, stmt, \"continue\");\n  ctx->getBase()->getLoop()->flat = false;\n\n  stmt->setDone();\n  if (!ctx->staticLoops.back().empty()) {\n    resultStmt = N<BreakStmt>();\n    resultStmt->setDone();\n  }\n}\n\n/// Transform a while loop.\n/// @example\n///   `while cond: ...`           ->  `while cond: ...`\n///   `while cond: ... else: ...` -> ```no_break = True\n///                                     while cond:\n///                                       ...\n///                                     if no_break: ...```\nvoid TypecheckVisitor::visit(WhileStmt *stmt) {\n  // Check for while-else clause\n  std::string breakVar;\n  if (stmt->getElse() && stmt->getElse()->firstInBlock()) {\n    // no_break = True\n    breakVar = getTemporaryVar(\"no_break\");\n    prependStmts->push_back(\n        transform(N<AssignStmt>(N<IdExpr>(breakVar), N<BoolExpr>(true))));\n  }\n\n  ctx->staticLoops.push_back(stmt->gotoVar.empty() ? \"\" : stmt->gotoVar);\n  ctx->getBase()->loops.emplace_back(breakVar);\n\n  auto oldExpectedType = getStdLibType(\"bool\")->shared_from_this();\n  std::swap(ctx->expectedType, oldExpectedType);\n  stmt->cond = transform(stmt->getCond());\n  std::swap(ctx->expectedType, oldExpectedType);\n  wrapExpr(&stmt->cond, getStdLibType(\"bool\"));\n\n  ctx->blockLevel++;\n  stmt->suite = SuiteStmt::wrap(transform(stmt->getSuite()));\n  ctx->blockLevel--;\n  ctx->staticLoops.pop_back();\n\n  // Complete while-else clause\n  if (stmt->getElse() && stmt->getElse()->firstInBlock()) {\n    auto es = stmt->getElse();\n    stmt->elseSuite = nullptr;\n    resultStmt = transform(N<SuiteStmt>(stmt, N<IfStmt>(N<IdExpr>(breakVar), es)));\n  }\n  ctx->getBase()->loops.pop_back();\n\n  if (stmt->getCond()->isDone() && stmt->getSuite()->isDone())\n    stmt->setDone();\n}\n\n/// Typecheck for statements. Wrap the iterator expression with `__iter__` if needed.\n/// See @c transformHeterogenousTupleFor for iterating heterogenous tuples.\nvoid TypecheckVisitor::visit(ForStmt *stmt) {\n  stmt->decorator = transformForDecorator(stmt->getDecorator());\n\n  if (auto fc = cast<CallExpr>(stmt->getDecorator())) {\n    if (auto fi = cast<IdExpr>(fc->getExpr());\n        fi && fi->getType()->getFunc() &&\n        fi->getType()->getFunc()->getFuncName() ==\n            getMangledFunc(\"std.openmp\", \"for_par\")) {\n      if (auto n = extractFuncGeneric(fi->getType(), 3)->getBoolStatic();\n          n && n->value) {\n        prependStmts->push_back(\n            transform(N<ImportStmt>(N<IdExpr>(\"gpu\"), nullptr, std::vector<Param>{},\n                                    nullptr, getTemporaryVar(\"_\"))));\n      }\n    }\n  }\n\n  std::string breakVar;\n  // Needs in-advance transformation to prevent name clashes with the iterator variable\n  stmt->getIter()->setAttribute(\n      Attr::ExprNoSpecial); // do not expand special calls here,\n                            // might be needed for statis loops!\n  stmt->iter = transform(stmt->getIter());\n\n  // Check for for-else clause\n  Stmt *assign = nullptr;\n  if (stmt->getElse() && stmt->getElse()->firstInBlock()) {\n    breakVar = getTemporaryVar(\"no_break\");\n    assign = transform(N<AssignStmt>(N<IdExpr>(breakVar), N<BoolExpr>(true)));\n  }\n\n  // Extract the iterator type of the for\n  auto iterType = extractClassType(stmt->getIter());\n  if (!iterType)\n    return; // wait until the iterator is known\n\n  auto [delay, staticLoop] = transformStaticForLoop(stmt);\n  if (delay)\n    return;\n  if (staticLoop) {\n    resultStmt = staticLoop;\n    return;\n  }\n\n  // Replace for (i, j) in ... { ... } with for tmp in ...: { i, j = tmp ; ... }\n  if (!cast<IdExpr>(stmt->getVar())) {\n    auto var = N<IdExpr>(ctx->cache->getTemporaryVar(\"for\"));\n    auto ns = unpackAssignment(stmt->getVar(), var);\n    stmt->suite = N<SuiteStmt>(ns, stmt->getSuite());\n    stmt->var = var;\n  }\n\n  // Case: iterating a non-generator. Wrap with `__iter__`\n  bool isGenerator =\n      iterType->name == (stmt->isAsync() ? \"AsyncGenerator\" : \"Generator\");\n  if (!isGenerator && !stmt->isWrapped()) {\n    stmt->iter = transform(N<CallExpr>(N<DotExpr>(stmt->getIter(), \"__iter__\")));\n    iterType = extractClassType(stmt->getIter());\n    stmt->wrapped = true;\n    if (!iterType)\n      return;\n    isGenerator = iterType->name == (stmt->isAsync() ? \"AsyncGenerator\" : \"Generator\");\n  }\n\n  ctx->getBase()->loops.emplace_back(breakVar);\n  auto var = cast<IdExpr>(stmt->getVar());\n  seqassert(var, \"corrupt for variable: {}\", *(stmt->getVar()));\n\n  if (!var->hasAttribute(Attr::ExprDominated) &&\n      !var->hasAttribute(Attr::ExprDominatedUsed)) {\n    ctx->addVar(\n        getUnmangledName(var->getValue()), ctx->generateCanonicalName(var->getValue()),\n        stmt->getVar()->getType() ? stmt->getVar()->getType()->shared_from_this()\n                                  : instantiateUnbound(),\n        getTime());\n  } else if (var->hasAttribute(Attr::ExprDominatedUsed)) {\n    var->eraseAttribute(Attr::ExprDominatedUsed);\n    var->setAttribute(Attr::ExprDominated);\n    stmt->suite = N<SuiteStmt>(\n        N<AssignStmt>(N<IdExpr>(fmt::format(\"{}{}\", var->getValue(), VAR_USED_SUFFIX)),\n                      N<BoolExpr>(true), nullptr, AssignStmt::UpdateMode::Update),\n        stmt->getSuite());\n  }\n  stmt->var = transform(stmt->getVar());\n\n  // Unify iterator variable and the iterator type\n  if (iterType && !isGenerator)\n    E(Error::EXPECTED_GENERATOR, stmt->getIter());\n  if (iterType)\n    unify(stmt->getVar()->getType(), extractClassGeneric(iterType));\n\n  ctx->staticLoops.emplace_back();\n  ctx->blockLevel++;\n  stmt->suite = SuiteStmt::wrap(transform(stmt->getSuite()));\n  ctx->blockLevel--;\n  ctx->staticLoops.pop_back();\n\n  if (ctx->getBase()->getLoop()->flat)\n    stmt->flat = true;\n\n  // Complete for-else clause\n  if (stmt->getElse() && stmt->getElse()->firstInBlock()) {\n    auto es = stmt->getElse();\n    stmt->elseSuite = nullptr;\n    resultStmt =\n        transform(N<SuiteStmt>(assign, stmt, N<IfStmt>(N<IdExpr>(breakVar), es)));\n    stmt->elseSuite = nullptr;\n  }\n\n  ctx->getBase()->loops.pop_back();\n\n  if (stmt->getIter()->isDone() && stmt->getSuite()->isDone())\n    stmt->setDone();\n}\n\n/// Transform and check for OpenMP decorator.\n/// @example\n///   `@par(num_threads=2, openmp=\"schedule(static)\")` ->\n///   `for_par(num_threads=2, schedule=\"static\")`\nExpr *TypecheckVisitor::transformForDecorator(Expr *decorator) {\n  if (!decorator)\n    return nullptr;\n  Expr *callee = decorator;\n  if (auto c = cast<CallExpr>(callee))\n    callee = c->getExpr();\n  auto ci = cast<IdExpr>(transform(callee));\n  if (!ci || !startswith(ci->getValue(), getMangledFunc(\"std.openmp\", \"for_par\"))) {\n    E(Error::LOOP_DECORATOR, decorator);\n  }\n\n  std::vector<CallArg> args;\n  std::string openmp;\n  std::vector<CallArg> omp;\n  if (auto c = cast<CallExpr>(decorator))\n    for (auto &a : *c) {\n      if (a.getName() == \"openmp\" ||\n          (a.getName().empty() && openmp.empty() && cast<StringExpr>(a.getExpr()))) {\n        auto ompOrErr =\n            parseOpenMP(ctx->cache, cast<StringExpr>(a.getExpr())->getValue(),\n                        a.value->getSrcInfo());\n        if (!ompOrErr)\n          throw exc::ParserException(ompOrErr.takeError());\n        omp = *ompOrErr;\n      } else {\n        args.emplace_back(a.getName(), transform(a.getExpr()));\n      }\n    }\n  for (auto &a : omp)\n    args.emplace_back(a.getName(), transform(a.getExpr()));\n  return transform(N<CallExpr>(transform(N<IdExpr>(\"for_par\")), args));\n}\n\n/// Handle static for constructs.\n/// @example\n///   `for i in statictuple(1, x): <suite>` ->\n///   ```loop = True\n///      while loop:\n///        while loop:\n///          i: Literal[int] = 1; <suite>; break\n///        while loop:\n///          i = x; <suite>; break\n///        loop = False   # also set to False on break\n/// If a loop is flat, while wrappers are removed.\n/// A separate suite is generated for each static iteration.\nstd::pair<bool, Stmt *> TypecheckVisitor::transformStaticForLoop(const ForStmt *stmt) {\n  auto loopVar = getTemporaryVar(\"loop\");\n  auto suite = clean_clone(stmt->getSuite());\n  auto [ok, delay, preamble, items] = transformStaticLoopCall(\n      stmt->getVar(), &suite, stmt->getIter(), [&](Stmt *assigns) {\n        Stmt *ret = nullptr;\n        if (!stmt->flat) {\n          auto brk = N<BreakStmt>();\n          brk->setDone(); // Avoid transforming this one to skip extra checks\n          // var [: Static] := expr; suite...\n          auto loop = N<WhileStmt>(N<IdExpr>(loopVar),\n                                   N<SuiteStmt>(assigns, clone(suite), brk));\n          loop->gotoVar = loopVar;\n          ret = loop;\n        } else {\n          ret = N<SuiteStmt>(assigns, clone(stmt->getSuite()));\n        }\n        return ret;\n      });\n  if (!ok)\n    return {false, nullptr};\n  if (delay)\n    return {true, nullptr};\n\n  // Close the loop\n  auto block = N<SuiteStmt>();\n  block->addStmt(preamble);\n  for (auto &i : items)\n    block->addStmt(cast<Stmt>(i));\n  Stmt *loop = nullptr;\n  if (!stmt->flat) {\n    ctx->blockLevel++;\n    auto a = N<AssignStmt>(N<IdExpr>(loopVar), N<BoolExpr>(false));\n    a->setUpdate();\n    block->addStmt(a);\n    loop = transform(N<SuiteStmt>(N<AssignStmt>(N<IdExpr>(loopVar), N<BoolExpr>(true)),\n                                  N<WhileStmt>(N<IdExpr>(loopVar), block)));\n    ctx->blockLevel--;\n  } else {\n    loop = transform(block);\n  }\n  return {false, loop};\n}\n\nstd::tuple<bool, bool, Stmt *, std::vector<ASTNode *>>\nTypecheckVisitor::transformStaticLoopCall(Expr *varExpr, SuiteStmt **varSuite,\n                                          Expr *iter,\n                                          const std::function<ASTNode *(Stmt *)> &wrap,\n                                          bool allowNonHeterogenous) {\n  if (!iter->getClassType())\n    return {true, true, nullptr, {}};\n\n  std::vector<std::string> vars{};\n  if (auto ei = cast<IdExpr>(varExpr)) {\n    vars.push_back(ei->getValue());\n  } else {\n    Items<Expr *> *list = nullptr;\n    if (auto el = cast<ListExpr>(varExpr))\n      list = el;\n    else if (auto et = cast<TupleExpr>(varExpr))\n      list = et;\n    if (list) {\n      for (const auto &it : *list)\n        if (auto eli = cast<IdExpr>(it)) {\n          vars.push_back(eli->getValue());\n        } else {\n          return {false, false, nullptr, {}};\n        }\n    } else {\n      return {false, false, nullptr, {}};\n    }\n  }\n\n  Stmt *preamble = nullptr;\n  iter = getHeadExpr(iter);\n  auto fn =\n      cast<CallExpr>(iter) ? cast<IdExpr>(cast<CallExpr>(iter)->getExpr()) : nullptr;\n  std::vector<Stmt *> block;\n  if (fn &&\n      startswith(fn->getValue(), getMangledFunc(\"std.internal.static\", \"tuple\"))) {\n    block = populateStaticTupleLoop(iter, vars);\n  } else if (fn && startswith(fn->getValue(),\n                              getMangledFunc(\"std.internal.static\", \"range\", 1))) {\n    block = populateSimpleStaticRangeLoop(iter, vars);\n  } else if (fn && startswith(fn->getValue(),\n                              getMangledFunc(\"std.internal.static\", \"range\"))) {\n    block = populateStaticRangeLoop(iter, vars);\n  } else if (fn &&\n             startswith(fn->getValue(), getMangledMethod(\"std.internal.static\",\n                                                         \"function\", \"overloads\"))) {\n    block = populateStaticFnOverloadsLoop(iter, vars);\n  } else if (fn && startswith(fn->getValue(),\n                              getMangledFunc(\"std.internal.static\", \"enumerate\"))) {\n    block = populateStaticEnumerateLoop(iter, vars);\n  } else if (fn && startswith(fn->getValue(),\n                              getMangledFunc(\"std.internal.static\", \"vars\"))) {\n    block = populateStaticVarsLoop(iter, vars);\n  } else if (fn && startswith(fn->getValue(),\n                              getMangledFunc(\"std.internal.static\", \"vars_types\"))) {\n    block = populateStaticVarTypesLoop(iter, vars);\n  } else {\n    if (iter->getType()->is(TYPE_TUPLE)) {\n      // Maybe heterogenous?\n      if (!iter->getType()->canRealize())\n        return {true, true, nullptr, {}}; // wait until the tuple is fully realizable\n      if (!isHeterogenous(iter->getClassType()) && !allowNonHeterogenous)\n        return {false, false, nullptr, {}};\n      block = populateStaticHeterogenousTupleLoop(iter, vars);\n      preamble = block.back();\n      block.pop_back();\n    } else {\n      return {false, false, nullptr, {}};\n    }\n  }\n  std::vector<ASTNode *> wrapBlock;\n  wrapBlock.reserve(block.size());\n  for (auto b : block) {\n    wrapBlock.push_back(wrap(b));\n  }\n  return {true, false, preamble, wrapBlock};\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/op.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <string>\n#include <tuple>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/cache.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/match.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nusing namespace codon::error;\n\nnamespace codon::ast {\n\nusing namespace types;\nusing namespace matcher;\n\n/// Replace unary operators with the appropriate magic calls.\n/// Also evaluate static expressions. See @c evaluateStaticUnary for details.\nvoid TypecheckVisitor::visit(UnaryExpr *expr) {\n  expr->expr = transform(expr->getExpr());\n\n  if (cast<IntExpr>(expr->getExpr()) && expr->getOp() == \"-\") {\n    // Special case: make - INT(val) same as INT(-val) to simplify IR and everything\n    resultExpr = transform(N<IntExpr>(-cast<IntExpr>(expr->getExpr())->getValue()));\n    return;\n  }\n\n  StaticType *staticType = nullptr;\n  static std::unordered_map<LiteralKind, std::unordered_set<std::string>> staticOps = {\n      {LiteralKind::Int, {\"-\", \"+\", \"!\", \"~\"}},\n      {LiteralKind::String, {\"!\"}},\n      {LiteralKind::Bool, {\"!\"}}};\n  // Handle static expressions\n  if (auto s = expr->getExpr()->getType()->getStaticKind()) {\n    if (in(staticOps[s], expr->getOp())) {\n      if ((resultExpr = evaluateStaticUnary(expr))) {\n        staticType = resultExpr->getType()->getStatic();\n      } else {\n        return;\n      }\n    }\n  } else if (isUnbound(expr->getExpr())) {\n    return;\n  }\n\n  if (expr->getOp() == \"!\") {\n    // `not expr` -> `expr.__bool__().__invert__()`\n    resultExpr = transform(N<CallExpr>(N<DotExpr>(\n        N<CallExpr>(N<DotExpr>(expr->getExpr(), \"__bool__\")), \"__invert__\")));\n  } else {\n    std::string magic;\n    if (expr->getOp() == \"~\")\n      magic = \"invert\";\n    else if (expr->getOp() == \"+\")\n      magic = \"pos\";\n    else if (expr->getOp() == \"-\")\n      magic = \"neg\";\n    else\n      seqassert(false, \"invalid unary operator '{}'\", expr->getOp());\n    resultExpr = transform(\n        N<CallExpr>(N<DotExpr>(expr->getExpr(), fmt::format(\"__{}__\", magic))));\n  }\n\n  if (staticType)\n    resultExpr->setType(staticType->shared_from_this());\n}\n\n/// Replace binary operators with the appropriate magic calls.\n/// See @c transformBinarySimple , @c transformBinaryIs , @c transformBinaryMagic and\n/// @c transformBinaryInplaceMagic for details.\n/// Also evaluate static expressions. See @c evaluateStaticBinary for details.\nvoid TypecheckVisitor::visit(BinaryExpr *expr) {\n  expr->lexpr = transform(expr->getLhs(), true);\n\n  // Static short-circuit\n  if (expr->getLhs()->getType()->getStaticKind() && expr->op == \"&&\") {\n    if (auto tb = expr->getLhs()->getType()->getBoolStatic()) {\n      if (!tb->value) {\n        if (ctx->expectedType && ctx->expectedType->is(\"bool\"))\n          resultExpr = transform(N<BoolExpr>(false));\n        else\n          resultExpr = expr->getLhs();\n      } else {\n        resultExpr =\n            transform(N<StmtExpr>(N<ExprStmt>(expr->getLhs()), expr->getRhs()));\n      }\n    } else if (auto ts = expr->getLhs()->getType()->getStrStatic()) {\n      if (ts->value.empty()) {\n        if (ctx->expectedType && ctx->expectedType->is(\"bool\"))\n          resultExpr = transform(N<BoolExpr>(false));\n        else\n          resultExpr = expr->getLhs();\n      } else {\n        resultExpr =\n            transform(N<StmtExpr>(N<ExprStmt>(expr->getLhs()), expr->getRhs()));\n      }\n    } else if (auto ti = expr->getLhs()->getType()->getIntStatic()) {\n      if (!ti->value) {\n        if (ctx->expectedType && ctx->expectedType->is(\"bool\"))\n          resultExpr = transform(N<BoolExpr>(false));\n        else\n          resultExpr = expr->getLhs();\n      } else {\n        resultExpr =\n            transform(N<StmtExpr>(N<ExprStmt>(expr->getLhs()), expr->getRhs()));\n      }\n    } else {\n      expr->getType()->getUnbound()->staticKind = LiteralKind::Bool;\n    }\n    return;\n  } else if (expr->getLhs()->getType()->getStaticKind() && expr->op == \"||\") {\n    if (auto tb = expr->getLhs()->getType()->getBoolStatic()) {\n      if (tb->value) {\n        if (ctx->expectedType && ctx->expectedType->is(\"bool\"))\n          resultExpr = transform(N<BoolExpr>(true));\n        else\n          resultExpr = expr->getLhs();\n      } else {\n        resultExpr =\n            transform(N<StmtExpr>(N<ExprStmt>(expr->getLhs()), expr->getRhs()));\n      }\n    } else if (auto ts = expr->getLhs()->getType()->getStrStatic()) {\n      if (!ts->value.empty()) {\n        if (ctx->expectedType && ctx->expectedType->is(\"bool\"))\n          resultExpr = transform(N<BoolExpr>(true));\n        else\n          resultExpr = expr->getLhs();\n      } else {\n        resultExpr =\n            transform(N<StmtExpr>(N<ExprStmt>(expr->getLhs()), expr->getRhs()));\n      }\n    } else if (auto ti = expr->getLhs()->getType()->getIntStatic()) {\n      if (ti->value) {\n        if (ctx->expectedType && ctx->expectedType->is(\"bool\"))\n          resultExpr = transform(N<BoolExpr>(true));\n        else\n          resultExpr = expr->getLhs();\n      } else {\n        resultExpr =\n            transform(N<StmtExpr>(N<ExprStmt>(expr->getLhs()), expr->getRhs()));\n      }\n    } else {\n      expr->getType()->getUnbound()->staticKind = LiteralKind::Bool;\n    }\n    return;\n  }\n\n  expr->rexpr = transform(expr->getRhs(), true);\n\n  StaticType *staticType = nullptr;\n  static std::unordered_map<LiteralKind, std::unordered_set<std::string>> staticOps = {\n      {LiteralKind::Int,\n       {\"<\", \"<=\", \">\", \">=\", \"==\", \"!=\", \"&&\", \"||\", \"+\", \"-\", \"*\", \"//\", \"%\", \"&\",\n        \"|\", \"^\", \">>\", \"<<\"}},\n      {LiteralKind::String, {\"==\", \"!=\", \"+\"}},\n      {LiteralKind::Bool, {\"<\", \"<=\", \">\", \">=\", \"==\", \"!=\", \"&&\", \"||\"}}};\n  if (auto l = expr->getLhs()->getType()->getStaticKind(),\n      r = expr->getRhs()->getType()->getStaticKind();\n      l && r) {\n    bool isStatic = l == r && in(staticOps[l], expr->getOp());\n    if (!isStatic &&\n        ((l == LiteralKind::Int && r == LiteralKind::Bool) ||\n         (r == LiteralKind::Int && l == LiteralKind::Bool)) &&\n        in(staticOps[LiteralKind::Int], expr->getOp()))\n      isStatic = true;\n    if (isStatic) {\n      if ((resultExpr = evaluateStaticBinary(expr)))\n        staticType = resultExpr->getType()->getStatic();\n      else\n        return;\n    }\n  }\n\n  if (isTypeExpr(expr->getLhs()) && isTypeExpr(expr->getRhs()) &&\n      expr->getOp() == \"|\") {\n    // Case: unions\n    resultExpr = transform(N<InstantiateExpr>(\n        N<IdExpr>(\"Union\"), std::vector<Expr *>{expr->getLhs(), expr->getRhs()}));\n  } else if (auto e = transformBinarySimple(expr)) {\n    // Case: simple binary expressions\n    resultExpr = e;\n  } else if (expr->getLhs()->getType()->getUnbound() ||\n             (expr->getOp() != \"is\" && expr->getRhs()->getType()->getUnbound())) {\n    // Case: types are unknown, so continue later\n    return;\n  } else if (expr->getOp() == \"is\") {\n    // Case: is operator\n    resultExpr = transformBinaryIs(expr);\n  } else {\n    if (auto ei = transformBinaryInplaceMagic(expr, false)) {\n      // Case: in-place magic methods\n      resultExpr = ei;\n    } else if (auto em = transformBinaryMagic(expr)) {\n      // Case: normal magic methods\n      resultExpr = em;\n    } else if (expr->getLhs()->getType()->is(TYPE_OPTIONAL)) {\n      // Special case: handle optionals if everything else fails.\n      // Assumes that optionals have no relevant magics (except for __eq__)\n      resultExpr = transform(\n          N<BinaryExpr>(N<CallExpr>(N<IdExpr>(FN_OPTIONAL_UNWRAP), expr->getLhs()),\n                        expr->getOp(), expr->getRhs(), expr->isInPlace()));\n    } else {\n      // Nothing found: report an error\n      E(Error::OP_NO_MAGIC, expr, expr->getOp(),\n        expr->getLhs()->getType()->prettyString(),\n        expr->getRhs()->getType()->prettyString());\n    }\n  }\n\n  if (staticType)\n    resultExpr->setType(staticType->shared_from_this());\n}\n\n/// Transform chain binary expression.\n/// @example\n///   `a <= b <= c` -> `(a <= (chain := b)) and (chain <= c)`\n/// The assignment above ensures that all expressions are executed only once.\nvoid TypecheckVisitor::visit(ChainBinaryExpr *expr) {\n  seqassert(expr->exprs.size() >= 2, \"not enough expressions in ChainBinaryExpr\");\n  std::vector<Expr *> items;\n  std::string prev;\n  for (int i = 1; i < expr->exprs.size(); i++) {\n    auto l = prev.empty() ? clone(expr->exprs[i - 1].second) : N<IdExpr>(prev);\n    prev = ctx->generateCanonicalName(\"chain\");\n    auto r =\n        (i + 1 == expr->exprs.size())\n            ? clone(expr->exprs[i].second)\n            : N<StmtExpr>(N<AssignStmt>(N<IdExpr>(prev), clone(expr->exprs[i].second)),\n                          N<IdExpr>(prev));\n    items.emplace_back(N<BinaryExpr>(l, expr->exprs[i].first, r));\n  }\n\n  Expr *final = items.back();\n  for (auto i = items.size() - 1; i-- > 0;)\n    final = N<BinaryExpr>(items[i], \"&&\", final);\n\n  auto oldExpectedType = getStdLibType(\"bool\")->shared_from_this();\n  std::swap(ctx->expectedType, oldExpectedType);\n  resultExpr = transform(final);\n  std::swap(ctx->expectedType, oldExpectedType);\n}\n\n/// Helper function that locates the pipe ellipsis within a collection of (possibly\n/// nested) CallExprs.\n/// @return  List of CallExprs and their locations within the parent CallExpr\n///          needed to access the ellipsis.\n/// @example\n/// `foo(bar(1, baz(...)))` returns `[{0, baz}, {1, bar}, {0, foo}]`\nstd::vector<std::pair<size_t, Expr *>> TypecheckVisitor::findEllipsis(Expr *expr) {\n  auto call = cast<CallExpr>(expr);\n  if (!call)\n    return {};\n  size_t ai = 0;\n  for (auto &a : *call) {\n    if (auto el = cast<EllipsisExpr>(a)) {\n      if (el->isPipe())\n        return {{ai, expr}};\n    } else if (cast<CallExpr>(a)) {\n      auto v = findEllipsis(a);\n      if (!v.empty()) {\n        v.emplace_back(ai, expr);\n        return v;\n      }\n    }\n    ai++;\n  }\n  return {};\n}\n\n/// Typecheck pipe expressions.\n/// Each stage call `foo(x)` without an ellipsis will be transformed to `foo(..., x)`.\n/// Stages that are not in the form of CallExpr will be transformed to it (e.g., `foo`\n/// -> `foo(...)`).\n/// Special care is taken of stages that can expand to multiple stages (e.g., `a |> foo`\n/// might become `a |> unwrap |> foo` to satisfy type constraints).\nvoid TypecheckVisitor::visit(PipeExpr *expr) {\n  bool hasGenerator = false;\n\n  // Return T if t is of type `Generator[T]`; otherwise just `type(t)`\n  auto getIterableType = [&](Type *t) {\n    if (t->is(\"Generator\")) {\n      hasGenerator = true;\n      return extractClassGeneric(t);\n    }\n    return t;\n  };\n\n  // List of output types\n  // (e.g., for `a|>b|>c` it is `[type(a), type(a|>b), type(a|>b|>c)]`).\n  // Note: the generator types are completely preserved (i.e., not extracted)\n  expr->inTypes.clear();\n\n  // Process the pipeline head\n  expr->front().expr = transform(expr->front().expr);\n  auto inType = expr->front().expr->getType(); // input type to the next stage\n  expr->inTypes.push_back(inType->shared_from_this());\n  inType = getIterableType(inType);\n  auto done = expr->front().expr->isDone();\n  for (size_t pi = 1; pi < expr->size(); pi++) {\n    int inTypePos = -1;                   // ellipsis position\n    Expr **ec = &((*expr)[pi].expr);      // a pointer so that we can replace it\n    while (auto se = cast<StmtExpr>(*ec)) // handle StmtExpr (e.g., in partial calls)\n      ec = &(se->expr);\n\n    if (auto call = cast<CallExpr>(*ec)) {\n      // Case: a call. Find the position of the pipe ellipsis within it\n      for (size_t ia = 0; inTypePos == -1 && ia < call->size(); ia++)\n        if (cast<EllipsisExpr>((*call)[ia].value))\n          inTypePos = static_cast<int>(ia);\n      // No ellipses found? Prepend it as the first argument\n      if (inTypePos == -1) {\n        call->items.insert(call->items.begin(),\n                           CallArg{\"\", N<EllipsisExpr>(EllipsisExpr::PARTIAL)});\n        inTypePos = 0;\n      }\n    } else {\n      // Case: not a call. Convert it to a call with a single ellipsis\n      (*expr)[pi].expr =\n          N<CallExpr>((*expr)[pi].expr, N<EllipsisExpr>(EllipsisExpr::PARTIAL));\n      ec = &(*expr)[pi].expr;\n      inTypePos = 0;\n    }\n\n    // Set the ellipsis type\n    auto el = cast<EllipsisExpr>((*cast<CallExpr>(*ec))[inTypePos].value);\n    el->mode = EllipsisExpr::PIPE;\n    // Don't unify unbound inType yet (it might become a generator that needs to be\n    // extracted)\n    if (!el->getType())\n      el->setType(instantiateUnbound());\n    if (inType && !inType->getUnbound())\n      unify(el->getType(), inType);\n\n    // Transform the call. Because a transformation might wrap the ellipsis in layers,\n    // make sure to extract these layers and move them to the pipeline.\n    // Example: `foo(...)` that is transformed to `foo(unwrap(...))` will become\n    // `unwrap(...) |> foo(...)`\n    *ec = transform(*ec);\n    auto layers = findEllipsis(*ec);\n    seqassert(!layers.empty(), \"can't find the ellipsis\");\n    if (layers.size() > 1) {\n      // Prepend layers\n      for (auto &[pos, prepend] : layers) {\n        (*cast<CallExpr>(prepend))[pos].value = N<EllipsisExpr>(EllipsisExpr::PIPE);\n        expr->items.insert(expr->items.begin() + pi++, {\"|>\", prepend});\n      }\n      // Rewind the loop (yes, the current expression will get transformed again)\n      /// TODO: avoid reevaluation\n      expr->items.erase(expr->items.begin() + pi);\n      pi = pi - layers.size() - 1;\n      continue;\n    }\n\n    if ((*ec)->getType())\n      unify((*expr)[pi].expr->getType(), (*ec)->getType());\n    (*expr)[pi].expr = *ec;\n    inType = (*expr)[pi].expr->getType();\n    if (!realize(inType))\n      done = false;\n    expr->inTypes.push_back(inType->shared_from_this());\n\n    // Do not extract the generator in the last stage of a pipeline\n    if (pi + 1 < expr->items.size())\n      inType = getIterableType(inType);\n  }\n  unify(expr->getType(), (hasGenerator ? getStdLibType(\"NoneType\") : inType));\n  if (done)\n    expr->setDone();\n}\n\n/// Transform index expressions.\n/// @example\n///   `foo[T]`   -> Instantiate(foo, [T]) if `foo` is a type\n///   `tup[1]`   -> `tup.item1` if `tup` is tuple\n///   `foo[idx]` -> `foo.__getitem__(idx)`\n///   expr.itemN or a sub-tuple if index is static (see transformStaticTupleIndex()),\nvoid TypecheckVisitor::visit(IndexExpr *expr) {\n  if (match(expr, M<IndexExpr>(M<IdExpr>(MOr(\"Literal\", \"Static\")),\n                               M<IdExpr>(MOr(\"int\", \"str\", \"bool\"))))) {\n    // Special case: static types.\n    auto typ = instantiateUnbound();\n    typ->staticKind = getStaticGeneric(expr);\n    unify(expr->getType(), typ);\n    expr->setDone();\n    return;\n  } else if (match(expr->expr, M<IdExpr>(MOr(\"Literal\", \"Static\")))) {\n    E(Error::BAD_STATIC_TYPE, expr->getIndex());\n  }\n  if (match(expr->expr, M<IdExpr>(\"tuple\")))\n    cast<IdExpr>(expr->expr)->setValue(TYPE_TUPLE);\n  expr->expr = transform(expr->expr, true);\n\n  // IndexExpr[i1, ..., iN] is internally represented as\n  // IndexExpr[TupleExpr[i1, ..., iN]] for N > 1\n  std::vector<Expr *> items;\n  bool isTuple = false;\n  if (auto t = cast<TupleExpr>(expr->getIndex())) {\n    items = t->items;\n    isTuple = true;\n  } else {\n    items.push_back(expr->getIndex());\n  }\n  auto origIndex = clone(expr->getIndex());\n  for (auto &i : items) {\n    if (cast<ListExpr>(i) && isTypeExpr(expr->getExpr())) {\n      // Special case: `A[[A, B], C]` -> `A[Tuple[A, B], C]` (e.g., in\n      // `Function[...]`)\n      i = N<InstantiateExpr>(N<IdExpr>(TYPE_TUPLE), cast<ListExpr>(i)->items);\n    }\n    i = transform(i, true);\n  }\n  if (isTypeExpr(expr->getExpr())) {\n    resultExpr = transform(N<InstantiateExpr>(expr->getExpr(), items));\n    return;\n  }\n\n  expr->index = (!isTuple && items.size() == 1) ? items[0] : N<TupleExpr>(items);\n  auto cls = expr->getExpr()->getClassType();\n  if (!cls) {\n    // Wait until the type becomes known\n    return;\n  }\n\n  // Case: static tuple access\n  // Note: needs untransformed origIndex to parse statics nicely\n  auto [isStaticTuple, tupleExpr] =\n      transformStaticTupleIndex(cls, expr->getExpr(), origIndex);\n  if (isStaticTuple) {\n    if (tupleExpr)\n      resultExpr = tupleExpr;\n  } else {\n    // Case: normal __getitem__\n    resultExpr = transform(\n        N<CallExpr>(N<DotExpr>(expr->getExpr(), \"__getitem__\"), expr->getIndex()));\n  }\n}\n\n/// Transform an instantiation to canonical realized name.\n/// @example\n///   Instantiate(foo, [bar]) -> Id(\"foo[bar]\")\nvoid TypecheckVisitor::visit(InstantiateExpr *expr) {\n  expr->expr = transformType(expr->getExpr());\n\n  TypePtr typ = nullptr;\n  size_t typeParamsSize = expr->size();\n  if (extractType(expr->expr)->is(TYPE_TUPLE)) {\n    if (!expr->empty()) {\n      expr->items.front() = transform(expr->front());\n      if (expr->front()->getType()->getStaticKind() == LiteralKind::Int) {\n        auto et = N<InstantiateExpr>(\n            N<IdExpr>(\"Tuple\"),\n            std::vector<Expr *>(expr->items.begin() + 1, expr->items.end()));\n        resultExpr = transform(N<InstantiateExpr>(N<IdExpr>(\"__NTuple__\"),\n                                                  std::vector<Expr *>{(*expr)[0], et}));\n        return;\n      }\n    }\n    auto t = generateTuple(typeParamsSize);\n    typ = instantiateType(t);\n  } else {\n    typ = instantiateType(expr->getExpr()->getSrcInfo(), extractType(expr->getExpr()));\n  }\n  seqassert(typ->getClass(), \"unknown type: {}\", *(expr->getExpr()));\n\n  auto &generics = typ->getClass()->generics;\n  bool isUnion = typ->getUnion() != nullptr;\n  if (!isUnion && typeParamsSize != generics.size())\n    E(Error::GENERICS_MISMATCH, expr, getUserFacingName(typ->getClass()->name),\n      generics.size(), typeParamsSize);\n\n  if (isId(expr->getExpr(), TRAIT_CALLABLE)) {\n    // Case: CallableTrait[...] trait instantiation\n\n    // CallableTrait error checking.\n    std::vector<TypePtr> types;\n    for (auto &typeParam : *expr) {\n      typeParam = transformType(typeParam);\n      if (typeParam->getType()->getStaticKind())\n        E(Error::INST_CALLABLE_STATIC, typeParam);\n      types.push_back(extractType(typeParam)->shared_from_this());\n    }\n    auto ub = instantiateUnbound();\n    // Set up the CallableTrait\n    ub->getLink()->trait = std::make_shared<CallableTrait>(ctx->cache, types);\n    unify(expr->getType(), instantiateTypeVar(ub.get()));\n  } else if (isId(expr->getExpr(), TRAIT_TYPE)) {\n    // Case: TypeTrait[...] trait instantiation\n    (*expr)[0] = transformType((*expr)[0]);\n    auto ub = instantiateUnbound();\n    ub->getLink()->trait =\n        std::make_shared<TypeTrait>(extractType(expr->front())->shared_from_this());\n    unify(expr->getType(), ub);\n  } else {\n    for (size_t i = 0; i < expr->size(); i++) {\n      (*expr)[i] = transformType((*expr)[i]);\n      auto t = instantiateType((*expr)[i]->getSrcInfo(), extractType((*expr)[i]));\n      if (isUnion || (*expr)[i]->getType()->getStaticKind() !=\n                         generics[i].getType()->getStaticKind()) {\n        if (cast<NoneExpr>((*expr)[i])) // `None` -> `NoneType`\n          (*expr)[i] = transformType((*expr)[i]);\n        if (!isTypeExpr((*expr)[i]))\n          E(Error::EXPECTED_TYPE, (*expr)[i], \"type\");\n      }\n      if (isUnion) {\n        if (!typ->getUnion()->addType(t.get()))\n          E(error::Error::UNION_TOO_BIG, (*expr)[i],\n            typ->getUnion()->pendingTypes.size());\n      } else {\n        unify(t.get(), generics[i].getType());\n      }\n    }\n    if (isUnion) {\n      typ->getUnion()->seal();\n    }\n\n    unify(expr->getType(), instantiateTypeVar(typ.get()));\n    // If the type is realizable, use the realized name instead of instantiation\n    // (e.g. use Id(\"Ptr[byte]\") instead of Instantiate(Ptr, {byte}))\n    if (auto rt = realize(expr->getType())) {\n      auto t = extractType(rt);\n      resultExpr = N<IdExpr>(t->realizedName());\n      resultExpr->setType(rt->shared_from_this());\n      resultExpr->setDone();\n    }\n  }\n\n  // Handle side effects\n  if (!ctx->simpleTypes) {\n    std::vector<Stmt *> prepends;\n    for (auto &t : *expr) {\n      if (hasSideEffect(t)) {\n        auto name = getTemporaryVar(\"call\");\n        auto front =\n            transform(N<AssignStmt>(N<IdExpr>(name), t, getParamType(t->getType())));\n        auto swap = transformType(N<IdExpr>(name));\n        t = swap;\n        prepends.emplace_back(front);\n      }\n    }\n    if (!prepends.empty()) {\n      resultExpr = transform(N<StmtExpr>(prepends, resultExpr ? resultExpr : expr));\n    }\n  }\n}\n\n/// Transform a slice expression.\n/// @example\n///   `start::step` -> `Slice(start, Optional.__new__(), step)`\nvoid TypecheckVisitor::visit(SliceExpr *expr) {\n  Expr *none = N<CallExpr>(N<DotExpr>(N<IdExpr>(TYPE_OPTIONAL), \"__new__\"));\n  resultExpr = transform(N<CallExpr>(N<IdExpr>(getStdLibType(\"Slice\")->name),\n                                     expr->getStart() ? expr->getStart() : clone(none),\n                                     expr->getStop() ? expr->getStop() : clone(none),\n                                     expr->getStep() ? expr->getStep() : clone(none)));\n}\n\n/// Evaluate a static unary expression and return the resulting static expression.\n/// If the expression cannot be evaluated yet, return nullptr.\n/// Supported operators: (strings) not (ints) not, -, +\nExpr *TypecheckVisitor::evaluateStaticUnary(const UnaryExpr *expr) {\n  // Case: static strings\n  if (expr->getExpr()->getType()->getStaticKind() == LiteralKind::String) {\n    if (expr->getOp() == \"!\") {\n      if (expr->getExpr()->getType()->canRealize()) {\n        bool value = getStrLiteral(expr->getExpr()->getType()).empty();\n        LOG_TYPECHECK(\"[cond::un] {}: {}\", getSrcInfo(), value);\n        return transform(N<IntExpr>(value));\n      } else {\n        // Cannot be evaluated yet: just set the type\n        expr->getType()->getUnbound()->staticKind = LiteralKind::Int;\n      }\n    }\n    return nullptr;\n  }\n\n  // Case: static bools\n  if (expr->getExpr()->getType()->getStaticKind() == LiteralKind::Bool) {\n    if (expr->getOp() == \"!\") {\n      if (expr->getExpr()->getType()->canRealize()) {\n        bool value = getBoolLiteral(expr->getExpr()->getType());\n        LOG_TYPECHECK(\"[cond::un] {}: {}\", getSrcInfo(), value);\n        return transform(N<BoolExpr>(!value));\n      } else {\n        // Cannot be evaluated yet: just set the type\n        expr->getType()->getUnbound()->staticKind = LiteralKind::Bool;\n      }\n    }\n    return nullptr;\n  }\n\n  // Case: static integers\n  if (expr->getOp() == \"-\" || expr->getOp() == \"+\" || expr->getOp() == \"!\" ||\n      expr->getOp() == \"~\") {\n    if (expr->getExpr()->getType()->canRealize()) {\n      int64_t value = getIntLiteral(expr->getExpr()->getType());\n      if (expr->getOp() == \"+\")\n        ;\n      else if (expr->getOp() == \"-\")\n        value = -value;\n      else if (expr->getOp() == \"~\")\n        value = ~value;\n      else\n        value = !static_cast<bool>(value);\n      LOG_TYPECHECK(\"[cond::un] {}: {}\", getSrcInfo(), value);\n      if (expr->getOp() == \"!\")\n        return transform(N<BoolExpr>(value));\n      else\n        return transform(N<IntExpr>(value));\n    } else {\n      // Cannot be evaluated yet: just set the type\n      expr->getType()->getUnbound()->staticKind =\n          expr->getOp() == \"!\" ? LiteralKind::Bool : LiteralKind::Int;\n    }\n  }\n\n  return nullptr;\n}\n\n/// Division and modulus implementations.\nstd::pair<int64_t, int64_t> divMod(const std::shared_ptr<TypeContext> &ctx, int64_t a,\n                                   int64_t b) {\n  if (!b) {\n    E(Error::STATIC_DIV_ZERO, ctx->getSrcInfo());\n    return {0, 0};\n  } else if (ctx->cache->pythonCompat) {\n    // Use Python implementation.\n    int64_t d = a / b;\n    int64_t m = a - d * b;\n    if (m && ((b ^ m) < 0)) {\n      m += b;\n      d -= 1;\n    }\n    return {d, m};\n  } else {\n    // Use C implementation.\n    return {a / b, a % b};\n  }\n}\n\n/// Evaluate a static binary expression and return the resulting static expression.\n/// If the expression cannot be evaluated yet, return nullptr.\n/// Supported operators: (strings) +, ==, !=\n///                      (ints) <, <=, >, >=, ==, !=, and, or, +, -, *, //, %, ^, |, &\nExpr *TypecheckVisitor::evaluateStaticBinary(const BinaryExpr *expr) {\n  // Case: static strings\n  if (expr->getRhs()->getType()->getStaticKind() == LiteralKind::String) {\n    if (expr->getOp() == \"+\") {\n      // `\"a\" + \"b\"` -> `\"ab\"`\n      if (expr->getLhs()->getType()->getStrStatic() &&\n          expr->getRhs()->getType()->getStrStatic()) {\n        auto value = getStrLiteral(expr->getLhs()->getType()) +\n                     getStrLiteral(expr->getRhs()->getType());\n        LOG_TYPECHECK(\"[cond::bin] {}: {}\", getSrcInfo(), value);\n        return transform(N<StringExpr>(value));\n      } else {\n        // Cannot be evaluated yet: just set the type\n        expr->getType()->getUnbound()->staticKind = LiteralKind::String;\n      }\n    } else {\n      // `\"a\" == \"b\"` -> `False` (also handles `!=`)\n      if (expr->getLhs()->getType()->getStrStatic() &&\n          expr->getRhs()->getType()->getStrStatic()) {\n        bool eq = getStrLiteral(expr->getLhs()->getType()) ==\n                  getStrLiteral(expr->getRhs()->getType());\n        bool value = expr->getOp() == \"==\" ? eq : !eq;\n        LOG_TYPECHECK(\"[cond::bin] {}: {}\", getSrcInfo(), value);\n        return transform(N<BoolExpr>(value));\n      } else {\n        // Cannot be evaluated yet: just set the type\n        expr->getType()->getUnbound()->staticKind = LiteralKind::Bool;\n      }\n    }\n    return nullptr;\n  }\n\n  // Case: static integers\n  if (expr->getLhs()->getType()->getStatic() &&\n      expr->getRhs()->getType()->getStatic()) {\n    int64_t lvalue = expr->getLhs()->getType()->getIntStatic()\n                         ? getIntLiteral(expr->getLhs()->getType())\n                         : getBoolLiteral(expr->getLhs()->getType());\n    int64_t rvalue = expr->getRhs()->getType()->getIntStatic()\n                         ? getIntLiteral(expr->getRhs()->getType())\n                         : getBoolLiteral(expr->getRhs()->getType());\n    if (expr->getOp() == \"<\")\n      lvalue = lvalue < rvalue;\n    else if (expr->getOp() == \"<=\")\n      lvalue = lvalue <= rvalue;\n    else if (expr->getOp() == \">\")\n      lvalue = lvalue > rvalue;\n    else if (expr->getOp() == \">=\")\n      lvalue = lvalue >= rvalue;\n    else if (expr->getOp() == \"==\")\n      lvalue = lvalue == rvalue;\n    else if (expr->getOp() == \"!=\")\n      lvalue = lvalue != rvalue;\n    else if (expr->getOp() == \"&&\")\n      lvalue = lvalue ? rvalue : lvalue;\n    else if (expr->getOp() == \"||\")\n      lvalue = lvalue ? lvalue : rvalue;\n    else if (expr->getOp() == \"+\")\n      lvalue = lvalue + rvalue;\n    else if (expr->getOp() == \"-\")\n      lvalue = lvalue - rvalue;\n    else if (expr->getOp() == \"*\")\n      lvalue = lvalue * rvalue;\n    else if (expr->getOp() == \"^\")\n      lvalue = lvalue ^ rvalue;\n    else if (expr->getOp() == \"&\")\n      lvalue = lvalue & rvalue;\n    else if (expr->getOp() == \"|\")\n      lvalue = lvalue | rvalue;\n    else if (expr->getOp() == \">>\")\n      lvalue = lvalue >> rvalue;\n    else if (expr->getOp() == \"<<\")\n      lvalue = lvalue << rvalue;\n    else if (expr->getOp() == \"//\")\n      lvalue = divMod(ctx, lvalue, rvalue).first;\n    else if (expr->getOp() == \"%\")\n      lvalue = divMod(ctx, lvalue, rvalue).second;\n    else\n      seqassert(false, \"unknown static operator {}\", expr->getOp());\n    LOG_TYPECHECK(\"[cond::bin] {}: {}\", getSrcInfo(), lvalue);\n    if (in(std::set<std::string>{\"==\", \"!=\", \"<\", \"<=\", \">\", \">=\"}, expr->getOp())) {\n      return transform(N<BoolExpr>(lvalue));\n    } else if ((expr->getOp() == \"&&\" || expr->getOp() == \"||\") &&\n               expr->getLhs()->getType()->getBoolStatic() &&\n               expr->getLhs()->getType()->getBoolStatic()) {\n      return transform(N<BoolExpr>(lvalue));\n    } else {\n      return transform(N<IntExpr>(lvalue));\n    }\n  } else {\n    // Cannot be evaluated yet: just set the type\n    if (in(std::set<std::string>{\"==\", \"!=\", \"<\", \"<=\", \">\", \">=\"}, expr->getOp())) {\n      expr->getType()->getUnbound()->staticKind = LiteralKind::Bool;\n    } else if ((expr->getOp() == \"&&\" || expr->getOp() == \"||\") &&\n               expr->getLhs()->getType()->getBoolStatic() &&\n               expr->getLhs()->getType()->getBoolStatic()) {\n      expr->getType()->getUnbound()->staticKind = LiteralKind::Bool;\n    } else {\n      expr->getType()->getUnbound()->staticKind = LiteralKind::Int;\n    }\n  }\n\n  return nullptr;\n}\n\n/// Transform a simple binary expression.\n/// @example\n///   `a and b`    -> `b if a else False`\n///   `a or b`     -> `True if a else b`\n///   `a in b`     -> `a.__contains__(b)`\n///   `a not in b` -> `not (a in b)`\n///   `a is not b` -> `not (a is b)`\nExpr *TypecheckVisitor::transformBinarySimple(const BinaryExpr *expr) {\n  // Case: simple transformations\n  if (expr->getOp() == \"&&\") {\n    if (ctx->expectedType && ctx->expectedType->is(\"bool\")) {\n      return transform(N<IfExpr>(expr->getLhs(),\n                                 N<CallExpr>(N<DotExpr>(expr->getRhs(), \"__bool__\")),\n                                 N<BoolExpr>(false)));\n    } else {\n      auto lt = realize(expr->getLhs()->getType());\n      auto rt = realize(expr->getRhs()->getType());\n      if (!lt || !rt) {\n        return const_cast<BinaryExpr *>(expr); // delay\n      } else {\n        auto vn = getTemporaryVar(\"cond\");\n        auto ve = N<AssignExpr>(N<IdExpr>(vn), expr->getLhs());\n        if (lt->realizedName() == rt->realizedName()) {\n          return N<IfExpr>(ve, expr->getRhs(), N<IdExpr>(vn));\n        } else {\n          auto T = N<InstantiateExpr>(\n              N<IdExpr>(\"Union\"), std::vector<Expr *>{N<IdExpr>(lt->realizedName()),\n                                                      N<IdExpr>(rt->realizedName())});\n          return N<IfExpr>(ve, N<CallExpr>(T, expr->getRhs()),\n                           N<CallExpr>(clone(T), N<IdExpr>(vn)));\n        }\n      }\n    }\n  } else if (expr->getOp() == \"||\") {\n    if (ctx->expectedType && ctx->expectedType->is(\"bool\")) {\n      return transform(N<IfExpr>(expr->getLhs(), N<BoolExpr>(true),\n                                 N<CallExpr>(N<DotExpr>(expr->getRhs(), \"__bool__\"))));\n    } else {\n      auto lt = realize(expr->getLhs()->getType());\n      auto rt = realize(expr->getRhs()->getType());\n      if (!lt || !rt) {\n        return const_cast<BinaryExpr *>(expr); // delay\n      } else {\n        auto vn = getTemporaryVar(\"cond\");\n        auto ve = N<AssignExpr>(N<IdExpr>(vn), expr->getLhs());\n        if (lt->realizedName() == rt->realizedName()) {\n          return N<IfExpr>(ve, N<IdExpr>(vn), expr->getRhs());\n        } else {\n          auto T = N<InstantiateExpr>(\n              N<IdExpr>(getMangledClass(\"std.internal.core\", \"Union\")),\n              std::vector<Expr *>{N<IdExpr>(lt->realizedName()),\n                                  N<IdExpr>(rt->realizedName())});\n          return N<IfExpr>(ve, N<CallExpr>(T, N<IdExpr>(vn)),\n                           N<CallExpr>(clone(T), expr->getRhs()));\n        }\n      }\n    }\n  } else if (expr->getOp() == \"not in\") {\n    return transform(N<CallExpr>(N<DotExpr>(\n        N<CallExpr>(N<DotExpr>(expr->getRhs(), \"__contains__\"), expr->getLhs()),\n        \"__invert__\")));\n  } else if (expr->getOp() == \"in\") {\n    return transform(\n        N<CallExpr>(N<DotExpr>(expr->getRhs(), \"__contains__\"), expr->getLhs()));\n  } else if (expr->getOp() == \"is\") {\n    if (cast<NoneExpr>(expr->getLhs()) && cast<NoneExpr>(expr->getRhs()))\n      return transform(N<BoolExpr>(true));\n    else if (cast<NoneExpr>(expr->getLhs()))\n      return transform(N<BinaryExpr>(expr->getRhs(), \"is\", expr->getLhs()));\n  } else if (expr->getOp() == \"is not\") {\n    return transform(\n        N<UnaryExpr>(\"!\", N<BinaryExpr>(expr->getLhs(), \"is\", expr->getRhs())));\n  }\n  return nullptr;\n}\n\n/// Transform a binary `is` expression by checking for type equality. Handle special `is\n/// None` cаses as well. See inside for details.\nExpr *TypecheckVisitor::transformBinaryIs(const BinaryExpr *expr) {\n  seqassert(expr->op == \"is\", \"not an is binary expression\");\n\n  // Case: `is None` expressions\n  if (cast<NoneExpr>(expr->getRhs())) {\n    if (extractClassType(expr->getLhs())->is(\"NoneType\"))\n      return transform(N<BoolExpr>(true));\n    if (!extractClassType(expr->getLhs())->is(TYPE_OPTIONAL)) {\n      // lhs is not optional: `return False`\n      return transform(N<BoolExpr>(false));\n    } else {\n      // Special case: Optional[Optional[... Optional[NoneType]]...] == NoneType\n      auto g = extractClassType(expr->getLhs());\n      for (; extractClassGeneric(g)->is(\"Optional\");\n           g = extractClassGeneric(g)->getClass())\n        ;\n      if (!extractClassGeneric(g)->getClass()) {\n        auto typ = instantiateUnbound();\n        typ->staticKind = LiteralKind::Bool;\n        unify(expr->getType(), typ);\n        return nullptr;\n      }\n      if (extractClassGeneric(g)->is(\"NoneType\"))\n        return transform(N<BoolExpr>(true));\n\n      // lhs is optional: `return lhs.__has__().__invert__()`\n      if (expr->getType()->getUnbound() && expr->getType()->getStaticKind())\n        expr->getType()->getUnbound()->staticKind = LiteralKind::Runtime;\n      return transform(N<CallExpr>(N<DotExpr>(\n          N<CallExpr>(N<DotExpr>(expr->getLhs(), \"__has__\")), \"__invert__\")));\n    }\n  }\n\n  // Check the type equality (operand types and __raw__ pointers must match).\n  auto lc = realize(expr->getLhs()->getType());\n  auto rc = realize(expr->getRhs()->getType());\n  if (!lc || !rc) {\n    // Types not known: return early\n    unify(expr->getType(), getStdLibType(\"bool\"));\n    return nullptr;\n  }\n  if (isTypeExpr(expr->getLhs()) && isTypeExpr(expr->getRhs()))\n    return transform(N<BoolExpr>(lc->realizedName() == rc->realizedName()));\n  if (!lc->getClass()->isRecord() && !rc->getClass()->isRecord()) {\n    // Both reference types: `return lhs.__raw__() == rhs.__raw__()`\n    return transform(\n        N<BinaryExpr>(N<CallExpr>(N<DotExpr>(expr->getLhs(), \"__raw__\")),\n                      \"==\", N<CallExpr>(N<DotExpr>(expr->getRhs(), \"__raw__\"))));\n  }\n  if (lc->is(TYPE_OPTIONAL)) {\n    // lhs is optional: `return lhs.__is_optional__(rhs)`\n    return transform(\n        N<CallExpr>(N<DotExpr>(expr->getLhs(), \"__is_optional__\"), expr->getRhs()));\n  }\n  if (rc->is(TYPE_OPTIONAL)) {\n    // rhs is optional: `return rhs.__is_optional__(lhs)`\n    return transform(\n        N<CallExpr>(N<DotExpr>(expr->getRhs(), \"__is_optional__\"), expr->getLhs()));\n  }\n  if (lc->realizedName() != rc->realizedName()) {\n    // tuple names do not match: `return False`\n    return transform(N<BoolExpr>(false));\n  }\n  // Same tuple types: `return lhs == rhs`\n  return transform(N<BinaryExpr>(expr->getLhs(), \"==\", expr->getRhs()));\n}\n\n/// Return a binary magic opcode for the provided operator.\nstd::pair<std::string, std::string>\nTypecheckVisitor::getMagic(const std::string &op) const {\n  // Table of supported binary operations and the corresponding magic methods.\n  static auto magics = std::unordered_map<std::string, std::string>{\n      {\"+\", \"add\"},     {\"-\", \"sub\"},       {\"*\", \"mul\"},     {\"**\", \"pow\"},\n      {\"/\", \"truediv\"}, {\"//\", \"floordiv\"}, {\"@\", \"matmul\"},  {\"%\", \"mod\"},\n      {\"<\", \"lt\"},      {\"<=\", \"le\"},       {\">\", \"gt\"},      {\">=\", \"ge\"},\n      {\"==\", \"eq\"},     {\"!=\", \"ne\"},       {\"<<\", \"lshift\"}, {\">>\", \"rshift\"},\n      {\"&\", \"and\"},     {\"|\", \"or\"},        {\"^\", \"xor\"},\n  };\n  auto mi = magics.find(op);\n  if (mi == magics.end())\n    seqassert(false, \"invalid binary operator '{}'\", op);\n\n  static auto rightMagics = std::unordered_map<std::string, std::string>{\n      {\"<\", \"gt\"}, {\"<=\", \"ge\"}, {\">\", \"lt\"}, {\">=\", \"le\"}, {\"==\", \"eq\"}, {\"!=\", \"ne\"},\n  };\n  auto rm = in(rightMagics, op);\n  return {mi->second, rm ? *rm : \"r\" + mi->second};\n}\n\n/// Transform an in-place binary expression.\n/// @example\n///   `a op= b` -> `a.__iopmagic__(b)`\n/// @param isAtomic if set, use atomic magics if available.\nExpr *TypecheckVisitor::transformBinaryInplaceMagic(BinaryExpr *expr, bool isAtomic) {\n  auto [magic, _] = getMagic(expr->getOp());\n  auto lt = expr->getLhs()->getClassType();\n  seqassert(lt, \"lhs type not known\");\n\n  FuncType *method = nullptr;\n\n  // Atomic operations: check if `lhs.__atomic_op__(Ptr[lhs], rhs)` exists\n  if (isAtomic) {\n    auto ptr = instantiateType(getStdLibType(\"Ptr\"), std::vector<types::Type *>{lt});\n    if ((method = findBestMethod(lt, fmt::format(\"__atomic_{}__\", magic),\n                                 {ptr.get(), expr->getRhs()->getType()}))) {\n      expr->lexpr = N<CallExpr>(N<IdExpr>(\"__ptr__\"), expr->getLhs());\n    }\n  }\n\n  // In-place operations: check if `lhs.__iop__(lhs, rhs)` exists\n  if (!method && expr->isInPlace()) {\n    method = findBestMethod(lt, fmt::format(\"__i{}__\", magic),\n                            std::vector<Expr *>{expr->getLhs(), expr->getRhs()});\n  }\n\n  if (method)\n    return transform(\n        N<CallExpr>(N<IdExpr>(method->getFuncName()), expr->getLhs(), expr->getRhs()));\n  return nullptr;\n}\n\n/// Transform a magic binary expression.\n/// @example\n///   `a op b` -> `a.__opmagic__(b)`\nExpr *TypecheckVisitor::transformBinaryMagic(const BinaryExpr *expr) {\n  auto [magic, rightMagic] = getMagic(expr->getOp());\n  auto lt = expr->getLhs()->getType();\n  auto rt = expr->getRhs()->getType();\n\n  if (!lt->is(\"pyobj\") && rt->is(\"pyobj\")) {\n    // Special case: `obj op pyobj` -> `rhs.__rmagic__(lhs)` on lhs\n    // Assumes that pyobj implements all left and right magics\n    auto l = getTemporaryVar(\"l\");\n    auto r = getTemporaryVar(\"r\");\n    return transform(N<StmtExpr>(\n        N<AssignStmt>(N<IdExpr>(l), expr->getLhs()),\n        N<AssignStmt>(N<IdExpr>(r), expr->getRhs()),\n        N<CallExpr>(N<DotExpr>(N<IdExpr>(r), fmt::format(\"__{}__\", rightMagic)),\n                    N<IdExpr>(l))));\n  }\n  if (lt->getUnion()) {\n    // Special case: `union op obj` -> `union.__magic__(rhs)`\n    return transform(N<CallExpr>(\n        N<DotExpr>(expr->getLhs(), fmt::format(\"__{}__\", magic)), expr->getRhs()));\n  }\n\n  // Normal operations: check if `lhs.__magic__(lhs, rhs)` exists\n  if (auto method =\n          findBestMethod(lt->getClass(), fmt::format(\"__{}__\", magic),\n                         std::vector<Expr *>{expr->getLhs(), expr->getRhs()})) {\n    // Normal case: `__magic__(lhs, rhs)`\n    return transform(\n        N<CallExpr>(N<IdExpr>(method->getFuncName()), expr->getLhs(), expr->getRhs()));\n  }\n\n  // Right-side magics: check if `rhs.__rmagic__(rhs, lhs)` exists\n  if (auto method =\n          findBestMethod(rt->getClass(), fmt::format(\"__{}__\", rightMagic),\n                         std::vector<Expr *>{expr->getRhs(), expr->getLhs()})) {\n    auto l = getTemporaryVar(\"l\");\n    auto r = getTemporaryVar(\"r\");\n    return transform(N<StmtExpr>(\n        N<AssignStmt>(N<IdExpr>(l), expr->getLhs()),\n        N<AssignStmt>(N<IdExpr>(r), expr->getRhs()),\n        N<CallExpr>(N<IdExpr>(method->getFuncName()), N<IdExpr>(r), N<IdExpr>(l))));\n  }\n\n  return nullptr;\n}\n\n/// Given a tuple type and the expression `expr[index]`, check if an `index` is static\n/// (integer or slice). If so, statically extract the specified tuple item or a\n/// sub-tuple (if the index is a slice).\n/// Works only on normal tuples and partial functions.\nstd::pair<bool, Expr *>\nTypecheckVisitor::transformStaticTupleIndex(ClassType *tuple, Expr *expr, Expr *index) {\n  bool isStaticString = expr->getType()->getStaticKind() == LiteralKind::String;\n  if (isStaticString && !expr->getType()->canRealize()) {\n    return {true, nullptr};\n  } else if (!isStaticString) {\n    if (!tuple->isRecord())\n      return {false, nullptr};\n    if (!tuple->is(TYPE_TUPLE)) {\n      if (tuple->is(TYPE_OPTIONAL)) {\n        if (auto newTuple = extractClassGeneric(tuple)->getClass()) {\n          return transformStaticTupleIndex(\n              newTuple, transform(N<CallExpr>(N<IdExpr>(FN_OPTIONAL_UNWRAP), expr)),\n              index);\n        } else {\n          return {true, nullptr};\n        }\n      }\n      return {false, nullptr};\n    }\n  }\n\n  // Extract the static integer value from expression\n  auto getInt = [&](int64_t *o, Expr *e) {\n    if (!e)\n      return true;\n\n    auto ore = transform(clone(e));\n    if (auto s = ore->getType()->getIntStatic()) {\n      *o = s->value;\n      return true;\n    }\n    return false;\n  };\n\n  std::string str = isStaticString ? getStrLiteral(expr->getType()) : \"\";\n  auto sz =\n      static_cast<int64_t>(isStaticString ? str.size() : getClassFields(tuple).size());\n  int64_t start = 0, stop = sz, step = 1, multiple = 0;\n  if (getInt(&start, index)) {\n    // Case: `tuple[int]`\n    auto i = translateIndex(start, stop);\n    if (i < 0 || i >= stop)\n      E(Error::TUPLE_RANGE_BOUNDS, index, stop - 1, i);\n    start = i;\n  } else if (auto slice = cast<SliceExpr>(index)) {\n    // Case: `tuple[int:int:int]`\n    if (!getInt(&start, slice->getStart()) || !getInt(&stop, slice->getStop()) ||\n        !getInt(&step, slice->getStep()))\n      return {false, nullptr};\n\n    // Adjust slice indices (Python slicing rules)\n    if (slice->getStep() && !slice->getStart())\n      start = step > 0 ? 0 : (sz - 1);\n    if (slice->getStep() && !slice->getStop())\n      stop = step > 0 ? sz : -(sz + 1);\n    sliceAdjustIndices(sz, &start, &stop, step);\n    multiple = 1;\n  } else {\n    return {false, nullptr};\n  }\n\n  if (isStaticString) {\n    if (!multiple) {\n      return {true, transform(N<StringExpr>(str.substr(start, 1)))};\n    } else {\n      std::string newStr;\n      for (auto i = start; (step > 0) ? (i < stop) : (i > stop); i += step)\n        newStr += str[i];\n      return {true, transform(N<StringExpr>(newStr))};\n    }\n  } else {\n    auto classFields = getClassFields(tuple);\n    if (!multiple) {\n      return {true, transform(N<DotExpr>(expr, classFields[start].name))};\n    } else {\n      // Generate a sub-tuple\n      auto var = N<IdExpr>(getTemporaryVar(\"tup\"));\n      auto ass = N<AssignStmt>(var, expr);\n      std::vector<Expr *> te;\n      for (auto i = start; (step > 0) ? (i < stop) : (i > stop); i += step) {\n        if (i < 0 || i >= sz)\n          E(Error::TUPLE_RANGE_BOUNDS, index, sz - 1, i);\n        te.push_back(N<DotExpr>(clone(var), classFields[i].name));\n      }\n      generateTuple(te.size());\n      Expr *e = transform(N<StmtExpr>(std::vector<Stmt *>{ass},\n                                      N<CallExpr>(N<IdExpr>(TYPE_TUPLE), te)));\n      return {true, e};\n    }\n  }\n}\n\n/// Follow Python indexing rules for static tuple indices.\n/// Taken from https://github.com/python/cpython/blob/main/Objects/sliceobject.c.\nint64_t TypecheckVisitor::translateIndex(int64_t idx, int64_t len, bool clamp) const {\n  if (idx < 0)\n    idx += len;\n  if (clamp) {\n    if (idx < 0)\n      idx = 0;\n    if (idx > len)\n      idx = len;\n  } else if (idx < 0 || idx >= len) {\n    E(Error::TUPLE_RANGE_BOUNDS, getSrcInfo(), len - 1, idx);\n  }\n  return idx;\n}\n\n/// Follow Python slice indexing rules for static tuple indices.\n/// Taken from https://github.com/python/cpython/blob/main/Objects/sliceobject.c.\n/// Quote (sliceobject.c:269): \"this is harder to get right than you might think\"\nint64_t TypecheckVisitor::sliceAdjustIndices(int64_t length, int64_t *start,\n                                             int64_t *stop, int64_t step) const {\n  if (step == 0)\n    E(Error::SLICE_STEP_ZERO, getSrcInfo());\n\n  if (*start < 0) {\n    *start += length;\n    if (*start < 0) {\n      *start = (step < 0) ? -1 : 0;\n    }\n  } else if (*start >= length) {\n    *start = (step < 0) ? length - 1 : length;\n  }\n\n  if (*stop < 0) {\n    *stop += length;\n    if (*stop < 0) {\n      *stop = (step < 0) ? -1 : 0;\n    }\n  } else if (*stop >= length) {\n    *stop = (step < 0) ? length - 1 : length;\n  }\n\n  if (step < 0) {\n    if (*stop < *start) {\n      return (*start - *stop - 1) / (-step) + 1;\n    }\n  } else {\n    if (*start < *stop) {\n      return (*stop - *start - 1) / step + 1;\n    }\n  }\n  return 0;\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/special.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <fmt/args.h>\n#include <limits>\n#include <map>\n#include <memory>\n#include <string>\n#include <tuple>\n#include <vector>\n\n#include \"codon/cir/attribute.h\"\n#include \"codon/cir/types/types.h\"\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/visitors/scoping/scoping.h\"\n#include \"codon/parser/visitors/typecheck/typecheck.h\"\n\nusing namespace codon::error;\n\nnamespace codon::ast {\n\nusing namespace types;\n\n/// Generate ASTs for all internal functions that deal with vtable generation.\n/// Intended to be called once the typechecking is done.\n/// TODO: add JIT compatibility.\n\nvoid TypecheckVisitor::prepareVTables() {\n  // def RTTIType._get_thunk_id(F, T):\n  //   return VID\n  auto fn =\n      getFunction(getMangledMethod(\"std.internal.core\", \"RTTIType\", \"_get_thunk_id\"));\n  auto oldAst = fn->ast;\n  // Keep iterating as thunks can generate more thunks.\n  std::unordered_set<std::string> cache;\n  for (bool added = true; added;) {\n    added = false;\n    for (const auto &[rn, real] : fn->realizations) {\n      if (in(cache, rn))\n        continue;\n      cache.insert(rn);\n      added = true;\n      fn->ast->suite = generateGetThunkIDAst(real->getType());\n      real->type->ast = fn->ast;\n      LOG_REALIZE(\"[poly] {} : {}\", real->type->debugString(2), fn->ast->toString(2));\n      realizeFunc(real->type.get(), true);\n      fn->ast = oldAst;\n    }\n  }\n\n  fn = getFunction(\n      getMangledMethod(\"std.internal.core\", \"RTTIType\", \"_populate_vtables\"));\n  fn->ast->suite = generateClassPopulateVTablesAST();\n  auto typ = fn->realizations.begin()->second->getType();\n  typ->ast = fn->ast;\n  LOG_REALIZE(\"[poly] {} : {}\", typ->debugString(2), fn->ast->toString(2));\n  realizeFunc(typ, true);\n\n  // def RTTIType._dist(B, D):\n  //   return Tuple[<types before B is reached in D>].__elemsize__\n  fn = getFunction(getMangledMethod(\"std.internal.core\", \"RTTIType\", \"_dist\"));\n  oldAst = fn->ast;\n  for (const auto &real : fn->realizations | std::views::values) {\n    fn->ast->suite = generateBaseDerivedDistAST(real->getType());\n    real->type->ast = fn->ast;\n    LOG_REALIZE(\"[poly] {} : {}\", real->type->debugString(2), fn->ast->toString(2));\n    realizeFunc(real->type.get(), true);\n  }\n  fn->ast = oldAst;\n}\n\nSuiteStmt *TypecheckVisitor::generateClassPopulateVTablesAST() {\n  auto suite = N<SuiteStmt>();\n  for (const auto &cls : ctx->cache->classes | std::views::values) {\n    for (const auto &[r, real] : cls.realizations) {\n      if (real->vtable.empty())\n        continue;\n      // RTTIType._init_vtable(size, real.type)\n      suite->addStmt(N<ExprStmt>(\n          N<CallExpr>(N<DotExpr>(N<IdExpr>(\"RTTIType\"), \"_init_vtable\"),\n                      N<IntExpr>(ctx->cache->thunkIds.size() + 2), N<IdExpr>(r))));\n      LOG_REALIZE(\"[poly] {} -> {}\", r, real->id);\n      for (const auto &[key, fn] : real->vtable) {\n        auto id = in(ctx->cache->thunkIds, key);\n        seqassert(id, \"key {} not found in thunkIds\", key);\n        std::vector<Expr *> ids;\n        for (const auto &t : *fn)\n          ids.push_back(N<IdExpr>(t.getType()->realizedName()));\n        // p[real.ID].__setitem__(f.ID, Function[<TYPE_F>](f).__raw__())\n        LOG_REALIZE(\"[poly] vtable[{}!!{}][{}] = {}\", real->getType()->realizedName(),\n                    real->id, *id, fn->realizedName());\n        Expr *fnCall = N<CallExpr>(\n            N<InstantiateExpr>(\n                N<IdExpr>(\"Function\"),\n                std::vector<Expr *>{N<InstantiateExpr>(N<IdExpr>(TYPE_TUPLE), ids),\n                                    N<IdExpr>(fn->getRetType()->realizedName())}),\n            N<IdExpr>(fn->realizedName()));\n        suite->addStmt(N<ExprStmt>(\n            N<CallExpr>(N<DotExpr>(N<IdExpr>(\"RTTIType\"), \"_set_vtable_fn\"),\n                        N<IntExpr>(real->id), N<IntExpr>(int64_t(*id)),\n                        N<CallExpr>(N<DotExpr>(fnCall, \"__raw__\")), N<IdExpr>(r))));\n      }\n    }\n  }\n  return suite;\n}\n\nSuiteStmt *TypecheckVisitor::generateBaseDerivedDistAST(FuncType *f) {\n  auto baseTyp = extractFuncGeneric(f, 0)->getClass();\n  size_t baseTypFields = 0;\n  for (auto &fld : getClassFields(baseTyp)) {\n    if (fld.baseClass == baseTyp->name) {\n      baseTypFields++;\n    }\n  }\n\n  std::unordered_set<std::string> alreadyDerived;\n  for (auto &m : getClass(baseTyp)->mro)\n    alreadyDerived.insert(m->name);\n\n  auto derivedTyp = extractFuncGeneric(f, 1)->getClass();\n  auto fields = getClassFields(derivedTyp);\n  auto types = std::vector<Expr *>{};\n  auto found = false;\n  for (auto &fld : fields) {\n    if (in(alreadyDerived, fld.baseClass)) {\n      found = true;\n      break;\n    } else {\n      auto ft = realize(instantiateType(fld.getType(), derivedTyp));\n      types.push_back(N<IdExpr>(ft->realizedName()));\n    }\n  }\n  seqassert(found || !baseTypFields, \"cannot find distance between {} and {}\",\n            derivedTyp->name, baseTyp->name);\n  Stmt *suite = N<ReturnStmt>(\n      N<DotExpr>(N<InstantiateExpr>(N<IdExpr>(TYPE_TUPLE), types), \"__elemsize__\"));\n  return SuiteStmt::wrap(suite);\n}\n\nFunctionStmt *TypecheckVisitor::generateThunkAST(const FuncType *fp, ClassType *base,\n                                                 const ClassType *derived) {\n  auto ct = instantiateType(extractClassType(derived->name), base->getClass());\n  std::vector<types::Type *> args;\n  for (const auto &a : *fp)\n    args.push_back(a.getType());\n  args[0] = ct.get();\n  auto m = findBestMethod(ct->getClass(), getUnmangledName(fp->getFuncName()), args);\n  if (!m) {\n    // Print a nice error message\n    std::vector<std::string> a;\n    for (auto &t : args)\n      a.emplace_back(fmt::format(\"{}\", t->prettyString()));\n    std::string argsNice = fmt::format(\"({})\", join(a, \", \"));\n    E(Error::DOT_NO_ATTR_ARGS, getSrcInfo(), ct->prettyString(),\n      getUnmangledName(fp->getFuncName()), argsNice);\n  }\n\n  std::vector<std::string> ns;\n  for (auto &a : args)\n    ns.push_back(a->realizedName());\n  auto thunkName =\n      fmt::format(\"_thunk.{}.{}.{}\", base->name, fp->getFuncName(), join(ns, \".\"));\n  if (getFunction(getMangledFunc(\"\", thunkName)))\n    return nullptr;\n\n  // Thunk contents:\n  // def _thunk.<BASE>.<FN>.<ARGS>(self, <ARGS...>):\n  //   return <FN>(\n  //     RTTIType._to_derived(self, <BASE>, <DERIVED>),\n  //     <ARGS...>)\n  std::vector<Param> fnArgs;\n  fnArgs.emplace_back(\"self\", N<IdExpr>(base->realizedName()), nullptr);\n  for (size_t i = 1; i < args.size(); i++)\n    fnArgs.emplace_back(getUnmangledName((*fp->ast)[i].getName()),\n                        N<IdExpr>(args[i]->realizedName()), nullptr);\n  std::vector<Expr *> callArgs;\n  callArgs.emplace_back(N<CallExpr>(N<DotExpr>(N<IdExpr>(\"RTTIType\"), \"_to_derived\"),\n                                    N<IdExpr>(\"self\"), N<IdExpr>(base->realizedName()),\n                                    N<IdExpr>(derived->realizedName())));\n  for (size_t i = 1; i < args.size(); i++)\n    callArgs.emplace_back(N<IdExpr>(getUnmangledName((*fp->ast)[i].getName())));\n\n  std::vector<Expr *> debugCallArgs{N<StringExpr>(base->name),\n                                    N<StringExpr>(fp->getFuncName()),\n                                    N<StringExpr>(join(ns, \".\"))};\n  debugCallArgs.insert(debugCallArgs.end(), callArgs.begin(), callArgs.end());\n  auto thunkAst = N<FunctionStmt>(\n      thunkName, nullptr, fnArgs,\n      N<SuiteStmt>(\n          // For debugging\n          N<ExprStmt>(N<CallExpr>(N<IdExpr>(getMangledMethod(\n                                      \"std.internal.core\", \"RTTIType\", \"_thunk_debug\")),\n                                  debugCallArgs)),\n          N<ReturnStmt>(N<CallExpr>(N<IdExpr>(m->ast->getName()), callArgs))));\n  thunkAst->setAttribute(Attr::Inline);\n  return cast<FunctionStmt>(transform(thunkAst));\n}\n\n/// Generate thunks in all derived classes for a given virtual function (must be fully\n/// realizable) and the corresponding base class.\n/// @return unique thunk ID.\nSuiteStmt *TypecheckVisitor::generateGetThunkIDAst(types::FuncType *f) {\n  auto fp = extractType(extractFuncGeneric(f))->getFunc();\n  auto cp = extractType(extractFuncGeneric(f, 1))->getClass();\n\n  seqassert(cp && cp->canRealize() && fp && fp->canRealize() &&\n                fp->getRetType()->canRealize(),\n            \"bad {}\", f->debugString(2));\n\n  // TODO: ugly, ugly; surely needs refactoring\n\n  // Function signature for storing thunks\n  auto sig = [&](const types::FuncType *ft) -> std::string {\n    std::vector<std::string> gs;\n    for (const auto &a : *ft)\n      gs.emplace_back(a.getType()->realizedName());\n    gs.emplace_back(\"|\");\n    for (auto &a : ft->funcGenerics)\n      if (!a.name.empty())\n        gs.push_back(a.type->realizedName());\n    return fmt::format(\"{}:{}\", getUnmangledName(ft->getFuncName()), join(gs, \",\"));\n  };\n\n  // Set up the base class information\n  auto baseCls = cp->name;\n  auto fnSig = sig(fp);\n  auto key = std::make_pair(baseCls, fnSig);\n\n  // Add or extract thunk ID\n  auto baseRealization = getClassRealization(cp);\n  seqassert(!in(baseRealization->vtable, key), \"thunk {}.{} already added\", baseCls,\n            fnSig);\n  if (!in(ctx->cache->thunkIds, key))\n    ctx->cache->thunkIds[key] = 1 + ctx->cache->thunkIds.size();\n  auto vid = ctx->cache->thunkIds[key];\n  baseRealization->vtable[key] =\n      std::static_pointer_cast<FuncType>(fp->shared_from_this());\n\n  // Iterate through all derived classes and instantiate the corresponding thunk\n  for (const auto &[clsName, cls] : ctx->cache->classes) {\n    bool inMro = false;\n    for (auto &m : cls.mro)\n      if (m && m->is(baseCls)) {\n        inMro = true;\n        break;\n      }\n    if (inMro && clsName != baseCls) {\n      for (const auto &real : cls.realizations | std::views::values) {\n        if (auto thunkAst = generateThunkAST(fp, cp, real->getType())) {\n          auto thunkFn = getFunction(thunkAst->name);\n          auto ti =\n              std::static_pointer_cast<FuncType>(instantiateType(thunkFn->getType()));\n          auto tm = realizeFunc(ti.get(), true);\n          seqassert(tm, \"bad thunk {}\", thunkFn->type->debugString(2));\n          seqassert(!in(real->vtable, key), \"thunk {}.{} already added to {}\", baseCls,\n                    fnSig, real->getType()->realizedName());\n          real->vtable[key] =\n              std::static_pointer_cast<FuncType>(tm->shared_from_this());\n          LOG_REALIZE(\"[thunk]: {}->{}@{} == {}\", baseCls,\n                      real->getType()->realizedName(), key, vid);\n        }\n      }\n    }\n  }\n  return N<SuiteStmt>(N<ReturnStmt>(N<IntExpr>(vid)));\n}\n\nSuiteStmt *TypecheckVisitor::generateFunctionCallInternalAST(FuncType *type) {\n  // Special case: Function.__call_internal__\n  /// TODO: move to IR one day\n  std::vector<Stmt *> items;\n  items.push_back(nullptr);\n  std::vector<std::string> ll;\n  std::vector<std::string> lla;\n  seqassert(extractFuncArgType(type, 1)->is(TYPE_TUPLE), \"bad function base: {}\",\n            extractFuncArgType(type, 1)->debugString(2));\n  auto as = extractFuncArgType(type, 1)->getClass()->generics.size();\n  auto [_, ag] = (*type->ast)[1].getNameWithStars();\n  for (int i = 0; i < as; i++) {\n    ll.push_back(fmt::format(\"%{} = extractvalue {{}} %args, {}\", i, i));\n    items.push_back(N<ExprStmt>(N<IdExpr>(ag)));\n  }\n  items.push_back(N<ExprStmt>(N<IdExpr>(\"TR\")));\n  for (int i = 0; i < as; i++) {\n    items.push_back(N<ExprStmt>(N<IndexExpr>(N<IdExpr>(ag), N<IntExpr>(i))));\n    lla.push_back(fmt::format(\"{{}} %{}\", i));\n  }\n  items.push_back(N<ExprStmt>(N<IdExpr>(\"TR\")));\n  ll.push_back(fmt::format(\"%{} = call {{}} %self({})\", as, combine2(lla)));\n  ll.push_back(fmt::format(\"ret {{}} %{}\", as));\n  items[0] = N<ExprStmt>(N<StringExpr>(combine2(ll, \"\\n\")));\n  return N<SuiteStmt>(items);\n}\n\nSuiteStmt *TypecheckVisitor::generateUnionNewAST(const FuncType *type) {\n  auto unionType = type->funcParent->getUnion();\n  seqassert(unionType, \"expected union, got {}\", *(type->funcParent));\n\n  Stmt *suite = N<ReturnStmt>(N<CallExpr>(N<DotExpr>(N<IdExpr>(\"Union\"), \"_new\"),\n                                          N<IdExpr>(type->ast->begin()->name),\n                                          N<IdExpr>(unionType->realizedName())));\n  return SuiteStmt::wrap(suite);\n}\n\nSuiteStmt *TypecheckVisitor::generateUnionTagAST(FuncType *type) {\n  //   return Union._get_data(union, T0)\n  auto tag = getIntLiteral(extractFuncGeneric(type));\n  auto unionType = extractFuncArgType(type)->getUnion();\n  auto unionTypes = unionType->getRealizationTypes();\n  if (tag < 0 || tag >= unionTypes.size())\n    E(Error::CUSTOM, getSrcInfo(), \"bad union tag\");\n  auto selfVar = type->ast->begin()->name;\n  auto suite = N<SuiteStmt>(N<ReturnStmt>(N<CallExpr>(\n      N<IdExpr>(getMangledMethod(\"std.internal.core\", \"Union\", \"_get_data\")),\n      N<IdExpr>(selfVar), N<IdExpr>(unionTypes[tag]->realizedName()))));\n  return suite;\n}\n\nSuiteStmt *TypecheckVisitor::generateNamedKeysAST(FuncType *type) {\n  auto n = getIntLiteral(extractFuncGeneric(type));\n  if (n < 0 || n >= ctx->cache->generatedTupleNames.size())\n    E(Error::CUSTOM, getSrcInfo(), \"bad namedkeys index\");\n  std::vector<Expr *> s;\n  for (auto &k : ctx->cache->generatedTupleNames[n])\n    s.push_back(N<StringExpr>(k));\n  auto suite = N<SuiteStmt>(N<ReturnStmt>(N<TupleExpr>(s)));\n  return suite;\n}\n\nSuiteStmt *TypecheckVisitor::generateTupleMulAST(FuncType *type) {\n  auto n = std::max(static_cast<int64_t>(0), getIntLiteral(extractFuncGeneric(type)));\n  auto t = extractFuncArgType(type)->getClass();\n  if (!t || !t->is(TYPE_TUPLE))\n    return nullptr;\n  std::vector<Expr *> exprs;\n  for (size_t i = 0; i < n; i++)\n    for (size_t j = 0; j < t->generics.size(); j++)\n      exprs.push_back(\n          N<IndexExpr>(N<IdExpr>(type->ast->front().getName()), N<IntExpr>(j)));\n  auto suite = N<SuiteStmt>(N<ReturnStmt>(N<TupleExpr>(exprs)));\n  return suite;\n}\n\n/// Generate ASTs for dynamically generated functions.\nSuiteStmt *TypecheckVisitor::generateSpecialAst(types::FuncType *type) {\n  // Clone the generic AST that is to be realized\n  auto ast = type->ast;\n  if (ast->hasAttribute(Attr::AutoGenerated) && endswith(ast->name, \".__iter__:0\") &&\n      isHeterogenous(extractFuncArgType(type, 0))) {\n    // Special case: do not realize auto-generated heterogenous __iter__\n    E(Error::EXPECTED_TYPE, getSrcInfo(), \"iterable\");\n  } else if (ast->hasAttribute(Attr::AutoGenerated) &&\n             endswith(ast->name, \".__getitem__:0\") &&\n             isHeterogenous(extractFuncArgType(type, 0))) {\n    // Special case: do not realize auto-generated heterogenous __getitem__\n    E(Error::EXPECTED_TYPE, getSrcInfo(), \"iterable\");\n  } else if (startswith(ast->name, \"Function.__call_internal__\")) {\n    return generateFunctionCallInternalAST(type);\n  } else if (startswith(ast->name, \"Union.__new__\")) {\n    return generateUnionNewAST(type);\n  } else if (startswith(ast->name,\n                        getMangledMethod(\"std.internal.core\", \"Union\", \"_tag\"))) {\n    return generateUnionTagAST(type);\n  } else if (startswith(ast->name, getMangledMethod(\"std.internal.core\", \"NamedTuple\",\n                                                    \"_namedkeys\"))) {\n    return generateNamedKeysAST(type);\n  } else if (startswith(ast->name,\n                        getMangledMethod(\"std.internal.core\", \"__magic__\", \"mul\"))) {\n    return generateTupleMulAST(type);\n  }\n  return nullptr;\n}\n\n/// Transform named tuples.\n/// @example\n///   `namedtuple(\"NT\", [\"a\", (\"b\", int)])` -> ```@tuple\n///                                               class NT[T1]:\n///                                                 a: T1\n///                                                 b: int```\nExpr *TypecheckVisitor::transformNamedTuple(CallExpr *expr) {\n  // Ensure that namedtuple call is valid\n  auto name = getStrLiteral(extractFuncGeneric(expr->getExpr()->getType()));\n  if (expr->size() != 1)\n    E(Error::CALL_NAMEDTUPLE, expr);\n\n  // Construct the class statement\n  std::vector<Param> generics, params;\n  auto orig = cast<TupleExpr>(expr->front().getExpr()->getOrigExpr());\n  size_t ti = 1;\n  for (auto *i : *orig) {\n    if (auto s = cast<StringExpr>(i)) {\n      generics.emplace_back(fmt::format(\"T{}\", ti), N<IdExpr>(TYPE_TYPE), nullptr,\n                            true);\n      params.emplace_back(s->getValue(), N<IdExpr>(fmt::format(\"T{}\", ti++)), nullptr);\n      continue;\n    }\n    auto t = cast<TupleExpr>(i);\n    if (t && t->size() == 2 && cast<StringExpr>((*t)[0])) {\n      params.emplace_back(cast<StringExpr>((*t)[0])->getValue(), transformType((*t)[1]),\n                          nullptr);\n      continue;\n    }\n    E(Error::CALL_NAMEDTUPLE, i);\n  }\n  for (auto &g : generics)\n    params.push_back(g);\n  auto cls = N<SuiteStmt>(\n      N<ClassStmt>(name, params, nullptr, std::vector<Expr *>{N<IdExpr>(\"tuple\")}));\n  if (auto err = ast::ScopingVisitor::apply(ctx->cache, cls))\n    throw exc::ParserException(std::move(err));\n  prependStmts->push_back(transform(cls));\n  return transformType(N<IdExpr>(name));\n}\n\n/// Transform partial calls (Python syntax).\n/// @example\n///   `partial(foo, 1, a=2)` -> `foo(1, a=2, ...)`\nExpr *TypecheckVisitor::transformFunctoolsPartial(CallExpr *expr) {\n  if (expr->empty())\n    E(Error::CALL_PARTIAL, getSrcInfo());\n  std::vector<CallArg> args(expr->items.begin() + 1, expr->items.end());\n  args.emplace_back(\"\", N<EllipsisExpr>(EllipsisExpr::PARTIAL));\n  return transform(N<CallExpr>(expr->begin()->value, args));\n}\n\n/// Typecheck superf method. This method provides the access to the previous matching\n/// overload.\n/// @example\n///   ```class cls:\n///        def foo(): print('foo 1')\n///        def foo():\n///          superf()  # access the previous foo\n///          print('foo 2')\n///      cls.foo()```\n///   prints \"foo 1\" followed by \"foo 2\"\nExpr *TypecheckVisitor::transformSuperF(CallExpr *expr) {\n  auto func = ctx->getBase()->type->getFunc();\n\n  // Find list of matching superf methods\n  std::vector<types::FuncType *> supers;\n  if (!isDispatch(func)) {\n    if (auto a = func->ast->getAttribute<ir::StringValueAttribute>(Attr::ParentClass)) {\n      auto c = getClass(a->value);\n      if (auto m = in(c->methods, getUnmangledName(func->getFuncName()))) {\n        for (auto &overload : getOverloads(*m)) {\n          if (isDispatch(overload))\n            continue;\n          if (overload == func->getFuncName())\n            break;\n          supers.emplace_back(getFunction(overload)->getType());\n        }\n      }\n      std::ranges::reverse(supers);\n    }\n  }\n  if (supers.empty())\n    E(Error::CALL_SUPERF, expr);\n\n  seqassert(expr->size() == 1 && cast<CallExpr>(expr->begin()->getExpr()),\n            \"bad superf call\");\n  std::vector<CallArg> newArgs;\n  for (const auto &a : *cast<CallExpr>(expr->begin()->getExpr()))\n    newArgs.emplace_back(a.getExpr());\n  auto m = findMatchingMethods(\n      func->funcParent ? func->funcParent->getClass() : nullptr, supers, newArgs);\n  if (m.empty())\n    E(Error::CALL_SUPERF, expr);\n  auto c = transform(N<CallExpr>(N<IdExpr>(m[0]->getFuncName()), newArgs));\n  return c;\n}\n\n/// Typecheck and transform super method. Replace it with the current self object cast\n/// to the first inherited type.\n/// TODO: only an empty super() is currently supported.\nExpr *TypecheckVisitor::transformSuper() {\n  if (!ctx->getBase()->type)\n    E(Error::CALL_SUPER_PARENT, getSrcInfo());\n  auto funcTyp = ctx->getBase()->type->getFunc();\n  if (!funcTyp || !funcTyp->ast->hasAttribute(Attr::Method))\n    E(Error::CALL_SUPER_PARENT, getSrcInfo());\n  if (funcTyp->empty())\n    E(Error::CALL_SUPER_PARENT, getSrcInfo());\n\n  ClassType *typ = extractFuncArgType(funcTyp)->getClass();\n  auto cls = getClass(typ);\n  auto cands = cls->staticParentClasses;\n  if (cands.empty()) {\n    // Dynamic inheritance: use MRO\n    // TODO: maybe super() should be split into two separate functions...\n    const auto &vCands = cls->mro;\n    if (vCands.size() < 2)\n      E(Error::CALL_SUPER_PARENT, getSrcInfo());\n\n    auto superTyp = instantiateType(vCands[1].get(), typ);\n    auto self = N<IdExpr>(funcTyp->ast->begin()->name);\n    self->setType(typ->shared_from_this());\n\n    auto typExpr = N<IdExpr>(superTyp->getClass()->name);\n    typExpr->setType(instantiateTypeVar(superTyp->getClass()));\n    return transform(N<CallExpr>(N<DotExpr>(N<IdExpr>(\"Super\"), \"_super\"), self,\n                                 typExpr, N<IntExpr>(1)));\n  }\n\n  const auto &name = cands.front(); // the first inherited type\n  auto superTyp = instantiateType(extractClassType(name), typ);\n  if (typ->isRecord()) {\n    // Case: tuple types. Return `tuple(obj.args...)`\n    std::vector<Expr *> members;\n    for (auto &field : getClassFields(superTyp->getClass()))\n      members.push_back(\n          N<DotExpr>(N<IdExpr>(funcTyp->ast->begin()->getName()), field.name));\n    Expr *e = transform(N<TupleExpr>(members));\n    auto ft = getClassFieldTypes(superTyp->getClass());\n    for (size_t i = 0; i < ft.size(); i++)\n      unify(ft[i].get(), extractClassGeneric(e->getType(), i)); // see super_tuple test\n    e->setType(superTyp->shared_from_this());\n    return e;\n  } else {\n    // Case: reference types. Return `Super._super(self, T)`\n    auto self = N<IdExpr>(funcTyp->ast->begin()->name);\n    self->setType(typ->shared_from_this());\n    return castToSuperClass(self, superTyp->getClass());\n  }\n}\n\n/// Typecheck __ptr__ method. This method creates a pointer to an object. Ensure that\n/// the argument is a variable binding.\nExpr *TypecheckVisitor::transformPtr(CallExpr *expr) {\n  expr->begin()->value = transform(expr->begin()->getExpr());\n\n  auto head = getHeadExpr(expr->begin()->getExpr());\n  std::vector<std::string> members;\n  for (bool last = true;; last = false) {\n    auto t = extractClassType(head);\n    if (!t)\n      return nullptr;\n    if (!last && !t->isRecord())\n      E(Error::CALL_PTR_VAR, expr->begin()->getExpr());\n\n    if (auto id = cast<IdExpr>(head)) {\n      auto val = id ? ctx->find(id->getValue(), getTime()) : nullptr;\n      if (!val || !val->isVar())\n        E(Error::CALL_PTR_VAR, expr->begin()->getExpr());\n      break;\n    } else if (auto dot = cast<DotExpr>(head)) {\n      head = dot->getExpr();\n    } else {\n      E(Error::CALL_PTR_VAR, expr->begin()->getExpr());\n      break;\n    }\n  }\n\n  unify(expr->getType(),\n        instantiateType(getStdLibType(\"Ptr\"), {expr->begin()->getExpr()->getType()}));\n  if (expr->begin()->getExpr()->isDone())\n    expr->setDone();\n  return nullptr;\n}\n\n/// Typecheck __array__ method. This method creates a stack-allocated array via alloca.\nExpr *TypecheckVisitor::transformArray(CallExpr *expr) {\n  auto arrTyp = expr->expr->getType()->getFunc();\n  unify(expr->getType(),\n        instantiateType(getStdLibType(\"Array\"),\n                        {extractClassGeneric(arrTyp->getParentType())}));\n  if (realize(expr->getType()))\n    expr->setDone();\n  return nullptr;\n}\n\n/// Transform isinstance method to a static boolean expression.\n/// Special cases:\n///   `isinstance(obj, ByVal)` is True if `type(obj)` is a tuple type\n///   `isinstance(obj, ByRef)` is True if `type(obj)` is a reference type\nExpr *TypecheckVisitor::transformIsInstance(CallExpr *expr) {\n  if (auto u = expr->getType()->getUnbound())\n    u->staticKind = LiteralKind::Bool;\n\n  expr->begin()->value = transform(expr->begin()->getExpr());\n  auto typ = expr->begin()->getExpr()->getClassType();\n  if (!typ || !typ->canRealize())\n    return nullptr;\n\n  expr->begin()->value = transform(expr->begin()->getExpr()); // again to realize it\n\n  typ = extractClassType(typ);\n  auto &typExpr = (*expr)[1].value;\n  if (cast<CallExpr>(typExpr)) {\n    // Handle `isinstance(obj, (type1, type2, ...))`\n    if (typExpr->getOrigExpr() && cast<TupleExpr>(typExpr->getOrigExpr())) {\n      Expr *result = transform(N<BoolExpr>(false));\n      for (auto *i : *cast<TupleExpr>(typExpr->getOrigExpr())) {\n        result = transform(N<BinaryExpr>(\n            result, \"||\",\n            N<CallExpr>(N<IdExpr>(\"isinstance\"), expr->begin()->getExpr(), i)));\n      }\n      return result;\n    }\n  }\n\n  auto tei = cast<IdExpr>(typExpr);\n  if (tei && tei->getValue() == \"type\") {\n    return transform(N<BoolExpr>(isTypeExpr(expr->begin()->value)));\n  } else if (tei && tei->getValue() == \"type[Tuple]\") {\n    return transform(N<BoolExpr>(typ->is(TYPE_TUPLE)));\n  } else if (tei && tei->getValue() == \"type[ByVal]\") {\n    return transform(N<BoolExpr>(typ->isRecord()));\n  } else if (tei && tei->getValue() == \"type[ByRef]\") {\n    return transform(N<BoolExpr>(!typ->isRecord()));\n  } else if (tei && tei->getValue() == \"type[Union]\") {\n    return transform(N<BoolExpr>(typ->getUnion() != nullptr));\n  } else if (!extractType(typExpr)->getUnion() && typ->getUnion()) {\n    auto unionTypes = typ->getUnion()->getRealizationTypes();\n    int tag = -1;\n    for (size_t ui = 0; ui < unionTypes.size(); ui++) {\n      if (extractType(typExpr)->unify(unionTypes[ui], nullptr) >= 0) {\n        tag = static_cast<int>(ui);\n        break;\n      }\n    }\n    if (tag == -1)\n      return transform(N<BoolExpr>(false));\n    return transform(\n        N<BinaryExpr>(N<CallExpr>(N<DotExpr>(N<IdExpr>(\"Union\"), \"_get_tag\"),\n                                  expr->begin()->getExpr()),\n                      \"==\", N<IntExpr>(tag)));\n  } else if (typExpr->getType()->is(\"pyobj\")) {\n    if (typ->is(\"pyobj\")) {\n      return transform(\n          N<CallExpr>(N<IdExpr>(getMangledFunc(\"std.internal.python\", \"_isinstance\")),\n                      expr->begin()->getExpr(), (*expr)[1].getExpr()));\n    } else {\n      return transform(N<BoolExpr>(false));\n    }\n  }\n\n  typExpr = transformType(typExpr);\n  auto targetType = extractType(typExpr);\n  // Check static super types (i.e., statically inherited) as well\n  for (auto &tx : getStaticSuperTypes(typ->getClass())) {\n    types::Type::Unification us;\n    auto s = tx->unify(targetType, &us);\n    us.undo();\n    if (s >= 0)\n      return transform(N<BoolExpr>(true));\n  }\n\n  // Check RTTI super types\n  for (auto &tx : getRTTISuperTypes(typ->getClass())) {\n    types::Type::Unification us;\n    auto s = tx->unify(targetType, &us);\n    us.undo();\n    if (s >= 0)\n      return transform(N<BoolExpr>(true));\n  }\n\n  // Check runtime RTTI info if needed\n  for (auto &tx : getRTTISuperTypes(targetType->getClass())) {\n    types::Type::Unification us;\n    auto s = tx->unify(typ, &us);\n    us.undo();\n    if (s >= 0) {\n      // check RTTI match\n      return transform(N<CallExpr>(\n          N<IdExpr>(getMangledMethod(\"std.internal.core\", \"RTTIType\", \"_isinstance\")),\n          expr->begin()->getExpr(), (*expr)[1].getExpr()));\n    }\n  }\n\n  return transform(N<BoolExpr>(false));\n}\n\n/// Transform staticlen method to a static integer expression. This method supports only\n/// static strings and tuple types.\nExpr *TypecheckVisitor::transformStaticLen(CallExpr *expr) {\n  if (auto u = expr->getType()->getUnbound())\n    u->staticKind = LiteralKind::Int;\n\n  expr->begin()->value = transform(expr->begin()->getExpr());\n  auto typ = extractType(expr->begin()->getExpr());\n\n  if (auto ss = typ->getStrStatic()) {\n    // Case: staticlen on static strings\n    return transform(N<IntExpr>(ss->value.size()));\n  }\n  if (!typ->getClass())\n    return nullptr;\n  if (typ->getUnion()) {\n    if (realize(typ))\n      return transform(N<IntExpr>(typ->getUnion()->getRealizationTypes().size()));\n    return nullptr;\n  }\n  if (!typ->getClass()->isRecord())\n    E(Error::EXPECTED_TUPLE, expr->begin()->getExpr());\n  return transform(N<IntExpr>(getClassFields(typ->getClass()).size()));\n}\n\n/// Transform hasattr method to a static boolean expression.\n/// This method also supports additional argument types that are used to check\n/// for a matching overload (not available in Python).\nExpr *TypecheckVisitor::transformHasAttr(CallExpr *expr) {\n  if (auto u = expr->getType()->getUnbound())\n    u->staticKind = LiteralKind::Bool;\n\n  auto typ = extractClassType((*expr)[0].getExpr());\n  if (!typ)\n    return nullptr;\n\n  auto member = getStrLiteral(extractFuncGeneric(expr->getExpr()->getType()));\n  std::vector<std::pair<std::string, types::Type *>> args{{\"\", typ}};\n\n  if (auto tup = cast<CallExpr>((*expr)[1].getExpr())) {\n    for (auto &a : *tup) {\n      a.value = transform(a.getExpr());\n      if (!a.getExpr()->getClassType())\n        return nullptr;\n      auto t = extractType(a);\n      args.emplace_back(\"\", t->is(\"TypeWrap\") ? extractClassGeneric(t) : t);\n    }\n  }\n  for (auto &[n, ne] : extractNamedTuple((*expr)[2].getExpr())) {\n    ne = transform(ne);\n    auto t = extractType(ne);\n    args.emplace_back(n, t->is(\"TypeWrap\") ? extractClassGeneric(t) : t);\n  }\n\n  if (typ->getUnion()) {\n    Expr *cond = nullptr;\n    auto unionTypes = typ->getUnion()->getRealizationTypes();\n    for (auto &unionType : unionTypes) {\n      auto tu = realize(unionType);\n      if (!tu)\n        return nullptr;\n      auto te = N<IdExpr>(tu->getClass()->realizedName());\n      auto e = N<BinaryExpr>(\n          N<CallExpr>(N<IdExpr>(\"isinstance\"), (*expr)[0].getExpr(), te), \"&&\",\n          N<CallExpr>(N<IdExpr>(\"hasattr\"), te, N<StringExpr>(member)));\n      cond = !cond ? e : N<BinaryExpr>(cond, \"||\", e);\n    }\n    if (!cond)\n      return transform(N<BoolExpr>(false));\n    return transform(cond);\n  } else if (typ->is(\"NamedTuple\")) {\n    if (!typ->canRealize())\n      return nullptr;\n    auto id = getIntLiteral(typ);\n    seqassert(id >= 0 && id < ctx->cache->generatedTupleNames.size(), \"bad id: {}\", id);\n    const auto &names = ctx->cache->generatedTupleNames[id];\n    return transform(N<BoolExpr>(in(names, member)));\n  }\n\n  bool exists = !findMethod(typ->getClass(), member).empty() ||\n                findMember(typ->getClass(), member);\n  if (exists && args.size() > 1) {\n    exists &= findBestMethod(typ, member, args) != nullptr;\n  }\n  return transform(N<BoolExpr>(exists));\n}\n\n/// Transform getattr method to a DotExpr.\nExpr *TypecheckVisitor::transformGetAttr(CallExpr *expr) {\n  auto name = getStrLiteral(extractFuncGeneric(expr->expr->getType()));\n\n  // special handling for NamedTuple\n  if (expr->begin()->getExpr()->getType() &&\n      expr->begin()->getExpr()->getType()->is(\"NamedTuple\")) {\n    auto val = expr->begin()->getExpr()->getClassType();\n    auto id = getIntLiteral(val);\n    seqassert(id >= 0 && id < ctx->cache->generatedTupleNames.size(), \"bad id: {}\", id);\n    auto names = ctx->cache->generatedTupleNames[id];\n    for (size_t i = 0; i < names.size(); i++)\n      if (names[i] == name) {\n        return transform(\n            N<IndexExpr>(N<DotExpr>(expr->begin()->getExpr(), \"args\"), N<IntExpr>(i)));\n      }\n    E(Error::DOT_NO_ATTR, expr, val->prettyString(), name);\n  }\n  return transform(N<DotExpr>(expr->begin()->getExpr(), name));\n}\n\n/// Transform setattr method to a AssignMemberStmt.\nExpr *TypecheckVisitor::transformSetAttr(CallExpr *expr) {\n  auto attr = getStrLiteral(extractFuncGeneric(expr->expr->getType()));\n  return transform(\n      N<StmtExpr>(N<AssignMemberStmt>((*expr)[0].getExpr(), attr, (*expr)[1].getExpr()),\n                  N<CallExpr>(N<IdExpr>(\"NoneType\"))));\n}\n\n/// Raise a compiler error.\nExpr *TypecheckVisitor::transformCompileError(CallExpr *expr) const {\n  auto msg = getStrLiteral(extractFuncGeneric(expr->expr->getType()));\n  E(Error::CUSTOM, expr, msg.c_str());\n  return nullptr;\n}\n\n/// Convert a class to a tuple.\nExpr *TypecheckVisitor::transformTupleFn(CallExpr *expr) {\n  for (auto &a : *expr)\n    a.value = transform(a.getExpr());\n  auto cls = extractClassType(expr->begin()->getExpr()->getType());\n  if (!cls)\n    return nullptr;\n\n  // tuple(ClassType) is a tuple type that corresponds to a class\n  if (isTypeExpr(expr->begin()->getExpr())) {\n    if (!realize(cls))\n      return expr;\n\n    std::vector<Expr *> items;\n    auto ft = getClassFieldTypes(cls);\n    for (size_t i = 0; i < ft.size(); i++) {\n      auto rt = realize(ft[i].get());\n      seqassert(rt, \"cannot realize '{}' in {}\", getClass(cls)->fields[i].name,\n                cls->debugString(2));\n      items.push_back(N<IdExpr>(rt->realizedName()));\n    }\n    auto e = transform(N<InstantiateExpr>(N<IdExpr>(TYPE_TUPLE), items));\n    return e;\n  }\n\n  std::vector<Expr *> args;\n  std::string var = getTemporaryVar(\"tup\");\n  for (auto &field : getClassFields(cls))\n    args.emplace_back(N<DotExpr>(N<IdExpr>(var), field.name));\n\n  return transform(N<StmtExpr>(N<AssignStmt>(N<IdExpr>(var), expr->begin()->getExpr()),\n                               N<TupleExpr>(args)));\n}\n\n/// Transform type function to a type IdExpr identifier.\nExpr *TypecheckVisitor::transformTypeFn(CallExpr *expr) {\n  expr->begin()->value = transform(expr->begin()->getExpr());\n  unify(expr->getType(), instantiateTypeVar(expr->begin()->getExpr()->getType()));\n  if (!realize(expr->getType()))\n    return nullptr;\n\n  auto e = N<IdExpr>(expr->getType()->realizedName());\n  e->setType(expr->getType()->shared_from_this());\n  e->setDone();\n  return e;\n}\n\n/// Transform static.realized function to a fully realized type identifier.\nExpr *TypecheckVisitor::transformRealizedFn(CallExpr *expr) {\n  auto fn = extractType((*expr)[0].getExpr()->getType())->shared_from_this();\n  auto pt = (*expr)[0].getExpr()->getType()->getPartial();\n  if (!fn->getFunc() && pt && pt->isPartialEmpty()) {\n    auto pft = pt->getPartialFunc()->generalize(0);\n    fn = instantiateType(pft.get());\n  }\n  if (!fn->getFunc())\n    E(Error::CALL_REALIZED_FN, (*expr)[0].getExpr());\n  auto argt = (*expr)[1].getExpr()->getType()->getClass();\n  if (!argt)\n    return nullptr;\n  seqassert(argt->name == TYPE_TUPLE, \"not a tuple\");\n  for (size_t i = 0; i < std::min(argt->size(), fn->getFunc()->size()); i++) {\n    auto at = (*argt)[i]->is(\"TypeWrap\") ? extractClassGeneric((*argt)[i]) : (*argt)[i];\n    unify((*fn->getFunc())[i], at);\n  }\n  if (auto f = realize(fn.get())) {\n    auto e = N<IdExpr>(f->getFunc()->realizedName());\n    e->setType(f->shared_from_this());\n    e->setDone();\n    return e;\n  }\n  return nullptr;\n}\n\n/// Transform __static_print__ function to a fully realized type identifier.\nExpr *TypecheckVisitor::transformStaticPrintFn(CallExpr *expr) const {\n  for (auto &a : *cast<CallExpr>(expr->begin()->getExpr())) {\n    fmt::print(stderr, \"[print] {}: {} ({}){}\\n\", getSrcInfo(),\n               a.getExpr()->getType() ? a.getExpr()->getType()->debugString(2) : \"-\",\n               a.getExpr()->getType() ? a.getExpr()->getType()->realizedName() : \"-\",\n               a.getExpr()->getType()->getStatic() ? \" [static]\" : \"\");\n  }\n  return nullptr;\n}\n\n/// Transform static.has_rtti to a static boolean that indicates RTTI status of a type.\nExpr *TypecheckVisitor::transformHasRttiFn(const CallExpr *expr) {\n  if (auto u = expr->getType()->getUnbound())\n    u->staticKind = LiteralKind::Bool;\n\n  auto t = extractFuncGeneric(expr->getExpr()->getType())->getClass();\n  if (!t)\n    return nullptr;\n  return transform(N<BoolExpr>(getClass(t)->hasRTTI()));\n}\n\n// Transform internal.static calls\nExpr *TypecheckVisitor::transformStaticFnCanCall(CallExpr *expr) {\n  if (auto u = expr->getType()->getUnbound())\n    u->staticKind = LiteralKind::Bool;\n\n  auto typ = extractClassType((*expr)[0].getExpr());\n  if (!typ)\n    return nullptr;\n\n  auto inargs = unpackTupleTypes((*expr)[1].getExpr());\n  auto kwargs = unpackTupleTypes((*expr)[2].getExpr());\n  seqassert(inargs && kwargs, \"bad call to fn_can_call\");\n\n  std::vector<CallArg> callArgs;\n  for (auto &[v, t] : *inargs) {\n    callArgs.emplace_back(v, N<NoneExpr>()); // dummy expression\n    callArgs.back().getExpr()->setType(t->shared_from_this());\n  }\n  for (auto &[v, t] : *kwargs) {\n    callArgs.emplace_back(v, N<NoneExpr>()); // dummy expression\n    callArgs.back().getExpr()->setType(t->shared_from_this());\n  }\n  if (auto fn = typ->getFunc()) {\n    return transform(N<BoolExpr>(canCall(fn, callArgs) >= 0));\n  } else if (auto pt = typ->getPartial()) {\n    return transform(N<BoolExpr>(canCall(pt->getPartialFunc(), callArgs, pt) >= 0));\n  } else {\n    compilationWarning(\"cannot use fn_can_call on non-functions\", getSrcInfo().file,\n                       getSrcInfo().line, getSrcInfo().col);\n    return transform(N<BoolExpr>(false));\n  }\n}\n\nExpr *TypecheckVisitor::transformStaticFnArgHasType(CallExpr *expr) {\n  if (auto u = expr->getType()->getUnbound())\n    u->staticKind = LiteralKind::Bool;\n\n  auto fn = extractFunction(expr->begin()->getExpr()->getType());\n  if (!fn)\n    E(Error::CUSTOM, getSrcInfo(), \"expected a function, got '{}'\",\n      expr->begin()->getExpr()->getType()->prettyString());\n  auto idx = extractFuncGeneric(expr->getExpr()->getType())->getIntStatic();\n  seqassert(idx, \"expected a static integer\");\n  return transform(N<BoolExpr>(idx->value >= 0 && idx->value < fn->size() &&\n                               (*fn)[idx->value]->canRealize()));\n}\n\nExpr *TypecheckVisitor::transformStaticFnArgGetType(CallExpr *expr) {\n  auto fn = extractFunction(expr->begin()->getExpr()->getType());\n  if (!fn)\n    E(Error::CUSTOM, getSrcInfo(), \"expected a function, got '{}'\",\n      expr->begin()->getExpr()->getType()->prettyString());\n  auto idx = extractFuncGeneric(expr->getExpr()->getType())->getIntStatic();\n  seqassert(idx, \"expected a static integer\");\n  if (idx->value < 0 || idx->value >= fn->size() || !(*fn)[idx->value]->canRealize())\n    E(Error::CUSTOM, getSrcInfo(), \"argument does not have type\");\n  return transform(N<IdExpr>((*fn)[idx->value]->realizedName()));\n}\n\nExpr *TypecheckVisitor::transformStaticFnArgs(CallExpr *expr) {\n  auto fn = extractFunction(expr->begin()->value->getType());\n  if (!fn)\n    E(Error::CUSTOM, getSrcInfo(), \"expected a function, got '{}'\",\n      expr->begin()->getExpr()->getType()->prettyString());\n  std::vector<Expr *> v;\n  v.reserve(fn->ast->size());\n  for (const auto &a : *fn->ast) {\n    auto [_, n] = a.getNameWithStars();\n    n = getUnmangledName(n);\n    v.push_back(N<StringExpr>(n));\n  }\n  return transform(N<TupleExpr>(v));\n}\n\nExpr *TypecheckVisitor::transformStaticFnHasDefault(CallExpr *expr) {\n  if (auto u = expr->getType()->getUnbound())\n    u->staticKind = LiteralKind::Bool;\n\n  auto fn = extractFunction(expr->begin()->getExpr()->getType());\n  if (!fn)\n    E(Error::CUSTOM, getSrcInfo(), \"expected a function, got '{}'\",\n      expr->begin()->getExpr()->getType()->prettyString());\n  auto idx = extractFuncGeneric(expr->getExpr()->getType())->getIntStatic();\n  seqassert(idx, \"expected a static integer\");\n  if (idx->value < 0 || idx->value >= fn->ast->size())\n    E(Error::CUSTOM, getSrcInfo(), \"argument out of bounds\");\n  return transform(N<BoolExpr>((*fn->ast)[idx->value].getDefault() != nullptr));\n}\n\nExpr *TypecheckVisitor::transformStaticFnGetDefault(CallExpr *expr) {\n  auto fn = extractFunction(expr->begin()->getExpr()->getType());\n  if (!fn)\n    E(Error::CUSTOM, getSrcInfo(), \"expected a function, got '{}'\",\n      expr->begin()->getExpr()->getType()->prettyString());\n  auto idx = extractFuncGeneric(expr->getExpr()->getType())->getIntStatic();\n  seqassert(idx, \"expected a static integer\");\n  if (idx->value < 0 || idx->value >= fn->ast->size())\n    E(Error::CUSTOM, getSrcInfo(), \"argument out of bounds\");\n  return transform((*fn->ast)[idx->value].getDefault());\n}\n\nExpr *TypecheckVisitor::transformStaticFnWrapCallArgs(CallExpr *expr) {\n  auto typ = expr->begin()->getExpr()->getClassType();\n  if (!typ)\n    return nullptr;\n\n  auto fn = extractFunction(expr->begin()->getExpr()->getType());\n  if (!fn)\n    E(Error::CUSTOM, getSrcInfo(), \"expected a function, got '{}'\",\n      expr->begin()->getExpr()->getType()->prettyString());\n\n  std::vector<CallArg> callArgs;\n  if (auto tup = cast<TupleExpr>((*expr)[1].getExpr()->getOrigExpr())) {\n    for (auto *a : *tup) {\n      callArgs.emplace_back(\"\", a);\n    }\n  }\n  if (auto kw = cast<CallExpr>((*expr)[1].getExpr()->getOrigExpr())) {\n    auto kwCls = getClass(expr->getClassType());\n    seqassert(kwCls, \"cannot find {}\", expr->getClassType()->name);\n    for (size_t i = 0; i < kw->size(); i++) {\n      callArgs.emplace_back(kwCls->fields[i].name, (*kw)[i].getExpr());\n    }\n  }\n  auto tempCall = transform(N<CallExpr>(N<IdExpr>(fn->getFuncName()), callArgs));\n  if (!tempCall->isDone())\n    return nullptr;\n\n  std::vector<Expr *> tupArgs;\n  for (auto &a : *cast<CallExpr>(tempCall))\n    tupArgs.push_back(a.getExpr());\n  return transform(N<TupleExpr>(tupArgs));\n}\n\nExpr *TypecheckVisitor::transformStaticVars(CallExpr *expr) {\n  auto t = extractFuncGeneric(expr->getExpr()->getType());\n  if (!t || !t->getClass())\n    return nullptr;\n  auto withIdx = getBoolLiteral(t);\n\n  types::ClassType *typ = nullptr;\n  std::vector<Expr *> tupleItems;\n  auto e = transform(expr->begin()->getExpr());\n  if (!((typ = e->getClassType())))\n    return nullptr;\n\n  size_t idx = 0;\n  for (auto &f : getClassFields(typ)) {\n    auto k = N<StringExpr>(f.name);\n    auto v = N<DotExpr>(expr->begin()->value, f.name);\n    if (withIdx) {\n      auto i = N<IntExpr>(idx);\n      tupleItems.push_back(N<TupleExpr>(std::vector<Expr *>{i, k, v}));\n    } else {\n      tupleItems.push_back(N<TupleExpr>(std::vector<Expr *>{k, v}));\n    }\n    idx++;\n  }\n  return transform(N<TupleExpr>(tupleItems));\n}\n\nExpr *TypecheckVisitor::transformStaticTupleType(const CallExpr *expr) {\n  auto funcTyp = expr->getExpr()->getType()->getFunc();\n  auto t = extractFuncGeneric(funcTyp)->getClass();\n  if (!t || !realize(t))\n    return nullptr;\n  auto n = getIntLiteral(extractFuncGeneric(funcTyp, 1));\n  types::TypePtr typ = nullptr;\n  auto f = getClassFields(t);\n  if (n < 0 || n >= f.size())\n    E(Error::CUSTOM, getSrcInfo(), \"invalid index\");\n  auto rt = realize(instantiateType(f[n].getType(), t));\n  return transform(N<IdExpr>(rt->realizedName()));\n}\n\n/// Transform staticlen method to a static integer expression. This method supports only\n/// static strings and tuple types.\nExpr *TypecheckVisitor::transformStaticFormat(CallExpr *expr) {\n  if (auto u = expr->getType()->getUnbound())\n    u->staticKind = LiteralKind::String;\n\n  auto funcTyp = expr->getExpr()->getType()->getFunc();\n  auto fmt = getStrLiteral(extractFuncGeneric(funcTyp, 0));\n  auto arg = getStrLiteral(extractFuncGeneric(funcTyp, 1));\n  size_t start = 0;\n  fmt::dynamic_format_arg_store<fmt::format_context> store;\n  while ((start = fmt.find(\"%%\", start)) != std::string::npos) {\n    fmt.replace(start, 2, \"{}\");\n    store.push_back(arg);\n    start += 2;\n  }\n  return transform(N<StringExpr>(fmt::vformat(fmt, store)));\n}\n\n/// Transform staticlen method to a static integer expression. This method supports only\n/// static strings and tuple types.\nExpr *TypecheckVisitor::transformStaticIntToStr(CallExpr *expr) {\n  if (auto u = expr->getType()->getUnbound())\n    u->staticKind = LiteralKind::String;\n\n  auto funcTyp = expr->getExpr()->getType()->getFunc();\n  auto val = getIntLiteral(extractFuncGeneric(funcTyp, 0));\n  return transform(N<StringExpr>(std::to_string(val)));\n}\n\nstd::vector<Stmt *>\nTypecheckVisitor::populateStaticTupleLoop(Expr *iter,\n                                          const std::vector<std::string> &vars) {\n  std::vector<Stmt *> block;\n  auto stmt = N<AssignStmt>(N<IdExpr>(vars[0]), nullptr, nullptr);\n  auto call = cast<CallExpr>(cast<CallExpr>(iter)->front());\n  if (vars.size() != 1)\n    E(Error::CUSTOM, getSrcInfo(), \"expected one item\");\n  for (auto &a : *call) {\n    stmt->rhs = transform(clean_clone(a.value));\n    if (auto st = stmt->rhs->getType()->getStatic()) {\n      stmt->type = N<IndexExpr>(N<IdExpr>(\"Literal\"), N<IdExpr>(st->name));\n    } else {\n      stmt->type = nullptr;\n    }\n    block.push_back(clone(stmt));\n  }\n  return block;\n}\n\nstd::vector<Stmt *>\nTypecheckVisitor::populateSimpleStaticRangeLoop(Expr *iter,\n                                                const std::vector<std::string> &vars) {\n  if (vars.size() != 1)\n    E(Error::CUSTOM, getSrcInfo(), \"expected one item\");\n  auto fn =\n      cast<CallExpr>(iter) ? cast<IdExpr>(cast<CallExpr>(iter)->getExpr()) : nullptr;\n  auto stmt = N<AssignStmt>(N<IdExpr>(vars[0]), nullptr, nullptr);\n  std::vector<Stmt *> block;\n  auto ed = getIntLiteral(extractFuncGeneric(fn->getType()));\n  if (ed > MAX_STATIC_ITER)\n    E(Error::STATIC_RANGE_BOUNDS, fn, MAX_STATIC_ITER, ed);\n  for (int64_t i = 0; i < ed; i++) {\n    stmt->rhs = N<IntExpr>(i);\n    stmt->type = N<IndexExpr>(N<IdExpr>(\"Literal\"), N<IdExpr>(\"int\"));\n    block.push_back(clone(stmt));\n  }\n  return block;\n}\n\nstd::vector<Stmt *>\nTypecheckVisitor::populateStaticRangeLoop(Expr *iter,\n                                          const std::vector<std::string> &vars) {\n  if (vars.size() != 1)\n    E(Error::CUSTOM, getSrcInfo(), \"expected one item\");\n  auto fn =\n      cast<CallExpr>(iter) ? cast<IdExpr>(cast<CallExpr>(iter)->getExpr()) : nullptr;\n  auto stmt = N<AssignStmt>(N<IdExpr>(vars[0]), nullptr, nullptr);\n  std::vector<Stmt *> block;\n  auto st = getIntLiteral(extractFuncGeneric(fn->getType(), 0));\n  auto ed = getIntLiteral(extractFuncGeneric(fn->getType(), 1));\n  auto step = getIntLiteral(extractFuncGeneric(fn->getType(), 2));\n  if (std::abs(st - ed) / std::abs(step) > MAX_STATIC_ITER)\n    E(Error::STATIC_RANGE_BOUNDS, fn, MAX_STATIC_ITER,\n      std::abs(st - ed) / std::abs(step));\n  for (int64_t i = st; step > 0 ? i < ed : i > ed; i += step) {\n    stmt->rhs = N<IntExpr>(i);\n    stmt->type = N<IndexExpr>(N<IdExpr>(\"Literal\"), N<IdExpr>(\"int\"));\n    block.push_back(clone(stmt));\n  }\n  return block;\n}\n\nstd::vector<Stmt *>\nTypecheckVisitor::populateStaticFnOverloadsLoop(Expr *iter,\n                                                const std::vector<std::string> &vars) {\n  if (vars.size() != 1)\n    E(Error::CUSTOM, getSrcInfo(), \"expected one item\");\n  auto fn =\n      cast<CallExpr>(iter) ? cast<IdExpr>(cast<CallExpr>(iter)->getExpr()) : nullptr;\n  auto stmt = N<AssignStmt>(N<IdExpr>(vars[0]), nullptr, nullptr);\n  std::vector<Stmt *> block;\n  auto typ = extractFuncGeneric(fn->getType(), 0)->getClass();\n  seqassert(extractFuncGeneric(fn->getType(), 1)->getStrStatic(), \"bad static string\");\n  auto name = getStrLiteral(extractFuncGeneric(fn->getType(), 1));\n\n  std::vector<std::string> overloads;\n  if (typ->is(\"NoneType\")) {\n    if (auto func = ctx->cache->typeCtx->find(name)) {\n      auto root = getRootName(func->getType()->getFunc());\n      overloads = getOverloads(root);\n    }\n  } else {\n    if (auto n = in(getClass(typ)->methods, name))\n      overloads = getOverloads(*n);\n  }\n  if (!overloads.empty()) {\n    for (int mti = static_cast<int>(overloads.size()) - 1; mti >= 0; mti--) {\n      auto &method = overloads[mti];\n      auto cfn = getFunction(method);\n      if (isDispatch(method) || !cfn->type)\n        continue;\n      if (isHeterogenous(typ)) {\n        if (cfn->ast->hasAttribute(Attr::AutoGenerated) &&\n            (endswith(cfn->ast->name, \".__iter__:0\") ||\n             endswith(cfn->ast->name, \".__getitem__:0\"))) {\n          // ignore __getitem__ and other heterogenuous methods\n          continue;\n        }\n      }\n      stmt->rhs = N<IdExpr>(method);\n      block.push_back(clone(stmt));\n    }\n  }\n  return block;\n}\n\nstd::vector<Stmt *>\nTypecheckVisitor::populateStaticEnumerateLoop(Expr *iter,\n                                              const std::vector<std::string> &vars) {\n  if (vars.size() != 2)\n    E(Error::CUSTOM, getSrcInfo(), \"expected two items\");\n  auto fn =\n      cast<CallExpr>(iter) ? cast<IdExpr>(cast<CallExpr>(iter)->getExpr()) : nullptr;\n  std::vector<Stmt *> block;\n  auto typ = extractFuncArgType(fn->getType())->getClass();\n  if (typ && typ->isRecord()) {\n    for (size_t i = 0; i < getClassFields(typ).size(); i++) {\n      auto b = N<SuiteStmt>(std::vector<Stmt *>{\n          N<AssignStmt>(N<IdExpr>(vars[0]), N<IntExpr>(i),\n                        N<IndexExpr>(N<IdExpr>(\"Literal\"), N<IdExpr>(\"int\"))),\n          N<AssignStmt>(\n              N<IdExpr>(vars[1]),\n              N<IndexExpr>(clone((*cast<CallExpr>(iter))[0].value), N<IntExpr>(i)))});\n      block.push_back(b);\n    }\n  } else {\n    E(Error::CUSTOM, getSrcInfo(), \"static.enumerate needs a tuple\");\n  }\n  return block;\n}\n\nstd::vector<Stmt *>\nTypecheckVisitor::populateStaticVarsLoop(Expr *iter,\n                                         const std::vector<std::string> &vars) {\n  auto fn =\n      cast<CallExpr>(iter) ? cast<IdExpr>(cast<CallExpr>(iter)->getExpr()) : nullptr;\n  bool withIdx = getBoolLiteral(extractFuncGeneric(fn->getType()));\n  if (!withIdx && vars.size() != 2)\n    E(Error::CUSTOM, getSrcInfo(), \"expected two items\");\n  else if (withIdx && vars.size() != 3)\n    E(Error::CUSTOM, getSrcInfo(), \"expected three items\");\n  std::vector<Stmt *> block;\n  auto typ = extractFuncArgType(fn->getType())->getClass();\n  size_t idx = 0;\n  if (typ->is(\"TypeWrap\")) { // type passed!\n    for (auto &f : getClass(extractClassGeneric(typ))->classVars) {\n      std::vector<Stmt *> stmts;\n      if (withIdx) {\n        stmts.push_back(\n            N<AssignStmt>(N<IdExpr>(vars[0]), N<IntExpr>(idx),\n                          N<IndexExpr>(N<IdExpr>(\"Literal\"), N<IdExpr>(\"int\"))));\n      }\n      stmts.push_back(\n          N<AssignStmt>(N<IdExpr>(vars[withIdx]), N<StringExpr>(f.first),\n                        N<IndexExpr>(N<IdExpr>(\"Literal\"), N<IdExpr>(\"str\"))));\n      stmts.push_back(N<AssignStmt>(N<IdExpr>(vars[withIdx + 1]), N<IdExpr>(f.second)));\n      auto b = N<SuiteStmt>(stmts);\n      block.push_back(b);\n      idx++;\n    }\n  } else {\n    for (auto &f : getClassFields(typ)) {\n      std::vector<Stmt *> stmts;\n      if (withIdx) {\n        stmts.push_back(\n            N<AssignStmt>(N<IdExpr>(vars[0]), N<IntExpr>(idx),\n                          N<IndexExpr>(N<IdExpr>(\"Literal\"), N<IdExpr>(\"int\"))));\n      }\n      stmts.push_back(\n          N<AssignStmt>(N<IdExpr>(vars[withIdx]), N<StringExpr>(f.name),\n                        N<IndexExpr>(N<IdExpr>(\"Literal\"), N<IdExpr>(\"str\"))));\n      stmts.push_back(\n          N<AssignStmt>(N<IdExpr>(vars[withIdx + 1]),\n                        N<DotExpr>(clone((*cast<CallExpr>(iter))[0].value), f.name)));\n      auto b = N<SuiteStmt>(stmts);\n      block.push_back(b);\n      idx++;\n    }\n  }\n  return block;\n}\n\nstd::vector<Stmt *>\nTypecheckVisitor::populateStaticVarTypesLoop(Expr *iter,\n                                             const std::vector<std::string> &vars) {\n  auto fn =\n      cast<CallExpr>(iter) ? cast<IdExpr>(cast<CallExpr>(iter)->getExpr()) : nullptr;\n  auto typ = realize(extractFuncGeneric(fn->getType(), 0)->getClass());\n  bool withIdx = getBoolLiteral(extractFuncGeneric(fn->getType(), 1));\n  if (!withIdx && vars.size() != 1)\n    E(Error::CUSTOM, getSrcInfo(), \"expected one item\");\n  else if (withIdx && vars.size() != 2)\n    E(Error::CUSTOM, getSrcInfo(), \"expected two items\");\n\n  seqassert(typ, \"vars_types expects a realizable type, got '{}' instead\",\n            *(extractFuncGeneric(fn->getType(), 0)));\n  std::vector<Stmt *> block;\n  if (auto utyp = typ->getUnion()) {\n    for (size_t i = 0; i < utyp->getRealizationTypes().size(); i++) {\n      std::vector<Stmt *> stmts;\n      if (withIdx) {\n        stmts.push_back(\n            N<AssignStmt>(N<IdExpr>(vars[0]), N<IntExpr>(i),\n                          N<IndexExpr>(N<IdExpr>(\"Literal\"), N<IdExpr>(\"int\"))));\n      }\n      stmts.push_back(\n          N<AssignStmt>(N<IdExpr>(vars[1]),\n                        N<IdExpr>(utyp->getRealizationTypes()[i]->realizedName())));\n      auto b = N<SuiteStmt>(stmts);\n      block.push_back(b);\n    }\n  } else {\n    size_t idx = 0;\n    for (auto &f : getClassFields(typ->getClass())) {\n      auto ta = realize(instantiateType(f.type.get(), typ->getClass()));\n      seqassert(ta, \"cannot realize '{}'\", f.type->debugString(2));\n      std::vector<Stmt *> stmts;\n      if (withIdx) {\n        stmts.push_back(\n            N<AssignStmt>(N<IdExpr>(vars[0]), N<IntExpr>(idx),\n                          N<IndexExpr>(N<IdExpr>(\"Literal\"), N<IdExpr>(\"int\"))));\n      }\n      stmts.push_back(\n          N<AssignStmt>(N<IdExpr>(vars[withIdx]), N<IdExpr>(ta->realizedName())));\n      auto b = N<SuiteStmt>(stmts);\n      block.push_back(b);\n      idx++;\n    }\n  }\n  return block;\n}\n\nstd::vector<Stmt *> TypecheckVisitor::populateStaticHeterogenousTupleLoop(\n    Expr *iter, const std::vector<std::string> &vars) {\n  std::vector<Stmt *> block;\n  std::string tupleVar;\n  Stmt *preamble = nullptr;\n  if (!cast<IdExpr>(iter)) {\n    tupleVar = getTemporaryVar(\"tuple\");\n    preamble = N<AssignStmt>(N<IdExpr>(tupleVar), iter);\n  } else {\n    tupleVar = cast<IdExpr>(iter)->getValue();\n  }\n  for (size_t i = 0; i < iter->getClassType()->generics.size(); i++) {\n    auto s = N<SuiteStmt>();\n    if (vars.size() > 1) {\n      for (size_t j = 0; j < vars.size(); j++) {\n        s->addStmt(\n            N<AssignStmt>(N<IdExpr>(vars[j]),\n                          N<IndexExpr>(N<IndexExpr>(N<IdExpr>(tupleVar), N<IntExpr>(i)),\n                                       N<IntExpr>(j))));\n      }\n    } else {\n      s->addStmt(N<AssignStmt>(N<IdExpr>(vars[0]),\n                               N<IndexExpr>(N<IdExpr>(tupleVar), N<IntExpr>(i))));\n    }\n    block.push_back(s);\n  }\n  block.push_back(preamble);\n  return block;\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/typecheck.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"typecheck.h\"\n\n#include <fmt/format.h>\n#include <memory>\n#include <utility>\n#include <vector>\n\n#include \"codon/cir/pyextension.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/match.h\"\n#include \"codon/parser/peg/peg.h\"\n#include \"codon/parser/visitors/scoping/scoping.h\"\n#include \"codon/parser/visitors/typecheck/ctx.h\"\n\nusing namespace codon::error;\n\nnamespace codon::ast {\n\nusing namespace types;\nusing namespace matcher;\n\n/// Simplify an AST node. Load standard library if needed.\n/// @param cache     Pointer to the shared cache ( @c Cache )\n/// @param file      Filename to be used for error reporting\n/// @param barebones Use the bare-bones standard library for faster testing\n/// @param defines   User-defined static values (typically passed as `codon run -DX=Y`).\n///                  Each value is passed as a string.\nStmt *TypecheckVisitor::apply(\n    Cache *cache, Stmt *node, const std::string &file,\n    const std::unordered_map<std::string, std::string> &defines,\n    const std::unordered_map<std::string, std::string> &earlyDefines, bool barebones) {\n  auto preamble = cache->N<SuiteStmt>();\n  seqassertn(cache->module, \"cache's module is not set\");\n\n  // Load standard library if it has not been loaded\n  if (!in(cache->imports, STDLIB_IMPORT))\n    loadStdLibrary(cache, preamble, earlyDefines, barebones);\n\n  // Set up the context and the cache\n  auto ctx = std::make_shared<TypeContext>(cache, file);\n  cache->imports[file].update(MAIN_IMPORT, file, ctx);\n  cache->imports[MAIN_IMPORT] = cache->imports[file];\n  ctx->setFilename(file);\n  ctx->moduleName = {ImportFile::PACKAGE, file, MODULE_MAIN};\n\n  // Prepare the code\n  auto tv = TypecheckVisitor(ctx, preamble);\n  auto *suite = tv.N<SuiteStmt>();\n  auto &stmts = suite->items;\n  // Load compile-time defines (e.g., codon run -DFOO=1 ...)\n  for (auto &d : defines) {\n    if (startswith(d.second, \"str:\")) {\n      stmts.push_back(tv.N<AssignStmt>(\n          tv.N<IdExpr>(d.first), tv.N<StringExpr>(d.second.substr(4)),\n          tv.N<IndexExpr>(tv.N<IdExpr>(\"Literal\"), tv.N<IdExpr>(\"str\"))));\n    } else if (startswith(d.second, \"bool:\")) {\n      stmts.push_back(tv.N<AssignStmt>(\n          tv.N<IdExpr>(d.first), tv.N<BoolExpr>(d.second == \"bool:True\" ? true : false),\n          tv.N<IndexExpr>(tv.N<IdExpr>(\"Literal\"), tv.N<IdExpr>(\"bool\"))));\n    } else {\n      stmts.push_back(tv.N<AssignStmt>(\n          tv.N<IdExpr>(d.first),\n          tv.N<IntExpr>(startswith(d.second, \"int:\") ? d.second.substr(4) : d.second),\n          tv.N<IndexExpr>(tv.N<IdExpr>(\"Literal\"), tv.N<IdExpr>(\"int\"))));\n    }\n  }\n  // Set up __name__\n  stmts.push_back(\n      tv.N<AssignStmt>(tv.N<IdExpr>(\"__name__\"), tv.N<StringExpr>(MODULE_MAIN)));\n  stmts.push_back(tv.N<AssignStmt>(tv.N<IdExpr>(\"__file__\"), tv.N<StringExpr>(file)));\n  stmts.push_back(node);\n\n  if (auto err = ScopingVisitor::apply(cache, suite, &ctx->globalShadows))\n    throw exc::ParserException(std::move(err));\n  auto n = tv.inferTypes(suite, true);\n  if (!n) {\n    auto errors = tv.findTypecheckErrors(suite);\n    throw exc::ParserException(errors);\n  }\n\n  suite = tv.N<SuiteStmt>();\n  suite->items.push_back(preamble);\n\n  // Add dominated assignment declarations\n  suite->items.insert(suite->items.end(), ctx->scope.back().stmts.begin(),\n                      ctx->scope.back().stmts.end());\n  suite->items.push_back(n);\n\n  if (cast<SuiteStmt>(n))\n    tv.prepareVTables();\n\n  if (!ctx->cache->errors.empty())\n    throw exc::ParserException(ctx->cache->errors);\n\n  return suite;\n}\n\nvoid TypecheckVisitor::loadStdLibrary(\n    Cache *cache, SuiteStmt *preamble,\n    const std::unordered_map<std::string, std::string> &earlyDefines, bool barebones) {\n  // Load the internal.__init__\n  auto stdlib = std::make_shared<TypeContext>(cache, STDLIB_IMPORT);\n  auto stdlibPath = getImportFile(cache, STDLIB_INTERNAL_MODULE, \"\", true);\n  const std::string initFile = \"__init__.codon\";\n  if (!stdlibPath || !endswith(stdlibPath->path, initFile))\n    E(Error::COMPILER_NO_STDLIB);\n\n  /// Use __init_test__ for faster testing (e.g., #%% name,barebones)\n  /// TODO: get rid of it one day...\n  if (barebones) {\n    stdlibPath->path =\n        stdlibPath->path.substr(0, stdlibPath->path.size() - initFile.size()) +\n        \"__init_test__.codon\";\n  }\n  stdlib->setFilename(stdlibPath->path);\n  cache->imports[stdlibPath->path].update(STDLIB_IMPORT, stdlibPath->path, stdlib);\n  cache->imports[STDLIB_IMPORT] = cache->imports[stdlibPath->path];\n\n  // Load the standard library\n  stdlib->isStdlibLoading = true;\n  stdlib->moduleName = {ImportFile::STDLIB, stdlibPath->path, \"__init__\"};\n  stdlib->setFilename(stdlibPath->path);\n\n  // 1. Core definitions\n  cache->classes[VAR_CLASS_TOPLEVEL] = Cache::Class();\n  auto coreOrErr =\n      parseCode(stdlib->cache, stdlibPath->path, \"from internal.core import *\");\n  if (!coreOrErr)\n    throw exc::ParserException(coreOrErr.takeError());\n  auto *core = *coreOrErr;\n  if (auto err = ScopingVisitor::apply(stdlib->cache, core))\n    throw exc::ParserException(std::move(err));\n  auto tv = TypecheckVisitor(stdlib, preamble);\n  core = tv.inferTypes(core, true);\n  preamble->addStmt(core);\n\n  // 2. Load early compile-time defines (for standard library)\n  for (auto &d : earlyDefines) {\n    AssignStmt *s = nullptr;\n    if (startswith(d.second, \"str:\")) {\n      s = tv.N<AssignStmt>(\n          tv.N<IdExpr>(d.first), tv.N<StringExpr>(d.second.substr(4)),\n          tv.N<IndexExpr>(tv.N<IdExpr>(\"Literal\"), tv.N<IdExpr>(\"str\")));\n    } else if (startswith(d.second, \"bool:\")) {\n      s = tv.N<AssignStmt>(\n          tv.N<IdExpr>(d.first), tv.N<BoolExpr>(d.second == \"bool:True\" ? true : false),\n          tv.N<IndexExpr>(tv.N<IdExpr>(\"Literal\"), tv.N<IdExpr>(\"bool\")));\n    } else {\n      s = tv.N<AssignStmt>(\n          tv.N<IdExpr>(d.first),\n          tv.N<IntExpr>(startswith(d.second, \"int:\") ? d.second.substr(4) : d.second),\n          tv.N<IndexExpr>(tv.N<IdExpr>(\"Literal\"), tv.N<IdExpr>(\"int\")));\n    }\n    auto def = tv.transform(s);\n    preamble->addStmt(def);\n  }\n\n  // 3. Load stdlib\n  auto stdOrErr = parseFile(stdlib->cache, stdlibPath->path);\n  if (!stdOrErr)\n    throw exc::ParserException(stdOrErr.takeError());\n  auto std = *stdOrErr;\n  if (auto err = ScopingVisitor::apply(stdlib->cache, std, &stdlib->globalShadows))\n    throw exc::ParserException(std::move(err));\n  tv = TypecheckVisitor(stdlib, preamble);\n  std = tv.inferTypes(std, true);\n  preamble->addStmt(std);\n  stdlib->isStdlibLoading = false;\n}\n\n/// Simplify an AST node. Assumes that the standard library is loaded.\nStmt *TypecheckVisitor::apply(const std::shared_ptr<TypeContext> &ctx, Stmt *node,\n                              const std::string &file) {\n  auto oldFilename = ctx->getFilename();\n  ctx->setFilename(file);\n  auto preamble = ctx->cache->N<ast::SuiteStmt>();\n  auto tv = TypecheckVisitor(ctx, preamble);\n  auto n = tv.inferTypes(node, true);\n  ctx->setFilename(oldFilename);\n  if (!n) {\n    auto errors = tv.findTypecheckErrors(node);\n    throw exc::ParserException(errors);\n  }\n  if (!ctx->cache->errors.empty())\n    throw exc::ParserException(ctx->cache->errors);\n\n  auto suite = ctx->cache->N<SuiteStmt>(preamble);\n  suite->addStmt(n);\n  return suite;\n}\n\n/**************************************************************************************/\n\nTypecheckVisitor::TypecheckVisitor(std::shared_ptr<TypeContext> ctx, SuiteStmt *pre,\n                                   const std::shared_ptr<std::vector<Stmt *>> &stmts)\n    : ctx(std::move(ctx)), resultExpr(nullptr), resultStmt(nullptr) {\n  preamble = pre ? pre : this->ctx->cache->N<SuiteStmt>();\n  prependStmts = stmts ? stmts : std::make_shared<std::vector<Stmt *>>();\n}\n\n/**************************************************************************************/\n\nExpr *TypecheckVisitor::transform(Expr *expr) { return transform(expr, true); }\n\n/// Transform an expression node.\nExpr *TypecheckVisitor::transform(Expr *expr, bool allowTypes) {\n  if (!expr)\n    return nullptr;\n\n  if (!expr->getType())\n    expr->setType(instantiateUnbound());\n\n  if (!expr->isDone()) {\n    TypecheckVisitor v(ctx, preamble, prependStmts);\n    v.setSrcInfo(expr->getSrcInfo());\n    ctx->pushNode(expr);\n    expr->accept(v);\n    ctx->popNode();\n    if (v.resultExpr) {\n      for (auto it = expr->attributes_begin(); it != expr->attributes_end(); ++it) {\n        const auto *attr = expr->getAttribute(*it);\n        if (!v.resultExpr->hasAttribute(*it))\n          v.resultExpr->setAttribute(*it, attr->clone());\n      }\n      v.resultExpr->setOrigExpr(expr->getOrigExpr() ? expr->getOrigExpr() : expr);\n      expr = v.resultExpr;\n      if (!expr->getType())\n        expr->setType(instantiateUnbound());\n    }\n    if (!allowTypes && expr && isTypeExpr(expr))\n      E(Error::UNEXPECTED_TYPE, expr, \"type\");\n    if (expr->isDone())\n      ctx->changedNodes++;\n  }\n  if (expr) {\n    if (!expr->hasAttribute(Attr::ExprDoNotRealize)) {\n      if (auto p = realize(expr->getType())) {\n        unify(expr->getType(), p);\n      }\n    }\n    LOG_TYPECHECK(\"[expr] {}: {}{}\", getSrcInfo(), *(expr),\n                  expr->isDone() ? \"[done]\" : \"\");\n  }\n  return expr;\n}\n\n/// Transform a type expression node.\n/// Special case: replace `None` with `NoneType`\n/// @throw @c ParserException if a node is not a type (use @c transform instead).\nExpr *TypecheckVisitor::transformType(Expr *expr, bool simple) {\n  if (cast<NoneExpr>(expr)) {\n    auto ne = N<IdExpr>(\"NoneType\");\n    ne->setSrcInfo(expr->getSrcInfo());\n    expr = ne;\n  }\n  if (simple != ctx->simpleTypes)\n    std::swap(ctx->simpleTypes, simple);\n  expr = transform(expr);\n  if (simple != ctx->simpleTypes)\n    std::swap(ctx->simpleTypes, simple);\n  if (expr) {\n    if (expr->getType()->getStaticKind()) {\n      ;\n    } else if (isTypeExpr(expr)) {\n      expr->setType(instantiateType(expr->getType()));\n    } else if (expr->getType()->getUnbound() &&\n               !expr->getType()->getUnbound()->genericName.empty()) {\n      // generic!\n      expr->setType(instantiateType(expr->getType()));\n    } else if (expr->getType()->getUnbound() && expr->getType()->getUnbound()->trait) {\n      // generic (is type)!\n      expr->setType(instantiateType(expr->getType()));\n    } else {\n      E(Error::EXPECTED_TYPE, expr, \"type\");\n    }\n  }\n  return expr;\n}\n\nvoid TypecheckVisitor::defaultVisit(Expr *e) {\n  seqassert(false, \"unexpected AST node {}\", e->toString());\n}\n\n/// Transform a statement node.\nStmt *TypecheckVisitor::transform(Stmt *stmt) {\n  if (!stmt || stmt->isDone())\n    return stmt;\n\n  TypecheckVisitor v(ctx, preamble);\n  v.setSrcInfo(stmt->getSrcInfo());\n  ctx->pushNode(stmt);\n\n  int64_t time = 0;\n  if (auto a = stmt->getAttribute<ir::IntValueAttribute>(Attr::ExprTime))\n    time = a->value;\n  auto oldTime = ctx->time;\n  ctx->time = time;\n  stmt->accept(v);\n  ctx->time = oldTime;\n  ctx->popNode();\n  if (v.resultStmt)\n    stmt = v.resultStmt;\n  if (!v.prependStmts->empty()) {\n    if (stmt)\n      v.prependStmts->push_back(stmt);\n    bool done = true;\n    for (auto &s : *(v.prependStmts))\n      done &= s->isDone();\n    stmt = N<SuiteStmt>(*v.prependStmts);\n    if (done)\n      stmt->setDone();\n  }\n  if (stmt->isDone())\n    ctx->changedNodes++;\n  return stmt;\n}\n\nvoid TypecheckVisitor::defaultVisit(Stmt *s) {\n  seqassert(false, \"unexpected AST node {}\", s->toString());\n}\n\n/**************************************************************************************/\n\n/// Typecheck statement expressions.\nvoid TypecheckVisitor::visit(StmtExpr *expr) {\n  auto done = true;\n  for (auto &s : *expr) {\n    s = transform(s);\n    done &= s->isDone();\n  }\n  expr->expr = transform(expr->getExpr());\n  unify(expr->getType(), expr->getExpr()->getType());\n  if (done && expr->getExpr()->isDone())\n    expr->setDone();\n}\n\n/// Typecheck a list of statements.\nvoid TypecheckVisitor::visit(SuiteStmt *stmt) {\n  std::vector<Stmt *> stmts; // for filtering out nullptr statements\n  auto done = true;\n\n  std::vector<Stmt *> prepend;\n  if (auto b = stmt->getAttribute<BindingsAttribute>(Attr::Bindings)) {\n    for (auto &[n, bd] : b->bindings) {\n      prepend.push_back(N<AssignStmt>(N<IdExpr>(n), nullptr));\n      if (bd.count > 0)\n        prepend.push_back(N<AssignStmt>(\n            N<IdExpr>(fmt::format(\"{}{}\", n, VAR_USED_SUFFIX)), N<BoolExpr>(false)));\n    }\n    stmt->eraseAttribute(Attr::Bindings);\n  }\n  if (!prepend.empty())\n    stmt->items.insert(stmt->items.begin(), prepend.begin(), prepend.end());\n  for (auto *s : *stmt) {\n    if (ctx->returnEarly) {\n      // If returnEarly is set (e.g., in the function) ignore the rest\n      break;\n    }\n    if ((s = transform(s))) {\n      if (!cast<SuiteStmt>(s)) {\n        done &= s->isDone();\n        stmts.push_back(s);\n      } else {\n        for (auto *ss : *cast<SuiteStmt>(s)) {\n          if (ss) {\n            done &= ss->isDone();\n            stmts.push_back(ss);\n          }\n        }\n      }\n    }\n  }\n  stmt->items = stmts;\n  if (done)\n    stmt->setDone();\n}\n\n/// Typecheck expression statements.\nvoid TypecheckVisitor::visit(ExprStmt *stmt) {\n  stmt->expr = transform(stmt->getExpr());\n  if (stmt->getExpr()->isDone())\n    stmt->setDone();\n}\n\nvoid TypecheckVisitor::visit(CustomStmt *stmt) {\n  if (stmt->getSuite()) {\n    auto fn = in(ctx->cache->customBlockStmts, stmt->getKeyword());\n    seqassert(fn, \"unknown keyword {}\", stmt->getKeyword());\n    resultStmt = fn->second(this, stmt);\n  } else {\n    auto fn = in(ctx->cache->customExprStmts, stmt->getKeyword());\n    seqassert(fn, \"unknown keyword {}\", stmt->getKeyword());\n    resultStmt = (*fn)(this, stmt);\n  }\n}\n\nvoid TypecheckVisitor::visit(CommentStmt *stmt) { stmt->setDone(); }\n\nvoid TypecheckVisitor::visit(DirectiveStmt *stmt) {\n  if (stmt->getKey() == \"auto_python\") {\n    ctx->autoPython = stmt->getValue() == \"1\";\n    compilationWarning(\n        fmt::format(\"directive '{}' = {}\", stmt->getKey(), ctx->autoPython),\n        stmt->getSrcInfo().file, stmt->getSrcInfo().line, stmt->getSrcInfo().col);\n  } else {\n    compilationWarning(fmt::format(\"unknown directive '{}'\", stmt->getKey()),\n                       stmt->getSrcInfo().file, stmt->getSrcInfo().line,\n                       stmt->getSrcInfo().col);\n  }\n  stmt->setDone();\n}\n\n/**************************************************************************************/\n\n/// Select the best method indicated of an object that matches the given argument\n/// types. See @c findMatchingMethods for details.\ntypes::FuncType *\nTypecheckVisitor::findBestMethod(ClassType *typ, const std::string &member,\n                                 const std::vector<types::Type *> &args) {\n  std::vector<CallArg> callArgs;\n  for (auto &a : args) {\n    callArgs.emplace_back(\"\", N<NoneExpr>()); // dummy expression\n    callArgs.back().value->setType(a->shared_from_this());\n  }\n  auto methods = findMethod(typ, member, false);\n  auto m = findMatchingMethods(typ, methods, callArgs);\n  return m.empty() ? nullptr : m[0];\n}\n\n/// Select the best method indicated of an object that matches the given argument\n/// types. See @c findMatchingMethods for details.\ntypes::FuncType *TypecheckVisitor::findBestMethod(ClassType *typ,\n                                                  const std::string &member,\n                                                  const std::vector<Expr *> &args) {\n  std::vector<CallArg> callArgs;\n  for (auto &a : args)\n    callArgs.emplace_back(\"\", a);\n  auto methods = findMethod(typ, member, false);\n  auto m = findMatchingMethods(typ, methods, callArgs);\n  return m.empty() ? nullptr : m[0];\n}\n\n/// Select the best method indicated of an object that matches the given argument\n/// types. See @c findMatchingMethods for details.\ntypes::FuncType *TypecheckVisitor::findBestMethod(\n    ClassType *typ, const std::string &member,\n    const std::vector<std::pair<std::string, types::Type *>> &args) {\n  std::vector<CallArg> callArgs;\n  for (auto &[n, a] : args) {\n    callArgs.emplace_back(n, N<NoneExpr>()); // dummy expression\n    callArgs.back().value->setType(a->shared_from_this());\n  }\n  auto methods = findMethod(typ, member, false);\n  auto m = findMatchingMethods(typ, methods, callArgs);\n  return m.empty() ? nullptr : m[0];\n}\n\n/// Check if a function can be called with the given arguments.\n/// See @c reorderNamedArgs for details.\nint TypecheckVisitor::canCall(types::FuncType *fn, const std::vector<CallArg> &args,\n                              types::ClassType *part) {\n  std::vector<types::Type *> partialArgs;\n  if (part && part->getPartial()) {\n    auto known = part->getPartialMask();\n    auto knownArgTypes = extractClassGeneric(part, 1)->getClass();\n    for (size_t i = 0, k = 0; i < known.size(); i++)\n      if (known[i] == ClassType::PartialFlag::Included) {\n        partialArgs.push_back(extractClassGeneric(knownArgTypes, static_cast<int>(k)));\n        k++;\n      } else if (known[i] == ClassType::PartialFlag::Default) {\n        k++;\n      }\n  }\n\n  std::vector<std::pair<types::Type *, size_t>> reordered;\n  auto niGenerics = fn->ast->getNonInferrableGenerics();\n  auto score = reorderNamedArgs(\n      fn, args,\n      [&](int s, int k, const std::vector<std::vector<int>> &slots, bool _) {\n        for (int si = 0, gi = 0, pi = 0; si < slots.size(); si++) {\n          if ((*fn->ast)[si].isGeneric()) {\n            if (slots[si].empty()) {\n              // is this \"real\" type?\n              if (in(niGenerics, (*fn->ast)[si].getName()) &&\n                  !(*fn->ast)[si].getDefault())\n                return -1;\n              reordered.emplace_back(nullptr, 0);\n            } else {\n              seqassert(gi < fn->funcGenerics.size(), \"bad fn\");\n              if (!extractFuncGeneric(fn, gi)->getStaticKind() &&\n                  !isTypeExpr(args[slots[si][0]]))\n                return -1;\n              reordered.emplace_back(args[slots[si][0]].getExpr()->getType(),\n                                     slots[si][0]);\n            }\n            gi++;\n          } else if (si == s || si == k || slots[si].size() != 1) {\n            // Partials\n            if (slots[si].empty() && part && part->getPartial() &&\n                part->getPartialMask()[si] == ClassType::PartialFlag::Included) {\n              reordered.emplace_back(partialArgs[pi++], 0);\n            } else {\n              // Ignore *args, *kwargs and default arguments\n              reordered.emplace_back(nullptr, 0);\n            }\n          } else {\n            reordered.emplace_back(args[slots[si][0]].getExpr()->getType(),\n                                   slots[si][0]);\n          }\n        }\n        return 0;\n      },\n      [](error::Error, const SrcInfo &, const std::string &) { return -1; },\n      part && part->getPartial() ? part->getPartialMask() : \"\");\n  int ai = 0, mai = 0, gi = 0, real_gi = 0;\n  for (; score != -1 && ai < reordered.size(); ai++) {\n    auto expectTyp = (*fn->ast)[ai].isValue() ? extractFuncArgType(fn, mai++)\n                                              : extractFuncGeneric(fn, gi++);\n    auto [argType, argTypeIdx] = reordered[ai];\n    if (!argType)\n      continue;\n    real_gi += !(*fn->ast)[ai].isValue();\n    if (!(*fn->ast)[ai].isValue()) {\n      // Check if this is a good generic!\n      if (expectTyp && expectTyp->getStaticKind()) {\n        if (!args[argTypeIdx].getExpr()->getType()->getStaticKind()) {\n          score = -1;\n          break;\n        } else {\n          argType = args[argTypeIdx].getExpr()->getType();\n        }\n      } else {\n        /// TODO: check if these are real types or if traits are satisfied\n        continue;\n      }\n    }\n\n    auto [_, newArgTyp, _ignore] = canWrapExpr(argType, expectTyp, fn);\n    if (!newArgTyp)\n      newArgTyp = argType->shared_from_this();\n    if (newArgTyp->unify(expectTyp, nullptr) < 0)\n      score = -1;\n  }\n  if (score >= 0)\n    score += (real_gi == fn->funcGenerics.size());\n  return score;\n}\n\n/// Select the best method among the provided methods given the list of arguments.\n/// See @c reorderNamedArgs for details.\nstd::vector<types::FuncType *> TypecheckVisitor::findMatchingMethods(\n    types::ClassType *typ, const std::vector<types::FuncType *> &methods,\n    const std::vector<CallArg> &args, types::ClassType *part) {\n  // Pick the last method that accepts the given arguments.\n  std::vector<types::FuncType *> results;\n  for (const auto &mi : methods) {\n    if (!mi)\n      continue; // avoid overloads that have not been seen yet\n    auto method = instantiateType(mi, typ);\n    int score = canCall(method->getFunc(), args, part);\n    if (score != -1) {\n      results.push_back(mi);\n    }\n  }\n  return results;\n}\n\n/// Wrap an expression to coerce it to the expected type if the type of the expression\n/// does not match it. Also unify types.\n/// @example\n///   expected `Generator`                -> `expr.__iter__()`\n///   expected `float`, got `int`         -> `float(expr)`\n///   expected `Optional[T]`, got `T`     -> `Optional(expr)`\n///   expected `T`, got `Optional[T]`     -> `unwrap(expr)`\n///   expected `Function`, got a function -> partialize function\n///   expected `T`, got `Union[T...]`     -> `Union._get(expr, T)`\n///   expected `Union[T...]`, got `T`     -> `Union._new(expr, Union[T...])`\n///   expected base class, got derived    -> downcast to base class\n/// @param allowUnwrap allow optional unwrapping.\nbool TypecheckVisitor::wrapExpr(Expr **expr, Type *expectedType, FuncType *callee,\n                                bool allowUnwrap) {\n  auto [canWrap, newArgTyp, fn] = canWrapExpr((*expr)->getType(), expectedType, callee,\n                                              allowUnwrap, cast<EllipsisExpr>(*expr));\n  // TODO: get rid of this line one day!\n  if ((*expr)->getType()->getStaticKind() &&\n      (!expectedType || !expectedType->getStaticKind()))\n    (*expr)->setType(getUnderlyingStaticType((*expr)->getType())->shared_from_this());\n  if (canWrap && fn) {\n    *expr = transform(fn(*expr));\n  }\n  return canWrap;\n}\n\nstd::tuple<bool, TypePtr, std::function<Expr *(Expr *)>>\nTypecheckVisitor::canWrapExpr(Type *exprType, Type *expectedType, FuncType *callee,\n                              bool allowUnwrap, bool isEllipsis) {\n  auto expectedClass = expectedType->getClass();\n  auto exprClass = exprType->getClass();\n  auto doArgWrap = !callee || !callee->ast->hasFunctionAttribute(getMangledFunc(\n                                  \"std.internal.attributes\", \"no_argument_wrap\"));\n  if (!doArgWrap)\n    return {true, expectedType ? expectedType->shared_from_this() : nullptr, nullptr};\n\n  TypePtr type = nullptr;\n  std::function<Expr *(Expr *)> fn = nullptr;\n\n  if (callee && exprType->is(TYPE_TYPE)) {\n    auto c = extractClassType(exprType);\n    if (!c)\n      return {false, nullptr, nullptr};\n    if (!(expectedType && (expectedType->is(TYPE_TYPE)))) {\n      type = instantiateType(getStdLibType(\"TypeWrap\"), std::vector<types::Type *>{c});\n      fn = [&](Expr *expr) -> Expr * {\n        return N<CallExpr>(N<IdExpr>(\"TypeWrap\"), expr);\n      };\n    }\n    return {true, type, fn};\n  }\n\n  std::unordered_set<std::string> hints = {\"Generator\", \"float\", TYPE_OPTIONAL,\n                                           \"pyobj\"};\n  if (!expectedType || !expectedType->getStaticKind()) {\n    if (exprType->getStaticKind()) {\n      exprType = getUnderlyingStaticType(exprType);\n      exprClass = exprType->getClass();\n      type = exprType->shared_from_this();\n    }\n  }\n\n  if (!exprClass && expectedClass && in(hints, expectedClass->name)) {\n    return {false, nullptr, nullptr}; // argument type not yet known.\n  }\n\n  else if (expectedClass && !expectedClass->is(\"Capsule\") && exprClass &&\n           exprClass->is(\"Capsule\")) {\n    type = extractClassGeneric(exprClass)->shared_from_this();\n    fn = [&](Expr *expr) -> Expr * {\n      return N<CallExpr>(\n          N<IdExpr>(getMangledMethod(\"std.internal.core\", \"Capsule\", \"_get\")), expr);\n    };\n  } else if (expectedClass && expectedClass->is(\"Capsule\") && exprClass &&\n             !exprClass->is(\"Capsule\")) {\n    type = instantiateType(getStdLibType(\"Capsule\"), std::vector<Type *>{exprClass});\n    fn = [&](Expr *expr) -> Expr * {\n      return N<CallExpr>(\n          N<IdExpr>(getMangledMethod(\"std.internal.core\", \"Capsule\", \"make\")), expr);\n    };\n  }\n\n  else if (expectedClass && !expectedClass->is(\"Any\") && exprClass &&\n           exprClass->is(\"Any\")) {\n    type = expectedClass->shared_from_this();\n    fn = [this, type](Expr *expr) -> Expr * {\n      auto r = realize(type.get());\n      seqassert(r, \"not realizable\");\n      return N<CallExpr>(N<IdExpr>(\"Any.unwrap\"), expr, N<IdExpr>(r->realizedName()));\n    };\n  } else if (expectedClass && expectedClass->is(\"Any\") && exprClass &&\n             !exprClass->is(\"Any\")) {\n    type = expectedClass->shared_from_this();\n    fn = [&](Expr *expr) -> Expr * { return N<CallExpr>(N<IdExpr>(\"Any\"), expr); };\n  }\n\n  else if (expectedClass && expectedClass->is(\"Generator\") &&\n           !exprClass->is(expectedClass->name) && !isEllipsis) {\n    if (findMethod(exprClass, \"__iter__\").empty())\n      return {false, nullptr, nullptr};\n    // Note: do not do this in pipelines (TODO: why?)\n    type = instantiateType(expectedClass);\n    fn = [&](Expr *expr) -> Expr * {\n      return N<CallExpr>(N<DotExpr>(expr, \"__iter__\"));\n    };\n  }\n\n  else if (expectedClass && expectedClass->is(\"float\") && exprClass->is(\"int\")) {\n    type = instantiateType(expectedClass);\n    fn = [&](Expr *expr) -> Expr * { return N<CallExpr>(N<IdExpr>(\"float\"), expr); };\n  }\n\n  else if (!callee && expectedClass && expectedClass->is(\"bool\") && exprClass &&\n           !exprClass->is(\"bool\")) {\n    // Do not do this in function calls---only use for if-else wrapping\n    type = instantiateType(expectedClass);\n    fn = [&](Expr *expr) -> Expr * {\n      return N<CallExpr>(N<DotExpr>(expr, \"__bool__\"));\n    };\n  }\n\n  else if (expectedClass && expectedClass->is(TYPE_OPTIONAL) && exprClass &&\n           !exprClass->is(expectedClass->name)) {\n    type =\n        instantiateType(getStdLibType(TYPE_OPTIONAL), std::vector<Type *>{exprClass});\n    fn = [&](Expr *expr) -> Expr * {\n      return N<CallExpr>(N<IdExpr>(TYPE_OPTIONAL), expr);\n    };\n  } else if (allowUnwrap && expectedClass && exprClass &&\n             exprClass->is(TYPE_OPTIONAL) &&\n             !exprClass->is(expectedClass->name)) { // unwrap optional\n    type = instantiateType(extractClassGeneric(exprClass));\n    fn = [&](Expr *expr) -> Expr * {\n      return N<CallExpr>(N<IdExpr>(FN_OPTIONAL_UNWRAP), expr);\n    };\n  }\n\n  else if (expectedClass && expectedClass->is(\"pyobj\") &&\n           !exprClass->is(expectedClass->name)) { // wrap to pyobj\n    if (findMethod(exprClass, \"__to_py__\").empty())\n      return {false, nullptr, nullptr};\n    type = instantiateType(expectedClass);\n    fn = [&](Expr *expr) -> Expr * {\n      return N<CallExpr>(N<IdExpr>(\"pyobj\"),\n                         N<CallExpr>(N<DotExpr>(expr, \"__to_py__\")));\n    };\n  }\n\n  else if (allowUnwrap && expectedClass && exprClass && exprClass->is(\"pyobj\") &&\n           !exprClass->is(expectedClass->name)) { // unwrap pyobj\n    if (findMethod(expectedClass, \"__from_py__\").empty())\n      return {false, nullptr, nullptr};\n    type = instantiateType(expectedClass);\n    auto texpr = N<IdExpr>(expectedClass->name);\n    texpr->setType(expectedType->shared_from_this());\n    fn = [this, texpr](Expr *expr) -> Expr * {\n      return N<CallExpr>(N<DotExpr>(texpr, \"__from_py__\"), N<DotExpr>(expr, \"p\"));\n    };\n  }\n\n  else if (expectedClass && expectedClass->is(TYPE_CALLABLE) && exprClass &&\n           (exprClass->getPartial() || exprClass->getFunc() ||\n            exprClass->is(TYPE_FUNCTION))) {\n    // Get list of arguments\n    std::vector<Type *> argTypes;\n    Type *retType;\n    std::shared_ptr<FuncType> fnType = nullptr;\n    if (!exprClass->getPartial()) {\n      auto targs = extractClassGeneric(exprClass)->getClass();\n      for (size_t i = 0; i < targs->size(); i++)\n        argTypes.push_back((*targs)[i]);\n      retType = extractClassGeneric(exprClass, 1);\n    } else {\n      fnType = instantiateType(exprClass->getPartial()->getPartialFunc());\n      std::vector<types::Type *> argumentTypes;\n      auto known = exprClass->getPartial()->getPartialMask();\n      for (size_t i = 0; i < known.size(); i++) {\n        if (known[i] != ClassType::PartialFlag::Included)\n          argTypes.push_back((*fnType)[i]);\n      }\n      retType = fnType->getRetType();\n    }\n    auto expectedArgs = extractClassGeneric(expectedClass)->getClass();\n    if (argTypes.size() != expectedArgs->size())\n      return {false, nullptr, nullptr};\n    for (size_t i = 0; i < argTypes.size(); i++) {\n      if (argTypes[i]->unify((*expectedArgs)[i], nullptr) < 0)\n        return {false, nullptr, nullptr};\n    }\n    if (retType->unify(extractClassGeneric(expectedClass, 1), nullptr) < 0)\n      return {false, nullptr, nullptr};\n\n    type = expectedType->shared_from_this();\n    fn = [this, type](Expr *expr) -> Expr * {\n      auto exprClass = expr->getType()->getClass();\n      auto expectedClass = type->getClass();\n\n      std::vector<Type *> argTypes;\n      Type *retType;\n      std::shared_ptr<FuncType> fnType = nullptr;\n      if (!exprClass->getPartial()) {\n        auto targs = extractClassGeneric(exprClass)->getClass();\n        for (size_t i = 0; i < targs->size(); i++)\n          argTypes.push_back((*targs)[i]);\n        retType = extractClassGeneric(exprClass, 1);\n      } else {\n        fnType = instantiateType(exprClass->getPartial()->getPartialFunc());\n        std::vector<types::Type *> argumentTypes;\n        auto known = exprClass->getPartial()->getPartialMask();\n        for (size_t i = 0; i < known.size(); i++) {\n          if (known[i] != ClassType::PartialFlag::Included)\n            argTypes.push_back((*fnType)[i]);\n        }\n        retType = fnType->getRetType();\n      }\n      auto expectedArgs = extractClassGeneric(expectedClass)->getClass();\n      for (size_t i = 0; i < argTypes.size(); i++)\n        unify(argTypes[i], (*expectedArgs)[i]);\n      unify(retType, extractClassGeneric(expectedClass, 1));\n\n      std::string fname;\n      Expr *retFn = nullptr, *dataArg = nullptr, *dataType = nullptr;\n      if (exprClass->getPartial()) {\n        auto rf = realize(exprClass);\n        fname = rf->realizedName();\n        seqassert(rf, \"not realizable\");\n        retFn = N<IndexExpr>(\n            N<CallExpr>(N<IndexExpr>(N<IdExpr>(\"Ptr\"), N<IdExpr>(rf->realizedName())),\n                        N<IdExpr>(\"data\")),\n            N<IntExpr>(0));\n        dataType = N<IdExpr>(\"cobj\");\n      } else if (exprClass->getFunc()) {\n        auto rf = realize(exprClass);\n        seqassert(rf, \"not realizable\");\n        fname = rf->realizedName();\n        retFn = N<IdExpr>(rf->getFunc()->realizedName());\n        dataArg = N<CallExpr>(N<IdExpr>(\"cobj\"));\n        dataType = N<IdExpr>(\"cobj\");\n      } else {\n        seqassert(exprClass->is(\"Function\"), \"bad type: {}\", exprClass->debugString(2));\n        auto rf = realize(exprClass);\n        seqassert(rf, \"not realizable\");\n        fname = rf->realizedName();\n        retFn = N<CallExpr>(N<IdExpr>(rf->realizedName()), N<IdExpr>(\"data\"));\n        dataType = N<IdExpr>(\"cobj\");\n      }\n      fname = fmt::format(\".proxy.{}\", fname);\n\n      if (!ctx->find(fname)) {\n        // Create wrapper if needed\n        auto f = N<FunctionStmt>(\n            fname, nullptr,\n            std::vector<Param>{\n                Param{\"data\", dataType},\n                Param{\"args\", N<IdExpr>(expectedArgs->realizedName())}}, // Tuple[...]\n            N<SuiteStmt>(\n                N<ReturnStmt>(N<CallExpr>(retFn, N<StarExpr>(N<IdExpr>(\"args\"))))));\n        f = cast<FunctionStmt>(transform(f));\n      }\n      auto e = N<CallExpr>(N<IdExpr>(TYPE_CALLABLE), N<IdExpr>(fname),\n                           dataArg ? dataArg : expr);\n      return e;\n    };\n  } else if (callee && exprClass && exprType->getFunc() &&\n             !(expectedClass && expectedClass->is(\"Function\"))) {\n    // Wrap raw Seq functions into Partial(...) call for easy realization.\n    // Special case: Seq functions are embedded (via lambda!)\n    if (expectedClass)\n      type = instantiateType(expectedClass);\n    auto fnName = exprType->getFunc()->ast->getName();\n    fn = [&, fnName](Expr *expr) -> Expr * {\n      auto p = N<CallExpr>(N<IdExpr>(fnName), N<EllipsisExpr>(EllipsisExpr::PARTIAL));\n      if (auto se = cast<StmtExpr>(expr))\n        return N<StmtExpr>(se->items, p);\n      return p;\n    };\n  } else if (expectedClass && expectedClass->is(\"Function\") && exprClass &&\n             exprClass->getPartial() && exprClass->getPartial()->isPartialEmpty()) {\n    type = instantiateType(expectedClass);\n    auto fnName = exprClass->getPartial()->getPartialFunc()->ast->name;\n    auto t = instantiateType(ctx->forceFind(fnName)->getType());\n    if (type->unify(t.get(), nullptr) >= 0)\n      fn = [&](Expr *expr) -> Expr * { return N<IdExpr>(fnName); };\n    else\n      type = nullptr;\n  }\n\n  else if (allowUnwrap && exprClass && exprType->getUnion() && expectedClass &&\n           !expectedClass->getUnion()) {\n    // Extract union types via Union._get\n    if (auto t = realize(expectedClass)) {\n      auto e = realize(exprType);\n      if (!e)\n        return {false, nullptr, nullptr};\n      bool ok = false;\n      for (auto &ut : e->getUnion()->getRealizationTypes()) {\n        if (ut->unify(t, nullptr) >= 0) {\n          ok = true;\n          break;\n        }\n      }\n      if (ok) {\n        type = t->shared_from_this();\n        fn = [this, type](Expr *expr) -> Expr * {\n          return N<CallExpr>(\n              N<IdExpr>(getMangledMethod(\"std.internal.core\", \"Union\", \"_get\")), expr,\n              N<IdExpr>(type->realizedName()));\n        };\n      }\n    } else {\n      return {false, nullptr, nullptr};\n    }\n  }\n\n  else if (exprClass && expectedClass && expectedClass->getUnion()) {\n    // Make union types via Union._new\n    if (!expectedClass->getUnion()->isSealed()) {\n      if (!expectedClass->getUnion()->addType(exprClass))\n        E(error::Error::UNION_TOO_BIG, expectedClass->getSrcInfo(),\n          expectedClass->getUnion()->pendingTypes.size());\n    }\n    if (auto t = realize(expectedClass)) {\n      if (expectedClass->unify(exprClass, nullptr) == -1) {\n        type = t->shared_from_this();\n        fn = [this, type](Expr *expr) -> Expr * {\n          return N<CallExpr>(N<DotExpr>(N<IdExpr>(\"Union\"), \"_new\"), expr,\n                             N<IdExpr>(type->realizedName()));\n        };\n      }\n    } else {\n      return {false, nullptr, nullptr};\n    }\n  }\n\n  else if (exprClass && exprClass->is(TYPE_TYPE) && expectedClass &&\n           (expectedClass->is(\"TypeWrap\"))) {\n    type = instantiateType(getStdLibType(\"TypeWrap\"),\n                           std::vector<types::Type *>{exprClass});\n    fn = [this](Expr *expr) -> Expr * {\n      return N<CallExpr>(N<IdExpr>(\"TypeWrap\"), expr);\n    };\n  }\n\n  else if (exprClass && exprClass->is(\"Super\") && expectedClass &&\n           !expectedClass->is(\"Super\")) {\n    // Super[T] to T\n    type = extractClassGeneric(exprClass)->shared_from_this();\n    fn = [this](Expr *expr) -> Expr * {\n      return N<CallExpr>(\n          N<IdExpr>(getMangledMethod(\"std.internal.core\", \"Super\", \"_unwrap\")), expr);\n    };\n  }\n\n  else if (exprClass && expectedClass && !exprClass->is(expectedClass->name)) {\n    // Cast derived classes to base classes\n    const auto &mros = ctx->cache->getClass(exprClass)->mro;\n    for (size_t i = 1; i < mros.size(); i++) {\n      auto t = instantiateType(mros[i].get(), exprClass);\n      if (t->unify(expectedClass, nullptr) >= 0) {\n        type = expectedClass->shared_from_this();\n        fn = [this, type](Expr *expr) -> Expr * {\n          return castToSuperClass(expr, type->getClass(), true);\n        };\n        break;\n      }\n    }\n  }\n\n  return {true, type, fn};\n}\n\n/// Cast derived class to a base class.\nExpr *TypecheckVisitor::castToSuperClass(Expr *expr, ClassType *superTyp,\n                                         bool isVirtual) {\n  ClassType *typ = expr->getClassType();\n  for (auto &field : getClassFields(typ)) {\n    for (auto &parentField : getClassFields(superTyp))\n      if (field.name == parentField.name) {\n        auto t = instantiateType(field.getType(), typ);\n        unify(t.get(), instantiateType(parentField.getType(), superTyp));\n      }\n  }\n  realize(superTyp);\n  auto typExpr = N<IdExpr>(superTyp->realizedName());\n  return transform(\n      N<CallExpr>(N<DotExpr>(N<IdExpr>(\"Super\"), \"_super\"), expr, typExpr));\n}\n\n/// Unpack a Tuple or KwTuple expression into (name, type) vector.\n/// Name is empty when handling Tuple; otherwise it matches names of KwTuple.\nstd::shared_ptr<std::vector<std::pair<std::string, types::Type *>>>\nTypecheckVisitor::unpackTupleTypes(const Expr *expr) {\n  auto ret = std::make_shared<std::vector<std::pair<std::string, types::Type *>>>();\n  if (auto tup = cast<TupleExpr>(expr->getOrigExpr())) {\n    for (auto &a : *tup) {\n      a = transform(a);\n      if (!a->getClassType())\n        return nullptr;\n      ret->emplace_back(\"\", a->getType());\n    }\n  } else if (cast<CallExpr>(expr->getOrigExpr())) {\n    auto val = extractClassType(expr->getType());\n    if (!val || !val->is(\"NamedTuple\") || !extractClassGeneric(val, 1)->getClass() ||\n        !extractClassGeneric(val)->canRealize())\n      return nullptr;\n    auto id = getIntLiteral(val);\n    seqassert(id >= 0 && id < ctx->cache->generatedTupleNames.size(), \"bad id: {}\", id);\n    auto names = ctx->cache->generatedTupleNames[id];\n    auto types = extractClassGeneric(val, 1)->getClass();\n    seqassert(startswith(types->name, \"Tuple\"), \"bad NamedTuple argument\");\n    for (size_t i = 0; i < types->generics.size(); i++) {\n      if (!extractClassGeneric(types, i))\n        return nullptr;\n      ret->emplace_back(names[i], extractClassGeneric(types, i));\n    }\n  } else {\n    return nullptr;\n  }\n  return ret;\n}\n\nstd::vector<std::pair<std::string, Expr *>>\nTypecheckVisitor::extractNamedTuple(Expr *expr) {\n  std::vector<std::pair<std::string, Expr *>> ret;\n\n  seqassert(expr->getType()->is(\"NamedTuple\") &&\n                extractClassGeneric(expr->getClassType())->canRealize(),\n            \"bad named tuple: {}\", *expr);\n  auto id = getIntLiteral(expr->getClassType());\n  seqassert(id >= 0 && id < ctx->cache->generatedTupleNames.size(), \"bad id: {}\", id);\n  auto names = ctx->cache->generatedTupleNames[id];\n  for (size_t i = 0; i < names.size(); i++) {\n    ret.emplace_back(names[i], N<IndexExpr>(N<DotExpr>(expr, \"args\"), N<IntExpr>(i)));\n  }\n  return ret;\n}\n\nstd::vector<Cache::Class::ClassField>\nTypecheckVisitor::getClassFields(types::ClassType *t) const {\n  auto f = getClass(t->name)->fields;\n  if (t->is(TYPE_TUPLE))\n    f = std::vector<Cache::Class::ClassField>(f.begin(),\n                                              f.begin() + t->generics.size());\n  return f;\n}\n\nstd::vector<types::TypePtr>\nTypecheckVisitor::getClassFieldTypes(types::ClassType *cls) {\n  return withClassGenerics(cls, [&]() {\n    std::vector<types::TypePtr> result;\n    for (auto &field : getClassFields(cls)) {\n      auto ftyp = instantiateType(field.getType(), cls);\n      if (!ftyp->canRealize() && field.typeExpr) {\n        auto t = extractType(transform(clean_clone(field.typeExpr)));\n        unify(ftyp.get(), t);\n      }\n      result.push_back(ftyp);\n    }\n    return result;\n  });\n}\n\ntypes::Type *TypecheckVisitor::extractType(types::Type *t) const {\n  while (t && t->is(TYPE_TYPE))\n    t = extractClassGeneric(t);\n  return t;\n}\n\ntypes::Type *TypecheckVisitor::extractType(Expr *e) const {\n  if (cast<IdExpr>(e) && cast<IdExpr>(e)->getValue() == TYPE_TYPE)\n    return e->getType();\n  if (auto i = cast<InstantiateExpr>(e))\n    if (cast<IdExpr>(i->getExpr()) &&\n        cast<IdExpr>(i->getExpr())->getValue() == TYPE_TYPE)\n      return e->getType();\n  return extractType(e->getType());\n}\n\ntypes::Type *TypecheckVisitor::extractType(const std::string &s) const {\n  auto c = ctx->forceFind(s);\n  return s == TYPE_TYPE ? c->getType() : extractType(c->getType());\n}\n\ntypes::ClassType *TypecheckVisitor::extractClassType(Expr *e) const {\n  auto t = extractType(e);\n  return t->getClass();\n}\n\ntypes::ClassType *TypecheckVisitor::extractClassType(types::Type *t) const {\n  return extractType(t)->getClass();\n}\n\ntypes::ClassType *TypecheckVisitor::extractClassType(const std::string &s) const {\n  return extractType(s)->getClass();\n}\n\nbool TypecheckVisitor::isUnbound(types::Type *t) { return t->getUnbound() != nullptr; }\n\nbool TypecheckVisitor::isUnbound(const Expr *e) { return isUnbound(e->getType()); }\n\nbool TypecheckVisitor::hasOverloads(const std::string &root) const {\n  auto i = in(ctx->cache->overloads, root);\n  return i && i->size() > 1;\n}\n\nstd::vector<std::string> TypecheckVisitor::getOverloads(const std::string &root) const {\n  auto i = in(ctx->cache->overloads, root);\n  seqassert(i, \"bad root\");\n  return *i;\n}\n\nstd::string TypecheckVisitor::getUnmangledName(const std::string &s) const {\n  if (in(ctx->cache->reverseIdentifierLookup, s))\n    return ctx->cache->rev(s);\n  return s;\n}\n\nstd::string TypecheckVisitor::getUserFacingName(const std::string &s) const {\n  auto n = getUnmangledName(s);\n  if (startswith(n, \"$\"))\n    n = n.substr(1);\n  return n;\n}\n\nCache::Class *TypecheckVisitor::getClass(const std::string &t) const {\n  auto i = in(ctx->cache->classes, t);\n  return i;\n}\n\nCache::Class *TypecheckVisitor::getClass(types::Type *t) const {\n  if (t) {\n    if (auto c = t->getClass())\n      return getClass(c->name);\n  }\n  seqassert(false, \"bad class\");\n  return nullptr;\n}\n\nCache::Function *TypecheckVisitor::getFunction(const std::string &n) const {\n  auto i = in(ctx->cache->functions, n);\n  return i;\n}\n\nCache::Function *TypecheckVisitor::getFunction(types::Type *t) const {\n  seqassert(t->getFunc(), \"bad function\");\n  return getFunction(t->getFunc()->getFuncName());\n}\n\nCache::Class::ClassRealization *\nTypecheckVisitor::getClassRealization(types::Type *t) const {\n  seqassert(t->canRealize(), \"bad class\");\n  auto i = in(getClass(t)->realizations, t->getClass()->realizedName());\n  seqassert(i, \"bad class realization\");\n  return i->get();\n}\n\nstd::string TypecheckVisitor::getRootName(const types::FuncType *t) const {\n  auto i = in(ctx->cache->functions, t->getFuncName());\n  seqassert(i && !i->rootName.empty(), \"bad function\");\n  return i->rootName;\n}\n\nbool TypecheckVisitor::isTypeExpr(const Expr *e) {\n  return e && e->getType() && e->getType()->is(TYPE_TYPE);\n}\n\nCache::Module *TypecheckVisitor::getImport(const std::string &s) const {\n  auto i = in(ctx->cache->imports, s);\n  seqassert(i, \"bad import\");\n  return i;\n}\n\nbool TypecheckVisitor::isDispatch(const std::string &s) {\n  return endswith(s, FN_DISPATCH_SUFFIX);\n}\n\nbool TypecheckVisitor::isDispatch(const FunctionStmt *ast) {\n  return ast && isDispatch(ast->name);\n}\n\nbool TypecheckVisitor::isDispatch(types::Type *f) {\n  return f->getFunc() && isDispatch(f->getFunc()->ast);\n}\n\nbool TypecheckVisitor::isHeterogenous(types::Type *type) {\n  if (!type->getClass() || !type->getClass()->isRecord())\n    return false;\n  std::vector<TypePtr> fs;\n  if (type->is(TYPE_TUPLE)) {\n    for (auto &g : type->getClass()->generics)\n      fs.push_back(g.getType()->shared_from_this());\n  } else {\n    fs = getClassFieldTypes(type->getClass());\n  }\n  if (fs.size() > 1) {\n    std::string first = fs[0]->realizedName();\n    for (int i = 1; i < fs.size(); i++)\n      if (fs[i]->realizedName() != first)\n        return true;\n  }\n  return false;\n}\n\nvoid TypecheckVisitor::addClassGenerics(types::ClassType *typ, bool func,\n                                        bool onlyMangled, bool instantiate) {\n  auto addGen = [&](const types::ClassType::Generic &g) {\n    auto t = g.type;\n    if (instantiate) {\n      if (auto l = t->getLink())\n        if (l->kind == types::LinkType::Generic) {\n          auto lx = std::make_shared<types::LinkType>(*l);\n          lx->kind = types::LinkType::Unbound;\n          t = lx;\n        }\n    }\n    seqassert(!g.staticKind || t->getStaticKind(), \"{} not a static: {}\", g.name,\n              *(g.type));\n    if (!g.staticKind && !t->is(TYPE_TYPE))\n      t = instantiateTypeVar(t.get());\n    auto n = onlyMangled ? g.name : getUnmangledName(g.name);\n    auto v = ctx->addType(n, g.name, t);\n    v->generic = true;\n  };\n\n  if (func && typ->getFunc()) {\n    auto tf = typ->getFunc();\n    for (auto parent = tf->funcParent; parent;) {\n      if (auto f = parent->getFunc()) {\n        // Add parent function generics\n        for (auto &g : f->funcGenerics)\n          addGen(g);\n        parent = f->funcParent;\n      } else {\n        // Add parent class generics\n        seqassert(parent->getClass(), \"not a class: {}\", *parent);\n        for (auto &g : parent->getClass()->hiddenGenerics)\n          addGen(g);\n        for (auto &g : parent->getClass()->generics)\n          addGen(g);\n        break;\n      }\n    }\n    for (auto &g : tf->funcGenerics)\n      addGen(g);\n  } else {\n    for (auto &g : typ->hiddenGenerics)\n      addGen(g);\n    for (auto &g : typ->generics)\n      addGen(g);\n  }\n}\n\ntypes::TypePtr TypecheckVisitor::instantiateTypeVar(types::Type *t) {\n  return instantiateType(ctx->forceFind(TYPE_TYPE)->getType(), {t});\n}\n\nvoid TypecheckVisitor::registerGlobal(const std::string &name) const {\n  if (!in(ctx->cache->globals, name)) {\n    ctx->cache->globals[name] = nullptr;\n  }\n}\n\ntypes::ClassType *TypecheckVisitor::getStdLibType(const std::string &type) const {\n  auto t = getImport(STDLIB_IMPORT)->ctx->forceFind(type)->getType();\n  if (type == TYPE_TYPE)\n    return t->getClass();\n  return extractClassType(t);\n}\n\ntypes::Type *TypecheckVisitor::extractClassGeneric(types::Type *t, size_t idx) const {\n  seqassert(t->getClass() && idx < t->getClass()->generics.size(), \"bad class\");\n  return t->getClass()->generics[idx].type.get();\n}\n\ntypes::Type *TypecheckVisitor::extractFuncGeneric(types::Type *t, size_t idx) const {\n  seqassert(t->getFunc() && idx < t->getFunc()->funcGenerics.size(), \"bad function\");\n  return t->getFunc()->funcGenerics[idx].type.get();\n}\n\ntypes::Type *TypecheckVisitor::extractFuncArgType(types::Type *t, size_t idx) const {\n  seqassert(t->getFunc(), \"bad function\");\n  return extractClassGeneric(extractClassGeneric(t), idx);\n}\n\nstd::string TypecheckVisitor::getClassMethod(types::Type *typ,\n                                             const std::string &member) const {\n  if (auto cls = getClass(typ)) {\n    if (auto t = in(cls->methods, member))\n      return *t;\n  }\n  seqassertn(false, \"cannot find '{}' in '{}'\", member, *typ);\n  return \"\";\n}\n\nstd::string TypecheckVisitor::getTemporaryVar(const std::string &s) const {\n  return ctx->cache->getTemporaryVar(s);\n}\n\nstd::string TypecheckVisitor::getStrLiteral(types::Type *t, size_t pos) const {\n  seqassert(t && t->getClass(), \"not a class\");\n  if (t->getStrStatic())\n    return t->getStrStatic()->value;\n  auto ct = extractClassGeneric(t, pos);\n  seqassert(ct->canRealize() && ct->getStrStatic(), \"not a string literal\");\n  return ct->getStrStatic()->value;\n}\n\nint64_t TypecheckVisitor::getIntLiteral(types::Type *t, size_t pos) const {\n  seqassert(t && t->getClass(), \"not a class\");\n  if (t->getIntStatic())\n    return t->getIntStatic()->value;\n  auto ct = extractClassGeneric(t, pos);\n  seqassert(ct->canRealize() && ct->getIntStatic(), \"not a int literal\");\n  return ct->getIntStatic()->value;\n}\n\nbool TypecheckVisitor::getBoolLiteral(types::Type *t, size_t pos) const {\n  seqassert(t && t->getClass(), \"not a class\");\n  if (t->getBoolStatic())\n    return t->getBoolStatic()->value;\n  auto ct = extractClassGeneric(t, pos);\n  seqassert(ct->canRealize() && ct->getBoolStatic(), \"not a bool literal\");\n  return ct->getBoolStatic()->value;\n}\n\nExpr *TypecheckVisitor::getParamType(Type *t) {\n  if (!t)\n    return nullptr;\n  if (t->is(TYPE_TYPE)) {\n    return N<IdExpr>(TYPE_TYPE);\n  } else if (auto st = t->getStaticKind()) {\n    return N<IndexExpr>(N<IdExpr>(\"Literal\"), N<IdExpr>(Type::stringFromLiteral(st)));\n  } else {\n    return nullptr;\n  }\n}\n\nbool TypecheckVisitor::hasSideEffect(Expr *e) {\n  return !match(e, MOr(M<IdExpr>(), M<DotExpr>(M<IdExpr>()), M<InstantiateExpr>(),\n                       M<BoolExpr>(), M<IntExpr>(), M<StringExpr>(), M<NoneExpr>(),\n                       M<FloatExpr>(), M<EllipsisExpr>(), M<YieldExpr>()));\n}\n\nExpr *TypecheckVisitor::getHeadExpr(Expr *e) {\n  while (auto se = cast<StmtExpr>(e))\n    e = se->getExpr();\n  return e;\n}\n\nbool TypecheckVisitor::isImportFn(const std::string &s) {\n  return startswith(s, \"%_import_\");\n}\n\nint64_t TypecheckVisitor::getTime() const { return ctx->time; }\n\ntypes::Type *TypecheckVisitor::getUnderlyingStaticType(types::Type *t) const {\n  if (t->getStatic()) {\n    return t->getStatic()->getNonStaticType();\n  } else if (auto c = t->getStaticKind()) {\n    return getStdLibType(Type::stringFromLiteral(c));\n  }\n  return t;\n}\n\nstd::shared_ptr<types::LinkType>\nTypecheckVisitor::instantiateUnbound(const SrcInfo &srcInfo, int level) const {\n  auto typ = std::make_shared<types::LinkType>(\n      ctx->cache, types::LinkType::Unbound, ctx->cache->unboundCount++, level, nullptr);\n  typ->setSrcInfo(srcInfo);\n  return typ;\n}\n\nstd::shared_ptr<types::LinkType>\nTypecheckVisitor::instantiateUnbound(const SrcInfo &srcInfo) const {\n  return instantiateUnbound(srcInfo, ctx->typecheckLevel);\n}\n\nstd::shared_ptr<types::LinkType> TypecheckVisitor::instantiateUnbound() const {\n  return instantiateUnbound(getSrcInfo(), ctx->typecheckLevel);\n}\n\ntypes::TypePtr TypecheckVisitor::instantiateType(const SrcInfo &srcInfo,\n                                                 types::Type *type,\n                                                 types::ClassType *generics) const {\n  seqassert(type, \"type is null\");\n  std::unordered_map<int, types::TypePtr> genericCache;\n  if (generics) {\n    for (auto &g : generics->hiddenGenerics)\n      if (g.type &&\n          !(g.type->getLink() && g.type->getLink()->kind == types::LinkType::Generic)) {\n        genericCache[g.id] = g.type;\n      }\n    for (auto &g : generics->generics)\n      if (g.type &&\n          !(g.type->getLink() && g.type->getLink()->kind == types::LinkType::Generic)) {\n        genericCache[g.id] = g.type;\n      }\n    // special case: __SELF__\n    if (type->getFunc() && !type->getFunc()->funcGenerics.empty() &&\n        getUnmangledName(type->getFunc()->funcGenerics[0].name) == \"__SELF__\") {\n      genericCache[type->getFunc()->funcGenerics[0].id] = generics->shared_from_this();\n    }\n  }\n  auto t = type->instantiate(ctx->typecheckLevel, &(ctx->cache->unboundCount),\n                             &genericCache);\n  for (auto &val : genericCache | std::views::values) {\n    if (auto l = val->getLink()) {\n      val->setSrcInfo(srcInfo);\n      if (l->defaultType) {\n        ctx->getBase()->pendingDefaults[0].insert(val);\n      }\n    }\n  }\n  if (t->getUnion() && !t->getUnion()->isSealed()) {\n    t->setSrcInfo(srcInfo);\n    ctx->getBase()->pendingDefaults[0].insert(t);\n  }\n  return t;\n}\n\ntypes::TypePtr\nTypecheckVisitor::instantiateType(const SrcInfo &srcInfo, types::Type *root,\n                                  const std::vector<types::Type *> &generics) const {\n  auto c = root->getClass();\n  seqassert(c, \"root class is null\");\n  // dummy generic type\n  auto g = std::make_shared<types::ClassType>(ctx->cache, \"\");\n  if (generics.size() != c->generics.size()) {\n    E(Error::GENERICS_MISMATCH, srcInfo, getUserFacingName(c->name), c->generics.size(),\n      generics.size());\n  }\n  for (int i = 0; i < c->generics.size(); i++) {\n    auto t = generics[i];\n    seqassert(c->generics[i].type, \"generic is null\");\n    if (!c->generics[i].staticKind && t->getStatic())\n      t = t->getStatic()->getNonStaticType();\n    g->generics.emplace_back(\"\", t->shared_from_this(), c->generics[i].id,\n                             c->generics[i].staticKind);\n  }\n  return instantiateType(srcInfo, root, g.get());\n}\n\nstd::vector<types::FuncType *> TypecheckVisitor::findMethod(types::ClassType *type,\n                                                            const std::string &method,\n                                                            bool hideShadowed) {\n  std::vector<types::FuncType *> vv;\n  std::unordered_set<std::string> signatureLoci;\n\n  auto populate = [&](const auto &cls) {\n    auto t = in(cls.methods, method);\n    if (!t)\n      return;\n\n    auto mt = getOverloads(*t);\n    for (int mti = static_cast<int>(mt.size()) - 1; mti >= 0; --mti) {\n      const auto &exactMethod = mt[mti];\n      auto f = getFunction(exactMethod);\n      if (isDispatch(exactMethod) || !f->getType())\n        continue;\n      if (hideShadowed) {\n        auto sig = f->ast->getSignature();\n        if (!in(signatureLoci, sig)) {\n          signatureLoci.insert(sig);\n          vv.emplace_back(f->getType());\n        }\n      } else {\n        vv.emplace_back(f->getType());\n      }\n    }\n  };\n  if (type->is(\"Capsule\") || type->is(\"Super\")) {\n    type = extractClassGeneric(type)->getClass();\n  }\n  if (type && type->is(TYPE_TUPLE) && method == \"__new__\" && !type->generics.empty()) {\n    generateTuple(type->generics.size());\n    auto mc = getClass(TYPE_TUPLE);\n    populate(*mc);\n    for (auto f : vv)\n      if (f->size() == type->generics.size())\n        return {f};\n    return {};\n  }\n  if (auto cls = getClass(type)) {\n    for (const auto &pc : cls->mro) {\n      auto mc = getClass(pc->name == \"__NTuple__\" ? TYPE_TUPLE : pc->name);\n      populate(*mc);\n    }\n  }\n  return vv;\n}\n\nCache::Class::ClassField *\nTypecheckVisitor::findMember(types::ClassType *type, const std::string &member) const {\n  if (type->is(\"Capsule\")) {\n    type = extractClassGeneric(type)->getClass();\n  }\n  if (auto cls = getClass(type)) {\n    for (const auto &pc : cls->mro) {\n      auto mc = getClass(pc.get());\n      for (auto &mm : mc->fields) {\n        if (pc->is(TYPE_TUPLE) && (&mm - &(mc->fields[0])) >= type->generics.size())\n          break;\n        if (mm.name == member)\n          return &mm;\n      }\n    }\n  }\n  return nullptr;\n}\n\nint TypecheckVisitor::reorderNamedArgs(const types::FuncType *func,\n                                       const std::vector<CallArg> &args,\n                                       const ReorderDoneFn &onDone,\n                                       const ReorderErrorFn &onError,\n                                       const std::string &known) const {\n  // See https://docs.python.org/3.6/reference/expressions.html#calls for details.\n  // Final score:\n  //  - +1 for each matched argument\n  //  -  0 for *args/**kwargs/default arguments\n  //  - -1 for failed match\n  int score = 0;\n\n  // 0. Find *args and **kwargs\n  // True if there is a trailing ellipsis (full partial: fn(all_args, ...))\n  bool partial = !args.empty() && args.back().name.empty() &&\n                 cast<EllipsisExpr>(args.back().value) &&\n                 cast<EllipsisExpr>(args.back().value)->isPartial();\n\n  int starArgIndex = -1, kwstarArgIndex = -1;\n  for (int i = 0; i < func->ast->size(); i++) {\n    if (startswith((*func->ast)[i].name, \"**\"))\n      kwstarArgIndex = i, score -= 2;\n    else if (startswith((*func->ast)[i].name, \"*\"))\n      starArgIndex = i, score -= 2;\n  }\n\n  // 1. Assign positional arguments to slots\n  // Each slot contains a list of arg's indices\n  std::vector<std::vector<int>> slots(func->ast->size());\n  seqassert(known.empty() || func->ast->size() == known.size(), \"bad 'known' string\");\n  std::vector<int> extra;\n  std::map<std::string, int> namedArgs,\n      extraNamedArgs; // keep the map---we need it sorted!\n  for (int ai = 0, si = 0; ai < args.size() - partial; ai++) {\n    if (args[ai].name.empty()) {\n      while (!known.empty() && si < slots.size() &&\n             known[si] == ClassType::PartialFlag::Included)\n        si++;\n      if (si < slots.size() && (starArgIndex == -1 || si < starArgIndex))\n        slots[si++] = {ai};\n      else\n        extra.emplace_back(ai);\n    } else {\n      namedArgs[args[ai].name] = ai;\n    }\n  }\n  score += 2 * static_cast<int>(slots.size() - func->funcGenerics.size());\n\n  for (auto ai : std::vector<int>{std::max(starArgIndex, kwstarArgIndex),\n                                  std::min(starArgIndex, kwstarArgIndex)})\n    if (ai != -1 && !slots[ai].empty()) {\n      extra.insert(extra.begin(), ai);\n      slots[ai].clear();\n    }\n\n  // 2. Assign named arguments to slots\n  if (!namedArgs.empty()) {\n    std::map<std::string, int> slotNames;\n    for (int i = 0; i < func->ast->size(); i++)\n      if (known.empty() || known[i] != ClassType::PartialFlag::Included) {\n        auto [_, n] = (*func->ast)[i].getNameWithStars();\n        slotNames[getUnmangledName(n)] = i;\n      }\n    for (auto &n : namedArgs) {\n      if (!in(slotNames, n.first))\n        extraNamedArgs[n.first] = n.second;\n      else if (slots[slotNames[n.first]].empty())\n        slots[slotNames[n.first]].push_back(n.second);\n      else\n        return onError(Error::CALL_REPEATED_NAME, args[n.second].value->getSrcInfo(),\n                       Emsg(Error::CALL_REPEATED_NAME, n.first));\n    }\n  }\n\n  // 3. Fill in *args, if present\n  if (!extra.empty() && starArgIndex == -1)\n    return onError(Error::CALL_ARGS_MANY, getSrcInfo(),\n                   Emsg(Error::CALL_ARGS_MANY, getUserFacingName(func->ast->getName()),\n                        func->ast->size(), args.size() - partial));\n\n  if (starArgIndex != -1)\n    slots[starArgIndex] = extra;\n\n  // 4. Fill in **kwargs, if present\n  if (!extraNamedArgs.empty() && kwstarArgIndex == -1)\n    return onError(Error::CALL_ARGS_INVALID,\n                   args[extraNamedArgs.begin()->second].value->getSrcInfo(),\n                   Emsg(Error::CALL_ARGS_INVALID, extraNamedArgs.begin()->first,\n                        getUnmangledName(func->ast->getName())));\n  if (kwstarArgIndex != -1)\n    for (auto &val : extraNamedArgs | std::views::values)\n      slots[kwstarArgIndex].push_back(val);\n\n  // 5. Fill in the default arguments\n  for (auto i = 0; i < func->ast->size(); i++)\n    if (slots[i].empty() && i != starArgIndex && i != kwstarArgIndex) {\n      if (((*func->ast)[i].isValue() &&\n           ((*func->ast)[i].getDefault() ||\n            (!known.empty() && known[i] == ClassType::PartialFlag::Included))) ||\n          startswith((*func->ast)[i].getName(), \"$\")) {\n        score -= 2;\n      } else if (!partial && (*func->ast)[i].isValue()) {\n        auto [_, n] = (*func->ast)[i].getNameWithStars();\n        return onError(Error::CALL_ARGS_MISSING, getSrcInfo(),\n                       Emsg(Error::CALL_ARGS_MISSING,\n                            getUnmangledName(func->ast->getName()),\n                            getUnmangledName(n)));\n      }\n    }\n  auto s = onDone(starArgIndex, kwstarArgIndex, slots, partial);\n  return s != -1 ? score + s : -1;\n}\n\nbool TypecheckVisitor::isCanonicalName(const std::string &name) {\n  return name.rfind('.') != std::string::npos;\n}\n\ntypes::FuncType *TypecheckVisitor::extractFunction(types::Type *t) {\n  if (auto f = t->getFunc())\n    return f;\n  if (auto p = t->getPartial())\n    return p->getPartialFunc();\n  return nullptr;\n}\n\nclass SearchVisitor : public CallbackASTVisitor<void, void> {\n  std::function<bool(Expr *)> exprPredicate;\n  std::function<bool(Stmt *)> stmtPredicate;\n\npublic:\n  std::vector<ASTNode *> result;\n\npublic:\n  SearchVisitor(const std::function<bool(Expr *)> &exprPredicate,\n                const std::function<bool(Stmt *)> &stmtPredicate)\n      : exprPredicate(exprPredicate), stmtPredicate(stmtPredicate) {}\n  void transform(Expr *expr) override {\n    if (expr && exprPredicate(expr)) {\n      result.push_back(expr);\n    } else {\n      SearchVisitor v(exprPredicate, stmtPredicate);\n      if (expr)\n        expr->accept(v);\n      result.insert(result.end(), v.result.begin(), v.result.end());\n    }\n  }\n  void transform(Stmt *stmt) override {\n    if (stmt && stmtPredicate(stmt)) {\n      SearchVisitor v(exprPredicate, stmtPredicate);\n      stmt->accept(v);\n      result.insert(result.end(), v.result.begin(), v.result.end());\n    }\n  }\n};\n\nParserErrors TypecheckVisitor::findTypecheckErrors(Stmt *n) const {\n  SearchVisitor v([](const Expr *e) { return !e->isDone(); },\n                  [](const Stmt *s) { return !s->isDone(); });\n  v.transform(n);\n  std::vector<ErrorMessage> errors;\n  for (auto e : v.result) {\n    auto code = ctx->cache->getContent(e->getSrcInfo());\n    if (!code.empty())\n      errors.emplace_back(fmt::format(\"cannot typecheck '{}'\", code), e->getSrcInfo());\n    else\n      errors.emplace_back(fmt::format(\"cannot typecheck the expression\"),\n                          e->getSrcInfo());\n  }\n  return ParserErrors(errors);\n}\n\n/***** Cython-like code generation *****/\n\nconst std::string CYTHON_MODULE = \"std.internal.python\";\nconst std::string CYTHON_WRAP = \"_PyWrap\";\nconst std::string CYTHON_ITER = \"_PyWrap.IterWrap\";\n\nir::PyType TypecheckVisitor::cythonizeClass(const std::string &name) {\n  auto c = getClass(name);\n  auto ci = getImport(c->module);\n  if (!ci->name.empty())\n    return {\"\", \"\"};\n  if (!in(c->methods, \"__to_py__\") || !in(c->methods, \"__from_py__\"))\n    return {\"\", \"\"};\n\n  LOG_USER(\"[py] Cythonizing {} ({})\", getUserFacingName(name), name);\n  ir::PyType py{getUnmangledName(name), c->ast->getDocstr()};\n\n  auto tc = extractType(ctx->forceFind(name)->getType());\n  if (!tc->canRealize())\n    E(Error::CUSTOM, c->ast, \"cannot realize '{}' for Python export\",\n      getUnmangledName(name));\n  tc = realize(tc);\n  seqassertn(tc, \"cannot realize '{}'\", name);\n\n  // 1. Replace to_py / from_py with _PyWrap.wrap_to_py/from_py\n  if (auto ofnn = in(c->methods, \"__to_py__\")) {\n    auto fnn = getOverloads(*ofnn).front(); // default first overload!\n    auto fna = getFunction(fnn)->ast;\n    fna->suite = SuiteStmt::wrap(N<ReturnStmt>(N<CallExpr>(\n        N<IdExpr>(getMangledMethod(CYTHON_MODULE, CYTHON_WRAP, \"wrap_to_py\")),\n        N<IdExpr>(fna->begin()->name))));\n  }\n  if (auto ofnn = in(c->methods, \"__from_py__\")) {\n    auto fnn = getOverloads(*ofnn).front(); // default first overload!\n    auto fna = getFunction(fnn)->ast;\n    fna->suite = SuiteStmt::wrap(N<ReturnStmt>(N<CallExpr>(\n        N<IdExpr>(getMangledMethod(CYTHON_MODULE, CYTHON_WRAP, \"wrap_from_py\")),\n        N<IdExpr>(fna->begin()->name), N<IdExpr>(name))));\n  }\n  for (auto &n : std::vector<std::string>{\"__from_py__\", \"__to_py__\"}) {\n    auto fnn = getOverloads(*in(c->methods, n)).front();\n    auto fn = getFunction(fnn);\n    ir::Func *oldIR = nullptr;\n    if (!fn->realizations.empty())\n      oldIR = fn->realizations.begin()->second->ir;\n    fn->realizations.clear();\n    auto tf = realize(fn->type);\n    seqassertn(tf, \"cannot re-realize '{}'\", fnn);\n    if (oldIR) {\n      std::vector<ir::Value *> args;\n      for (auto it = oldIR->arg_begin(); it != oldIR->arg_end(); ++it) {\n        args.push_back(ctx->cache->module->Nr<ir::VarValue>(*it));\n      }\n      cast<ir::BodiedFunc>(oldIR)->setBody(\n          ir::util::series(ir::util::call(fn->realizations.begin()->second->ir, args)));\n    }\n  }\n  for (auto &r : getFunction(getMangledMethod(CYTHON_MODULE, CYTHON_WRAP, \"py_type\"))\n                         ->realizations |\n                     std::views::values) {\n    if (r->type->funcGenerics[0].type->unify(tc, nullptr) >= 0) {\n      py.typePtrHook = r->ir;\n      break;\n    }\n  }\n\n  // 2. Handle methods\n  auto methods = c->methods;\n  for (const auto &[n, ofnn] : methods) {\n    auto canonicalName = getOverloads(ofnn).back();\n    auto fn = getFunction(canonicalName);\n    if (getOverloads(ofnn).size() == 1 && fn->ast->hasAttribute(Attr::AutoGenerated))\n      continue;\n    auto fna = fn->ast;\n    bool isMethod = fna->hasAttribute(Attr::Method);\n    bool isProperty = fna->hasAttribute(Attr::Property);\n\n    std::string call = getMangledMethod(CYTHON_MODULE, CYTHON_WRAP, \"wrap_multiple\");\n    bool isMagic = false;\n    if (startswith(n, \"__\") && endswith(n, \"__\")) {\n      auto m = n.substr(2, n.size() - 4);\n      if (m == \"new\" && c->ast->hasAttribute(Attr::Tuple))\n        m = \"init\";\n      auto cls = getClass(getMangledClass(CYTHON_MODULE, CYTHON_WRAP));\n      if (auto i = in(cls->methods, \"wrap_magic_\" + m)) {\n        call = getMangledMethod(CYTHON_MODULE, CYTHON_WRAP, \"wrap_magic_\" + m);\n        isMagic = true;\n      }\n    }\n    if (isProperty)\n      call = getMangledMethod(CYTHON_MODULE, CYTHON_WRAP, \"wrap_get\");\n\n    auto generics = std::vector<types::TypePtr>{tc->shared_from_this()};\n    if (isProperty) {\n      generics.push_back(instantiateStatic(getUnmangledName(canonicalName)));\n    } else if (!isMagic) {\n      generics.push_back(instantiateStatic(n));\n      generics.push_back(instantiateStatic(static_cast<int64_t>(isMethod)));\n    }\n    auto f = realizeIRFunc(getFunction(call)->getType(), generics);\n    if (!f)\n      continue;\n\n    LOG_USER(\"[py] {} -> {} (method={}; property={})\", n, call, isMethod, isProperty);\n    if (isProperty) {\n      py.getset.push_back({getUnmangledName(canonicalName), \"\", f, nullptr});\n    } else if (n == \"__repr__\") {\n      py.repr = f;\n    } else if (n == \"__add__\") {\n      py.add = f;\n    } else if (n == \"__iadd__\") {\n      py.iadd = f;\n    } else if (n == \"__sub__\") {\n      py.sub = f;\n    } else if (n == \"__isub__\") {\n      py.isub = f;\n    } else if (n == \"__mul__\") {\n      py.mul = f;\n    } else if (n == \"__imul__\") {\n      py.imul = f;\n    } else if (n == \"__mod__\") {\n      py.mod = f;\n    } else if (n == \"__imod__\") {\n      py.imod = f;\n    } else if (n == \"__divmod__\") {\n      py.divmod = f;\n    } else if (n == \"__pow__\") {\n      py.pow = f;\n    } else if (n == \"__ipow__\") {\n      py.ipow = f;\n    } else if (n == \"__neg__\") {\n      py.neg = f;\n    } else if (n == \"__pos__\") {\n      py.pos = f;\n    } else if (n == \"__abs__\") {\n      py.abs = f;\n    } else if (n == \"__bool__\") {\n      py.bool_ = f;\n    } else if (n == \"__invert__\") {\n      py.invert = f;\n    } else if (n == \"__lshift__\") {\n      py.lshift = f;\n    } else if (n == \"__ilshift__\") {\n      py.ilshift = f;\n    } else if (n == \"__rshift__\") {\n      py.rshift = f;\n    } else if (n == \"__irshift__\") {\n      py.irshift = f;\n    } else if (n == \"__and__\") {\n      py.and_ = f;\n    } else if (n == \"__iand__\") {\n      py.iand = f;\n    } else if (n == \"__xor__\") {\n      py.xor_ = f;\n    } else if (n == \"__ixor__\") {\n      py.ixor = f;\n    } else if (n == \"__or__\") {\n      py.or_ = f;\n    } else if (n == \"__ior__\") {\n      py.ior = f;\n    } else if (n == \"__int__\") {\n      py.int_ = f;\n    } else if (n == \"__float__\") {\n      py.float_ = f;\n    } else if (n == \"__floordiv__\") {\n      py.floordiv = f;\n    } else if (n == \"__ifloordiv__\") {\n      py.ifloordiv = f;\n    } else if (n == \"__truediv__\") {\n      py.truediv = f;\n    } else if (n == \"__itruediv__\") {\n      py.itruediv = f;\n    } else if (n == \"__index__\") {\n      py.index = f;\n    } else if (n == \"__matmul__\") {\n      py.matmul = f;\n    } else if (n == \"__imatmul__\") {\n      py.imatmul = f;\n    } else if (n == \"__len__\") {\n      py.len = f;\n    } else if (n == \"__getitem__\") {\n      py.getitem = f;\n    } else if (n == \"__setitem__\") {\n      py.setitem = f;\n    } else if (n == \"__contains__\") {\n      py.contains = f;\n    } else if (n == \"__hash__\") {\n      py.hash = f;\n    } else if (n == \"__call__\") {\n      py.call = f;\n    } else if (n == \"__str__\") {\n      py.str = f;\n    } else if (n == \"__iter__\") {\n      py.iter = f;\n    } else if (n == \"__del__\") {\n      py.del = f;\n    } else if (n == \"__init__\" ||\n               (c->ast->hasAttribute(Attr::Tuple) && n == \"__new__\")) {\n      py.init = f;\n    } else {\n      py.methods.push_back(ir::PyFunction{\n          n, fna->getDocstr(), f,\n          fna->hasAttribute(Attr::Method) ? ir::PyFunction::Type::METHOD\n                                          : ir::PyFunction::Type::CLASS,\n          // always use FASTCALL for now; works even for 0- or 1- arg methods\n          2});\n      py.methods.back().keywords = true;\n    }\n  }\n\n  for (auto &m : py.methods) {\n    if (in(std::set<std::string>{\"__lt__\", \"__le__\", \"__eq__\", \"__ne__\", \"__gt__\",\n                                 \"__ge__\"},\n           m.name)) {\n      py.cmp = realizeIRFunc(\n          ctx->forceFind(getMangledMethod(CYTHON_MODULE, CYTHON_WRAP, \"wrap_cmp\"))\n              ->type->getFunc(),\n          {tc->shared_from_this()});\n      break;\n    }\n  }\n\n  if (c->realizations.size() != 1)\n    E(Error::CUSTOM, c->ast, \"cannot pythonize generic class '{}'\", name);\n  auto r = c->realizations.begin()->second;\n  py.type = r->ir;\n  seqassertn(!r->type->is(TYPE_TUPLE), \"tuples not yet done\");\n  for (auto &mn : r->fields | std::views::keys) {\n    /// TODO: handle PyMember for tuples\n    // Generate getters & setters\n    auto generics =\n        std::vector<types::TypePtr>{tc->shared_from_this(), instantiateStatic(mn)};\n    auto gf = realizeIRFunc(\n        getFunction(getMangledMethod(CYTHON_MODULE, CYTHON_WRAP, \"wrap_get\"))\n            ->getType(),\n        generics);\n    ir::Func *sf = nullptr;\n    if (!c->ast->hasAttribute(Attr::Tuple))\n      sf = realizeIRFunc(\n          getFunction(getMangledMethod(CYTHON_MODULE, CYTHON_WRAP, \"wrap_set\"))\n              ->getType(),\n          generics);\n    py.getset.push_back({mn, \"\", gf, sf});\n    LOG_USER(\"[py] member -> {} . {}\", name, mn);\n  }\n  return py;\n}\n\nir::PyType TypecheckVisitor::cythonizeIterator(const std::string &name) {\n  LOG_USER(\"[py] iterfn: {}\", name);\n  ir::PyType py{name, \"\"};\n  auto cr = ctx->cache->classes[CYTHON_ITER].realizations[name];\n  auto tc = cr->getType();\n  for (auto &r : getFunction(getMangledMethod(CYTHON_MODULE, CYTHON_WRAP, \"py_type\"))\n                         ->realizations |\n                     std::views::values) {\n    if (extractFuncGeneric(r->getType())->unify(tc, nullptr) >= 0) {\n      py.typePtrHook = r->ir;\n      break;\n    }\n  }\n\n  for (auto &n : std::vector<std::string>{\"_iter\", \"_iternext\"}) {\n    auto fnn = getOverloads(getClass(CYTHON_ITER)->methods[n]).front();\n    TypePtr t;\n    if (n == \"_iter\") {\n      t = instantiateType(getFunction(fnn)->getType(), tc->getClass());\n    } else {\n      auto ut = extractClassGeneric(tc)->getClass();\n      if (!ut)\n        continue;\n      auto fn = findBestMethod(ut, \"__iter__\", std::vector<types::Type *>{ut});\n      if (!fn)\n        continue;\n      auto fnt = realize(instantiateType(fn, ut));\n      if (!fnt)\n        continue;\n      t = instantiateType(getFunction(fnn)->getType(), tc->getClass());\n      unify(extractFuncGeneric(t->getFunc()), fnt->getFunc()->getRetType());\n    }\n    if (auto rtv = realize(t.get())) {\n      auto f =\n          getFunction(rtv->getFunc()->getFuncName())->realizations[rtv->realizedName()];\n      if (n == \"_iter\")\n        py.iter = f->ir;\n      else\n        py.iternext = f->ir;\n    }\n  }\n  py.type = cr->ir;\n  return py;\n}\n\nir::PyFunction TypecheckVisitor::cythonizeFunction(const std::string &name) {\n  if (auto f = getFunction(name); f->isToplevel) {\n    auto fnName = getMangledMethod(CYTHON_MODULE, CYTHON_WRAP, \"wrap_multiple\");\n    auto generics =\n        std::vector<types::TypePtr>{getStdLibType(\"NoneType\")->shared_from_this(),\n                                    instantiateStatic(f->ast->getName()),\n                                    instantiateStatic(static_cast<int64_t>(0))};\n    if (auto ir = realizeIRFunc(getFunction(fnName)->getType(), generics)) {\n      LOG_USER(\"[py] toplevel -> {} ({}): ({})\", getUserFacingName(name), name,\n               f->getType()->debugString(2));\n      ir::PyFunction fn{getUnmangledName(name), f->ast->getDocstr(), ir,\n                        ir::PyFunction::Type::TOPLEVEL,\n                        static_cast<int>(f->ast->size())};\n      fn.keywords = true;\n      return fn;\n    }\n  }\n  return {\"\", \"\"};\n}\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/typecheck/typecheck.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <string>\n#include <tuple>\n#include <unordered_map>\n#include <unordered_set>\n#include <utility>\n#include <vector>\n\n#include \"codon/parser/ast.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/parser/visitors/typecheck/ctx.h\"\n#include \"codon/parser/visitors/visitor.h\"\n\nnamespace codon::ast {\n\n/**\n * Visitor that infers expression types and performs type-guided transformations.\n *\n * -> Note: this stage *modifies* the provided AST. Clone it before simplification\n *    if you need it intact.\n */\nclass TypecheckVisitor : public ReplacingCallbackASTVisitor {\n  /// Shared simplification context.\n  std::shared_ptr<TypeContext> ctx;\n  /// Statements to prepend before the current statement.\n  std::shared_ptr<std::vector<Stmt *>> prependStmts = nullptr;\n  SuiteStmt *preamble = nullptr;\n\n  /// Each new expression is stored here (as @c visit does not return anything) and\n  /// later returned by a @c transform call.\n  Expr *resultExpr = nullptr;\n  /// Each new statement is stored here (as @c visit does not return anything) and\n  /// later returned by a @c transform call.\n  Stmt *resultStmt = nullptr;\n\npublic:\n  // static Stmt * apply(Cache *cache, const Stmt * &stmts);\n  static Stmt *\n  apply(Cache *cache, Stmt *node, const std::string &file,\n        const std::unordered_map<std::string, std::string> &defines = {},\n        const std::unordered_map<std::string, std::string> &earlyDefines = {},\n        bool barebones = false);\n  static Stmt *apply(const std::shared_ptr<TypeContext> &cache, Stmt *node,\n                     const std::string &file = \"<internal>\");\n\nprivate:\n  static void loadStdLibrary(Cache *, SuiteStmt *,\n                             const std::unordered_map<std::string, std::string> &,\n                             bool);\n\npublic:\n  explicit TypecheckVisitor(\n      std::shared_ptr<TypeContext> ctx, SuiteStmt *preamble = nullptr,\n      const std::shared_ptr<std::vector<Stmt *>> &stmts = nullptr);\n\npublic: // Convenience transformators\n  Expr *transform(Expr *e) override;\n  Expr *transform(Expr *expr, bool allowTypes);\n  Stmt *transform(Stmt *s) override;\n  Expr *transformType(Expr *expr, bool simple = false);\n\nprivate:\n  void defaultVisit(Expr *e) override;\n  void defaultVisit(Stmt *s) override;\n\nprivate: // Node typechecking rules\n  /* Basic type expressions (basic.cpp) */\n  void visit(NoneExpr *) override;\n  void visit(BoolExpr *) override;\n  void visit(IntExpr *) override;\n  Expr *transformInt(IntExpr *);\n  void visit(FloatExpr *) override;\n  Expr *transformFloat(FloatExpr *);\n  void visit(StringExpr *) override;\n\n  /* Identifier access expressions (access.cpp) */\n  void visit(IdExpr *) override;\n  void checkCapture(const TypeContext::Item &) const;\n  void visit(DotExpr *) override;\n  std::pair<size_t, TypeContext::Item> getImport(const std::vector<std::string> &);\n  Expr *getClassMember(DotExpr *);\n  types::FuncType *getDispatch(const std::string &);\n\n  /* Collection and comprehension expressions (collections.cpp) */\n  void visit(TupleExpr *) override;\n  void visit(ListExpr *) override;\n  void visit(SetExpr *) override;\n  void visit(DictExpr *) override;\n  Expr *transformComprehension(const std::string &, const std::string &,\n                               std::vector<Expr *> &);\n  void visit(GeneratorExpr *) override;\n\n  /* Conditional expression and statements (cond.cpp) */\n  void visit(RangeExpr *) override;\n  void visit(IfExpr *) override;\n  void visit(IfStmt *) override;\n  void visit(MatchStmt *) override;\n  Stmt *transformPattern(Expr *, Expr *, Stmt *);\n\n  /* Operators (op.cpp) */\n  void visit(UnaryExpr *) override;\n  Expr *evaluateStaticUnary(const UnaryExpr *);\n  void visit(BinaryExpr *) override;\n  Expr *evaluateStaticBinary(const BinaryExpr *);\n  Expr *transformBinarySimple(const BinaryExpr *);\n  Expr *transformBinaryIs(const BinaryExpr *);\n  std::pair<std::string, std::string> getMagic(const std::string &) const;\n  Expr *transformBinaryInplaceMagic(BinaryExpr *, bool);\n  Expr *transformBinaryMagic(const BinaryExpr *);\n  void visit(ChainBinaryExpr *) override;\n  void visit(PipeExpr *) override;\n  void visit(IndexExpr *) override;\n  std::pair<bool, Expr *> transformStaticTupleIndex(types::ClassType *, Expr *, Expr *);\n  int64_t translateIndex(int64_t, int64_t, bool = false) const;\n  int64_t sliceAdjustIndices(int64_t, int64_t *, int64_t *, int64_t) const;\n  void visit(InstantiateExpr *) override;\n  void visit(SliceExpr *) override;\n\n  /* Calls (call.cpp) */\n  void visit(PrintStmt *) override;\n  /// Holds partial call information for a CallExpr.\n  struct PartialCallData {\n    bool isPartial = false;                  // true if the call is partial\n    std::string var;                         // set if calling a partial type itself\n    std::string known;                       // mask of known arguments\n    Expr *args = nullptr, *kwArgs = nullptr; // partial *args/**kwargs expressions\n  };\n  void visit(StarExpr *) override;\n  void visit(KeywordStarExpr *) override;\n  void visit(EllipsisExpr *) override;\n  void visit(CallExpr *) override;\n  static void validateCall(CallExpr *expr);\n  bool transformCallArgs(CallExpr *);\n  std::pair<std::shared_ptr<types::FuncType>, Expr *> getCalleeFn(CallExpr *,\n                                                                  PartialCallData &);\n  Expr *callReorderArguments(types::FuncType *, CallExpr *, PartialCallData &);\n  bool typecheckCallArgs(types::FuncType *, std::vector<CallArg> &,\n                         const PartialCallData &);\n  std::pair<bool, Expr *> transformSpecialCall(CallExpr *);\n  std::vector<types::TypePtr> getStaticSuperTypes(types::ClassType *);\n  std::vector<types::TypePtr> getRTTISuperTypes(types::ClassType *);\n\n  /* Assignments (assign.cpp) */\n  void visit(AssignExpr *) override;\n  void visit(AssignStmt *) override;\n  Stmt *unpackAssignment(Expr *lhs, Expr *rhs);\n  Stmt *transformUpdate(AssignStmt *);\n  Stmt *transformAssignment(AssignStmt *, bool = false);\n  void visit(DelStmt *) override;\n  void visit(AssignMemberStmt *) override;\n  std::pair<bool, Stmt *> transformInplaceUpdate(AssignStmt *);\n\n  /* Imports (import.cpp) */\n  void visit(ImportStmt *) override;\n  Stmt *transformSpecialImport(const ImportStmt *);\n  std::vector<std::string> getImportPath(Expr *, size_t = 0) const;\n  Stmt *transformCImport(const std::string &, const std::vector<Param> &, Expr *,\n                         const std::string &);\n  Stmt *transformCVarImport(const std::string &, Expr *, const std::string &);\n  Stmt *transformCDLLImport(Expr *, const std::string &, const std::vector<Param> &,\n                            Expr *, const std::string &, bool);\n  Stmt *transformPythonImport(Expr *, const std::vector<Param> &, Expr *,\n                              const std::string &);\n  Stmt *transformNewImport(const ImportFile &);\n\n  /* Loops (loops.cpp) */\n  void visit(BreakStmt *) override;\n  void visit(ContinueStmt *) override;\n  void visit(WhileStmt *) override;\n  void visit(ForStmt *) override;\n  Expr *transformForDecorator(Expr *);\n  std::pair<bool, Stmt *> transformStaticForLoop(const ForStmt *);\n\n  /* Errors and exceptions (error.cpp) */\n  void visit(AssertStmt *) override;\n  void visit(TryStmt *) override;\n  void visit(ThrowStmt *) override;\n  void visit(WithStmt *) override;\n\n  /* Functions (function.cpp) */\n  void visit(YieldExpr *) override;\n  void visit(AwaitExpr *) override;\n  void visit(ReturnStmt *) override;\n  void visit(YieldStmt *) override;\n  void visit(YieldFromStmt *) override;\n  void visit(LambdaExpr *) override;\n  void visit(GlobalStmt *) override;\n  void visit(FunctionStmt *) override;\n  Stmt *transformPythonDefinition(const std::string &, const std::vector<Param> &,\n                                  Expr *, Stmt *);\n  Stmt *transformLLVMDefinition(Stmt *);\n  std::tuple<bool, std::string, std::string> getDecorator(Expr *);\n  std::shared_ptr<types::ClassType> getFuncTypeBase(size_t);\n\nprivate:\n  /* Classes (class.cpp) */\n  void visit(ClassStmt *) override;\n  std::vector<types::TypePtr> parseBaseClasses(std::vector<Expr *> &,\n                                               std::vector<Param> &, const Stmt *,\n                                               const std::string &, const Expr *,\n                                               types::ClassType *);\n  bool autoDeduceMembers(ClassStmt *, std::vector<Param> &);\n  static std::vector<Stmt *> getClassMethods(Stmt *s);\n  void transformNestedClasses(const ClassStmt *, std::vector<Stmt *> &,\n                              std::vector<Stmt *> &, std::vector<Stmt *> &);\n  Stmt *codegenMagic(const std::string &, Expr *, const std::vector<Param> &, bool);\n  int generateKwId(const std::vector<std::string> & = {}) const;\n\npublic:\n  types::ClassType *generateTuple(size_t n, bool = true);\n\nprivate:\n  /* The rest (typecheck.cpp) */\n  void visit(SuiteStmt *) override;\n  void visit(ExprStmt *) override;\n  void visit(StmtExpr *) override;\n  void visit(CommentStmt *stmt) override;\n  void visit(CustomStmt *) override;\n  void visit(DirectiveStmt *) override;\n\npublic:\n  /* Type inference (infer.cpp) */\n  types::Type *unify(types::Type *a, types::Type *b) const;\n  types::Type *unify(types::Type *a, types::TypePtr &&b) { return unify(a, b.get()); }\n  types::Type *realize(types::Type *);\n  types::TypePtr &&realize(types::TypePtr &&t) {\n    realize(t.get());\n    return std::move(t);\n  }\n\nprivate:\n  Stmt *inferTypes(Stmt *, bool isToplevel = false);\n  types::Type *realizeFunc(types::FuncType *, bool = false);\n  types::Type *realizeType(types::ClassType *);\n  SuiteStmt *generateSpecialAst(types::FuncType *);\n  codon::ir::types::Type *makeIRType(types::ClassType *);\n  codon::ir::Func *\n  makeIRFunction(const std::shared_ptr<Cache::Function::FunctionRealization> &);\n\nprivate:\n  types::FuncType *findBestMethod(types::ClassType *typ, const std::string &member,\n                                  const std::vector<types::Type *> &args);\n  types::FuncType *findBestMethod(types::ClassType *typ, const std::string &member,\n                                  const std::vector<Expr *> &args);\n  types::FuncType *\n  findBestMethod(types::ClassType *typ, const std::string &member,\n                 const std::vector<std::pair<std::string, types::Type *>> &args);\n  int canCall(types::FuncType *, const std::vector<CallArg> &,\n              types::ClassType * = nullptr);\n  std::vector<types::FuncType *> findMatchingMethods(\n      types::ClassType *typ, const std::vector<types::FuncType *> &methods,\n      const std::vector<CallArg> &args, types::ClassType *part = nullptr);\n  Expr *castToSuperClass(Expr *expr, types::ClassType *superTyp, bool = false);\n  void prepareVTables();\n  std::vector<std::pair<std::string, Expr *>> extractNamedTuple(Expr *);\n  std::vector<types::TypePtr> getClassFieldTypes(types::ClassType *);\n  static std::vector<std::pair<size_t, Expr *>> findEllipsis(Expr *);\n\npublic:\n  bool wrapExpr(Expr **expr, types::Type *expectedType,\n                types::FuncType *callee = nullptr, bool allowUnwrap = true);\n  std::tuple<bool, types::TypePtr, std::function<Expr *(Expr *)>>\n  canWrapExpr(types::Type *exprType, types::Type *expectedType,\n              types::FuncType *callee = nullptr, bool allowUnwrap = true,\n              bool isEllipsis = false);\n  std::vector<Cache::Class::ClassField> getClassFields(types::ClassType *) const;\n  std::shared_ptr<TypeContext> getCtx() const { return ctx; }\n  Expr *generatePartialCall(const std::string &, types::FuncType *, Expr * = nullptr,\n                            Expr * = nullptr);\n\n  friend struct Cache;\n  friend struct TypeContext;\n  friend class types::CallableTrait;\n  friend class types::UnionType;\n\nprivate: // Helpers\n  std::shared_ptr<std::vector<std::pair<std::string, types::Type *>>>\n  unpackTupleTypes(const Expr *);\n  std::tuple<bool, bool, Stmt *, std::vector<ASTNode *>>\n  transformStaticLoopCall(Expr *, SuiteStmt **, Expr *,\n                          const std::function<ASTNode *(Stmt *)> &, bool = false);\n\npublic:\n  template <typename Tn, typename... Ts> Tn *N(Ts &&...args) {\n    Tn *t = ctx->cache->N<Tn>(std::forward<Ts>(args)...);\n    t->setSrcInfo(getSrcInfo());\n    if (cast<Stmt>(t) && getTime())\n      t->setAttribute(Attr::ExprTime, getTime());\n    return t;\n  }\n  template <typename Tn, typename... Ts> Tn *NC(Ts &&...args) {\n    Tn *t = ctx->cache->N<Tn>(std::forward<Ts>(args)...);\n    return t;\n  }\n\nprivate:\n  template <typename... Ts> void log(const std::string &prefix, Ts &&...args) {\n    fmt::print(codon::getLogger().log, fmt::runtime(\"[{}] [{}${}]: \" + prefix + \"\\n\"),\n               ctx->getSrcInfo(), ctx->getBaseName(), ctx->getBase()->iteration,\n               std::forward<Ts>(args)...);\n  }\n  template <typename... Ts>\n  void logfile(const std::string &file, const std::string &prefix, Ts &&...args) {\n    if (in(ctx->getSrcInfo().file, file))\n      fmt::print(codon::getLogger().log, fmt::runtime(\"[{}] [{}${}]: \" + prefix + \"\\n\"),\n                 ctx->getSrcInfo(), ctx->getBaseName(), ctx->getBase()->iteration,\n                 std::forward<Ts>(args)...);\n  }\n\npublic:\n  types::Type *extractType(types::Type *t) const;\n  types::Type *extractType(Expr *e) const;\n  types::Type *extractType(const std::string &) const;\n  types::ClassType *extractClassType(Expr *e) const;\n  types::ClassType *extractClassType(types::Type *t) const;\n  types::ClassType *extractClassType(const std::string &s) const;\n  static bool isUnbound(types::Type *t);\n  static bool isUnbound(const Expr *e);\n  bool hasOverloads(const std::string &root) const;\n  std::vector<std::string> getOverloads(const std::string &root) const;\n  std::string getUnmangledName(const std::string &s) const;\n  std::string getUserFacingName(const std::string &s) const;\n  Cache::Class *getClass(const std::string &t) const;\n  Cache::Class *getClass(types::Type *t) const;\n  Cache::Function *getFunction(const std::string &n) const;\n  Cache::Function *getFunction(types::Type *t) const;\n  Cache::Class::ClassRealization *getClassRealization(types::Type *t) const;\n  std::string getRootName(const types::FuncType *t) const;\n  static bool isTypeExpr(const Expr *e);\n  Cache::Module *getImport(const std::string &s) const;\n  static bool isDispatch(const std::string &s);\n  static bool isDispatch(const FunctionStmt *ast);\n  static bool isDispatch(types::Type *f);\n  bool isHeterogenous(types::Type *);\n  void addClassGenerics(types::ClassType *typ, bool func = false,\n                        bool onlyMangled = false, bool instantiate = false);\n  template <typename F>\n  auto withClassGenerics(types::ClassType *typ, F fn, bool func = false,\n                         bool onlyMangled = false, bool instantiate = false) {\n    ctx->addBlock();\n    addClassGenerics(typ, func, onlyMangled, instantiate);\n    auto t = fn();\n    ctx->popBlock();\n    return t;\n  }\n  types::TypePtr instantiateTypeVar(types::Type *t);\n  void registerGlobal(const std::string &s) const;\n  types::ClassType *getStdLibType(const std::string &type) const;\n  types::Type *extractClassGeneric(types::Type *t, size_t idx = 0) const;\n  types::Type *extractFuncGeneric(types::Type *t, size_t idx = 0) const;\n  types::Type *extractFuncArgType(types::Type *t, size_t idx = 0) const;\n  std::string getClassMethod(types::Type *typ, const std::string &member) const;\n  std::string getTemporaryVar(const std::string &s) const;\n  static bool isImportFn(const std::string &s);\n  int64_t getTime() const;\n  types::Type *getUnderlyingStaticType(types::Type *t) const;\n\n  int64_t getIntLiteral(types::Type *t, size_t pos = 0) const;\n  bool getBoolLiteral(types::Type *t, size_t pos = 0) const;\n  std::string getStrLiteral(types::Type *t, size_t pos = 0) const;\n  Expr *getParamType(types::Type *t);\n  static bool hasSideEffect(Expr *);\n  static Expr *getHeadExpr(Expr *e);\n\n  Expr *transformNamedTuple(CallExpr *);\n  Expr *transformFunctoolsPartial(CallExpr *);\n  Expr *transformSuperF(CallExpr *);\n  Expr *transformSuper();\n  Expr *transformPtr(CallExpr *);\n  Expr *transformArray(CallExpr *);\n  Expr *transformIsInstance(CallExpr *);\n  Expr *transformStaticLen(CallExpr *);\n  Expr *transformHasAttr(CallExpr *);\n  Expr *transformGetAttr(CallExpr *);\n  Expr *transformSetAttr(CallExpr *);\n  Expr *transformCompileError(CallExpr *) const;\n  Expr *transformTupleFn(CallExpr *);\n  Expr *transformTypeFn(CallExpr *);\n  Expr *transformRealizedFn(CallExpr *);\n  Expr *transformStaticPrintFn(CallExpr *) const;\n  Expr *transformHasRttiFn(const CallExpr *);\n  Expr *transformStaticFnCanCall(CallExpr *);\n  Expr *transformStaticFnArgHasType(CallExpr *);\n  Expr *transformStaticFnArgGetType(CallExpr *);\n  Expr *transformStaticFnArgs(CallExpr *);\n  Expr *transformStaticFnHasDefault(CallExpr *);\n  Expr *transformStaticFnGetDefault(CallExpr *);\n  Expr *transformStaticFnWrapCallArgs(CallExpr *);\n  Expr *transformStaticVars(CallExpr *);\n  Expr *transformStaticTupleType(const CallExpr *);\n  Expr *transformStaticFormat(CallExpr *);\n  Expr *transformStaticIntToStr(CallExpr *);\n  SuiteStmt *generateClassPopulateVTablesAST();\n  SuiteStmt *generateBaseDerivedDistAST(types::FuncType *);\n  FunctionStmt *generateThunkAST(const types::FuncType *fp, types::ClassType *base,\n                                 const types::ClassType *derived);\n  SuiteStmt *generateGetThunkIDAst(types::FuncType *);\n  SuiteStmt *generateFunctionCallInternalAST(types::FuncType *);\n  SuiteStmt *generateUnionNewAST(const types::FuncType *);\n  SuiteStmt *generateUnionTagAST(types::FuncType *);\n  SuiteStmt *generateNamedKeysAST(types::FuncType *);\n  SuiteStmt *generateTupleMulAST(types::FuncType *);\n  std::vector<Stmt *> populateStaticTupleLoop(Expr *, const std::vector<std::string> &);\n  std::vector<Stmt *> populateSimpleStaticRangeLoop(Expr *,\n                                                    const std::vector<std::string> &);\n  std::vector<Stmt *> populateStaticRangeLoop(Expr *, const std::vector<std::string> &);\n  std::vector<Stmt *> populateStaticFnOverloadsLoop(Expr *,\n                                                    const std::vector<std::string> &);\n  std::vector<Stmt *> populateStaticEnumerateLoop(Expr *,\n                                                  const std::vector<std::string> &);\n  std::vector<Stmt *> populateStaticVarsLoop(Expr *, const std::vector<std::string> &);\n  std::vector<Stmt *> populateStaticVarTypesLoop(Expr *,\n                                                 const std::vector<std::string> &);\n  std::vector<Stmt *>\n  populateStaticHeterogenousTupleLoop(Expr *, const std::vector<std::string> &);\n  ParserErrors findTypecheckErrors(Stmt *n) const;\n\npublic:\n  /// Create an unbound type with the provided typechecking level.\n  std::shared_ptr<types::LinkType> instantiateUnbound(const SrcInfo &info,\n                                                      int level) const;\n  std::shared_ptr<types::LinkType> instantiateUnbound(const SrcInfo &info) const;\n  std::shared_ptr<types::LinkType> instantiateUnbound() const;\n\n  /// Call `type->instantiate`.\n  /// Prepare the generic instantiation table with the given a generic parameter.\n  /// Example: when instantiating List[T].foo, generics=List[int].foo will ensure that\n  ///          T=int.\n  types::TypePtr instantiateType(const SrcInfo &info, types::Type *type,\n                                 types::ClassType *generics = nullptr) const;\n  types::TypePtr instantiateType(const SrcInfo &info, types::Type *root,\n                                 const std::vector<types::Type *> &generics) const;\n  template <typename T>\n  std::shared_ptr<T> instantiateType(T *type, types::ClassType *generics = nullptr) {\n    return std::static_pointer_cast<T>(\n        instantiateType(getSrcInfo(), std::move(type), generics));\n  }\n  template <typename T>\n  std::shared_ptr<T> instantiateType(T *root,\n                                     const std::vector<types::Type *> &generics) {\n    return std::static_pointer_cast<T>(\n        instantiateType(getSrcInfo(), std::move(root), generics));\n  }\n  std::shared_ptr<types::IntStaticType> instantiateStatic(int64_t i) const {\n    return std::make_shared<types::IntStaticType>(ctx->cache, i);\n  }\n  std::shared_ptr<types::StrStaticType> instantiateStatic(const std::string &s) const {\n    return std::make_shared<types::StrStaticType>(ctx->cache, s);\n  }\n  std::shared_ptr<types::BoolStaticType> instantiateStatic(bool i) const {\n    return std::make_shared<types::BoolStaticType>(ctx->cache, i);\n  }\n\n  /// Returns the list of generic methods that correspond to typeName.method.\n  std::vector<types::FuncType *> findMethod(types::ClassType *type,\n                                            const std::string &method,\n                                            bool hideShadowed = true);\n  /// Returns the generic type of typeName.member, if it exists (nullptr otherwise).\n  /// Special cases: __elemsize__ and __atomic__.\n  Cache::Class::ClassField *findMember(types::ClassType *, const std::string &) const;\n\n  using ReorderDoneFn =\n      std::function<int(int, int, const std::vector<std::vector<int>> &, bool)>;\n  using ReorderErrorFn = std::function<int(error::Error, const SrcInfo &, std::string)>;\n  /// Reorders a given vector or named arguments (consisting of names and the\n  /// corresponding types) according to the signature of a given function.\n  /// Returns the reordered vector and an associated reordering score (missing\n  /// default arguments' score is half of the present arguments).\n  /// Score is -1 if the given arguments cannot be reordered.\n  /// @param known Bitmask that indicated if an argument is already provided\n  ///              (partial function) or not.\n  int reorderNamedArgs(const types::FuncType *func, const std::vector<CallArg> &args,\n                       const ReorderDoneFn &onDone, const ReorderErrorFn &onError,\n                       const std::string &known = \"\") const;\n\n  static bool isCanonicalName(const std::string &name);\n  static types::FuncType *extractFunction(types::Type *t);\n\n  ir::PyType cythonizeClass(const std::string &name);\n  ir::PyType cythonizeIterator(const std::string &name);\n  ir::PyFunction cythonizeFunction(const std::string &name);\n  ir::Func *realizeIRFunc(types::FuncType *fn,\n                          const std::vector<types::TypePtr> &generics = {});\n  // types::Type *getType(const std::string &);\n};\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/visitor.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"visitor.h\"\n\n#include \"codon/parser/ast.h\"\n\nnamespace codon::ast {\n\nvoid ASTVisitor::defaultVisit(Expr *expr) {}\nvoid ASTVisitor::defaultVisit(Stmt *stmt) {}\n\nvoid ASTVisitor::visit(NoneExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(BoolExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(IntExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(FloatExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(StringExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(IdExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(StarExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(KeywordStarExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(TupleExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(ListExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(SetExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(DictExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(GeneratorExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(IfExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(UnaryExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(BinaryExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(ChainBinaryExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(PipeExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(IndexExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(CallExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(DotExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(SliceExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(EllipsisExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(LambdaExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(YieldExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(AwaitExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(AssignExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(RangeExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(InstantiateExpr *expr) { defaultVisit(expr); }\nvoid ASTVisitor::visit(StmtExpr *expr) { defaultVisit(expr); }\n\nvoid ASTVisitor::visit(SuiteStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(BreakStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(ContinueStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(ExprStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(AssignStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(AssignMemberStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(DelStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(PrintStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(ReturnStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(YieldStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(AssertStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(WhileStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(ForStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(IfStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(MatchStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(ImportStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(TryStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(ExceptStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(GlobalStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(ThrowStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(FunctionStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(ClassStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(YieldFromStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(WithStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(CustomStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(DirectiveStmt *stmt) { defaultVisit(stmt); }\nvoid ASTVisitor::visit(CommentStmt *stmt) { defaultVisit(stmt); }\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/parser/visitors/visitor.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <vector>\n\n#include \"codon/parser/ast.h\"\n\n#include <ranges>\n\nnamespace codon::ast {\n\n/**\n * Base Seq AST visitor.\n * Each visit() by default calls an appropriate defaultVisit().\n */\nstruct ASTVisitor {\n  virtual ~ASTVisitor() {}\n\nprotected:\n  /// Default expression node visitor if a particular visitor is not overloaded.\n  virtual void defaultVisit(Expr *expr);\n  /// Default statement node visitor if a particular visitor is not overloaded.\n  virtual void defaultVisit(Stmt *stmt);\n\npublic:\n  virtual void visit(NoneExpr *);\n  virtual void visit(BoolExpr *);\n  virtual void visit(IntExpr *);\n  virtual void visit(FloatExpr *);\n  virtual void visit(StringExpr *);\n  virtual void visit(IdExpr *);\n  virtual void visit(StarExpr *);\n  virtual void visit(KeywordStarExpr *);\n  virtual void visit(TupleExpr *);\n  virtual void visit(ListExpr *);\n  virtual void visit(SetExpr *);\n  virtual void visit(DictExpr *);\n  virtual void visit(GeneratorExpr *);\n  virtual void visit(IfExpr *);\n  virtual void visit(UnaryExpr *);\n  virtual void visit(BinaryExpr *);\n  virtual void visit(ChainBinaryExpr *);\n  virtual void visit(PipeExpr *);\n  virtual void visit(IndexExpr *);\n  virtual void visit(CallExpr *);\n  virtual void visit(DotExpr *);\n  virtual void visit(SliceExpr *);\n  virtual void visit(EllipsisExpr *);\n  virtual void visit(LambdaExpr *);\n  virtual void visit(YieldExpr *);\n  virtual void visit(AwaitExpr *);\n  virtual void visit(AssignExpr *);\n  virtual void visit(RangeExpr *);\n  virtual void visit(InstantiateExpr *);\n  virtual void visit(StmtExpr *);\n\n  virtual void visit(AssignMemberStmt *);\n  virtual void visit(SuiteStmt *);\n  virtual void visit(BreakStmt *);\n  virtual void visit(ContinueStmt *);\n  virtual void visit(ExprStmt *);\n  virtual void visit(AssignStmt *);\n  virtual void visit(DelStmt *);\n  virtual void visit(PrintStmt *);\n  virtual void visit(ReturnStmt *);\n  virtual void visit(YieldStmt *);\n  virtual void visit(AssertStmt *);\n  virtual void visit(WhileStmt *);\n  virtual void visit(ForStmt *);\n  virtual void visit(IfStmt *);\n  virtual void visit(MatchStmt *);\n  virtual void visit(ImportStmt *);\n  virtual void visit(TryStmt *);\n  virtual void visit(ExceptStmt *);\n  virtual void visit(GlobalStmt *);\n  virtual void visit(ThrowStmt *);\n  virtual void visit(FunctionStmt *);\n  virtual void visit(ClassStmt *);\n  virtual void visit(YieldFromStmt *);\n  virtual void visit(WithStmt *);\n  virtual void visit(CustomStmt *);\n  virtual void visit(DirectiveStmt *);\n  virtual void visit(CommentStmt *);\n};\n\n/**\n * Callback AST visitor.\n * This visitor extends base ASTVisitor and stores node's source location (SrcObject).\n * Function transform() will visit a node and return the appropriate transformation. As\n * each node type (expression or statement) might return a different type,\n * this visitor is generic for each different return type.\n */\ntemplate <typename TE, typename TS>\nstruct CallbackASTVisitor : public ASTVisitor, public SrcObject {\n  virtual TE transform(Expr *expr) = 0;\n  virtual TS transform(Stmt *stmt) = 0;\n\n  /// Convenience method that transforms a vector of nodes.\n  template <typename T> auto transform(const std::vector<T> &ts) {\n    std::vector<T> r;\n    for (auto &e : ts)\n      r.push_back(transform(e));\n    return r;\n  }\n\npublic:\n  void visit(NoneExpr *expr) override {}\n  void visit(BoolExpr *expr) override {}\n  void visit(IntExpr *expr) override {}\n  void visit(FloatExpr *expr) override {}\n  void visit(StringExpr *expr) override {}\n  void visit(IdExpr *expr) override {}\n  void visit(StarExpr *expr) override { transform(expr->expr); }\n  void visit(KeywordStarExpr *expr) override { transform(expr->expr); }\n  void visit(TupleExpr *expr) override {\n    for (auto &i : expr->items)\n      transform(i);\n  }\n  void visit(ListExpr *expr) override {\n    for (auto &i : expr->items)\n      transform(i);\n  }\n  void visit(SetExpr *expr) override {\n    for (auto &i : expr->items)\n      transform(i);\n  }\n  void visit(DictExpr *expr) override {\n    for (auto &i : expr->items)\n      transform(i);\n  }\n  void visit(GeneratorExpr *expr) override { transform(expr->loops); }\n  void visit(IfExpr *expr) override {\n    transform(expr->cond);\n    transform(expr->ifexpr);\n    transform(expr->elsexpr);\n  }\n  void visit(UnaryExpr *expr) override { transform(expr->expr); }\n  void visit(BinaryExpr *expr) override {\n    transform(expr->lexpr);\n    transform(expr->rexpr);\n  }\n  void visit(ChainBinaryExpr *expr) override {\n    for (auto &val : expr->exprs | std::views::values)\n      transform(val);\n  }\n  void visit(PipeExpr *expr) override {\n    for (auto &e : expr->items)\n      transform(e.expr);\n  }\n  void visit(IndexExpr *expr) override {\n    transform(expr->expr);\n    transform(expr->index);\n  }\n  void visit(CallExpr *expr) override {\n    transform(expr->expr);\n    for (auto &a : expr->items)\n      transform(a.value);\n  }\n  void visit(DotExpr *expr) override { transform(expr->expr); }\n  void visit(SliceExpr *expr) override {\n    transform(expr->start);\n    transform(expr->stop);\n    transform(expr->step);\n  }\n  void visit(EllipsisExpr *expr) override {}\n  void visit(LambdaExpr *expr) override {\n    for (auto &a : expr->items) {\n      transform(a.type);\n      transform(a.defaultValue);\n    }\n    transform(expr->expr);\n  }\n  void visit(YieldExpr *expr) override {}\n  void visit(AwaitExpr *expr) override { transform(expr->expr); }\n  void visit(AssignExpr *expr) override {\n    transform(expr->var);\n    transform(expr->expr);\n  }\n  void visit(RangeExpr *expr) override {\n    transform(expr->start);\n    transform(expr->stop);\n  }\n  void visit(InstantiateExpr *expr) override {\n    transform(expr->expr);\n    for (auto &e : expr->items)\n      transform(e);\n  }\n  void visit(StmtExpr *expr) override {\n    for (auto &s : expr->items)\n      transform(s);\n    transform(expr->expr);\n  }\n  void visit(SuiteStmt *stmt) override {\n    for (auto &s : stmt->items)\n      transform(s);\n  }\n  void visit(BreakStmt *stmt) override {}\n  void visit(ContinueStmt *stmt) override {}\n  void visit(ExprStmt *stmt) override { transform(stmt->expr); }\n  void visit(AssignStmt *stmt) override {\n    transform(stmt->lhs);\n    transform(stmt->rhs);\n    transform(stmt->type);\n  }\n  void visit(AssignMemberStmt *stmt) override {\n    transform(stmt->lhs);\n    transform(stmt->rhs);\n    transform(stmt->type);\n  }\n  void visit(DelStmt *stmt) override { transform(stmt->expr); }\n  void visit(PrintStmt *stmt) override {\n    for (auto &e : stmt->items)\n      transform(e);\n  }\n  void visit(ReturnStmt *stmt) override { transform(stmt->expr); }\n  void visit(YieldStmt *stmt) override { transform(stmt->expr); }\n  void visit(AssertStmt *stmt) override {\n    transform(stmt->expr);\n    transform(stmt->message);\n  }\n  void visit(WhileStmt *stmt) override {\n    transform(stmt->cond);\n    transform(stmt->suite);\n    transform(stmt->elseSuite);\n  }\n  void visit(ForStmt *stmt) override {\n    transform(stmt->var);\n    transform(stmt->iter);\n    transform(stmt->suite);\n    transform(stmt->elseSuite);\n    transform(stmt->decorator);\n    for (auto &a : stmt->ompArgs)\n      transform(a.value);\n  }\n  void visit(IfStmt *stmt) override {\n    transform(stmt->cond);\n    transform(stmt->ifSuite);\n    transform(stmt->elseSuite);\n  }\n  void visit(MatchStmt *stmt) override {\n    transform(stmt->expr);\n    for (auto &m : stmt->items) {\n      transform(m.pattern);\n      transform(m.guard);\n      transform(m.suite);\n    }\n  }\n  void visit(ImportStmt *stmt) override {\n    transform(stmt->from);\n    transform(stmt->what);\n    for (auto &a : stmt->args) {\n      transform(a.type);\n      transform(a.defaultValue);\n    }\n    transform(stmt->ret);\n  }\n  void visit(TryStmt *stmt) override {\n    transform(stmt->suite);\n    for (auto &a : stmt->items)\n      transform(a);\n    transform(stmt->elseSuite);\n    transform(stmt->finally);\n  }\n  void visit(ExceptStmt *stmt) override {\n    transform(stmt->exc);\n    transform(stmt->suite);\n  }\n  void visit(GlobalStmt *stmt) override {}\n  void visit(ThrowStmt *stmt) override {\n    transform(stmt->expr);\n    transform(stmt->from);\n  }\n  void visit(FunctionStmt *stmt) override {\n    transform(stmt->ret);\n    for (auto &a : stmt->items) {\n      transform(a.type);\n      transform(a.defaultValue);\n    }\n    transform(stmt->suite);\n    for (auto &d : stmt->decorators)\n      transform(d);\n  }\n  void visit(ClassStmt *stmt) override {\n    for (auto &a : stmt->items) {\n      transform(a.type);\n      transform(a.defaultValue);\n    }\n    transform(stmt->suite);\n    for (auto &d : stmt->decorators)\n      transform(d);\n    for (auto &d : stmt->baseClasses)\n      transform(d);\n    for (auto &d : stmt->staticBaseClasses)\n      transform(d);\n  }\n  void visit(YieldFromStmt *stmt) override { transform(stmt->expr); }\n  void visit(WithStmt *stmt) override {\n    for (auto &a : stmt->items)\n      transform(a);\n    transform(stmt->suite);\n  }\n  void visit(CustomStmt *stmt) override {\n    transform(stmt->expr);\n    transform(stmt->suite);\n  }\n};\n\n/**\n * Callback AST visitor.\n * This visitor extends base ASTVisitor and stores node's source location (SrcObject).\n * Function transform() will visit a node and return the appropriate transformation. As\n * each node type (expression or statement) might return a different type,\n * this visitor is generic for each different return type.\n */\nstruct ReplacingCallbackASTVisitor : public CallbackASTVisitor<Expr *, Stmt *> {\npublic:\n  void visit(StarExpr *expr) override { expr->expr = transform(expr->expr); }\n  void visit(KeywordStarExpr *expr) override { expr->expr = transform(expr->expr); }\n  void visit(TupleExpr *expr) override {\n    for (auto &i : expr->items)\n      i = transform(i);\n  }\n  void visit(ListExpr *expr) override {\n    for (auto &i : expr->items)\n      i = transform(i);\n  }\n  void visit(SetExpr *expr) override {\n    for (auto &i : expr->items)\n      i = transform(i);\n  }\n  void visit(DictExpr *expr) override {\n    for (auto &i : expr->items)\n      i = transform(i);\n  }\n  void visit(GeneratorExpr *expr) override { expr->loops = transform(expr->loops); }\n  void visit(IfExpr *expr) override {\n    expr->cond = transform(expr->cond);\n    expr->ifexpr = transform(expr->ifexpr);\n    expr->elsexpr = transform(expr->elsexpr);\n  }\n  void visit(UnaryExpr *expr) override { expr->expr = transform(expr->expr); }\n  void visit(BinaryExpr *expr) override {\n    expr->lexpr = transform(expr->lexpr);\n    expr->rexpr = transform(expr->rexpr);\n  }\n  void visit(ChainBinaryExpr *expr) override {\n    for (auto &val : expr->exprs | std::views::values)\n      val = transform(val);\n  }\n  void visit(PipeExpr *expr) override {\n    for (auto &e : expr->items)\n      e.expr = transform(e.expr);\n  }\n  void visit(IndexExpr *expr) override {\n    expr->expr = transform(expr->expr);\n    expr->index = transform(expr->index);\n  }\n  void visit(CallExpr *expr) override {\n    expr->expr = transform(expr->expr);\n    for (auto &a : expr->items)\n      a.value = transform(a.value);\n  }\n  void visit(DotExpr *expr) override { expr->expr = transform(expr->expr); }\n  void visit(SliceExpr *expr) override {\n    expr->start = transform(expr->start);\n    expr->stop = transform(expr->stop);\n    expr->step = transform(expr->step);\n  }\n  void visit(EllipsisExpr *expr) override {}\n  void visit(LambdaExpr *expr) override {\n    for (auto &a : expr->items) {\n      a.type = transform(a.type);\n      a.defaultValue = transform(a.defaultValue);\n    }\n    expr->expr = transform(expr->expr);\n  }\n  void visit(YieldExpr *expr) override {}\n  void visit(AwaitExpr *expr) override { expr->expr = transform(expr->expr); }\n  void visit(AssignExpr *expr) override {\n    expr->var = transform(expr->var);\n    expr->expr = transform(expr->expr);\n  }\n  void visit(RangeExpr *expr) override {\n    expr->start = transform(expr->start);\n    expr->stop = transform(expr->stop);\n  }\n  void visit(InstantiateExpr *expr) override {\n    expr->expr = transform(expr->expr);\n    for (auto &e : expr->items)\n      e = transform(e);\n  }\n  void visit(StmtExpr *expr) override {\n    for (auto &s : expr->items)\n      s = transform(s);\n    expr->expr = transform(expr->expr);\n  }\n  void visit(SuiteStmt *stmt) override {\n    for (auto &s : stmt->items)\n      s = transform(s);\n  }\n  void visit(ExprStmt *stmt) override { stmt->expr = transform(stmt->expr); }\n  void visit(AssignStmt *stmt) override {\n    stmt->lhs = transform(stmt->lhs);\n    stmt->rhs = transform(stmt->rhs);\n    stmt->type = transform(stmt->type);\n  }\n  void visit(AssignMemberStmt *stmt) override {\n    stmt->lhs = transform(stmt->lhs);\n    stmt->rhs = transform(stmt->rhs);\n    stmt->type = transform(stmt->type);\n  }\n  void visit(DelStmt *stmt) override { stmt->expr = transform(stmt->expr); }\n  void visit(PrintStmt *stmt) override {\n    for (auto &e : stmt->items)\n      e = transform(e);\n  }\n  void visit(ReturnStmt *stmt) override { stmt->expr = transform(stmt->expr); }\n  void visit(YieldStmt *stmt) override { stmt->expr = transform(stmt->expr); }\n  void visit(AssertStmt *stmt) override {\n    stmt->expr = transform(stmt->expr);\n    stmt->message = transform(stmt->message);\n  }\n  void visit(WhileStmt *stmt) override {\n    stmt->cond = transform(stmt->cond);\n    stmt->suite = SuiteStmt::wrap(transform(stmt->suite));\n    stmt->elseSuite = SuiteStmt::wrap(transform(stmt->elseSuite));\n  }\n  void visit(ForStmt *stmt) override {\n    stmt->var = transform(stmt->var);\n    stmt->iter = transform(stmt->iter);\n    stmt->suite = SuiteStmt::wrap(transform(stmt->suite));\n    stmt->elseSuite = SuiteStmt::wrap(transform(stmt->elseSuite));\n    stmt->decorator = transform(stmt->decorator);\n    for (auto &a : stmt->ompArgs)\n      a.value = transform(a.value);\n  }\n  void visit(IfStmt *stmt) override {\n    stmt->cond = transform(stmt->cond);\n    stmt->ifSuite = SuiteStmt::wrap(transform(stmt->ifSuite));\n    stmt->elseSuite = SuiteStmt::wrap(transform(stmt->elseSuite));\n  }\n  void visit(MatchStmt *stmt) override {\n    stmt->expr = transform(stmt->expr);\n    for (auto &m : stmt->items) {\n      m.pattern = transform(m.pattern);\n      m.guard = transform(m.guard);\n      m.suite = SuiteStmt::wrap(transform(m.suite));\n    }\n  }\n  void visit(ImportStmt *stmt) override {\n    stmt->from = transform(stmt->from);\n    stmt->what = transform(stmt->what);\n    for (auto &a : stmt->args) {\n      a.type = transform(a.type);\n      a.defaultValue = transform(a.defaultValue);\n    }\n    stmt->ret = transform(stmt->ret);\n  }\n  void visit(TryStmt *stmt) override {\n    stmt->suite = SuiteStmt::wrap(transform(stmt->suite));\n    for (auto &a : stmt->items)\n      a = static_cast<ExceptStmt *>(transform(a));\n    stmt->elseSuite = SuiteStmt::wrap(transform(stmt->elseSuite));\n    stmt->finally = SuiteStmt::wrap(transform(stmt->finally));\n  }\n  void visit(ExceptStmt *stmt) override {\n    stmt->exc = transform(stmt->exc);\n    stmt->suite = SuiteStmt::wrap(transform(stmt->suite));\n  }\n  void visit(GlobalStmt *stmt) override {}\n  void visit(ThrowStmt *stmt) override {\n    stmt->expr = transform(stmt->expr);\n    stmt->from = transform(stmt->from);\n  }\n  void visit(FunctionStmt *stmt) override {\n    stmt->ret = transform(stmt->ret);\n    for (auto &a : stmt->items) {\n      a.type = transform(a.type);\n      a.defaultValue = transform(a.defaultValue);\n    }\n    stmt->suite = SuiteStmt::wrap(transform(stmt->suite));\n    for (auto &d : stmt->decorators)\n      d = transform(d);\n  }\n  void visit(ClassStmt *stmt) override {\n    for (auto &a : stmt->items) {\n      a.type = transform(a.type);\n      a.defaultValue = transform(a.defaultValue);\n    }\n    stmt->suite = SuiteStmt::wrap(transform(stmt->suite));\n    for (auto &d : stmt->decorators)\n      d = transform(d);\n    for (auto &d : stmt->baseClasses)\n      d = transform(d);\n    for (auto &d : stmt->staticBaseClasses)\n      d = transform(d);\n  }\n  void visit(YieldFromStmt *stmt) override { stmt->expr = transform(stmt->expr); }\n  void visit(WithStmt *stmt) override {\n    for (auto &a : stmt->items)\n      a = transform(a);\n    stmt->suite = SuiteStmt::wrap(transform(stmt->suite));\n  }\n  void visit(CustomStmt *stmt) override {\n    stmt->expr = transform(stmt->expr);\n    stmt->suite = SuiteStmt::wrap(transform(stmt->suite));\n  }\n};\n\n} // namespace codon::ast\n"
  },
  {
    "path": "codon/runtime/exc.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"codon/runtime/lib.h\"\n#include \"llvm/BinaryFormat/Dwarf.h\"\n#include <backtrace.h>\n#include <cassert>\n#include <cstdint>\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n#include <iostream>\n#include <mutex>\n#include <sstream>\n#include <string>\n#include <vector>\n\n#if defined(__APPLE__) && (__arm64__ || __aarch64__)\n#define APPLE_SILICON\n#endif\n\n#ifdef APPLE_SILICON\n#include \"llvm/BinaryFormat/MachO.h\"\n#include <dlfcn.h>\n\n// https://github.com/llvm/llvm-project/issues/49036\n// Define a minimal mach header for JIT'd code.\nstatic llvm::MachO::mach_header_64 fake_mach_header = {\n    .magic = llvm::MachO::MH_MAGIC_64,\n    .cputype = llvm::MachO::CPU_TYPE_ARM64,\n    .cpusubtype = llvm::MachO::CPU_SUBTYPE_ARM64_ALL,\n    .filetype = llvm::MachO::MH_DYLIB,\n    .ncmds = 0,\n    .sizeofcmds = 0,\n    .flags = 0,\n    .reserved = 0};\n\n// Declare libunwind SPI types and functions.\nstruct unw_dynamic_unwind_sections {\n  uintptr_t dso_base;\n  uintptr_t dwarf_section;\n  size_t dwarf_section_length;\n  uintptr_t compact_unwind_section;\n  size_t compact_unwind_section_length;\n};\n\nint find_dynamic_unwind_sections(uintptr_t addr, unw_dynamic_unwind_sections *info) {\n  info->dso_base = (uintptr_t)&fake_mach_header;\n  info->dwarf_section = 0;\n  info->dwarf_section_length = 0;\n  info->compact_unwind_section = 0;\n  info->compact_unwind_section_length = 0;\n  return 1;\n}\n\n// Typedef for callback above.\ntypedef int (*unw_find_dynamic_unwind_sections)(\n    uintptr_t addr, struct unw_dynamic_unwind_sections *info);\n#endif\n\nstruct BacktraceFrame {\n  char *function;\n  char *filename;\n  uintptr_t pc;\n  int32_t lineno;\n};\n\nstruct Backtrace {\n  static const size_t LIMIT = 20;\n  struct BacktraceFrame *frames;\n  size_t count;\n\n  void push_back(const char *function, const char *filename, uintptr_t pc,\n                 int32_t lineno) {\n    if (count >= LIMIT || !function || !filename) {\n      return;\n    } else if (count == 0) {\n      frames = (BacktraceFrame *)seq_alloc(LIMIT * sizeof(*frames));\n    }\n\n    size_t functionLen = strlen(function) + 1;\n    auto *functionDup = (char *)seq_alloc_atomic(functionLen);\n    memcpy(functionDup, function, functionLen);\n\n    size_t filenameLen = strlen(filename) + 1;\n    auto *filenameDup = (char *)seq_alloc_atomic(filenameLen);\n    memcpy(filenameDup, filename, filenameLen);\n\n    frames[count++] = {functionDup, filenameDup, pc, lineno};\n  }\n\n  void push_back(uintptr_t pc) { push_back(\"<invalid>\", \"<invalid>\", pc, 0); }\n\n  void free() {\n    for (auto i = 0; i < count; i++) {\n      auto *frame = &frames[i];\n      seq_free(frame->function);\n      seq_free(frame->filename);\n    }\n    seq_free(frames);\n    frames = nullptr;\n    count = 0;\n  }\n};\n\nvoid seq_backtrace_error_callback(void *data, const char *msg, int errnum) {\n  // printf(\"seq_backtrace_error_callback: %s (errnum = %d)\\n\", msg, errnum);\n}\n\nint seq_backtrace_full_callback(void *data, uintptr_t pc, const char *filename,\n                                int lineno, const char *function) {\n  auto *bt = ((Backtrace *)data);\n  bt->push_back(function, filename, pc, lineno);\n  return (bt->count < Backtrace::LIMIT) ? 0 : 1;\n}\n\nint seq_backtrace_simple_callback(void *data, uintptr_t pc) {\n  auto *bt = ((Backtrace *)data);\n  bt->push_back(pc);\n  return (bt->count < Backtrace::LIMIT) ? 0 : 1;\n}\n\n/*\n * This is largely based on\n * llvm/examples/ExceptionDemo/ExceptionDemo.cpp\n */\n\nnamespace {\ntemplate <typename Type_> static uintptr_t ReadType(const uint8_t *&p) {\n  Type_ value;\n  memcpy(&value, p, sizeof(Type_));\n  p += sizeof(Type_);\n  return static_cast<uintptr_t>(value);\n}\n} // namespace\n\n// Note: this should match Codon definition\nstruct TypeInfo {\n  seq_int_t id;\n  seq_int_t *parent_ids;\n  seq_int_t n_parent_ids;\n  seq_str_t raw_name;\n  // other fields do not need to be included\n};\n\nstruct RTTIObject {\n  void *data;\n  TypeInfo *type;\n};\n\nstruct CodonBaseExceptionType {\n  int type;\n};\n\nstruct CodonBaseException {\n  void *obj;\n  Backtrace bt;\n  _Unwind_Exception unwindException;\n};\n\nstruct CodonExceptionHeader {\n  seq_str_t msg;\n  seq_str_t func;\n  seq_str_t file;\n  seq_int_t line;\n  seq_int_t col;\n  void *python_type;\n  void *cause;\n};\n\nvoid seq_exc_init(int flags) {\n#ifdef APPLE_SILICON\n  if (!(flags & SEQ_FLAG_STANDALONE)) {\n    if (auto *unw_add_find_dynamic_unwind_sections =\n            (int (*)(unw_find_dynamic_unwind_sections find_dynamic_unwind_sections))\n                dlsym(RTLD_DEFAULT, \"__unw_add_find_dynamic_unwind_sections\")) {\n      unw_add_find_dynamic_unwind_sections(find_dynamic_unwind_sections);\n    }\n  }\n#endif\n}\n\nstatic void seq_delete_exc(_Unwind_Exception *expToDelete) {\n  if (!expToDelete || expToDelete->exception_class != SEQ_EXCEPTION_CLASS)\n    return;\n  auto *exc = (CodonBaseException *)((char *)expToDelete + seq_exc_offset());\n  if (seq_flags & SEQ_FLAG_DEBUG) {\n    exc->bt.free();\n  }\n  seq_free(exc);\n}\n\nstatic void seq_delete_unwind_exc(_Unwind_Reason_Code reason,\n                                  _Unwind_Exception *expToDelete) {\n  seq_delete_exc(expToDelete);\n}\n\nstatic struct backtrace_state *state = nullptr;\nstatic std::mutex stateLock;\n\nSEQ_FUNC void *seq_alloc_exc(void *obj) {\n  const size_t size = sizeof(CodonBaseException);\n  auto *e = (CodonBaseException *)memset(seq_alloc(size), 0, size);\n  assert(e);\n  e->obj = obj;\n  e->unwindException.exception_class = SEQ_EXCEPTION_CLASS;\n  e->unwindException.exception_cleanup = seq_delete_unwind_exc;\n  if (seq_flags & SEQ_FLAG_DEBUG) {\n    e->bt.frames = nullptr;\n    e->bt.count = 0;\n\n    if (seq_flags & SEQ_FLAG_STANDALONE) {\n      if (!state) {\n        stateLock.lock();\n        if (!state)\n          state =\n              backtrace_create_state(/*filename=*/nullptr, /*threaded=*/1,\n                                     seq_backtrace_error_callback, /*data=*/nullptr);\n        stateLock.unlock();\n      }\n      backtrace_full(state, /*skip=*/1, seq_backtrace_full_callback,\n                     seq_backtrace_error_callback, &e->bt);\n    } else {\n      backtrace_simple(/*state=*/nullptr, /*skip=*/1, seq_backtrace_simple_callback,\n                       seq_backtrace_error_callback, &e->bt);\n    }\n  }\n  return &(e->unwindException);\n}\n\nstatic void print_from_last_dot(seq_str_t s, std::ostringstream &buf) {\n  char *p = s.str;\n  int64_t n = s.len;\n\n  for (int64_t i = n - 1; i >= 0; i--) {\n    if (p[i] == '.') {\n      p += (i + 1);\n      n -= (i + 1);\n      break;\n    }\n  }\n\n  buf.write(p, (size_t)n);\n}\n\nstatic std::function<void(const codon::runtime::JITError &)> jitErrorCallback;\n\nSEQ_FUNC void seq_terminate(void *exc) {\n  auto *base = (CodonBaseException *)((char *)exc + seq_exc_offset());\n  void *obj = base->obj;\n  auto *hdr = *(CodonExceptionHeader **)obj;\n  auto tname = ((RTTIObject *)obj)->type->raw_name;\n\n  if (std::string(tname.str, tname.len) == \"SystemExit\") {\n    seq_int_t status = *(seq_int_t *)(hdr + 1);\n    exit((int)status);\n  }\n\n  std::ostringstream buf;\n  if (seq_flags & SEQ_FLAG_CAPTURE_OUTPUT)\n    buf << codon::runtime::getCapturedOutput();\n\n  buf << \"\\033[1m\";\n  print_from_last_dot(tname, buf);\n  if (hdr->msg.len > 0) {\n    buf << \": \\033[0m\";\n    buf.write(hdr->msg.str, hdr->msg.len);\n  } else {\n    buf << \"\\033[0m\";\n  }\n\n  buf << \"\\n\\n\\033[1mRaised from:\\033[0m \\033[32m\";\n  buf.write(hdr->func.str, hdr->func.len);\n  buf << \"\\033[0m\\n\";\n  buf.write(hdr->file.str, hdr->file.len);\n  if (hdr->line > 0) {\n    buf << \":\" << hdr->line;\n    if (hdr->col > 0)\n      buf << \":\" << hdr->col;\n  }\n  buf << \"\\n\";\n\n  if ((seq_flags & SEQ_FLAG_DEBUG) && (seq_flags & SEQ_FLAG_STANDALONE)) {\n    auto *bt = &base->bt;\n    if (bt->count > 0) {\n      buf << \"\\n\\033[1mBacktrace:\\033[0m\\n\";\n      for (unsigned i = 0; i < bt->count; i++) {\n        auto *frame = &bt->frames[i];\n        buf << \"  \"\n            << codon::runtime::makeBacktraceFrameString(\n                   frame->pc, std::string(frame->function),\n                   std::string(frame->filename), frame->lineno)\n            << \"\\n\";\n      }\n    }\n  }\n\n  auto output = buf.str();\n  if (seq_flags & SEQ_FLAG_STANDALONE) {\n    fwrite(output.data(), 1, output.size(), stderr);\n    abort();\n  } else {\n    auto *bt = &base->bt;\n    std::string msg(hdr->msg.str, hdr->msg.len);\n    std::string file(hdr->file.str, hdr->file.len);\n    std::string type(tname.str, tname.len);\n\n    std::vector<uintptr_t> backtrace;\n    if (seq_flags & SEQ_FLAG_DEBUG) {\n      for (unsigned i = 0; i < bt->count; i++) {\n        backtrace.push_back(bt->frames[i].pc);\n      }\n    }\n    codon::runtime::JITError e(output, msg, type, file, (int)hdr->line, (int)hdr->col,\n                               backtrace);\n    if (jitErrorCallback)\n      jitErrorCallback(e);\n    else\n      throw e;\n  }\n}\n\nSEQ_FUNC void seq_throw(void *exc) {\n  _Unwind_Reason_Code code = _Unwind_RaiseException((_Unwind_Exception *)exc);\n  (void)code;\n  seq_terminate(exc);\n}\n\nstatic uintptr_t readULEB128(const uint8_t **data) {\n  uintptr_t result = 0;\n  uintptr_t shift = 0;\n  unsigned char byte;\n  const uint8_t *p = *data;\n\n  do {\n    byte = *p++;\n    result |= (byte & 0x7f) << shift;\n    shift += 7;\n  } while (byte & 0x80);\n\n  *data = p;\n\n  return result;\n}\n\nstatic uintptr_t readSLEB128(const uint8_t **data) {\n  uintptr_t result = 0;\n  uintptr_t shift = 0;\n  unsigned char byte;\n  const uint8_t *p = *data;\n\n  do {\n    byte = *p++;\n    result |= (byte & 0x7f) << shift;\n    shift += 7;\n  } while (byte & 0x80);\n\n  *data = p;\n\n  if ((byte & 0x40) && (shift < (sizeof(result) << 3))) {\n    result |= (~0 << shift);\n  }\n\n  return result;\n}\n\nstatic unsigned getEncodingSize(uint8_t encoding) {\n  if (encoding == llvm::dwarf::DW_EH_PE_omit)\n    return 0;\n\n  switch (encoding & 0x0F) {\n  case llvm::dwarf::DW_EH_PE_absptr:\n    return sizeof(uintptr_t);\n  case llvm::dwarf::DW_EH_PE_udata2:\n    return sizeof(uint16_t);\n  case llvm::dwarf::DW_EH_PE_udata4:\n    return sizeof(uint32_t);\n  case llvm::dwarf::DW_EH_PE_udata8:\n    return sizeof(uint64_t);\n  case llvm::dwarf::DW_EH_PE_sdata2:\n    return sizeof(int16_t);\n  case llvm::dwarf::DW_EH_PE_sdata4:\n    return sizeof(int32_t);\n  case llvm::dwarf::DW_EH_PE_sdata8:\n    return sizeof(int64_t);\n  default:\n    // not supported\n    abort();\n  }\n}\n\nstatic uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) {\n  uintptr_t result = 0;\n  const uint8_t *p = *data;\n\n  if (encoding == llvm::dwarf::DW_EH_PE_omit)\n    return result;\n\n  // first get value\n  switch (encoding & 0x0F) {\n  case llvm::dwarf::DW_EH_PE_absptr:\n    result = ReadType<uintptr_t>(p);\n    break;\n  case llvm::dwarf::DW_EH_PE_uleb128:\n    result = readULEB128(&p);\n    break;\n    // Note: This case has not been tested\n  case llvm::dwarf::DW_EH_PE_sleb128:\n    result = readSLEB128(&p);\n    break;\n  case llvm::dwarf::DW_EH_PE_udata2:\n    result = ReadType<uint16_t>(p);\n    break;\n  case llvm::dwarf::DW_EH_PE_udata4:\n    result = ReadType<uint32_t>(p);\n    break;\n  case llvm::dwarf::DW_EH_PE_udata8:\n    result = ReadType<uint64_t>(p);\n    break;\n  case llvm::dwarf::DW_EH_PE_sdata2:\n    result = ReadType<int16_t>(p);\n    break;\n  case llvm::dwarf::DW_EH_PE_sdata4:\n    result = ReadType<int32_t>(p);\n    break;\n  case llvm::dwarf::DW_EH_PE_sdata8:\n    result = ReadType<int64_t>(p);\n    break;\n  default:\n    // not supported\n    abort();\n  }\n\n  // then add relative offset\n  switch (encoding & 0x70) {\n  case llvm::dwarf::DW_EH_PE_absptr:\n    // do nothing\n    break;\n  case llvm::dwarf::DW_EH_PE_pcrel:\n    result += (uintptr_t)(*data);\n    break;\n  case llvm::dwarf::DW_EH_PE_textrel:\n  case llvm::dwarf::DW_EH_PE_datarel:\n  case llvm::dwarf::DW_EH_PE_funcrel:\n  case llvm::dwarf::DW_EH_PE_aligned:\n  default:\n    // not supported\n    abort();\n  }\n\n  // then apply indirection\n  if (encoding & llvm::dwarf::DW_EH_PE_indirect) {\n    result = *((uintptr_t *)result);\n  }\n\n  *data = p;\n\n  return result;\n}\n\nstatic bool isinstance(void *obj, seq_int_t type) {\n  auto *info = ((RTTIObject *)obj)->type;\n  if (info->id == type)\n    return true;\n  if (info->parent_ids) {\n    auto *p = info->parent_ids;\n    while (*p) {\n      if (*p++ == type) {\n        return true;\n      }\n    }\n  }\n  return false;\n}\n\nstatic bool handleActionValue(int64_t *resultAction, uint8_t TTypeEncoding,\n                              const uint8_t *ClassInfo, uintptr_t actionEntry,\n                              uint64_t exceptionClass,\n                              _Unwind_Exception *exceptionObject) {\n  bool ret = false;\n\n  if (!resultAction || !exceptionObject || (exceptionClass != SEQ_EXCEPTION_CLASS))\n    return ret;\n\n  auto *excp =\n      (struct CodonBaseException *)(((char *)exceptionObject) + seq_exc_offset());\n  const uint8_t *actionPos = (uint8_t *)actionEntry, *tempActionPos;\n  int64_t typeOffset = 0, actionOffset;\n\n  for (int i = 0;; i++) {\n    // Each emitted dwarf action corresponds to a 2 tuple of\n    // type info address offset, and action offset to the next\n    // emitted action.\n    typeOffset = (int64_t)readSLEB128(&actionPos);\n    tempActionPos = actionPos;\n    actionOffset = (int64_t)readSLEB128(&tempActionPos);\n\n    assert(typeOffset >= 0);\n\n    // Note: A typeOffset == 0 implies that a cleanup llvm.eh.selector\n    //       argument has been matched.\n    if (typeOffset > 0) {\n      unsigned EncSize = getEncodingSize(TTypeEncoding);\n      const uint8_t *EntryP = ClassInfo - typeOffset * EncSize;\n      uintptr_t P = readEncodedPointer(&EntryP, TTypeEncoding);\n      auto *ThisClassInfo = reinterpret_cast<CodonBaseExceptionType *>(P);\n      auto ThisClassType = ThisClassInfo->type;\n      // type=0 means catch-all\n      if (ThisClassType == 0 || isinstance(excp->obj, ThisClassType)) {\n        *resultAction = ThisClassType;\n        ret = true;\n        break;\n      }\n    }\n\n    if (!actionOffset)\n      break;\n\n    actionPos += actionOffset;\n  }\n\n  return ret;\n}\n\nstatic _Unwind_Reason_Code handleLsda(int version, const uint8_t *lsda,\n                                      _Unwind_Action actions, uint64_t exceptionClass,\n                                      _Unwind_Exception *exceptionObject,\n                                      _Unwind_Context *context) {\n  _Unwind_Reason_Code ret = _URC_CONTINUE_UNWIND;\n\n  if (!lsda)\n    return ret;\n\n  // Get the current instruction pointer and offset it before next\n  // instruction in the current frame which threw the exception.\n  uintptr_t pc = _Unwind_GetIP(context) - 1;\n\n  // Get beginning current frame's code (as defined by the\n  // emitted dwarf code)\n  uintptr_t funcStart = _Unwind_GetRegionStart(context);\n  uintptr_t pcOffset = pc - funcStart;\n  const uint8_t *ClassInfo = nullptr;\n\n  // Note: See JITDwarfEmitter::EmitExceptionTable(...) for corresponding\n  //       dwarf emission\n\n  // Parse LSDA header.\n  uint8_t lpStartEncoding = *lsda++;\n\n  if (lpStartEncoding != llvm::dwarf::DW_EH_PE_omit) {\n    readEncodedPointer(&lsda, lpStartEncoding);\n  }\n\n  uint8_t ttypeEncoding = *lsda++;\n  uintptr_t classInfoOffset;\n\n  if (ttypeEncoding != llvm::dwarf::DW_EH_PE_omit) {\n    // Calculate type info locations in emitted dwarf code which\n    // were flagged by type info arguments to llvm.eh.selector\n    // intrinsic\n    classInfoOffset = readULEB128(&lsda);\n    ClassInfo = lsda + classInfoOffset;\n  }\n\n  // Walk call-site table looking for range that\n  // includes current PC.\n\n  uint8_t callSiteEncoding = *lsda++;\n  auto callSiteTableLength = (uint32_t)readULEB128(&lsda);\n  const uint8_t *callSiteTableStart = lsda;\n  const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength;\n  const uint8_t *actionTableStart = callSiteTableEnd;\n  const uint8_t *callSitePtr = callSiteTableStart;\n\n  while (callSitePtr < callSiteTableEnd) {\n    uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding);\n    uintptr_t length = readEncodedPointer(&callSitePtr, callSiteEncoding);\n    uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding);\n\n    // Note: Action value\n    uintptr_t actionEntry = readULEB128(&callSitePtr);\n\n    if (exceptionClass != SEQ_EXCEPTION_CLASS) {\n      // We have been notified of a foreign exception being thrown,\n      // and we therefore need to execute cleanup landing pads\n      actionEntry = 0;\n    }\n\n    if (landingPad == 0) {\n      continue; // no landing pad for this entry\n    }\n\n    if (actionEntry) {\n      actionEntry += (uintptr_t)actionTableStart - 1;\n    }\n\n    bool exceptionMatched = false;\n\n    if ((start <= pcOffset) && (pcOffset < (start + length))) {\n      int64_t actionValue = 0;\n\n      if (actionEntry) {\n        exceptionMatched =\n            handleActionValue(&actionValue, ttypeEncoding, ClassInfo, actionEntry,\n                              exceptionClass, exceptionObject);\n      }\n\n      if (!(actions & _UA_SEARCH_PHASE)) {\n        // Found landing pad for the PC.\n        // Set Instruction Pointer to so we re-enter function\n        // at landing pad. The landing pad is created by the\n        // compiler to take two parameters in registers.\n        _Unwind_SetGR(context, __builtin_eh_return_data_regno(0),\n                      (uintptr_t)exceptionObject);\n\n        // Note: this virtual register directly corresponds\n        //       to the return of the llvm.eh.selector intrinsic\n        if (!actionEntry || !exceptionMatched) {\n          // We indicate cleanup only\n          _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);\n        } else {\n          // Matched type info index of llvm.eh.selector intrinsic\n          // passed here.\n          _Unwind_SetGR(context, __builtin_eh_return_data_regno(1),\n                        (uintptr_t)actionValue);\n        }\n\n        // To execute landing pad set here\n        _Unwind_SetIP(context, funcStart + landingPad);\n        ret = _URC_INSTALL_CONTEXT;\n      } else if (exceptionMatched) {\n        ret = _URC_HANDLER_FOUND;\n      }\n\n      break;\n    }\n  }\n\n  return ret;\n}\n\nSEQ_FUNC _Unwind_Reason_Code seq_personality(int version, _Unwind_Action actions,\n                                             uint64_t exceptionClass,\n                                             _Unwind_Exception *exceptionObject,\n                                             _Unwind_Context *context) {\n  const auto *lsda = (uint8_t *)_Unwind_GetLanguageSpecificData(context);\n  // The real work of the personality function is captured here\n  return handleLsda(version, lsda, actions, exceptionClass, exceptionObject, context);\n}\n\nSEQ_FUNC int64_t seq_exc_offset() {\n  static CodonBaseException dummy = {};\n  return (int64_t)((uintptr_t)&dummy - (uintptr_t)&(dummy.unwindException));\n}\n\nstd::string codon::runtime::makeBacktraceFrameString(uintptr_t pc,\n                                                     const std::string &func,\n                                                     const std::string &file, int line,\n                                                     int col) {\n  std::ostringstream buf;\n  buf << \"[\\033[33m0x\" << std::hex << pc << std::dec << \"\\033[0m]\";\n  if (!func.empty()) {\n    buf << \" \\033[32m\" << func << \"\\033[0m\";\n    if (!file.empty()) {\n      buf << \" at \\033[36m\" << file << \"\\033[0m\";\n      if (line != 0) {\n        buf << \":\\033[33m\" << line << \"\\033[0m\";\n        if (col != 0) {\n          buf << \":\\033[33m\" << col << \"\\033[0m\";\n        }\n      }\n    }\n  }\n  return buf.str();\n}\n\nvoid codon::runtime::setJITErrorCallback(\n    std::function<void(const codon::runtime::JITError &)> callback) {\n  jitErrorCallback = callback;\n}\n"
  },
  {
    "path": "codon/runtime/floatlib/extenddftf2.c",
    "content": "//===-- lib/extenddftf2.c - double -> quad conversion -------------*- C -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n\n#define QUAD_PRECISION\n#include \"fp_lib.h\"\n\n#if defined(CRT_HAS_TF_MODE)\n#define SRC_DOUBLE\n#define DST_QUAD\n#include \"fp_extend_impl.inc\"\n\nCOMPILER_RT_ABI fp_t __extenddftf2(double a) {\n  return __extendXfYf2__(a);\n}\n\n#endif\n"
  },
  {
    "path": "codon/runtime/floatlib/extendhfsf2.c",
    "content": "//===-- lib/extendhfsf2.c - half -> single conversion -------------*- C -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n\n#define SRC_HALF\n#define DST_SINGLE\n#include \"fp_extend_impl.inc\"\n\n// Use a forwarding definition and noinline to implement a poor man's alias,\n// as there isn't a good cross-platform way of defining one.\nCOMPILER_RT_ABI NOINLINE float __extendhfsf2(src_t a) {\n  return __extendXfYf2__(a);\n}\n\nCOMPILER_RT_ABI float __gnu_h2f_ieee(src_t a) { return __extendhfsf2(a); }\n\n#if defined(__ARM_EABI__)\n#if defined(COMPILER_RT_ARMHF_TARGET)\nAEABI_RTABI float __aeabi_h2f(src_t a) { return __extendhfsf2(a); }\n#else\nCOMPILER_RT_ALIAS(__extendhfsf2, __aeabi_h2f)\n#endif\n#endif\n"
  },
  {
    "path": "codon/runtime/floatlib/extendhftf2.c",
    "content": "//===-- lib/extendhftf2.c - half -> quad conversion ---------------*- C -*-===//\n//\n//                     The LLVM Compiler Infrastructure\n//\n// This file is dual licensed under the MIT and the University of Illinois Open\n// Source Licenses. See LICENSE.TXT for details.\n//\n//===----------------------------------------------------------------------===//\n\n#define QUAD_PRECISION\n#include \"fp_lib.h\"\n\n#if defined(CRT_HAS_TF_MODE) && defined(COMPILER_RT_HAS_FLOAT16)\n#define SRC_HALF\n#define DST_QUAD\n#include \"fp_extend_impl.inc\"\n\nCOMPILER_RT_ABI long double __extendhftf2(_Float16 a) {\n  return __extendXfYf2__(a);\n}\n\n#endif\n"
  },
  {
    "path": "codon/runtime/floatlib/extendsfdf2.c",
    "content": "//===-- lib/extendsfdf2.c - single -> double conversion -----------*- C -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n\n#define SRC_SINGLE\n#define DST_DOUBLE\n#include \"fp_extend_impl.inc\"\n\nCOMPILER_RT_ABI double __extendsfdf2(float a) { return __extendXfYf2__(a); }\n\n#if defined(__ARM_EABI__)\n#if defined(COMPILER_RT_ARMHF_TARGET)\nAEABI_RTABI double __aeabi_f2d(float a) { return __extendsfdf2(a); }\n#else\nCOMPILER_RT_ALIAS(__extendsfdf2, __aeabi_f2d)\n#endif\n#endif\n"
  },
  {
    "path": "codon/runtime/floatlib/extendsftf2.c",
    "content": "//===-- lib/extendsftf2.c - single -> quad conversion -------------*- C -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n\n#define QUAD_PRECISION\n#include \"fp_lib.h\"\n\n#if defined(CRT_HAS_TF_MODE)\n#define SRC_SINGLE\n#define DST_QUAD\n#include \"fp_extend_impl.inc\"\n\nCOMPILER_RT_ABI fp_t __extendsftf2(float a) {\n  return __extendXfYf2__(a);\n}\n\n#endif\n"
  },
  {
    "path": "codon/runtime/floatlib/fp_extend.h",
    "content": "//===-lib/fp_extend.h - low precision -> high precision conversion -*- C\n//-*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// Set source and destination setting\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef FP_EXTEND_HEADER\n#define FP_EXTEND_HEADER\n\n#include \"int_lib.h\"\n\n#if defined SRC_SINGLE\ntypedef float src_t;\ntypedef uint32_t src_rep_t;\n#define SRC_REP_C UINT32_C\nstatic const int srcSigBits = 23;\n#define src_rep_t_clz clzsi\n\n#elif defined SRC_DOUBLE\ntypedef double src_t;\ntypedef uint64_t src_rep_t;\n#define SRC_REP_C UINT64_C\nstatic const int srcSigBits = 52;\nstatic __inline int src_rep_t_clz(src_rep_t a) {\n#if defined __LP64__\n  return __builtin_clzl(a);\n#else\n  if (a & REP_C(0xffffffff00000000))\n    return clzsi(a >> 32);\n  else\n    return 32 + clzsi(a & REP_C(0xffffffff));\n#endif\n}\n\n#elif defined SRC_HALF\n#ifdef COMPILER_RT_HAS_FLOAT16\ntypedef _Float16 src_t;\n#else\ntypedef uint16_t src_t;\n#endif\ntypedef uint16_t src_rep_t;\n#define SRC_REP_C UINT16_C\nstatic const int srcSigBits = 10;\n#define src_rep_t_clz __builtin_clz\n\n#else\n#error Source should be half, single, or double precision!\n#endif // end source precision\n\n#if defined DST_SINGLE\ntypedef float dst_t;\ntypedef uint32_t dst_rep_t;\n#define DST_REP_C UINT32_C\nstatic const int dstSigBits = 23;\n\n#elif defined DST_DOUBLE\ntypedef double dst_t;\ntypedef uint64_t dst_rep_t;\n#define DST_REP_C UINT64_C\nstatic const int dstSigBits = 52;\n\n#elif defined DST_QUAD\ntypedef long double dst_t;\ntypedef __uint128_t dst_rep_t;\n#define DST_REP_C (__uint128_t)\nstatic const int dstSigBits = 112;\n\n#else\n#error Destination should be single, double, or quad precision!\n#endif // end destination precision\n\n// End of specialization parameters.  Two helper routines for conversion to and\n// from the representation of floating-point data as integer values follow.\n\nstatic __inline src_rep_t srcToRep(src_t x) {\n  const union {\n    src_t f;\n    src_rep_t i;\n  } rep = {.f = x};\n  return rep.i;\n}\n\nstatic __inline dst_t dstFromRep(dst_rep_t x) {\n  const union {\n    dst_t f;\n    dst_rep_t i;\n  } rep = {.i = x};\n  return rep.f;\n}\n// End helper routines.  Conversion implementation follows.\n\n#endif // FP_EXTEND_HEADER\n"
  },
  {
    "path": "codon/runtime/floatlib/fp_extend_impl.inc",
    "content": "//=-lib/fp_extend_impl.inc - low precision -> high precision conversion -*-- -//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// This file implements a fairly generic conversion from a narrower to a wider\n// IEEE-754 floating-point type.  The constants and types defined following the\n// includes below parameterize the conversion.\n//\n// It does not support types that don't use the usual IEEE-754 interchange\n// formats; specifically, some work would be needed to adapt it to\n// (for example) the Intel 80-bit format or PowerPC double-double format.\n//\n// Note please, however, that this implementation is only intended to support\n// *widening* operations; if you need to convert to a *narrower* floating-point\n// type (e.g. double -> float), then this routine will not do what you want it\n// to.\n//\n// It also requires that integer types at least as large as both formats\n// are available on the target platform; this may pose a problem when trying\n// to add support for quad on some 32-bit systems, for example.  You also may\n// run into trouble finding an appropriate CLZ function for wide source types;\n// you will likely need to roll your own on some platforms.\n//\n// Finally, the following assumptions are made:\n//\n// 1. Floating-point types and integer types have the same endianness on the\n//    target platform.\n//\n// 2. Quiet NaNs, if supported, are indicated by the leading bit of the\n//    significand field being set.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"fp_extend.h\"\n\nstatic __inline dst_t __extendXfYf2__(src_t a) {\n  // Various constants whose values follow from the type parameters.\n  // Any reasonable optimizer will fold and propagate all of these.\n  const int srcBits = sizeof(src_t) * CHAR_BIT;\n  const int srcExpBits = srcBits - srcSigBits - 1;\n  const int srcInfExp = (1 << srcExpBits) - 1;\n  const int srcExpBias = srcInfExp >> 1;\n\n  const src_rep_t srcMinNormal = SRC_REP_C(1) << srcSigBits;\n  const src_rep_t srcInfinity = (src_rep_t)srcInfExp << srcSigBits;\n  const src_rep_t srcSignMask = SRC_REP_C(1) << (srcSigBits + srcExpBits);\n  const src_rep_t srcAbsMask = srcSignMask - 1;\n  const src_rep_t srcQNaN = SRC_REP_C(1) << (srcSigBits - 1);\n  const src_rep_t srcNaNCode = srcQNaN - 1;\n\n  const int dstBits = sizeof(dst_t) * CHAR_BIT;\n  const int dstExpBits = dstBits - dstSigBits - 1;\n  const int dstInfExp = (1 << dstExpBits) - 1;\n  const int dstExpBias = dstInfExp >> 1;\n\n  const dst_rep_t dstMinNormal = DST_REP_C(1) << dstSigBits;\n\n  // Break a into a sign and representation of the absolute value.\n  const src_rep_t aRep = srcToRep(a);\n  const src_rep_t aAbs = aRep & srcAbsMask;\n  const src_rep_t sign = aRep & srcSignMask;\n  dst_rep_t absResult;\n\n  // If sizeof(src_rep_t) < sizeof(int), the subtraction result is promoted\n  // to (signed) int.  To avoid that, explicitly cast to src_rep_t.\n  if ((src_rep_t)(aAbs - srcMinNormal) < srcInfinity - srcMinNormal) {\n    // a is a normal number.\n    // Extend to the destination type by shifting the significand and\n    // exponent into the proper position and rebiasing the exponent.\n    absResult = (dst_rep_t)aAbs << (dstSigBits - srcSigBits);\n    absResult += (dst_rep_t)(dstExpBias - srcExpBias) << dstSigBits;\n  }\n\n  else if (aAbs >= srcInfinity) {\n    // a is NaN or infinity.\n    // Conjure the result by beginning with infinity, then setting the qNaN\n    // bit (if needed) and right-aligning the rest of the trailing NaN\n    // payload field.\n    absResult = (dst_rep_t)dstInfExp << dstSigBits;\n    absResult |= (dst_rep_t)(aAbs & srcQNaN) << (dstSigBits - srcSigBits);\n    absResult |= (dst_rep_t)(aAbs & srcNaNCode) << (dstSigBits - srcSigBits);\n  }\n\n  else if (aAbs) {\n    // a is denormal.\n    // renormalize the significand and clear the leading bit, then insert\n    // the correct adjusted exponent in the destination type.\n    const int scale = src_rep_t_clz(aAbs) - src_rep_t_clz(srcMinNormal);\n    absResult = (dst_rep_t)aAbs << (dstSigBits - srcSigBits + scale);\n    absResult ^= dstMinNormal;\n    const int resultExponent = dstExpBias - srcExpBias - scale + 1;\n    absResult |= (dst_rep_t)resultExponent << dstSigBits;\n  }\n\n  else {\n    // a is zero.\n    absResult = 0;\n  }\n\n  // Apply the signbit to the absolute value.\n  const dst_rep_t result = absResult | (dst_rep_t)sign << (dstBits - srcBits);\n  return dstFromRep(result);\n}\n"
  },
  {
    "path": "codon/runtime/floatlib/fp_lib.h",
    "content": "//===-- lib/fp_lib.h - Floating-point utilities -------------------*- C -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// This file is a configuration header for soft-float routines in compiler-rt.\n// This file does not provide any part of the compiler-rt interface, but defines\n// many useful constants and utility routines that are used in the\n// implementation of the soft-float routines in compiler-rt.\n//\n// Assumes that float, double and long double correspond to the IEEE-754\n// binary32, binary64 and binary 128 types, respectively, and that integer\n// endianness matches floating point endianness on the target platform.\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef FP_LIB_HEADER\n#define FP_LIB_HEADER\n\n#include \"int_lib.h\"\n#include \"int_math.h\"\n#include \"int_types.h\"\n#include <limits.h>\n#include <stdbool.h>\n#include <stdint.h>\n\n#if defined SINGLE_PRECISION\n\ntypedef uint16_t half_rep_t;\ntypedef uint32_t rep_t;\ntypedef uint64_t twice_rep_t;\ntypedef int32_t srep_t;\ntypedef float fp_t;\n#define HALF_REP_C UINT16_C\n#define REP_C UINT32_C\n#define significandBits 23\n\nstatic __inline int rep_clz(rep_t a) { return clzsi(a); }\n\n// 32x32 --> 64 bit multiply\nstatic __inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) {\n  const uint64_t product = (uint64_t)a * b;\n  *hi = product >> 32;\n  *lo = product;\n}\nCOMPILER_RT_ABI fp_t __addsf3(fp_t a, fp_t b);\n\n#elif defined DOUBLE_PRECISION\n\ntypedef uint32_t half_rep_t;\ntypedef uint64_t rep_t;\ntypedef int64_t srep_t;\ntypedef double fp_t;\n#define HALF_REP_C UINT32_C\n#define REP_C UINT64_C\n#define significandBits 52\n\nstatic __inline int rep_clz(rep_t a) {\n#if defined __LP64__\n  return __builtin_clzl(a);\n#else\n  if (a & REP_C(0xffffffff00000000))\n    return clzsi(a >> 32);\n  else\n    return 32 + clzsi(a & REP_C(0xffffffff));\n#endif\n}\n\n#define loWord(a) (a & 0xffffffffU)\n#define hiWord(a) (a >> 32)\n\n// 64x64 -> 128 wide multiply for platforms that don't have such an operation;\n// many 64-bit platforms have this operation, but they tend to have hardware\n// floating-point, so we don't bother with a special case for them here.\nstatic __inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) {\n  // Each of the component 32x32 -> 64 products\n  const uint64_t plolo = loWord(a) * loWord(b);\n  const uint64_t plohi = loWord(a) * hiWord(b);\n  const uint64_t philo = hiWord(a) * loWord(b);\n  const uint64_t phihi = hiWord(a) * hiWord(b);\n  // Sum terms that contribute to lo in a way that allows us to get the carry\n  const uint64_t r0 = loWord(plolo);\n  const uint64_t r1 = hiWord(plolo) + loWord(plohi) + loWord(philo);\n  *lo = r0 + (r1 << 32);\n  // Sum terms contributing to hi with the carry from lo\n  *hi = hiWord(plohi) + hiWord(philo) + hiWord(r1) + phihi;\n}\n#undef loWord\n#undef hiWord\n\nCOMPILER_RT_ABI fp_t __adddf3(fp_t a, fp_t b);\n\n#elif defined QUAD_PRECISION\n#if defined(CRT_HAS_F128) && defined(CRT_HAS_128BIT)\ntypedef uint64_t half_rep_t;\ntypedef __uint128_t rep_t;\ntypedef __int128_t srep_t;\ntypedef tf_float fp_t;\n#define HALF_REP_C UINT64_C\n#define REP_C (__uint128_t)\n#if defined(CRT_HAS_IEEE_TF)\n// Note: Since there is no explicit way to tell compiler the constant is a\n// 128-bit integer, we let the constant be casted to 128-bit integer\n#define significandBits 112\n#define TF_MANT_DIG (significandBits + 1)\n\nstatic __inline int rep_clz(rep_t a) {\n  const union {\n    __uint128_t ll;\n#if _YUGA_BIG_ENDIAN\n    struct {\n      uint64_t high, low;\n    } s;\n#else\n    struct {\n      uint64_t low, high;\n    } s;\n#endif\n  } uu = {.ll = a};\n\n  uint64_t word;\n  uint64_t add;\n\n  if (uu.s.high) {\n    word = uu.s.high;\n    add = 0;\n  } else {\n    word = uu.s.low;\n    add = 64;\n  }\n  return __builtin_clzll(word) + add;\n}\n\n#define Word_LoMask UINT64_C(0x00000000ffffffff)\n#define Word_HiMask UINT64_C(0xffffffff00000000)\n#define Word_FullMask UINT64_C(0xffffffffffffffff)\n#define Word_1(a) (uint64_t)((a >> 96) & Word_LoMask)\n#define Word_2(a) (uint64_t)((a >> 64) & Word_LoMask)\n#define Word_3(a) (uint64_t)((a >> 32) & Word_LoMask)\n#define Word_4(a) (uint64_t)(a & Word_LoMask)\n\n// 128x128 -> 256 wide multiply for platforms that don't have such an operation;\n// many 64-bit platforms have this operation, but they tend to have hardware\n// floating-point, so we don't bother with a special case for them here.\nstatic __inline void wideMultiply(rep_t a, rep_t b, rep_t *hi, rep_t *lo) {\n\n  const uint64_t product11 = Word_1(a) * Word_1(b);\n  const uint64_t product12 = Word_1(a) * Word_2(b);\n  const uint64_t product13 = Word_1(a) * Word_3(b);\n  const uint64_t product14 = Word_1(a) * Word_4(b);\n  const uint64_t product21 = Word_2(a) * Word_1(b);\n  const uint64_t product22 = Word_2(a) * Word_2(b);\n  const uint64_t product23 = Word_2(a) * Word_3(b);\n  const uint64_t product24 = Word_2(a) * Word_4(b);\n  const uint64_t product31 = Word_3(a) * Word_1(b);\n  const uint64_t product32 = Word_3(a) * Word_2(b);\n  const uint64_t product33 = Word_3(a) * Word_3(b);\n  const uint64_t product34 = Word_3(a) * Word_4(b);\n  const uint64_t product41 = Word_4(a) * Word_1(b);\n  const uint64_t product42 = Word_4(a) * Word_2(b);\n  const uint64_t product43 = Word_4(a) * Word_3(b);\n  const uint64_t product44 = Word_4(a) * Word_4(b);\n\n  const __uint128_t sum0 = (__uint128_t)product44;\n  const __uint128_t sum1 = (__uint128_t)product34 + (__uint128_t)product43;\n  const __uint128_t sum2 =\n      (__uint128_t)product24 + (__uint128_t)product33 + (__uint128_t)product42;\n  const __uint128_t sum3 = (__uint128_t)product14 + (__uint128_t)product23 +\n                           (__uint128_t)product32 + (__uint128_t)product41;\n  const __uint128_t sum4 =\n      (__uint128_t)product13 + (__uint128_t)product22 + (__uint128_t)product31;\n  const __uint128_t sum5 = (__uint128_t)product12 + (__uint128_t)product21;\n  const __uint128_t sum6 = (__uint128_t)product11;\n\n  const __uint128_t r0 = (sum0 & Word_FullMask) + ((sum1 & Word_LoMask) << 32);\n  const __uint128_t r1 = (sum0 >> 64) + ((sum1 >> 32) & Word_FullMask) +\n                         (sum2 & Word_FullMask) + ((sum3 << 32) & Word_HiMask);\n\n  *lo = r0 + (r1 << 64);\n  *hi = (r1 >> 64) + (sum1 >> 96) + (sum2 >> 64) + (sum3 >> 32) + sum4 + (sum5 << 32) +\n        (sum6 << 64);\n}\n#undef Word_1\n#undef Word_2\n#undef Word_3\n#undef Word_4\n#undef Word_HiMask\n#undef Word_LoMask\n#undef Word_FullMask\n#endif // defined(CRT_HAS_IEEE_TF)\n#else\ntypedef long double fp_t;\n#endif // defined(CRT_HAS_F128) && defined(CRT_HAS_128BIT)\n#else\n#error SINGLE_PRECISION, DOUBLE_PRECISION or QUAD_PRECISION must be defined.\n#endif\n\n#if defined(SINGLE_PRECISION) || defined(DOUBLE_PRECISION) ||                          \\\n    (defined(QUAD_PRECISION) && defined(CRT_HAS_TF_MODE))\n#define typeWidth (sizeof(rep_t) * CHAR_BIT)\n\nstatic __inline rep_t toRep(fp_t x) {\n  const union {\n    fp_t f;\n    rep_t i;\n  } rep = {.f = x};\n  return rep.i;\n}\n\nstatic __inline fp_t fromRep(rep_t x) {\n  const union {\n    fp_t f;\n    rep_t i;\n  } rep = {.i = x};\n  return rep.f;\n}\n\n#if !defined(QUAD_PRECISION) || defined(CRT_HAS_IEEE_TF)\n#define exponentBits (typeWidth - significandBits - 1)\n#define maxExponent ((1 << exponentBits) - 1)\n#define exponentBias (maxExponent >> 1)\n\n#define implicitBit (REP_C(1) << significandBits)\n#define significandMask (implicitBit - 1U)\n#define signBit (REP_C(1) << (significandBits + exponentBits))\n#define absMask (signBit - 1U)\n#define exponentMask (absMask ^ significandMask)\n#define oneRep ((rep_t)exponentBias << significandBits)\n#define infRep exponentMask\n#define quietBit (implicitBit >> 1)\n#define qnanRep (exponentMask | quietBit)\n\nstatic __inline int normalize(rep_t *significand) {\n  const int shift = rep_clz(*significand) - rep_clz(implicitBit);\n  *significand <<= shift;\n  return 1 - shift;\n}\n\nstatic __inline void wideLeftShift(rep_t *hi, rep_t *lo, int count) {\n  *hi = *hi << count | *lo >> (typeWidth - count);\n  *lo = *lo << count;\n}\n\nstatic __inline void wideRightShiftWithSticky(rep_t *hi, rep_t *lo,\n                                              unsigned int count) {\n  if (count < typeWidth) {\n    const bool sticky = (*lo << (typeWidth - count)) != 0;\n    *lo = *hi << (typeWidth - count) | *lo >> count | sticky;\n    *hi = *hi >> count;\n  } else if (count < 2 * typeWidth) {\n    const bool sticky = *hi << (2 * typeWidth - count) | *lo;\n    *lo = *hi >> (count - typeWidth) | sticky;\n    *hi = 0;\n  } else {\n    const bool sticky = *hi | *lo;\n    *lo = sticky;\n    *hi = 0;\n  }\n}\n\n// Implements logb methods (logb, logbf, logbl) for IEEE-754. This avoids\n// pulling in a libm dependency from compiler-rt, but is not meant to replace\n// it (i.e. code calling logb() should get the one from libm, not this), hence\n// the __compiler_rt prefix.\nstatic __inline fp_t __compiler_rt_logbX(fp_t x) {\n  rep_t rep = toRep(x);\n  int exp = (rep & exponentMask) >> significandBits;\n\n  // Abnormal cases:\n  // 1) +/- inf returns +inf; NaN returns NaN\n  // 2) 0.0 returns -inf\n  if (exp == maxExponent) {\n    if (((rep & signBit) == 0) || (x != x)) {\n      return x; // NaN or +inf: return x\n    } else {\n      return -x; // -inf: return -x\n    }\n  } else if (x == 0.0) {\n    // 0.0: return -inf\n    return fromRep(infRep | signBit);\n  }\n\n  if (exp != 0) {\n    // Normal number\n    return exp - exponentBias; // Unbias exponent\n  } else {\n    // Subnormal number; normalize and repeat\n    rep &= absMask;\n    const int shift = 1 - normalize(&rep);\n    exp = (rep & exponentMask) >> significandBits;\n    return exp - exponentBias - shift; // Unbias exponent\n  }\n}\n\n// Avoid using scalbn from libm. Unlike libc/libm scalbn, this function never\n// sets errno on underflow/overflow.\nstatic __inline fp_t __compiler_rt_scalbnX(fp_t x, int y) {\n  const rep_t rep = toRep(x);\n  int exp = (rep & exponentMask) >> significandBits;\n\n  if (x == 0.0 || exp == maxExponent)\n    return x; // +/- 0.0, NaN, or inf: return x\n\n  // Normalize subnormal input.\n  rep_t sig = rep & significandMask;\n  if (exp == 0) {\n    exp += normalize(&sig);\n    sig &= ~implicitBit; // clear the implicit bit again\n  }\n\n  if (__builtin_sadd_overflow(exp, y, &exp)) {\n    // Saturate the exponent, which will guarantee an underflow/overflow below.\n    exp = (y >= 0) ? INT_MAX : INT_MIN;\n  }\n\n  // Return this value: [+/-] 1.sig * 2 ** (exp - exponentBias).\n  const rep_t sign = rep & signBit;\n  if (exp >= maxExponent) {\n    // Overflow, which could produce infinity or the largest-magnitude value,\n    // depending on the rounding mode.\n    return fromRep(sign | ((rep_t)(maxExponent - 1) << significandBits)) * 2.0f;\n  } else if (exp <= 0) {\n    // Subnormal or underflow. Use floating-point multiply to handle truncation\n    // correctly.\n    fp_t tmp = fromRep(sign | (REP_C(1) << significandBits) | sig);\n    exp += exponentBias - 1;\n    if (exp < 1)\n      exp = 1;\n    tmp *= fromRep((rep_t)exp << significandBits);\n    return tmp;\n  } else\n    return fromRep(sign | ((rep_t)exp << significandBits) | sig);\n}\n\n#endif // !defined(QUAD_PRECISION) || defined(CRT_HAS_IEEE_TF)\n\n// Avoid using fmax from libm.\nstatic __inline fp_t __compiler_rt_fmaxX(fp_t x, fp_t y) {\n  // If either argument is NaN, return the other argument. If both are NaN,\n  // arbitrarily return the second one. Otherwise, if both arguments are +/-0,\n  // arbitrarily return the first one.\n  return (crt_isnan(x) || x < y) ? y : x;\n}\n\n#endif\n\n#if defined(SINGLE_PRECISION)\n\nstatic __inline fp_t __compiler_rt_logbf(fp_t x) { return __compiler_rt_logbX(x); }\nstatic __inline fp_t __compiler_rt_scalbnf(fp_t x, int y) {\n  return __compiler_rt_scalbnX(x, y);\n}\nstatic __inline fp_t __compiler_rt_fmaxf(fp_t x, fp_t y) {\n#if defined(__aarch64__)\n  // Use __builtin_fmaxf which turns into an fmaxnm instruction on AArch64.\n  return __builtin_fmaxf(x, y);\n#else\n  // __builtin_fmaxf frequently turns into a libm call, so inline the function.\n  return __compiler_rt_fmaxX(x, y);\n#endif\n}\n\n#elif defined(DOUBLE_PRECISION)\n\nstatic __inline fp_t __compiler_rt_logb(fp_t x) { return __compiler_rt_logbX(x); }\nstatic __inline fp_t __compiler_rt_scalbn(fp_t x, int y) {\n  return __compiler_rt_scalbnX(x, y);\n}\nstatic __inline fp_t __compiler_rt_fmax(fp_t x, fp_t y) {\n#if defined(__aarch64__)\n  // Use __builtin_fmax which turns into an fmaxnm instruction on AArch64.\n  return __builtin_fmax(x, y);\n#else\n  // __builtin_fmax frequently turns into a libm call, so inline the function.\n  return __compiler_rt_fmaxX(x, y);\n#endif\n}\n\n#elif defined(QUAD_PRECISION) && defined(CRT_HAS_TF_MODE)\n// The generic implementation only works for ieee754 floating point. For other\n// floating point types, continue to rely on the libm implementation for now.\n#if defined(CRT_HAS_IEEE_TF)\nstatic __inline tf_float __compiler_rt_logbtf(tf_float x) {\n  return __compiler_rt_logbX(x);\n}\nstatic __inline tf_float __compiler_rt_scalbntf(tf_float x, int y) {\n  return __compiler_rt_scalbnX(x, y);\n}\nstatic __inline tf_float __compiler_rt_fmaxtf(tf_float x, tf_float y) {\n  return __compiler_rt_fmaxX(x, y);\n}\n#define __compiler_rt_logbl __compiler_rt_logbtf\n#define __compiler_rt_scalbnl __compiler_rt_scalbntf\n#define __compiler_rt_fmaxl __compiler_rt_fmaxtf\n#define crt_fabstf crt_fabsf128\n#define crt_copysigntf crt_copysignf128\n#elif defined(CRT_LDBL_128BIT)\nstatic __inline tf_float __compiler_rt_logbtf(tf_float x) { return crt_logbl(x); }\nstatic __inline tf_float __compiler_rt_scalbntf(tf_float x, int y) {\n  return crt_scalbnl(x, y);\n}\nstatic __inline tf_float __compiler_rt_fmaxtf(tf_float x, tf_float y) {\n  return crt_fmaxl(x, y);\n}\n#define __compiler_rt_logbl crt_logbl\n#define __compiler_rt_scalbnl crt_scalbnl\n#define __compiler_rt_fmaxl crt_fmaxl\n#define crt_fabstf crt_fabsl\n#define crt_copysigntf crt_copysignl\n#else\n#error Unsupported TF mode type\n#endif\n\n#endif // *_PRECISION\n\n#endif // FP_LIB_HEADER\n"
  },
  {
    "path": "codon/runtime/floatlib/fp_trunc.h",
    "content": "//=== lib/fp_trunc.h - high precision -> low precision conversion *- C -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// Set source and destination precision setting\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef FP_TRUNC_HEADER\n#define FP_TRUNC_HEADER\n\n#include \"int_lib.h\"\n\n#if defined SRC_SINGLE\ntypedef float src_t;\ntypedef uint32_t src_rep_t;\n#define SRC_REP_C UINT32_C\nstatic const int srcSigBits = 23;\n\n#elif defined SRC_DOUBLE\ntypedef double src_t;\ntypedef uint64_t src_rep_t;\n#define SRC_REP_C UINT64_C\nstatic const int srcSigBits = 52;\n\n#elif defined SRC_QUAD\ntypedef long double src_t;\ntypedef __uint128_t src_rep_t;\n#define SRC_REP_C (__uint128_t)\nstatic const int srcSigBits = 112;\n\n#else\n#error Source should be double precision or quad precision!\n#endif // end source precision\n\n#if defined DST_DOUBLE\ntypedef double dst_t;\ntypedef uint64_t dst_rep_t;\n#define DST_REP_C UINT64_C\nstatic const int dstSigBits = 52;\n\n#elif defined DST_SINGLE\ntypedef float dst_t;\ntypedef uint32_t dst_rep_t;\n#define DST_REP_C UINT32_C\nstatic const int dstSigBits = 23;\n\n#elif defined DST_HALF\n#ifdef COMPILER_RT_HAS_FLOAT16\ntypedef _Float16 dst_t;\n#else\ntypedef uint16_t dst_t;\n#endif\ntypedef uint16_t dst_rep_t;\n#define DST_REP_C UINT16_C\nstatic const int dstSigBits = 10;\n\n#elif defined DST_BFLOAT\ntypedef __bf16 dst_t;\ntypedef uint16_t dst_rep_t;\n#define DST_REP_C UINT16_C\nstatic const int dstSigBits = 7;\n\n#else\n#error Destination should be single precision or double precision!\n#endif // end destination precision\n\n// End of specialization parameters.  Two helper routines for conversion to and\n// from the representation of floating-point data as integer values follow.\n\nstatic __inline src_rep_t srcToRep(src_t x) {\n  const union {\n    src_t f;\n    src_rep_t i;\n  } rep = {.f = x};\n  return rep.i;\n}\n\nstatic __inline dst_t dstFromRep(dst_rep_t x) {\n  const union {\n    dst_t f;\n    dst_rep_t i;\n  } rep = {.i = x};\n  return rep.f;\n}\n\n#endif // FP_TRUNC_HEADER\n"
  },
  {
    "path": "codon/runtime/floatlib/fp_trunc_impl.inc",
    "content": "//= lib/fp_trunc_impl.inc - high precision -> low precision conversion *-*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// This file implements a fairly generic conversion from a wider to a narrower\n// IEEE-754 floating-point type in the default (round to nearest, ties to even)\n// rounding mode.  The constants and types defined following the includes below\n// parameterize the conversion.\n//\n// This routine can be trivially adapted to support conversions to\n// half-precision or from quad-precision. It does not support types that don't\n// use the usual IEEE-754 interchange formats; specifically, some work would be\n// needed to adapt it to (for example) the Intel 80-bit format or PowerPC\n// double-double format.\n//\n// Note please, however, that this implementation is only intended to support\n// *narrowing* operations; if you need to convert to a *wider* floating-point\n// type (e.g. float -> double), then this routine will not do what you want it\n// to.\n//\n// It also requires that integer types at least as large as both formats\n// are available on the target platform; this may pose a problem when trying\n// to add support for quad on some 32-bit systems, for example.\n//\n// Finally, the following assumptions are made:\n//\n// 1. Floating-point types and integer types have the same endianness on the\n//    target platform.\n//\n// 2. Quiet NaNs, if supported, are indicated by the leading bit of the\n//    significand field being set.\n//\n//===----------------------------------------------------------------------===//\n\n#include \"fp_trunc.h\"\n\nstatic __inline dst_t __truncXfYf2__(src_t a) {\n  // Various constants whose values follow from the type parameters.\n  // Any reasonable optimizer will fold and propagate all of these.\n  const int srcBits = sizeof(src_t) * CHAR_BIT;\n  const int srcExpBits = srcBits - srcSigBits - 1;\n  const int srcInfExp = (1 << srcExpBits) - 1;\n  const int srcExpBias = srcInfExp >> 1;\n\n  const src_rep_t srcMinNormal = SRC_REP_C(1) << srcSigBits;\n  const src_rep_t srcSignificandMask = srcMinNormal - 1;\n  const src_rep_t srcInfinity = (src_rep_t)srcInfExp << srcSigBits;\n  const src_rep_t srcSignMask = SRC_REP_C(1) << (srcSigBits + srcExpBits);\n  const src_rep_t srcAbsMask = srcSignMask - 1;\n  const src_rep_t roundMask = (SRC_REP_C(1) << (srcSigBits - dstSigBits)) - 1;\n  const src_rep_t halfway = SRC_REP_C(1) << (srcSigBits - dstSigBits - 1);\n  const src_rep_t srcQNaN = SRC_REP_C(1) << (srcSigBits - 1);\n  const src_rep_t srcNaNCode = srcQNaN - 1;\n\n  const int dstBits = sizeof(dst_t) * CHAR_BIT;\n  const int dstExpBits = dstBits - dstSigBits - 1;\n  const int dstInfExp = (1 << dstExpBits) - 1;\n  const int dstExpBias = dstInfExp >> 1;\n\n  const int underflowExponent = srcExpBias + 1 - dstExpBias;\n  const int overflowExponent = srcExpBias + dstInfExp - dstExpBias;\n  const src_rep_t underflow = (src_rep_t)underflowExponent << srcSigBits;\n  const src_rep_t overflow = (src_rep_t)overflowExponent << srcSigBits;\n\n  const dst_rep_t dstQNaN = DST_REP_C(1) << (dstSigBits - 1);\n  const dst_rep_t dstNaNCode = dstQNaN - 1;\n\n  // Break a into a sign and representation of the absolute value.\n  const src_rep_t aRep = srcToRep(a);\n  const src_rep_t aAbs = aRep & srcAbsMask;\n  const src_rep_t sign = aRep & srcSignMask;\n  dst_rep_t absResult;\n\n  if (aAbs - underflow < aAbs - overflow) {\n    // The exponent of a is within the range of normal numbers in the\n    // destination format.  We can convert by simply right-shifting with\n    // rounding and adjusting the exponent.\n    absResult = aAbs >> (srcSigBits - dstSigBits);\n    absResult -= (dst_rep_t)(srcExpBias - dstExpBias) << dstSigBits;\n\n    const src_rep_t roundBits = aAbs & roundMask;\n    // Round to nearest.\n    if (roundBits > halfway)\n      absResult++;\n    // Tie to even.\n    else if (roundBits == halfway)\n      absResult += absResult & 1;\n  } else if (aAbs > srcInfinity) {\n    // a is NaN.\n    // Conjure the result by beginning with infinity, setting the qNaN\n    // bit and inserting the (truncated) trailing NaN field.\n    absResult = (dst_rep_t)dstInfExp << dstSigBits;\n    absResult |= dstQNaN;\n    absResult |=\n        ((aAbs & srcNaNCode) >> (srcSigBits - dstSigBits)) & dstNaNCode;\n  } else if (aAbs >= overflow) {\n    // a overflows to infinity.\n    absResult = (dst_rep_t)dstInfExp << dstSigBits;\n  } else {\n    // a underflows on conversion to the destination type or is an exact\n    // zero.  The result may be a denormal or zero.  Extract the exponent\n    // to get the shift amount for the denormalization.\n    const int aExp = aAbs >> srcSigBits;\n    const int shift = srcExpBias - dstExpBias - aExp + 1;\n\n    const src_rep_t significand = (aRep & srcSignificandMask) | srcMinNormal;\n\n    // Right shift by the denormalization amount with sticky.\n    if (shift > srcSigBits) {\n      absResult = 0;\n    } else {\n      const bool sticky = (significand << (srcBits - shift)) != 0;\n      src_rep_t denormalizedSignificand = significand >> shift | sticky;\n      absResult = denormalizedSignificand >> (srcSigBits - dstSigBits);\n      const src_rep_t roundBits = denormalizedSignificand & roundMask;\n      // Round to nearest\n      if (roundBits > halfway)\n        absResult++;\n      // Ties to even\n      else if (roundBits == halfway)\n        absResult += absResult & 1;\n    }\n  }\n\n  // Apply the signbit to the absolute value.\n  const dst_rep_t result = absResult | sign >> (srcBits - dstBits);\n  return dstFromRep(result);\n}\n"
  },
  {
    "path": "codon/runtime/floatlib/int_endianness.h",
    "content": "//===-- int_endianness.h - configuration header for compiler-rt -----------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// This file is a configuration header for compiler-rt.\n// This file is not part of the interface of this library.\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef INT_ENDIANNESS_H\n#define INT_ENDIANNESS_H\n\n#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__) &&                        \\\n    defined(__ORDER_LITTLE_ENDIAN__)\n\n// Clang and GCC provide built-in endianness definitions.\n#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__\n#define _YUGA_LITTLE_ENDIAN 0\n#define _YUGA_BIG_ENDIAN 1\n#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__\n#define _YUGA_LITTLE_ENDIAN 1\n#define _YUGA_BIG_ENDIAN 0\n#endif // __BYTE_ORDER__\n\n#else // Compilers other than Clang or GCC.\n\n#if defined(__SVR4) && defined(__sun)\n#include <sys/byteorder.h>\n\n#if defined(_BIG_ENDIAN)\n#define _YUGA_LITTLE_ENDIAN 0\n#define _YUGA_BIG_ENDIAN 1\n#elif defined(_LITTLE_ENDIAN)\n#define _YUGA_LITTLE_ENDIAN 1\n#define _YUGA_BIG_ENDIAN 0\n#else // !_LITTLE_ENDIAN\n#error \"unknown endianness\"\n#endif // !_LITTLE_ENDIAN\n\n#endif // Solaris\n\n// ..\n\n#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) ||           \\\n    defined(__minix)\n#include <sys/endian.h>\n\n#if _BYTE_ORDER == _BIG_ENDIAN\n#define _YUGA_LITTLE_ENDIAN 0\n#define _YUGA_BIG_ENDIAN 1\n#elif _BYTE_ORDER == _LITTLE_ENDIAN\n#define _YUGA_LITTLE_ENDIAN 1\n#define _YUGA_BIG_ENDIAN 0\n#endif // _BYTE_ORDER\n\n#endif // *BSD\n\n#if defined(__OpenBSD__)\n#include <machine/endian.h>\n\n#if _BYTE_ORDER == _BIG_ENDIAN\n#define _YUGA_LITTLE_ENDIAN 0\n#define _YUGA_BIG_ENDIAN 1\n#elif _BYTE_ORDER == _LITTLE_ENDIAN\n#define _YUGA_LITTLE_ENDIAN 1\n#define _YUGA_BIG_ENDIAN 0\n#endif // _BYTE_ORDER\n\n#endif // OpenBSD\n\n// ..\n\n// Mac OSX has __BIG_ENDIAN__ or __LITTLE_ENDIAN__ automatically set by the\n// compiler (at least with GCC)\n#if defined(__APPLE__) || defined(__ellcc__)\n\n#ifdef __BIG_ENDIAN__\n#if __BIG_ENDIAN__\n#define _YUGA_LITTLE_ENDIAN 0\n#define _YUGA_BIG_ENDIAN 1\n#endif\n#endif // __BIG_ENDIAN__\n\n#ifdef __LITTLE_ENDIAN__\n#if __LITTLE_ENDIAN__\n#define _YUGA_LITTLE_ENDIAN 1\n#define _YUGA_BIG_ENDIAN 0\n#endif\n#endif // __LITTLE_ENDIAN__\n\n#endif // Mac OSX\n\n// ..\n\n#if defined(_WIN32)\n\n#define _YUGA_LITTLE_ENDIAN 1\n#define _YUGA_BIG_ENDIAN 0\n\n#endif // Windows\n\n#endif // Clang or GCC.\n\n// .\n\n#if !defined(_YUGA_LITTLE_ENDIAN) || !defined(_YUGA_BIG_ENDIAN)\n#error Unable to determine endian\n#endif // Check we found an endianness correctly.\n\n#endif // INT_ENDIANNESS_H\n"
  },
  {
    "path": "codon/runtime/floatlib/int_lib.h",
    "content": "//===-- int_lib.h - configuration header for compiler-rt  -----------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// This file is a configuration header for compiler-rt.\n// This file is not part of the interface of this library.\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef INT_LIB_H\n#define INT_LIB_H\n\n// Assumption: Signed integral is 2's complement.\n// Assumption: Right shift of signed negative is arithmetic shift.\n// Assumption: Endianness is little or big (not mixed).\n\n// ABI macro definitions\n\n#if __ARM_EABI__\n#ifdef COMPILER_RT_ARMHF_TARGET\n#define COMPILER_RT_ABI\n#else\n#define COMPILER_RT_ABI __attribute__((__pcs__(\"aapcs\")))\n#endif\n#else\n#define COMPILER_RT_ABI\n#endif\n\n#define AEABI_RTABI __attribute__((__pcs__(\"aapcs\")))\n\n#if defined(_MSC_VER) && !defined(__clang__)\n#define ALWAYS_INLINE __forceinline\n#define NOINLINE __declspec(noinline)\n#define NORETURN __declspec(noreturn)\n#define UNUSED\n#else\n#define ALWAYS_INLINE __attribute__((always_inline))\n#define NOINLINE __attribute__((noinline))\n#define NORETURN __attribute__((noreturn))\n#define UNUSED __attribute__((unused))\n#endif\n\n#define STR(a) #a\n#define XSTR(a) STR(a)\n#define SYMBOL_NAME(name) XSTR(__USER_LABEL_PREFIX__) #name\n\n#if defined(__ELF__) || defined(__MINGW32__) || defined(__wasm__) || defined(_AIX)\n#define COMPILER_RT_ALIAS(name, aliasname)                                             \\\n  COMPILER_RT_ABI __typeof(name) aliasname __attribute__((__alias__(#name)));\n#elif defined(__APPLE__)\n#if defined(VISIBILITY_HIDDEN)\n#define COMPILER_RT_ALIAS_VISIBILITY(name)                                             \\\n  __asm__(\".private_extern \" SYMBOL_NAME(name));\n#else\n#define COMPILER_RT_ALIAS_VISIBILITY(name)\n#endif\n#define COMPILER_RT_ALIAS(name, aliasname)                                             \\\n  __asm__(\".globl \" SYMBOL_NAME(aliasname));                                           \\\n  COMPILER_RT_ALIAS_VISIBILITY(aliasname)                                              \\\n  __asm__(SYMBOL_NAME(aliasname) \" = \" SYMBOL_NAME(name));                             \\\n  COMPILER_RT_ABI __typeof(name) aliasname;\n#elif defined(_WIN32)\n#define COMPILER_RT_ALIAS(name, aliasname)\n#else\n#error Unsupported target\n#endif\n\n#if (defined(__FreeBSD__) || defined(__NetBSD__)) &&                                   \\\n    (defined(_KERNEL) || defined(_STANDALONE))\n//\n// Kernel and boot environment can't use normal headers,\n// so use the equivalent system headers.\n// NB: FreeBSD (and OpenBSD) deprecate machine/limits.h in\n// favour of sys/limits.h, so prefer the former, but fall\n// back on the latter if not available since NetBSD only has\n// the latter.\n//\n#if defined(__has_include) && __has_include(<sys/limits.h>)\n#include <sys/limits.h>\n#else\n#include <machine/limits.h>\n#endif\n#include <sys/stdint.h>\n#include <sys/types.h>\n#else\n// Include the standard compiler builtin headers we use functionality from.\n#include <float.h>\n#include <limits.h>\n#include <stdbool.h>\n#include <stdint.h>\n#endif\n\n// Include the commonly used internal type definitions.\n#include \"int_types.h\"\n\n// Include internal utility function declarations.\n#include \"int_util.h\"\n\nCOMPILER_RT_ABI int __paritysi2(si_int a);\nCOMPILER_RT_ABI int __paritydi2(di_int a);\n\nCOMPILER_RT_ABI di_int __divdi3(di_int a, di_int b);\nCOMPILER_RT_ABI si_int __divsi3(si_int a, si_int b);\nCOMPILER_RT_ABI su_int __udivsi3(su_int n, su_int d);\n\nCOMPILER_RT_ABI su_int __udivmodsi4(su_int a, su_int b, su_int *rem);\nCOMPILER_RT_ABI du_int __udivmoddi4(du_int a, du_int b, du_int *rem);\n#ifdef CRT_HAS_128BIT\nCOMPILER_RT_ABI int __clzti2(ti_int a);\nCOMPILER_RT_ABI tu_int __udivmodti4(tu_int a, tu_int b, tu_int *rem);\n#endif\n\n// Definitions for builtins unavailable on MSVC\n#if defined(_MSC_VER) && !defined(__clang__)\n#include <intrin.h>\n\nint __inline __builtin_ctz(uint32_t value) {\n  unsigned long trailing_zero = 0;\n  if (_BitScanForward(&trailing_zero, value))\n    return trailing_zero;\n  return 32;\n}\n\nint __inline __builtin_clz(uint32_t value) {\n  unsigned long leading_zero = 0;\n  if (_BitScanReverse(&leading_zero, value))\n    return 31 - leading_zero;\n  return 32;\n}\n\n#if defined(_M_ARM) || defined(_M_X64)\nint __inline __builtin_clzll(uint64_t value) {\n  unsigned long leading_zero = 0;\n  if (_BitScanReverse64(&leading_zero, value))\n    return 63 - leading_zero;\n  return 64;\n}\n#else\nint __inline __builtin_clzll(uint64_t value) {\n  if (value == 0)\n    return 64;\n  uint32_t msh = (uint32_t)(value >> 32);\n  uint32_t lsh = (uint32_t)(value & 0xFFFFFFFF);\n  if (msh != 0)\n    return __builtin_clz(msh);\n  return 32 + __builtin_clz(lsh);\n}\n#endif\n\n#define __builtin_clzl __builtin_clzll\n\nbool __inline __builtin_sadd_overflow(int x, int y, int *result) {\n  if ((x < 0) != (y < 0)) {\n    *result = x + y;\n    return false;\n  }\n  int tmp = (unsigned int)x + (unsigned int)y;\n  if ((tmp < 0) != (x < 0))\n    return true;\n  *result = tmp;\n  return false;\n}\n\n#endif // defined(_MSC_VER) && !defined(__clang__)\n\n#endif // INT_LIB_H\n"
  },
  {
    "path": "codon/runtime/floatlib/int_math.h",
    "content": "//===-- int_math.h - internal math inlines --------------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// This file is not part of the interface of this library.\n//\n// This file defines substitutes for the libm functions used in some of the\n// compiler-rt implementations, defined in such a way that there is not a direct\n// dependency on libm or math.h. Instead, we use the compiler builtin versions\n// where available. This reduces our dependencies on the system SDK by foisting\n// the responsibility onto the compiler.\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef INT_MATH_H\n#define INT_MATH_H\n\n#ifndef __has_builtin\n#define __has_builtin(x) 0\n#endif\n\n#if defined(_MSC_VER) && !defined(__clang__)\n#include <math.h>\n#include <stdlib.h>\n#endif\n\n#if defined(_MSC_VER) && !defined(__clang__)\n#define CRT_INFINITY INFINITY\n#else\n#define CRT_INFINITY __builtin_huge_valf()\n#endif\n\n#if defined(_MSC_VER) && !defined(__clang__)\n#define crt_isfinite(x) _finite((x))\n#define crt_isinf(x) !_finite((x))\n#define crt_isnan(x) _isnan((x))\n#else\n// Define crt_isfinite in terms of the builtin if available, otherwise provide\n// an alternate version in terms of our other functions. This supports some\n// versions of GCC which didn't have __builtin_isfinite.\n#if __has_builtin(__builtin_isfinite)\n#define crt_isfinite(x) __builtin_isfinite((x))\n#elif defined(__GNUC__)\n#define crt_isfinite(x)                                                                \\\n  __extension__(({                                                                     \\\n    __typeof((x)) x_ = (x);                                                            \\\n    !crt_isinf(x_) && !crt_isnan(x_);                                                  \\\n  }))\n#else\n#error \"Do not know how to check for infinity\"\n#endif // __has_builtin(__builtin_isfinite)\n#define crt_isinf(x) __builtin_isinf((x))\n#define crt_isnan(x) __builtin_isnan((x))\n#endif // _MSC_VER\n\n#if defined(_MSC_VER) && !defined(__clang__)\n#define crt_copysign(x, y) copysign((x), (y))\n#define crt_copysignf(x, y) copysignf((x), (y))\n#define crt_copysignl(x, y) copysignl((x), (y))\n#else\n#define crt_copysign(x, y) __builtin_copysign((x), (y))\n#define crt_copysignf(x, y) __builtin_copysignf((x), (y))\n#define crt_copysignl(x, y) __builtin_copysignl((x), (y))\n#endif\n\n#if defined(_MSC_VER) && !defined(__clang__)\n#define crt_fabs(x) fabs((x))\n#define crt_fabsf(x) fabsf((x))\n#define crt_fabsl(x) fabs((x))\n#else\n#define crt_fabs(x) __builtin_fabs((x))\n#define crt_fabsf(x) __builtin_fabsf((x))\n#define crt_fabsl(x) __builtin_fabsl((x))\n#endif\n\n#if defined(_MSC_VER) && !defined(__clang__)\n#define crt_fmaxl(x, y) __max((x), (y))\n#else\n#define crt_fmaxl(x, y) __builtin_fmaxl((x), (y))\n#endif\n\n#if defined(_MSC_VER) && !defined(__clang__)\n#define crt_logbl(x) logbl((x))\n#else\n#define crt_logbl(x) __builtin_logbl((x))\n#endif\n\n#if defined(_MSC_VER) && !defined(__clang__)\n#define crt_scalbnl(x, y) scalbnl((x), (y))\n#else\n#define crt_scalbnl(x, y) __builtin_scalbnl((x), (y))\n#endif\n\n#endif // INT_MATH_H\n"
  },
  {
    "path": "codon/runtime/floatlib/int_types.h",
    "content": "//===-- int_lib.h - configuration header for compiler-rt  -----------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// This file is not part of the interface of this library.\n//\n// This file defines various standard types, most importantly a number of unions\n// used to access parts of larger types.\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef INT_TYPES_H\n#define INT_TYPES_H\n\n#include \"int_endianness.h\"\n\n// si_int is defined in Linux sysroot's asm-generic/siginfo.h\n#ifdef si_int\n#undef si_int\n#endif\ntypedef int32_t si_int;\ntypedef uint32_t su_int;\n#if UINT_MAX == 0xFFFFFFFF\n#define clzsi __builtin_clz\n#define ctzsi __builtin_ctz\n#elif ULONG_MAX == 0xFFFFFFFF\n#define clzsi __builtin_clzl\n#define ctzsi __builtin_ctzl\n#else\n#error could not determine appropriate clzsi macro for this system\n#endif\n\ntypedef int64_t di_int;\ntypedef uint64_t du_int;\n\ntypedef union {\n  di_int all;\n  struct {\n#if _YUGA_LITTLE_ENDIAN\n    su_int low;\n    si_int high;\n#else\n    si_int high;\n    su_int low;\n#endif // _YUGA_LITTLE_ENDIAN\n  } s;\n} dwords;\n\ntypedef union {\n  du_int all;\n  struct {\n#if _YUGA_LITTLE_ENDIAN\n    su_int low;\n    su_int high;\n#else\n    su_int high;\n    su_int low;\n#endif // _YUGA_LITTLE_ENDIAN\n  } s;\n} udwords;\n\n#if defined(__LP64__) || defined(__wasm__) || defined(__mips64) ||                     \\\n    defined(__SIZEOF_INT128__) || defined(_WIN64)\n#define CRT_HAS_128BIT\n#endif\n\n// MSVC doesn't have a working 128bit integer type. Users should really compile\n// compiler-rt with clang, but if they happen to be doing a standalone build for\n// asan or something else, disable the 128 bit parts so things sort of work.\n#if defined(_MSC_VER) && !defined(__clang__)\n#undef CRT_HAS_128BIT\n#endif\n\n#ifdef CRT_HAS_128BIT\ntypedef int ti_int __attribute__((mode(TI)));\ntypedef unsigned tu_int __attribute__((mode(TI)));\n\ntypedef union {\n  ti_int all;\n  struct {\n#if _YUGA_LITTLE_ENDIAN\n    du_int low;\n    di_int high;\n#else\n    di_int high;\n    du_int low;\n#endif // _YUGA_LITTLE_ENDIAN\n  } s;\n} twords;\n\ntypedef union {\n  tu_int all;\n  struct {\n#if _YUGA_LITTLE_ENDIAN\n    du_int low;\n    du_int high;\n#else\n    du_int high;\n    du_int low;\n#endif // _YUGA_LITTLE_ENDIAN\n  } s;\n} utwords;\n\nstatic __inline ti_int make_ti(di_int h, di_int l) {\n  twords r;\n  r.s.high = h;\n  r.s.low = l;\n  return r.all;\n}\n\nstatic __inline tu_int make_tu(du_int h, du_int l) {\n  utwords r;\n  r.s.high = h;\n  r.s.low = l;\n  return r.all;\n}\n\n#endif // CRT_HAS_128BIT\n\n// FreeBSD's boot environment does not support using floating-point and poisons\n// the float and double keywords.\n#if defined(__FreeBSD__) && defined(_STANDALONE)\n#define CRT_HAS_FLOATING_POINT 0\n#else\n#define CRT_HAS_FLOATING_POINT 1\n#endif\n\n#if CRT_HAS_FLOATING_POINT\ntypedef union {\n  su_int u;\n  float f;\n} float_bits;\n\ntypedef union {\n  udwords u;\n  double f;\n} double_bits;\n#endif\n\ntypedef struct {\n#if _YUGA_LITTLE_ENDIAN\n  udwords low;\n  udwords high;\n#else\n  udwords high;\n  udwords low;\n#endif // _YUGA_LITTLE_ENDIAN\n} uqwords;\n\n// Check if the target supports 80 bit extended precision long doubles.\n// Notably, on x86 Windows, MSVC only provides a 64-bit long double, but GCC\n// still makes it 80 bits. Clang will match whatever compiler it is trying to\n// be compatible with. On 32-bit x86 Android, long double is 64 bits, while on\n// x86_64 Android, long double is 128 bits.\n#if (defined(__i386__) || defined(__x86_64__)) &&                                      \\\n    !(defined(_MSC_VER) || defined(__ANDROID__))\n#define HAS_80_BIT_LONG_DOUBLE 1\n#elif defined(__m68k__) || defined(__ia64__)\n#define HAS_80_BIT_LONG_DOUBLE 1\n#else\n#define HAS_80_BIT_LONG_DOUBLE 0\n#endif\n\n#if CRT_HAS_FLOATING_POINT\ntypedef union {\n  uqwords u;\n  long double f;\n} long_double_bits;\n\n#if __STDC_VERSION__ >= 199901L\ntypedef float _Complex Fcomplex;\ntypedef double _Complex Dcomplex;\ntypedef long double _Complex Lcomplex;\n\n#define COMPLEX_REAL(x) __real__(x)\n#define COMPLEX_IMAGINARY(x) __imag__(x)\n#else\ntypedef struct {\n  float real, imaginary;\n} Fcomplex;\n\ntypedef struct {\n  double real, imaginary;\n} Dcomplex;\n\ntypedef struct {\n  long double real, imaginary;\n} Lcomplex;\n\n#define COMPLEX_REAL(x) (x).real\n#define COMPLEX_IMAGINARY(x) (x).imaginary\n#endif\n#endif\n#endif // INT_TYPES_H\n"
  },
  {
    "path": "codon/runtime/floatlib/int_util.h",
    "content": "//===-- int_util.h - internal utility functions ---------------------------===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n//\n// This file is not part of the interface of this library.\n//\n// This file defines non-inline utilities which are available for use in the\n// library. The function definitions themselves are all contained in int_util.c\n// which will always be compiled into any compiler-rt library.\n//\n//===----------------------------------------------------------------------===//\n\n#ifndef INT_UTIL_H\n#define INT_UTIL_H\n\n/// \\brief Trigger a program abort (or panic for kernel code).\n#define compilerrt_abort() __compilerrt_abort_impl(__FILE__, __LINE__, __func__)\n\nNORETURN void __compilerrt_abort_impl(const char *file, int line, const char *function);\n\n#define COMPILE_TIME_ASSERT(expr) COMPILE_TIME_ASSERT1(expr, __COUNTER__)\n#define COMPILE_TIME_ASSERT1(expr, cnt) COMPILE_TIME_ASSERT2(expr, cnt)\n#define COMPILE_TIME_ASSERT2(expr, cnt)                                                \\\n  typedef char ct_assert_##cnt[(expr) ? 1 : -1] UNUSED\n\n// Force unrolling the code specified to be repeated N times.\n#define REPEAT_0_TIMES(code_to_repeat) /* do nothing */\n#define REPEAT_1_TIMES(code_to_repeat) code_to_repeat\n#define REPEAT_2_TIMES(code_to_repeat)                                                 \\\n  REPEAT_1_TIMES(code_to_repeat)                                                       \\\n  code_to_repeat\n#define REPEAT_3_TIMES(code_to_repeat)                                                 \\\n  REPEAT_2_TIMES(code_to_repeat)                                                       \\\n  code_to_repeat\n#define REPEAT_4_TIMES(code_to_repeat)                                                 \\\n  REPEAT_3_TIMES(code_to_repeat)                                                       \\\n  code_to_repeat\n\n#define REPEAT_N_TIMES_(N, code_to_repeat) REPEAT_##N##_TIMES(code_to_repeat)\n#define REPEAT_N_TIMES(N, code_to_repeat) REPEAT_N_TIMES_(N, code_to_repeat)\n\n#endif // INT_UTIL_H\n"
  },
  {
    "path": "codon/runtime/floatlib/truncdfbf2.c",
    "content": "//===-- lib/truncdfbf2.c - double -> bfloat conversion ------------*- C -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n\n#define SRC_DOUBLE\n#define DST_BFLOAT\n#include \"fp_trunc_impl.inc\"\n\nCOMPILER_RT_ABI dst_t __truncdfbf2(double a) { return __truncXfYf2__(a); }\n"
  },
  {
    "path": "codon/runtime/floatlib/truncdfhf2.c",
    "content": "//===-- lib/truncdfhf2.c - double -> half conversion --------------*- C -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n\n#define SRC_DOUBLE\n#define DST_HALF\n#include \"fp_trunc_impl.inc\"\n\nCOMPILER_RT_ABI dst_t __truncdfhf2(double a) { return __truncXfYf2__(a); }\n\n#if defined(__ARM_EABI__)\n#if defined(COMPILER_RT_ARMHF_TARGET)\nAEABI_RTABI dst_t __aeabi_d2h(double a) { return __truncdfhf2(a); }\n#else\nCOMPILER_RT_ALIAS(__truncdfhf2, __aeabi_d2h)\n#endif\n#endif\n"
  },
  {
    "path": "codon/runtime/floatlib/truncdfsf2.c",
    "content": "//===-- lib/truncdfsf2.c - double -> single conversion ------------*- C -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n\n#define SRC_DOUBLE\n#define DST_SINGLE\n#include \"fp_trunc_impl.inc\"\n\nCOMPILER_RT_ABI float __truncdfsf2(double a) { return __truncXfYf2__(a); }\n\n#if defined(__ARM_EABI__)\n#if defined(COMPILER_RT_ARMHF_TARGET)\nAEABI_RTABI float __aeabi_d2f(double a) { return __truncdfsf2(a); }\n#else\nCOMPILER_RT_ALIAS(__truncdfsf2, __aeabi_d2f)\n#endif\n#endif\n"
  },
  {
    "path": "codon/runtime/floatlib/truncsfbf2.c",
    "content": "//===-- lib/truncsfbf2.c - single -> bfloat conversion ------------*- C -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n\n#define SRC_SINGLE\n#define DST_BFLOAT\n#include \"fp_trunc_impl.inc\"\n\nCOMPILER_RT_ABI dst_t __truncsfbf2(float a) { return __truncXfYf2__(a); }\n"
  },
  {
    "path": "codon/runtime/floatlib/truncsfhf2.c",
    "content": "//===-- lib/truncsfhf2.c - single -> half conversion --------------*- C -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n\n#define SRC_SINGLE\n#define DST_HALF\n#include \"fp_trunc_impl.inc\"\n\n// Use a forwarding definition and noinline to implement a poor man's alias,\n// as there isn't a good cross-platform way of defining one.\nCOMPILER_RT_ABI NOINLINE dst_t __truncsfhf2(float a) {\n  return __truncXfYf2__(a);\n}\n\nCOMPILER_RT_ABI dst_t __gnu_f2h_ieee(float a) { return __truncsfhf2(a); }\n\n#if defined(__ARM_EABI__)\n#if defined(COMPILER_RT_ARMHF_TARGET)\nAEABI_RTABI dst_t __aeabi_f2h(float a) { return __truncsfhf2(a); }\n#else\nCOMPILER_RT_ALIAS(__truncsfhf2, __aeabi_f2h)\n#endif\n#endif\n"
  },
  {
    "path": "codon/runtime/floatlib/trunctfdf2.c",
    "content": "//===-- lib/truncdfsf2.c - quad -> double conversion --------------*- C -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n\n#define QUAD_PRECISION\n#include \"fp_lib.h\"\n\n#if defined(CRT_HAS_TF_MODE)\n#define SRC_QUAD\n#define DST_DOUBLE\n#include \"fp_trunc_impl.inc\"\n\nCOMPILER_RT_ABI double __trunctfdf2(long double a) { return __truncXfYf2__(a); }\n\n#endif\n"
  },
  {
    "path": "codon/runtime/floatlib/trunctfhf2.c",
    "content": "//===-- lib/trunctfhf2.c - quad -> half conversion ----------------*- C -*-===//\n//\n//                     The LLVM Compiler Infrastructure\n//\n// This file is dual licensed under the MIT and the University of Illinois Open\n// Source Licenses. See LICENSE.TXT for details.\n//\n//===----------------------------------------------------------------------===//\n\n#define QUAD_PRECISION\n#include \"fp_lib.h\"\n\n#if defined(CRT_HAS_TF_MODE) && defined(COMPILER_RT_HAS_FLOAT16)\n#define SRC_QUAD\n#define DST_HALF\n#include \"fp_trunc_impl.inc\"\n\nCOMPILER_RT_ABI _Float16 __trunctfhf2(long double a) {\n  return __truncXfYf2__(a);\n}\n\n#endif\n"
  },
  {
    "path": "codon/runtime/floatlib/trunctfsf2.c",
    "content": "//===-- lib/trunctfsf2.c - quad -> single conversion --------------*- C -*-===//\n//\n// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.\n// See https://llvm.org/LICENSE.txt for license information.\n// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n//\n//===----------------------------------------------------------------------===//\n\n#define QUAD_PRECISION\n#include \"fp_lib.h\"\n\n#if defined(CRT_HAS_TF_MODE)\n#define SRC_QUAD\n#define DST_SINGLE\n#include \"fp_trunc_impl.inc\"\n\nCOMPILER_RT_ABI float __trunctfsf2(long double a) { return __truncXfYf2__(a); }\n\n#endif\n"
  },
  {
    "path": "codon/runtime/lib.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <cassert>\n#include <cerrno>\n#include <chrono>\n#include <climits>\n#include <cstddef>\n#include <cstdint>\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n#include <ctime>\n#include <fmt/format.h>\n#include <fstream>\n#include <iostream>\n#include <mutex>\n#include <sstream>\n#include <string>\n#include <thread>\n#include <unistd.h>\n#include <unwind.h>\n#include <vector>\n\n#define GC_THREADS\n#include \"codon/runtime/lib.h\"\n#include <dlfcn.h>\n#include <gc.h>\n\n#define FASTFLOAT_ALLOWS_LEADING_PLUS\n#define FASTFLOAT_SKIP_WHITE_SPACE\n#include \"fast_float/fast_float.h\"\n\n/*\n * General\n */\n\n#define USE_STANDARD_MALLOC 0\n\n// OpenMP patch with GC callbacks\ntypedef int (*gc_setup_callback)(GC_stack_base *);\ntypedef void (*gc_roots_callback)(void *, void *);\nextern \"C\" void __kmpc_set_gc_callbacks(gc_setup_callback get_stack_base,\n                                        gc_setup_callback register_thread,\n                                        gc_roots_callback add_roots,\n                                        gc_roots_callback del_roots);\n\nvoid seq_exc_init(int flags);\n\nint seq_flags;\n\nSEQ_FUNC void seq_init(int flags) {\n#if !USE_STANDARD_MALLOC\n  GC_INIT();\n  GC_set_warn_proc(GC_ignore_warn_proc);\n  GC_allow_register_threads();\n  __kmpc_set_gc_callbacks(GC_get_stack_base, (gc_setup_callback)GC_register_my_thread,\n                          GC_add_roots, GC_remove_roots);\n#endif\n\n  seq_exc_init(flags);\n  seq_flags = flags;\n}\n\nSEQ_FUNC seq_int_t seq_pid() { return (seq_int_t)getpid(); }\n\nSEQ_FUNC seq_int_t seq_time() {\n  auto duration = std::chrono::system_clock::now().time_since_epoch();\n  seq_int_t nanos =\n      std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();\n  return nanos;\n}\n\nSEQ_FUNC seq_int_t seq_time_monotonic() {\n  auto duration = std::chrono::steady_clock::now().time_since_epoch();\n  seq_int_t nanos =\n      std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();\n  return nanos;\n}\n\nSEQ_FUNC seq_int_t seq_time_highres() {\n  auto duration = std::chrono::high_resolution_clock::now().time_since_epoch();\n  seq_int_t nanos =\n      std::chrono::duration_cast<std::chrono::nanoseconds>(duration).count();\n  return nanos;\n}\n\nstatic void copy_time_c_to_seq(struct tm *x, seq_time_t *output) {\n  output->year = x->tm_year;\n  output->yday = x->tm_yday;\n  output->sec = x->tm_sec;\n  output->min = x->tm_min;\n  output->hour = x->tm_hour;\n  output->mday = x->tm_mday;\n  output->mon = x->tm_mon;\n  output->wday = x->tm_wday;\n  output->isdst = x->tm_isdst;\n}\n\nstatic void copy_time_seq_to_c(seq_time_t *x, struct tm *output) {\n  output->tm_year = x->year;\n  output->tm_yday = x->yday;\n  output->tm_sec = x->sec;\n  output->tm_min = x->min;\n  output->tm_hour = x->hour;\n  output->tm_mday = x->mday;\n  output->tm_mon = x->mon;\n  output->tm_wday = x->wday;\n  output->tm_isdst = x->isdst;\n}\n\nSEQ_FUNC bool seq_localtime(seq_int_t secs, seq_time_t *output) {\n  struct tm result;\n  time_t now = (secs >= 0 ? secs : time(nullptr));\n  if (now == (time_t)-1 || !localtime_r(&now, &result))\n    return false;\n  copy_time_c_to_seq(&result, output);\n  return true;\n}\n\nSEQ_FUNC bool seq_gmtime(seq_int_t secs, seq_time_t *output) {\n  struct tm result;\n  time_t now = (secs >= 0 ? secs : time(nullptr));\n  if (now == (time_t)-1 || !gmtime_r(&now, &result))\n    return false;\n  copy_time_c_to_seq(&result, output);\n  return true;\n}\n\nSEQ_FUNC seq_int_t seq_mktime(seq_time_t *time) {\n  struct tm result;\n  copy_time_seq_to_c(time, &result);\n  return mktime(&result);\n}\n\nSEQ_FUNC void seq_sleep(double secs) {\n  std::this_thread::sleep_for(std::chrono::duration<double, std::ratio<1>>(secs));\n}\n\nextern char **environ;\nSEQ_FUNC char **seq_env() { return environ; }\n\n/*\n * GC\n */\n\nSEQ_FUNC void *seq_alloc(size_t n) {\n#if USE_STANDARD_MALLOC\n  return malloc(n);\n#else\n  return GC_MALLOC(n);\n#endif\n}\n\nSEQ_FUNC void *seq_alloc_atomic(size_t n) {\n#if USE_STANDARD_MALLOC\n  return malloc(n);\n#else\n  return GC_MALLOC_ATOMIC(n);\n#endif\n}\n\nSEQ_FUNC void *seq_alloc_uncollectable(size_t n) {\n#if USE_STANDARD_MALLOC\n  return malloc(n);\n#else\n  return GC_MALLOC_UNCOLLECTABLE(n);\n#endif\n}\n\nSEQ_FUNC void *seq_alloc_atomic_uncollectable(size_t n) {\n#if USE_STANDARD_MALLOC\n  return malloc(n);\n#else\n  return GC_MALLOC_ATOMIC_UNCOLLECTABLE(n);\n#endif\n}\n\nSEQ_FUNC void *seq_realloc(void *p, size_t newsize, size_t oldsize) {\n#if USE_STANDARD_MALLOC\n  return realloc(p, newsize);\n#else\n  return GC_REALLOC(p, newsize);\n#endif\n}\n\nSEQ_FUNC void seq_free(void *p) {\n#if USE_STANDARD_MALLOC\n  free(p);\n#else\n  GC_FREE(p);\n#endif\n}\n\nSEQ_FUNC void seq_register_finalizer(void *p, void (*f)(void *obj, void *data)) {\n#if !USE_STANDARD_MALLOC\n  GC_REGISTER_FINALIZER(p, f, nullptr, nullptr, nullptr);\n#endif\n}\n\nSEQ_FUNC void seq_gc_add_roots(void *start, void *end) {\n#if !USE_STANDARD_MALLOC\n  GC_add_roots(start, end);\n#endif\n}\n\nSEQ_FUNC void seq_gc_remove_roots(void *start, void *end) {\n#if !USE_STANDARD_MALLOC\n  GC_remove_roots(start, end);\n#endif\n}\n\nSEQ_FUNC void seq_gc_clear_roots() {\n#if !USE_STANDARD_MALLOC\n  GC_clear_roots();\n#endif\n}\n\nSEQ_FUNC void seq_gc_exclude_static_roots(void *start, void *end) {\n#if !USE_STANDARD_MALLOC\n  GC_exclude_static_roots(start, end);\n#endif\n}\n\n/*\n * String conversion\n */\nstatic seq_str_t string_conv(const std::string &s) {\n  auto n = s.size();\n  auto *p = (char *)seq_alloc_atomic(n);\n  memcpy(p, s.data(), n);\n  return {(seq_int_t)n, p};\n}\n\ntemplate <typename T> std::string default_format(T n) {\n  return fmt::format(FMT_STRING(\"{}\"), n);\n}\n\ntemplate <> std::string default_format(double n) {\n  return fmt::format(FMT_STRING(\"{:g}\"), n);\n}\n\ntemplate <typename T> seq_str_t fmt_conv(T n, seq_str_t format, bool *error) {\n  *error = false;\n  try {\n    if (format.len == 0) {\n      return string_conv(default_format(n));\n    } else {\n      auto locale = std::locale(\"en_US.UTF-8\");\n      std::string fstr(format.str, format.len);\n      return string_conv(fmt::format(\n          locale, fmt::runtime(fmt::format(FMT_STRING(\"{{:{}}}\"), fstr)), n));\n    }\n  } catch (const std::runtime_error &f) {\n    *error = true;\n    return string_conv(f.what());\n  }\n}\n\nSEQ_FUNC seq_str_t seq_str_int(seq_int_t n, seq_str_t format, bool *error) {\n  return fmt_conv<seq_int_t>(n, format, error);\n}\n\nSEQ_FUNC seq_str_t seq_str_uint(seq_int_t n, seq_str_t format, bool *error) {\n  return fmt_conv<uint64_t>(n, format, error);\n}\n\nSEQ_FUNC seq_str_t seq_str_float(double f, seq_str_t format, bool *error) {\n  return fmt_conv<double>(f, format, error);\n}\n\nSEQ_FUNC seq_str_t seq_str_ptr(void *p, seq_str_t format, bool *error) {\n  return fmt_conv(fmt::ptr(p), format, error);\n}\n\nSEQ_FUNC seq_str_t seq_str_str(seq_str_t s, seq_str_t format, bool *error) {\n  std::string t(s.str, s.len);\n  return fmt_conv(t, format, error);\n}\n\nSEQ_FUNC seq_int_t seq_int_from_str(seq_str_t s, const char **e, int base) {\n  seq_int_t result;\n  auto r = fast_float::from_chars(s.str, s.str + s.len, result, base);\n  *e = (r.ec == std::errc()) ? r.ptr : s.str;\n  return result;\n}\n\nSEQ_FUNC double seq_float_from_str(seq_str_t s, const char **e) {\n  double result;\n  auto r = fast_float::from_chars(s.str, s.str + s.len, result);\n  *e = (r.ec == std::errc() || r.ec == std::errc::result_out_of_range) ? r.ptr : s.str;\n  return result;\n}\n\n/*\n * General I/O\n */\n\nSEQ_FUNC seq_str_t seq_check_errno() {\n  if (errno) {\n    std::string msg = strerror(errno);\n    auto *buf = (char *)seq_alloc_atomic(msg.size());\n    memcpy(buf, msg.data(), msg.size());\n    return {(seq_int_t)msg.size(), buf};\n  }\n  return {0, nullptr};\n}\n\nSEQ_FUNC void seq_print(seq_str_t str) { seq_print_full(str, stdout); }\n\nstatic std::ostringstream capture;\nstatic std::mutex captureLock;\n\nSEQ_FUNC void seq_print_full(seq_str_t str, FILE *fo) {\n  if ((seq_flags & SEQ_FLAG_CAPTURE_OUTPUT) && (fo == stdout || fo == stderr)) {\n    captureLock.lock();\n    capture.write(str.str, str.len);\n    captureLock.unlock();\n  } else {\n    fwrite(str.str, 1, (size_t)str.len, fo);\n  }\n}\n\nstd::string codon::runtime::getCapturedOutput() {\n  std::string result = capture.str();\n  capture.str(\"\");\n  return result;\n}\n\nSEQ_FUNC void *seq_stdin() { return stdin; }\n\nSEQ_FUNC void *seq_stdout() { return stdout; }\n\nSEQ_FUNC void *seq_stderr() { return stderr; }\n\n/*\n * Threading\n */\n\nSEQ_FUNC void *seq_lock_new() {\n  return (void *)new (seq_alloc_atomic(sizeof(std::timed_mutex))) std::timed_mutex();\n}\n\nSEQ_FUNC bool seq_lock_acquire(void *lock, bool block, double timeout) {\n  auto *m = (std::timed_mutex *)lock;\n  if (timeout < 0.0) {\n    if (block) {\n      m->lock();\n      return true;\n    } else {\n      return m->try_lock();\n    }\n  } else {\n    return m->try_lock_for(std::chrono::duration<double>(timeout));\n  }\n}\n\nSEQ_FUNC void seq_lock_release(void *lock) {\n  auto *m = (std::timed_mutex *)lock;\n  m->unlock();\n}\n\nSEQ_FUNC void *seq_rlock_new() {\n  return (void *)new (seq_alloc_atomic(sizeof(std::recursive_timed_mutex)))\n      std::recursive_timed_mutex();\n}\n\nSEQ_FUNC bool seq_rlock_acquire(void *lock, bool block, double timeout) {\n  auto *m = (std::recursive_timed_mutex *)lock;\n  if (timeout < 0.0) {\n    if (block) {\n      m->lock();\n      return true;\n    } else {\n      return m->try_lock();\n    }\n  } else {\n    return m->try_lock_for(std::chrono::duration<double>(timeout));\n  }\n}\n\nSEQ_FUNC void seq_rlock_release(void *lock) {\n  auto *m = (std::recursive_timed_mutex *)lock;\n  m->unlock();\n}\n"
  },
  {
    "path": "codon/runtime/lib.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <cstddef>\n#include <cstdint>\n#include <cstdio>\n#include <cstdlib>\n#include <functional>\n#include <stdexcept>\n#include <string>\n#include <vector>\n\n#include <unwind.h>\n\n#define SEQ_FLAG_DEBUG (1 << 0)          // compiled/running in debug mode\n#define SEQ_FLAG_CAPTURE_OUTPUT (1 << 1) // capture writes to stdout/stderr\n#define SEQ_FLAG_STANDALONE (1 << 2)     // compiled as a standalone object/binary\n\n#define SEQ_EXCEPTION_CLASS 0x6f626a0073657100\n\n#define SEQ_FUNC extern \"C\"\n\ntypedef int64_t seq_int_t;\n\nstruct seq_str_t {\n  seq_int_t len;\n  char *str;\n};\n\nstruct seq_time_t {\n  int16_t year;\n  int16_t yday;\n  int8_t sec;\n  int8_t min;\n  int8_t hour;\n  int8_t mday;\n  int8_t mon;\n  int8_t wday;\n  int8_t isdst;\n};\n\nSEQ_FUNC int seq_flags;\n\nSEQ_FUNC void seq_init(int flags);\n\nSEQ_FUNC seq_int_t seq_pid();\nSEQ_FUNC seq_int_t seq_time();\nSEQ_FUNC seq_int_t seq_time_monotonic();\nSEQ_FUNC seq_int_t seq_time_highres();\nSEQ_FUNC bool seq_localtime(seq_int_t secs, seq_time_t *output);\nSEQ_FUNC bool seq_gmtime(seq_int_t secs, seq_time_t *output);\nSEQ_FUNC seq_int_t seq_mktime(seq_time_t *time);\nSEQ_FUNC void seq_sleep(double secs);\nSEQ_FUNC char **seq_env();\nSEQ_FUNC void seq_assert_failed(seq_str_t file, seq_int_t line);\n\nSEQ_FUNC void *seq_alloc(size_t n);\nSEQ_FUNC void *seq_alloc_atomic(size_t n);\nSEQ_FUNC void *seq_alloc_uncollectable(size_t n);\nSEQ_FUNC void *seq_alloc_atomic_uncollectable(size_t n);\nSEQ_FUNC void *seq_realloc(void *p, size_t newsize, size_t oldsize);\nSEQ_FUNC void seq_free(void *p);\nSEQ_FUNC void seq_register_finalizer(void *p, void (*f)(void *obj, void *data));\n\nSEQ_FUNC void seq_gc_add_roots(void *start, void *end);\nSEQ_FUNC void seq_gc_remove_roots(void *start, void *end);\nSEQ_FUNC void seq_gc_clear_roots();\nSEQ_FUNC void seq_gc_exclude_static_roots(void *start, void *end);\n\nSEQ_FUNC void *seq_alloc_exc(void *obj);\nSEQ_FUNC void seq_throw(void *exc);\nSEQ_FUNC _Unwind_Reason_Code seq_personality(int version, _Unwind_Action actions,\n                                             uint64_t exceptionClass,\n                                             _Unwind_Exception *exceptionObject,\n                                             _Unwind_Context *context);\nSEQ_FUNC int64_t seq_exc_offset();\n\nSEQ_FUNC seq_str_t seq_str_int(seq_int_t n, seq_str_t format, bool *error);\nSEQ_FUNC seq_str_t seq_str_uint(seq_int_t n, seq_str_t format, bool *error);\nSEQ_FUNC seq_str_t seq_str_float(double f, seq_str_t format, bool *error);\nSEQ_FUNC seq_str_t seq_str_ptr(void *p, seq_str_t format, bool *error);\nSEQ_FUNC seq_str_t seq_str_str(seq_str_t s, seq_str_t format, bool *error);\n\nSEQ_FUNC void *seq_stdin();\nSEQ_FUNC void *seq_stdout();\nSEQ_FUNC void *seq_stderr();\n\nSEQ_FUNC void seq_print(seq_str_t str);\nSEQ_FUNC void seq_print_full(seq_str_t str, FILE *fo);\n\nSEQ_FUNC void *seq_lock_new();\nSEQ_FUNC bool seq_lock_acquire(void *lock, bool block, double timeout);\nSEQ_FUNC void seq_lock_release(void *lock);\nSEQ_FUNC void *seq_rlock_new();\nSEQ_FUNC bool seq_rlock_acquire(void *lock, bool block, double timeout);\nSEQ_FUNC void seq_rlock_release(void *lock);\n\nnamespace codon {\nnamespace runtime {\nclass JITError : public std::runtime_error {\nprivate:\n  std::string output;\n  std::string type;\n  std::string file;\n  int line;\n  int col;\n  std::vector<uintptr_t> backtrace;\n\npublic:\n  JITError(const std::string &output, const std::string &what, const std::string &type,\n           const std::string &file, int line, int col,\n           std::vector<uintptr_t> backtrace = {})\n      : std::runtime_error(what), output(output), type(type), file(file), line(line),\n        col(col), backtrace(std::move(backtrace)) {}\n\n  std::string getOutput() const { return output; }\n  std::string getType() const { return type; }\n  std::string getFile() const { return file; }\n  int getLine() const { return line; }\n  int getCol() const { return col; }\n  std::vector<uintptr_t> getBacktrace() const { return backtrace; }\n};\n\nstd::string makeBacktraceFrameString(uintptr_t pc, const std::string &func = \"\",\n                                     const std::string &file = \"\", int line = 0,\n                                     int col = 0);\n\nstd::string getCapturedOutput();\n\nvoid setJITErrorCallback(std::function<void(const JITError &)> callback);\n\n} // namespace runtime\n} // namespace codon\n"
  },
  {
    "path": "codon/runtime/numpy/loops.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"codon/runtime/lib.h\"\n\n#if (defined(__aarch64__) || defined(__arm64__)) && !defined(__ARM_FEATURE_SVE)\n#define HWY_DISABLED_TARGETS HWY_ALL_SVE\n#endif\n\n// clang-format off\n#undef HWY_TARGET_INCLUDE\n#define HWY_TARGET_INCLUDE \"codon/runtime/numpy/loops.cpp\"\n#include \"hwy/foreach_target.h\"\n#include \"hwy/highway.h\"\n#include \"hwy/contrib/math/math-inl.h\"\n// clang-format on\n\n#include <cmath>\n#include <cstring>\n#include <limits>\n\nHWY_BEFORE_NAMESPACE();\n\nnamespace {\nnamespace HWY_NAMESPACE {\n\nnamespace hn = hwy::HWY_NAMESPACE;\n\nstruct AcosFunctor {\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    return Acos(d, v);\n  }\n\n  static inline double scalar(const double x) { return acos(x); }\n\n  static inline float scalar(const float x) { return acosf(x); }\n};\n\nstruct AcoshFunctor {\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    const auto nan = Set(d, std::numeric_limits<T>::quiet_NaN());\n    const auto pinf = Set(d, std::numeric_limits<T>::infinity());\n    const auto pone = Set(d, static_cast<T>(1.0));\n    return IfThenElse(v == pinf, pinf, IfThenElse(v < pone, nan, Acosh(d, v)));\n  }\n\n  static inline double scalar(const double x) { return acosh(x); }\n\n  static inline float scalar(const float x) { return acoshf(x); }\n};\n\nstruct AsinFunctor {\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    return Asin(d, v);\n  }\n\n  static inline double scalar(const double x) { return asin(x); }\n\n  static inline float scalar(const float x) { return asinf(x); }\n};\n\nstruct AsinhFunctor {\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    const auto pinf = Set(d, std::numeric_limits<T>::infinity());\n    const auto ninf = Set(d, -std::numeric_limits<T>::infinity());\n    const auto zero = Set(d, static_cast<T>(0.0));\n    return IfThenElse(IsNaN(v), v, IfThenElse(IsInf(v), v, Asinh(d, v)));\n  }\n\n  static inline double scalar(const double x) { return asinh(x); }\n\n  static inline float scalar(const float x) { return asinhf(x); }\n};\n\nstruct AtanFunctor {\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    const auto ppi2 = Set(d, static_cast<T>(+3.14159265358979323846264 / 2));\n    const auto npi2 = Set(d, static_cast<T>(-3.14159265358979323846264 / 2));\n    const auto pinf = Set(d, std::numeric_limits<T>::infinity());\n    const auto ninf = Set(d, -std::numeric_limits<T>::infinity());\n    return IfThenElse(v == pinf, ppi2, IfThenElse(v == ninf, npi2, Atan(d, v)));\n  }\n\n  static inline double scalar(const double x) { return atan(x); }\n\n  static inline float scalar(const float x) { return atanf(x); }\n};\n\nstruct AtanhFunctor {\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    const auto nan = Set(d, std::numeric_limits<T>::quiet_NaN());\n    const auto pinf = Set(d, std::numeric_limits<T>::infinity());\n    const auto ninf = Set(d, -std::numeric_limits<T>::infinity());\n    const auto pone = Set(d, static_cast<T>(1.0));\n    const auto none = Set(d, static_cast<T>(-1.0));\n    const auto nzero = Set(d, static_cast<T>(0.0));\n    return IfThenElse(\n        v == pone, pinf,\n        IfThenElse(v == none, ninf, IfThenElse(Abs(v) > pone, nan, Atanh(d, v))));\n  }\n\n  static inline double scalar(const double x) { return atanh(x); }\n\n  static inline float scalar(const float x) { return atanhf(x); }\n};\n\nstruct Atan2Functor {\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v1, const V &v2) {\n    constexpr bool kIsF32 = (sizeof(T) == 4);\n    using TI = hwy::MakeSigned<T>;\n    const hn::Rebind<TI, hn::ScalableTag<T>> di;\n\n    auto pzero = Set(di, kIsF32 ? static_cast<TI>(0x00000000L)\n                                : static_cast<TI>(0x0000000000000000LL));\n    auto nzero = Set(di, kIsF32 ? static_cast<TI>(0x80000000L)\n                                : static_cast<TI>(0x8000000000000000LL));\n\n    auto negneg = And(BitCast(di, v1) == nzero, BitCast(di, v2) == nzero);\n    auto posneg = And(BitCast(di, v1) == pzero, BitCast(di, v2) == nzero);\n\n    const auto ppi = Set(d, static_cast<T>(+3.14159265358979323846264));\n    const auto npi = Set(d, static_cast<T>(-3.14159265358979323846264));\n    return BitCast(d,\n                   IfThenElse(negneg, BitCast(di, npi),\n                              BitCast(di, IfThenElse(posneg, BitCast(di, ppi),\n                                                     BitCast(di, Atan2(d, v1, v2))))));\n  }\n\n  static inline auto scalar(const double x, const double y) { return atan2(x, y); }\n\n  static inline auto scalar(const float x, const float y) { return atan2f(x, y); }\n};\n\nstruct CosFunctor {\n  template <typename T> static inline T limit() {\n    if constexpr (std::is_same_v<T, double>) {\n      return 3.37e9;\n    } else if constexpr (std::is_same_v<T, float>) {\n      return 2.63e7f;\n    } else {\n      return T{};\n    }\n  }\n\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    // Values outside of [-LIMIT, LIMIT] are not valid for SIMD version.\n    const T LIMIT = limit<T>();\n    HWY_LANES_CONSTEXPR size_t L = hn::Lanes(d);\n    T tmp[L];\n    Store(v, d, tmp);\n\n    for (auto i = 0; i < L; ++i) {\n      const auto x = tmp[i];\n      if (x < -LIMIT || x > LIMIT) {\n        // Just use scalar version in this case.\n        for (auto j = 0; j < L; ++j)\n          tmp[j] = scalar(tmp[j]);\n        return Load(d, tmp);\n      }\n    }\n\n    return Cos(d, v);\n  }\n\n  static inline double scalar(const double x) { return cos(x); }\n\n  static inline float scalar(const float x) { return cosf(x); }\n};\n\nstruct ExpFunctor {\n  template <typename T> static inline T limit() {\n    if constexpr (std::is_same_v<T, double>) {\n      return 1000.0;\n    } else if constexpr (std::is_same_v<T, float>) {\n      return 128.0f;\n    } else {\n      return T{};\n    }\n  }\n\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    const auto lim = Set(d, limit<T>());\n    const auto pinf = Set(d, std::numeric_limits<T>::infinity());\n    return IfThenElse(IsNaN(v), v, IfThenElse(v >= lim, pinf, Exp(d, v)));\n  }\n\n  static inline double scalar(const double x) { return exp(x); }\n\n  static inline float scalar(const float x) { return expf(x); }\n};\n\nstruct Exp2Functor {\n  template <typename T> static inline T limit() {\n    if constexpr (std::is_same_v<T, double>) {\n      return 2048.0;\n    } else if constexpr (std::is_same_v<T, float>) {\n      return 128.0f;\n    } else {\n      return T{};\n    }\n  }\n\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    const auto lim = Set(d, limit<T>());\n    const auto pinf = Set(d, std::numeric_limits<T>::infinity());\n    return IfThenElse(IsNaN(v), v, IfThenElse(v >= lim, pinf, Exp2(d, v)));\n  }\n\n  static inline double scalar(const double x) { return exp2(x); }\n\n  static inline float scalar(const float x) { return exp2f(x); }\n};\n\nstruct Expm1Functor {\n  template <typename T> static inline T limit() {\n    if constexpr (std::is_same_v<T, double>) {\n      return 1000.0;\n    } else if constexpr (std::is_same_v<T, float>) {\n      return 128.0f;\n    } else {\n      return T{};\n    }\n  }\n\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    const auto lim = Set(d, limit<T>());\n    const auto pinf = Set(d, std::numeric_limits<T>::infinity());\n    return IfThenElse(IsNaN(v), v, IfThenElse(v >= lim, pinf, Expm1(d, v)));\n  }\n\n  static inline double scalar(const double x) { return expm1(x); }\n\n  static inline float scalar(const float x) { return expm1f(x); }\n};\n\nstruct LogFunctor {\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    const auto nan = Set(d, std::numeric_limits<T>::quiet_NaN());\n    const auto pinf = Set(d, std::numeric_limits<T>::infinity());\n    const auto ninf = Set(d, -std::numeric_limits<T>::infinity());\n    const auto zero = Set(d, static_cast<T>(0.0));\n    return IfThenElse(\n        v == zero, ninf,\n        IfThenElse(v < zero, nan,\n                   IfThenElse(v == pinf, pinf, IfThenElse(IsNaN(v), v, Log(d, v)))));\n  }\n\n  static inline double scalar(const double x) { return log(x); }\n\n  static inline float scalar(const float x) { return logf(x); }\n};\n\nstruct Log10Functor {\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    const auto nan = Set(d, std::numeric_limits<T>::quiet_NaN());\n    const auto pinf = Set(d, std::numeric_limits<T>::infinity());\n    const auto ninf = Set(d, -std::numeric_limits<T>::infinity());\n    const auto zero = Set(d, static_cast<T>(0.0));\n    return IfThenElse(\n        v == zero, ninf,\n        IfThenElse(v < zero, nan,\n                   IfThenElse(v == pinf, pinf, IfThenElse(IsNaN(v), v, Log10(d, v)))));\n  }\n\n  static inline double scalar(const double x) { return log10(x); }\n\n  static inline float scalar(const float x) { return log10f(x); }\n};\n\nstruct Log1pFunctor {\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    const auto nan = Set(d, std::numeric_limits<T>::quiet_NaN());\n    const auto pinf = Set(d, std::numeric_limits<T>::infinity());\n    const auto ninf = Set(d, -std::numeric_limits<T>::infinity());\n    const auto none = Set(d, static_cast<T>(-1.0));\n    return IfThenElse(\n        v == none, ninf,\n        IfThenElse(v < none, nan,\n                   IfThenElse(v == pinf, pinf, IfThenElse(IsNaN(v), v, Log1p(d, v)))));\n  }\n\n  static inline double scalar(const double x) { return log1p(x); }\n\n  static inline float scalar(const float x) { return log1pf(x); }\n};\n\nstruct Log2Functor {\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    const auto nan = Set(d, std::numeric_limits<T>::quiet_NaN());\n    const auto pinf = Set(d, std::numeric_limits<T>::infinity());\n    const auto ninf = Set(d, -std::numeric_limits<T>::infinity());\n    const auto zero = Set(d, static_cast<T>(0.0));\n    return IfThenElse(\n        v == zero, ninf,\n        IfThenElse(v < zero, nan,\n                   IfThenElse(v == pinf, pinf, IfThenElse(IsNaN(v), v, Log2(d, v)))));\n  }\n\n  static inline double scalar(const double x) { return log2(x); }\n\n  static inline float scalar(const float x) { return log2f(x); }\n};\n\nstruct SinFunctor {\n  template <typename T> static inline T limit() {\n    if constexpr (std::is_same_v<T, double>) {\n      return 6.74e9;\n    } else if constexpr (std::is_same_v<T, float>) {\n      return 5.30e8f;\n    } else {\n      return T{};\n    }\n  }\n\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    // Values outside of [-LIMIT, LIMIT] are not valid for SIMD version.\n    const T LIMIT = limit<T>();\n    HWY_LANES_CONSTEXPR size_t L = hn::Lanes(d);\n    T tmp[L];\n    Store(v, d, tmp);\n\n    for (auto i = 0; i < L; ++i) {\n      const auto x = tmp[i];\n      if (x < -LIMIT || x > LIMIT) {\n        // Just use scalar version in this case.\n        for (auto j = 0; j < L; ++j)\n          tmp[j] = scalar(tmp[j]);\n        return Load(d, tmp);\n      }\n    }\n\n    return Sin(d, v);\n  }\n\n  static inline double scalar(const double x) { return sin(x); }\n\n  static inline float scalar(const float x) { return sinf(x); }\n};\n\nstruct SinhFunctor {\n  template <typename T> static inline T limit() {\n    if constexpr (std::is_same_v<T, double>) {\n      return 709.0;\n    } else if constexpr (std::is_same_v<T, float>) {\n      return 88.7228f;\n    } else {\n      return T{};\n    }\n  }\n\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    // Values outside of [-LIMIT, LIMIT] are not valid for SIMD version.\n    const T LIMIT = limit<T>();\n    HWY_LANES_CONSTEXPR size_t L = hn::Lanes(d);\n    T tmp[L];\n    Store(v, d, tmp);\n\n    for (auto i = 0; i < L; ++i) {\n      const auto x = tmp[i];\n      if (x < -LIMIT || x > LIMIT) {\n        // Just use scalar version in this case.\n        for (auto j = 0; j < L; ++j)\n          tmp[j] = scalar(tmp[j]);\n        return Load(d, tmp);\n      }\n    }\n\n    return Sinh(d, v);\n  }\n\n  static inline double scalar(const double x) { return sinh(x); }\n\n  static inline float scalar(const float x) { return sinhf(x); }\n};\n\nstruct TanhFunctor {\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v) {\n    return Tanh(d, v);\n  }\n\n  static inline double scalar(const double x) { return tanh(x); }\n\n  static inline float scalar(const float x) { return tanhf(x); }\n};\n\nstruct HypotFunctor {\n  template <typename T, typename V>\n  static inline auto vector(const hn::ScalableTag<T> d, const V &v1, const V &v2) {\n    return Hypot(d, v1, v2);\n  }\n\n  static inline auto scalar(const double x, const double y) { return hypot(x, y); }\n\n  static inline auto scalar(const float x, const float y) { return hypotf(x, y); }\n};\n\ntemplate <typename T, typename F>\nvoid UnaryLoop(const T *in, size_t is, T *out, size_t os, size_t n) {\n  const hn::ScalableTag<T> d;\n  HWY_LANES_CONSTEXPR size_t L = hn::Lanes(d);\n  T tmp[L];\n  size_t i;\n\n  if (is == sizeof(T) && os == sizeof(T)) {\n    for (i = 0; i + L <= n; i += L) {\n      memcpy(tmp, in + i, L * sizeof(T));\n      auto vec = hn::Load(d, tmp);\n      Store(F::template vector<T, decltype(vec)>(d, vec), d, tmp);\n      memcpy(out + i, tmp, L * sizeof(T));\n    }\n\n    for (; i < n; ++i)\n      out[i] = F::scalar(in[i]);\n  } else {\n    for (i = 0; i + L <= n; i += L) {\n      for (size_t j = 0; j < L; ++j)\n        tmp[j] = *(T *)((char *)in + (i + j) * is);\n\n      auto vec = hn::Load(d, tmp);\n      Store(F::template vector<T, decltype(vec)>(d, vec), d, tmp);\n\n      for (size_t j = 0; j < L; ++j)\n        *(T *)((char *)out + (i + j) * os) = tmp[j];\n    }\n\n    for (; i < n; ++i)\n      *(T *)((char *)out + i * os) = F::scalar(*(T *)((char *)in + i * is));\n  }\n}\n\ntemplate <typename T, typename F>\nvoid BinaryLoop(const T *in1, size_t is1, const T *in2, size_t is2, T *out, size_t os,\n                size_t n) {\n  const hn::ScalableTag<T> d;\n  HWY_LANES_CONSTEXPR size_t L = hn::Lanes(d);\n  T tmp1[L];\n  T tmp2[L];\n  size_t i;\n\n  if (is1 == sizeof(T) && is2 == sizeof(T) && os == sizeof(T)) {\n    for (i = 0; i + L <= n; i += L) {\n      memcpy(tmp1, in1 + i, L * sizeof(T));\n      memcpy(tmp2, in2 + i, L * sizeof(T));\n      auto vec1 = hn::Load(d, tmp1);\n      auto vec2 = hn::Load(d, tmp2);\n      Store(F::template vector<T, decltype(vec1)>(d, vec1, vec2), d, tmp1);\n      memcpy(out + i, tmp1, L * sizeof(T));\n    }\n\n    for (; i < n; ++i)\n      out[i] = F::scalar(in1[i], in2[i]);\n  } else if (is1 == 0 && is2 == sizeof(T) && os == sizeof(T)) {\n    for (size_t j = 0; j < L; ++j)\n      tmp1[j] = in1[0];\n\n    for (i = 0; i + L <= n; i += L) {\n      memcpy(tmp2, in2 + i, L * sizeof(T));\n      auto vec1 = hn::Load(d, tmp1);\n      auto vec2 = hn::Load(d, tmp2);\n      Store(F::template vector<T, decltype(vec1)>(d, vec1, vec2), d, tmp1);\n      memcpy(out + i, tmp1, L * sizeof(T));\n    }\n\n    for (; i < n; ++i)\n      out[i] = F::scalar(in1[0], in2[i]);\n  } else if (is1 == sizeof(T) && is2 == 0 && os == sizeof(T)) {\n    for (size_t j = 0; j < L; ++j)\n      tmp2[j] = in2[0];\n\n    for (i = 0; i + L <= n; i += L) {\n      memcpy(tmp1, in1 + i, L * sizeof(T));\n      auto vec1 = hn::Load(d, tmp1);\n      auto vec2 = hn::Load(d, tmp2);\n      Store(F::template vector<T, decltype(vec1)>(d, vec1, vec2), d, tmp1);\n      memcpy(out + i, tmp1, L * sizeof(T));\n    }\n\n    for (; i < n; ++i)\n      out[i] = F::scalar(in1[i], in2[0]);\n  } else {\n    for (i = 0; i + L <= n; i += L) {\n      for (size_t j = 0; j < L; ++j) {\n        tmp1[j] = *(T *)((char *)in1 + (i + j) * is1);\n        tmp2[j] = *(T *)((char *)in2 + (i + j) * is2);\n      }\n\n      auto vec1 = hn::Load(d, tmp1);\n      auto vec2 = hn::Load(d, tmp2);\n      Store(F::template vector<T, decltype(vec1)>(d, vec1, vec2), d, tmp1);\n\n      for (size_t j = 0; j < L; ++j)\n        *(T *)((char *)out + (i + j) * os) = tmp1[j];\n    }\n\n    for (; i < n; ++i)\n      *(T *)((char *)out + i * os) =\n          F::scalar(*(T *)((char *)in1 + i * is1), *(T *)((char *)in2 + i * is2));\n  }\n}\n\nvoid LoopAcos32(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, AcosFunctor>(in, is, out, os, n);\n}\n\nvoid LoopAcos64(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, AcosFunctor>(in, is, out, os, n);\n}\n\nvoid LoopAcosh32(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, AcoshFunctor>(in, is, out, os, n);\n}\n\nvoid LoopAcosh64(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, AcoshFunctor>(in, is, out, os, n);\n}\n\nvoid LoopAsin32(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, AsinFunctor>(in, is, out, os, n);\n}\n\nvoid LoopAsin64(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, AsinFunctor>(in, is, out, os, n);\n}\n\nvoid LoopAsinh32(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, AsinhFunctor>(in, is, out, os, n);\n}\n\nvoid LoopAsinh64(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, AsinhFunctor>(in, is, out, os, n);\n}\n\nvoid LoopAtan32(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, AtanFunctor>(in, is, out, os, n);\n}\n\nvoid LoopAtan64(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, AtanFunctor>(in, is, out, os, n);\n}\n\nvoid LoopAtanh32(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, AtanhFunctor>(in, is, out, os, n);\n}\n\nvoid LoopAtanh64(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, AtanhFunctor>(in, is, out, os, n);\n}\n\nvoid LoopAtan232(const float *in1, size_t is1, const float *in2, size_t is2, float *out,\n                 size_t os, size_t n) {\n  BinaryLoop<float, Atan2Functor>(in1, is1, in2, is2, out, os, n);\n}\n\nvoid LoopAtan264(const double *in1, size_t is1, const double *in2, size_t is2,\n                 double *out, size_t os, size_t n) {\n  BinaryLoop<double, Atan2Functor>(in1, is1, in2, is2, out, os, n);\n}\n\nvoid LoopCos32(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, CosFunctor>(in, is, out, os, n);\n}\n\nvoid LoopCos64(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, CosFunctor>(in, is, out, os, n);\n}\n\nvoid LoopExp32(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, ExpFunctor>(in, is, out, os, n);\n}\n\nvoid LoopExp64(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, ExpFunctor>(in, is, out, os, n);\n}\n\nvoid LoopExp232(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, Exp2Functor>(in, is, out, os, n);\n}\n\nvoid LoopExp264(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, Exp2Functor>(in, is, out, os, n);\n}\n\nvoid LoopExpm132(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, Expm1Functor>(in, is, out, os, n);\n}\n\nvoid LoopExpm164(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, Expm1Functor>(in, is, out, os, n);\n}\n\nvoid LoopLog32(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, LogFunctor>(in, is, out, os, n);\n}\n\nvoid LoopLog64(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, LogFunctor>(in, is, out, os, n);\n}\n\nvoid LoopLog1032(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, Log10Functor>(in, is, out, os, n);\n}\n\nvoid LoopLog1064(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, Log10Functor>(in, is, out, os, n);\n}\n\nvoid LoopLog1p32(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, Log1pFunctor>(in, is, out, os, n);\n}\n\nvoid LoopLog1p64(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, Log1pFunctor>(in, is, out, os, n);\n}\n\nvoid LoopLog232(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, Log2Functor>(in, is, out, os, n);\n}\n\nvoid LoopLog264(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, Log2Functor>(in, is, out, os, n);\n}\n\nvoid LoopSin32(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, SinFunctor>(in, is, out, os, n);\n}\n\nvoid LoopSin64(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, SinFunctor>(in, is, out, os, n);\n}\n\nvoid LoopSinh32(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, SinhFunctor>(in, is, out, os, n);\n}\n\nvoid LoopSinh64(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, SinhFunctor>(in, is, out, os, n);\n}\n\nvoid LoopTanh32(const float *in, size_t is, float *out, size_t os, size_t n) {\n  UnaryLoop<float, TanhFunctor>(in, is, out, os, n);\n}\n\nvoid LoopTanh64(const double *in, size_t is, double *out, size_t os, size_t n) {\n  UnaryLoop<double, TanhFunctor>(in, is, out, os, n);\n}\n\nvoid LoopHypot32(const float *in1, size_t is1, const float *in2, size_t is2, float *out,\n                 size_t os, size_t n) {\n  BinaryLoop<float, HypotFunctor>(in1, is1, in2, is2, out, os, n);\n}\n\nvoid LoopHypot64(const double *in1, size_t is1, const double *in2, size_t is2,\n                 double *out, size_t os, size_t n) {\n  BinaryLoop<double, HypotFunctor>(in1, is1, in2, is2, out, os, n);\n}\n\n} // namespace HWY_NAMESPACE\n} // namespace\nHWY_AFTER_NAMESPACE();\n\n#if HWY_ONCE\n\nHWY_EXPORT(LoopAcos32);\nHWY_EXPORT(LoopAcos64);\nHWY_EXPORT(LoopAcosh32);\nHWY_EXPORT(LoopAcosh64);\nHWY_EXPORT(LoopAsin32);\nHWY_EXPORT(LoopAsin64);\nHWY_EXPORT(LoopAsinh32);\nHWY_EXPORT(LoopAsinh64);\nHWY_EXPORT(LoopAtan32);\nHWY_EXPORT(LoopAtan64);\nHWY_EXPORT(LoopAtanh32);\nHWY_EXPORT(LoopAtanh64);\nHWY_EXPORT(LoopAtan232);\nHWY_EXPORT(LoopAtan264);\nHWY_EXPORT(LoopCos32);\nHWY_EXPORT(LoopCos64);\nHWY_EXPORT(LoopExp32);\nHWY_EXPORT(LoopExp64);\nHWY_EXPORT(LoopExp232);\nHWY_EXPORT(LoopExp264);\nHWY_EXPORT(LoopExpm132);\nHWY_EXPORT(LoopExpm164);\nHWY_EXPORT(LoopLog32);\nHWY_EXPORT(LoopLog64);\nHWY_EXPORT(LoopLog1032);\nHWY_EXPORT(LoopLog1064);\nHWY_EXPORT(LoopLog1p32);\nHWY_EXPORT(LoopLog1p64);\nHWY_EXPORT(LoopLog232);\nHWY_EXPORT(LoopLog264);\nHWY_EXPORT(LoopSin32);\nHWY_EXPORT(LoopSin64);\nHWY_EXPORT(LoopSinh32);\nHWY_EXPORT(LoopSinh64);\nHWY_EXPORT(LoopTanh32);\nHWY_EXPORT(LoopTanh64);\nHWY_EXPORT(LoopHypot32);\nHWY_EXPORT(LoopHypot64);\n\nSEQ_FUNC void cnp_acos_float32(const float *in, size_t is, float *out, size_t os,\n                               size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopAcos32);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_acos_float64(const double *in, size_t is, double *out, size_t os,\n                               size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopAcos64);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_acosh_float32(const float *in, size_t is, float *out, size_t os,\n                                size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopAcosh32);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_acosh_float64(const double *in, size_t is, double *out, size_t os,\n                                size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopAcosh64);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_asin_float32(const float *in, size_t is, float *out, size_t os,\n                               size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopAsin32);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_asin_float64(const double *in, size_t is, double *out, size_t os,\n                               size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopAsin64);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_asinh_float32(const float *in, size_t is, float *out, size_t os,\n                                size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopAsinh32);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_asinh_float64(const double *in, size_t is, double *out, size_t os,\n                                size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopAsinh64);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_atan_float32(const float *in, size_t is, float *out, size_t os,\n                               size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopAtan32);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_atan_float64(const double *in, size_t is, double *out, size_t os,\n                               size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopAtan64);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_atanh_float32(const float *in, size_t is, float *out, size_t os,\n                                size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopAtanh32);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_atanh_float64(const double *in, size_t is, double *out, size_t os,\n                                size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopAtanh64);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_atan2_float32(const float *in1, size_t is1, const float *in2,\n                                size_t is2, float *out, size_t os, size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopAtan232);\n  return ptr(in1, is1, in2, is2, out, os, n);\n}\n\nSEQ_FUNC void cnp_atan2_float64(const double *in1, size_t is1, const double *in2,\n                                size_t is2, double *out, size_t os, size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopAtan264);\n  return ptr(in1, is1, in2, is2, out, os, n);\n}\n\nSEQ_FUNC void cnp_cos_float32(const float *in, size_t is, float *out, size_t os,\n                              size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopCos32);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_cos_float64(const double *in, size_t is, double *out, size_t os,\n                              size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopCos64);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_exp_float32(const float *in, size_t is, float *out, size_t os,\n                              size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopExp32);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_exp_float64(const double *in, size_t is, double *out, size_t os,\n                              size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopExp64);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_exp2_float32(const float *in, size_t is, float *out, size_t os,\n                               size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopExp232);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_exp2_float64(const double *in, size_t is, double *out, size_t os,\n                               size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopExp264);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_expm1_float32(const float *in, size_t is, float *out, size_t os,\n                                size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopExpm132);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_expm1_float64(const double *in, size_t is, double *out, size_t os,\n                                size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopExpm164);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_log_float32(const float *in, size_t is, float *out, size_t os,\n                              size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopLog32);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_log_float64(const double *in, size_t is, double *out, size_t os,\n                              size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopLog64);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_log10_float32(const float *in, size_t is, float *out, size_t os,\n                                size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopLog1032);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_log10_float64(const double *in, size_t is, double *out, size_t os,\n                                size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopLog1064);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_log1p_float32(const float *in, size_t is, float *out, size_t os,\n                                size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopLog1p32);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_log1p_float64(const double *in, size_t is, double *out, size_t os,\n                                size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopLog1p64);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_log2_float32(const float *in, size_t is, float *out, size_t os,\n                               size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopLog232);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_log2_float64(const double *in, size_t is, double *out, size_t os,\n                               size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopLog264);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_sin_float32(const float *in, size_t is, float *out, size_t os,\n                              size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopSin32);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_sin_float64(const double *in, size_t is, double *out, size_t os,\n                              size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopSin64);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_sinh_float32(const float *in, size_t is, float *out, size_t os,\n                               size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopSinh32);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_sinh_float64(const double *in, size_t is, double *out, size_t os,\n                               size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopSinh64);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_tanh_float32(const float *in, size_t is, float *out, size_t os,\n                               size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopTanh32);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_tanh_float64(const double *in, size_t is, double *out, size_t os,\n                               size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopTanh64);\n  return ptr(in, is, out, os, n);\n}\n\nSEQ_FUNC void cnp_hypot_float32(const float *in1, size_t is1, const float *in2,\n                                size_t is2, float *out, size_t os, size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopHypot32);\n  return ptr(in1, is1, in2, is2, out, os, n);\n}\n\nSEQ_FUNC void cnp_hypot_float64(const double *in1, size_t is1, const double *in2,\n                                size_t is2, double *out, size_t os, size_t n) {\n  const auto ptr = HWY_DYNAMIC_POINTER(LoopHypot64);\n  return ptr(in1, is1, in2, is2, out, os, n);\n}\n\n#endif // HWY_ONCE\n"
  },
  {
    "path": "codon/runtime/numpy/sort.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"codon/runtime/lib.h\"\n#include \"hwy/contrib/sort/vqsort-inl.h\"\n\nSEQ_FUNC void cnp_sort_int16(int16_t *data, int64_t n) {\n  hwy::VQSort(data, n, hwy::SortAscending());\n}\n\nSEQ_FUNC void cnp_sort_uint16(uint16_t *data, int64_t n) {\n  hwy::VQSort(data, n, hwy::SortAscending());\n}\n\nSEQ_FUNC void cnp_sort_int32(int32_t *data, int64_t n) {\n  hwy::VQSort(data, n, hwy::SortAscending());\n}\n\nSEQ_FUNC void cnp_sort_uint32(uint32_t *data, int64_t n) {\n  hwy::VQSort(data, n, hwy::SortAscending());\n}\n\nSEQ_FUNC void cnp_sort_int64(int64_t *data, int64_t n) {\n  hwy::VQSort(data, n, hwy::SortAscending());\n}\n\nSEQ_FUNC void cnp_sort_uint64(uint64_t *data, int64_t n) {\n  hwy::VQSort(data, n, hwy::SortAscending());\n}\n\nSEQ_FUNC void cnp_sort_uint128(hwy::uint128_t *data, int64_t n) {\n  hwy::VQSort(data, n, hwy::SortAscending());\n}\n\nSEQ_FUNC void cnp_sort_float32(float *data, int64_t n) {\n  hwy::VQSort(data, n, hwy::SortAscending());\n}\n\nSEQ_FUNC void cnp_sort_float64(double *data, int64_t n) {\n  hwy::VQSort(data, n, hwy::SortAscending());\n}\n"
  },
  {
    "path": "codon/runtime/numpy/zmath.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n// This file resolves ABI issues with single-precision complex\n// math functions.\n\n#include \"codon/runtime/lib.h\"\n\n#include <complex>\n\nSEQ_FUNC void cnp_cexpf(float r, float i, float *z) {\n  std::complex<float> x(r, i);\n  auto y = std::exp(x);\n  z[0] = y.real();\n  z[1] = y.imag();\n}\n\nSEQ_FUNC void cnp_clogf(float r, float i, float *z) {\n  std::complex<float> x(r, i);\n  auto y = std::log(x);\n  z[0] = y.real();\n  z[1] = y.imag();\n}\n\nSEQ_FUNC void cnp_csqrtf(float r, float i, float *z) {\n  std::complex<float> x(r, i);\n  auto y = std::sqrt(x);\n  z[0] = y.real();\n  z[1] = y.imag();\n}\n\nSEQ_FUNC void cnp_ccoshf(float r, float i, float *z) {\n  std::complex<float> x(r, i);\n  auto y = std::cosh(x);\n  z[0] = y.real();\n  z[1] = y.imag();\n}\n\nSEQ_FUNC void cnp_csinhf(float r, float i, float *z) {\n  std::complex<float> x(r, i);\n  auto y = std::sinh(x);\n  z[0] = y.real();\n  z[1] = y.imag();\n}\n\nSEQ_FUNC void cnp_ctanhf(float r, float i, float *z) {\n  std::complex<float> x(r, i);\n  auto y = std::tanh(x);\n  z[0] = y.real();\n  z[1] = y.imag();\n}\n\nSEQ_FUNC void cnp_cacoshf(float r, float i, float *z) {\n  std::complex<float> x(r, i);\n  auto y = std::acosh(x);\n  z[0] = y.real();\n  z[1] = y.imag();\n}\n\nSEQ_FUNC void cnp_casinhf(float r, float i, float *z) {\n  std::complex<float> x(r, i);\n  auto y = std::asinh(x);\n  z[0] = y.real();\n  z[1] = y.imag();\n}\n\nSEQ_FUNC void cnp_catanhf(float r, float i, float *z) {\n  std::complex<float> x(r, i);\n  auto y = std::atanh(x);\n  z[0] = y.real();\n  z[1] = y.imag();\n}\n\nSEQ_FUNC void cnp_ccosf(float r, float i, float *z) {\n  std::complex<float> x(r, i);\n  auto y = std::cos(x);\n  z[0] = y.real();\n  z[1] = y.imag();\n}\n\nSEQ_FUNC void cnp_csinf(float r, float i, float *z) {\n  std::complex<float> x(r, i);\n  auto y = std::sin(x);\n  z[0] = y.real();\n  z[1] = y.imag();\n}\n\nSEQ_FUNC void cnp_ctanf(float r, float i, float *z) {\n  std::complex<float> x(r, i);\n  auto y = std::tan(x);\n  z[0] = y.real();\n  z[1] = y.imag();\n}\n\nSEQ_FUNC void cnp_cacosf(float r, float i, float *z) {\n  std::complex<float> x(r, i);\n  auto y = std::acos(x);\n  z[0] = y.real();\n  z[1] = y.imag();\n}\n\nSEQ_FUNC void cnp_casinf(float r, float i, float *z) {\n  std::complex<float> x(r, i);\n  auto y = std::asin(x);\n  z[0] = y.real();\n  z[1] = y.imag();\n}\n\nSEQ_FUNC void cnp_catanf(float r, float i, float *z) {\n  std::complex<float> x(r, i);\n  auto y = std::atan(x);\n  z[0] = y.real();\n  z[1] = y.imag();\n}\n"
  },
  {
    "path": "codon/runtime/re.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"codon/runtime/lib.h\"\n#include <cstring>\n#include <re2/re2.h>\n#include <string>\n#include <string_view>\n#include <unordered_map>\n#include <utility>\n#include <vector>\n\nusing Regex = re2::RE2;\nusing re2::StringPiece;\n\n/*\n * Flags -- (!) must match Codon's\n */\n\n#define ASCII (1 << 0)\n#define DEBUG (1 << 1)\n#define IGNORECASE (1 << 2)\n#define LOCALE (1 << 3)\n#define MULTILINE (1 << 4)\n#define DOTALL (1 << 5)\n#define VERBOSE (1 << 6)\n\nstatic inline Regex::Options flags2opt(seq_int_t flags) {\n  Regex::Options opt;\n  opt.set_log_errors(false);\n  opt.set_encoding(Regex::Options::Encoding::EncodingLatin1);\n\n  if (flags & ASCII) {\n    // nothing\n  }\n\n  if (flags & DEBUG) {\n    // nothing\n  }\n\n  if (flags & IGNORECASE) {\n    opt.set_case_sensitive(false);\n  }\n\n  if (flags & LOCALE) {\n    // nothing\n  }\n\n  if (flags & MULTILINE) {\n    opt.set_one_line(false);\n  }\n\n  if (flags & DOTALL) {\n    opt.set_dot_nl(true);\n  }\n\n  if (flags & VERBOSE) {\n    // nothing\n  }\n\n  return opt;\n}\n\n/*\n * Internal helpers & utilities\n */\n\nstruct Span {\n  seq_int_t start;\n  seq_int_t end;\n};\n\ntemplate <typename KV> struct GCMapAllocator : public std::allocator<KV> {\n  GCMapAllocator() = default;\n  GCMapAllocator(GCMapAllocator<KV> const &) = default;\n\n  template <typename KV1> GCMapAllocator(const GCMapAllocator<KV1> &) noexcept {}\n\n  KV *allocate(std::size_t n) { return (KV *)seq_alloc_uncollectable(n * sizeof(KV)); }\n\n  void deallocate(KV *p, std::size_t n) { seq_free(p); }\n\n  template <typename U> struct rebind {\n    using other = GCMapAllocator<U>;\n  };\n};\n\nstatic inline seq_str_t convert(const std::string &p) {\n  seq_int_t n = p.size();\n  auto *s = (char *)seq_alloc_atomic(n);\n  std::memcpy(s, p.data(), n);\n  return {n, s};\n}\n\nstatic inline StringPiece str2sp(const seq_str_t &s) {\n  return StringPiece(s.str, s.len);\n}\n\nusing Key = std::pair<seq_str_t, seq_int_t>;\n\nstruct KeyEqual {\n  bool operator()(const Key &a, const Key &b) const {\n    return a.second == b.second && str2sp(a.first) == str2sp(b.first);\n  }\n};\n\nstruct KeyHash {\n  std::size_t operator()(const Key &k) const {\n    using sv = std::string_view;\n    return std::hash<sv>()(sv(k.first.str, k.first.len)) ^ k.second;\n  }\n};\n\nstatic thread_local std::unordered_map<const Key, Regex, KeyHash, KeyEqual,\n                                       GCMapAllocator<std::pair<const Key, Regex>>>\n    cache;\n\nstatic inline Regex *get(const seq_str_t &p, seq_int_t flags) {\n  auto key = std::make_pair(p, flags);\n  auto it = cache.find(key);\n  if (it == cache.end()) {\n    auto result = cache.emplace(std::piecewise_construct, std::forward_as_tuple(key),\n                                std::forward_as_tuple(str2sp(p), flags2opt(flags)));\n    return &result.first->second;\n  } else {\n    return &it->second;\n  }\n}\n\n/*\n * Matching\n */\n\nSEQ_FUNC Span *seq_re_match(Regex *re, seq_int_t anchor, seq_str_t s, seq_int_t pos,\n                            seq_int_t endpos) {\n  const int num_groups = re->NumberOfCapturingGroups() + 1; // need $0\n  std::vector<StringPiece> groups;\n  groups.resize(num_groups);\n\n  if (!re->Match(str2sp(s), pos, endpos, static_cast<Regex::Anchor>(anchor),\n                 groups.data(), groups.size())) {\n    // Ensure that groups are null before converting to spans!\n    for (auto &it : groups) {\n      it = StringPiece();\n    }\n  }\n\n  auto *spans = (Span *)seq_alloc_atomic(num_groups * sizeof(Span));\n  unsigned i = 0;\n  for (const auto &it : groups) {\n    if (it.data() == nullptr) {\n      spans[i++] = {-1, -1};\n    } else {\n      spans[i++] = {static_cast<seq_int_t>(it.data() - s.str),\n                    static_cast<seq_int_t>(it.data() - s.str + it.size())};\n    }\n  }\n\n  return spans;\n}\n\nSEQ_FUNC Span seq_re_match_one(Regex *re, seq_int_t anchor, seq_str_t s, seq_int_t pos,\n                               seq_int_t endpos) {\n  StringPiece m;\n  if (!re->Match(str2sp(s), pos, endpos, static_cast<Regex::Anchor>(anchor), &m, 1))\n    return {-1, -1};\n  else\n    return {static_cast<seq_int_t>(m.data() - s.str),\n            static_cast<seq_int_t>(m.data() - s.str + m.size())};\n}\n\n/*\n * General functions\n */\n\nSEQ_FUNC seq_str_t seq_re_escape(seq_str_t p) {\n  return convert(Regex::QuoteMeta(str2sp(p)));\n}\n\nSEQ_FUNC Regex *seq_re_compile(seq_str_t p, seq_int_t flags) { return get(p, flags); }\n\nSEQ_FUNC void seq_re_purge() { cache.clear(); }\n\n/*\n * Pattern methods\n */\n\nSEQ_FUNC seq_int_t seq_re_pattern_groups(Regex *pattern) {\n  return pattern->NumberOfCapturingGroups();\n}\n\nSEQ_FUNC seq_int_t seq_re_group_name_to_index(Regex *pattern, seq_str_t name) {\n  const auto &mapping = pattern->NamedCapturingGroups();\n  auto it = mapping.find(std::string(name.str, name.len));\n  return (it != mapping.end()) ? it->second : -1;\n}\n\nSEQ_FUNC seq_str_t seq_re_group_index_to_name(Regex *pattern, seq_int_t index) {\n  const auto &mapping = pattern->CapturingGroupNames();\n  auto it = mapping.find(index);\n  seq_str_t empty = {0, nullptr};\n  return (it != mapping.end()) ? convert(it->second) : empty;\n}\n\nSEQ_FUNC bool seq_re_check_rewrite_string(Regex *pattern, seq_str_t rewrite,\n                                          seq_str_t *error) {\n  std::string e;\n  bool ans = pattern->CheckRewriteString(str2sp(rewrite), &e);\n  if (!ans)\n    *error = convert(e);\n  return ans;\n}\n\nSEQ_FUNC seq_str_t seq_re_pattern_error(Regex *pattern) {\n  if (pattern->ok())\n    return {0, nullptr};\n  return convert(pattern->error());\n}\n"
  },
  {
    "path": "codon/util/common.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"common.h\"\n\n#include \"llvm/Support/Path.h\"\n#include <cstdlib>\n#include <iostream>\n#include <string>\n#include <vector>\n\nnamespace codon {\nnamespace {\nvoid compilationMessage(const std::string &header, const std::string &msg,\n                        const std::string &file, int line, int col, int len,\n                        int errorCode, MessageGroupPos pos) {\n  auto &out = getLogger().err;\n  seqassertn(!(file.empty() && (line > 0 || col > 0)),\n             \"empty filename with non-zero line/col: file={}, line={}, col={}\", file,\n             line, col);\n  seqassertn(!(col > 0 && line <= 0), \"col but no line: file={}, line={}, col={}\", file,\n             line, col);\n\n  switch (pos) {\n  case MessageGroupPos::NONE:\n    break;\n  case MessageGroupPos::HEAD:\n    break;\n  case MessageGroupPos::MID:\n    fmt::print(out, \"├─ \");\n    break;\n  case MessageGroupPos::LAST:\n    fmt::print(out, \"╰─ \");\n    break;\n  }\n\n  fmt::print(out, \"\\033[1m\");\n  if (!file.empty()) {\n    auto f = file.substr(file.rfind('/') + 1);\n    fmt::print(out, \"{}\", f == \"-\" ? \"<stdin>\" : f);\n  }\n  if (line > 0)\n    fmt::print(out, \":{}\", line);\n  if (col > 0) {\n    fmt::print(out, \" ({}\", col);\n    if (len > 0)\n      fmt::print(out, \"-{})\", col + len);\n    else\n      fmt::print(out, \")\");\n  }\n  if (!file.empty())\n    fmt::print(out, \": \");\n  fmt::print(out, \"{}\\033[1m {}\\033[0m{}\\n\", header, msg,\n             errorCode != -1\n                 ? fmt::format(\" (see https://exaloop.io/error/{:04d})\", errorCode)\n                 : \"\");\n}\n\nstd::vector<Logger> loggers;\n} // namespace\n\nstd::ostream &operator<<(std::ostream &out, const codon::SrcInfo &src) {\n  out << llvm::sys::path::filename(src.file).str() << \":\" << src.line << \":\" << src.col;\n  return out;\n}\n\nvoid compilationError(const std::string &msg, const std::string &file, int line,\n                      int col, int len, int errorCode, bool terminate,\n                      MessageGroupPos pos) {\n  compilationMessage(\"\\033[1;31merror:\\033[0m\", msg, file, line, col, len, errorCode,\n                     pos);\n  if (terminate)\n    exit(EXIT_FAILURE);\n}\n\nvoid compilationWarning(const std::string &msg, const std::string &file, int line,\n                        int col, int len, int errorCode, bool terminate,\n                        MessageGroupPos pos) {\n  compilationMessage(\"\\033[1;33mwarning:\\033[0m\", msg, file, line, col, len, errorCode,\n                     pos);\n  if (terminate)\n    exit(EXIT_FAILURE);\n}\n\nvoid Logger::parse(const std::string &s) {\n  flags |= s.find('t') != std::string::npos ? FLAG_TIME : 0;\n  flags |= s.find('r') != std::string::npos ? FLAG_REALIZE : 0;\n  flags |= s.find('T') != std::string::npos ? FLAG_TYPECHECK : 0;\n  flags |= s.find('i') != std::string::npos ? FLAG_IR : 0;\n  flags |= s.find('l') != std::string::npos ? FLAG_USER : 0;\n}\n} // namespace codon\n\ncodon::Logger &codon::getLogger() {\n  if (loggers.empty())\n    loggers.emplace_back();\n  return loggers.back();\n}\n\nvoid codon::pushLogger() { loggers.emplace_back(); }\n\nbool codon::popLogger() {\n  if (loggers.empty())\n    return false;\n  loggers.pop_back();\n  return true;\n}\n\nvoid codon::assertionFailure(const char *expr_str, const char *file, int line,\n                             const std::string &msg) {\n  auto &out = getLogger().err;\n  out << \"Assert failed:\\t\" << msg << \"\\n\"\n      << \"Expression:\\t\" << expr_str << \"\\n\"\n      << \"Source:\\t\\t\" << file << \":\" << line << \"\\n\";\n  abort();\n}\n"
  },
  {
    "path": "codon/util/common.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <chrono>\n#include <fmt/format.h>\n#include <fmt/ostream.h>\n#include <fmt/ranges.h>\n#include <fmt/std.h>\n#include <iostream>\n#include <ostream>\n\n#include \"codon/compiler/error.h\"\n#include \"codon/config/config.h\"\n#include \"codon/parser/ast/error.h\"\n\n#pragma clang diagnostic push\n#pragma clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"\n\n#define DBGI(c, ...)                                                                   \\\n  fmt::print(codon::getLogger().log, \"{}\" c \"\\n\",                                      \\\n             std::string(2 * codon::getLogger().level, ' '), ##__VA_ARGS__)\n#define DBG(c, ...) fmt::print(codon::getLogger().log, c \"\\n\", ##__VA_ARGS__)\n#define LOG(c, ...) DBG(c, ##__VA_ARGS__)\n#define LOG_TIME(c, ...)                                                               \\\n  {                                                                                    \\\n    if (codon::getLogger().flags & codon::Logger::FLAG_TIME)                           \\\n      DBG(c, ##__VA_ARGS__);                                                           \\\n  }\n#define LOG_REALIZE(c, ...)                                                            \\\n  {                                                                                    \\\n    if (codon::getLogger().flags & codon::Logger::FLAG_REALIZE)                        \\\n      DBG(c, ##__VA_ARGS__);                                                           \\\n  }\n#define LOG_TYPECHECK(c, ...)                                                          \\\n  {                                                                                    \\\n    if (codon::getLogger().flags & codon::Logger::FLAG_TYPECHECK)                      \\\n      DBG(c, ##__VA_ARGS__);                                                           \\\n  }\n#define LOG_IR(c, ...)                                                                 \\\n  {                                                                                    \\\n    if (codon::getLogger().flags & codon::Logger::FLAG_IR)                             \\\n      DBG(c, ##__VA_ARGS__);                                                           \\\n  }\n#define LOG_USER(c, ...)                                                               \\\n  {                                                                                    \\\n    if (codon::getLogger().flags & codon::Logger::FLAG_USER)                           \\\n      DBG(c, ##__VA_ARGS__);                                                           \\\n  }\n\n#define TIME(name) codon::Timer __timer(name)\n\n#ifndef NDEBUG\n#define seqassertn(expr, msg, ...)                                                     \\\n  ((expr) ? (void)(0)                                                                  \\\n          : codon::assertionFailure(#expr, __FILE__, __LINE__,                         \\\n                                    fmt::format(msg, ##__VA_ARGS__)))\n#define seqassert(expr, msg, ...)                                                      \\\n  ((expr) ? (void)(0)                                                                  \\\n          : codon::assertionFailure(                                                   \\\n                #expr, __FILE__, __LINE__,                                             \\\n                fmt::format(msg \" [{}]\", ##__VA_ARGS__, getSrcInfo())))\n#else\n#define seqassertn(expr, msg, ...) ;\n#define seqassert(expr, msg, ...) ;\n#endif\n#pragma clang diagnostic pop\n\nnamespace codon {\n\nvoid assertionFailure(const char *expr_str, const char *file, int line,\n                      const std::string &msg);\n\nstruct Logger {\n  static constexpr int FLAG_TIME = (1 << 0);\n  static constexpr int FLAG_REALIZE = (1 << 1);\n  static constexpr int FLAG_TYPECHECK = (1 << 2);\n  static constexpr int FLAG_IR = (1 << 3);\n  static constexpr int FLAG_USER = (1 << 4);\n\n  int flags;\n  int level;\n  std::ostream &out;\n  std::ostream &err;\n  std::ostream &log;\n\n  Logger() : flags(0), level(0), out(std::cout), err(std::cerr), log(std::clog) {}\n\n  void parse(const std::string &logs);\n};\n\nLogger &getLogger();\nvoid pushLogger();\nbool popLogger();\n\nclass Timer {\nprivate:\n  using clock_type = std::chrono::high_resolution_clock;\n  std::string name;\n  std::chrono::time_point<clock_type> start, end;\n\npublic:\n  bool logged;\n\npublic:\n  void log() {\n    if (!logged) {\n      LOG_TIME(\"[T] {} = {:.3f}\", name, elapsed());\n      logged = true;\n    }\n  }\n\n  double elapsed(std::chrono::time_point<clock_type> end = clock_type::now()) const {\n    return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() /\n           1000.0;\n  }\n\n  Timer(std::string name) : name(std::move(name)), start(), end(), logged(false) {\n    start = clock_type::now();\n  }\n\n  ~Timer() { log(); }\n};\n\nstd::ostream &operator<<(std::ostream &out, const codon::SrcInfo &src);\n\nstruct SrcObject {\nprivate:\n  SrcInfo info;\n\npublic:\n  SrcObject() : info() {}\n  SrcObject(const SrcObject &s) { setSrcInfo(s.getSrcInfo()); }\n\n  virtual ~SrcObject() = default;\n\n  SrcInfo getSrcInfo() const { return info; }\n\n  SrcObject *setSrcInfo(SrcInfo info) {\n    this->info = std::move(info);\n    return this;\n  }\n};\ntemplate <class... TA> void E(error::Error e, codon::SrcObject *o, const TA &...args) {\n  E(e, o->getSrcInfo(), args...);\n}\ntemplate <class... TA>\nvoid E(error::Error e, const codon::SrcObject &o, const TA &...args) {\n  E(e, o.getSrcInfo(), args...);\n}\ntemplate <class... TA>\nvoid E(error::Error e, const std::shared_ptr<SrcObject> &o, const TA &...args) {\n  E(e, o->getSrcInfo(), args...);\n}\n\nenum MessageGroupPos {\n  NONE = 0,\n  HEAD,\n  MID,\n  LAST,\n};\n\nvoid compilationError(const std::string &msg, const std::string &file = \"\",\n                      int line = 0, int col = 0, int len = 0, int errorCode = -1,\n                      bool terminate = true, MessageGroupPos pos = NONE);\n\nvoid compilationWarning(const std::string &msg, const std::string &file = \"\",\n                        int line = 0, int col = 0, int len = 0, int errorCode = -1,\n                        bool terminate = false, MessageGroupPos pos = NONE);\n\n} // namespace codon\n\ntemplate <> struct fmt::formatter<codon::SrcInfo> : fmt::ostream_formatter {};\n"
  },
  {
    "path": "codon/util/jupyter.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"codon/util/jupyter.h\"\n#include <cstdio>\n\nnamespace codon {\nint startJupyterKernel(const std::string &argv0,\n                       const std::vector<std::string> &plugins,\n                       const std::string &configPath) {\n  fprintf(stderr,\n          \"Jupyter support not included. Please install Codon Jupyter plugin.\\n\");\n  return EXIT_FAILURE;\n}\n\n} // namespace codon\n"
  },
  {
    "path": "codon/util/jupyter.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <string>\n#include <vector>\n\nnamespace codon {\nint startJupyterKernel(const std::string &argv0,\n                       const std::vector<std::string> &plugins,\n                       const std::string &configPath);\n} // namespace codon\n"
  },
  {
    "path": "codon/util/peg2cpp.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <algorithm>\n#include <any>\n#include <cassert>\n#include <cctype>\n#include <cstring>\n#include <fstream>\n#include <functional>\n#include <initializer_list>\n#include <iostream>\n#include <limits>\n#include <list>\n#include <map>\n#include <memory>\n#include <mutex>\n#include <peglib.h>\n#include <set>\n#include <sstream>\n#include <string>\n#include <unordered_map>\n#include <unordered_set>\n#include <vector>\n\n#define FMT_HEADER_ONLY\n#include <fmt/format.h>\n\nusing namespace std;\n\nstring escape(const string &str) {\n  string r;\n  for (unsigned char c : str) {\n    switch (c) {\n    case '\\n':\n      r += \"\\\\\\\\n\";\n      break;\n    case '\\r':\n      r += \"\\\\\\\\r\";\n      break;\n    case '\\t':\n      r += \"\\\\\\\\t\";\n      break;\n    case '\\\\':\n      r += \"\\\\\\\\\";\n      break;\n    case '\"':\n      r += \"\\\\\\\"\";\n      break;\n    default:\n      if (c < 32 || c >= 127)\n        r += fmt::format(\"\\\\\\\\x{:x}\", c);\n      else\n        r += c;\n    }\n  }\n  return r;\n}\ntemplate <typename T>\nstring join(const T &items, const string &delim = \" \", int start = 0, int end = -1) {\n  string s;\n  if (end == -1)\n    end = items.size();\n  for (int i = start; i < end; i++)\n    s += (i > start ? delim : \"\") + items[i];\n  return s;\n}\n\n// const string PREDICATE = \".predicate\";\n// bool is_predicate(const std::string &name) {\n//   return (name.size() > PREDICATE.size() && name.substr(name.size() -\n//   PREDICATE.size()) == PREDICATE);\n// }\n\nclass PrintVisitor : public peg::Ope::Visitor {\n  vector<string> v;\n\npublic:\n  static string parse(const shared_ptr<peg::Ope> &op) {\n    PrintVisitor v;\n    op->accept(v);\n    if (v.v.size()) {\n      if (v.v[0].empty())\n        return fmt::format(\"P[\\\"{}\\\"]\", v.v[1]);\n      else\n        return fmt::format(\"{}({})\", v.v[0], join(v.v, \", \", 1));\n    }\n    return \"-\";\n  };\n\nprivate:\n  void visit(peg::Sequence &s) override {\n    v = {\"seq\"};\n    for (auto &o : s.opes_)\n      v.push_back(parse(o));\n  }\n  void visit(peg::PrioritizedChoice &s) override {\n    v = {\"cho\"};\n    for (auto &o : s.opes_)\n      v.push_back(parse(o));\n  }\n  void visit(peg::Repetition &s) override {\n    if (s.is_zom())\n      v = {\"zom\", parse(s.ope_)};\n    else if (s.min_ == 1 && s.max_ == std::numeric_limits<size_t>::max())\n      v = {\"oom\", parse(s.ope_)};\n    else if (s.min_ == 0 && s.max_ == 1)\n      v = {\"opt\", parse(s.ope_)};\n    else\n      v = {\"rep\", parse(s.ope_), to_string(s.min_), to_string(s.max_)};\n  }\n  void visit(peg::AndPredicate &s) override { v = {\"apd\", parse(s.ope_)}; }\n  void visit(peg::NotPredicate &s) override { v = {\"npd\", parse(s.ope_)}; }\n  void visit(peg::LiteralString &s) override {\n    v = {s.ignore_case_ ? \"liti\" : \"lit\", fmt::format(\"\\\"{}\\\"\", escape(s.lit_))};\n  }\n  void visit(peg::CharacterClass &s) override {\n    vector<string> sv;\n    for (auto &c : s.ranges_)\n      sv.push_back(fmt::format(\"{{0x{:x}, 0x{:x}}}\", (int)c.first, (int)c.second));\n    v = {s.negated_ ? \"ncls\" : \"cls\", \"vc{\" + join(sv, \",\") + \"}\"};\n  }\n  void visit(peg::Character &s) override { v = {\"chr\", fmt::format(\"'{}'\", s.ch_)}; }\n  void visit(peg::AnyCharacter &s) override { v = {\"dot\"}; }\n  void visit(peg::Cut &s) override { v = {\"cut\"}; }\n  void visit(peg::Reference &s) override {\n    if (s.is_macro_) {\n      vector<string> vs;\n      for (auto &o : s.args_)\n        vs.push_back(parse(o));\n      v = {\"ref\",  \"P\",    fmt::format(\"\\\"{}\\\"\", s.name_),\n           \"\\\"\\\"\", \"true\", \"{\" + join(vs, \", \") + \"}\"};\n    } else {\n      v = {\"ref\", \"P\", fmt::format(\"\\\"{}\\\"\", s.name_)};\n    }\n  }\n  void visit(peg::TokenBoundary &s) override { v = {\"tok\", parse(s.ope_)}; }\n  void visit(peg::Ignore &s) override { v = {\"ign\", parse(s.ope_)}; }\n  void visit(peg::Recovery &s) override { v = {\"rec\", parse(s.ope_)}; }\n  // infix TODO\n};\n\nint main(int argc, char **argv) {\n  peg::parser parser;\n  fmt::print(\"Generating grammar from {}\\n\", argv[1]);\n  ifstream ifs(argv[1]);\n  string g((istreambuf_iterator<char>(ifs)), istreambuf_iterator<char>());\n  ifs.close();\n\n  string start;\n  peg::Rules dummy = {};\n  if (string(argv[3]) == \"codon\")\n    dummy[\"NLP\"] = peg::usr([](const char *, size_t, peg::SemanticValues &,\n                               any &) -> size_t { return -1; });\n  bool enablePackratParsing;\n  string preamble;\n  peg::Log log = [](size_t line, size_t col, const string &msg, const string &rule) {\n    cerr << line << \":\" << col << \": \" << msg << \" (\" << rule << \")\\n\";\n  };\n  auto grammar = peg::ParserGenerator::get_instance().perform_core(\n      g.c_str(), g.size(), dummy, start, enablePackratParsing, preamble, log);\n  assert(grammar);\n\n  string rules, actions, actionFns;\n  string action_preamble = \"  auto &CTX = any_cast<ParseContext &>(DT);\\n\";\n  string const_action_preamble =\n      \"  const auto &CTX = any_cast<const ParseContext &>(DT);\\n\";\n  string loc_preamble = \"  const auto &LI = VS.line_info();\\n\"\n                        \"  auto LOC = codon::SrcInfo(\\n\"\n                        \"    VS.path, LI.first + CTX.line_offset,\\n\"\n                        \"    LI.second + CTX.col_offset,\\n\"\n                        \"    VS.sv().size());\\n\";\n\n  for (auto &[name, def] : *grammar) {\n    auto op = def.get_core_operator();\n    if (dummy.find(name) != dummy.end())\n      continue;\n\n    rules += fmt::format(\"  {}P[\\\"{}\\\"] <= {};\\n\", def.ignoreSemanticValue ? \"~\" : \"\",\n                         name, PrintVisitor::parse(op));\n    rules += fmt::format(\"  P[\\\"{}\\\"].name = \\\"{}\\\";\\n\", name, escape(name));\n    if (def.is_macro)\n      rules += fmt::format(\"  P[\\\"{}\\\"].is_macro = true;\\n\", name);\n    if (!def.enable_memoize)\n      rules += fmt::format(\"  P[\\\"{}\\\"].enable_memoize = false;\\n\", name);\n    if (!def.params.empty()) {\n      vector<string> params;\n      for (auto &p : def.params)\n        params.push_back(fmt::format(\"\\\"{}\\\"\", escape(p)));\n      rules += fmt::format(\"  P[\\\"{}\\\"].params = {{{}}};\\n\", name, join(params, \", \"));\n    }\n\n    string code = op->code;\n    if (code.empty()) {\n      bool all_empty = true;\n      if (auto ope = dynamic_cast<peg::PrioritizedChoice *>(op.get())) {\n        for (int i = 0; i < ope->opes_.size(); i++)\n          if (!ope->opes_[i]->code.empty()) {\n            code +=\n                fmt::format(\"  if (VS.choice() == {}) {}\\n\", i, ope->opes_[i]->code);\n            all_empty = false;\n          } else {\n            code += fmt::format(\"  if (VS.choice() == {}) return V0;\\n\", i);\n          }\n      }\n      if (all_empty)\n        code = \"\";\n      if (!code.empty())\n        code = \"{\\n\" + code + \"}\";\n    }\n    if (!code.empty()) {\n      code = code.substr(1, code.size() - 2);\n      if (code.find(\"LOC\") != std::string::npos)\n        code = loc_preamble + code;\n      if (code.find(\"CTX\") != std::string::npos)\n        code = action_preamble + code;\n      actions += fmt::format(\"P[\\\"{}\\\"] = fn_{};\\n\", name, name);\n      actionFns += fmt::format(\n          \"auto fn_{}(peg::SemanticValues &VS, any &DT) {{\\n{}\\n}};\\n\", name, code);\n    }\n    if (!(code = def.predicate_code).empty()) {\n      code = code.substr(1, code.size() - 2);\n      if (code.find(\"LOC\") != std::string::npos)\n        code = loc_preamble + code;\n      if (code.find(\"CTX\") != std::string::npos)\n        code = const_action_preamble + code;\n      actions += fmt::format(\"P[\\\"{}\\\"].predicate = pred_{};\\n\", name, name);\n      actionFns += fmt::format(\"auto pred_{}(const peg::SemanticValues &VS, const any \"\n                               \"&DT, std::string &MSG) {{\\n{}\\n}};\\n\",\n                               name, code);\n    }\n  };\n\n  FILE *fout = fopen(argv[2], \"w\");\n  fmt::print(fout, \"// clang-format off\\n\");\n  fmt::print(fout, \"#pragma clang diagnostic push\\n\");\n  fmt::print(fout, \"#pragma clang diagnostic ignored \\\"-Wreturn-type\\\"\\n\");\n  if (!preamble.empty())\n    fmt::print(fout, \"{}\\n\", preamble.substr(1, preamble.size() - 2));\n  string rules_preamble = \"  using namespace peg;\\n\"\n                          \"  using peg::seq;\\n\"\n                          \"  using vc = vector<pair<char32_t, char32_t>>;\\n\";\n  fmt::print(fout, \"void init_{}_rules(peg::Grammar &P) {{\\n{}\\n{}\\n}}\\n\", argv[3],\n             rules_preamble, rules);\n  fmt::print(fout, \"{}\\n\", actionFns);\n  fmt::print(fout, \"void init_{}_actions(peg::Grammar &P) {{\\n  {}\\n}}\\n\", argv[3],\n             actions);\n  fmt::print(fout, \"// clang-format on\\n\");\n  fmt::print(fout, \"#pragma clang diagnostic pop\\n\");\n  fclose(fout);\n\n  return 0;\n}\n"
  },
  {
    "path": "codon/util/serialize.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n\n#include <functional>\n#include <string>\n#include <unordered_map>\n\n#include \"codon/util/tser.h\"\n\nnamespace codon {\n\ntemplate <class Archive, class Base> struct PolymorphicSerializer {\n  struct Serializer {\n    std::function<void(Base *, Archive &)> save;\n    std::function<void(Base *&, Archive &)> load;\n  };\n  template <class Derived> static Serializer serializerFor() {\n    return {[](Base *b, Archive &a) { a.save(*(static_cast<Derived *>(b))); },\n            [](Base *&b, Archive &a) {\n              b = new Derived();\n              a.load(static_cast<Derived &>(*b));\n            }};\n  }\n\n  static inline std::unordered_map<void *, std::string> _serializers;\n  static inline std::unordered_map<std::string, Serializer> _factory;\n  template <class... Derived> static void register_types() {\n    (_serializers.emplace((void *)(Derived::nodeId()), Derived::_typeName), ...);\n    (_factory.emplace(std::string(Derived::_typeName), serializerFor<Derived>()), ...);\n  }\n  static void save(const std::string &s, Base *b, Archive &a) {\n    auto i = _factory.find(s);\n    assert(i != _factory.end() && \"bad op\");\n    i->second.save(b, a);\n  }\n  static void load(const std::string &s, Base *&b, Archive &a) {\n    auto i = _factory.find(s);\n    assert(i != _factory.end() && \"bad op\");\n    i->second.load(b, a);\n  }\n};\n} // namespace codon\n\n#define SERIALIZE(Type, ...)                                                           \\\n  inline decltype(auto) members() const { return std::tie(__VA_ARGS__); }              \\\n  inline decltype(auto) members() { return std::tie(__VA_ARGS__); }                    \\\n  static constexpr std::array<char, tser::detail::str_size(#__VA_ARGS__)>              \\\n      _memberNameData = []() {                                                         \\\n        std::array<char, tser::detail::str_size(#__VA_ARGS__)> chars{'\\0'};            \\\n        size_t _idx = 0;                                                               \\\n        constexpr auto *ini(#__VA_ARGS__);                                             \\\n        for (char const *_c = ini; *_c; ++_c, ++_idx)                                  \\\n          if (*_c != ',' && *_c != ' ')                                                \\\n            chars[_idx] = *_c;                                                         \\\n        return chars;                                                                  \\\n      }();                                                                             \\\n  static constexpr const char *_typeName = #Type;                                      \\\n  static constexpr std::array<const char *, tser::detail::n_args(#__VA_ARGS__)>        \\\n      _memberNames = []() {                                                            \\\n        std::array<const char *, tser::detail::n_args(#__VA_ARGS__)> out{};            \\\n        for (size_t _i = 0, nArgs = 0; nArgs < tser::detail::n_args(#__VA_ARGS__);     \\\n             ++_i) {                                                                   \\\n          while (Type::_memberNameData[_i] == '\\0')                                    \\\n            _i++;                                                                      \\\n          out[nArgs++] = &Type::_memberNameData[_i];                                   \\\n          while (Type::_memberNameData[++_i] != '\\0')                                  \\\n            ;                                                                          \\\n        }                                                                              \\\n        return out;                                                                    \\\n      }()\n\n#define BASE(T) tser::base<T>(this)\n"
  },
  {
    "path": "codon/util/tser.h",
    "content": "// Licensed under the Boost License <https://opensource.org/licenses/BSL-1.0>.\n// SPDX-License-Identifier: BSL-1.0\n#pragma once\n#include <array>\n#include <cstring>\n#include <ostream>\n#include <string>\n#include <string_view>\n#include <tuple>\n#include <type_traits>\n// #include \"tser/varint_encoding.hpp\"// Licensed under the Boost License\n// <https://opensource.org/licenses/BSL-1.0>. SPDX-License-Identifier: BSL-1.0\n\n#include <type_traits>\nnamespace tser {\ntemplate <typename T> size_t encode_varint(T value, char *output) {\n  size_t i = 0;\n  if constexpr (std::is_signed_v<T>)\n    value = static_cast<T>(value << 1 ^ (value >> (sizeof(T) * 8 - 1)));\n  for (; value > 127; ++i, value >>= 7)\n    output[i] = static_cast<char>(static_cast<uint8_t>(value & 127) | 128);\n  output[i++] = static_cast<uint8_t>(value) & 127;\n  return i;\n}\ntemplate <typename T> size_t decode_varint(T &value, const char *const input) {\n  size_t i = 0;\n  for (value = 0; i == 0 || (input[i - 1] & 128); i++)\n    value |= static_cast<T>(input[i] & 127) << (7 * i);\n  if constexpr (std::is_signed_v<T>)\n    value = (value & 1) ? -static_cast<T>((value + 1) >> 1) : (value + 1) >> 1;\n  return i;\n}\n} // namespace tser\n\n// #include \"tser/base64_encoding.hpp\"// Licensed under the Boost License\n// <https://opensource.org/licenses/BSL-1.0>. SPDX-License-Identifier: BSL-1.0\n\n#include <array>\n#include <string>\n#include <string_view>\nnamespace tser {\n// tables for the base64 conversions\nstatic constexpr auto g_encodingTable =\n    \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\nstatic constexpr auto g_decodingTable = []() {\n  std::array<unsigned char, 256> decTable{};\n  for (unsigned char i = 0; i < 64u; ++i)\n    decTable[static_cast<unsigned char>(g_encodingTable[i])] = i;\n  return decTable;\n}();\nstatic std::string encode_base64(std::string_view in) {\n  std::string out;\n  unsigned val = 0;\n  int valb = -6;\n  for (char c : in) {\n    val = (val << 8) + static_cast<unsigned char>(c);\n    valb += 8;\n    while (valb >= 0) {\n      out.push_back(g_encodingTable[(val >> valb) & 63u]);\n      valb -= 6;\n    }\n  }\n  if (valb > -6)\n    out.push_back(g_encodingTable[((val << 8) >> (valb + 8)) & 0x3F]);\n  return out;\n}\nstatic std::string decode_base64(std::string_view in) {\n  std::string out;\n  unsigned val = 0;\n  int valb = -8;\n  for (char c : in) {\n    val = (val << 6) + g_decodingTable[static_cast<unsigned char>(c)];\n    valb += 6;\n    if (valb >= 0) {\n      out.push_back(char((val >> valb) & 0xFF));\n      valb -= 8;\n    }\n  }\n  return out;\n}\n} // namespace tser\n\nnamespace tser {\n// implementation details for C++20 is_detected\nnamespace detail {\nstruct ns {\n  ~ns() = delete;\n  ns(ns const &) = delete;\n};\ntemplate <class Default, class AlwaysVoid, template <class...> class Op, class... Args>\nstruct detector {\n  using value_t = std::false_type;\n  using type = Default;\n};\ntemplate <class Default, template <class...> class Op, class... Args>\nstruct detector<Default, std::void_t<Op<Args...>>, Op, Args...> {\n  using value_t = std::true_type;\n  using type = Op<Args...>;\n};\ntemplate <class T> struct is_array : std::is_array<T> {};\ntemplate <template <typename, size_t> class TArray, typename T, size_t N>\nstruct is_array<TArray<T, N>> : std::true_type {};\nconstexpr size_t n_args(char const *c, size_t nargs = 1) {\n  for (; *c; ++c)\n    if (*c == ',')\n      ++nargs;\n  return nargs;\n}\nconstexpr size_t str_size(char const *c, size_t strSize = 1) {\n  for (; *c; ++c)\n    ++strSize;\n  return strSize;\n}\n} // namespace detail\n// we need a bunch of template metaprogramming for being able to differentiate between\n// different types\ntemplate <template <class...> class Op, class... Args>\nconstexpr bool is_detected_v =\n    detail::detector<detail::ns, void, Op, Args...>::value_t::value;\n\nclass BinaryArchive;\ntemplate <class T> using has_begin_t = decltype(*std::begin(std::declval<T>()));\ntemplate <class T> using has_members_t = decltype(std::declval<T>().members());\ntemplate <class T>\nusing has_smaller_t = decltype(std::declval<T>() < std::declval<T>());\ntemplate <class T> using has_equal_t = decltype(std::declval<T>() == std::declval<T>());\ntemplate <class T>\nusing has_nequal_t = decltype(std::declval<T>() != std::declval<T>());\ntemplate <class T>\nusing has_outstream_op_t = decltype(std::declval<std::ostream>() << std::declval<T>());\ntemplate <class T> using has_tuple_t = std::tuple_element_t<0, T>;\ntemplate <class T> using has_optional_t = decltype(std::declval<T>().has_value());\ntemplate <class T> using has_element_t = typename T::element_type;\ntemplate <class T> using has_mapped_t = typename T::mapped_type;\ntemplate <class T>\nusing has_custom_save_t =\n    decltype(std::declval<T>().save(std::declval<BinaryArchive &>()));\ntemplate <class T>\nusing has_free_save_t =\n    decltype(std::declval<const T &>() << std::declval<BinaryArchive &>());\ntemplate <class T> constexpr bool is_container_v = is_detected_v<has_begin_t, T>;\ntemplate <class T> constexpr bool is_tuple_v = is_detected_v<has_tuple_t, T>;\ntemplate <class T> constexpr bool is_tser_t_v = is_detected_v<has_members_t, T>;\ntemplate <class T>\nconstexpr bool is_pointer_like_v =\n    std::is_pointer_v<T> || is_detected_v<has_element_t, T> ||\n    is_detected_v<has_optional_t, T>;\n// implementation of the recursive json printing\ntemplate <typename T> constexpr inline decltype(auto) print(std::ostream &os, T &&val) {\n  using V = std::decay_t<T>;\n  if constexpr (std::is_constructible_v<std::string, T> || std::is_same_v<V, char>)\n    os << \"\\\"\" << val << \"\\\"\";\n  else if constexpr (is_container_v<V>) {\n    size_t i = 0;\n    os << \"\\n[\";\n    for (auto &elem : val)\n      os << (i++ == 0 ? \"\" : \",\") << tser::print(os, elem);\n    os << \"]\\n\";\n  } else if constexpr (is_tser_t_v<V> && !is_detected_v<has_outstream_op_t, V>) {\n    auto pMem = [&](auto &...memberVal) {\n      size_t i = 0;\n      (((os << (i != 0 ? \", \" : \"\") << '\\\"'),\n        os << V::_memberNames[i++] << \"\\\" : \" << tser::print(os, memberVal)),\n       ...);\n    };\n    os << \"{ \\\"\" << V::_typeName << \"\\\": {\";\n    std::apply(pMem, val.members());\n    os << \"}}\\n\";\n  } else if constexpr (std::is_enum_v<V> && !is_detected_v<has_outstream_op_t, V>) {\n    os << tser::print(os, static_cast<std::underlying_type_t<V>>(val));\n  } else if constexpr (is_tuple_v<V> && !is_detected_v<has_outstream_op_t, V>) {\n    std::apply(\n        [&](auto &...t) {\n          int i = 0;\n          os << \"{\";\n          (((i++ != 0 ? os << \", \" : os), tser::print(os, t)), ...);\n          os << \"}\";\n        },\n        val);\n  } else if constexpr (is_pointer_like_v<V>) {\n    os << (val ? (os << (tser::print(os, *val)), \"\") : \"null\");\n  } else\n    os << val;\n  return \"\";\n}\n// we have to implement the tuple < operator ourselves for recursive introspection\ntemplate <typename T> constexpr inline bool less(const T &lhs, const T &rhs);\ntemplate <class T, std::size_t... I>\nconstexpr inline bool less(const T &lhs, const T &rhs, std::index_sequence<I...>) {\n  bool isSmaller = false;\n  (void)((less(std::get<I>(lhs), std::get<I>(rhs))\n              ? (static_cast<void>(isSmaller = true), false)\n              : (less(std::get<I>(rhs), std::get<I>(lhs)) ? false : true)) &&\n         ...);\n  return isSmaller;\n}\ntemplate <typename T> constexpr inline bool less(const T &lhs, const T &rhs) {\n  if constexpr (is_tser_t_v<T>)\n    return less(lhs.members(), rhs.members());\n  else if constexpr (is_tuple_v<T>)\n    return less(lhs, rhs, std::make_index_sequence<std::tuple_size_v<T>>());\n  else if constexpr (is_container_v<T> &&\n                     !tser::is_detected_v<tser::has_smaller_t, T>) {\n    if (lhs.size() != rhs.size())\n      return lhs.size() < rhs.size();\n    return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());\n  } else if constexpr (std::is_enum_v<T>)\n    return static_cast<std::underlying_type_t<T>>(lhs) <\n           static_cast<std::underlying_type_t<T>>(rhs);\n  else\n    return lhs < rhs;\n}\n\nclass BinaryArchive {\n  std::string m_bytes = std::string(1024, '\\0');\n  size_t m_bufferSize = 0, m_readOffset = 0;\n\npublic:\n  explicit BinaryArchive(const size_t initialSize = 1024)\n      : m_bytes(initialSize, '\\0') {}\n  explicit BinaryArchive(std::string encodedStr)\n      : m_bytes(decode_base64(encodedStr)), m_bufferSize(m_bytes.size()) {}\n  template <typename T, std::enable_if_t<!std::is_integral_v<T>, int> = 0>\n  explicit BinaryArchive(const T &t) {\n    save(t);\n  }\n  template <typename T> void save(const T &t) {\n    if constexpr (is_detected_v<has_free_save_t, T>)\n      operator<<(t, *this);\n    else if constexpr (is_detected_v<has_custom_save_t, T>)\n      t.save(*this);\n    else if constexpr (is_tser_t_v<T>)\n      std::apply([&](auto &...mVal) { (save(mVal), ...); }, t.members());\n    else if constexpr (is_tuple_v<T>)\n      std::apply([&](auto &...tVal) { (save(tVal), ...); }, t);\n    else if constexpr (is_pointer_like_v<T>) {\n      save(static_cast<bool>(t));\n      if (t)\n        save(*t);\n    } else if constexpr (is_container_v<T>) {\n      if constexpr (!detail::is_array<T>::value)\n        save(t.size());\n      for (auto &val : t)\n        save(val);\n    } else {\n      if (m_bufferSize + sizeof(T) + sizeof(T) / 4 > m_bytes.size())\n        m_bytes.resize((m_bufferSize + sizeof(T)) * 2);\n      if constexpr (std::is_integral_v<T> && sizeof(T) > 2)\n        m_bufferSize += encode_varint(t, m_bytes.data() + m_bufferSize);\n      else {\n        std::memcpy(m_bytes.data() + m_bufferSize, std::addressof(t), sizeof(T));\n        m_bufferSize += sizeof(T);\n      }\n    }\n  }\n  template <typename T> void load(T &t) {\n    using V = std::decay_t<T>;\n    if constexpr (is_detected_v<has_free_save_t, V>)\n      operator>>(t, *this);\n    else if constexpr (is_detected_v<has_custom_save_t, T>)\n      t.load(*this);\n    else if constexpr (is_tser_t_v<T>)\n      std::apply([&](auto &...mVal) { (load(mVal), ...); }, t.members());\n    else if constexpr (is_tuple_v<V>)\n      std::apply([&](auto &...tVal) { (load(tVal), ...); }, t);\n    else if constexpr (is_pointer_like_v<T>) {\n      if constexpr (std::is_pointer_v<T>) {\n        t = load<bool>() ? (t = new std::remove_pointer_t<T>(), load(*t), t) : nullptr;\n      } else if constexpr (is_detected_v<has_optional_t, T>)\n        t = load<bool>() ? T(load<typename V::value_type>()) : T();\n      else // smart pointer\n        t = T(load<has_element_t<V> *>());\n    } else if constexpr (is_container_v<T>) {\n      if constexpr (!detail::is_array<T>::value) {\n        const auto size = load<decltype(t.size())>();\n        using VT = typename V::value_type;\n        for (size_t i = 0; i < size; ++i)\n          if constexpr (!is_detected_v<has_mapped_t, V>)\n            t.insert(t.end(), load<VT>());\n          else // we have to special case map, because of the const key\n            t.emplace(\n                VT{load<typename V::key_type>(), load<typename V::mapped_type>()});\n      } else {\n        for (auto &val : t)\n          load(val);\n      }\n    } else {\n      if constexpr (std::is_integral_v<T> && sizeof(T) > 2)\n        m_readOffset += decode_varint(t, m_bytes.data() + m_readOffset);\n      else {\n        std::memcpy(&t, m_bytes.data() + m_readOffset, sizeof(T));\n        m_readOffset += sizeof(T);\n      }\n    }\n  }\n  template <typename T> T load() {\n    std::remove_const_t<T> t{};\n    load(t);\n    return t;\n  }\n  template <typename T>\n  friend BinaryArchive &operator<<(BinaryArchive &ba, const T &t) {\n    ba.save(t);\n    return ba;\n  }\n  template <typename T> friend BinaryArchive &operator>>(BinaryArchive &ba, T &t) {\n    ba.load(t);\n    return ba;\n  }\n  void reset() {\n    m_bufferSize = 0;\n    m_readOffset = 0;\n  }\n  void initialize(std::string_view str) {\n    m_bytes = str;\n    m_bufferSize = str.size();\n    m_readOffset = 0;\n  }\n  std::string_view get_buffer() const {\n    return std::string_view(m_bytes.data(), m_bufferSize);\n  }\n  friend std::ostream &operator<<(std::ostream &os, const BinaryArchive &ba) {\n    return os << encode_base64(ba.get_buffer()) << '\\n';\n  }\n};\ntemplate <class Base, typename Derived>\nstd::conditional_t<std::is_const_v<Derived>, const Base, Base> &base(Derived *thisPtr) {\n  return *thisPtr;\n}\ntemplate <typename T> auto load(std::string_view encoded) {\n  BinaryArchive ba(encoded);\n  return ba.load<T>();\n}\n} // namespace tser\n// this macro defines printing, serialisation and comparision operators (==,!=,<) for\n// custom types\n#define DEFINE_SERIALIZABLE(Type, ...)                                                 \\\n  inline decltype(auto) members() const { return std::tie(__VA_ARGS__); }              \\\n  inline decltype(auto) members() { return std::tie(__VA_ARGS__); }                    \\\n  static constexpr std::array<char, tser::detail::str_size(#__VA_ARGS__)>              \\\n      _memberNameData = []() {                                                         \\\n        std::array<char, tser::detail::str_size(#__VA_ARGS__)> chars{'\\0'};            \\\n        size_t _idx = 0;                                                               \\\n        constexpr auto *ini(#__VA_ARGS__);                                             \\\n        for (char const *_c = ini; *_c; ++_c, ++_idx)                                  \\\n          if (*_c != ',' && *_c != ' ')                                                \\\n            chars[_idx] = *_c;                                                         \\\n        return chars;                                                                  \\\n      }();                                                                             \\\n  static constexpr const char *_typeName = #Type;                                      \\\n  static constexpr std::array<const char *, tser::detail::n_args(#__VA_ARGS__)>        \\\n      _memberNames = []() {                                                            \\\n        std::array<const char *, tser::detail::n_args(#__VA_ARGS__)> out{};            \\\n        for (size_t _i = 0, nArgs = 0; nArgs < tser::detail::n_args(#__VA_ARGS__);     \\\n             ++_i) {                                                                   \\\n          while (Type::_memberNameData[_i] == '\\0')                                    \\\n            _i++;                                                                      \\\n          out[nArgs++] = &Type::_memberNameData[_i];                                   \\\n          while (Type::_memberNameData[++_i] != '\\0')                                  \\\n            ;                                                                          \\\n        }                                                                              \\\n        return out;                                                                    \\\n      }();                                                                             \\\n  template <typename OT,                                                               \\\n            std::enable_if_t<std::is_same_v<OT, Type> &&                               \\\n                                 !tser::is_detected_v<tser::has_equal_t, OT>,          \\\n                             int> = 0>                                                 \\\n  friend bool operator==(const Type &lhs, const OT &rhs) {                             \\\n    return lhs.members() == rhs.members();                                             \\\n  }                                                                                    \\\n  template <typename OT,                                                               \\\n            std::enable_if_t<std::is_same_v<OT, Type> &&                               \\\n                                 !tser::is_detected_v<tser::has_nequal_t, OT>,         \\\n                             int> = 0>                                                 \\\n  friend bool operator!=(const Type &lhs, const OT &rhs) {                             \\\n    return !(lhs == rhs);                                                              \\\n  }                                                                                    \\\n  template <typename OT,                                                               \\\n            std::enable_if_t<std::is_same_v<OT, Type> &&                               \\\n                                 !tser::is_detected_v<tser::has_smaller_t, OT>,        \\\n                             int> = 0>                                                 \\\n  friend bool operator<(const OT &lhs, const OT &rhs) {                                \\\n    return tser::less(lhs, rhs);                                                       \\\n  }                                                                                    \\\n  template <typename OT,                                                               \\\n            std::enable_if_t<std::is_same_v<OT, Type> &&                               \\\n                                 !tser::is_detected_v<tser::has_outstream_op_t, OT>,   \\\n                             int> = 0>                                                 \\\n  friend std::ostream &operator<<(std::ostream &os, const OT &t) {                     \\\n    tser::print(os, t);                                                                \\\n    return os;                                                                         \\\n  }\n"
  },
  {
    "path": "docs/css/extra.css",
    "content": "/* Announcement bar */\n.md-banner {\n  background-color: #5865F2;\n  background-image: radial-gradient(#777D92 1px, transparent 0);\n  background-size: 10px 10px;\n}\n\n.md-banner__inner {\n  font-size: 0.8rem;\n  margin: 0.3rem auto;\n  text-align: center;\n}\n\n.md-banner__inner a {\n  color: #fff;\n  display: inline-block;\n  position: relative;\n  text-decoration: none;\n}\n\n.md-banner__inner a:hover {\n  color: #fff;\n  text-decoration: none;\n}\n\n.md-banner__inner a:after {\n  background: none repeat scroll 0 0 transparent;\n  bottom: 0;\n  content: \"\";\n  display: block;\n  height: 1px;\n  left: 50%;\n  position: absolute;\n  background: #fff;\n  transition: width 0.3s ease 0s, left 0.3s ease 0s;\n  width: 0;\n}\n\n.md-banner__inner a:hover:after {\n  width: 100%;\n  left: 0;\n}\n\n.discord-icon svg {\n  height: 0.9em;\n  vertical-align: middle;\n  display: inline;\n  fill: currentColor;\n}\n\n\n/* Cards */\n.card-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));\n  gap: 1rem;\n  margin-top: 1rem;\n}\n\n.card {\n  display: block;\n  height: 100%;\n  border-radius: 0.25rem;\n  padding: 1rem;\n  text-decoration: none;\n  color: inherit;\n  background-color: var(--md-default-bg-color);\n}\n\n.card-img {\n  position: absolute;\n  bottom: 0.5rem;\n  right: 0.5rem;\n  z-index: 0;\n  opacity: 0.1;\n  pointer-events: none;\n}\n\n.card-img img {\n  max-width: 100px;\n  height: auto;\n}\n\n.card h3 {\n  margin-top: 0;\n  margin-bottom: 0.5rem;\n  color: var(--md-typeset-color);\n  position: relative;\n  display: inline-block;\n  transition: color 0.25s ease-in-out;\n}\n\n.card:hover h3 {\n  color: var(--md-accent-fg-color);\n}\n\n.card h3::after {\n  content: \"\";\n  position: absolute;\n  left: 0;\n  bottom: -2px;\n  width: 0%;\n  height: 0.075rem;\n  background-color: var(--md-accent-fg-color);\n  transition: width 0.25s ease-in-out;\n}\n\n.card:hover h3::after {\n  width: 100%;\n}\n\n.card p {\n  margin: 0;\n  font-size: 0.9rem;\n  color: var(--md-typeset-color);\n  transition: color 0.25s ease-in-out;\n}\n\n.card:hover p {\n  color: var(--md-accent-fg-color);\n}\n\n.card-wrap {\n  display: block;\n  position: relative;\n  background-color: #DBDBDB;\n  padding: 0.075rem;\n  border-radius: 0.25rem;\n  transition: background-color 0.25s ease-in-out;\n}\n\n[data-md-color-scheme=\"default\"] .card-wrap:hover {\n  background-color: var(--md-accent-fg-color);\n}\n\n[data-md-color-scheme=\"default\"] .card-icon-dark {\n  display: none;\n}\n\n[data-md-color-scheme=\"slate\"] .card-wrap:hover {\n  background-color: var(--md-accent-fg-color);\n}\n\n[data-md-color-scheme=\"slate\"] .card-icon-light {\n  display: none;\n}\n\n\n/* Labs */\n[data-md-color-scheme=\"default\"] .labs-logo-dark {\n  display: none;\n}\n\n[data-md-color-scheme=\"slate\"] .labs-logo-light {\n  display: none;\n}\n\n.lab-grid {\n  display: grid;\n  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));\n  gap: 1rem;\n  margin-top: 1rem;\n}\n\n.lab {\n  display: block;\n  height: 100%;\n  border-radius: 0.5rem;\n  padding: 1rem;\n  text-decoration: none;\n  color: inherit;\n  background-image: linear-gradient(to bottom right, #226BF9, #512888);\n  -webkit-box-shadow: 0px 10px 13px -7px #000000, 5px 5px 9px 4px rgba(0,0,0,0);\n  box-shadow: 0px 10px 13px -7px #000000, 5px 5px 9px 4px rgba(0,0,0,0);\n}\n\n.lab:hover {\n  background-image: linear-gradient(to bottom right, #226BF9D0, #512888D0);\n}\n\n.lab hr {\n  border: 0;\n  background-image: linear-gradient(to right, #00000000, #F8FAF9, #00000000);\n  margin-top: 10px;\n  margin-bottom: 10px;\n}\n\n.lab h3 {\n  position: relative;\n  z-index: 1;\n  margin-top: 0;\n  margin-bottom: 0;\n  color: #F8FAF9;\n}\n\n.lab p {\n  position: relative;\n  z-index: 1;\n  margin: 0;\n  font-size: 0.75rem;\n  color: #F8FAF9;\n}\n\n.lab-tag-new {\n  background-color: #0E0E0E;\n  color: white;\n  padding: 4px 8px;\n  text-align: center;\n  border-radius: 5px;\n  font-size: 0.5rem;\n  font-weight: bold;\n}\n\n.lab-tag-time {\n  background-color: #F8FAF9;\n  color: #0E0E0E;\n  border-radius: 5px;\n  padding: 4px 8px;\n  text-align: center;\n  font-size: 0.5rem;\n}\n\n.lab-tag-difficulty {\n  background-color: #777D92;\n  color: white;\n  border-radius: 5px;\n  padding: 4px 8px;\n  text-align: center;\n  font-size: 0.5rem;\n  font-weight: bold;\n}\n\n.lab-img {\n  position: absolute;\n  bottom: 0.5rem;\n  right: 0.5rem;\n  z-index: 0;\n  opacity: 0.15;\n  pointer-events: none;\n}\n\n.lab-img img {\n  max-width: 120px;\n  height: auto;\n}\n\n.lab-wrap {\n  display: block;\n  position: relative;\n  background-color: none;\n  padding: 0;\n  border-radius: 0.5rem;\n}\n\n\n/* Header */\n.md-header {\n  background-color: #0E0E0E;\n  border-bottom: 0.5px solid #F8FAF970;\n}\n\n\n/* Light mode */\n[data-md-color-scheme=\"default\"] {\n  --md-default-bg-color: #F8FAF9;\n}\n\n[data-md-color-scheme=\"default\"] .md-tabs {\n  background-color: #151720;\n}\n\n\n/* API reference tags */\n.api-tag {\n  background-color: #226BF9C0;\n  border: 1px solid #226BF9;\n  font-family: var(--md-code-font-family, monospace);\n  color: white;\n  padding: 4px 8px;\n  text-align: center;\n  border-radius: 5px;\n  font-size: 0.5rem;\n  display: inline-block;\n  transition: background-color 0.2s ease;\n  position: relative;\n  cursor: pointer;\n}\n\n.api-tag:hover {\n  background-color: #226BF9;\n}\n\n.api-tag-tooltip {\n  position: relative;\n  display: inline-block;\n}\n\n.api-tag-tooltiptext {\n  opacity: 0;\n  pointer-events: none;\n  width: max-content;\n  max-width: 200px;\n  background-color: #226BF9C0;\n  border: 1px solid #226BF9;\n  color: white;\n  text-align: center;\n  border-radius: 6px;\n  padding: 6px 8px;\n  position: absolute;\n  z-index: 10;\n  bottom: 100%; /* Position above */\n  left: 50%;\n  transform: translateX(-50%) scale(0.95);\n  transition: opacity 0.2s ease, transform 0.2s ease;\n  font-size: 0.7rem;\n  font-weight: 500;\n}\n\n.api-tag-tooltiptext::after {\n  content: '';\n  position: absolute;\n  top: 100%;\n  left: 50%;\n  transform: translateX(-50%);\n  border-width: 6px;\n  border-style: solid;\n  border-color: #226BF9 transparent transparent transparent;\n}\n\n.api-tag-tooltip:hover .api-tag-tooltiptext {\n  opacity: 1;\n  pointer-events: auto;\n  transform: translateX(-50%) scale(1);\n}\n\n\n/* Dark mode */\n[data-md-color-scheme=\"slate\"] {\n  --md-primary-bg-color: #F8FAF9;\n  --md-default-bg-color: #0E0E0E;\n  --md-typeset-color: #F8FAF9;\n\n  --md-footer-bg-color--dark: #0E0E0E;\n}\n\n[data-md-color-scheme=\"slate\"] .md-tabs {\n  background-color: #151720;\n}\n\n[data-md-color-scheme=\"slate\"] .md-nav__title {\n  background-color: #0E0E0E !important;\n}\n\n[data-md-color-scheme=\"slate\"] .md-nav__source {\n  background-color: #151720;\n}\n\n[data-md-color-scheme=\"slate\"] {\n  /* base bg + fg */\n  --md-code-bg-color:             #011627;\n  --md-code-fg-color:             #d6deeb;\n\n  /* Night Owl token colours */\n  --md-code-hl-comment-color:     #637777;               /* comments */\n  --md-code-hl-keyword-color:     #c792ea;               /* import, def, for, in */\n  --md-code-hl-function-color:    #82aaff;               /* definition names */\n  --md-code-hl-call-color:        #d6deeb;               /* built-in calls like range() */\n  --md-code-hl-variable-color:    #d6deeb;               /* locals / params (i, j, k) */\n  --md-code-hl-constant-color:    #9ba3b2;               /* e.g. constants / enums */\n  --md-code-hl-number-color:      #f78c6c;               /* numeric literals */\n  --md-code-hl-string-color:      #ecc48d;               /* string literals */\n  --md-code-hl-name-color:        #82aaff;               /* class names, tags */\n  --md-code-hl-operator-color:    #c792ea;               /* +, =, etc. */\n  --md-code-hl-punctuation-color: #9ba3b2;               /* commas, colons, brackets */\n  --md-code-hl-generic-color:     #d6deeb;               /* fallback */\n\n  /* line-highlight (selection) */\n  --md-code-hl-color:             rgba(29, 59, 83, 0.99);\n\n  /* inline code */\n  --md-code-inline-color:         #d6deeb;\n  --md-code-inline-bg-color:      #011221;\n}\n"
  },
  {
    "path": "docs/developers/build.md",
    "content": "!!! tip\n\n    Unless you really need to build Codon from source, it is strongly\n    recommended to use pre-built binaries if possible.\n\n## Dependencies\n\nCodon uses an LLVM fork based on LLVM 20. To build it, you can do the following:\n\n``` bash\ngit clone --depth 1 -b codon https://github.com/exaloop/llvm-project\ncmake -S llvm-project/llvm -B llvm-project/build \\\n    -DCMAKE_BUILD_TYPE=Release \\\n    -DLLVM_INCLUDE_TESTS=OFF \\\n    -DLLVM_ENABLE_RTTI=ON \\\n    -DLLVM_ENABLE_ZLIB=OFF \\\n    -DLLVM_ENABLE_ZSTD=OFF \\\n    -DLLVM_ENABLE_PROJECTS=\"openmp\" \\\n    -DLLVM_TARGETS_TO_BUILD=all\ncmake --build llvm-project/build\ncmake --install llvm-project/build --prefix=llvm-project/install\n```\n\nYou can use `-DLLVM_ENABLE_PROJECTS=\"clang;openmp\"` if you do not have `clang`\ninstalled on your system. We also recommend setting a local prefix during\ninstallation to avoid clashes with the system LLVM.\n\n## Build\n\nCodon requires `libgfortran`, the parent directory of which must be specified via the\n`CODON_SYSTEM_LIBRARIES` environment variable. For example, on macOS, with a\n`brew`-installed `libgfortran` (obtainable via `brew install gcc`):\n\n```bash\nexport CODON_SYSTEM_LIBRARIES=/opt/homebrew/opt/gcc/lib/gcc/current\n```\n\nOn Linux:\n\n```bash\nexport CODON_SYSTEM_LIBRARIES=/usr/lib/x86_64-linux-gnu\n```\n\nThen, the following can generally be used to build Codon. The build process\nwill automatically download and build several smaller dependencies.\n\n```bash\ncd codon\nmkdir build\ncmake -S . -B build \\\n    -DCMAKE_BUILD_TYPE=Release \\\n    -DLLVM_DIR=$(llvm-config --cmakedir) \\\n    -DCMAKE_C_COMPILER=clang \\\n    -DCMAKE_CXX_COMPILER=clang++\ncmake --build build --config Release\ncmake --install build --prefix=install\n```\n\nThis will produce the `codon` executable in the `install/bin` directory, as\nwell as `codon_test` in the `build` directory which runs the test suite.\nAdditionally, a number of shared libraries are produced in `install/lib/codon`:\n\n- `libcodonc`: The compiler library used by the `codon` command-line tool.\n- `libcodonrt`: The runtime library used during execution.\n- `libomp`: OpenMP runtime used to execute parallel code.\n\n!!! warning\n\n    Make sure the `llvm-config` being used corresponds to Codon's LLVM. You can also use\n    `-DLLVM_DIR=llvm-project/install/lib/cmake/llvm` on the first `cmake` command if you\n    followed the instructions above for compiling LLVM.\n\n## Jupyter support\n\nTo enable Jupyter support, you will need to build the Jupyter plugin:\n\n```bash\n# Linux version:\ncmake -S jupyter -B jupyter/build \\\n    -DCMAKE_BUILD_TYPE=Release \\\n    -DCMAKE_C_COMPILER=clang \\\n    -DCMAKE_CXX_COMPILER=clang++ \\\n    -DLLVM_DIR=$(llvm-config --cmakedir) \\\n    -DCODON_PATH=install \\\n    -DOPENSSL_ROOT_DIR=$(openssl version -d | cut -d' ' -f2 | tr -d '\"') \\\n    -DOPENSSL_CRYPTO_LIBRARY=/usr/lib64/libssl.so \\\n    -DXEUS_USE_DYNAMIC_UUID=ON\n# n.b. OPENSSL_CRYPTO_LIBRARY might differ on your system.\n\n# On macOS, do this instead:\nOPENSSL_ROOT_DIR=/usr/local/opt/openssl cmake -S jupyter -B jupyter/build \\\n    -DCMAKE_BUILD_TYPE=Release \\\n    -DCMAKE_C_COMPILER=clang \\\n    -DCMAKE_CXX_COMPILER=clang++ \\\n    -DLLVM_DIR=$(llvm-config --cmakedir) \\\n    -DCODON_PATH=install\n\n# Then:\ncmake --build jupyter/build\ncmake --install jupyter/build\n```\n"
  },
  {
    "path": "docs/developers/compilation.md",
    "content": "This page describes the internal compilation flow from source code to native code.\nIt is intended for Codon developers and contributors seeking to understand or extend\nthe compiler.\n\n![Codon compilation pipeline](../img/codon-pipeline.svg){ width=\"100%\" }\n\n## Overview\n\nCodon compiles Python code to native machine code using a custom frontend (parser and type\nchecker), an intermediate representation (IR), and LLVM as the backend. The compilation\nprocess is ahead-of-time (AOT) by default, though a just-in-time (JIT) mode is available\nas well.\n\nThe major compilation stages are:\n\n1. [Parsing](#parsing)\n2. [Type checking](#type-checking)\n3. [Codon IR generation](#codon-ir-generation)\n4. [Codon IR optimization](#codon-ir-optimization)\n5. [LLVM lowering](#llvm-lowering)\n6. [Code generation](#code-generation)\n\n## Parsing\n\nThe parser converts source code into an [abstract syntax tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree)\n(AST) using Python's grammar.\nCodon uses a custom [PEG](https://en.wikipedia.org/wiki/Parsing_expression_grammar) parser.\n\nFor example, this code:\n\n``` python\nx = 2 + 3\n```\n\nwould create the following AST after parsing:\n\n``` mermaid\ngraph TD\n  A(=) --> B(x);\n  A --> C(&plus;);\n  C --> D(2);\n  C --> E(3);\n```\n\nRelevant code and grammars can be found in\n[`codon/parser/peg/`](https://github.com/exaloop/codon/tree/develop/codon/parser/peg).\n\n\n## Type checking\n\nUnlike CPython, Codon performs type checking to determine data types ahead of time.\nType checking is applied on the AST, and attaches a type to every applicable AST node.\nFor example, the AST above would be typed as follows:\n\n``` mermaid\ngraph TD\n  A(\"= : int\") --> B(\"x : int\");\n  A --> C(\"&plus; : int\");\n  C --> D(\"2 : int\");\n  C --> E(\"3 : int\");\n```\n\nIn practice, numerous translation steps are applied to the AST as well. For example, operators like\n`+`, `-`, etc. are replaced by magic method calls like `int.__add__`, `float.__sub__`, etc. Similarly,\nfunctions without explicit type annotations are specialized when called for the given argument types,\na technique known as [*monomorphization*](https://en.wikipedia.org/wiki/Monomorphization).\n\nInternally, Coodn's type checker uses a modified\n[Hindley-Milner](https://en.wikipedia.org/wiki/Hindley–Milner_type_system) type system, adapted to handle\nvarious special cases relevant to Python code and semantics.\n\nRelevant code can be found in\n[`codon/parser/visitors/typecheck/`](https://github.com/exaloop/codon/tree/develop/codon/parser/visitors/typecheck).\nSee also the [*CC 2023* Codon paper](https://dl.acm.org/doi/abs/10.1145/3578360.3580275) for a\ndetailed overview of Coodn's type checker.\n\n\n## Codon IR generation\n\nAfter type checking, the typed AST is lowered to [Codon IR](ir.md). Codon IR utilizes\na vastly reduced set of nodes as compared to the AST, making it more practical for optimizations\nand analyses.\n\nRelevant code can be found in\n[`codon/parser/visitors/translate/`](https://github.com/exaloop/codon/tree/develop/codon/parser/visitors/translate).\n\n\n## Codon IR optimization\n\nCodon IR performs a suite of analyses and optimizations, ranging from general-purpose compiler\noptimizations like constant folding and dead code elimination to more specialized optimizations\nlike operator fusion for NumPy. Learn more in the [Codon IR docs](ir.md).\n\nRelevant code can be found in\n[`codon/cir/`](https://github.com/exaloop/codon/tree/develop/codon/cir).\n\n\n## LLVM lowering\n\nNext, Codon IR is lowered to [LLVM IR](https://llvm.org/docs/LangRef.html). Below,\nwe describe this process in detail.\n\n### Python types to LLVM types\n\nPython types need to be mapped to LLVM types. Some of the conversions are quite straightforward:\n\n- `int` becomes an LLVM `i64` (note that this deviates from Python's arbitrary-\n  width integers)\n- `float` becomes an LLVM `double`\n- `bool` becomes an LLVM `i8` (we could use `i1` in theory, but `i8` is\n  compatible with C/C++)\n\nTuple types are converted to `struct`s containing the tuple's element types.\nFor example, the type of `(42, 3.14, True)` becomes the LLVM structure\ntype `{i64, double, i8}`. Since tuples are immutable, these structs are passed by\nvalue, and in many cases tuples are completely optimized out by LLVM's optimization\npasses.\n\nUser-defined classes are similar, except instead of passing by value, they are dynamically\nallocated and passed by pointer, which allows mutations to be handled correctly.\nFor example, consider:\n\n``` python\nclass C:\n    a: int\n    b: float\n    c: bool\n```\n\nWhen creating an instance `C()`, under the hood a dynamic memory allocation occurs to store\nthe contents of the same `{i64, double, i8}`, and return a pointer to that memory as\nthe result of instantiation (after calling `C.__init__()`).\n\nThere are several other LLVM types that Codon exposes, like `Ptr[T]` to\nrepresent a pointer to an object of type `T`. Other Python types, however,\nare constructed from these building blocks. For example, the built-in collection\ntypes like `list`, `dict` and `set` are all implemented within Codon as classes; some\nother built-in types are implemented as named tuples, and so on.\n\n### Operators\n\nCodon IR has no concept of operators like `+`, `-`, etc. Instead, it represents these\noperations as magic method calls. Magic methods of primitive types like `int` are\nimplemented in Codon using [inline LLVM](../language/llvm.md). For example:\n\n``` python\nclass int:\n    ...\n\n    @llvm\n    def __add__(self, other: int) -> int:\n        %tmp = add i64 %self, %other\n        ret i64 %tmp\n\n    @llvm\n    def __add__(self, other: float) -> float:\n        %tmp1 = sitofp i64 %self to double\n        %tmp2 = fadd double %tmp1, %other\n        ret double %tmp2\n```\n\nNote that Codon supports method overloading: the compiler will choose the correct\n`__add__` based on the right-hand side's type during the parsing and type checking\nstages.\n\n### Control flow\n\nControl flow constructs require the creation of multiple LLVM\n[*basic blocks*](https://en.wikipedia.org/wiki/Basic_block).\nFor example, consider the following pseudocode:\n\n``` python\nif condition:\n    true_branch\nelse:\n    false_branch\n```\n\nCompilation would roughly proceed as follows, where $B$ denotes the *current* basic block\nas maintained by the LLVM lowering pass, and is where all new instructions are inserted:\n\n1. Create four new basic blocks: $B_\\mathrm{cond}$, $B_\\mathrm{true}$,\n   $B_\\mathrm{false}$ and $B_\\mathrm{exit}$.\n\n2. Generate a branch to $B_\\mathrm{cond}$.\n\n2. Set $B \\gets B_\\mathrm{cond}$ and generate code $C$ for `condition`, then generate\n   a conditional branch to $B_\\mathrm{true}$ and $B_\\mathrm{false}$ based on $C$.\n\n3. Set $B \\gets B_\\mathrm{true}$ and generate code for `true_branch`, then add a\n   branch to $B_\\mathrm{exit}$.\n\n4. Set $B \\gets B_\\mathrm{false}$ and generate code for `false_branch`, then add a\n   branch to $B_\\mathrm{exit}$.\n\n5. Set $B \\gets B_\\mathrm{exit}$.\n\nAs a diagram...\n\n``` mermaid\ngraph TD\n    B_0(B<sub>0</sub>) ---> B_cond\n    B_cond(B<sub>cond</sub>) -- C is true --> B_true\n    B_cond -- C is false --> B_false\n    B_true(B<sub>true</sub>) ---> B_exit(B<sub>exit</sub>)\n    B_false(B<sub>false</sub>) ---> B_exit\n```\n\nHere is another example involving a loop:\n\n``` python\nwhile condition:\n    body\n```\n\nCompilation would proceed as follows:\n\n1. Create three new basic blocks: $B_\\mathrm{cond}$, $B_\\mathrm{body}$ and\n   $B_\\mathrm{exit}$\n\n2. Generate a branch to $B_\\mathrm{cond}$.\n\n3. Set $B \\gets B_\\mathrm{cond}$ and generate code $C$ for `condition`, then\n   generate a conditional branch to $B_\\mathrm{body}$ and $B_\\mathrm{exit}$\n   based on $C$.\n\n4. Set $B \\gets B_\\mathrm{body}$ and generate code for `body`, then add a\n   branch to $B_\\mathrm{cond}$.\n\n5. Set $B \\gets B_\\mathrm{exit}$.\n\nAgain as a diagram...\n\n``` mermaid\ngraph TD\n    B_0(B<sub>0</sub>) ---> B_cond\n    B_cond(B<sub>cond</sub>) -- C is true --> B_body\n    B_cond -- C is false --> B_exit(B<sub>exit</sub>)\n    B_body(B<sub>body</sub>) ----> B_cond\n```\n\n`break` and `continue` are supported as follows: `break` simply becomes a branch to\n$B_\\mathrm{exit}$ and `continue` becomes a branch to $B_\\mathrm{cond}$. One notable\nexception to this pertains to `finally` blocks, which is discussed below.\n\nOther control flow constructs (like `elif`) work in an analogous way, and\nin fact can be constructed using just `if`-`else` and `while`.\n\n### `try`-`except`-`finally`\n\n`try`-`except`-`finally` is much more complex than other control flow constructs in terms\nor how it maps to LLVM IR. Exception handling itself is implemented in Codon using the\n[Itanium C++ ABI](https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html)\nfor zero-cost exceptions, along with LLVM's exception handling instructions:\n(`landingpad`, `resume`, `invoke`) and specialized\n[*personality function*](https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-personality).\nThere is additional bookkeeping required for knowing when we're compiling inside\na `try` block, which requires functions to be called with the LLVM `invoke` instruction\n(rather than the usual `call`) and to specify a basic block to branch to if an exception\noccurs (i.e. the basic block corresponding to `except`).\n\n`try`-`except`-`finally` becomes complex in cases where `finally` changes control flow\nin non-trivial ways, such as:\n\n``` python\ndef foo():\n    try:\n        return 1\n    finally:\n        return 2\n```\n\nWhat does `foo()` return? If you're not familiar with `finally` semantics, you might be\ninclined to say `1`, but the correct answer is `2`: `finally` blocks are *always* executed.\n\nThis has important implications for compilation: namely, *branches always need to be\naware of enclosing `try`-`finally` blocks*. Here is another example:\n\n``` python\ndef bar():\n    for i in range(10):\n        print(i)\n        try:\n            if i == 5:\n                break\n        finally:\n            continue\n```\n\nWhen `i == 5`, we'll reach the `break` statement, *but\nthe `break` needs to actually branch to the `finally` block*, otherwise the `finally`\nblock would we skipped over. Now, the `finally` itself has a `continue`, which overrides\nthe previous `break` and resumes the loop. So, in the end, all the integers `0`...`9`\nare printed.\n\nTo generate correct code for `try`-`except`-`finally`, Codon does the following:\n\n- Firstly, the LLVM lowering pass maintains a stack of enclosing `try`-`except`-`finally` blocks,\n  since if we reach a `return`, `break` or `continue`, we need to know whether\n  we really need to branch to some `finally` block.\n\n- Next, it constructs a state machine for each series of nested `try`-`except`-`finally`\n  blocks, since once we *do* reach the `finally`, we need to know how we got there in order to\n  determine what action needs to be taken next. For instance, if we got there via a `return`,\n  we need to actually execute that return statement at the end of the block; or perhaps we got\n  there by catching an exception that needs to be delegated to a parent `try`-`except`-`finally`.\n\nThe aforementioned state machine has the following states:\n\n- `NORMAL`: The `finally` was reached through normal execution of the code. Nothing special\n  needs to be done; just branch to the next block normally.\n\n- `THROWN`: An exception was thrown and we are executing the `finally` before propagating\n  the exception out of the function. The exception object itself will be stored in a pre-defined\n  place that we can access from the `finally` block.\n\n- `CAUGHT`: An exception was caught and we are reaching the `finally` through some `except` block.\n  Again nothing special needs to be done here; just branch to the next block normally.\n\n- `RETHROW`: The `finally` is being executed while unwinding and the exception must be re-thrown.\n\n- `RETURN`: The `finally` was reached after encountering an enclosed `return` statement. After\n  executing the `finally`, we need to execute the `return`. The return value itself will be\n  stored in a pre-defined place that we can access from the `finally` block.\n\n- `BREAK`: The `finally` was reached after encountering a `break`. We need to actually execute\n  the `break` after executing the `finally` block.\n\n- `CONTINUE`: The `finally` was reached after encountering a `continue`. We need to actually\n  execute the `continue` after executing the `finally` block.\n\nImportantly, these actions are *recursive*, so when we say *\"execute the `return` after\nexecuting the `finally` block\"*, that may entail branching to *another* enclosing `finally` block\nand repeating the same action.\n\nHere is a real example of what this state machine looks like. Consider:\n\n``` python\ndef baz():\n    for i in range(5):\n        try:\n            try:\n                if i == 3:\n                    return i\n            finally:\n                if i > 2:\n                    break\n        finally:\n            if i < 4:\n                continue\n    return i\n```\n\nHere are the various transitions between the states for different conditions throughout the\nloop:\n\n``` mermaid\nstateDiagram-v2\n    [*] --> NORMAL\n\n    NORMAL --> RETURN: i = 3\n    NORMAL --> BREAK: i > 3\n    NORMAL --> CONTINUE: i ≤ 2\n\n    RETURN --> BREAK: i > 2\n    RETURN --> CONTINUE: i ≤ 2\n    RETURN --> [*]: otherwise\n\n    BREAK --> CONTINUE: i < 4\n    BREAK --> [*]: otherwise\n\n    CONTINUE --> NORMAL\n```\n\nThe internal state machine will transition between the states based on these conditions until the\nloop terminates, signified by reaching the end state.\n\n### Variables\n\nLLVM IR uses [*static single assignment form*](https://en.wikipedia.org/wiki/Static_single-assignment_form),\nor SSA, which effectively means LLVM IR variables must be assigned *exactly* once. As a result, we\ncan't map Python variables directly to LLVM IR variables. Instead, we map each Python variable to\na stack-allocated piece of memory:\n\n``` python\nx = 42\n```\n\nbecomes:\n\n``` llvm\n%x = alloca i64, align 8\nstore i64 42, i64* %x, align 8\n```\n\n`alloca` is an LLVM IR instruction that allocates space on the current stack frame; `alloca i64` allocates\nspace for a 64-bit integer. Treating variables this way is standard practice when compiling to LLVM IR,\nand C/C++ compilers will do the same (e.g. `long x = 42` produces this exact code with Clang).\n\nMany variables are implicitly introduced by the parser and/or type checker. For example:\n\n``` python\na, b = b, a\n```\n\n... a common Python idiom for swapping the values of two variables, will implicitly be transformed into\n\n``` python\ntmp = (b, a)\na = tmp[0]\nb = tmp[1]\n```\n\nthus introducing the new variable `tmp`.\n\n### Functions\n\nPython functions map directly to LLVM functions:\n\n``` python\ndef foo(a: int, b: int):\n    return a + b\n```\n\nbecomes:\n\n``` llvm\ndefine i64 @foo(i64 %0, i64 %1) {\n  %a = alloca i64, align 8\n  %b = alloca i64, align 8\n  store i64 %0, i64* %a, align 8\n  store i64 %1, i64* %b, align 8\n  %a0 = load i64, i64* %a, align 8\n  %b0 = load i64, i64* %b, align 8\n  %ans = add nsw i64 %a0, %b0\n  ret i64 %ans\n}\n```\n\nNotice that space is allocated for the arguments via `alloca`, since they should be treated like normal\nvariables inside the function.\n\n### Generators\n\nGenerators are implemented using [LLVM coroutines](https://llvm.org/docs/Coroutines.html).\nCoroutines are like functions that allow suspension and resumption (much like what\nhappens with Python's `yield`). Coroutines maintain their state (i.e. local variables, position in the\nfunction, yielded value) in a *coroutine frame*. Coroutines in LLVM are indeed also like\nnormal functions, but delineate their resume/suspend points with special intrinsics and \"return\" a handle\nto their coroutine frames. Here are some of the important LLVM intrinsics that Codon uses when generating\ncode for coroutines:\n\n- `@llvm.coro.id`: Returns a token that can identify the coroutine, which can be passed to many of the\n  other intrinsics.\n- `@llvm.coro.size.i64`: Returns the size of the coroutine frame for dynamic allocation.\n- `@llvm.coro.begin`: Returns a \"handle\" to the coroutine frame.\n- `@llvm.coro.suspend`: Marks a suspension point.\n- `@llvm.coro.end`: Marks the end of the coroutine and destroys the coroutine frame.\n- `@llvm.coro.resume`: Resumes a coroutine given a coroutine handle.\n- `@llvm.coro.done`: Checks if a coroutine is at its final suspend point.\n- `@llvm.coro.promise`: Returns a pointer to the *coroutine promise*: a region of memory that stores\n  values \"yielded\" from the coroutine.\n- `@llvm.coro.destroy`: Destroys a finished coroutine.\n\nWith these primitives, generators are implemented roughly as follows:\n\n- Functions with `yield` are converted to LLVM coroutines.\n- Code is generated for `yield` statements by storing the yielded value in the coroutine promise,\n  then calling `@llvm.coro.suspend`.\n- Python's `next()` built-in is implemented by calling `@llvm.coro.done` to see if the given generator\n  is finished, then calling `@llvm.coro.resume` to resume it and finally `@llvm.coro.promise` to obtain\n  the generated value.\n- `for x in generator` is implemented by repeatedly calling `@llvm.coro.resume`/`@llvm.coro.promise`\n  until `@llvm.coro.done` indicates that we should stop.\n\nHere is an example:\n\n``` python\nfor i in range(3):\n    print(i)\n```\n\nHere is the (simplified) LLVM IR generated for this snippet:\n\n``` llvm\nentry:\n  %g = call ptr @range(i64 3)\n  br label %for\n\nfor:\n  call void @llvm.coro.resume(ptr %g)\n  %done = call i1 @llvm.coro.done(ptr %g)\n  br i1 %done, label %exit, label %body\n\nbody:\n  %p = call ptr @llvm.coro.promise(ptr %g, i32 8, i1 false)\n  %i = load i64, ptr %p\n  call void @print(i64 %i)\n  br label %for\n\nexit:\n  call void @llvm.coro.destroy(ptr %g)\n```\n\nIn summary:\n\n- The call to `@range` returns a *handle* (with type `ptr`) to the range generator.\n- The generator is resumed with `@llvm.coro.resume` (note that all coroutines will be initially suspended to\n  match Python's generator semantics).\n- If `@llvm.coro.done` indicates that the generator is done, the loop is exited and `@llvm.coro.destroy` is\n  called to destroy the generator.\n- Otherwise, in the body of the loop, the next value for `i` is obtained by calling `@llvm.coro.promise`\n  (the other arguments are simply the alignment of the promise (`i32 8`) and whether we want to obtain a\n  promise given a handle or vice versa (`i1 false`)).\n\nCoroutines are heavily optimized by LLVM. The code above, after optimizations are applied, becomes simply:\n\n``` llvm\n  call void @print(i64 0)\n  call void @print(i64 1)\n  call void @print(i64 2)\n```\n\n!!! info\n\n   Codon's LLVM fork implements coroutine elision analysis through escape analysis of the coroutine\n   handle, as opposed to the standard analysis that relies on examining `@llvm.coro.destroy` calls.\n   This was found to be much better at determining which coroutines can be optimized away.\n\n### Program structure\n\nPython doesn't have an explicit `main()` function as an entry point like C does.\nHowever, we need to generate a `main()` function in LLVM IR to be able to execute the generated code..\n\nCodon handles this by putting everything at the top level into its own implicit function:\n\n``` python\na = 42\nprint(a * 2)\n```\n\nbecomes:\n\n``` python\na = 0\ndef main():\n    global a\n    a = 42\n    print(a * 2)\n```\n\nIf compiling to a shared object, a [global constructor](https://en.wikipedia.org/wiki/Crt0)\nis added to call the implicit `main()`, which ensures initialization is still performed.\n\nRelevant code can be found in\n[`codon/cir/llvm`](https://github.com/exaloop/codon/tree/develop/codon/cir/llvm).\n\n\n## Code generation\n\nCodon invokes LLVM’s code generation infrastructure to emit native code. This also includes\ninvoking LLVM's optimization pipeline when compiling in release mode (corresponding to Clang's\n`-O3`). Codon also has several of its own LLVM optimization passes:\n\n- Allocation removal pass: Lowers dynamic allocation of known, small size to stack allocations.\n  This is useful when e.g. instantiating classes that don't escape the enclosing function.\n- Allocation hoist pass: Hoists dynamic allocations outside of loops when possible. This allows\n  the same memory to be reused across loop iterations which improves cache performance.\n- Allocation free pass: Automatically frees dynamic allocations when they are no longer being\n  used. Improves cache performance. This pass is off by default but can be enabled with the\n  hidden `-auto-free` compiler flag.\n- Coroutine branch simplification pass: Some of LLVM's standard coroutine passes can result\n  in complex branches that are difficult to reason about. This pass simplifies those branches.\n- Architecture-specific passes: Enables vectorization and other features specific to the host\n  architecture. Similar to `-march=native` in Clang. Can be disabled with the `-disable-native`\n  compiler flag.\n\n### GPU code generation\n\nIf the program contains [GPU kernels](../parallel/gpu.md), those kernels are separated into a new\nLLVM module to be handled specially. In particular:\n\n- Certain functions are replaced with GPU-compatible alternatives. For instance, the `sqrt()`\n  function might be replaced with `__nv_sqrt` from CUDA's libdevice library.\n- Exceptions are disabled and replaced with unreachable directives, which enables additional\n  optimizations.\n- The module is compiled to [PTX](https://en.wikipedia.org/wiki/Parallel_Thread_Execution) code\n  and written to disk. The PTX code is then loaded at runtime to invoke the kernel.\n\n\n## JIT compilation\n\nCodon also includes a JIT based on LLVM's [ORC JIT](https://llvm.org/docs/ORCv2.html) and implemented\nwith LLVM's [`LLJIT`](https://llvm.org/doxygen/classllvm_1_1orc_1_1LLJIT.html). This JIT\nis used to support the [`@codon.jit` Python decorator](../integrations/python/codon-from-python.md) as\nwell as [Codon's Jupyter kernel](../integrations/jupyter.md).\n\nNote that `codon run` also uses `LLJIT` to execute the generated LLVM code.\n\nRelevant code can be found in\n[`codon/compiler/`](https://github.com/exaloop/codon/tree/develop/codon/compiler).\n\n\n## Debugging\n\nThe `-log l` flag will dump the outputs of the various compilation stages to files that can be\nexamined. This includes:\n\n- AST with type information\n- Both unoptimized and optimized Codon IR\n- LLVM IR\n\nSee the [relevant docs](../start/usage.md#logging) for other debugging options.\n"
  },
  {
    "path": "docs/developers/contribute.md",
    "content": "Thank you for considering contributing to Codon! This page contains some helpful information for getting started.\nThe best place to ask questions or get feedback is [our Discord](https://discord.gg/HeWRhagCmP).\n\n## Development workflow\n\nAll development is done on the [`develop`](https://github.com/exaloop/codon/tree/develop) branch. Just before release,\nwe bump the version number, merge into [`master`](https://github.com/exaloop/codon/tree/master) and tag the build with\na tag of the form `vX.Y.Z` where `X`, `Y` and `Z` are the [SemVer](https://semver.org) major, minor and patch numbers,\nrespectively. Our CI build process automatically builds and deploys tagged commits as a new GitHub release.\n\n## Coding standards\n\nAll C++ code should be formatted with [ClangFormat](https://clang.llvm.org/docs/ClangFormat.html) using the `.clang-format`\nfile in the root of the repository.\n\n## Writing tests\n\nTests are written as Codon programs. The [`test/core/`](https://github.com/exaloop/codon/tree/master/test/core) directory\ncontains some examples. If you add a new test file, be sure to add it to\n[`test/main.cpp`](https://github.com/exaloop/codon/blob/master/test/main.cpp) so that it will be executed as part of the test\nsuite. There are two ways to write tests for Codon:\n\n### New style\n\nExample:\n\n```python\n@test\ndef my_test():\n    assert 2 + 2 == 4\nmy_test()\n```\n\n**Semantics:** `assert` statements in functions marked `@test` are not compiled to standard assertions: they don't terminate\nthe program when the condition fails, but instead print source information, fail the test, and move on.\n\n### Old style\n\nExample:\n\n```python\nprint(2 + 2)  # EXPECT: 4\n```\n\n**Semantics:** The source file is scanned for `EXPECT`s, executed, then the output is compared to the \"expected\" output. Note\nthat if you have, for example, an `EXPECT` in a loop, you will need to duplicate it however many times the loop is executed.\nUsing `EXPECT` is helpful mainly in cases where you need to test control flow, **otherwise prefer the new style**.\n\n## Pull requests\n\nPull requests should generally be based on the `develop` branch. Before submitting a pull request, please make sure...\n\n- ... to provide a clear description of the purpose of the pull request.\n- ... to include tests for any new or changed code.\n- ... that all code is formatted as per the guidelines above.\n"
  },
  {
    "path": "docs/developers/extend.md",
    "content": "Codon supports plugins that can be loaded dynamically. Plugins can\ncontain IR passes that can be inserted into the Codon IR pass pipeline,\nas well as LLVM passes to be used during backend code generation.\n\n## Configuration\n\nPlugin information is specified through a [TOML](https://toml.io/en/) file\ncalled `plugin.toml`. The following fields are supported:\n\n- `about.name`: Plugin name\n- `about.description`: Plugin description\n- `about.version`: Plugin version, using [semantic versioning](https://semver.org)\n- `about.url`: Plugin URL\n- `about.supported`: Supported Codon versions, using semantic versioning ranges\n- `library.cpp`: Shared library to be loaded upon loading the plugin, which includes\n  the plugin implementation (see below) and any necessary runtime functions. The\n  library extension (i.e. `.so` or `.dylib`) will be added automatically, and should\n  not be included.\n- `library.codon`: Standard library code that should be included with the plugin.\n  It is recommended to put this code in directory `stdlib/<plugin_name>`, whereupon\n  the value of this parameter would be `\"stdlib\"`.\n- `library.link`: Libraries to be linked when compiling to an executable. The string\n  `{root}` will be replaced with the path to the TOML configuration file. For example,\n  a value similar to `{root}/build/libmyplugin.a` might be used, assuming the plugin\n  also builds a static library containing necessary runtime functions.\n\nHere is an example configuration file for the validate pass shown in the\n[Codon IR docs](ir.md#bidirectionality):\n\n``` toml\n[about]\nname = \"MyValidate\"\ndescription = \"my validation plugin\"\nversion = \"0.0.1\"\nurl = \"https://example.com\"\nsupported = \">=0.18.0\"\n\n[library]\ncpp = \"build/libmyvalidate\"\n```\n\n## Implementation\n\nPlugins must extend the `codon::DSL` class to implement their functionality, and provide\na function `extern \"C\" std::unique_ptr<codon::DSL> load()` that returns an instance of\ntheir subclass. The `load()` function is invoked by Codon automatically when it loads a\nplugin from a shared library.\n\nContinuing on the same validate example from above, let's implement the pass as a\nplugin. We can use the same pass code in the section linked above, and create a subclass\nof the `codon::DSL` class while adding a function `load()` to our library that\nreturns an instance of it:\n\n``` cpp\nclass MyValidate : public codon::DSL {\npublic:\n  void addIRPasses(transform::PassManager *pm, bool debug) override {\n    std::string insertBefore = debug ? \"\" : \"core-folding-pass-group\";\n    pm->registerPass(std::make_unique<ValidateFoo>(), insertBefore);\n  }\n};\n\nextern \"C\" std::unique_ptr<codon::DSL> load() {\n  return std::make_unique<MyValidate>();\n}\n```\n\nThe `codon::DSL` class has methods for adding Codon IR passes, LLVM IR passes and even new\nsyntax features like keywords (hence the name DSL, since this class effectively\nenables the creation of domain-specific languages within Codon). In this case,\nwe insert our pass into the Codon IR pass manager before the standard folding pass.\n\nA `CMakeLists.txt` is also required, which specifies how to build the plugin to a shared\nlibrary with CMake. A complete implementation of this example plugin, along with the `CMakeLists.txt`,\ncan be found [on GitHub](https://github.com/exaloop/example-codon-plugin).\n\n### Adding LLVM passes\n\nPlugins can add new LLVM passes by overriding the `void addLLVMPasses(llvm::PassBuilder *pb, bool debug)`\nmethod of the `codon::DSL` class. Refer to the\n[`llvm::PassBuilder` docs](https://llvm.org/doxygen/classllvm_1_1PassBuilder.html) for details on adding\npasses.\n"
  },
  {
    "path": "docs/developers/ir.md",
    "content": "After type checking but before native code generation, the Codon compiler\nmakes use of a new [intermediate representation](https://en.wikipedia.org/wiki/Intermediate_representation)\ncalled CIR, where a number of higher-level optimizations, transformations and analyses take place.\nCIR offers a comprehensive framework for writing new optimizations or\nanalyses without having to deal with cumbersome abstract syntax trees (ASTs)\nalbeit while preserving higher-level program constructs and semantics.\n\n## Motivation\n\nIn Codon's optimization framework, we often found the need to reason about higher-level\nPython constructs, which wasn't feasible through LLVM IR. For instance, one optimization\nCodon performs pertains to removing redundant dictionary accesses, e.g. as found in this\ncommon code pattern:\n\n``` python\nd[k] = d.get(k, 0) + 1  # increment count of key 'k' in dict 'd'\n```\n\nThe original code performs two lookups of key `k`, but only one lookup is required. To\naddress this, we want to find patterns of the form `d[k] = d.get(k, i) + j` and replace them\nwith an implementation that performs just one lookup of `k`. It turns out that this is nearly\nimpossible to do at the LLVM IR level as `dict.__setitem__()` (which corresponds to the\nassignment `d[k] = ...`) and `dict.get()` both compile to large, complex LLVM IR that we'd\nhave no hope of recognizing and making sense of after the fact — in other words, the semantic\nmeaning of the original code (i.e. \"increment the count of `k` in `d`\") is effectively lost.\n\nTo solve this problem, Codon employs its own IR, which sits between the abstract syntax tree and\nLLVM IR in the compilation process. Codon IR is much simpler than the AST, but still retains\nenough information to reason about semantics and higher-level constructs. Identifying the\ndictionary pattern above, for example, amounts to simply identifying a short sequence of IR\nthat resembles `dict.__setitem__(d, k, dict.get(d, k, i) + j)`.\n\n## At a glance\n\nHere is a small (simplified) example showcasing CIR in action. Consider the code:\n\n``` python\ndef fib(n):\n    if n < 2:\n        return 1\n    else:\n        return fib(n - 1) + fib(n - 2)\n```\n\nWhen instantiated with an `int` argument, the following IR gets produced (the\nnames have been cleaned up for simplicity):\n\n``` lisp\n(bodied_func\n  '\"fib[int]\"\n  (type '\"fib[int]\")\n  (args (var '\"n\" (type '\"int\") (global false)))\n  (vars)\n  (series\n    (if (call '\"int.__lt__[int,int]\" '\"n\" 2)\n      (series (return 1))\n      (series\n        (return\n          (call\n            '\"int.__add__[int,int]\"\n            (call\n              '\"fib[int]\"\n              (call '\"int.__sub__[int,int]\" '\"n\" 1))\n            (call\n              '\"fib[int]\"\n              (call '\"int.__sub__[int,int]\" '\"n\" 2))))))))\n```\n\nA few interesting points to consider:\n\n- CIR is hierarchical like ASTS, but unlike ASTs it uses a vastly reduced\n  set of nodes, making it much easier to work with and reason about.\n- Operators are expressed as function calls. In fact, CIR has no explicit\n  concept of `+`, `-`, etc. and instead expresses these via their corresponding\n  magic methods (`__add__`, `__sub__`, etc.).\n- CIR has no concept of generic types. By the time CIR is generated, all types\n  need to have been resolved.\n\n## Structure\n\nCIR is comprised of a set of *nodes*, each with a specific semantic meaning.\nThere are nodes for representing constants (e.g. `42`), instructions (e.g. `call`)\ncontrol flow (e.g. `if`), types (e.g. `int`) and so on.\n\nHere is the node hierarchy:\n\n``` mermaid\ngraph TD\n  N(Node) --> M(Module);\n  N --> T(Type);\n  N --> VR(Var);\n  N --> VL(Value);\n  VR --> FN(Func);\n  VL --> C(Const);\n  VL --> I(Instr);\n  VL --> FL(Flow);\n```\n\nHere is a breakdown of the different types of nodes:\n\n- **Module**: a special container node that represents the entire program, i.e. all\n  the functions needed by the program as well as the main code that is executed when\n  running the program (in practice, we simply put this code into a new implicit \"main\"\n  function).\n- **Type**: a kind of node that is attached to all other nodes and represents the given\n  node's data type, be it an integer, float, string etc. One important aspect of Codon IR\n  is that it's fully typed, meaning we never deal with ambiguous or unknown types; type\n  checking is done on the AST before Codon IR is generated.\n- **Var**: represents a variable in a program. Variables are usually produced from assignments\n  like `x = y`, but they can also be produced from control-flow statements like\n  `for i in range(10)`, which creates variable `i`.\n- **Func**: a type of **Var** that represents a function.\n- **Value**: a generic node that represents values, which can be anything from constants to\n  variable references or function calls and so on.\n- **Instr**: a type of Value that represents a specific operation, such as a function call,\n  return statement, conditional expression (i.e. `x if y else z`) and so on.\n- **Flow**: represents control-flow statements like `if`-statements, `while`-loops,\n  `try`-`except` etc. These are first-class values in Codon IR.\n- **Const**: represents a constant like `42`, `\"hello\"` or `3.14`.\n\n## Uses\n\nCIR provides a framework for doing program optimizations, analyses and transformations.\nThese operations are collectively known as IR *passes*.\n\nA number of built-in passes and other functionalities are provided by CIR. These can be\nused as building blocks to create new passes. Examples include:\n\n- Control-flow graph creation\n- Reaching definitions\n- Dominator analysis\n- Side effect analysis\n- Constant propagation and folding\n- Canonicalization\n- Inlining and outlining\n- Python-specific optimizations targeting several common Python idioms\n\nWe're regularly adding new standard passes, so this list is always growing.\n\n### An example\n\nLet's look at a real example. Imagine we want to write a pass that transforms expressions\nof the form `<int const> + <int const>` into a single `<int const>` denoting the result.\nIn other words, a simple form of constant folding that only looks at addition on integers.\nThe resulting pass would like this:\n\n``` cpp\n#include \"codon/cir/transform/pass.h\"\n#include \"codon/cir/util/irtools.h\"\n\nusing namespace codon::ir;\n\nclass MyAddFolder : public transform::OperatorPass {\npublic:\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n\n  void handle(CallInstr *v) override {\n    auto *f = util::getFunc(v->getCallee());\n    if (!f || f->getUnmangledName() != \"__add__\" || v->numArgs() != 2)\n        return;\n\n    auto *lhs = cast<IntConst>(v->front());\n    auto *rhs = cast<IntConst>(v->back());\n\n    if (lhs && rhs) {\n      long sum = lhs->getVal() + rhs->getVal();\n      v->replaceAll(v->getModule()->getInt(sum));\n    }\n  }\n};\n\nconst std::string MyAddFolder::KEY = \"my-add-folder\";\n```\n\nSo how does this actually work, and what do the different components mean? Here\nare some notable points:\n\n- Most passes can inherit from `transform::OperatorPass`. `OperatorPass` is a combination\n  of an `Operator` and a `Pass`. An `Operator` is a utility visitor that provides hooks for\n  handling all the different node types (i.e. through the `handle()` methods). `Pass` is the\n  base class representing a generic pass, which simply provides a `run()` method that takes\n  a module.\n- Because of this, `MyAddFolder::handle(CallInstr *)` will be called on every call instruction\n  in the module.\n- Within our `handle()`, we first check to see if the function being called is `__add__`, indicating\n  addition (in practice there would be a more specific check to make sure this is *the* `__add__`),\n  and if so we extract the first and second arguments.\n- We cast these arguments to `IntConst`. If the results are non-null, then both arguments were in fact\n  integer constants, meaning we can replace the original call instruction with a new constant that\n  represents the result of the addition. In CIR, all nodes are \"replaceable\" via a `replaceAll()` method.\n- Lastly, notice that all passes have a `KEY` field to uniquely identify them.\n\n### Bidirectionality\n\nAn important and often very useful feature of CIR is that it is *bidirectional*, meaning it's possible\nto return to the type checking stage to generate new IR nodes that were not initially present in the\nmodule. For example, imagine that your pass needs to use a `List` with some new element type; that list's\nmethods need to be instantiated by the type checker for use in CIR. In practice this bidirectionality\noften lets you write large parts of your optimization or transformation in Codon, and pull out the necessary\nfunctions or types as needed in the pass.\n\nCIR's `Module` class has three methods to enable this feature:\n\n``` cpp\n  /// Gets or realizes a function.\n  /// @param funcName the function name\n  /// @param args the argument types\n  /// @param generics the generics\n  /// @param module the module of the function\n  /// @return the function or nullptr\n  Func *getOrRealizeFunc(const std::string &funcName, std::vector<types::Type *> args,\n                         std::vector<types::Generic> generics = {},\n                         const std::string &module = \"\");\n\n  /// Gets or realizes a method.\n  /// @param parent the parent class\n  /// @param methodName the method name\n  /// @param rType the return type\n  /// @param args the argument types\n  /// @param generics the generics\n  /// @return the method or nullptr\n  Func *getOrRealizeMethod(types::Type *parent, const std::string &methodName,\n                           std::vector<types::Type *> args,\n                           std::vector<types::Generic> generics = {});\n\n  /// Gets or realizes a type.\n  /// @param typeName mangled type name\n  /// @param generics the generics\n  /// @return the function or nullptr\n  types::Type *getOrRealizeType(const std::string &typeName,\n                                std::vector<types::Generic> generics = {});\n```\n\nLet's see bidirectionality in action. Consider the following Codon code:\n\n``` python\ndef foo(x):\n    return x*3 + x\n\ndef validate(x, y):\n    assert y == x*4\n\na = foo(10)\nb = foo(1.5)\nc = foo('a')\n```\n\nAssume we want our pass to insert a call to `validate()` after each assignment that takes the assigned variable\nand the argument passed to `foo()`. We would do something like the following:\n\n``` cpp\n#include \"codon/cir/transform/pass.h\"\n#include \"codon/cir/util/irtools.h\"\n\nusing namespace codon::ir;\n\nclass ValidateFoo : public transform::OperatorPass {\npublic:\n  static const std::string KEY;\n  std::string getKey() const override { return KEY; }\n\n  void handle(AssignInstr *v) override {\n    auto *M = v->getModule();\n    auto *var = v->getLhs();\n    auto *call = cast<CallInstr>(v->getRhs());\n    if (!call)\n      return;\n\n    auto *foo = util::getFunc(call->getCallee());\n    if (!foo || foo->getUnmangledName() != \"foo\")\n      return;\n\n    auto *arg1 = call->front();         // argument of 'foo' call\n    auto *arg2 = M->Nr<VarValue>(var);  // result of 'foo' call\n    auto *validate =\n      M->getOrRealizeFunc(\"validate\", {arg1->getType(), arg2->getType()});\n    if (!validate)\n      return;\n\n    auto *validateCall = util::call(validate, {arg1, arg2});\n    insertAfter(validateCall);  // call 'validate' after 'foo'\n  }\n};\n\nconst std::string ValidateFoo::KEY = \"validate-foo\";\n```\n\nNote that `insertAfter` is a convenience method of `Operator` that inserts the given node \"after\" the node\nbeing visited (along with `insertBefore` which inserts *before* the node being visited).\n\nRunning this pass on the snippet above, we would get:\n\n``` python\na = foo(10)\nvalidate(10, a)\n\nb = foo(1.5)\nvalidate(1.5, b)\n\nc = foo('a')\nvalidate('a', c)\n```\n\nNotice that we used `getOrRealizeFunc` to create three different instances of `validate`: one for `int`\narguments, one for `float` arguments and finally one for `str` arguments.\n\n## Extending the IR\n\nCIR is extensible, and it is possible to add new constants, instructions, flows and types. This can be\ndone by subclassing the corresponding *custom* base class; to create a custom type, for example, you\nwould subclass `CustomType`. Let's look at an example where we extend CIR to add a 32-bit float type\n(note that Codon already has a native 32-bit float type; this is just for demonstration purposes):\n\n``` cpp\nusing namespace codon::ir;\n\n#include \"codon/cir/dsl/nodes.h\"\n#include \"codon/cir/llvm/llvisitor.h\"\n\nclass Builder : public dsl::codegen::TypeBuilder {\npublic:\n  llvm::Type *buildType(LLVMVisitor *v) override {\n    return v->getBuilder()->getFloatTy();\n  }\n\n  llvm::DIType *buildDebugType(LLVMVisitor *v) override {\n    auto *module = v->getModule();\n    auto &layout = module->getDataLayout();\n    auto &db = v->getDebugInfo();\n    auto *t = buildType(v);\n    return db.builder->createBasicType(\n           \"float_32\",\n           layout.getTypeAllocSizeInBits(t),\n           llvm::dwarf::DW_ATE_float);\n  }\n};\n\nclass Float32 : public dsl::CustomType {\npublic:\n  std::unique_ptr<TypeBuilder> getBuilder() const override {\n    return std::make_unique<Builder>();\n  }\n};\n```\n\nNotice that, in order to specify how to generate code for our `Float32` type, we create a `TypeBuilder`\nsubclass with methods for building the corresponding LLVM IR type. There is also a `ValueBuilder` for\nnew constants and converting them to LLVM IR, as well as a `CFBuilder` for new instructions and creating\ncontrol-flow graphs out of them.\n\n!!! tip\n\n    When subclassing nodes other than types (e.g. instructions, flows, etc.), be sure to use the `AcceptorExtend`\n    [CRTP](https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern) class, as in\n    `class MyNewInstr : public AcceptorExtend<MyNewInstr, dsl::CustomInstr>`.\n\n\n## Utilities\n\nThe `codon/cir/util/` directory has a number of utility and generally helpful functions, for things like\ncloning IR, inlining/outlining, matching and more. `codon/cir/util/irtools.h` in particular has many helpful\nfunctions for performing various common tasks. If you're working with CIR, be sure to take a look at these\nfunctions to make your life easier!\n\n## Standard pass pipeline\n\nThese standard sets of passes are run in `release`-mode:\n\n- Python-specific optimizations: a series of passes to optimize common Python patterns and\n  idioms. Examples include dictionary updates of the form `d[k] = d.get(k, x) <op> y`, and\n  optimizing them to do just *one* access into the dictionary, as well as optimizing repeated\n  string concatenations, various I/O patterns, list operations and so on.\n\n- Imperative `for`-loop lowering: loops of the form `for i in range(a, b, c)` (with `c` constant)\n  are lowered to a special IR node, since these loops are important for e.g. multithreading later.\n\n- A series of program analyses whose results are available to later passes:\n  - [Control-flow analysis](https://en.wikipedia.org/wiki/Control_flow_analysis)\n  - [Reaching definition analysis](https://en.wikipedia.org/wiki/Reaching_definition)\n  - [Dominator analysis](https://en.wikipedia.org/wiki/Dominator_(graph_theory))\n  - [Capture (or escape) analysis](https://en.wikipedia.org/wiki/Escape_analysis)\n\n- Parallel loop lowering for multithreading or GPU\n\n- Constant propagation and folding. This also includes dead code elimination and (in non-JIT mode)\n  global variable demotion.\n\n- Library-specific optimizations, such as operator fusion for [NumPy](../libraries/numpy.md).\n\nCodon [plugins](extend.md) can inject their own passes into the pipeline as well.\n"
  },
  {
    "path": "docs/developers/roadmap.md",
    "content": "Codon's goal is to be as close to CPython as possible while still\nbeing fully statically compilable. While Codon already supports\nmuch of Python, there is still much to be done to fully realize\nits potential. Here is a high-level roadmap of the features we\nwant to implement or explore.\n\n## Core features\n\n- Parallelism\n\n    - `async`/`await` support\n    - `multiprocessing` support\n    - Automatic locking in parallel code (e.g. if mutating a\n    data structure shared between threads)\n    - Race detection\n\n- Compatibility with Python 3.10+:\n    - Argument separators (`/` and `*`)\n    - Constructor object matching in the `match` statement\n    - Support accessing various object properties (`__dict__`, `__slots__`\n      etc.) as much as possible in Codon's static context\n\n- Optional automatic switching between Codon and CPython (i.e.\n  compile only compatible functions and leave the rest to Python)\n\n- Better error messages\n    - Warning support\n    - Explain performance considerations\n    - Explain Codon-specific optimizations (e.g. Codon IR passes)\n    - Explain that a CPython feature is not supported\n\n- Modules and incremental compilation\n    - Cache compilation modules\n    - Fast generics compilation in debug mode for quick turnarounds\n\n- Memory management\n    - Auto-tune GC\n    - Optional alternative memory management modes like reference\n      counting\n\n- GPU support\n    - Target Apple, AMD and Intel GPUs\n    - GPU-specific compiler optimizations (e.g. for using various\n      Python constructs on the GPU)\n\n- Interoperability with other languages\n    - Direct C++ interoperability via Clang\n    - R interoperability\n\n## Libraries\n\nCurrently, missing Python functionality can be easily accessed via a\n[`from python import` statement](../integrations/python/python-from-codon.md),\nwhich is sufficient in most cases as many libraries are just thin wrappers\naround a C library and/or not performance-sensitive.\n\nHowever, we would like to support the following modules natively:\n\n- Python's standard library\n    - Complete builtins support\n    - 1-to-1 compatibility with existing Python functions and modules\n    - File modules: `os`, `sys`, `struct`, `pathlib` and so on\n    - Pretty much everything else on an as-needed basis\n\n- Native NumPy, Pandas, etc.: Having Codon-native versions of the most\n  popular 3rd-party libraries would allow them to work with Codon's\n  other features like multithreading and GPU. We're currently prioritizing\n  NumPy and Pandas but aim to later target other popular libraries as well.\n    - **As of Codon 0.18, NumPy is natively supported!**\n\n- Unicode support\n\n- Python's testing infrastructure\n\n## Infrastructure & Tools\n\n- Windows support\n\n- A sane package manager similar to Rust's\n  [Cargo](https://github.com/rust-lang/cargo)\n\n- Auto-detection of installed Python libraries\n\n- Improved `codon.jit` library support\n    - Better error messages\n    - Better installation flow\n\n- Fully static binary support like Go\n    - Remove `libcodonrt` (runtime library) dependency if needed\n    - Remove `libcpp` dependency\n\n- Improved Jupyter support\n    - Auto-completion and code inspection\n    - Jupyter magic command support\n\n- Plugins for Visual Studio Code, Vim, Emacs and so on\n\n## Nice to have\n\n- Implement Codon in Codon\n"
  },
  {
    "path": "docs/index.md",
    "content": "# Welcome to Codon\n\nCodon is a high-performance Python implementation that compiles to native machine code without\nany runtime overhead. Typical speedups over vanilla Python are on the order of 10-100x or more, on\na single thread. Codon's performance is typically on par with (and sometimes better than) that of\nC/C++. Unlike Python, Codon supports native multithreading, which can lead to speedups many times\nhigher still.\n\n!!! tip\n\n    Think of Codon as Python reimagined for static, ahead-of-time compilation, built from the ground\n    up with best possible performance in mind.\n\n## Explore\n\n<div class=\"card-grid\">\n\n  <div class=\"card-wrap\">\n    <a class=\"card\" id=\"card-start\" href=\"start/install\">\n      <h3>Getting Started &#8594;</h3>\n      <p>Learn how to install Codon and run your first program.</p>\n    </a>\n    <div class=\"card-img\">\n      <img src=\"img/card-start-dark.svg\" class=\"card-icon-dark\" height=\"90\" width=\"100\" />\n    </div>\n    <div class=\"card-img\">\n      <img src=\"img/card-start-light.svg\" class=\"card-icon-light\" height=\"90\" width=\"100\" />\n    </div>\n  </div>\n\n  <div class=\"card-wrap\">\n    <a class=\"card\" href=\"labs\">\n      <h3>Labs &#8594;</h3>\n      <p>Step-by-step guides to learn Codon concepts and features.</p>\n    </a>\n    <div class=\"card-img\">\n      <img src=\"img/card-labs-dark.svg\" class=\"card-icon-dark\" height=\"90\" width=\"100\" />\n    </div>\n    <div class=\"card-img\">\n      <img src=\"img/card-labs-light.svg\" class=\"card-icon-light\" height=\"90\" width=\"100\" />\n    </div>\n  </div>\n\n  <div class=\"card-wrap\">\n    <a class=\"card\" href=\"parallel/multithreading\">\n      <h3>Parallelism &#8594;</h3>\n      <p>Learn parallel programming in Codon.</p>\n    </a>\n    <div class=\"card-img\">\n      <img src=\"img/card-parallel-dark.svg\" class=\"card-icon-dark\" height=\"90\" width=\"100\" />\n    </div>\n    <div class=\"card-img\">\n      <img src=\"img/card-parallel-light.svg\" class=\"card-icon-light\" height=\"90\" width=\"100\" />\n    </div>\n  </div>\n\n  <div class=\"card-wrap\">\n    <a class=\"card\" href=\"libraries/api\">\n      <h3>API Reference &#8594;</h3>\n      <p>Explore built-in types, functions, and modules available in Codon.</p>\n    </a>\n    <div class=\"card-img\">\n      <img src=\"img/card-api-dark.svg\" class=\"card-icon-dark\" height=\"90\" width=\"100\" />\n    </div>\n    <div class=\"card-img\">\n      <img src=\"img/card-api-light.svg\" class=\"card-icon-light\" height=\"90\" width=\"100\" />\n    </div>\n  </div>\n\n</div>\n\n## Uses\n\n- [x] Accelerate Python code without having to rewrite it in another language\n- [x] Write multithreaded or GPU code in Python\n- [x] Compile Python code to run on various kinds of hardware, such as edge devices or embedded systems\n- [x] JIT-compile performance-sensitive functions in an existing Python codebase\n- [x] Accelerate and parallelize NumPy code\n- [x] Create Python extensions without using C or Cython\n\n## Goals\n\n- :bulb: **No learning curve:** Be as close to CPython as possible in terms of syntax, semantics and libraries\n- :rocket: **Top-notch performance:** At *least* on par with low-level languages like C, C++ or Rust\n- :computer: **Hardware support:** Full, seamless support for multicore programming, multithreading (no GIL!), GPU and more\n- :chart_with_upwards_trend: **Optimizations:** Comprehensive optimization framework that can target high-level Python constructs\n  and libraries\n- :battery: **Interoperability:** Full interoperability with Python's ecosystem of packages and libraries\n\n## Non-goals\n\n- :x: *Drop-in replacement for CPython:* Codon is not a drop-in replacement for CPython. There are some\n  aspects of Python that are not suitable for static compilation — we don't support these in Codon.\n  There are ways to use Codon in larger Python codebases via its [JIT decorator](integrations/python/codon-from-python.md)\n  or [Python extension backend](integrations/python/extensions.md). Codon also supports\n  calling any Python module via its [Python interoperability](integrations/python/python-from-codon.md).\n  See also [*\"Differences with Python\"*](language/overview.md#differences-with-python) in the docs.\n\n- :x: *New syntax and language constructs:* We try to avoid adding new syntax, keywords or other language\n  features as much as possible. While Codon does add some new syntax in a couple places (e.g. to express\n  parallelism), we try to make it as familiar and intuitive as possible.\n"
  },
  {
    "path": "docs/integrations/cpp/codon-from-cpp.md",
    "content": "Codon can be called from C/C++ code by compiling to a\nshared library that can be linked to a C/C++ application.\n\nCodon functions can be made externally visible by annotating\nthem with `@export` decorator:\n\n``` python\n@export\ndef foo(n: int):\n    for i in range(n):\n        print(i * i)\n    return n * n\n```\n\nNote that only top-level, non-generic functions can be exported. Now we\ncan create a shared library containing `foo` (assuming source file\n`foo.codon`):\n\n``` bash\ncodon build --relocation-model=pic --lib -o libfoo.so foo.codon\n```\n\nNow we can call `foo` from a C program (if you're using C++, mark the\nCodon function as `extern \"C\"`):\n\n``` c\n#include <stdint.h>\n#include <stdio.h>\n\nint64_t foo(int64_t);\n// In C++, it would be:\n// extern \"C\" int64_t foo(int64_t);\n\nint main() {\n  printf(\"%llu\\n\", foo(10));\n}\n```\n\nCompile:\n\n``` bash\ngcc -o foo -L. -lfoo foo.c  # or g++ if using C++\n```\n\nNow running `./foo` will invoke `foo()` as defined in Codon, with an\nargument of `10`.\n\nNote that if the generated shared library is in a non-standard path, you\ncan either:\n\n- Add the `rpath` to the `gcc` command: `-Wl,-rpath=/path/to/lib/dir`\n- Add the library path to `LD_LIBRARY_PATH` (or `DYLD_LIBRARY_PATH` if\n  using macOS): `export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/lib/dir`.\n\nType conversions between Codon and C/C++ are detailed [here](cpp-from-codon.md#type-conversions).\n"
  },
  {
    "path": "docs/integrations/cpp/cpp-from-codon.md",
    "content": "C/C++ functions can be called from Codon via the `from C import`\nimport statement. Unlike standard imports, `from C import` must\nspecify the imported function's argument and return types. For\nexample:\n\n``` python\n# import the C standard library 'sqrt' function\nfrom C import sqrt(float) -> float\nprint(sqrt(2.0))  # 1.41421\n```\n\nYou can also rename C-imported functions:\n\n``` python\n# cobj is a C pointer (void*, char*, etc.)\n# None can be used to represent C's void\nfrom C import puts(cobj) -> None as print_line\nprint_line(\"hello\".c_str())  # prints \"hello\"; c_str() converts Codon str to C string\n```\n\nYou can also add [annotations](../../language/llvm.md#annotations) such as\n`@pure` to C-imported functions by instead declaring them with the `@C`\nattribute:\n\n``` python\n@C\n@pure\ndef sqrt(x: float) -> float:\n    pass\n\nprint(sqrt(2.0))  # 1.41421\n```\n\n!!! warning\n\n    If you're using C++, remember to declare any functions you want to call from\n    Codon with `extern \"C\"` to enable C linkage, which Codon expects.\n\n## Type conversions\n\nThe following table shows the conversions between Codon and C/C++ types:\n\n| Codon         | C/C++                                |\n| ------------- | ------------------------------------ |\n| `int`         | `int64_t`                            |\n| `float`       | `double`                             |\n| `bool`        | `bool`                               |\n| `complex`     | `{double, double}` (real and imag.)  |\n| `str`         | `{int64_t, char*}` (length and data) |\n| `tuple`       | Struct of fields                     |\n| `class`       | Pointer to corresponding tuple       |\n| `Ptr[T]`      | `T*`                                 |\n\n!!! warning\n\n    Use caution when returning structures from C-imported functions, as C compilers\n    might use an ABI that differs from Codon's ABI. It is recommended to instead\n    pass a pointer as an argument that the callee can populate with the return value.\n\n### Optionals\n\nCodon also has an `Optional[T]` type for representing `None` values, which\nis represented in one of two ways:\n\n- If `T` is a reference type (i.e. a type defined with `class`), then `Optional[T]`\n  is represented the same way as `T` (i.e. a pointer to dynamically-allocated member\n  data) with null representing `None`.\n\n- Otherwise, `Optional[T]` is represented as a C structure `{bool, T}` where the\n  boolean field indicates whether the value is present.\n\n### NumPy arrays\n\nNumPy array types are parameterized by the data type (`dtype`) and array dimension\n(`ndim`). They correspond to the following C structure definition:\n\n``` c\nstruct ndarray {\n  int64_t shape[ndim];\n  int64_t strides[ndim];\n  dtype *data\n};\n```\n\nRefer to the [NumPy documentation](../../libraries/numpy.md#array-abi) for an explanation of these fields.\n\n## Dynamic loading\n\nShared libraries can be loaded dynamically as follows:\n\n``` python\nLIBRARY = \"libhello.so\"\n\n# load dynamically from 'libhello.so'\nfrom C import LIBRARY.foo(int, float) -> None\nfrom C import LIBRARY.bar() -> int as baz\n\nx = foo(1, 2.2)\ny = baz()\n```\n\nDynamic C imports are implemented by calling `dlopen()` on the given shared library,\nfollowed by `dlsym()` to obtain the required functions.\n"
  },
  {
    "path": "docs/integrations/cpp/jit.md",
    "content": "Codon has a JIT that can be embedded in and called from C++ applications.\nThe Codon distribution contains all the necessary headers and shared\nlibraries for compiling and linking against the Codon compiler library,\nwhich includes the JIT.\n\n## A minimal example\n\n``` cpp\n#include <iostream>\n\n#include \"codon/compiler/jit.h\"\n\nint main(int argc, char **argv) {\n  std::string mode = \"\";\n  std::string stdlib = \"/path/to/.codon/lib/codon/stdlib\";\n  std::string code = \"sum(i**2 for i in range(10))\";\n\n  codon::jit::JIT jit(argv[0], mode, stdlib);\n  llvm::cantFail(jit.init());\n  std::cout << llvm::cantFail(jit.execute(code)) << std::endl;\n}\n```\n\nThe `codon::jit::JIT` constructor takes three arguments:\n\n- `argv[0]`: as received by `main()`\n- `mode`: currently supports either an empty string or `\"jupyter\"`, where the\n  latter adds support for the\n  [`_repr_mimebundle_`](https://ipython.readthedocs.io/en/latest/whatsnew/version5.html#define-repr-mimebundle)\n  method.\n- `stdlib`: path to Codon standard library; `~/.codon/lib/codon/stdlib` if using\n  a standard Codon installation\n\nNext, the `init()` method is used to initialize the JIT. Note that JIT methods make\nuse of [LLVM's error handling](https://llvm.org/docs/ProgrammersManual.html#error-handling),\nhence the use of [`llvm::cantFail()`](https://llvm.org/doxygen/namespacellvm.html#aa1e1474f15df639f0d874b21f15666f7).\n\nLastly, code can be executed using the `execute()` method, which captures and returns\nthe output of the provided code string.\n\nThe program can be compiled as follows:\n\n``` bash\nexport CODON_DIR=~/.codon  # or wherever Codon is installed\ng++ -std=c++20 -I${CODON_DIR}/include \\\n               -L${CODON_DIR}/lib/codon \\\n               -Wl,-rpath,${CODON_DIR}/lib/codon \\\n               -lcodonc \\\n               test.cpp\n```\n\n## JIT API\n\n`codon::jit::JIT` provides the following methods:\n\n### `init`\n\n``` cpp\nllvm::Error init(bool forgetful = false);\n```\n\nInitializes the JIT. If `forgetful` is `true`, then the JIT will not remember\nglobal variables or functions defined in previous inputs, effectively resulting\nin a fresh JIT on each input (albeit without having to perform initialization\nrepeatedly).\n\n### `compile` (code string)\n\n``` cpp\nllvm::Expected<ir::Func *> compile(const std::string &code,\n                                   const std::string &file = \"\", int line = 0);\n```\n\nCompiles the given code string to a [Codon IR](../../developers/ir.md) function representing\nthe JIT input. Optional file and line information can be passed through the `file` and\n`line` arguments, respectively. Does not invoke the LLVM backend.\n\n\n### `compile` (IR function)\n\n``` cpp\nllvm::Error compile(const ir::Func *input,\n                    llvm::orc::ResourceTrackerSP rt = nullptr);\n```\n\nCompiles the given Codon IR function in the JIT. An optional\n[`llvm::orc::ResourceTracker`](https://llvm.org/doxygen/classllvm_1_1orc_1_1ResourceTracker.html)\ncan be provided to manage the JIT-compiled code. A resource tracker can be created via\n`jit.getEngine()->getMainJITDylib().createResourceTracker()`.\n\n### `address`\n\n``` cpp\nllvm::Expected<void *> address(const ir::Func *input,\n                               llvm::orc::ResourceTrackerSP rt = nullptr);\n```\n\nReturns a pointer to the compiled function corresponding to the provided Codon IR function. As\nabove, An optional `llvm::orc::ResourceTracker` can be provided to manage the JIT-compiled code.\nThe returned pointer can be cast to the appropriate function pointer and called. For example,\nbuilding on the code above, we can do the following:\n\n``` cpp\nauto *f = llvm::cantFail(jit.compile(code));\nauto *p = llvm::cantFail(jit.address(f));\nreinterpret_cast<void (*)()>(p)();  // prints 285\n```\n\n### `execute`\n\n``` cpp\nllvm::Expected<std::string> execute(const std::string &code,\n                                    const std::string &file = \"\", int line = 0,\n                                    bool debug = false,\n                                    llvm::orc::ResourceTrackerSP rt = nullptr);\n```\n\nRuns the full compilation pipeline and executes the given code string. Optional arguments correspond\nto those described above. Returns captured output of the code string.\n\n## Controlling memory usage\n\nMemory allocated by Codon during parsing and type checking is automatically released between\nJIT invocations. Memory allocated by LLVM during compilation can be controlled via\n`llvm::orc::ResourceTracker` as described above.\n\nCodon IR provides a mechanism to release allocated IR nodes via an arena interface:\n\n``` cpp\njit.getCompiler()->getModule()->pushArena();\n// ... work with jit ...\njit.getCompiler()->getModule()->popArena();\n```\n\nOnce an arena is popped, all IR nodes allocated since the corresponding push will be deallocated.\nArenas should almost always be used in \"forgetful\" mode (i.e. when passing `forgetful=true` to the\n`init()` method), since otherwise IR nodes might need to be reused in future JIT inputs.\n"
  },
  {
    "path": "docs/integrations/jupyter.md",
    "content": "Codon ships with a kernel that can be used by Jupyter, invoked\nwith the `codon jupyter ...` subcommand.\n\nTo add the Codon kernel, add the following `kernel.json` file to\nthe directory `/path/to/jupyter/kernels/codon/`:\n\n``` json\n{\n    \"display_name\": \"Codon\",\n    \"argv\": [\n        \"/path/to/codon\",\n        \"jupyter\",\n        \"{connection_file}\"\n    ],\n    \"language\": \"python\"\n}\n```\n\nPlugins can also optionally be specified, as in:\n\n``` json\n{\n    \"display_name\": \"Codon\",\n    \"argv\": [\n        \"/path/to/codon\",\n        \"jupyter\",\n        \"-plugin\", \"/path/to/plugin\",\n        \"{connection_file}\"\n    ],\n    \"language\": \"python\"\n}\n```\n"
  },
  {
    "path": "docs/integrations/python/codon-from-python.md",
    "content": "Codon can be used within larger Python projects or codebases\nthrough its just-in-time (JIT) compilation mode. To use Codon's\nPython JIT compiler, install the `codon-jit` Python package:\n\n```bash\npip install codon-jit\n```\n\nThis library will attempt to use an installed version of Codon.\nIf Codon is installed at a non-standard path, set the `CODON_DIR`\nenvironment variable to the installation path.\n\n## Using `@codon.jit`\n\nThe `@codon.jit` decorator causes the annotated function to be\ncompiled by Codon, and automatically converts standard Python\nobjects to native Codon objects. For example:\n\n```python\nimport codon\nfrom time import time\n\ndef is_prime_python(n):\n    if n <= 1:\n        return False\n    for i in range(2, n):\n        if n % i == 0:\n            return False\n    return True\n\n@codon.jit\ndef is_prime_codon(n):\n    if n <= 1:\n        return False\n    for i in range(2, n):\n        if n % i == 0:\n            return False\n    return True\n\nt0 = time()\nans = sum(1 for i in range(100000, 200000) if is_prime_python(i))\nt1 = time()\nprint(f'[python] {ans} | took {t1 - t0} seconds')\n\nt0 = time()\nans = sum(1 for i in range(100000, 200000) if is_prime_codon(i))\nt1 = time()\nprint(f'[codon]  {ans} | took {t1 - t0} seconds')\n```\n\noutputs:\n\n```\n[python] 8392 | took 39.6610209941864 seconds\n[codon]  8392 | took 0.998633861541748 seconds\n```\n\n!!! info\n\n    `@par` (to parallelize `for`-loops) can be used in annotated functions\n    via a leading underscore: `_@par`.\n\n\n!!! warning\n\n    In general, changes made to objects in a JIT'd function will not be\n    reflected in the host Python application, since objects passed to Codon\n    are *converted* to Codon-native types. If objects need to be modified,\n    consider returning any necessary values and performing modifications\n    in Python. The one notable exception to this rule is NumPy arrays, which\n    are not copied but instead have their data pointer passed directly to\n    Codon, meaning array modifications *are* visible in the host Python\n    application.\n\n## Type conversions\n\n`@codon.jit` will attempt to convert any Python types that it can\nto native Codon types. The current conversion rules are as follows:\n\n- Basic types like `int`, `float`, `bool`, `str` and `complex` are\n  converted to the same type in Codon.\n\n- Tuples are converted to Codon tuples (which are then compiled\n  down to the equivalent of C structs).\n\n- Collection types like `list`, `dict` and `set` are converted to\n  the corresponding Codon collection type, with the restriction\n  that all elements in the collection must have the same type.\n\n- Other types are passed to Codon directly as Python objects.\n  Codon will then use its Python object API (\"`pyobj`\") to handle\n  and operate on these objects. Internally, this consists of calling\n  the appropriate CPython C API functions, e.g. `PyNumber_Add(a, b)`\n  for `a + b`.\n\n### Custom types\n\nUser-defined classes can be converted to Codon classes via `@codon.convert`:\n\n```python\nimport codon\n\n@codon.convert\nclass Foo:\n    __slots__ = 'a', 'b', 'c'\n\n    def __init__(self, n):\n        self.a = n\n        self.b = n**2\n        self.c = n**3\n\n    @codon.jit\n    def total(self):\n        return self.a + self.b + self.c\n\nprint(Foo(10).total())  # 1110\n```\n\n`@codon.convert` requires the annotated class to specify `__slots__`, which\nit uses to construct a generic Codon class (specifically, a named tuple) to\nstore the class's converted fields.\n\n## Passing globals to Codon\n\nGlobal variables, functions or modules can be passed to JIT'd functions through\nthe `pyvars` argument to `@codon.jit`:\n\n``` python\nimport codon\n\ndef foo(n):\n    print(f'n is {n}')\n\n@codon.jit(pyvars=['foo'])\ndef bar(n):\n    foo(n)  # calls the Python function 'foo'\n    return n ** 2\n\nprint(bar(9))  # 'n is 9' then '81'\n```\n\nThis also allows imported Python modules to be accessed by Codon. All `pyvars`\nare passed as Python objects. Note that JIT'd functions can call each other\nby default.\n\n!!! info\n\n    `pyvars` takes in variable names as strings, not the variables themselves.\n\n## Debugging\n\n`@codon.jit` takes an optional `debug` parameter that can be used to print debug\ninformation such as generated Codon functions and data types:\n\n``` python\nimport codon\n\n@codon.jit(debug=True)\ndef sum_of_squares(v):\n    return sum(i**2 for i in v)\n\nprint(sum_of_squares([1.4, 2.9, 3.14]))\n```\n\noutputs:\n\n```\n[codon::jit::execute] code:\ndef sum_of_squares(v):\n    return sum(i**2 for i in v)\n-----\n[python] sum_of_squares(['List[float]'])\n[codon::jit::executePython] wrapper:\n@export\ndef __codon_wrapped__sum_of_squares_0(args: cobj) -> cobj:\n    a0 = List[float].__from_py__(PyTuple_GetItem(args, 0))\n    return sum_of_squares(a0).__to_py__()\n-----\n20.229599999999998\n```\n\n## Internals and performance tips\n\nUnder the hood, the `codon` module maintains an instance of the Codon JIT,\nwhich it uses to dynamically compile annotated Python functions. These functions\nare then wrapped in *another* generated function that performs the type conversions.\nThe JIT maintains a cache of native function pointers corresponding to annotated\nPython functions with concrete input types. Hence, calling a JIT'd function\nmultiple times does not repeatedly invoke the entire Codon compiler pipeline,\nbut instead reuses the cached function pointer.\n\nAlthough object conversions from Python to Codon are generally cheap, they do\nimpose a small overhead, meaning **`@codon.jit` will work best on expensive and/or\nlong-running operations** rather than short-lived operations. By the same token,\n**the more work can be done in Codon, the better,** as opposed to repeatedly\ntransferring back and forth.\n"
  },
  {
    "path": "docs/integrations/python/extensions.md",
    "content": "Codon includes a build mode called `pyext` for generating\n[Python extensions](https://docs.python.org/3/extending/extending.html)\n(which are traditionally written in C, C++ or Cython):\n\n``` bash\ncodon build -pyext extension.codon  # add -release to enable optimizations\n```\n\n`codon build -pyext` accepts the following options:\n\n- `-o <output object>`: Writes the compilation result to the specified file.\n- `-module <module name>`: Specifies the generated Python module's name.\n\n!!! warning\n\n    It is recommended to use the `pyext` build mode with Python versions 3.9\n    and up.\n\n\n## Functions\n\nExtension functions written in Codon should generally be fully typed:\n\n``` python\ndef foo(a: int, b: float, c: str):  # return type will be deduced\n    return a * b + float(c)\n```\n\nThe `pyext` build mode will automatically generate all the necessary wrappers\nand hooks for converting a function written in Codon into a function that's\ncallable from Python.\n\nFunction arguments that are not explicitly typed will be treated as generic\nPython objects, and operated on through the CPython API.\n\nFunction overloads are also possible in Codon:\n\n``` python\ndef bar(x: int):\n    return x + 2\n\n@overload\ndef bar(x: str):\n    return x * 2\n```\n\nThis will result in a single Python function `bar()` that dispatches to the\ncorrect Codon `bar()` at runtime based on the argument's type (or raises a\n`TypeError` on an invalid input type).\n\n## Types\n\nCodon class definitions can also be converted to Python extension types via\nthe `@dataclass(python=True)` decorator:\n\n``` python\n@dataclass(python=True)\nclass Vec:\n    x: float\n    y: float\n\n    def __init__(self, x: float = 0.0, y: float = 0.0):\n        self.x = x\n        self.y = y\n\n    def __add__(self, other: Vec):\n        return Vec(self.x + other.x, self.y + other.y)\n\n    def __add__(self, other: float):\n        return Vec(self.x + other, self.y + other)\n\n    def __repr__(self):\n        return f'Vec({self.x}, {self.y})'\n```\n\nNow in Python (assuming we compile to a module `vec`):\n\n``` python\nfrom vec import Vec\n\na = Vec(x=3.0, y=4.0)  # Vec(3.0, 4.0)\nb = a + Vec(1, 2)      # Vec(4.0, 6.0)\nc = b + 10.0           # Vec(14.0, 16.0)\n```\n\n## Building with `setuptools`\n\nCodon's `pyext` build mode can be used with `setuptools`. Here is an example `setup.py` script:\n\n??? example \"Example `setup.py`\"\n\n    ``` python\n    # setup.py\n    import os\n    import sys\n    import shutil\n    from pathlib import Path\n    from setuptools import setup, Extension\n    from setuptools.command.build_ext import build_ext\n\n    # Find Codon\n    codon_path = os.environ.get('CODON_DIR')\n    if not codon_path:\n        c = shutil.which('codon')\n        if c:\n            codon_path = Path(c).parent / '..'\n    else:\n        codon_path = Path(codon_path)\n    for path in [\n        os.path.expanduser('~') + '/.codon',\n        os.getcwd() + '/..',\n    ]:\n        path = Path(path)\n        if not codon_path and path.exists():\n            codon_path = path\n            break\n\n    if (\n        not codon_path\n        or not (codon_path / 'include' / 'codon').exists()\n        or not (codon_path / 'lib' / 'codon').exists()\n    ):\n        print(\n            'Cannot find Codon.',\n            'Please either install Codon (https://github.com/exaloop/codon),',\n            'or set CODON_DIR if Codon is not in PATH.',\n            file=sys.stderr,\n        )\n        sys.exit(1)\n    codon_path = codon_path.resolve()\n    print('Found Codon:', str(codon_path))\n\n    # Build with Codon\n    class CodonExtension(Extension):\n        def __init__(self, name, source):\n            self.source = source\n            super().__init__(name, sources=[], language='c')\n\n    class BuildCodonExt(build_ext):\n        def build_extensions(self):\n            pass\n\n        def run(self):\n            inplace, self.inplace = self.inplace, False\n            super().run()\n            for ext in self.extensions:\n                self.build_codon(ext)\n            if inplace:\n                self.copy_extensions_to_source()\n\n        def build_codon(self, ext):\n            extension_path = Path(self.get_ext_fullpath(ext.name))\n            build_dir = Path(self.build_temp)\n            os.makedirs(build_dir, exist_ok=True)\n            os.makedirs(extension_path.parent.absolute(), exist_ok=True)\n\n            codon_cmd = str(codon_path / 'bin' / 'codon')\n            optimization = '-debug' if self.debug else '-release'\n            self.spawn([codon_cmd, 'build', optimization, '--relocation-model=pic', '-pyext',\n                        '-o', str(extension_path) + \".o\", '-module', ext.name, ext.source])\n\n            ext.runtime_library_dirs = [str(codon_path / 'lib' / 'codon')]\n            self.compiler.link_shared_object(\n                [str(extension_path) + '.o'],\n                str(extension_path),\n                libraries=['codonrt'],\n                library_dirs=ext.runtime_library_dirs,\n                runtime_library_dirs=ext.runtime_library_dirs,\n                extra_preargs=['-Wl,-rpath,@loader_path'],\n                debug=self.debug,\n                build_temp=self.build_temp,\n            )\n            self.distribution.codon_lib = extension_path\n\n    setup(\n        name='mymodule',\n        version='0.1',\n        packages=['mymodule'],\n        ext_modules=[\n            CodonExtension('mymodule', 'mymodule.codon'),\n        ],\n        cmdclass={'build_ext': BuildCodonExt}\n    )\n    ```\n\nThen, we can build with:\n\n``` bash\npython3 setup.py build_ext --inplace\n```\n\nFinally, we can `import mymodule` in Python and use the module.\n"
  },
  {
    "path": "docs/integrations/python/python-from-codon.md",
    "content": "There are two ways to call Python from Codon:\n\n- `from python import` allows importing and calling Python functions\n  from existing Python modules.\n- `@python` allows writing Python code directly in Codon.\n\nIn order to use these features, the `CODON_PYTHON` environment variable\nmust be set to the appropriate Python shared library:\n\n``` bash\nexport CODON_PYTHON=/path/to/libpython.X.Y.so\n```\n\nFor example, with a Homebrew-installed Python 3.9 on macOS, this might be\n\n```\n/usr/local/opt/python@3.9/Frameworks/Python.framework/Versions/3.9/lib/libpython3.9.dylib\n```\n\nNote that only Python versions 3.6 and later are supported.\n\n!!! tip \"Tip: Finding `libpython`\"\n\n    You can use\n    [this](https://raw.githubusercontent.com/exaloop/codon/refs/heads/develop/test/python/find-python-library.py)\n    script to locate the Python shared library for the `CODON_PYTHON` environment variable. Simply run it as\n    `python3 find-python-library.py` and it will print the library path.\n\n!!! info \"Info: Using virtual environments (`venv`)\"\n\n    If you are using a virtual environment created with `venv`, set `PYTHON_PATH` to the `site-packages`\n    directory inside your virtual environment (e.g. `.venv/lib/python3.11/site-packages`).\n\n!!! info \"Info: Using virtual environments (`uv`)\"\n\n    If you using a virtual environment created with `uv`, you can use the following steps to set up the necessary\n    environment variables. From the parent directory of your python project (where `pyproject.toml` is):\n\n    1. Run `uv python find --system` and set `PYTHON_HOME` to the result.\n    2. Set `CODON_PYTHON` to the `libpython.dylib` (or `.so`) file found in the folder from the previous\n      step (e.g. `lib/python3.11.dylib`) as an absolute path.\n    3. Set `PYTHON_PATH` to the `site-packages` folder inside your virtual environment (e.g `.venv/lib/python3.11/site-packages`).\n\n## Import Python modules in Codon\n\nPython modules can be imported and used in Codon-compiled programs through\na `from python import <module>` import statement. For example:\n\n``` python\nfrom python import sys  # imports Python's 'sys' module\nprint(sys.version)  # 3.11.12 (main, Apr  8 2025, 14:15:29) [Clang 17.0.0 (clang-1700.0.13.3)]\n```\n\nYou can also import third-party libraries. Here is an example that imports\nMatplotlib to create a simple plot:\n\n``` python\nfrom python import matplotlib.pyplot as plt\n\nx = [1, 2, 3, 4, 5]\ny = [2, 5, 3, 6, 4]\n\nfig, ax = plt.subplots()\nax.plot(x, y)\nplt.show()\n```\n\nObjects created from imported Python modules can be manipulated and operated on\nfrom Codon. Internally, such operations are implemented by using CPython's C API.\nFor example, we can create a Pandas dataframe in Codon, and perform operations\non it:\n\n``` python\nfrom python import pandas as pd\ndf = pd.DataFrame({'A': [1, 2, 3], 'B': [4, 5, 6]})  # internally a Python object\nprint(df['B'].max())  # 6\n```\n\n## Run Python code directly in Codon\n\nIt is also possible to annotate functions with the `@python` decorator in order\nto have them execute in Python, instead of being compiled by Codon:\n\n``` python\n@python\ndef version():\n\t# the following runs in plain Python\n    import sys\n    print(sys.version)\n\nversion()  # 3.11.12 (main, Apr  8 2025, 14:15:29) [Clang 17.0.0 (clang-1700.0.13.3)]\n```\n\n`@python` functions can specify return types, in which case returned values will\nbe checked and converted to native Codon types:\n\n``` python\n@python\ndef foo():\n    return 2 + 2\n\n@python\ndef bar() -> int:\n    return 2 + 2\n\n@python\ndef baz() -> int:\n    return 'abc'\n\n\nprint(foo())  # 4 (Python object)\nprint(bar())  # 4 (native Codon int)\nprint(baz())  # error: Python object did not have type 'int'\n```\n\nSimilarly, arguments can be type-annotated as well:\n\n``` python\n@python\ndef square(n: int) -> int:\n    return n * n\n\nprint(square(4))  # 16\n```\n\n## Data conversions\n\nCodon uses two new magic methods to transfer data to and from Python:\n\n- `__to_py__`: Produces a Python object (`PyObject*` in C) given a Codon object.\n- `__from_py__`: Produces a Codon object given a Python object.\n\nFor example:\n\n``` python\nimport python  # needed to initialize the Python runtime\n\no = (42).__to_py__()  # type of 'o' is 'Ptr', equivalent to a pointer in C\nprint(o)  # 0x100e00610\n\nn = int.__from_py__(o)  # converts Python object 'o' to native Codon integer\nprint(n)  # 42\n```\n\nCodon stores the results of `__to_py__` calls by wrapping them in an instance of a new\nclass called `pyobj`, which correctly handles the underlying Python object's reference\ncount. All operations on `pyobj`s then go through CPython's API.\n"
  },
  {
    "path": "docs/js/mathjax.js",
    "content": "window.MathJax = {\n  tex: {\n    inlineMath: [[\"\\\\(\", \"\\\\)\"]],\n    displayMath: [[\"\\\\[\", \"\\\\]\"]],\n    processEscapes: true,\n    processEnvironments: true\n  },\n  options: {\n    ignoreHtmlClass: \".*|\",\n    processHtmlClass: \"arithmatex\"\n  }\n};\n\ndocument$.subscribe(() => { \n  MathJax.startup.output.clearCache()\n  MathJax.typesetClear()\n  MathJax.texReset()\n  MathJax.typesetPromise()\n})\n"
  },
  {
    "path": "docs/labs/catalog/start.md",
    "content": "# 🧪 Getting started with Codon\n\n<div style=\"padding:56.25% 0 0 0;position:relative;\"><iframe src=\"https://player.vimeo.com/video/1104825525?badge=0&amp;autopause=0&amp;player_id=0&amp;app_id=58479\" frameborder=\"0\" allow=\"autoplay; fullscreen; picture-in-picture; clipboard-write; encrypted-media; web-share\" referrerpolicy=\"strict-origin-when-cross-origin\" style=\"position:absolute;top:0;left:0;width:100%;height:100%;\" title=\"Getting Started With Codon\"></iframe></div><script src=\"https://player.vimeo.com/api/player.js\"></script>\n\n## Install Codon\n\nRun the following shell command in your terminal to install Codon:\n\n``` bash\n/bin/bash -c \"$(curl -fsSL https://exaloop.io/install.sh)\"\n```\n\n## Run your first program\n\nSave the following code to file `fib.py`:\n\n``` python\nfrom time import time\n\ndef fib(n):\n    return n if n < 2 else fib(n - 1) + fib(n - 2)\n\nt0 = time()\nans = fib(40)\nt1 = time()\nprint(f'Computed fib(40) = {ans} in {t1 - t0} seconds.')\n```\n\nRun the program in Codon:\n\n``` bash\ncodon run -release fib.py\n```\n\nCompile to an executable:\n\n``` bash\ncodon build -release -o fib fib.py\n```\n\nRun the executable via `./fib`.\n\n## Use Codon's \"just-in-time\" (JIT) compiler\n\nInstall Codon's JIT with `pip`:\n\n``` bash\npip install codon-jit\n```\n\nSave the following code to file `primes.py`:\n\n``` python\nimport codon\nfrom time import time\n\ndef is_prime_python(n):\n    if n <= 1:\n        return False\n    for i in range(2, n):\n        if n % i == 0:\n            return False\n    return True\n\n@codon.jit\ndef is_prime_codon(n):\n    if n <= 1:\n        return False\n    for i in range(2, n):\n        if n % i == 0:\n            return False\n    return True\n\nt0 = time()\nans = sum(1 for i in range(100000, 200000) if is_prime_python(i))\nt1 = time()\nprint(f'[python] {ans} | took {t1 - t0} seconds')\n\nt0 = time()\nans = sum(1 for i in range(100000, 200000) if is_prime_codon(i))\nt1 = time()\nprint(f'[codon]  {ans} | took {t1 - t0} seconds')\n```\n\nRun with Python:\n\n``` python\npython3 primes.py\n```\n"
  },
  {
    "path": "docs/labs/index.md",
    "content": "---\ntitle: Labs\n---\n\n<h1 align=\"center\">\n  <img src=\"../img/codon-labs-dark.svg\" class=\"labs-logo-dark\" width=\"70%\" />\n  <img src=\"../img/codon-labs-light.svg\" class=\"labs-logo-light\" width=\"70%\" />\n</h1>\n\nLabs are short, self-contained guides on various features and applications\nof Codon. Each lab includes a written guide as well as a video guide, and\nlabs cover a variety of topics ranging from introductory to advanced use\ncases and domain-specific applications. Check out the catalog of labs below!\n\n!!! example \"Stay tuned\"\n\n    **More labs are coming soon!**\n\n## Explore labs\n\n<div class=\"lab-grid\">\n\n  <div class=\"lab-wrap\">\n    <a class=\"lab\" id=\"lab-start\" href=\"catalog/start\">\n      <h3>🧪 Getting Started with Codon</h3>\n      <div class=\"lab-tags\">\n        <span class=\"lab-tag-new\">✨ New</span>\n        <span class=\"lab-tag-difficulty\">🚀 Intro</span>\n        <span class=\"lab-tag-time\">⌛ 2 mins</span>\n      </div>\n      <hr />\n      <p>Learn how to install Codon and run your first program.</p>\n    </a>\n    <div class=\"lab-img\">\n      <img src=\"../img/card-labs-light.svg\" class=\"lab-icon-dark\" width=\"120\" />\n    </div>\n  </div>\n\n</div>\n\n## Contribute a lab\n\nDo you have an interesting use case or application of Codon that others would\nfind helpful to learn about? If so, consider creating a lab that we can include\nhere! Please reach out to us [on Discord](https://discord.gg/HeWRhagCmP) or\n[on email](mailto:info@exaloop.io) if you are considering\ncreating a lab so we can work together on the content and format!\n"
  },
  {
    "path": "docs/language/classes.md",
    "content": "Codon supports Python classes as you would expect. For example:\n\n``` python\nclass Point:\n    def __init__(self, x, y):\n        self.x = x\n        self.y = y\n\n    def __str__(self):\n        return f'({self.x}, {self.y})'\n\np = Point(3, 4)\nprint(p)  # (3, 4)\n```\n\nCodon will automatically infer class fields if none are\nspecified explicitly. Alternatively, the class fields can\nbe specified in the class body:\n\n``` python\nclass Point:\n    x: int\n    y: int\n\n    def __init__(self, x, y):\n        self.x = x\n        self.y = y\n\n    def __str__(self):\n        return f'({self.x}, {self.y})'\n```\n\nClass fields can reference the enclosing class through\nthe `Optional` type:\n\n``` python\nclass Point:\n    x: int\n    y: int\n    other: Optional[Point]\n\n    def __init__(self, x, y, other: Optional[Point] = None):\n        self.x = x\n        self.y = y\n        self.other = other\n\n    def __str__(self):\n        if self.other is None:\n            return f'({self.x}, {self.y})'\n        else:\n            return f'({self.x}, {self.y}) -> {str(self.other)}'\n\np = Point(3, 4)\nprint(p)  # (3, 4)\n\nq = Point(5, 6, p)\nprint(q)  # (5, 6) -> (3, 4)\n```\n\n## Overloading methods\n\nIn Python, class methods can be defined to take arguments of arbitrary types,\nand to reason about them through functions like `isinstance()`. While the same\nworks in Codon, Codon also offers another way to separate out method logic\nfor different input types: *method overloading*.\n\nMultiple methods with the same name but different arguments or argument types\ncan be defined in the same class. Codon will use the method corresponding to\nthe argument types provided in a given call of that method. For example:\n\n``` python\nclass Point:\n    ...\n\n    def foo(self, n: int):\n        print('int-foo called!', n)\n\n    def foo(self, s: str):\n        print('str-foo called!', s)\n\np = Point(3, 4)\np.foo(42)     # int-foo called! 42\np.foo('abc')  # str-foo called! abc\n```\n\nMethod resolution occurs *bottom-up*, meaning if multiple methods are applicable\nfor a given set of arguments, the latest one will be used.\n\n!!! note\n\n    Non-method functions can also be overloaded in Codon by adding the `@overload`\n    decorator, which prevents latter definitions from shadowing previous ones.\n\n## Tuple classes\n\nRegular classes are mutable and passed around by reference. Internally,\nclass data is dynamically allocated and a pointer to the allocated data\nis used to represent the class instance.\n\nCodon supports an alternative type of class that is immutable and avoids\nheap allocation: *tuple classes*. A tuple class is defined via the `@tuple`\nclass annotation. For example, we can rewrite the `Point` class above as\na tuple class:\n\n``` python\n@tuple\nclass Point:\n    x: int\n    y: int\n\n    def __str__(self):\n        return f'({self.x}, {self.y})'\n```\n\nBecause tuple class instances are immutable, tuple classes do not use\nthe usual `__init__` method, and can instead define new constructors via\nthe `__new__` method. A default `__new__` which takes all of the tuple\nclass's fields as arguments is automatically generated. We can define\nadditional `__new__` methods as follows, for instance:\n\n``` python\n@tuple\nclass Point:\n    x: int\n    y: int\n\n    # constructor (A)\n    def __new__():\n        return Point(0, 0)\n\n    # constructor (B)\n    def __new__(x: int):\n        return Point(x, 0)\n\n    def __str__(self):\n        return f'({self.x}, {self.y})'\n\nzero = Point()  # calls constructor (A)\none = Point(1)  # calls constructor (B)\n\nprint(zero)  # (0, 0)\nprint(one)   # (1, 0)\n\nzero.x = 1  # error: cannot modify tuple attributes\n```\n\nTuple classes can be more efficient than standard classes, particularly\nwhen storing many instances in an array or list.\n\nInternally, tuple classes correspond to C `struct`s. For example, the `Point`\ntuple class above would correspond exactly to the following `struct` definition\nin C:\n\n``` c\nstruct Point {\n  int64_t x;\n  int64_t y;\n};\n```\n\nAs a result, tuple classes can also be used when interoperating with a C API,\nas they can mirror API-specific data structures or layouts.\n\n## Inheritance\n\nCodon supports Python's inheritance and dynamic polymorphism. For example:\n\n``` python\nclass Shape:\n\n    def area(self):\n        return 0.0\n\n    def describe(self):\n        return \"This is a shape.\"\n\nclass Circle(Shape):\n    radius: float\n\n    def __init__(self, radius):\n        self.radius = radius\n\n    def area(self):\n        return 3.1416 * self.radius**2\n\n    def describe(self):\n        return f\"A circle with radius {self.radius}\"\n\nclass Rectangle(Shape):\n    width: float\n    height: float\n\n    def __init__(self, width, height):\n        self.width = width\n        self.height = height\n\n    def area(self):\n        return self.width * self.height\n\n    def describe(self):\n        return f\"A rectangle with width {self.width} and height {self.height}\"\n\nclass Square(Rectangle):\n\n    def __init__(self, width):\n        super().__init__(width, width)\n\n    def describe(self):\n        return super().describe().replace('rectangle', 'square')\n\n\nshapes: list[Shape] = []\nshapes.append(Circle(5))\nshapes.append(Rectangle(4, 6))\nshapes.append(Square(3))\n\nfor shape in shapes:\n    print(shape.describe(), f'(area={shape.area()})')\n```\n\n!!! warning\n\n    Tuple classes cannot be subclassed using standard inheritance. However, they can\n    be subclassed via static inheritance, as described below.\n\nIn the code above, the methods `area()` and `describe()` are overriden by the subclasses\nof `Square`. Codon follows Python's semantics and method resolution order.\n\n### Static inheritance\n\nIn addition to Python's dynamic inheritance, Codon supports static inheritance\n(or *early binding*), which can be expressed via the special `Static` type:\n\n``` python\nclass Foo:\n    x: int\n\n    def __init__(self, x: int):\n        self.x = x\n\n    def hello(self):\n        print('Foo')\n\nclass Bar(Static[Foo]):\n\n    def hello(self):\n        print('Bar')\n\nfoo = Foo(1)\nbar = Bar(2)\n\nprint(foo.x, bar.x)  # 1 2\nfoo.hello()          # Foo\nbar.hello()          # Bar\n```\n\nThe `hello()` method calls are resolved at compile time instead of at runtime, as would be the\ncase with standard, dynamic inheritance. Static inheritance is useful when you want to reuse a\nparticular class's functionality without paying the cost of\n[dynamic dispatch](https://en.wikipedia.org/wiki/Dynamic_dispatch) that is incurred with dynamic\ninheritance.\n\nStatic inheritance also works on tuple classes:\n\n``` python\n@tuple\nclass Foo:\n    x: int\n\n    def hello(self):\n        print('Foo')\n\n@tuple\nclass Bar(Static[Foo]):\n\n    def hello(self):\n        print('Bar')\n\nfoo = Foo(1)\nbar = Bar(2)\n\nprint(foo.x, bar.x)  # 1 2\nfoo.hello()          # Foo\nbar.hello()          # Bar\n```\n"
  },
  {
    "path": "docs/language/generics.md",
    "content": "Codon's type system is designed to be non-intrusive, meaning\nit will infer types as much as possible without manual type\nannotations. This way, Python programs typically \"just work\"\nwithout requiring code changes.\n\nOccasionally, it can be helpful to use Codon's type system to\nexpress more intricate type relationships. This can be achieved\nthrough *generics*.\n\n## Generic functions\n\nImagine we want to enforce that a particular function should\nonly accept a list argument; we can achieve that using\ngeneric types:\n\n``` python\ndef foo[T](x: list[T]):  # 'T' is a generic type parameter\n    print(max(x))\n\nfoo([20, 42, 12])        # 42\nfoo(['hello', 'world'])  # world\nfoo(100)                 # error: 'int' does not match expected type 'List[T]'\n```\n\n!!! info\n\n    This syntax is supported by Python 3.12 and up. See [PEP 695](https://peps.python.org/pep-0695/).\n\nIn this code, `T` is a *generic type parameter* that gets realized\nbased on the argument type:\n\n- `foo([20, 42, 12])` &#8594; the argument is a list of integers,\n  so `T` is realized as `int`.\n- `foo(['hello', 'world'])` &#8594; the argument is a list of strings,\n  so `T` is realized as `str`.\n- `foo(100)` &#8594; the argument is not a list at all, so a type checking\n  error occurs.\n\nAlternatively, Codon allows the generic type parameter to be specified\nas an argument:\n\n``` python\ndef foo(x: list[T], T: type):\n    print(max(x))\n\nfoo([20, 42, 12], int)    # can specify 'T' explicitly...\nfoo([20, 42, 12], T=int)  # ... or by name\nfoo([20, 42, 12])         # still works; 'T' inferred\n```\n\nThis syntax is useful for allowing generic type parameters to be specified\nexplicitly as arguments when the function is called, or for providing default\nvalues for them:\n\n``` python\ndef bar(x: list[T], T: type = int):\n    print(type(x))\n\nbar([])  # <class 'List[int]'>\n```\n\n## Generic classes\n\nClasses can also be generic:\n\n``` python\nclass A[T]:  # 'T' is a generic type parameter\n    x: T\n\n    def __init__(self, x: T):\n        self.x = x\n\n    def __repr__(self):\n        return f'A({self.x})'\n\nx = A(42)            # 'T' inferred\ny = A[str]('hello')  # 'T' explicitly given as 'str'\n\nprint(x)  # A(42)\nprint(y)  # A(hello)\n```\n\nGeneric type parameters can also be listed after the class fields:\n\n``` python\nclass A:  # identical to above definition\n    x: T\n    T: type\n\n    def __init__(self, x: T):\n        self.x = x\n```\n"
  },
  {
    "path": "docs/language/llvm.md",
    "content": "Codon supports inline [LLVM IR](https://llvm.org/docs/LangRef.html) via\nthe `@llvm` annotation:\n\n``` python\n@llvm\ndef llvm_add(a: int, b: int) -> int:\n    %res = add i64 %a, %b\n    ret i64 %res\n\nprint(llvm_add(3, 4))  # 7\n```\n\nNote that LLVM functions must explicitly specify argument\nand return types.\n\nLLVM functions can also be generic, and a format specifier\nin the body will be replaced by the appropriate LLVM type:\n\n``` python\n@llvm\ndef llvm_add[T](a: T, b: T) -> T:\n    %res = add {=T} %a, %b\n    ret {=T} %res\n\nprint(llvm_add(3, 4))          # 7\nprint(llvm_add(i8(5), i8(6)))  # 11\n```\n\nLLVM intrinsics can be accessed with `declare`:\n\n``` python\n@llvm\ndef popcnt(n: int) -> int:\n    declare i64 @llvm.ctpop.i64(i64)\n    %0 = call i64 @llvm.ctpop.i64(i64 %n)\n    ret i64 %0\n\nprint(popcnt(42))  # 3\n```\n\n## Annotations\n\nSometimes it can be helpful to annotate `@llvm` functions to give\nthe compiler more information as to how they behave. Codon has\na number of default annotations for LLVM functions (all of\nwhich also apply to external/C functions):\n\n- `@pure`: Function does not capture arguments (aside from\n  return value capturing as in `def foo(x): return x`), does not\n  modify arguments, and has no side effects. This is a\n  mathematically \"pure\" function.\n\n- `@no_side_effect`: Very similar to `@pure` but function may\n  return different results on different calls, such as the C\n  function `time()`.\n\n- `@nocapture`: Function does not capture any of its arguments\n  (again excluding return value capturing).\n\n- `@self_captures`: Function's first (`self`) argument captures\n  the other arguments, an example being `List.__setitem__()`.\n\nThese are mutually-exclusive annotations. Another complementary\nannotation can be used to indicate that the return\nvalue of the function captures its arguments:\n\n- `@derives`: Return value of the function captures its arguments.\n\nThese annotations are optional and do not affect program semantics.\n"
  },
  {
    "path": "docs/language/lowlevel.md",
    "content": "Codon provides various low-level programming features that can\nbe used in performance-sensitive settings or when interfacing\nwith external APIs.\n\n## Integer types\n\nWhile Codon's standard `int` type is a 64-bit signed integer,\ndifferent integer types are made available through the `Int`\nand `UInt` types:\n\n- `Int[N]`: Signed integer with `N` bits\n- `UInt[N]`: Unsigned integer with `N` bits\n\nFor example:\n\n``` python\na = Int[16](42)    # signed 16-bit integer 42\nb = UInt[128](99)  # unsigned 128-bit integer 99\n```\n\nThe Codon standard library provides shorthands for the common variants:\n\n- `i8`/`u8`: signed/unsigned 8-bit integer\n- `i16`/`u16`: signed/unsigned 16-bit integer\n- `i32`/`u32`: signed/unsigned 32-bit integer\n- `i64`/`u64`: signed/unsigned 64-bit integer\n\nYou can cast between different integer types freely:\n\n``` python\na = 42\nb = i16(a)\nc = u32(b)\n```\n\nSimilarly, you can perform arithmetic operations on integers of the\nsame types:\n\n``` python\nx = i32(10)\ny = i32(20)\nprint(x + y)  # 30\n```\n\n## Floating-point types\n\nCodon's standard `float` type represents a 64-bit floating-point value\n(IEEE 754 `binary64`). Codon supports several alternative floating-point\ntypes:\n\n- `float32`: 32-bit floating-point value (IEEE 754 `binary32`)\n- `float16`: 16-bit floating-point value (IEEE 754 `binary16`)\n- `float128`: 128-bit floating-point value (IEEE 754 `binary128`)\n- `bfloat16`: 16-bit \"brain\" floating-point value (7-bit significand).\n  Provides the same number of exponent bits as float, so that it matches\n  its dynamic range, but with greatly reduced precision.\n\nEach of these float types can be constructed from a standard `float`:\n\n``` python\nx = float32(3.14)\ny = float128(-1.0)\nz = bfloat16(0.5)\n```\n\nThey all also support the usual arithmetic operators:\n\n``` python\nx = bfloat16(2) ** bfloat16(0.5)\nprint(x)  # 1.41406\n```\n\n## Pointers\n\nCodon supports raw pointers natively via the `Ptr` type, which is\nparameterized by the type of the object being pointed to (i.e.\n`Ptr[int]` is an `int` pointer, `Ptr[float]` is a `float` pointer,\nand so on).\n\n!!! danger\n\n    Pointer operations are not bounds-checked, meaning dereferencing\n    an invalid pointer can cause a segmentation fault.\n\nBuffers of a specific type can be dynamically allocated by constructing\na `Ptr` type with an integer argument, representing the number of elements\nof the given type to allocate:\n\n``` python\nbuffer = Ptr[int](10)  # equivalent to 'malloc(10 * sizeof(int64_t))' in C\n```\n\nPointers can be dereferenced and indexed:\n\n``` python\nbuffer = Ptr[int](10)\nbuffer[0] = 10  # equivalent to '(*buffer) = 10' in C\nbuffer[5] = 42  # equivalent to 'buffer[5] = 42' in C\nprint(buffer[0])  # 10\nprint(buffer[5])  # 42\n```\n\nConstructing a pointer without any arguments results in a null pointer:\n\n``` python\nnull = Ptr[int]()  # equivalent to 'NULL' in C or 'nullptr' in C++\n```\n\nYou can cast between different pointer types:\n\n``` python\nx = Ptr[float](1)\nx[0] = 3.14\n\ny = Ptr[int](x)  # treat 'x' as an integer pointer\nprint(y[0])      # 4614253070214989087 - same bits as '3.14' float\n```\n\nPointers support various arithmetic and comparison operators:\n\n``` python\np = Ptr[int](1)\nq = p + 1  # equivalent to '&p[1]' in C\n\nprint(p == q)  # False\nprint(q - p)   # 1\nprint(p < q)   # True\n```\n\n### Pointers to variables\n\nIt is possible to obtain a pointer to a variable via the `__ptr__` intrinsic\nfunction:\n\n``` python\nx = 42\np = __ptr__(x)  # 'p' is a 'Ptr[int]'; equivalent to '&x' in C\np[0] = 99\nprint(x)  # 99\n```\n\nPointers to variables can be useful when interfacing with C APIs. For example,\nconsider the C standard library function [`frexp()`](https://en.cppreference.com/w/cpp/numeric/math/frexp.html)\nwhich stores one of its outputs in an argument pointer. We can call this\nfunction from Codon as follows:\n\n``` python\nfrom C import frexp(float, Ptr[i32]) -> float\n\nx = 16.4\nexponent = i32()\nmantissa = frexp(x, __ptr__(exponent))  # equivalent to 'frexp(x, &exponent)' in C\n\nprint(mantissa, exponent)  # 0.5125 5\n```\n\nRefer to [C/C++ integration](../integrations/cpp/cpp-from-codon.md) for more information about\ncalling C/C++ functions from Codon.\n\n### Pointers to fields\n\n`__ptr__` can also be used to obtain pointers to fields of tuple classes:\n\n``` python\n@tuple\nclass Point:\n    x: int\n    y: int\n\nr = Point(3, 4)\np = __ptr__(r.y)  # 'p' is a 'Ptr[int]'; equivalent to '&r.x' in C\np[0] = 99\nprint(r.x, r.y)  # 3 99\n```\n\nRecall that tuple class instances are immutable and passed by value, so\nassignments create new instances:\n\n``` python\n@tuple\nclass Point:\n    x: int\n    y: int\n\nr = Point(3, 4)\ns = r  # creates a copy of 'r'\np = __ptr__(r.y)\np[0] = 99\n\nprint(s.x, s.y)  # 3 4 (not changed by pointer modification)\n```\n\n### Software prefetching\n\nPointers have several methods to facilitate software prefetching. These\nmethods all have the form `__prefetch_[rw][0123]__`, where `[rw]` indicates\nwhether the prefetch is made for a \"read\" (`r`) or a \"write\" (`w`), and\nthe `[0123]` is a temporal locality specifier, with higher values indicating\nmore locality (i.e. that the value should be kept in cache).\n\nFor example:\n\n``` python\np = Ptr[int](1)\np.__prefetch_w3__()  # prefetch for write, high temporal locality\n```\n\nRefer to [LLVM's prefetch intrinsic](https://llvm.org/docs/LangRef.html#llvm-prefetch-intrinsic)\nfor additional information.\n\n!!! warning\n\n    Not all targets support software prefetching. Consult the relevant documentation\n    for your instruction set architecture for more information.\n\n## Static arrays\n\nArrays can be allocated on the stack via the `__array__` intrinsic function:\n\n``` python\ndef f():\n    arr = __array__[int](10)  # array of 10 integers; equivalent to 'int64_t arr[10]' in C\n    arr[0] = 42\n    print(arr[0])  # 42\n```\n\nArrays created with `__array__` have two fields: `ptr` (pointer to array data) and `len`\n(length of array). The argument of `__array__` must be a [literal](meta.md#literals) integer.\n"
  },
  {
    "path": "docs/language/meta.md",
    "content": "## Literals\n\nCodon supports compile-time metaprogramming through the\nspecial `Literal` type. `Literal`s represent constants which\ncan be operated on and manipulated *at compile time*.\nThere are three types of `Literal`s:\n\n- `Literal[int]`: integer constant\n- `Literal[str]`: string constant\n- `Literal[bool]`: boolean constant\n\n### Integer literals\n\nInteger literals can be defined explicitly as follows:\n\n``` python\nn: Literal[int] = 42\n```\n\nLiterals must be known at compile time. For example, the following\ncode will cause a compilation error:\n\n``` python\nfrom sys import argv\nn: Literal[int] = len(argv)  # error: value is not literal!\n```\n\nArithmetic operations on integer literals result in other\ninteger literals:\n\n``` python\nn: Literal[int] = 42\nm: Literal[int] = n + 1  # valid int literal\n```\n\nConditional expressions on literals also result in other\nliterals:\n\n``` python\nn: Literal[int] = 42\nm: Literal[int] = n//2 if n%2 == 0 else 3*n + 1\n```\n\nInteger literals can be passed as function arguments, and\nalso returned from functions:\n\n``` python\ndef fib(n: Literal[int]) -> Literal[int]:\n    return 1 if n < 2 else fib(n - 1) + fib(n - 2)\n\nn: Literal[int] = fib(10)  # computed entirely at compile time!\n```\n\n### String literals\n\nMuch like integer literals, string literals represent constant\nstrings:\n\n``` python\ns: Literal[str] = 'hello'\n```\n\nWhereas integer literals can be manipulated via arithmetic operations\nto produce other integer literals, string literals can be manipulated\nvia string operations to produce new string literals:\n\n``` python\nt: Literal[str] = s[1]   # 'e'\nu: Literal[str] = s[3:]  # 'lo'\n```\n\nMuch like integer literals, string literals can similarly be used in\nliteral conditional expressions and as function argument or return\ntypes.\n\n### Boolean literals\n\nFinally, boolean literals represent constant booleans:\n\n``` python\nb: Literal[bool] = True\n```\n\nBoolean operators can be used on boolean literals to produce new\nboolean literals:\n\n``` python\nb: Literal[bool] = True\nd: Literal[bool] = not b  # False\n```\n\nSome operations on integer and string literals produce boolean literals:\n\n``` python\nn: Literal[int] = 42\ns: Literal[str] = 'hello'\n\nb: Literal[bool] = (n < 10)          # False\nd: Literal[bool] = (s[2:4] == 'll')  # True\n```\n\n## Static loops\n\nIt is also possible to express loops where the loop index is a literal\ninteger, via the `codon.static` module:\n\n``` python\nimport codon.static\n\nfor i in static.range(10):\n    m: Literal[int] = 3*i + 1\n    print(m)\n```\n\nStatic loops are unrolled at compile time, which allows the loop index\nto take on literal values.\n\nStatic loops can also be used to create tuples, the lengths of which\nmust be compile-time constants in Codon:\n\n``` python\nimport codon.static\nt = tuple(i*i for i in static.range(5))\nprint(t)  # (0, 1, 4, 9, 16)\n```\n\nYou can loop over another tuple by obtaining its length as an integer\nliteral via `static.len()`:\n\n``` python\nimport codon.static\n\nt = tuple(i*i for i in static.range(5))\nu = tuple(t[i] + 1 for i in static.range(static.len(t)))\n\nprint(u)  # (1, 2, 5, 10, 17)\n```\n\n## Static evaluation\n\nLiteral expressions can be used as conditions in `if` statements, which\nenables the compiler to eliminate branches that it knows wil not be\nentered at runtime. This can be used to avoid type checking errors, for\nexample:\n\n``` python\ndef foo(x):\n    if isinstance(x, int):\n        return x + 1\n    elif isinstance(x, str):\n        return x + '!'\n    else:\n        return x\n\nprint(foo(42))       # 43\nprint(foo('hello'))  # hello!\nprint(foo(3.14))     # 3.14\n```\n\nNormally, Codon's type checker would flag an expression like `x + 1` as\nan error if the type of `x` is `str`. However, in the code above, the\nbranches that are not applicable to the type of `x` are eliminated so as\nto allow the code to type check and compile.\n\nHere is another, more involved example:\n\n``` python\ndef flatten(x):\n    if isinstance(x, list):\n        for a in x:\n            flatten(a)\n    else:\n        print(x)\n\nflatten([[1,2,3], [], [4, 5], [6]])  # 1, 2, ..., 6\n```\n\nStandard static typing on this program would be problematic since, if `x`\nis an `int`, it would not be iterable and hence would produce an error on\n`for a in x`. Static evaluation solves this problem by evaluating\n`isinstance(x, list)` at compile time and avoiding type checking the block\ncontaining the loop when `x` is not a list.\n\nStatic evaluation works with literal expressions, `isinstance()`, `hasattr()`\nand type comparisons like `type1 is type2`.\n"
  },
  {
    "path": "docs/language/overview.md",
    "content": "Codon strives to be as close to Python (specifically, CPython) as possible,\noutside of a few differences that stem from performance considerations or\nincompatibilities with Codon's static compilation paradigm.\n\nAs a result, if you know Python, you already know 99% of Codon!\n\nCodon also introduces several new elements to Python to facilitate low-level\nprogramming, parallel programming and compile-time metaprogramming, among\nother features. These elements are described in this section.\n\n## Differences with Python\n\n!!! tip\n\n    Found something that works differently in Codon than Python which\n    isn't mentioned below? Let us know\n    [on GitHub](https://github.com/exaloop/codon/issues/new).\n\nWhile Codon's syntax and semantics are nearly identical\nto Python's, there are some notable differences that are\nworth considering. Most of these design decisions were made\nwith the trade-off between performance and Python compatibility\nin mind.\n\nPlease see our [roadmap](../developers/roadmap.md) for more information\nabout how we plan to close some of these gaps in the future.\n\n### Data types\n\n- **Integers:** Codon's `int` is a 64-bit signed integer,\n  whereas Python's (after version 3) can be arbitrarily large.\n  However Codon does support larger integers via `Int[N]` where\n  `N` is the bit width.\n\n- **Strings:** Codon currently uses ASCII strings unlike\n  Python's unicode strings. Unicode strings are planned for\n  Codon 1.0.\n\n- **Dictionaries:** Codon's dictionary type does not preserve\n  insertion order, unlike Python's as of 3.6.\n\n- **Tuples**: Since tuples compile down to structs, tuple lengths\n  must be known at compile time, meaning you can't convert an\n  arbitrarily-sized list to a tuple, for instance.\n\n- **Arrays**: Codon includes a native NumPy implementation with\n  a corresponding `ndarray` type. Codon's `ndarray` is parameterized\n  by the data type (`dtype`) and dimension (`ndim`). In practice,\n  this almost never affects NumPy code as these parameters are\n  determined automatically at compile time. In some cases, such\n  as when reading array data from disk, they must be provided\n  programmatically. Learn more in the [Codon-NumPy docs](../libraries/numpy.md).\n\n### Type checking\n\nSince Codon performs static type checking ahead of time, a\nfew of Python's dynamic features are disallowed. For example,\nmonkey patching classes at runtime (although Codon supports a\nform of this at compile time) or adding objects of different\ntypes to a collection.\n\nThese few restrictions are ultimately what allow Codon to\ncompile to native code without any runtime performance overhead.\nFuture versions of Codon will lift some of these restrictions\nby the introduction of e.g. implicit union types.\n\n### Numerics\n\nFor performance reasons, some numeric operations use C semantics\nrather than Python semantics. This includes, for example, raising\nan exception when dividing by zero, or other checks done by `math`\nfunctions. Strict adherence to Python semantics can be enforced by\nusing the `-numerics=py` flag of the `codon` CLI. Note that this\ndoes *not* change `int`s from 64-bit.\n\n### Modules\n\nWhile most of the commonly used builtin modules have Codon-native\nimplementations, a few are not yet implemented. However these can\nstill be used within Codon\n[via `from python import`](../integrations/python/python-from-codon.md).\n"
  },
  {
    "path": "docs/libraries/api/.gitignore",
    "content": "*\n!.gitignore\n"
  },
  {
    "path": "docs/libraries/numpy.md",
    "content": "Codon ships with a feature-complete, fully-compiled native NumPy implementation.\nIt uses the same API as NumPy, but re-implements everything in Codon itself,\nallowing for a range of optimizations and performance improvements. Codon-NumPy\nworks with Codon's Python interoperability (you can transfer arrays to and from\nregular Python seamlessly), parallel backend (you can do array operations in\nparallel), and GPU backend (you can transfer arrays to and from the GPU seamlessly,\nand operate on them on the GPU).\n\n## Getting started\nImporting `numpy` in Codon will use Codon-NumPy (as opposed to\n`from python import numpy`, which would use standard NumPy):\n\n``` python\nimport numpy as np\n```\n\nWe can then create and manipulate arrays just like in standard NumPy:\n\n``` python\nx = np.arange(15, dtype=np.int64).reshape(3, 5)\nprint(x)\n#   0   1   2   3   4\n#   5   6   7   8   9\n#  10  11  12  13  14\n\nx[1:, ::2] = -99\n#   0   1   2   3   4\n# -99   6 -99   8 -99\n# -99  11 -99  13 -99\n\ny = x.max(axis=1)\nprint(y)\n#   4   8  13\n```\n\nIn Codon-NumPy, any Codon type can be used as the array type. The `numpy`\nmodule has the same aliases that regular NumPy has, like `np.int64`,\n`np.float32` etc., but these simply refer to the regular Codon types.\n\n!!! warning\n\n    Using a string (e.g. `\"i4\"` or `\"f8\"`) for the dtype is not yet supported.\n\n## Codon array type\nThe Codon array type is parameterized by the array data type (\"`dtype`\")\nand the array dimension (\"`ndim`\"). That means that, in Codon-NumPy, the\narray dimension is a property of the type, so a 1-d array is a different\ntype than a 2-d array and so on:\n\n``` python\nimport numpy as np\n\narr = np.array([[1.1, 2.2], [3.3, 4.4]])\nprint(arr.__class__.__name__)  # ndarray[float,2]\n\narr = np.arange(10)\nprint(arr.__class__.__name__)  # ndarray[int,1]\n```\n\nThe array dimension must also be known at compile-time. This allows the\ncompiler to perform a wider range of optimizations on array operations.\nUsually, this has no impact on the code as the NumPy functions can\ndetermine input and output dimensions automatically. However, the dimension\n(and dtype) must be given when, for instance, reading arrays from disk:\n\n``` python\n# 'dtype' argument specifies array type\n# 'ndim' argument specifies array dimension\narr = np.load('arr.npy', dtype=float, ndim=3)\n```\n\nA very limited number of NumPy functions return an array whose dimension\ncannot be deduced from its inputs. One such example is `squeeze()`, which\nremoves axes of length 1; since the number of axes of length 1 is not\ndeterminable at compile-time, this function requires an extra argument that\nindicates which axes to remove.\n\n## Python interoperability\nCodon's `ndarray` type supports Codon's standard Python interoperability API\n(i.e. `__to_py__` and `__from_py__` methods), so arrays can be transferred to\nand from Python seamlessly.\n\n### PyTorch integration\nBecause PyTorch tensors and NumPy arrays are interchangeable without copying\ndata, it is easy to use Codon to efficiently manipulate or operate on PyTorch\ntensors. This can be achieved either via Codon's just-in-time (JIT) compilation\nmode or via its Python extension mode.\n\n#### Using Codon JIT\nHere is an example showing initializing a $128 \\times 128 \\times 128$ tensor\n$A$ such that $A_{i,j,k} = i + j + k$:\n\n``` python\nimport numpy as np\nimport time\nimport codon\nimport torch\n\n@codon.jit\ndef initialize(arr):\n    for i in range(128):\n        for j in range(128):\n            for k in range(128):\n                arr[i, j, k] = i + j + k\n\n# first call JIT-compiles; subsequent calls use cached JIT'd code\ntensor = torch.empty(128, 128, 128)\ninitialize(tensor.numpy())\n\ntensor = torch.empty(128, 128, 128)\nt0 = time.time()\ninitialize(tensor.numpy())\nt1 = time.time()\n\nprint(tensor)\nprint(t1 - t0, 'seconds')\n```\n\nTimings on an M1 MacBook Pro:\n\n- Without `@codon.jit`: 0.1645 seconds\n- With `@codon.jit`: 0.001485 seconds (*110x speedup*)\n\nFor more information, see the [Codon JIT docs](../integrations/python/codon-from-python.md).\n\n#### Using Codon Python extensions\nCodon can compile directly to a Python extension module, similar to writing a C\nextension for CPython or using Cython.\n\nTaking the same example, we can create a file `init.py`:\n\n``` python\nimport numpy as np\nimport numpy.pybridge\n\ndef initialize(arr: np.ndarray[np.float32, 3]):\n    for i in range(128):\n        for j in range(128):\n            for k in range(128):\n                arr[i, j, k] = i + j + k\n```\n\nNote that extension module functions need to specify argument types. In this case,\nthe argument is a 3-dimensional array of type `float32`, which is expressed as\n`np.ndarray[np.float32, 3]` in Codon.\n\nNow we can use a setup script `setup.py` to create the extension module as described\nin the [Codon Python extension docs](../integrations/python/extensions.md):\n\n``` bash\npython3 setup.py build_ext --inplace  # setup.py from docs linked above\n```\n\nFinally, we can call the function from Python:\n\n``` python\nfrom codon_initialize import initialize\nimport torch\n\ntensor = torch.empty(128, 128, 128)\ninitialize(tensor.numpy())\nprint(tensor)\n```\n\nNote that there is no compilation happening at runtime with this approach. Instead,\neverything is compiled ahead of time when creating the extension. The timing is the same\nas the first approach.\n\nYou can also use any Codon compilation flags with this approach by adding them to the\n`spawn` call in the setup script. For example, you can use the `-disable-exceptions`\nflag to disable runtime exceptions, which can yield performance improvements and generate\nmore streamlined code.\n\n## Parallel processing\nUnlike Python, Codon has no global interpreter lock (\"GIL\") and supports full\nmultithreading, meaning NumPy code can be parallelized. For example:\n\n``` python\nimport numpy as np\nimport numpy.random as rnd\nimport time\n\nN = 100000000\nn = 10\n\nrng = rnd.default_rng(seed=0)\nx = rng.normal(size=(N,n))\ny = np.empty(n)\n\nt0 = time.time()\n\n@par(num_threads=n)\nfor i in range(n):\n    y[i] = x[:,i].sum()\n\nt1 = time.time()\n\nprint(y)\nprint(t1 - t0, 'seconds')\n# no par - 1.4s\n# w/ par - 0.4s\n```\n\n## GPU processing\nCodon-NumPy supports seamless GPU processing: arrays can be passed to and\nfrom the GPU, and array operations can be performed on the GPU using Codon's\nGPU backend. Here's an example that computes the Mandelbrot set:\n\n``` python\nimport numpy as np\nimport gpu\n\nMAX    = 1000  # maximum Mandelbrot iterations\nN      = 4096  # width and height of image\npixels = np.empty((N, N), int)\n\ndef scale(x, a, b):\n    return a + (x/N)*(b - a)\n\n@gpu.kernel\ndef mandelbrot(pixels):\n    i = (gpu.block.x * gpu.block.dim.x) + gpu.thread.x\n    j = (gpu.block.y * gpu.block.dim.y) + gpu.thread.y\n    c = complex(scale(j, -2.00, 0.47), scale(i, -1.12, 1.12))\n    z = 0j\n    iteration = 0\n\n    while abs(z) <= 2 and iteration < MAX:\n        z = z**2 + c\n        iteration += 1\n\n    pixels[i, j] = 255 * iteration/MAX\n\nmandelbrot(pixels, grid=(N//32, N//32), block=(32, 32))\n```\n\nHere is the same code using GPU-parallelized `for`-loops:\n\n``` python\nimport numpy as np\nimport gpu\n\nMAX    = 1000  # maximum Mandelbrot iterations\nN      = 4096  # width and height of image\npixels = np.empty((N, N), int)\n\ndef scale(x, a, b):\n    return a + (x/N)*(b - a)\n\n@par(gpu=True, collapse=2)  # <--\nfor i in range(N):\n    for j in range(N):\n        c = complex(scale(j, -2.00, 0.47), scale(i, -1.12, 1.12))\n        z = 0j\n        iteration = 0\n\n        while abs(z) <= 2 and iteration < MAX:\n            z = z**2 + c\n            iteration += 1\n\n        pixels[i, j] = 255 * iteration/MAX\n```\n\n## Linear algebra\nCodon-NumPy fully supports the NumPy linear algebra module which provides\na comprehensive set of functions for linear algebra operations. Importing\nthe linear algebra module, just like in standard NumPy:\n\n``` python\nimport numpy.linalg as LA\n```\n\nFor example, the `eig()` function computes the eigenvalues and eigenvectors\nof a square matrix:\n\n``` python\neigenvalues, eigenvectors = LA.eig(np.diag((1, 2, 3)))\nprint(eigenvalues)\n# 1.+0.j 2.+0.j 3.+0.j\n\nprint(eigenvectors)\n# [[1.+0.j 0.+0.j 0.+0.j]\n#  [0.+0.j 1.+0.j 0.+0.j]\n#  [0.+0.j 0.+0.j 1.+0.j]]\n```\n\nJust like standard NumPy, Codon will use an optimized BLAS library under the\nhood to implement many linear algebra operations. This defaults to OpenBLAS\non Linux and Apple's Accelerate framework on macOS.\n\nBecause Codon supports full multithreading, it's possible to use outer-loop\nparallelism to perform linear algebra operations in parallel. Here's an example\nthat multiplies several matrices in parallel:\n\n``` python\nimport numpy as np\nimport numpy.random as rnd\nimport time\n\nN = 5000\nn = 10\nrng = rnd.default_rng(seed=0)\na = rng.normal(size=(n, N, N))\nb = rng.normal(size=(n, N, N))\ny = np.empty((n, N, N))\nt0 = time.time()\n\n@par(num_threads=n)\nfor i in range(n):\n    y[i, :, :] = a[i, :, :] @ b[i, :, :]\n\nt1 = time.time()\nprint(y.sum())\nprint(t1 - t0, 'seconds')  # Python - 53s\n                           # Codon  -  6s\n```\n\n!!! tip\n\n    When using Codon's outer-loop parallelism, make sure to set the environment\n    variable `OPENBLAS_NUM_THREADS` to 1 (i.e. `export OPENBLAS_NUM_THREADS=1`)\n    to avoid conflicts with OpenBLAS multithreading.\n\n\n## NumPy-specific compiler optimizations\nCodon includes compiler passes that optimize NumPy code through methods like\noperator fusion, which combine distinct operations so that they can be executed\nduring a single pass through the argument arrays, saving both execution time and\nmemory (since intermediate arrays no longer need to be allocated).\n\nTo showcase this, here's a simple NumPy program that approximates $\\pi$.\nThe code below generates two random vectors $x$ and $y$ with entries in the\nrange $[0, 1)$ and computes the fraction of pairs of points that lie in the\ncircle of radius $0.5$ centered at $(0.5, 0.5)$, which is approximately\n$\\pi \\over 4$.\n\n``` python\nimport time\nimport numpy as np\n\nrng = np.random.default_rng(seed=0)\nx = rng.random(500_000_000)\ny = rng.random(500_000_000)\n\nt0 = time.time()\n# pi ~= 4 x (fraction of points in circle)\npi = ((x-1)**2 + (y-1)**2 < 1).sum() * (4 / len(x))\nt1 = time.time()\n\nprint(pi)\nprint(t1 - t0, 'seconds')\n```\n\nThe expression `(x-1)**2 + (y-1)**2 < 1` gets fused by Codon so that it is\nexecuted in just a single pass over the `x` and `y` arrays, rather than in\nmultiple passes for each sub-expression `x-1`, `y-1` etc. as is the case with\nstandard NumPy.\n\nHere are the resulting timings on an M1 MacBook Pro:\n\n- Python / standard NumPy: 2.4 seconds\n- Codon: 0.42 seconds (*6x speedup*)\n\nYou can display information about fused expressions by using the `-npfuse-verbose`\nflag of `codon`, as in `codon run -release -npfuse-verbose pi.py`. Here's the\noutput for the program above:\n\n```\nOptimizing expression at pi.py:10:7\nlt <array[bool, 1]> [cost=6]\n  add <array[f64, 1]> [cost=5]\n    pow <array[f64, 1]> [cost=2]\n      sub <array[f64, 1]> [cost=1]\n        a0 <array[f64, 1]>\n        a1 <i64>\n      a2 <i64>\n    pow <array[f64, 1]> [cost=2]\n      sub <array[f64, 1]> [cost=1]\n        a3 <array[f64, 1]>\n        a4 <i64>\n      a5 <i64>\n  a6 <i64>\n\n-> static fuse:\nlt <array[bool, 1]> [cost=6]\n  add <array[f64, 1]> [cost=5]\n    pow <array[f64, 1]> [cost=2]\n      sub <array[f64, 1]> [cost=1]\n        a0 <array[f64, 1]>\n        a1 <i64>\n      a2 <i64>\n    pow <array[f64, 1]> [cost=2]\n      sub <array[f64, 1]> [cost=1]\n        a3 <array[f64, 1]>\n        a4 <i64>\n      a5 <i64>\n  a6 <i64>\n```\n\nAs shown, the optimization pass employs a cost model to decide how to best handle\na given expression, be it by fusing or evaluating sequentially. You can adjust the\nfusion cost thresholds via the following flags:\n\n- `-npfuse-always <cost1>`: Expression cost below which to always fuse a given\n  expression (default: `10`).\n- `-npfuse-never <cost2>`: Expression cost above which (>) to never fuse a given\n  expression (default: `50`).\n\nGiven an expression cost `C`, the logic implemented in the pass is to:\n\n- Always fuse expressions where `C <= cost1`.\n- Fuse expressions where `cost1 < C <= cost2` if there is no broadcasting involved.\n- Never fuse expressions where `C > cost2` and instead evaluate them sequentially.\n\nThis logic is applied recursively to a given expression to determine the optimal\nevaluation strategy.\n\nYou can disable these optimizations altogether by disabling the corresponding\ncompiler pass via the flag `-disable-opt core-numpy-fusion`.\n\n## I/O\nCodon-NumPy supports most of NumPy's I/O API. One important difference, however,\nis that I/O functions must specify the dtype and dimension of arrays being read,\nsince Codon-NumPy array types are parameterized by dtype and dimension:\n\n``` python\nimport numpy as np\n\na = np.arange(27, dtype=np.int16).reshape(3, 3, 3)\nnp.save('arr.npy', a)\n\n# Notice the 'dtype' and 'ndim' arguments:\nb = np.load('arr.npy', dtype=np.int16, ndim=3)\n```\n\nWriting arrays has no such requirement.\n\n## Datetimes\nCodon-NumPy fully supports NumPy's datetime types: `datetime64` and `timedelta64`.\nOne difference from standard NumPy is how these types are specified. Here's an example:\n\n``` python\n# datetime64 type with units of \"1 day\"\n# same as \"dtype='datetime64[D]'\" in standard NumPy\ndt = np.array(['2020-01-02', '2021-09-15', '2022-07-01'],\n              dtype=np.datetime64['D', 1])\n\n# timedelta64 type with units of \"15 minutes\"\n# same as \"dtype='timedelta64[15m]'\" in standard NumPy\ntd = np.array([100, 200, 300], dtype=np.timedelta64['m', 15])\n```\n\n## Passing array data to C/C++\nYou can pass an `ndarray`'s underlying data pointer to a C/C++ function by using\nthe `data` attribute of the array. For example:\n\n``` python\nfrom C import foo(p: Ptr[float], n: int)\n\narr = np.ndarray([1.0, 2.0, 3.0])\nfoo(arr.data, arr.size)\n```\n\nOf course, it's the caller's responsibility to make sure the array is contiguous\nas needed and/or pass additional shape or stride information. See the\n[C interoperability](../integrations/cpp/cpp-from-codon.md) docs for more information.\n\n### Array ABI\n\nThe `ndarray[dtype, ndim]` data structure has three fields, in the following order:\n\n- `shape`: length-`ndim` tuple of non-negative 64-bit integers representing the array\n  shape\n- `strides`: length-`ndim` tuple of 64-bit integers representing the stride in bytes\n  along each axis of the array\n- `data`: pointer of type `dtype` to the array's data\n\nFor example, `ndarray[np.float32, 3]` would correspond to the following C structure:\n\n``` c\nstruct ndarray_float32_3 {\n  int64_t shape[3];\n  int64_t strides[3];\n  float *data;\n};\n```\n\nThis can be used to pass an entire `ndarray` object to a C function without breaking\nit up into its constituent components.\n\n## Performance tips\n\n### Array layouts\nAs with standard NumPy, Codon-NumPy performs best when array data is contiguous in\nmemory, ideally in row-major order (also called \"C order\"). Most NumPy functions will\nreturn C-order arrays, but operations like slicing and transposing arrays can alter\ncontiguity. You can use `numpy.ascontiguousarray()` to create a contiguous array from\nan arbitrary array.\n\n### Linux huge pages\nWhen working with large arrays on Linux, enabling\n[transparent hugepages](https://www.kernel.org/doc/html/latest/admin-guide/mm/transhuge.html)\ncan result in significant performance improvements.\n\nYou can check if transparent hugepages are enabled via\n\n``` bash\ncat /sys/kernel/mm/transparent_hugepage/enabled\n```\n\nand you can enable them via\n\n``` bash\necho \"always\" | sudo tee /sys/kernel/mm/transparent_hugepage/enabled\n```\n\n### Disabling exceptions\nBy default, Codon performs various validation checks at runtime (e.g. bounds checks when\nindexing an array) just like standard NumPy, and raises an exception if they fail. If\nyou know your program will not raise or catch any exceptions, you can disable these\nchecks through the `-disable-exceptions` compiler flag.\n\nNote that when using this flag, raising an exception will terminate the process with a\n`SIGTRAP`.\n\n### Fast-math\nYou can enable \"fast-math\" optimizations via the `-fast-math` compiler flag. It is\nadvisable to **use this flag with caution** as it changes floating point semantics and\nmakes assumptions regarding `inf` and `nan` values. For more information, consult LLVM's\ndocumentation on [fast-math flags](https://llvm.org/docs/LangRef.html#fast-math-flags).\n\n## Not-yet-supported\nThe following features of NumPy are not yet supported, but are planned for the future:\n- String operations\n- Masked arrays\n- Polynomials\n\nA few miscellaneous Python-specific functions like `get_include()` are also not\nsupported, as they are not applicable in Codon.\n"
  },
  {
    "path": "docs/libraries/stdlib.md",
    "content": "Codon implements much of Python's standard library natively.\nSome built-in modules and some methods of certain modules are\nnot yet available natively in Codon; these\n[can still be called through Python](../integrations/python/python-from-codon.md),\nhowever:\n\n``` python\nimport sys              # uses Codon's native 'sys' module\nfrom python import sys  # uses Python's 'sys' module\n```\n\n## Built-in modules\n\nThe following built-in modules are supported either in full\nor in part natively in Codon:\n\n| Module        | Notes |\n| ------------- | - |\n| `copy`        ||\n| `gzip`        ||\n| `random`      | Matches CPython's `random` outputs for same seed. |\n| `threading`   | Locks work with Codon's parallel programming features. |\n| `bisect`      ||\n| `datetime`    | `timedelta` are represented in microseconds. Time zones not supported. |\n| `heapq`       ||\n| `operator`    ||\n| `re`          | Uses [Google's RE2 library](https://github.com/google/re2) internally. |\n| `time`        ||\n| `bz2`         ||\n| `os`          ||\n| `cmath`       ||\n| `functools`   ||\n| `itertools`   ||\n| `statistics`  ||\n| `typing`      | Contents are available by default in Codon. |\n| `getopt`      ||\n| `math`        ||\n| `pickle`      | Codon uses its own pickle format, so generally not compatible with CPython pickling. |\n| `string`      ||\n| `collections` ||\n| `sys`         ||\n\n## Additional modules\n\nAlongside the standard modules above, Codon provides several additional\nmodules that support various Codon-specific features.\n\n- `openmp`: Contains [OpenMP](https://openmp.org) API, which can be used when\n  writing multithreaded programs. See [multithreading](../parallel/multithreading.md) for\n  more information.\n- `gpu`: Contains GPU API (e.g. CUDA intrinsics), which can be used when writing\n  GPU code. See [GPU](../parallel/gpu.md) for more information.\n- `python`: Contains internal machinery for interfacing with CPython. Most users will\n  not need to interact with this module directly.\n- `experimental`: Contains experimental features that are available for use, but might\n  not be stable nor complete.\n"
  },
  {
    "path": "docs/overrides/main.html",
    "content": "{% extends \"base.html\" %}\n\n{% block htmltitle %}\n  {% if page.meta and page.meta.title %}\n    <title>{{ page.meta.title }} | Codon</title>\n  {% elif page.title and not page.is_homepage %}\n    <title>{{ page.title | striptags }} | Codon</title>\n  {% else %}\n    <title>Documentation | Codon</title>\n  {% endif %}\n{% endblock %}\n\n{% block announce %}\n  <a href=\"https://discord.gg/HeWRhagCmP\">\n    <strong>\n      🎉 Join our community on Discord\n      <span class=\"discord-icon\">\n        {% include \".icons/fontawesome/brands/discord.svg\" %}\n      </span>\n    </strong> — Ask questions, share ideas, and get support!\n  </a>\n{% endblock %}\n"
  },
  {
    "path": "docs/parallel/gpu.md",
    "content": "Codon supports GPU programming through a native GPU backend.\nCurrently, only Nvidia devices are supported.\n\n!!! info\n\n    [CUDA](https://developer.nvidia.com/cuda-toolkit) is required for GPU programming.\n    Codon automatically loads the CUDA shared library `libcuda.so` (or `libcuda.dylib`).\n    If this library is not in a standard location, its path can be specified via the\n    `CODON_CUDA` environment variable.\n\nHere is a simple example:\n\n``` python\nimport gpu\n\n@gpu.kernel\ndef hello(a, b, c):\n    i = gpu.thread.x\n    c[i] = a[i] + b[i]\n\na = [i for i in range(16)]\nb = [2*i for i in range(16)]\nc = [0 for _ in range(16)]\n\nhello(a, b, c, grid=1, block=16)\nprint(c)  # [0, 3, 6, ..., 45]\n```\n\nThe same code can be written using Codon's `@par` syntax:\n\n``` python\na = [i for i in range(16)]\nb = [2*i for i in range(16)]\nc = [0 for _ in range(16)]\n\n@par(gpu=True)\nfor i in range(16):\n    c[i] = a[i] + b[i]\n```\n\nNumPy arrays and operations also work seamlessly in GPU code:\n\n``` python\nimport numpy as np\n\na = np.arange(16)\nb = np.arange(16) * 2\nc = np.empty(16, dtype=int)\n\n@par(gpu=True)\nfor i in range(16):\n    c[i] = a[i] + b[i]\n```\n\n## Writing GPU kernels\n\nBelow is a more comprehensive example for computing the\n[Mandelbrot set](https://en.wikipedia.org/wiki/Mandelbrot_set), and plotting it\nusing Matplotlib:\n\n``` python\nfrom python import matplotlib.pyplot as plt\nimport numpy as np\nimport gpu\n\nMAX    = 1000  # maximum Mandelbrot iterations\nN      = 4096  # width and height of image\npixels = np.empty((N, N), dtype=int)\n\ndef scale(x, a, b):\n    return a + (x/N)*(b - a)\n\n@gpu.kernel\ndef mandelbrot(pixels):\n    idx = (gpu.block.x * gpu.block.dim.x) + gpu.thread.x\n    i, j = divmod(idx, N)\n    c = complex(scale(j, -2.00, 0.47), scale(i, -1.12, 1.12))\n    z = 0j\n    iteration = 0\n\n    while abs(z) <= 2 and iteration < MAX:\n        z = z**2 + c\n        iteration += 1\n\n    pixels[i, j] = int(255 * iteration/MAX)\n\nmandelbrot(pixels, grid=(N*N)//1024, block=1024)\nplt.imshow(pixels)\nplt.show()\n```\n\nThe GPU version of the Mandelbrot code is about 450 times faster\nthan an equivalent CPU version.\n\nGPU kernels are marked with the `@gpu.kernel` annotation, and\ncompiled specially in Codon's backend. Kernel functions can\nuse the vast majority of features supported in Codon, with a\nfew notable exceptions:\n\n- Exception handling is not supported inside the kernel, meaning\n  kernel code should not throw or catch exceptions. `raise`\n  statements inside the kernel are marked as unreachable and\n  optimized out.\n\n- Functionality related to I/O is not supported (e.g. you can't\n  open a file in the kernel).\n\n- A few other modules and functions are not allowed, such as the\n  `re` module (which uses an external regex library) or the `os`\n  module.\n\n## Invoking GPU kernels\n\nThe kernel can be invoked via a simple call with added `grid` and\n`block` parameters. These parameters define the grid and block\ndimensions, respectively. Recall that GPU execution involves a *grid*\nof (`X` x `Y` x `Z`) *blocks* where each block contains (`x` x `y` x `z`)\nexecuting threads. Device-specific restrictions on grid and block sizes\napply.\n\nThe `grid` and `block` parameters can be one of:\n\n- Single integer `x`, giving dimensions `(x, 1, 1)`\n- Tuple of two integers `(x, y)`, giving dimensions `(x, y, 1)`\n- Tuple of three integers `(x, y, z)`, giving dimensions `(x, y, z)`\n- Instance of `gpu.Dim3` as in `Dim3(x, y, z)`, specifying the three dimensions\n\n## GPU intrinsics\n\nCodon's GPU module provides many of the same intrinsics that CUDA does:\n\n| Codon             | Description                             | CUDA equivalent |\n|-------------------|-----------------------------------------|-----------------|\n| `gpu.thread.x`    | x-coordinate of current thread in block | `threadId.x`    |\n| `gpu.block.x`     | x-coordinate of current block in grid   | `blockIdx.x`    |\n| `gpu.block.dim.x` | x-dimension of block                    | `blockDim.x`    |\n| `gpu.grid.dim.x`  | x-dimension of grid                     | `gridDim.x`     |\n\nThe same applies for the `y` and `z` coordinates. The `*.dim` objects are instances\nof `gpu.Dim3`.\n\n## Math functions\n\nAll the functions in the `math` module are supported in kernel functions, and\nare automatically replaced with GPU-optimized versions:\n\n``` python\nimport math\nimport gpu\n\n@gpu.kernel\ndef hello(x):\n    i = gpu.thread.x\n    x[i] = math.sqrt(x[i])  # uses __nv_sqrt from libdevice\n\nx = [float(i) for i in range(10)]\nhello(x, grid=1, block=10)\nprint(x)\n```\n\ngives:\n\n```\n[0, 1, 1.41421, 1.73205, 2, 2.23607, 2.44949, 2.64575, 2.82843, 3]\n```\n\nThe same is also applicable for NumPy library functions.\n\n## Libdevice\n\nCodon uses [libdevice](https://docs.nvidia.com/cuda/libdevice-users-guide/index.html)\nfor GPU-optimized math functions. The default libdevice path is\n`/usr/local/cuda/nvvm/libdevice/libdevice.10.bc`. An alternative path can be specified\nvia the `-libdevice` compiler flag.\n\n## Using raw pointers\n\nBy default, objects are converted entirely to their GPU counterparts, which have\nthe same data layout as the original objects (although the Codon compiler might perform\noptimizations by swapping a CPU implementation of a data type with a GPU-optimized\nimplementation that exposes the same API). This preserves all of Codon/Python's\nstandard semantics within the kernel.\n\nIt is possible to use a kernel with raw pointers via `gpu.raw`, which corresponds\nto how the kernel would be written in C++/CUDA:\n\n``` python\nimport gpu\n\n@gpu.kernel\ndef hello(a, b, c):\n    i = gpu.thread.x\n    c[i] = a[i] + b[i]\n\na = [i for i in range(16)]\nb = [2*i for i in range(16)]\nc = [0 for _ in range(16)]\n\n# call the kernel with three int-pointer arguments:\nhello(gpu.raw(a), gpu.raw(b), gpu.raw(c), grid=1, block=16)\nprint(c)  # output same as first snippet's\n```\n\n`gpu.raw` can avoid an extra pointer indirection, but outputs a Codon `Ptr` object,\nmeaning the corresponding kernel parameters will not have the full list API, instead\nhaving the more limited `Ptr` API (which primarily just supports indexing/assignment).\n\n## Object conversions\n\nA hidden API is used to copy objects to and from the GPU device. This API consists of\ntwo new *magic methods*:\n\n- `__to_gpu__(self)`: Allocates the necessary GPU memory and copies the object `self` to\n  the device.\n\n- `__from_gpu__(self, gpu_object)`: Copies the GPU memory of `gpu_object` (which is\n  a value returned by `__to_gpu__`) back to the CPU object `self`.\n\nFor primitive types like `int` and `float`, `__to_gpu__` simply returns `self` and\n`__from_gpu__` does nothing. These methods are defined for all the built-in types *and*\nare automatically generated for user-defined classes, so most objects can be transferred\nback and forth from the GPU seamlessly. A user-defined class that makes use of raw pointers\nor other low-level constructs will have to define these methods for GPU use. Please refer\nto the `gpu` module for implementation examples.\n\n## `@par(gpu=True)`\n\nCodon's `@par` syntax can be used to seamlessly parallelize existing loops on the GPU,\nwithout needing to explicitly write them as kernels. For loop nests, the `collapse` argument\ncan be used to cover the entire iteration space on the GPU. For example, here is the Mandelbrot\ncode above written using `@par`:\n\n``` python\nimport numpy as np\n\nMAX    = 1000  # maximum Mandelbrot iterations\nN      = 4096  # width and height of image\npixels = np.empty((N, N), dtype=int)\n\ndef scale(x, a, b):\n    return a + (x/N)*(b - a)\n\n@par(gpu=True, collapse=2)\nfor i in range(N):\n    for j in range(N):\n        c = complex(scale(j, -2.00, 0.47), scale(i, -1.12, 1.12))\n        z = 0j\n        iteration = 0\n\n        while abs(z) <= 2 and iteration < MAX:\n            z = z**2 + c\n            iteration += 1\n\n        pixels[i, j] = int(255 * iteration/MAX)\n```\n\nNote that the `gpu=True` option disallows shared variables (i.e. assigning out-of-loop\nvariables in the loop body) as well as reductions. The other GPU-specific restrictions\ndescribed here apply as well.\n\n## GPU target selection\n\nThe following compiler flags can be used to control GPU code generation and to specify\nthe target GPU architecture and feature set:\n\n- `--gpu-name <name>`:\n  Specifies the target GPU architecture or compute capability.\n  This value is passed directly to LLVM and determines the instruction set and hardware\n  features that the generated code targets.\n  Defaults to `sm_30`.\n\n- `--gpu-features <features>`:\n  Specifies a comma-separated list of LLVM-style GPU feature flags to enable or disable.\n  These flags control optional ISA and hardware features exposed to the compiler (for\n  example, enabling a specific PTX version with `+ptx42`).\n  Defaults to `+ptx42`.\n\n## Inspecting the generated PTX code\n\nThe generated PTX code can be saved to a file via the `-ptx <file>` flag. This code is\nembedded as constant data in the compiled output produced by Codon, and loaded at runtime\nvia the CUDA Driver API.\n\n## Troubleshooting\n\nCUDA errors resulting in kernel abortion are printed, and typically arise from invalid\ncode in the kernel, either via using exceptions or using unsupported modules/objects.\n"
  },
  {
    "path": "docs/parallel/multithreading.md",
    "content": "Codon supports parallelism and multithreading via OpenMP out of the box.\nHere's an example:\n\n``` python\n@par\nfor i in range(10):\n    import threading as thr\n    print('hello from thread', thr.get_ident())\n```\n\nBy default, parallel loops will use all available threads, or use the\nnumber of threads specified by the `OMP_NUM_THREADS` environment\nvariable. A specific thread number can be given directly on the `@par`\nline as well:\n\n``` python\n@par(num_threads=5)\nfor i in range(10):\n    import threading as thr\n    print('hello from thread', thr.get_ident())\n```\n\n`@par` supports several OpenMP parameters, including:\n\n-   `num_threads` (int): the number of threads to use when running the\n    loop\n-   `schedule` (str): either *static*, *dynamic*, *guided*, *auto* or\n    *runtime*\n-   `chunk_size` (int): chunk size when partitioning loop iterations\n-   `ordered` (bool): whether the loop iterations should be executed in\n    the same order\n-   `collapse` (int): number of loop nests to collapse into a single\n    iteration space\n\nOther OpenMP parameters like `private`, `shared` or `reduction`, are\ninferred automatically by the compiler. For example, the following loop\n\n``` python\na = 0\n@par\nfor i in range(N):\n    a += foo(i)\n```\n\nwill automatically generate a reduction for variable `a`.\n\n!!! warning\n\n    Modifying shared objects like lists or dictionaries within a parallel\n    section needs to be done with a lock or critical section. See below\n    for more details.\n\nHere is an example that finds the number of primes up to a\nuser-defined limit, using a parallel loop on 16 threads with a dynamic\nschedule and chunk size of 100:\n\n``` python\nfrom sys import argv\n\ndef is_prime(n):\n    factors = 0\n    for i in range(2, n):\n        if n % i == 0:\n            factors += 1\n    return factors == 0\n\nlimit = int(argv[1])\ntotal = 0\n\n@par(schedule='dynamic', chunk_size=100, num_threads=16)\nfor i in range(2, limit):\n    if is_prime(i):\n        total += 1\n\nprint(total)\n```\n\nStatic schedules work best when each loop iteration takes roughly the\nsame amount of time, whereas dynamic schedules are superior when each\niteration varies in duration. Since counting the factors of an integer\ntakes more time for larger integers, we use a dynamic schedule here.\n\n`@par` also supports C/C++ OpenMP pragma strings. For example, the\n`@par` line in the above example can also be written as:\n\n``` python\n# same as: @par(schedule='dynamic', chunk_size=100, num_threads=16)\n@par('schedule(dynamic, 100) num_threads(16)')\n```\n\n## Loop types\n\n`for`-loops can iterate over arbitrary generators, but OpenMP's\nparallel loop construct only applies to *imperative* for-loops of the\nform `for i in range(a, b, c)` (where `c` is constant). For general\nparallel for-loops of the form `for i in some_generator()`, a task-based\napproach is used instead, where each loop iteration is executed as an\nindependent task.\n\nThe Codon compiler also converts iterations over lists\n(`for a in some_list`) to imperative for-loops, meaning these loops can\nbe executed using OpenMP's loop parallelism.\n\n## Custom reductions\n\nCodon can automatically generate efficient reductions for `int` and\n`float` values. For other data types, user-defined reductions can be\nspecified. A class that supports reductions must include:\n\n-   A default constructor that represents the *zero value*\n-   An `__add__` method (assuming `+` is used as the reduction operator)\n\nHere is an example for reducing a new `Vector` type:\n\n``` python\n@tuple\nclass Vector:\n    x: int\n    y: int\n\n    def __new__():\n        return Vector(0, 0)\n\n    def __add__(self, other: Vector):\n        return Vector(self.x + other.x, self.y + other.y)\n\nv = Vector()\n@par\nfor i in range(100):\n    v += Vector(i,i)\nprint(v)  # (x: 4950, y: 4950)\n```\n\n## OpenMP constructs\n\nAll of OpenMP's API functions are accessible directly in Codon. For\nexample:\n\n``` python\nimport openmp as omp\nprint(omp.get_num_threads())\nomp.set_num_threads(32)\n```\n\nOpenMP's *critical*, *master*, *single* and *ordered* constructs can be\napplied via the corresponding decorators:\n\n``` python\nimport openmp as omp\n\n@omp.critical\ndef only_run_by_one_thread_at_a_time():\n    print('critical!', omp.get_thread_num())\n\n@omp.master\ndef only_run_by_master_thread():\n    print('master!', omp.get_thread_num())\n\n@omp.single\ndef only_run_by_single_thread():\n    print('single!', omp.get_thread_num())\n\n@omp.ordered\ndef run_ordered_by_iteration(i):\n    print('ordered!', i)\n\n@par(ordered=True)\nfor i in range(100):\n    only_run_by_one_thread_at_a_time()\n    only_run_by_master_thread()\n    only_run_by_single_thread()\n    run_ordered_by_iteration(i)\n```\n\nFor finer-grained locking, consider using the locks from the `threading`\nmodule:\n\n``` python\nfrom threading import Lock\nlock = Lock()  # or RLock for reentrant lock\n\n@par\nfor i in range(100):\n    with lock:\n        print('only one thread at a time allowed here')\n```\n\n## Thread-local variables\n\nVariables can be marked as *thread-local* via `threading.ThreadLocal`:\n\n``` python\nimport threading as thr\nx: thr.ThreadLocal[int] = 0\n```\n\nA `ThreadLocal[T]` variable is treated as if it had type `T`, but reads\nor writes of the variable are local to each thread. For example:\n\n``` python\nimport threading as thr\n\nx: thr.ThreadLocal[int] = 0\nlock = thr.Lock()\n\n@par\nfor i in range(4):\n    x = i  # each thread writes its own thread-local 'x'\n\n@par\nfor i in range(4):\n    with lock:\n        # Will print \"Thread i: x = i\" for i in 0..3\n        print(f'Thread {thr.get_native_id()}: x = {x}')\n```\n"
  },
  {
    "path": "docs/parallel/simd.md",
    "content": "Codon's `simd` module and `Vec[T, N]` type provide direct, ergonomic access to\n[SIMD](https://en.wikipedia.org/wiki/Single_instruction,_multiple_data)\ninstructions, including:\n\n- Explicit control over vector width and element type\n- Portable syntax for arithmetic, logic, comparisons, and math intrinsics\n- Reductions, masking, and overflow-aware operations\n\n\n## Vector types\n\n`simd.Vec[T, N]` represents an LLVM vector `<N x T>`:\n\n- `T` is a scalar numeric type (e.g. `float32`, `int`, `u32`, etc.)\n- `N` is an integeral [literal](../language/meta.md)\n\nConceptually:\n\n* `Vec[float32, 8]` ≈ *\"8-wide float32 SIMD register\"*\n* `Vec[u8, 16]` ≈ *\"16 byte-wide lanes\"*\n\nYou would typically use `Vec` in hot loops where:\n\n- Work is *data-parallel* (same operation applied to many elements)\n- The data is laid out in contiguous memory (arrays, lists, strings)\n- You care about *predictable* vectorization (not relying on the auto-vectorizer)\n\n\n## Creating vectors\n\n### Broadcasting a scalar\n\nThe simplest way to create a vector is to broadcast a scalar into all lanes:\n\n``` python\nfrom simd import Vec\n\n# 8-lane vector of all ones\nv = Vec[float, 8](1.0)\n\n# 4-lane vector of all -3\nw = Vec[int, 4](-3)\n```\n\n### Loading from pointers and arrays\n\n`Vec` can also load data from a pointer, such as the underlying buffer of a\nNumPy array. Here is an example that implements hand-vectorized addition of\ntwo arrays:\n\n``` python\nimport numpy as np\nfrom simd import Vec\n\ndef add_arrays(a: np.ndarray[float32, 1],\n               b: np.ndarray[float32, 1],\n               out: np.ndarray[float32, 1]):\n    W: Literal[int] = 8  # vector width\n    i = 0\n    while i + W <= len(a):\n        va = Vec[float32, W](a.data + i)            # load 8 elements from a[i..i+7]\n        vb = Vec[float32, W](b.data + i)            # load 8 elements from b[i..i+7]\n        vc = va + vb                                # SIMD add\n        Ptr[Vec[float32, W]](out.data + i)[0] = vc  # store back\n        i += W\n\n    # handle remaining tail elements (scalar)\n    while i < len(a):\n        out[i] = a[i] + b[i]\n        i += 1\n```\n\nNote that:\n\n- `Vec[T, N](ptr)` treats `ptr` as a `Ptr[Vec[T, N]]` and loads one SIMD vector\n- You can store by casting the output pointer to `Ptr[Vec[T, N]]`\n\nYou can also construct vectors from lists:\n\n``` python\ndata = [1.0, 2.0, 3.0, 4.0]\nv = Vec[float, 4](data)  # load from data[0..3]\n```\n\n### Loading from strings (bytes)\n\nFor byte-sized element types (`i8`, `u8`, `byte`), you can read directly from a string buffer:\n\n``` python\n# Load 16 bytes from a string\ns = \"ABCDEFGHIJKLMNOP\"\nv = Vec[u8, 16](s)\n\n# Skip first 4 bytes\nv2 = Vec[u8, 16](s, 4)\n```\n\n\n## SIMD arithmetic\n\nAll basic arithmetic operations are *lane-wise*:\n\n- `+`, `-`, `*` on integer and float vectors\n- `/` on float vectors (true division)\n- `//` and `%` on integer vectors\n- and so on...\n\nExample: fused multiply-add style accumulation for a dot product:\n\n``` python\nimport numpy as np\nfrom simd import Vec\n\ndef dot(a: Ptr[float32], b: Ptr[float32], n: int) -> float32:\n    W: Literal[int] = 8\n    i = 0\n    acc = Vec[float32, W](0.0f32)\n\n    while i + W <= n:\n        va = Vec[float32, W](a + i)\n        vb = Vec[float32, W](b + i)\n        acc = acc + va * vb   # lane-wise multiply + add\n        i += W\n\n    # horizontal reduce SIMD accumulator\n    total = acc.sum()\n\n    # handle tail scalars\n    while i < n:\n        total += a[i] * b[i]\n        i += 1\n\n    return total\n```\n\nNote that:\n\n- `va * vb` multiplies lanes\n- `acc.sum()` adds all lanes, resulting in a scalar\n\n\n## Masks, comparisons and branchless code\n\nComparisons between vectors (or between a vector and a scalar) produce *mask vectors*:\n\n``` python\nv: Vec[float, 8] = ...\nmask_negative = v < Vec[float, 8](0.0)  # Vec[u1, 8]\n```\n\nYou can then use `mask` to select between two vectors, *without branches*:\n\n``` python\ndef relu_vec(v: Vec[float, 8]) -> Vec[float, 8]:\n    zero = Vec[float, 8](0.0)\n    m = v > zero            # positive lanes\n    return v.mask(zero, m)  # if m: v else zero\n```\n\nThe general pattern is:\n\n- Build a mask via comparisons (`<`, `<=`, `>`, `>=`, `==`, `!=`)\n- Use `mask(self, other, mask)` to do: `mask ? self : other` lane-wise\n\nThis is useful to turn control-flow into data-flow, which is conducive to SIMD programming:\n\n- Clamping (`min`/`max`/conditionals)\n- Thresholding (e.g. `x > t ? x : 0`)\n- Selectively updating subset of lanes\n\n\n## Reductions and horizontal operations\n\n`Vec` includes a reduction over addition, both for integers and floats:\n\n``` python\nv = Vec[float32, 8](...)\ns = v.sum()  # returns float32\n```\n\nInternally this uses LLVM's vector reduction intrinsics and is typically much faster than\nmanually scattering and summing.\n\nYou can combine this with loops to build aggregate operations. Below is an example that\nimplements L2 norm:\n\n``` python\ndef l2_norm(xs: Ptr[float32], n: int) -> float32:\n    W: Literal[int] = 8\n    i = 0\n    acc = Vec[float32, W](0.0f32)\n\n    while i + W <= n:\n        v = Vec[float32, W](xs + i)\n        acc = acc + v * v\n        i += W\n\n    total = acc.sum()\n    while i < n:\n        total += xs[i] * xs[i]\n        i += 1\n\n    return np.sqrt(total)\n```\n\n\n## Integer-specific operations\n\nInteger types support additional operations, such as:\n\n- bitwise `&`, `|`, `^`\n- shifts `<<`, `>>`\n- `min`, `max`\n- overflow-aware add/sub\n\n### Saturating add example\n\nSuppose you want to add two `u8` images but clamp to 255 on overflow.\nYou can use the overflow-aware addition and a mask:\n\n``` python\ndef saturating_add_u8(a: Ptr[u8], b: Ptr[u8],\n                      out: Ptr[u8], n: int):\n    W: Literal[int] = 16\n    i = 0\n    max_val = Vec[u8, W](255u8)\n\n    while i + W <= n:\n        va = Vec[u8, W](a + i)\n        vb = Vec[u8, W](b + i)\n\n        (sum_vec, overflow) = va.add(vb, overflow=True)\n        # where overflow[i] == 1, clamp to 255\n        clamped = max_val.mask(sum_vec, overflow)\n\n        Ptr[Vec[u8, W]](out + i)[0] = clamped\n        i += W\n\n    while i < n:\n        s = int(a[i]) + int(b[i])\n        out[i] = u8(255 if s > 255 else s)\n        i += 1\n```\n\nNote:\n\n- `va.add(vb, overflow=True)` returns `(result, overflow_mask)`\n- Use `mask` to blend between a \"safe\" value and the raw result\n\n\n## Debugging\n\n### Scatter to list\n\nVectors can be transformed into lists:\n\n``` python\nv = Vec[int, 4](...)\nlst = v.scatter()  # List[int] of length 4\nprint(lst)         # e.g. [1, 1, 1, 1]\n```\n\n### Printing vectors\n\nVectors can be printed directly:\n\n``` python\nprint(v)  # e.g. \"<1,1,1,1>\"\n```\n"
  },
  {
    "path": "docs/start/changelog.md",
    "content": "Below you can find release notes for each major Codon release,\nlisting improvements, updates, optimizations and more for each\nnew version.\n\nThese release notes generally do not include small bug fixes. See the\n[closed issues](https://github.com/exaloop/codon/issues?q=is%3Aissue+is%3Aclosed)\nfor more information.\n\n## v0.19\n\n### New type checking engine\n\n- New type checker implementation that improves Python coverage.\n- Class fields are now inferred automatically.\n- Functions and classes no longer need forward declarations.\n- Functions can be passed around and stored more freely (e.g. list of\n  lambda functions is now possible).\n- More informative and helpful error messages.\n\n### Backend updates\n\n- Upgraded to LLVM 20 (from 17).\n- Upgraded to LLVM 20's OpenMP.\n- Various improvements to backend codegen which result in better\n  performance across the board.\n\n### Quality-of-life and other updates\n\n- Added support for `else` on `try`-statements.\n- Updated semantics of `nonlocal` variables to match Python.\n- Added broader support for Python's format strings.\n- Improved compilation time.\n\n## v0.18\n\n### License change\n\n- Codon is now truly open source under the Apache license.\n- Exaloop continues to offer enterprise licenses with added support, services\n  and custom solutions for organizations that want and need them. Contact\n  [info@exaloop.io](mailto:info@exaloop.io) to learn more.\n\n### New Codon-native NumPy implementation\n\n- New NumPy implementation for Codon, written in Codon itself.\n- Interoperable with Codon's multithreading and GPU backends.\n- NumPy-specific compiler optimizations (e.g. operator fusion optimizations)\n  added to Codon's standard optimization suite.\n- Learn more in the [Codon-NumPy docs](../libraries/numpy.md).\n\n### New compiler options\n\n- `-fast-math` will enable [fast-math optimizations](https://llvm.org/docs/LangRef.html#fast-math-flags).\n  Use this flag with caution as it changes floating-point semantics.\n\n## v0.17\n\n### LLVM upgrade\n\nUpgraded to LLVM 17 (from 15).\n\n### Standard library updates\n\n- New floating-point types `float16`, `bfloat16` and `float128`.\n- Updates to several existing functions, such as adding `key` and\n  `default` arguments to `min()` and `max()`.\n- Slice arguments can now be of any type, not just `int`.\n- Added `input()` function.\n\n### Other improvements\n\n- Property setters are now supported.\n- Updated import logic to match CPython's more closely.\n- Several improvements to dynamic polymorphism to match CPython more\n  closely.\n\n### New compiler options\n\n- `-disable-exceptions` will disable exceptions, potentially eliding\n  various runtime checks (e.g. bounds checks for lists). This flag\n  should only be used if you know that no exceptions will be raised\n  in the given program.\n\n## v0.16\n\n### Python extensions\n\nA new build mode is added to `codon` called `pyext` which compiles\nto Python extension modules, allowing Codon code to be imported and\ncalled directly from Python (similar to Cython). Please see the\n[docs](../integrations/python/extensions.md) for more information and usage\nexamples.\n\n### Standard library updates\n\n- Various additions to the standard library, such as `math.fsum()` and\n  the built-in `pow()`.\n\n- Added `complex64`, which is a complex number with 32-bit float real and\n  imaginary components.\n\n- Better `Int[N]` and `UInt[N]` support: can now convert ints wider than\n  64-bit to string; now supports more operators.\n\n### More Python-specific optimizations\n\nNew optimizations for specific patterns including `any()`/`all()` and\nmultiple list concatenations. These patterns are now recognized and\noptimized in Codon's IR.\n\n### Static expressions\n\nCodon now supports more compile-time static functions, such as `staticenumerate`.\n\n## v0.15\n\n### Union types\n\nCodon adds support for union types (e.g., `Union[int, float]`):\n\n```\ndef foo(cmd) -> Union:\n    if cmd == 'int': return 1\n    else: return \"s\"\nfoo('int')        # type is Union[int,str]\n5 + foo('int')    # 6\n'a' + foo('str')  # as\n```\n\n### Dynamic inheritance\n\nDynamic inheritance and polymorphism are now supported:\n\n```\nclass A:\n    def __repr__(): return 'A'\nclass B(A):\n    def __repr__(): return 'B'\nl = [A(), B(), A()]  # type of l is List[A]\nprint(l)  # [A, B, A]\n```\n\nThis feature is still a work in progress.\n\n### LLVM upgrade\n\nUpgraded to LLVM 15 (from 12). Note that LLVM 15 now uses\n[opaque pointers](https://llvm.org/docs/OpaquePointers.html),\ne.g. `ptr` instead of `i8*` or `i64*`, which affects `@llvm`\nfunctions written in Codon as well as LLVM IR output of\n`codon build`.\n\n### Standard library\n\n`random` module now matches Python exactly for the same seed.\n\n## v0.14\n\n### GPU support\n\nGPU kernels can now be written and called in Codon. Existing\nloops can be parallelized on the GPU with the `@par(gpu=True)`\nannotation. Please see the [docs](../parallel/gpu.md) for\nmore information and examples.\n\n### Semantics\n\nAdded `-numerics` flag, which specifies semantics of various\nnumeric operations:\n\n- `-numerics=c` (default): C semantics; best performance\n- `-numerics=py`: Python semantics (checks for zero divisors\n  and raises `ZeroDivisionError`, and adds domain checks to `math`\n  functions); might slightly decrease performance.\n\n### Types\n\nAdded `float32` type to represent 32-bit floats (equivalent to C's\n`float`). All `math` functions now have `float32` overloads.\n\n### Parallelism\n\nAdded `collapse` option to `@par`:\n\n``` python\n@par(collapse=2)  # parallelize entire iteration space of 2 loops\nfor i in range(N):\n    for j in range(N):\n        do_work(i, j)\n```\n\n### Standard library\n\nAdded `collections.defaultdict`.\n\n### Python interoperability\n\nVarious Python interoperability improvements: can now use `isinstance`\non Python objects/types and can now catch Python exceptions by name.\n\n## v0.13\n\n### Language\n\n#### Scoping\n\nScoping was changed to match Python scoping. For example:\n\n``` python\nif condition:\n    x = 42\n\nprint(x)\n```\n\nIf condition is `False`, referencing `x` causes a `NameError`\nto be raised at runtime, much like what happens in Python.\nThere is zero new performance overhead for code using the old\nscoping; code using the new scoping as above generates a flag to\nindicate whether the given variable has been assigned.\n\nMoreover, variables can now be assigned to different types:\n\n``` python\nx = 42\nprint(x)  # 42\nx = 'hello'\nprint(x)  # hello\n```\n\nThe same applies in Jupyter or JIT environments.\n\n#### Static methods\n\nAdded support for `@staticmethod` method decorator.\nClass variables are also supported:\n\n``` python\nclass Cls:\n    a = 5  # or \"a: ClassVar[int] = 5\" (PEP 526)\n\n    @staticmethod\n    def method():\n        print('hello world')\n\nc = Cls()\nCls.a, Cls.method(), c.a, c.method()  # supported\n```\n\n#### Tuple handling\n\nArbitrary classes can now be converted to tuples via the `tuple()`\nfunction.\n\n#### Void type\n\nThe `void` type has been completely removed in favor of the new\nand Pythonic `NoneType`, which compiles to an empty LLVM struct.\nThis does not affect C interoperability as the empty struct type\nis replaced by `void` by LLVM.\n\n#### Standard library\n\nThe `re` module is now fully supported, and uses\n[Google's `re2`](https://github.com/google/re2) as a backend. Future\nversions of Codon will also include an additional regex optimization\npass to compile constant (\"known at compile time\") regular expressions\nto native code.\n\n### C variables\n\nGlobal variables with C linkage can now be imported via `from C import`:\n\n``` python\n# assumes the C variable \"long foo\"\nfrom C import foo: int\nprint(foo)\n```\n\n### Parallelism\n\nNumerous improvements to the OpenMP backend, including the addition\nof task-based reductions:\n\n``` python\ntotal = 0\n@par\nfor a in some_arbitrary_generator():\n    total += do_work(a)  # now converted to task reduction\n```\n\n### Python interoperability\n\nIncluded revamped `codon` module for Python, with `@codon.jit` decorator\nfor compiling Python code in existing codebases. Further improved and\noptimized the Python bridge. Please see the\n[docs](../integrations/python/codon-from-python.md) for more information.\n\n### Codon IR\n\nNew capture analysis pass for Codon IR for improving tasks such as dead\ncode elimination and side effect analysis. This allows Codon IR to deduce\nwhether arbitrary, compilable Python expressions have side effects, capture\nvariables, and more.\n\n### Code generation and optimizations\n\nA new dynamic allocation optimization pass is included, which 1)\nremoves unused allocations (e.g. instantiating a class but never\nusing it) and 2) demotes small heap allocations to stack (`alloca`)\nallocations when possible. The latter optimization can frequently\nremove any overhead associated with instantiating most classes.\n\n### Command-line tool\n\nThe `codon` binary can now compile to shared libraries using the `-lib`\noption to `codon build` (or it can be deduced from a `.so` or `.dylib`\nextension on the output file name).\n\n### Errors\n\nAdded support for multiple error reporting.\n"
  },
  {
    "path": "docs/start/faq.md",
    "content": "## Technical\n\n### What is Codon?\n\nCodon is a high-performance Python compiler that compiles Python code to native machine code\nwithout any runtime overhead. Typical speedups over Python are on the order of 10-100x or more,\non a single thread. Codon's performance is typically on par with that of C/C++. Unlike Python,\nCodon supports native multithreading, which can lead to speedups many times higher still.\nCodon is extensible via a plugin infrastructure, which lets you incorporate new libraries,\ncompiler optimizations and even keywords.\n\n### What isn't Codon?\n\nWhile Codon supports nearly all of Python's syntax, it is not a drop-in replacement, and large\ncodebases might require modifications to be run through the Codon compiler. For example, some\nof Python's modules are not yet implemented within Codon, and a few of Python's dynamic features\nare disallowed. The Codon compiler produces detailed error messages to help identify and resolve\nany incompatibilities. Codon supports seamless [Python interoperability](../integrations/python/python-from-codon.md) to\nhandle cases where specific Python libraries or dynamism are required, and also supports writing\n[Python extension modules](../integrations/python/extensions.md) that can be imported and used\nfrom larger Python codebases.\n\n### Why Codon?\n\nPython is arguably the world's most popular programming language, and is gradually becoming the\n*lingua franca* particularly amongst non-technical or non-CS practitioners in numerous fields.\nIt provides a readable, clean syntax, is easy to learn, and has an unmatched ecosystem of libraries.\nHowever, Python's achilles heel has always been performance: a typical codebase in pure Python is\norders of magnitude slower than its C/C++/Rust counterpart.\n\nCodon bridges the gap between Python's simplicity and ease-of-use, and the performance of low-level\nlanguages like C++ or Rust, by using [novel compiler and type checking techniques](https://dl.acm.org/doi/abs/10.1145/3578360.3580275)\nto statically compile code ahead-of-time, avoiding all of vanilla Python's runtime overhead and\nperformance drawbacks.\n\n### How does Codon compare to...\n\n- **CPython?** Codon tries to follow CPython's syntax, semantics and APIs as\n  closely as possible, aside from a few cases where Codon differs from CPython for\n  performance reasons (one example being Codon's 64-bit `int` vs. CPython's arbitrary-\n  width `int`). Performance-wise, speedups over CPython are usually on the order of 10-100x.\n\n- **Numba?** While Codon does offer a JIT decorator similar to Numba's, Codon is in\n  general an ahead-of-time compiler that compiles end-to-end programs to native code.\n  It also supports compilation of a much broader set of Python constructs and libraries.\n\n- **PyPy?** PyPy strives to effectively be a drop-in replacement for CPython, whereas\n  Codon differs in a few places in order to eliminate any dynamic runtime or virtual\n  machine, and thereby attain much better performance.\n\n- **Cython?** Like Cython, Codon has a [Python-extension build mode](../integrations/python/extensions.md) that\n  compiles to Python extension modules, allowing Codon-compiled code to be imported and called\n  from plain Python.\n\n- **C++?** Codon often generates the same code as an equivalent C or C++ program. Codon\n  can sometimes generate *better* code than C/C++ compilers for a variety of reasons, such\n  as better container implementations, the fact that Codon does not use object files and\n  inlines all library code, or Codon-specific compiler optimizations that are not performed\n  with C or C++.\n\n- **Julia?** Codon's compilation process is actually much closer to C++ than to Julia. Julia\n  is a dynamically-typed language that performs type inference as an optimization, whereas\n  Codon type checks the entire program ahead of time. Codon also tries to circumvent the learning\n  curve of a new language by adopting Python's syntax and semantics.\n\n- **Mojo?** Mojo strives to add low-level programming support/features to the Python language,\n  while also supporting the rest of Python by relying on CPython. By contrast, Codon aims to\n  make Python itself more performant by using new type checking and compilation techniques,\n  without trying to be a superset or drop-in replacement. Codon tries to minimize new syntax\n  and language features with respect to Python.\n\nYou can see results from [Codon's benchmark suite](https://github.com/exaloop/codon/tree/develop/bench)\nsuite at [exaloop.io/#benchmarks](https://exaloop.io/#benchmarks).\nMore benchmarks can be found in the [2019 paper](https://dl.acm.org/doi/10.1145/3360551)\non bioinformatics-specific use cases (note that the name used in that paper is that of Codon's predecessor,\n\"Seq\").\n\n### I want to use Codon, but I have a large Python codebase I don't want to port.\n\nYou can use Codon on a per-function basis via the [`@codon.jit` decorator](../integrations/python/codon-from-python.md),\nwhich can be used within Python codebases. This will compile only the annotated functions\nand automatically handle data conversions to and from Codon. It also allows for\nthe use of any Codon-specific modules or extensions, such as multithreading.\n\nCodon can also [compile to Python extension modules](../integrations/python/extensions.md) that can be\nimported and used from Python.\n\n### What about interoperability with other languages and frameworks?\n\nInteroperability is and will continue to be a priority for Codon.\nWe don't want using Codon to render you unable to use all the other great frameworks and\nlibraries that exist. Codon supports full interoperability with Python and C/C++.\n\n### Does Codon use garbage collection?\n\nYes, Codon uses the [Boehm garbage collector](https://github.com/ivmai/bdwgc).\n\n### Codon doesn't support Python module X or function Y.\n\nWhile Codon covers a sizeable subset of Python's standard library, it does not yet cover\nevery function from every module. Note that missing functions can still be called through\nPython via `from python import`. Many of the functions that lack Codon-native implementations\n(e.g. I/O or OS related functions) will generally also not see substantial speedups from Codon.\n\n### Codon is no faster than Python for my application.\n\nApplications that spend most of their time in C-implemented library code generally do not\nsee substantial performance improvements in Codon. Similarly, applications that are I/O or\nnetwork-bound will have the same bottlenecks in Codon.\n\n### Codon is slower than Python for my application.\n\nPlease report any cases where Codon is noticeably slower than Python as bugs on our\n[issue tracker](https://github.com/exaloop/codon/issues).\n\n## Usage\n\n### Is Codon free and open source?\n\nYes, Codon is free and open source under the [Apache License, Version 2.0](https://www.apache.org/licenses/LICENSE-2.0).\nExaloop offers enterprise and custom solutions on top of Codon for a variety of applications, use cases and\nindustries; please email [info@exaloop.io](mailto:info@exaloop.io) to learn more.\n\n## Contributing\n\n### Does Codon accept outside contributions?\n\nAbsolutely, we'd be delighted to accept any contributions in the form of issues, bug reports,\nfeature requests or pull requests.\n\n### I want to contribute. Where do I start?\n\nIf you have a specific feature or use case in mind, here is a quick breakdown of the codebase\nto help provide a sense of where to look first:\n\n- [`codon/`](https://github.com/exaloop/codon/tree/develop/codon): compiler code\n  - [`codon/parser/`](https://github.com/exaloop/codon/tree/develop/codon/parser):\n    parser and type checker code: this is the first step of compilation\n  - [`codon/cir/`](https://github.com/exaloop/codon/tree/develop/codon/cir):\n    Codon IR and optimizations: the second step of compilation\n  - [`codon/cir/llvm/`](https://github.com/exaloop/codon/tree/develop/codon/cir/llvm):\n    conversion from Codon IR to LLVM IR and machine code: the last step of compilation\n  - [`codon/runtime/`](https://github.com/exaloop/codon/tree/develop/codon/runtime):\n    runtime library: used during execution\n- [`stdlib/`](https://github.com/exaloop/codon/tree/develop/stdlib): standard library code\n\nYou can also take a look at some of the [open issues](https://github.com/exaloop/codon/issues). If you\nhave any question or suggestions, please feel free to ask in [the forum](https://github.com/exaloop/codon/discussions).\n\n### Is there a Contributor License Agreement (CLA)?\n\nYes, there is a CLA that is required to be agreed to before any pull requests are merged.\nPlease see [exaloop.io/legal/cla](https://exaloop.io/legal/cla) for more information. To agree to\nthe CLA, send an email with your GitHub username to [info@exaloop.io](mailto:info@exaloop.io).\n"
  },
  {
    "path": "docs/start/install.md",
    "content": "## Install Codon\n\n=== \":simple-gnometerminal: &nbsp; Command-line tool\"\n\n    Use this command to install the Codon CLI that can be used to compile and\n    run programs from the command line:\n\n    ``` bash\n    /bin/bash -c \"$(curl -fsSL https://exaloop.io/install.sh)\"\n    ```\n\n    Follow the prompts to add the `codon` command to your path.\n\n=== \":simple-python: &nbsp; Python package\"\n\n    Use this command to install the `codon` Python package, which can be used to\n    compile functions in an existing Python codebase:\n\n    ``` bash\n    pip install codon-jit\n    ```\n\n    With this package installed, you can use the `@codon.jit` decorator.\n    [Learn more &#x2192;](../integrations/python/codon-from-python.md)\n\n!!! info\n\n    Codon is supported natively on :fontawesome-brands-linux: Linux and :fontawesome-brands-apple: macOS.\n    If you are using :fontawesome-brands-windows: Windows,\n    we recommend using Codon through [WSL](https://learn.microsoft.com/en-us/windows/wsl/about).\n\n## Run your first program\n\nWith the `codon` command installed, we can compile and run a simple hello-world program:\n\n``` python\nprint('Hello, World!')\n```\n\nIf we save this simple program to a file called `hello.py`, we can compile and run with\nthe `codon run` subcommand:\n\n``` bash\ncodon run hello.py\n```\n\nwhich prints the `Hello, World!` message as expected.\n\n## Enable optimizations\n\nBy default, `codon` runs programs without optimizations enabled. You can enable\noptimizations with the `-release` flag. Let's look at a slightly more involved example\nto see the effect of this flag:\n\n``` python\nfrom time import time\n\ndef fib(n):\n    return n if n < 2 else fib(n - 1) + fib(n - 2)\n\nt0 = time()\nans = fib(40)\nt1 = time()\nprint(f'Computed fib(40) = {ans} in {t1 - t0} seconds.')\n```\n\nThis program computes the 40th Fibonacci number using simple recursion. We can run\nthe program with optimizations enabled like this:\n\n``` bash\ncodon run -release fib.py\n```\n\nLet's see what happens when we run this program using Python and Codon without optimizations[^1]:\n\n<div style=\"text-align: center;\" markdown=\"1\">\n| Command                      | Time taken (sec.) | Speedup    |\n| ---------------------------- | ----------------: | ---------: |\n| `python3.13 fib.py`          |             14.26 |  1$\\times$ |\n| `codon run fib.py`           |             0.43  | 33$\\times$ |\n| `codon run -release fib.py`  |             0.31  | 46$\\times$ |\n</div>\n\nThat's a 46$\\times$ speedup from using Codon with the `-release` flag!\n\n[^1]: Times were measured on an M1 MacBook Pro.\n"
  },
  {
    "path": "docs/start/usage.md",
    "content": "The `codon` command includes several subcommands for compiling\nand executing code.\n\n## `codon run`\n\n`codon run` will compile and execute the provided program:\n\n``` bash\ncodon run file.py           # compile and run (defaults to debug mode)\ncodon run -debug file.py    # compile and run in debug mode\ncodon run -release file.py  # compile and run with optimizations\n```\n\nIf the file is given as `-`, then the program is read from standard\ninput:\n\n``` bash\necho 'print(\"hello\")' | codon run -release -\n# hello\n```\n\nProgram arguments can be provided after the file, as in:\n\n``` bash\ncodon run -release file.py arg1 arg2 arg3\n```\n\nFor example:\n\n``` bash\necho 'import sys; print(sys.argv)' | codon run -release - arg1 arg2 arg3\n# ['-', 'arg1', 'arg2', 'arg3']\n```\n\n## `codon build`\n\n`codon build` will compile to a desired output type, be it an executable, object file,\nshared library, LLVM IR or Python extension. The `-release` and `-debug` flags also apply\nto `codon build` in the same way as described from `codon run` above.\n\nThe optional `-o <file>` parameter can be used to specify the output file. If no output\ntype is specified, the output type is determined from the file name provided to `-o`. If\nno output file name is specified, the output file name is derived from the input file name,\nin combination with the output type (e.g. compiling `foo.py` to an object file results in\noutput file `foo.o`).\n\n### Compile to executable\n\nThe `-exe` flag can be used to generate an executable:\n\n``` bash\n# compile 'program.py' to executable 'program'\ncodon build -exe program.py\n\n# compile 'program.py' to executable 'hello'\ncodon build -exe -o hello program.py\n\n# compile 'program.py' to executable 'hello' with optimizations\ncodon build -exe -o hello -release program.py\n\n# compile 'program.py' to executable 'hello' with optimizations\n# '-exe' is inferred from `-o` argument\ncodon build -o hello -release program.py\n```\n\nCodon uses a C++ compiler to link the actual executable after compilation.\nExtra linker flags can be passed with the `-linker-flags` argument. For example:\n\n``` bash\n# includes /foo/bar in the executable's rpath\ncodon build -release -linker-flags '-Wl,-rpath,/foo/bar' program.py\n```\n\nMultiple linker flags can be passed by separating them with a space in the argument\nto `-linker-flags`.\n\n### Compile to shared library\n\nThe `-lib` flag can be used to generate a shared library:\n\n``` bash\n# compile 'program.py' to shared library 'program.so'\ncodon build -lib program.py\n\n# compile 'program.py' to shared library 'hello.so'\ncodon build -lib -o hello.so program.py\n\n# compile 'program.py' to shared library 'hello.so' with optimizations\ncodon build -lib -o hello.so -release program.py\n\n# compile 'program.py' to shared library 'hello.so' with optimizations\n# '-lib' is inferred from `-o` argument\ncodon build -o hello.so -release program.py\n```\n\nThe `-linker-flags` flag described above also applies when compiling to\na shared library.\n\n!!! info\n\n    When compiling to a shared library, the program's main code will be\n    included and executed when the library is loaded as a *global constructor*.\n    This allows you to include any necessary initialization code in the\n    main program. This behavior can be controlled via the `-global-ctor=<yes|no|auto>`\n    flag, which enables e.g. global constructor creation when compiling to other\n    output types like object files.\n\n!!! info\n\n    If you intend to call a Codon-generated shared library from C or C++,\n    be sure to mark relevant functions with `@export` to ensure they are\n    made visible by the linker. Exported functions can be called as regular\n    C functions (i.e. they follow the C ABI).\n    [Learn more &#x2192;](../integrations/cpp/codon-from-cpp.md)\n\n### Compile to object file\n\nThe `-obj` flag can be used to generate an object file:\n\n``` bash\n# compile 'program.py' to object file 'program.o'\ncodon build -obj program.py\n\n# compile 'program.py' to object file 'hello.o'\ncodon build -obj -o hello.o program.py\n\n# compile 'program.py' to object file 'hello.o' with optimizations\ncodon build -obj -o hello.o -release program.py\n\n# compile 'program.py' to object file 'hello.o' with optimizations\n# '-obj' is inferred from `-o` argument\ncodon build -o hello.o -release program.py\n```\n\n### Compile to assembly code\n\nThe `-asm` flag can be used to generate assembly code:\n\n``` bash\n# compile 'program.py' to assembly file 'program.s'\ncodon build -asm program.py\n\n# compile 'program.py' to assembly file 'hello.o'\ncodon build -asm -o hello.s program.py\n\n# compile 'program.py' to assembly file 'hello.s' with optimizations\ncodon build -asm -o hello.s -release program.py\n\n# compile 'program.py' to assembly file 'hello.s' with optimizations\n# '-asm' is inferred from `-o` argument\ncodon build -o hello.s -release program.py\n```\n\n### Compile to LLVM IR\n\nThe `-llvm` flag can be used to generate LLVM IR:\n\n``` bash\n# compile 'program.py' to LLVM IR file 'program.ll'\ncodon build -llvm program.py\n\n# compile 'program.py' to LLVM IR file 'hello.ll'\ncodon build -llvm -o hello.ll program.py\n\n# compile 'program.py' to LLVM IR file 'hello.ll' with optimizations\ncodon build -llvm -o hello.ll -release program.py\n\n# compile 'program.py' to LLVM IR file 'hello.ll' with optimizations\n# '-llvm' is inferred from `-o` argument\ncodon build -o hello.ll -release program.py\n```\n\n### Compile to Python extension\n\nThe `-pyext` flag can be used to generate a Python extension:\n\n``` bash\n# compile 'program.py' to Python extension 'program.o'\ncodon build -pyext program.py\n\n# compile 'program.py' to Python extension 'hello.o'\ncodon build -pyext -o hello.o program.py\n\n# compile 'program.py' to Python extension 'hello.o' with optimizations\ncodon build -pyext -o hello.o -release program.py\n\n# compile 'program.py' to Python extension 'hello.o' with module\n# name 'mymodule' and optimizations enabled\ncodon build -pyext -o hello.o -release -module mymodule program.py\n```\n\n!!! info\n\n    When using `-pyext`, you will also often want to use the `--relocation-model=pic`\n    flag to generate position-independent code.\n\n## `codon jit`\n\nCodon provides a debugging interface for its JIT compilation capabilities through the\n`codon jit` subcommand. This subcommand uses the same JIT engine internally as used by\nCodon's [Python JIT decorator](../integrations/python/codon-from-python.md) and\n[Jupyter kernel](../integrations/jupyter.md).\nHowever, it is intended to be used as a debugging utility rather than as a general usage mode.\n\n!!! warning\n\n    `codon jit` is intended to be used as a debugging utility for Codon's JIT compilation\n    capabilities. The interface may change between Codon versions.\n\n`codon jit` can be passed a file name to read from, or `-` to read from standard input.\nFor example:\n\n```bash\necho 'print(\"hello world\")' | codon jit -\n# >>> Codon JIT v0.19.0 <<<\n# hello world\n# [done]\n```\n\nIt can also be used as a REPL if no file is provided. JIT inputs can be separated with the string `#%%`.\n\n## Using Codon in an existing Python codebase\n\nCodon provides a Python package called `codon-jit` that can be installed with `pip`. This package\nsupports JIT compilation on a per-function basis within an existing Python codebase.\n\nLearn more in the [Python JIT docs](../integrations/python/codon-from-python.md).\n\n## Additional options\n\n### Disabling exceptions\n\nBy default, Codon does exception handling to match Python's semantics and behavior. If you\nknow your program does not raise or catch exceptions, they can be disabled altogether with\nthe `-disable-exceptions` flag.\n\n`-disable-exceptions` can lead to (sometimes substantial) performance improvements by enabling\nthe compiler to deduce additional information about the semantics of the program. For example,\nif the compiler can deduce that there will be no index errors in a loop that iterates over an\narray, it can potentially use that information to perform vectorization or other optimizations.\n\nIn some contexts, exceptions are disabled automatically by Codon, such as when compiling for\nGPU execution.\n\n### Numerical semantics\n\nFor performance reasons, certain numerical operations in Codon follow C semantics by default.\nFor example, integer division rounds towards zero in Codon whereas it rounds down in Python:\n\n``` python\nprint((-3) // 2)\n# Codon: -1\n# Python: -2\n```\n\nSimilarly, floating-point division by zero returns `inf` in Codon whereas it raises an exception\nin Python:\n\n``` python\nprint(1.0 / 0.0)\n# Codon: inf\n# Python: 'ZeroDivisionError: float division by zero'\n```\n\nThe `-numerics=<mode>` flag can be used to control this behavior:\n\n- `-numerics=c` (default): C semantics, as above\n- `-numerics=py`: Python semantics, matching Python behavior at the cost of performance\n\n### Fast-math\n\n[Fast-math optimizations](https://llvm.org/docs/LangRef.html#fast-math-flags) can be enabled with\nthe `-fast-math` flag. Note that this flag makes various assumptions about `nan` and `inf` values,\nso it is best to use it with caution.\n\n### Disabling optimization passes\n\nThe `-disable-opt <pass>` flag can be used to disable specific optimization passes. For example:\n\n``` bash\n# compile & run with optimizations, but don't perform NumPy fusion optimization\ncodon run -release -disable-opt core-numpy-fusion program.py\n```\n\n## Compile-time definitions\n\nLiteral variables can be passed on the command-line via the `-D` flag. These variables are\ntreated as compile-time constants and can be used for [compile-time metaprogramming](../language/meta.md).\n\nFor example, the following code:\n\n``` python\nn = Int[N](42)\nprint(n * n)\n```\n\ncan be executed with:\n\n``` bash\ncodon run -DN=16 program.py\n```\n\nto use a 16-bit integer as the type of variable `n`.\n\n## Logging\n\nCodon can display logging information and also output intermediate compilation results via the\n`-log <streams>` command. The argument to `-log` can contain any of the following characters:\n\n- `t` (time): Displays timings for various stages of the compilation process.\n- `T` (typecheck): Enables logging during type checking\n- `i` (IR): Enables logging during Codon IR passes\n- `l` (dump): Dumps intermediate compilation results, including AST, Codon IR and LLVM IR\n"
  },
  {
    "path": "jit/.gitignore",
    "content": "dist\ncodon/stdlib\ncodon/jit.cpp\n"
  },
  {
    "path": "jit/MANIFEST.in",
    "content": "include codon/*.pxd\n"
  },
  {
    "path": "jit/README.md",
    "content": "To install:\n\n```bash\n$ pip install .\n```\n\nIf Codon is installed in non-standard directory, please set `CODON_DIR` accordingly.\n\nTo use:\n\n```python\nimport codon\n\n@codon.jit\ndef ...\n```\n"
  },
  {
    "path": "jit/codon/__init__.py",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n__all__ = [\n    \"jit\", \"convert\", \"JITError\", \"JITWrapper\", \"_jit_register_fn\", \"_jit\"\n]\n\nfrom .decorator import jit, convert, execute, JITError, JITWrapper, _jit_register_fn, _jit_callback_fn, _jit\n\n__codon__ = False\n"
  },
  {
    "path": "jit/codon/decorator.py",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom argparse import ArgumentError\nimport ctypes\nimport inspect\nimport sys\nimport os\nimport functools\nimport itertools\nimport ast\nimport textwrap\nimport astunparse\nimport numpy as np\nfrom pathlib import Path\n\nsys.setdlopenflags(sys.getdlopenflags() | ctypes.RTLD_GLOBAL)\n\nfrom .codon_jit import JITWrapper, JITError, codon_library\n\nif \"CODON_PATH\" not in os.environ:\n    codon_path = []\n    codon_lib_path = codon_library()\n    if codon_lib_path:\n        codon_path.append(Path(codon_lib_path).parent / \"stdlib\")\n    codon_path.append(\n        Path(os.path.expanduser(\"~\")) / \".codon\" / \"lib\" / \"codon\" / \"stdlib\")\n    for path in codon_path:\n        if path.exists():\n            os.environ[\"CODON_PATH\"] = str(path.resolve())\n            break\n    else:\n        raise RuntimeError(\"Cannot locate Codon. Please install Codon or set CODON_PATH.\")\n\ndebug_override = int(os.environ.get(\"CODON_JIT_DEBUG\", 0))\n\npod_conversions = {\n    type(None): \"pyobj\",\n    int: \"int\",\n    float: \"float\",\n    bool: \"bool\",\n    str: \"str\",\n    complex: \"complex\",\n    slice: \"slice\",\n    np.bool_: \"bool\",\n    np.int8: \"i8\",\n    np.uint8: \"u8\",\n    np.int16: \"i16\",\n    np.uint16: \"u16\",\n    np.int32: \"i32\",\n    np.uint32: \"u32\",\n    np.int64: \"int\",\n    np.uint64: \"u64\",\n    np.float16: \"float16\",\n    np.float32: \"float32\",\n    np.float64: \"float\",\n    np.complex64: \"complex64\",\n    np.complex128: \"complex\",\n}\n\ncustom_conversions = {}\n_error_msgs = set()\n\ndef _common_type(t, debug, sample_size):\n    sub, is_optional = None, False\n    for i in itertools.islice(t, sample_size):\n        if i is None:\n            is_optional = True\n        else:\n            s = _codon_type(i, debug=debug, sample_size=sample_size)\n            if sub and sub != s:\n                return \"pyobj\"\n            sub = s\n    if is_optional and sub and sub != \"pyobj\":\n        sub = \"Optional[{}]\".format(sub)\n    return sub if sub else \"pyobj\"\n\ndef _codon_type(arg, **kwargs):\n    t = type(arg)\n\n    s = pod_conversions.get(t, \"\")\n    if s:\n        return s\n    if issubclass(t, list):\n        return \"List[{}]\".format(_common_type(arg, **kwargs))\n    if issubclass(t, set):\n        return \"Set[{}]\".format(_common_type(arg, **kwargs))\n    if issubclass(t, dict):\n        return \"Dict[{},{}]\".format(_common_type(arg.keys(), **kwargs),\n                                    _common_type(arg.values(), **kwargs))\n    if issubclass(t, tuple):\n        return \"Tuple[{}]\".format(\",\".join(\n            _codon_type(a, **kwargs) for a in arg))\n    if issubclass(t, np.ndarray):\n        if arg.dtype == np.bool_:\n            dtype = \"bool\"\n        elif arg.dtype == np.int8:\n            dtype = \"i8\"\n        elif arg.dtype == np.uint8:\n            dtype = \"u8\"\n        elif arg.dtype == np.int16:\n            dtype = \"i16\"\n        elif arg.dtype == np.uint16:\n            dtype = \"u16\"\n        elif arg.dtype == np.int32:\n            dtype = \"i32\"\n        elif arg.dtype == np.uint32:\n            dtype = \"u32\"\n        elif arg.dtype == np.int64:\n            dtype = \"int\"\n        elif arg.dtype == np.uint64:\n            dtype = \"u64\"\n        elif arg.dtype == np.float16:\n            dtype = \"float16\"\n        elif arg.dtype == np.float32:\n            dtype = \"float32\"\n        elif arg.dtype == np.float64:\n            dtype = \"float\"\n        elif arg.dtype == np.complex64:\n            dtype = \"complex64\"\n        elif arg.dtype == np.complex128:\n            dtype = \"complex\"\n        elif arg.dtype.name.startswith(\"datetime64[\"):\n            units, step = np.datetime_data(arg.dtype)\n            dtype = \"np.datetime64['{}',{}]\".format(units, step)\n        elif arg.dtype.name.startswith(\"timedelta64[\"):\n            units, step = np.datetime_data(arg.dtype)\n            dtype = \"np.timedelta64['{}',{}]\".format(units, step)\n        else:\n            return \"pyobj\"\n        return \"np.ndarray[{},{}]\".format(dtype, arg.ndim)\n\n    s = custom_conversions.get(t, \"\")\n    if s:\n        j = \",\".join(\n            _codon_type(getattr(arg, slot), **kwargs) for slot in t.__slots__)\n        return \"{}[{}]\".format(s, j)\n\n    debug = kwargs.get(\"debug\", 0)\n    if debug > 0:\n        msg = \"cannot convert \" + t.__name__\n        if msg not in _error_msgs:\n            print(\"[python]\", msg, file=sys.stderr)\n            _error_msgs.add(msg)\n    return \"pyobj\"\n\ndef _codon_types(args, **kwargs):\n    return tuple(_codon_type(arg, **kwargs) for arg in args)\n\ndef _reset_jit():\n    global _jit\n    _jit = JITWrapper()\n    init_code = (\n        \"from internal.python import \"\n        \"setup_decorator, PyTuple_GetItem, PyObject_GetAttrString\\n\"\n        \"setup_decorator()\\n\"\n        \"import numpy as np\\n\"\n        \"import numpy.pybridge\\n\"\n    )\n    if debug_override == 2:\n        print(f\"[jit_debug] execute:\\n{init_code}\", file=sys.stderr)\n    _jit.execute(init_code, \"\", 0, int(debug_override > 0))\n    return _jit\n\n_jit = _reset_jit()\n\nclass RewriteFunctionArgs(ast.NodeTransformer):\n    def __init__(self, args):\n        self.args = args\n\n    def visit_FunctionDef(self, node):\n        for a in self.args:\n            node.args.args.append(ast.arg(arg=a, annotation=None))\n        return node\n\ndef _obj_to_str(obj, **kwargs) -> str:\n    if inspect.isclass(obj):\n        lines = inspect.getsourcelines(obj)[0]\n        extra_spaces = lines[0].find(\"class\")\n        obj_str = \"\".join(l[extra_spaces:] for l in lines)\n        obj_name = obj.__name__\n    elif callable(obj) or isinstance(obj, str):\n        is_str = isinstance(obj, str)\n        lines = [i + '\\n' for i in obj.split('\\n')\n                 ] if is_str else inspect.getsourcelines(obj)[0]\n        if not is_str:\n            lines = lines[1:]\n        obj_str = textwrap.dedent(''.join(lines))\n\n        pyvars = kwargs.get(\"pyvars\", None)\n        if pyvars:\n            for i in pyvars:\n                if not isinstance(i, str):\n                    raise ValueError(\"pyvars only takes string literals\")\n            node = ast.fix_missing_locations(\n                RewriteFunctionArgs(pyvars).visit(ast.parse(obj_str)))\n            obj_str = astunparse.unparse(node)\n        if is_str:\n            try:\n                obj_name = ast.parse(obj_str).body[0].name\n            except:\n                raise ValueError(\"cannot infer function name!\")\n        else:\n            obj_name = obj.__name__\n    else:\n        raise TypeError(\"Function or class expected, got \" +\n                        type(obj).__name__)\n    return obj_name, obj_str.replace(\"_@par\", \"@par\")\n\ndef _parse_decorated(obj, **kwargs):\n    return _obj_to_str(obj, **kwargs)\n\ndef convert(t):\n    if not hasattr(t, \"__slots__\"):\n        raise JITError(\"class '{}' does not have '__slots__' attribute\".format(\n            str(t)))\n\n    name = t.__name__\n    slots = t.__slots__\n    code = (\"@tuple\\n\"\n            \"class \" + name + \"[\" +\n            \",\".join(\"T{}\".format(i) for i in range(len(slots))) + \"]:\\n\")\n    for i, slot in enumerate(slots):\n        code += \"    {}: T{}\\n\".format(slot, i)\n\n    # PyObject_GetAttrString\n    code += \"    def __from_py__(p: cobj):\\n\"\n    for i, slot in enumerate(slots):\n        code += \"        a{} = T{}.__from_py__(PyObject_GetAttrString(p, '{}'.ptr))\\n\".format(\n            i, i, slot)\n    code += \"        return {}({})\\n\".format(\n        name, \", \".join(\"a{}\".format(i) for i in range(len(slots))))\n\n    if debug_override == 2:\n        print(f\"[jit_debug] execute:\\n{code}\", file=sys.stderr)\n    _jit.execute(code, \"\", 0, int(debug_override > 0))\n    custom_conversions[t] = name\n    return t\n\ndef _jit_register_fn(f, pyvars, debug):\n    try:\n        obj_name, obj_str = _parse_decorated(f, pyvars=pyvars)\n        fn, fl = \"<internal>\", 1\n        if hasattr(f, \"__code__\"):\n            fn, fl = f.__code__.co_filename, f.__code__.co_firstlineno\n        if debug == 2:\n            print(f\"[jit_debug] execute:\\n{obj_str}\", file=sys.stderr)\n        _jit.execute(obj_str, fn, fl, int(debug > 0))\n        return obj_name\n    except JITError:\n        _reset_jit()\n        raise\n\ndef _jit_callback_fn(fn,\n                     obj_name,\n                     module,\n                     debug=0,\n                     sample_size=5,\n                     pyvars=None,\n                     *args,\n                     **kwargs):\n    if fn is not None:\n        sig = inspect.signature(fn)\n        bound_args = sig.bind(*args, **kwargs)\n        bound_args.apply_defaults()\n        args = tuple(bound_args.arguments[param] for param in sig.parameters)\n    else:\n        args = (*args, *kwargs.values())\n\n    try:\n        types = _codon_types(args, debug=debug, sample_size=sample_size)\n        if debug > 0:\n            print(\"[python] {}({})\".format(obj_name, list(types)), file=sys.stderr)\n        return _jit.run_wrapper(\n            obj_name, list(types), module, list(pyvars), args, int(debug > 0)\n        )\n    except JITError:\n        _reset_jit()\n        raise\n\ndef _jit_str_fn(fstr, debug=0, sample_size=5, pyvars=None):\n    obj_name = _jit_register_fn(fstr, pyvars, debug)\n\n    def wrapped(*args, **kwargs):\n        return _jit_callback_fn(None, obj_name, \"__main__\", debug, sample_size,\n                                pyvars, *args, **kwargs)\n\n    return wrapped\n\n\ndef jit(fn=None, debug=0, sample_size=5, pyvars=None):\n    if debug is None:\n        debug = 0\n    if not pyvars:\n        pyvars = []\n\n    if not isinstance(pyvars, list):\n        raise ArgumentError(\"pyvars must be a list\")\n\n    if debug_override:\n        debug = debug_override\n\n    if fn and isinstance(fn, str):\n        return _jit_str_fn(fn, debug, sample_size, pyvars)\n\n    def _decorate(f):\n        obj_name = _jit_register_fn(f, pyvars, debug)\n\n        @functools.wraps(f)\n        def wrapped(*args, **kwargs):\n            return _jit_callback_fn(f, obj_name, f.__module__, debug,\n                                    sample_size, pyvars, *args, **kwargs)\n\n        return wrapped\n\n    return _decorate(fn) if fn else _decorate\n\n\ndef execute(code, debug=0):\n    if debug is None:\n        debug = 0\n    if debug_override:\n        debug = debug_override\n    try:\n        if debug == 2:\n            print(f\"[jit_debug] execute:\\n{code}\", file=sys.stderr)\n        _jit.execute(code, \"<internal>\", 0, int(debug))\n    except JITError:\n        _reset_jit()\n        raise\n"
  },
  {
    "path": "jit/codon/jit.pxd",
    "content": "# Copyright (C) 2022-2025 Exaloop Inc. <https://exaloop.io>\n\nfrom libc.stdint cimport int32_t, uint8_t\n\ncdef extern from \"codon/compiler/jit_extern.h\":\n    cdef struct CJITResult:\n        void *result\n        char *error\n\n    void *jit_init(char *name)\n    void jit_exit(void *jit)\n\n    cdef char *get_jit_library()\n\n    cdef CJITResult jit_execute_safe(\n        void *jit, char *code, char *file, int32_t line, uint8_t debug\n    )\n    cdef CJITResult jit_execute_python(\n        void *jit, char *name, char **types, size_t types_size,\n        char *pyModule, char **py_vars, size_t py_vars_size,\n        void *arg, uint8_t debug\n    )\n"
  },
  {
    "path": "jit/codon/jit.pyx",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n# distutils: language=c\n# cython: language_level=3\n# cython: c_string_type=unicode\n# cython: c_string_encoding=utf8\n\ncimport codon.jit\nfrom libc.stdlib cimport malloc, calloc, free\nfrom libc.string cimport strcpy\nfrom libc.stdint cimport int32_t, uint8_t\n\n\nclass JITError(Exception):\n    pass\n\n\ncdef str get_free_str(char *s):\n    cdef bytes py_s\n    try:\n        py_s = s\n        return py_s.decode('utf-8')\n    finally:\n        free(s)\n\n\ncdef class JITWrapper:\n    cdef void* jit\n\n    def __cinit__(self):\n        self.jit = codon.jit.jit_init(b\"codon jit\")\n\n    def __dealloc__(self):\n        codon.jit.jit_exit(self.jit)\n\n    def execute(self, code: str, filename: str, fileno: int, debug) -> str:\n        result = codon.jit.jit_execute_safe(\n            self.jit, code.encode('utf-8'), filename.encode('utf-8'), fileno, <uint8_t>debug\n        )\n        if result.error is NULL:\n            return None\n        else:\n            msg = get_free_str(result.error)\n            raise JITError(msg)\n\n    def run_wrapper(self, name: str, types: list[str], module: str, pyvars: list[str], args, debug) -> object:\n        cdef char** c_types = <char**>calloc(len(types), sizeof(char*))\n        cdef char** c_pyvars = <char**>calloc(len(pyvars), sizeof(char*))\n        if not c_types or not c_pyvars:\n            raise JITError(\"Cython allocation failed\")\n        try:\n            for i, s in enumerate(types):\n                bytes = s.encode('utf-8')\n                c_types[i] = <char*>malloc(len(bytes) + 1)\n                strcpy(c_types[i], bytes)\n            for i, s in enumerate(pyvars):\n                bytes = s.encode('utf-8')\n                c_pyvars[i] = <char*>malloc(len(bytes) + 1)\n                strcpy(c_pyvars[i], bytes)\n\n            result = codon.jit.jit_execute_python(\n                self.jit, name.encode('utf-8'), c_types, len(types),\n                module.encode('utf-8'), c_pyvars, len(pyvars),\n                <void *>args, <uint8_t>debug\n            )\n            if result.error is NULL:\n                return <object>result.result\n            else:\n                msg = get_free_str(result.error)\n                raise JITError(msg)\n        finally:\n            for i in range(len(types)):\n                free(c_types[i])\n            free(c_types)\n            for i in range(len(pyvars)):\n                free(c_pyvars[i])\n            free(c_pyvars)\n\n\ndef codon_library():\n    cdef char* c = codon.jit.get_jit_library()\n    return get_free_str(c)\n"
  },
  {
    "path": "jit/pyproject.toml",
    "content": "[build-system]\nrequires = [\"cython\", \"setuptools\", \"wheel\", \"numpy\"]\n"
  },
  {
    "path": "jit/setup.py",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport os\nimport sys\nimport shutil\nimport subprocess\nfrom pathlib import Path\nfrom Cython.Distutils import build_ext\nfrom setuptools import setup\nfrom setuptools.extension import Extension\n\nexec(open(\"codon/version.py\").read())\n\next = \"dylib\" if sys.platform == \"darwin\" else \"so\"\n\ncodon_path = os.environ.get(\"CODON_DIR\")\nif not codon_path:\n    c = shutil.which(\"codon\")\n    if c:\n        codon_path = Path(c).parent / \"..\"\nelse:\n    codon_path = Path(codon_path)\nfor path in [\n    os.path.expanduser(\"~\") + \"/.codon\",\n    os.getcwd() + \"/..\",\n]:\n    path = Path(path)\n    if not codon_path and path.exists():\n        codon_path = path\n        break\n\nif (\n    not codon_path\n    or not (codon_path / \"include\" / \"codon\").exists()\n    or not (codon_path / \"lib\" / \"codon\").exists()\n):\n    print(\n        \"Cannot find Codon.\",\n        'Please either install Codon (/bin/bash -c \"$(curl -fsSL https://exaloop.io/install.sh)\"),',\n        \"or set CODON_DIR if Codon is not in PATH or installed in ~/.codon\",\n        file=sys.stderr,\n    )\n    sys.exit(1)\ncodon_path = codon_path.resolve()\nprint(\"Codon: \" + str(codon_path))\n\n\nif sys.platform == \"darwin\":\n    libraries=[\"codonrt\", \"codonc\"]\n    linker_args = [\"-Wl,-rpath,\" + str(codon_path / \"lib\" / \"codon\")]\nelse:\n    libraries=[\"codonrt\"]\n    linker_args = [\n        \"-Wl,-rpath=\" + str(codon_path / \"lib\" / \"codon\"),\n        \"-Wl,--no-as-needed\",\n        \"-lcodonc\",\n    ]\n\n    # TODO: handle ABI changes better\n    out = subprocess.check_output([\"nm\", \"-g\", str(codon_path / \"lib\" / \"codon\" / \"libcodonc.so\")])\n    out = [i for i in out.decode(sys.stdout.encoding).split(\"\\n\") if \"jitExecuteSafe\" in i]\n    if out and \"cxx11\" not in out[0]:\n        print(\"CXX11 ABI not detected\")\n        os.environ[\"CFLAGS\"] = os.environ.get(\"CFLAGS\", \"\") + \" -D_GLIBCXX_USE_CXX11_ABI=0\"\n\njit_extension = Extension(\n    \"codon.codon_jit\",\n    sources=[\"codon/jit.pyx\"],\n    libraries=libraries,\n    language=\"c\",\n    extra_compile_args=[\"-w\"],\n    extra_link_args=linker_args,\n    include_dirs=[str(codon_path / \"include\")],\n    library_dirs=[str(codon_path / \"lib\" / \"codon\")],\n)\n\nsetup(\n    name=\"codon-jit\",\n    version=__version__,\n    install_requires=[\"cython\", \"astunparse\"],\n    python_requires=\">=3.6\",\n    description=\"Codon JIT decorator\",\n    url=\"https://exaloop.io\",\n    long_description=\"Please see https://exaloop.io for more details.\",\n    author=\"Exaloop Inc.\",\n    author_email=\"info@exaloop.io\",\n    license=\"Apache License 2.0\",\n    ext_modules=[jit_extension],\n    packages=[\"codon\"],\n    include_package_data=True,\n    cmdclass={\n        \"build_ext\": build_ext,\n    },\n)\n"
  },
  {
    "path": "jupyter/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\nproject(\n  CodonJupyter\n  VERSION \"0.1\"\n  HOMEPAGE_URL \"https://github.com/exaloop/codon\"\n  DESCRIPTION \"Jupyter support for Codon\")\n\nif (CMAKE_VERSION VERSION_GREATER_EQUAL \"3.24.0\")\n  cmake_policy(SET CMP0135 NEW)\nendif()\n\nif(NOT CODON_PATH)\n    set(CODON_PATH \"$ENV{HOME}/.codon\")\nendif()\nmessage(STATUS \"Found Codon in ${CODON_PATH}\")\nif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n  set(CMAKE_INSTALL_PREFIX \"${CODON_PATH}/lib/codon/\" CACHE PATH \"Use the existing Codon installation\" FORCE)\nendif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n\n\nset(CMAKE_CXX_STANDARD 17)\nset(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS}\")\nif(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\")\n  set(CMAKE_CXX_FLAGS\n      \"${CMAKE_CXX_FLAGS} -pedantic -fvisibility-inlines-hidden -Wno-return-type-c-linkage -Wno-gnu-zero-variadic-macro-arguments -Wno-deprecated-declarations\"\n  )\nelse()\n  set(CMAKE_CXX_FLAGS \"${CMAKE_CXX_FLAGS} -Wno-return-type\")\nendif()\nset(CMAKE_CXX_FLAGS_DEBUG \"-g\")\nif(CMAKE_CXX_COMPILER_ID MATCHES \"Clang\")\n  set(CMAKE_CXX_FLAGS_DEBUG \"${CMAKE_CXX_FLAGS_DEBUG} -fno-limit-debug-info\")\nendif()\nset(CMAKE_CXX_FLAGS_RELEASE \"-O3\")\ninclude_directories(.)\n\nfind_package(LLVM REQUIRED CONFIG)\nseparate_arguments(LLVM_DEFINITIONS_LIST NATIVE_COMMAND ${LLVM_DEFINITIONS})\nadd_definitions(${LLVM_DEFINITIONS_LIST})\n\nset(CPM_DOWNLOAD_VERSION 0.32.3)\nset(CPM_DOWNLOAD_LOCATION \"${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake\")\nif(NOT (EXISTS ${CPM_DOWNLOAD_LOCATION}))\n    message(STATUS \"Downloading CPM.cmake...\")\n    file(DOWNLOAD https://github.com/TheLartians/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake ${CPM_DOWNLOAD_LOCATION})\nendif()\ninclude(${CPM_DOWNLOAD_LOCATION})\nCPMAddPackage(\n    NAME xtl\n    GITHUB_REPOSITORY \"xtensor-stack/xtl\"\n    VERSION 0.7.5\n    GIT_TAG 0.7.5\n    EXCLUDE_FROM_ALL YES\n    OPTIONS \"BUILD_TESTS OFF\")\nCPMAddPackage(\n    NAME json\n    GITHUB_REPOSITORY \"nlohmann/json\"\n    VERSION 3.11.2)\nCPMAddPackage(\n    NAME xeus\n    GITHUB_REPOSITORY \"jupyter-xeus/xeus\"\n    VERSION 3.0.5\n    GIT_TAG 3.0.5\n    EXCLUDE_FROM_ALL YES\n    PATCH_COMMAND git apply --reject --whitespace=fix ${CMAKE_SOURCE_DIR}/xeus.patch\n    OPTIONS \"BUILD_EXAMPLES OFF\"\n            \"XEUS_BUILD_SHARED_LIBS OFF\"\n            \"XEUS_STATIC_DEPENDENCIES ON\"\n            \"CMAKE_POSITION_INDEPENDENT_CODE ON\"\n            \"XEUS_DISABLE_ARCH_NATIVE ON\"\n            \"XEUS_USE_DYNAMIC_UUID ${XEUS_USE_DYNAMIC_UUID}\")\nif (xeus_ADDED)\n    install(TARGETS nlohmann_json EXPORT xeus-targets)\nendif()\n\nCPMAddPackage(\n  NAME libzmq\n  VERSION 4.3.4\n  URL https://github.com/zeromq/libzmq/releases/download/v4.3.4/zeromq-4.3.4.tar.gz\n  EXCLUDE_FROM_ALL YES\n  OPTIONS \"WITH_PERF_TOOL OFF\"\n          \"ZMQ_BUILD_TESTS OFF\"\n          \"ENABLE_CPACK OFF\"\n          \"BUILD_SHARED ON\"\n          \"WITH_LIBSODIUM OFF\"\n          \"WITH_TLS OFF\"\n          \"WITH_DOC OFF\")\nCPMAddPackage(\n    NAME cppzmq\n    URL https://github.com/zeromq/cppzmq/archive/refs/tags/v4.8.1.tar.gz\n    VERSION 4.9.0\n    EXCLUDE_FROM_ALL YES\n    OPTIONS \"CPPZMQ_BUILD_TESTS OFF\")\nCPMAddPackage(\n    NAME xeus-zmq\n    GITHUB_REPOSITORY \"jupyter-xeus/xeus-zmq\"\n    VERSION 1.0.3\n    GIT_TAG 1.0.3\n    EXCLUDE_FROM_ALL YES\n    PATCH_COMMAND patch -N -u CMakeLists.txt --ignore-whitespace -b ${CMAKE_SOURCE_DIR}/xeus.patch || true\n    OPTIONS \"XEUS_ZMQ_BUILD_TESTS OFF\"\n            \"XEUS_ZMQ_BUILD_SHARED_LIBS OFF\"\n            \"XEUS_ZMQ_STATIC_DEPENDENCIES ON\"\n            \"CMAKE_POSITION_INDEPENDENT_CODE ON\")\n\n# Codon Jupyter library\nset(CODON_JUPYTER_FILES jupyter.h jupyter.cpp)\nadd_library(codon_jupyter SHARED ${CODON_JUPYTER_FILES})\ntarget_include_directories(codon_jupyter PRIVATE \"${CODON_PATH}/include\" ${LLVM_INCLUDE_DIRS})\nadd_dependencies(codon_jupyter xeus-static xeus-zmq-static nlohmann_json)\ntarget_link_directories(codon_jupyter PRIVATE \"${CODON_PATH}/lib/codon\")\ntarget_link_libraries(codon_jupyter PRIVATE xeus-static xeus-zmq-static codonc)\n\ninstall(TARGETS codon_jupyter DESTINATION .)\n"
  },
  {
    "path": "jupyter/jupyter.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include \"jupyter.h\"\n\n#include <dirent.h>\n#include <fcntl.h>\n#include <iostream>\n#include <locale>\n#include <nlohmann/json.hpp>\n#include <unistd.h>\n#include <xeus-zmq/xserver_zmq.hpp>\n#include <xeus/xhelper.hpp>\n#include <xeus/xkernel.hpp>\n#include <xeus/xkernel_configuration.hpp>\n\n#include \"codon/compiler/compiler.h\"\n#include \"codon/compiler/error.h\"\n#include \"codon/compiler/jit.h\"\n#include \"codon/config/config.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/util/common.h\"\n\nusing std::move;\nusing std::string;\n\nnamespace nl = nlohmann;\nnamespace codon {\n\nCodonJupyter::CodonJupyter(const std::string &argv0,\n                           const std::vector<std::string> &plugins)\n    : argv0(argv0), plugins(plugins) {}\n\nnl::json CodonJupyter::execute_request_impl(int execution_counter, const string &code,\n                                            bool silent, bool store_history,\n                                            nl::json user_expressions,\n                                            bool allow_stdin) {\n  LOG(\"[codon-jupyter] execute_request_impl\");\n  auto result = jit->execute(code);\n  string failed;\n  llvm::handleAllErrors(\n      result.takeError(),\n      [&](const codon::error::ParserErrorInfo &e) {\n        std::vector<string> backtrace;\n        for (auto &msg : e)\n          for (auto &s : msg)\n            backtrace.push_back(s.getMessage());\n        string err = backtrace[0];\n        backtrace.erase(backtrace.begin());\n        failed = fmt::format(\"Compile error: {}\\nBacktrace:\\n{}\", err,\n                             ast::join(backtrace, \"  \\n\"));\n      },\n      [&](const codon::error::RuntimeErrorInfo &e) {\n        auto backtrace = e.getBacktrace();\n        failed = fmt::format(\"Runtime error: {}\\nBacktrace:\\n{}\", e.getMessage(),\n                             ast::join(backtrace, \"  \\n\"));\n      });\n  if (failed.empty()) {\n    std::string out = *result;\n    nl::json pub_data;\n    using std::string_literals::operator\"\"s;\n    std::string codonMimeMagic = \"\\x00\\x00__codon/mime__\\x00\"s;\n    if (ast::startswith(out, codonMimeMagic)) {\n      std::string mime = \"\";\n      int i = codonMimeMagic.size();\n      for (; i < out.size() && out[i]; i++)\n        mime += out[i];\n      if (i < out.size() && !out[i]) {\n        i += 1;\n      } else {\n        mime = \"text/plain\";\n        i = 0;\n      }\n      pub_data[mime] = out.substr(i);\n      LOG(\"> {}: {}\", mime, out.substr(i));\n    } else {\n      pub_data[\"text/plain\"] = out;\n    }\n    if (!out.empty())\n      publish_execution_result(execution_counter, move(pub_data), nl::json::object());\n    return nl::json{{\"status\", \"ok\"},\n                    {\"payload\", nl::json::array()},\n                    {\"user_expressions\", nl::json::object()}};\n  } else {\n    publish_stream(\"stderr\", failed);\n    return nl::json{{\"status\", \"error\"}};\n  }\n}\n\nvoid CodonJupyter::configure_impl() {\n  jit = std::make_unique<codon::jit::JIT>(argv0, \"jupyter\");\n  jit->getCompiler()->getLLVMVisitor()->setCapture();\n\n  for (const auto &plugin : plugins) {\n    // TODO: error handling on plugin init\n    bool failed = false;\n    llvm::handleAllErrors(jit->getCompiler()->load(plugin),\n                          [&failed](const codon::error::PluginErrorInfo &e) {\n                            codon::compilationError(e.getMessage(), /*file=*/\"\",\n                                                    /*line=*/0, /*col=*/0,\n                                                    /*terminate=*/false);\n                            failed = true;\n                          });\n  }\n  llvm::cantFail(jit->init());\n}\n\nnl::json CodonJupyter::complete_request_impl(const string &code, int cursor_pos) {\n  LOG(\"[codon-jupyter] complete_request_impl\");\n  return nl::json{{\"status\", \"ok\"}};\n}\n\nnl::json CodonJupyter::inspect_request_impl(const string &code, int cursor_pos,\n                                            int detail_level) {\n  LOG(\"[codon-jupyter] inspect_request_impl\");\n  return nl::json{{\"status\", \"ok\"}};\n}\n\nnl::json CodonJupyter::is_complete_request_impl(const string &code) {\n  LOG(\"[codon-jupyter] is_complete_request_impl\");\n  return nl::json{{\"status\", \"complete\"}};\n}\n\nnl::json CodonJupyter::kernel_info_request_impl() {\n  LOG(\"[codon-jupyter] kernel_info_request_impl\");\n  return xeus::create_info_reply(\"\", \"codon_kernel\", CODON_VERSION, \"python\", \"3.7\",\n                                 \"text/x-python\", \".codon\", \"python\", \"\", \"\",\n                                 \"Codon Kernel\");\n}\n\nvoid CodonJupyter::shutdown_request_impl() {\n  LOG(\"[codon-jupyter] shutdown_request_impl\");\n}\n\nint startJupyterKernel(const std::string &argv0,\n                       const std::vector<std::string> &plugins,\n                       const std::string &configPath) {\n  xeus::xconfiguration config = xeus::load_configuration(configPath);\n\n  auto context = xeus::make_context<zmq::context_t>();\n\n  LOG(\"[codon-jupyter] startJupyterKernel\");\n  auto interpreter = std::make_unique<CodonJupyter>(argv0, plugins);\n  xeus::xkernel kernel(config, xeus::get_user_name(), move(context), move(interpreter),\n                       xeus::make_xserver_zmq);\n  kernel.start();\n\n  return 0;\n}\n\n} // namespace codon\n"
  },
  {
    "path": "jupyter/jupyter.h",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#pragma once\n#include <codon/compiler/jit.h>\n#include <nlohmann/json.hpp>\n#include <xeus/xinterpreter.hpp>\n\nusing xeus::xinterpreter;\nnamespace nl = nlohmann;\n\nnamespace codon {\nclass CodonJupyter : public xinterpreter {\n  std::unique_ptr<codon::jit::JIT> jit;\n  std::string argv0;\n  std::vector<std::string> plugins;\n\npublic:\n  CodonJupyter(const std::string &argv0, const std::vector<std::string> &plugins);\n\nprivate:\n  void configure_impl() override;\n\n  nl::json execute_request_impl(int execution_counter, const std::string &code,\n                                bool silent, bool store_history,\n                                nl::json user_expressions, bool allow_stdin) override;\n\n  nl::json complete_request_impl(const std::string &code, int cursor_pos) override;\n\n  nl::json inspect_request_impl(const std::string &code, int cursor_pos,\n                                int detail_level) override;\n\n  nl::json is_complete_request_impl(const std::string &code) override;\n\n  nl::json kernel_info_request_impl() override;\n\n  void shutdown_request_impl() override;\n};\n\nint startJupyterKernel(const std::string &argv0,\n                       const std::vector<std::string> &plugins,\n                       const std::string &configPath);\n\n} // namespace codon\n"
  },
  {
    "path": "jupyter/share/jupyter/kernels/codon/kernel.json.in",
    "content": "{\n    \"display_name\": \"Codon\",\n    \"argv\": [\n        \"@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_BINDIR@/@EXECUTABLE_NAME@\",\n        \"jupyter\",\n        \"{connection_file}\"\n    ],\n    \"language\": \"python\"\n}\n"
  },
  {
    "path": "jupyter/xeus.patch",
    "content": "From 1b87a6fc56a5965066581decd40d0fced324ec1f Mon Sep 17 00:00:00 2001\nFrom: =?UTF-8?q?Ibrahim=20Numanagic=CC=81?= <ibrahim@exaloop.io>\nDate: Thu, 20 Apr 2023 11:22:56 -0700\nSubject: [PATCH] Codon fixes\n\n---\n CMakeLists.txt  | 13 ++++++++-----\n src/xserver.cpp |  3 ---\n 2 files changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/CMakeLists.txt b/CMakeLists.txt\nindex 53330ba..af02f69 100644\n--- a/CMakeLists.txt\n+++ b/CMakeLists.txt\n@@ -53,6 +53,7 @@ option(XEUS_BUILD_SHARED_LIBS \"Build xeus shared library.\" ON)\n option(XEUS_BUILD_STATIC_LIBS \"Build xeus static library (default if BUILD_SHARED_LIBS is OFF).\" ON)\n option(XEUS_STATIC_DEPENDENCIES \"link statically with xeus dependencies\" OFF)\n option(XEUS_EMSCRIPTEN_WASM_BUILD  \"build for wasm via emscripten\" OFF)\n+option(XEUS_USE_DYNAMIC_UUID  \"use dynamic linking for libuuid\" OFF)\n\n # Test options\n option(XEUS_BUILD_TESTS \"xeus test suite\" OFF)\n@@ -70,9 +71,10 @@ endif()\n\n message(STATUS \"XEUS_BUILD_SHARED_LIBS:          ${XEUS_BUILD_SHARED_LIBS}\")\n message(STATUS \"XEUS_BUILD_STATIC_LIBS:          ${XEUS_BUILD_STATIC_LIBS}\")\n-message(STATUS \"XEUS_STATIC_DEPENDENCIES:        ${XEUS_STATIC_DEPENDENCIES}\")\n+message(STATUS \"XEUS_STATIC_DEPENDENCIES:        ${XEUS_STATIC_DEPENDENCIES}\")\n message(STATUS \"XEUS_EMSCRIPTEN_WASM_BUILD:      ${XEUS_EMSCRIPTEN_WASM_BUILD}\")\n-message(STATUS \"XEUS_BUILD_TESTS:                ${XEUS_BUILD_TESTS}\")\n+message(STATUS \"XEUS_BUILD_TESTS:                ${XEUS_BUILD_TESTS}\")\n+message(STATUS \"XEUS_USE_DYNAMIC_UUID:      ${XEUS_USE_DYNAMIC_UUID}\")\n\n # Dependencies\n # ============\n@@ -170,12 +172,14 @@ macro(xeus_create_target target_name linkage output_name)\n         if (APPLE)\n             target_link_libraries(${target_name} PUBLIC \"-framework CoreFoundation\")\n         else ()\n-            if (XEUS_STATIC_DEPENDENCIES)\n+            if ((NOT XEUS_USE_DYNAMIC_UUID) AND (XEUS_STATIC_DEPENDENCIES))\n+                message(STATUS \"static libuuid linking\")\n                 find_path(LIBUUID_INCLUDE_DIR uuid/uuid.h)\n                 find_library(LIBUUID_LIBRARY libuuid.a)\n                 target_include_directories(${target_name} PRIVATE ${LIBUUID_INCLUDE_DIR})\n-                target_link_libraries(${target_name} PRIVATE ${LIBUUID_LIBRARY})\n+                target_link_libraries(${target_name} PRIVATE ${LIBUUID_LIBRARY})\n             else ()\n+            message(STATUS \"dynamic libuuid linking\")\n                 find_package(LibUUID REQUIRED)\n                 target_link_libraries(${target_name} PRIVATE LibUUID::LibUUID)\n             endif ()\n@@ -220,7 +224,6 @@ macro(xeus_create_target target_name linkage output_name)\n     if (CMAKE_CXX_COMPILER_ID MATCHES \"Clang\" OR\n         CMAKE_CXX_COMPILER_ID MATCHES \"GNU\" OR\n         CMAKE_CXX_COMPILER_ID MATCHES \"Intel\")\n-        target_compile_options(${target_name} PUBLIC -Wunused-parameter -Wextra -Wreorder)\n         message(STATUS \"CMAKE_CXX_FLAGS: ${CMAKE_CXX_FLAGS}\")\n     endif()\n\ndiff --git a/src/xserver.cpp b/src/xserver.cpp\nindex 82acb78..04caa3b 100644\n--- a/src/xserver.cpp\n+++ b/src/xserver.cpp\n@@ -40,9 +40,6 @@ namespace xeus\n\n     void xserver::start(xpub_message message)\n     {\n-        std::clog << \"Run with XEUS \" << XEUS_VERSION_MAJOR << \".\"\n-                                      << XEUS_VERSION_MINOR << \".\"\n-                                      << XEUS_VERSION_PATCH << std::endl;\n         start_impl(std::move(message));\n     }\n\n--\n2.40.0\n\n"
  },
  {
    "path": "mkdocs.yml",
    "content": "site_url: https://docs.exaloop.io\nsite_name: \"\"\nrepo_url: https://github.com/exaloop/codon\nrepo_name: exaloop/codon\n\ntheme:\n  name: material\n  custom_dir: docs/overrides\n  logo: img/codon-white.svg\n  icon:\n    repo: fontawesome/brands/github\n  favicon: img/favicon.png\n  font:\n    text: Open Sans\n    code: Source Code Mono\n  palette:\n    # Palette toggle for light mode\n    - scheme: default\n      toggle:\n        icon: material/brightness-7 \n        name: Switch to dark mode\n    # Palette toggle for dark mode\n    - scheme: slate\n      toggle:\n        icon: material/brightness-4\n        name: Switch to light mode\n  features:\n    - content.code.copy\n    - search.suggest\n    - search.highlight\n    - navigation.tracking\n    - navigation.sections\n    - navigation.expand\n    - navigation.tabs\n    - navigation.top\n    - announce.dismiss\n\nextra:\n  social:\n    - icon: fontawesome/brands/github\n      link: https://github.com/exaloop\n\n    - icon: fontawesome/brands/discord\n      link: https://discord.gg/HeWRhagCmP\n\n    - icon: fontawesome/brands/x-twitter\n      link: https://x.com/exaloop\n\n    - icon: fontawesome/brands/linkedin\n      link: https://linkedin.com/company/exaloop\n\n  generator: false\n\ncopyright: Copyright &copy; 2026 Exaloop, Inc.\n\nextra_javascript:\n  - js/mathjax.js\n  - https://unpkg.com/mathjax@3/es5/tex-mml-chtml.js\n\nextra_css:\n  - css/extra.css\n\nmarkdown_extensions:\n  - attr_list\n  - md_in_html\n  - pymdownx.highlight:\n      use_pygments: true\n      line_spans: __span\n  - pymdownx.inlinehilite\n  - pymdownx.snippets\n  - pymdownx.details\n  - pymdownx.extra\n  - pymdownx.blocks.caption\n  - pymdownx.emoji:\n      emoji_index: !!python/name:material.extensions.emoji.twemoji\n      emoji_generator: !!python/name:material.extensions.emoji.to_svg\n  - pymdownx.arithmatex:\n      generic: true\n  - pymdownx.superfences:\n      custom_fences:\n        - name: mermaid\n          class: mermaid\n          format: !!python/name:pymdownx.superfences.fence_code_format\n  - pymdownx.tabbed:\n      alternate_style: true\n  - pymdownx.tasklist:\n      custom_checkbox: true\n  - toc:\n      permalink: true\n  - admonition\n  - footnotes\n\nplugins:\n  - tags\n  - typeset\n  - optimize\n  - search\n  - macros\n  - redirects:\n      # redirects from old docs site\n      redirect_maps:\n        'codon/index.md': 'index.md'\n        'codon/general/intro.md': 'start/install.md'\n        'codon/general/faq.md': 'start/faq.md'\n        'codon/general/differences.md': 'language/overview.md#differences-with-python'\n        'codon/general/releases.md': 'start/changelog.md'\n        'codon/general/roadmap.md': 'developers/roadmap.md'\n        'codon/language-features/basics.md': 'language/overview.md'\n        'codon/language-features/collections.md': 'language/overview.md'\n        'codon/language-features/functions.md': 'language/overview.md'\n        'codon/language-features/classes.md': 'language/classes.md'\n        'codon/language-features/generators.md': 'language/overview.md'\n        'codon/language-features/statics.md': 'language/meta.md'\n        'codon/language-features/extra.md': 'language/lowlevel.md'\n        'codon/language-features/ffi.md': 'integrations/cpp/cpp-from-codon.md'\n        'codon/language-features/llvm.md': 'language/llvm.md'\n        'codon/interoperability/numpy.md': 'libraries/numpy.md'\n        'codon/interoperability/python.md': 'integrations/python/python-from-codon.md'\n        'codon/interoperability/decorator.md': 'integrations/python/codon-from-python.md'\n        'codon/interoperability/pyext.md': 'integrations/python/extensions.md'\n        'codon/interoperability/cpp.md': 'integrations/cpp/codon-from-cpp.md'\n        'codon/interoperability/jupyter.md': 'integrations/jupyter.md'\n        'codon/advanced/parallel.md': 'parallel/multithreading.md'\n        'codon/advanced/gpu.md': 'parallel/gpu.md'\n        'codon/advanced/ir.md': 'developers/ir.md'\n        'codon/advanced/build.md': 'developers/build.md'\n\nnav:\n  - Get Started:\n    - Installation: start/install.md\n    - Usage: start/usage.md\n    - FAQ: start/faq.md\n    - Changelog: start/changelog.md\n  - Language:\n    - Overview: language/overview.md\n    - Classes: language/classes.md\n    - Generics: language/generics.md\n    - Metaprogramming: language/meta.md\n    - Low-Level Programming: language/lowlevel.md\n    - Inline LLVM IR: language/llvm.md\n  - Libraries:\n    - Standard Library: libraries/stdlib.md\n    - NumPy: libraries/numpy.md\n    - API Reference: libraries/api/index.md\n  - Integrations:\n    - Python:\n      - Call Python from Codon: integrations/python/python-from-codon.md\n      - Call Codon from Python: integrations/python/codon-from-python.md\n      - Create Python Extensions: integrations/python/extensions.md\n    - C/C++:\n      - Call C/C++ from Codon: integrations/cpp/cpp-from-codon.md\n      - Call Codon from C/C++: integrations/cpp/codon-from-cpp.md\n      - Embed Codon JIT: integrations/cpp/jit.md\n    - Jupyter: integrations/jupyter.md\n  - Parallel Programming:\n    - Multithreading: parallel/multithreading.md\n    - GPU Programming: parallel/gpu.md\n    - SIMD Programming: parallel/simd.md\n  - Labs: labs/index.md\n  - Developers:\n    - Compilation Flow: developers/compilation.md\n    - Intermediate Representation: developers/ir.md\n    - Extend Codon: developers/extend.md\n    - Build from Source: developers/build.md\n    - Contribute: developers/contribute.md\n    - Roadmap: developers/roadmap.md\n  - Blog: https://exaloop.io/blog\n"
  },
  {
    "path": "scripts/Dockerfile.codon-build",
    "content": "FROM exaloop/codon-llvm as codon-llvm\nFROM nvidia/cuda:12.4.0-devel-centos7\nCOPY --from=codon-llvm /opt/llvm-codon /opt/llvm-codon\n\nRUN yum -y update\nRUN yum -y install centos-release-scl-rh epel-release\nRUN yum -y install \\\n    devtoolset-7 \\\n    ninja-build libuuid-devel openssl openssl-devel \\\n    libsodium-devel cmake3 zlib-devel git patch perl-Data-Dumper\nRUN scl enable devtoolset-7 -- g++ -v\n\nRUN git clone -b develop https://github.com/exaloop/codon /github/codon\nRUN scl enable devtoolset-7 -- cmake3 -S /github/codon -B /github/codon/build \\\n    -G Ninja \\\n    -DCMAKE_BUILD_TYPE=Release \\\n    -DCMAKE_C_COMPILER=/opt/llvm-codon/bin/clang \\\n    -DCMAKE_CXX_COMPILER=/opt/llvm-codon/bin/clang++ \\\n    -DLLVM_DIR=/opt/llvm-codon/lib/cmake/llvm\nRUN LD_LIBRARY_PATH=/usr/local/cuda-12.4/compat:${LD_LIBRARY_PATH} scl enable devtoolset-7 -- cmake3 --build /github/codon/build\nRUN scl enable devtoolset-7 -- cmake3 --install /github/codon/build --prefix /opt/codon\n\nRUN scl enable devtoolset-7 -- cmake3 -S /github/codon/jupyter -B /github/codon/jupyter/build \\\n    -G Ninja \\\n    -DCMAKE_BUILD_TYPE=Release \\\n    -DCMAKE_C_COMPILER=/opt/llvm-codon/bin/clang \\\n    -DCMAKE_CXX_COMPILER=/opt/llvm-codon/bin/clang++ \\\n    -DLLVM_DIR=/opt/llvm-codon/lib/cmake/llvm \\\n    -DCODON_PATH=/opt/codon \\\n    -DOPENSSL_ROOT_DIR=$(openssl version -d | cut -d' ' -f2 | tr -d '\"') \\\n    -DOPENSSL_CRYPTO_LIBRARY=/usr/lib64/libssl.so \\\n    -DXEUS_USE_DYNAMIC_UUID=ON\nRUN scl enable devtoolset-7 -- cmake3 --build /github/codon/jupyter/build\nRUN scl enable devtoolset-7 -- cmake3 --install /github/codon/jupyter/build\n\n# RUN mkdir -p /opt/codon/bin\n# RUN cp /github/codon/build/codon /opt/codon/bin/\n# RUN mkdir -p /opt/codon/lib/codon\n# RUN cp -r /github/codon/build/lib*.so /opt/codon/lib/codon/\n# RUN cp -r /github/codon/stdlib /opt/codon/lib/codon/\nRUN cd /github/codon && tar czvf /opt/codon-$(git rev-parse --short HEAD).tar.gz -C /opt codon/\nCMD cp /opt/codon-*.tar.gz /mnt/\n"
  },
  {
    "path": "scripts/Dockerfile.codon-jupyter",
    "content": "FROM exaloop/codon-llvm:15.0.1\nENV pass=\"codon-jupyter\"\n\n# Install dependencies\nRUN yum -y install openssl-devel libsodium-devel libuuid-devel\n\n# Build Codon core\nRUN git clone -b develop https://github.com/exaloop/codon /github/codon\nRUN cmake3 -S /github/codon -B /github/codon/build \\\n    -G Ninja \\\n    -DCMAKE_BUILD_TYPE=Release \\\n    -DCMAKE_C_COMPILER=/opt/llvm-codon/bin/clang \\\n    -DCMAKE_CXX_COMPILER=/opt/llvm-codon/bin/clang++ \\\n    -DLLVM_DIR=/opt/llvm-codon/lib/cmake/llvm \\\n    -DCMAKE_INSTALL_PREFIX=/opt/codon\nRUN cmake3 --build /github/codon/build\nRUN cmake3 --install /github/codon/build\n\n# Build Codon Jupyter support\nRUN cmake3 -S /github/codon/jupyter -B /github/codon/jupyter/build \\\n    -G Ninja \\\n    -DCMAKE_BUILD_TYPE=Release \\\n    -DCMAKE_C_COMPILER=/opt/llvm-codon/bin/clang \\\n    -DCMAKE_CXX_COMPILER=/opt/llvm-codon/bin/clang++ \\\n    -DLLVM_DIR=/opt/llvm-codon/lib/cmake/llvm \\\n    -DCODON_PATH=/opt/codon \\\n    -DOPENSSL_ROOT_DIR=$(openssl version -d | cut -d' ' -f2 | tr -d '\"') \\\n    -DOPENSSL_CRYPTO_LIBRARY=/usr/lib64/libssl.so \\\n    -DXEUS_USE_DYNAMIC_UUID=ON\nRUN cmake3 --build /github/codon/jupyter/build\nRUN cmake3 --install /github/codon/jupyter/build\n\n# Build Seq (bioinformatics plugin) for Codon\nRUN git clone -b develop https://github.com/exaloop/seq /github/seq\nRUN cmake3 -S /github/seq -B /github/seq/build \\\n    -G Ninja \\\n    -DCMAKE_BUILD_TYPE=Release \\\n    -DCMAKE_C_COMPILER=/opt/llvm-codon/bin/clang \\\n    -DCMAKE_CXX_COMPILER=/opt/llvm-codon/bin/clang++ \\\n    -DLLVM_DIR=/opt/llvm-codon/lib/cmake/llvm \\\n    -DCODON_PATH=/opt/codon\nRUN cmake3 --build /github/seq/build\nRUN cmake3 --install /github/seq/build\n\n# Set up Codon Jupyter kernel\nRUN pip3 install ipywidgets==7.6.5 numpy matplotlib pandas scipy jupyter plotly\nRUN mkdir -p /usr/share/jupyter/kernels/codon\nRUN echo '{\"display_name\": \"Codon\", \"argv\": [ \"/opt/codon/bin/codon\", \"jupyter\", \"-plugin\", \"seq\", \"{connection_file}\" ], \"language\": \"python\"}' > /usr/share/jupyter/kernels/codon/kernel.json\n\n# Launch Jupyter\nENV CODON_PYTHON=\"/usr/lib64/libpython3.so\"\nCMD jupyter notebook --port=8888 --no-browser --ip=0.0.0.0 --allow-root --NotebookApp.token=${pass}\n"
  },
  {
    "path": "scripts/Dockerfile.gpu",
    "content": "FROM nvidia/cuda:11.8.0-devel-centos7\nRUN yum -y update\nRUN yum -y install bzip2\nCOPY codon-gpu.tar.bz2 /opt\nRUN cd /opt && tar jxvf codon-gpu.tar.bz2\nCOPY test.codon .\nCMD /opt/codon/bin/codon run -release test.codon\n"
  },
  {
    "path": "scripts/Dockerfile.llvm-build",
    "content": "FROM nvidia/cuda:12.6.0-cudnn-devel-rockylinux8\n\nRUN yum -y install epel-release && \\\n    yum -y install --enablerepo=devel --enablerepo=powertools \\\n    \tninja-build libuuid-devel openssl openssl-devel python3 bzip2 \\\n    \tlibsodium-devel zlib-devel git patch perl-Data-Dumper wget cmake libgfortran \\\n\t    bzip2-devel xz-devel gcc-gfortran gcc-c++ libstdc++-static\n\nRUN git clone --depth 1 -b codon https://github.com/exaloop/llvm-project /github/llvm-src && \\\n    cmake -S /github/llvm-src/llvm -G Ninja \\\n           -B /github/llvm-src/llvm/build \\\n           -DCMAKE_BUILD_TYPE=Release \\\n           -DLLVM_INCLUDE_TESTS=OFF \\\n           -DLLVM_ENABLE_RTTI=ON \\\n           -DLLVM_ENABLE_ZLIB=OFF \\\n           -DLLVM_ENABLE_TERMINFO=OFF \\\n           -DLLVM_TARGETS_TO_BUILD=\"host;NVPTX\" \\\n           -DLLVM_BUILD_TOOLS=OFF \\\n           -DLLVM_ENABLE_PROJECTS=clang \\\n           -DCMAKE_INSTALL_PREFIX=/opt/llvm-codon\n\nRUN cmake --build /github/llvm-src/llvm/build\nRUN cmake --install /github/llvm-src/llvm/build && \\\n    cd /github/llvm-src && tar cjvf /opt/llvm-$(git rev-parse --abbrev-ref HEAD)-$(git rev-parse --short HEAD).tar.bz2 -C /opt /opt/llvm-codon/\n"
  },
  {
    "path": "scripts/deps.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\nexport INSTALLDIR=\"${PWD}/llvm\"\nexport SRCDIR=\"${PWD}/llvm-project\"\nmkdir -p \"${INSTALLDIR}\" \"${SRCDIR}\"\n\nexport JOBS=1\nif [ -n \"${1}\" ]; then export JOBS=\"${1}\"; fi\necho \"Using ${JOBS} cores...\"\n\nLLVM_BRANCH=\"codon\"\nif [ ! -f \"${INSTALLDIR}/bin/llvm-config\" ]; then\n  git clone --depth 1 -b \"${LLVM_BRANCH}\" https://github.com/exaloop/llvm-project \"${SRCDIR}\"\n\n  mkdir -p \"${SRCDIR}/llvm/build\"\n  cd \"${SRCDIR}/llvm/build\"\n  cmake .. \\\n      -DCMAKE_BUILD_TYPE=Release \\\n      -DLLVM_INCLUDE_TESTS=OFF \\\n      -DLLVM_ENABLE_RTTI=ON \\\n      -DLLVM_ENABLE_ZLIB=OFF \\\n      -DLLVM_ENABLE_ZSTD=OFF \\\n      -DLLVM_TARGETS_TO_BUILD=all \\\n      -DLLVM_ENABLE_PROJECTS=\"clang;openmp\" \\\n      -DCMAKE_INSTALL_PREFIX=\"${INSTALLDIR}\"\n  make -j \"${JOBS}\"\n  make install\n\n  \"${INSTALLDIR}/bin/llvm-config\" --cmakedir\n  cd ${INSTALLDIR}\n  rm -rf ${SRCDIR}\nfi\n"
  },
  {
    "path": "scripts/docgen.py",
    "content": "# Generates API reference for Codon standard library\n# See CI workflow for usage\n\nimport itertools\nimport os\nimport os.path\nimport sys\nimport collections\nimport json\n\nfrom pathlib import Path\n\n# 1. Set up paths and load JSON\njson_path = os.path.abspath(sys.argv[1])\nout_path = os.path.abspath(sys.argv[2])\nroots = [os.path.abspath(root) for root in sys.argv[3:]]\nprint(f\"Generating documentation for {json_path} ...\")\nwith open(json_path) as f:\n    j = json.load(f)\nprint(\"Load done!\")\n\n# 2. Get the list of modules and create the documentation tree\nmodules = {k: v[\"path\"] for k, v in j.items() if v[\"kind\"] == \"module\"}\nparsed_modules = collections.defaultdict(set)\n\ndef remove_root(path, roots):\n    for root in roots:\n        try:\n            rel = str(Path(path).relative_to(root))\n        except ValueError:\n            continue\n        return rel if rel != '.' else ''\n    return path\n\nfor mid, module in modules.items():\n    while module not in roots:\n        directory, name = os.path.split(module)\n        directory = remove_root(directory, roots)\n        os.makedirs(os.path.join(out_path, directory), exist_ok=True)\n        if name.endswith(\".codon\"):\n            name = name[:-6]  # drop suffix\n        parsed_modules[directory].add((name, mid))\n        module = os.path.split(module)[0]\n\nprint(\"Module read done!\")\n\nfor directory, modules in parsed_modules.items():\n    module = directory.replace(\"/\", \".\")\n    with open(f\"{out_path}/{directory}/index.md\", \"w\") as f:\n        if module:\n            print(f\"# `{module}`\\n\", file=f)\n        else:\n            print(\"# Standard Library Reference\\n\", file=f)\n\n        for m in sorted(set(m for m, _ in modules)):\n            if m == \"__init__\":\n                continue\n            base = os.path.join('libraries', 'api', directory, m)\n            is_dir = os.path.isdir(os.path.join(out_path, directory, m))\n            print(f\"- [`{m}`]({m}{'/index' if is_dir else ''}.md)\", file=f)\n\nprint(f\" - Done with directory tree\")\n\ndef parse_docstr(s, level=0):\n    \"\"\"Parse docstr s and indent it with level spaces\"\"\"\n    s = s.split(\"\\n\")\n    while s and s[0] == \"\":\n        s = s[1:]\n    while s and s[-1] == \"\":\n        s = s[:-1]\n    if not s:\n        return \"\"\n    i = 0\n    indent = len(list(itertools.takewhile(lambda i: i == \" \", s[0])))\n    lines = [l[indent:] for l in s]\n    return \"\\n\".join((\"   \" * level) + l for l in lines)\n\ndef parse_type(a):\n    \"\"\"Parse type signature\"\"\"\n    if isinstance(a, str) and not a.isdigit():\n        return a\n    elif isinstance(a, list):\n        head, tail = a[0], a[1:]\n    else:\n        head, tail = a, None\n    if head[0].isdigit() and head not in j:\n        return \"?\"\n    s = j[head][\"name\"] if head[0].isdigit() else head\n    if tail:\n        for ti, t in enumerate(tail):\n            s += \"[\" if not ti else \", \"\n            s += parse_type(t)\n        s += \"]\"\n    return s\n\ndef parse_fn(v):\n    \"\"\"Parse function signature after the name\"\"\"\n    s = \"\"\n    if \"generics\" in v and v[\"generics\"]:\n        # don't include argument-generics\n        generics_no_args = [\n            g for g in v[\"generics\"]\n            if not any(a[\"name\"] == g for a in v[\"args\"])\n        ]\n        if generics_no_args:\n            s += f'[{\", \".join(v[\"generics\"])}]'\n    s += \"(\"\n    cnt = 0\n    for ai, a in enumerate(v[\"args\"]):\n        s += \"\" if not cnt else \", \"\n        cnt += 1\n        s += f'{a[\"name\"] or \"_\"}'\n        if \"type\" in a and a[\"type\"]:\n            s += \": \" + parse_type(a[\"type\"])\n        if \"default\" in a:\n            s += \" = \" + a[\"default\"]\n    s += \")\"\n    if \"ret\" in v:\n        s += \" -> \" + parse_type(v[\"ret\"])\n    # if \"extern\" in v:\n    #     s += f\" (_{v['extern']} function_)\"\n    # s += \"\\n\"\n    return s\n\ntag_tooltips = {\n    \"llvm\": \"Function is implemented with inline LLVM IR\",\n    \"pure\":\n    \"Function has no side effects and returns same value for same inputs\",\n    \"nocapture\":\n    \"Function does not capture arguments (return value might capture)\",\n    \"derives\": \"Function return value captures arguments\",\n    \"no_side_effect\": \"Function has no side effects\",\n    \"self_captures\": \"Method's 'self' argument captures other arguments\",\n    \"C\": \"Function is external C function\",\n    \"overload\": \"Function is overloaded\",\n    \"tuple\": \"Class is named tuple (cannot write fields)\",\n    \"extend\": \"Class is extended to add given methods\",\n    \"staticmethod\": \"Method is static (does not take 'self' argument)\",\n    \"property\": \"Method is a class property\",\n    \"associative\": \"Binary operator is associative\",\n    \"commutative\": \"Binary operator is commutative\",\n    \"distributive\": \"Binary operator is distributive\",\n    \"inline\": \"Function always inlined\",\n    \"noinline\": \"Function never inlined\",\n    \"export\": \"Function is visible externally\",\n    \"test\": \"Function is a test function\",\n    \"__internal__\": \"Function is compiler-generated\",\n    \"__attribute__\": \"Function is an attribute\",\n}\n\ndef write_tag(tag, f):\n    tooltip = tag_tooltips.get(tag, \"\")\n    if tooltip:\n        f.write(f'  <span class=\"api-tag-tooltip\">'\n                f'     <span class=\"api-tag\">@{tag}</span>'\n                f'     <span class=\"api-tag-tooltiptext\">{tooltip}</span>'\n                f'  </span>')\n    else:\n        f.write(f'  <span class=\"api-tag\">@{tag}</span>')\n\ndef write_tags(v, f):\n    if \"extern\" in v:\n        write_tag(v[\"extern\"], f)\n    if \"attrs\" in v and v[\"attrs\"]:\n        for attr in v[\"attrs\"]:\n            write_tag(attr, f)\n\ndef write_docstr(v, f):\n    if \"doc\" in v:\n        if \"attrs\" in v and \"llvm\" in v[\"attrs\"]:\n            f.write(\"\\n``` llvm\\n\")\n            f.write(parse_docstr(v[\"doc\"]))\n            f.write(\"\\n```\")\n        else:\n            f.write(\"\\n\")\n            f.write(parse_docstr(v[\"doc\"]))\n            f.write(\"\\n\")\n    f.write(\"\\n\")\n\n# 3. Create documentation for each module\nvisited = set()\nfor directory, (name, mid) in {(d, m)\n                               for d, mm in parsed_modules.items()\n                               for m in mm}:\n    if directory:\n        module = f\"{directory.replace('/', '.')}.{name}\"\n    else:\n        module = name\n\n    file, mode = f\"{out_path}/{directory}/{name}.md\", \"w\"\n\n    if os.path.isdir(f\"{out_path}/{directory}/{name}\"):\n        continue\n\n    init = (name == \"__init__\")\n\n    if init:\n        file, mode = f\"{out_path}/{directory}/index.md\", \"a\"\n\n    if file in visited:\n        continue\n    else:\n        visited.add(file)\n\n    with open(file, mode) as f:\n        if not init:\n            f.write(f\"# module `{module}`\\n\")\n\n        directory_prefix = directory + \"/\" if directory != \".\" else \"\"\n        directory = directory.strip(\"/\")\n        dir_part = (directory + \"/\") if directory else \"\"\n        f.write(f\"\\nSource: [`stdlib/{dir_part}{name}.codon`](https://github.com/exaloop/codon/blob/master/stdlib/{dir_part}{name}.codon)\\n\\n\")\n\n        write_docstr(j[mid], f)\n\n        for i in j[mid][\"children\"]:\n            v = j[i]\n\n            if v[\"kind\"] == \"class\" and v[\"type\"] == \"extension\":\n                v[\"name\"] = j[v[\"parent\"]][\"name\"]\n            if v[\"name\"].startswith(\"_\"):\n                continue\n\n            icon = lambda name: f'<span style=\"color:#899499\">:material-{name}:</span>'\n\n            f.write(\"---\\n\")\n            f.write(\"## \")\n            if v[\"kind\"] == \"class\":\n                f.write(f'{icon(\"cube-outline\")} **`{v[\"name\"]}')\n                if \"generics\" in v and v[\"generics\"]:\n                    f.write(f'[{\",\".join(v[\"generics\"])}]')\n                f.write(\"`**\")\n                if v[\"type\"] == \"extension\":\n                    write_tag(\"extend\", f)\n                elif v[\"type\"] == \"type\":\n                    write_tag(\"tuple\", f)\n            elif v[\"kind\"] == \"function\":\n                f.write(f'{icon(\"function\")} **`{v[\"name\"]}{parse_fn(v)}`**')\n                write_tags(v, f)\n            elif v[\"kind\"] == \"variable\":\n                f.write(f'{icon(\"variable\")} **`{v[\"name\"]}`**')\n                if \"type\" in v:\n                    f.write(f': `{parse_type(v[\"type\"])}`')\n                if \"value\" in v:\n                    f.write(f' = `{v[\"value\"]}`')\n\n            write_docstr(v, f)\n\n            if v[\"kind\"] == \"class\":\n                if \"args\" in v:\n                    fields = [\n                        c for c in v[\"args\"] if not c[\"name\"].startswith(\"_\")\n                    ]\n                    if fields:\n                        f.write(\"## Fields\\n\")\n                        for c in fields:\n                            f.write(f'### `{c[\"name\"]}`')\n                            if \"type\" in c:\n                                f.write(f': `{parse_type(c[\"type\"])}`\\n')\n                            f.write(\"\\n\")\n                        f.write(\"\\n\")\n\n                mt = [c for c in v[\"members\"] if j[c][\"kind\"] == \"function\"]\n\n                props = [c for c in mt if \"property\" in j[c].get(\"attrs\", [])]\n                if props:\n                    print(\"## Properties\\n\", file=f)\n                    for c in props:\n                        v = j[c]\n                        f.write(f'### `{v[\"name\"]}`')\n                        write_tags(v, f)\n                        f.write(\"\\n\")\n                        write_docstr(v, f)\n\n                magics = [\n                    c for c in mt\n                    if len(j[c][\"name\"]) > 4 and j[c][\"name\"].startswith(\"__\")\n                    and j[c][\"name\"].endswith(\"__\")\n                ]\n                if magics:\n                    print(\"## Magic methods\\n\", file=f)\n                    for c in magics:\n                        v = j[c]\n                        f.write(f'### `{v[\"name\"]}{parse_fn(v)}`')\n                        write_tags(v, f)\n                        f.write(\"\\n\")\n                        write_docstr(v, f)\n                methods = [\n                    c for c in mt if j[c][\"name\"][0] != \"_\" and c not in props\n                ]\n                if methods:\n                    print(\"## Methods\\n\", file=f)\n                    for c in methods:\n                        v = j[c]\n                        f.write(f'### `{v[\"name\"]}{parse_fn(v)}`')\n                        write_tags(v, f)\n                        f.write(\"\\n\")\n                        write_docstr(v, f)\n            f.write(\"\\n\\n\")\n\n        f.write(\"\\n\\n\")\n\nprint(\" - Done with modules\")\n"
  },
  {
    "path": "scripts/fix_loader_paths.sh",
    "content": "#!/usr/bin/env bash\nset -euo pipefail\n\n# Exit unless on macOS\nif [[ \"$(uname -s)\" != \"Darwin\" ]]; then\n  echo \"This script must be run on macOS (Darwin). Exiting.\" >&2\n  exit 0\nfi\n\nDIR=\"${1:-codon-deploy}/lib/codon\"\n\nif [[ ! -d \"$DIR\" ]]; then\n  echo \"Directory not found: $DIR\" >&2\n  exit 1\nfi\n\ncommand -v install_name_tool >/dev/null || { echo \"install_name_tool not found\"; exit 1; }\ncommand -v otool             >/dev/null || { echo \"otool not found\"; exit 1; }\n\necho \"Patching dylibs/SOs in: $DIR\"\necho\n\n# Helper: check if file already has an RPATH\nhas_rpath() {\n  local f=\"$1\" p=\"$2\"\n  otool -l \"$f\" | awk '/LC_RPATH/{show=1} show && /path/ {print $2; show=0}' | grep -qx \"$p\"\n}\n\n# Iterate over all dylib/so files in DIR\nwhile IFS= read -r -d '' f; do\n  base=\"$(basename \"$f\")\"\n  echo \">>> $base\"\n\n  # Skip changing install_name for libcodon* files themselves\n  if [[ \"$f\" == *.dylib && ! \"$base\" =~ ^libcodon ]]; then\n    echo \"    - set id -> @loader_path/$base\"\n    install_name_tool -id \"@loader_path/$base\" \"$f\"\n  fi\n\n  # Rewrite @rpath deps to @loader_path if they exist in the same DIR\n  while IFS= read -r dep; do\n    dep_name=\"$(basename \"$dep\")\"\n    if [[ \"$dep\" == @rpath/* && -e \"$DIR/$dep_name\" ]]; then\n      echo \"    - change dep $dep -> @loader_path/$dep_name\"\n      install_name_tool -change \"$dep\" \"@loader_path/$dep_name\" \"$f\"\n    fi\n  done < <(otool -L \"$f\" | tail -n +2 | awk '{print $1}')\n\n  # Ensure LC_RPATH has @loader_path and @loader_path/../lib/codon\n  for rp in \"@loader_path\" \"@loader_path/../lib/codon\"; do\n    if ! has_rpath \"$f\" \"$rp\"; then\n      echo \"    - add rpath $rp\"\n      install_name_tool -add_rpath \"$rp\" \"$f\" || true\n    fi\n  done\n\n  # Sign to avoid Gatekeeper complaints after modification\n  if command -v codesign >/dev/null; then\n    codesign --force --sign - \"$f\" >/dev/null 2>&1 || true\n  fi\n\n  echo\ndone < <(find \"$DIR\" -maxdepth 1 -type f \\( -name '*.dylib' -o -name '*.so' \\) -print0)\n\necho \"Done.\"\n"
  },
  {
    "path": "scripts/get_system_libs.sh",
    "content": "#!/usr/bin/env bash\nset -e\n\ncheck_exists() {\n  local file=\"$1\"\n  if [ ! -e \"$file\" ]; then\n    echo \"Error: File '$file' does not exist.\" >&2\n    exit 1\n  fi\n}\n\nUNAME=\"$(uname -s)\"\nif [ \"$UNAME\" = \"Linux\" ]; then\n  LIBGFORTRAN_BASE=\"libgfortran.so.5\"\n  LIBQUADMATH_BASE=\"libquadmath.so.0\"\n  LIBGCC_BASE=\"libgcc_s.so.1\"\nelif [ \"$UNAME\" = \"Darwin\" ]; then\n  LIBGFORTRAN_BASE=\"libgfortran.5.dylib\"\n  LIBQUADMATH_BASE=\"libquadmath.0.dylib\"\n  LIBGCC_BASE=\"libgcc_s.1.1.dylib\"\nelse\n  echo \"WARNING: Could not autodetect platform type ('uname -s' = $UNAME); assuming Linux\" >&2\n  UNAME=\"Linux\"\nfi\n\nLIBR_DIR=$1\nDEST_DIR=$2\n\nLIBGFORTRAN=\"${LIBR_DIR}/${LIBGFORTRAN_BASE}\"\nLIBQUADMATH=\"${LIBR_DIR}/${LIBQUADMATH_BASE}\"\nLIBGCC=\"${LIBR_DIR}/${LIBGCC_BASE}\"\n\ncheck_exists \"${LIBGFORTRAN}\"\ncheck_exists \"${LIBGCC}\"\n\ncp -v \"${LIBGFORTRAN}\" \"${DEST_DIR}\"\ncp -v \"${LIBGCC}\" \"${DEST_DIR}\"\n\nif [ -e \"${LIBQUADMATH}\" ]; then\n  cp -v \"${LIBQUADMATH}\" \"${DEST_DIR}\"\n  HAS_LIBQUADMATH=1\nelse\n  HAS_LIBQUADMATH=0\nfi\n\nLIBGFORTRAN=\"${DEST_DIR}/${LIBGFORTRAN_BASE}\"\nLIBQUADMATH=\"${DEST_DIR}/${LIBQUADMATH_BASE}\"\nLIBGCC=\"${DEST_DIR}/${LIBGCC_BASE}\"\n\nchmod 755 \"${LIBGFORTRAN}\"\nchmod 755 \"${LIBGCC}\"\n[ \"$HAS_LIBQUADMATH\" -eq 1 ] && chmod 755 \"${LIBQUADMATH}\"\n\nif [ \"$UNAME\" = \"Darwin\" ]; then\n  install_name_tool -id \"@rpath/${LIBGFORTRAN_BASE}\" \"${LIBGFORTRAN}\"\n  install_name_tool -id \"@rpath/${LIBGCC_BASE}\" \"${LIBGCC}\"\n  codesign -f -s - \"${LIBGFORTRAN}\"\n  codesign -f -s - \"${LIBGCC}\"\n  if [ \"$HAS_LIBQUADMATH\" -eq 1 ]; then\n    install_name_tool -id \"@rpath/${LIBQUADMATH_BASE}\" \"${LIBQUADMATH}\"\n    codesign -f -s - \"${LIBQUADMATH}\"\n  fi\nelse\n  patchelf --set-rpath '$ORIGIN' \"${LIBGFORTRAN}\" || true\n  patchelf --set-rpath '$ORIGIN' \"${LIBGCC}\" || true\n  if [ \"$HAS_LIBQUADMATH\" -eq 1 ]; then\n    patchelf --set-rpath '$ORIGIN' \"${LIBQUADMATH}\" || true\n  fi\nfi\n"
  },
  {
    "path": "scripts/install.sh",
    "content": "#!/usr/bin/env bash\nset -e\nset -o pipefail\n\nCODON_INSTALL_DIR=~/.codon\nOS=$(uname -s | awk '{print tolower($0)}')\nARCH=$(uname -m)\n\nif [ \"$OS\" != \"linux\" ] && [ \"$OS\" != \"darwin\" ]; then\n  echo \"error: Pre-built binaries only exist for Linux and macOS.\" >&2\n  exit 1\nfi\n\nCODON_BUILD_ARCHIVE=codon-$OS-$ARCH.tar.gz\n\nmkdir -p $CODON_INSTALL_DIR\ncd $CODON_INSTALL_DIR\ncurl -L https://github.com/exaloop/codon/releases/latest/download/\"$CODON_BUILD_ARCHIVE\" | tar zxvf - --strip-components=1\n\nEXPORT_COMMAND=\"export PATH=$(pwd)/bin:\\$PATH\"\necho \"PATH export command:\"\necho \"  $EXPORT_COMMAND\"\n\nupdate_profile () {\n  if ! grep -F -q \"$EXPORT_COMMAND\" \"$1\"; then\n    read -p \"Update PATH in $1? [y/n] \" -n 1 -r\n    echo\n\n    if [[ $REPLY =~ ^[Yy]$ ]]; then\n      echo \"Updating $1\"\n      echo >> $1\n      echo \"# Codon compiler path (added by install script)\" >> $1\n      echo $EXPORT_COMMAND >> $1\n    else\n      echo \"Skipping.\"\n    fi\n  else\n    echo \"PATH already updated in $1; skipping update.\"\n  fi\n}\n\nif [[ \"$SHELL\" == *zsh ]]; then\n  if [ -e ~/.zshenv ]; then\n    update_profile ~/.zshenv\n  elif [ -e ~/.zshrc ]; then\n    update_profile ~/.zshrc\n  else\n    echo \"Could not find zsh configuration file to update PATH\"\n  fi\nelif [[ \"$SHELL\" == *bash ]]; then\n  if [ -e ~/.bash_profile ]; then\n    update_profile ~/.bash_profile\n  elif [ -e ~/.bash_login ]; then\n    update_profile ~/.bash_login\n  elif [ -e ~/.profile ]; then\n    update_profile ~/.profile\n  else\n    echo \"Could not find bash configuration file to update PATH\"\n  fi\nelse\n  echo \"Don't know how to update configuration file for shell $SHELL\"\nfi\n\necho \"Codon successfully installed at: $(pwd)\"\necho \"Open a new terminal session or update your PATH to use codon\"\n"
  },
  {
    "path": "stdlib/algorithms/heapsort.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\ndef _heapify(\n    arr: Array[T], begin: int, end: int, keyf: CallableTrait[[T], S], T: type, S: type\n):\n    \"\"\"\n    Makes the array a heap from [begin, end).\n    \"\"\"\n    root = begin\n    left = 2 * begin + 1\n    right = 2 * begin + 2\n\n    if left < end and keyf(arr[root]) < keyf(arr[left]):\n        root = left\n\n    if right < end and keyf(arr[root]) < keyf(arr[right]):\n        root = right\n\n    if root != begin:\n        arr[begin], arr[root] = arr[root], arr[begin]\n        _heapify(arr, root, end, keyf)\n\ndef _heap_sort(\n    arr: Array[T], begin: int, end: int, keyf: CallableTrait[[T], S], T: type, S: type\n):\n    if end - begin < 2:\n        return\n\n    arr = arr.slice(begin, end)\n    end -= begin\n    begin = 0\n\n    i = end // 2 - 1\n    while i >= 0:\n        _heapify(arr, i, end, keyf)\n        i -= 1\n\n    i = end - 1\n    while i >= 0:\n        arr[i], arr[0] = arr[0], arr[i]\n        _heapify(arr, 0, i, keyf)\n        i -= 1\n\ndef heap_sort_array(\n    collection: Array[T], size: int, keyf: CallableTrait[[T], S], T: type, S: type\n):\n    _heap_sort(collection, 0, size, keyf)\n\ndef heap_sort_inplace(\n    collection: List[T], keyf: CallableTrait[[T], S], T: type, S: type\n):\n    heap_sort_array(collection.arr, collection.len, keyf)\n\ndef heap_sort(collection: List[T], keyf: CallableTrait[[T], S], T: type, S: type) -> List[T]:\n    newlst = collection.__copy__()\n    heap_sort_inplace(newlst, keyf)\n    return newlst\n"
  },
  {
    "path": "stdlib/algorithms/insertionsort.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\ndef _insertion_sort(\n    arr: Array[T], begin: int, end: int, keyf: CallableTrait[[T], S], T: type, S: type\n):\n    i = begin + 1\n    while i < end:\n        x = arr[i]\n        j = i - 1\n        while j >= begin and keyf(x) < keyf(arr[j]):\n            arr[j + 1] = arr[j]\n            j -= 1\n        arr[j + 1] = x\n        i += 1\n\ndef insertion_sort_array(\n    collection: Array[T], size: int, keyf: CallableTrait[[T], S], T: type, S: type\n):\n    _insertion_sort(collection, 0, size, keyf)\n\ndef insertion_sort_inplace(\n    collection: List[T], keyf: CallableTrait[[T], S], T: type, S: type\n):\n    insertion_sort_array(collection.arr, collection.len, keyf)\n\ndef insertion_sort(\n    collection: List[T], keyf: CallableTrait[[T], S], T: type, S: type\n) -> List[T]:\n    newlst = collection.__copy__()\n    insertion_sort_inplace(newlst, keyf)\n    return newlst\n"
  },
  {
    "path": "stdlib/algorithms/pdqsort.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n# Parts of this file: https://github.com/orlp/pdqsort\n# License:\n#    Copyright (c) 2021 Orson Peters <orsonpeters@gmail.com>\n#\n#    This software is provided 'as-is', without any express or implied warranty. In no event will the\n#    authors be held liable for any damages arising from the use of this software.\n#\n#    Permission is granted to anyone to use this software for any purpose, including commercial\n#    applications, and to alter it and redistribute it freely, subject to the following restrictions:\n#\n#    1. The origin of this software must not be misrepresented; you must not claim that you wrote the\n#    original software. If you use this software in a product, an acknowledgment in the product\n#    documentation would be appreciated but is not required.\n#\n#    2. Altered source versions must be plainly marked as such, and must not be misrepresented as\n#    being the original software.\n#\n#    3. This notice may not be removed or altered from any source distribution.\n\nINSERTION_SORT_THRESHOLD = 24\nNINTHER_THRESHOLD = 128\nPARTIAL_INSERTION_SORT_LIMIT = 8\n\nfrom algorithms.insertionsort import _insertion_sort\nfrom algorithms.heapsort import _heap_sort\n\ndef _floor_log2(n: int) -> int:\n    log = 0\n    while True:\n        n >>= 1\n        if n == 0:\n            break\n        log += 1\n    return log\n\ndef _partial_insertion_sort(\n    arr: Array[T], begin: int, end: int, keyf: CallableTrait[[T], S], T: type, S: type\n) -> bool:\n    if begin == end:\n        return True\n\n    limit = 0\n    cur = begin + 1\n    while cur != end:\n        if limit > PARTIAL_INSERTION_SORT_LIMIT:\n            return False\n\n        sift = cur\n        sift_1 = cur - 1\n\n        if keyf(arr[sift]) < keyf(arr[sift_1]):\n            tmp = arr[sift]\n\n            while True:\n                arr[sift] = arr[sift_1]\n                sift -= 1\n                sift_1 -= 1\n                if sift == begin or not keyf(tmp) < keyf(arr[sift_1]):\n                    break\n\n            arr[sift] = tmp\n            limit += cur - sift\n\n        cur += 1\n\n    return True\n\ndef _partition_left(\n    arr: Array[T], begin: int, end: int, keyf: CallableTrait[[T], S], T: type, S: type\n) -> int:\n    pivot = arr[begin]\n    first = begin\n    last = end\n\n    while True:\n        last -= 1\n        if not keyf(pivot) < keyf(arr[last]):\n            break\n\n    if last + 1 == end:\n        while first < last:\n            first += 1\n            if keyf(pivot) < keyf(arr[first]):\n                break\n\n    else:\n        while True:\n            first += 1\n            if keyf(pivot) < keyf(arr[first]):\n                break\n\n    while first < last:\n        arr[first], arr[last] = arr[last], arr[first]\n        while True:\n            last -= 1\n            if not keyf(pivot) < keyf(arr[last]):\n                break\n        while True:\n            first += 1\n            if keyf(pivot) < keyf(arr[first]):\n                break\n\n    pivot_pos = last\n    arr[begin] = arr[pivot_pos]\n    arr[pivot_pos] = pivot\n\n    return pivot_pos\n\ndef _partition_right(\n    arr: Array[T], begin: int, end: int, keyf: CallableTrait[[T], S], T: type, S: type\n) -> Tuple[int, int]:\n    pivot = arr[begin]\n    first = begin\n    last = end\n\n    while True:\n        first += 1\n        if not keyf(arr[first]) < keyf(pivot):\n            break\n\n    if first - 1 == begin:\n        while first < last:\n            last -= 1\n            if keyf(arr[last]) < keyf(pivot):\n                break\n\n    else:\n        while True:\n            last -= 1\n            if keyf(arr[last]) < keyf(pivot):\n                break\n\n    already_partitioned = 0\n    if first >= last:\n        already_partitioned = 1\n\n    while first < last:\n        arr[first], arr[last] = arr[last], arr[first]\n\n        while True:\n            first += 1\n            if not keyf(arr[first]) < keyf(pivot):\n                break\n\n        while True:\n            last -= 1\n            if keyf(arr[last]) < keyf(pivot):\n                break\n\n    pivot_pos = first - 1\n    arr[begin] = arr[pivot_pos]\n    arr[pivot_pos] = pivot\n\n    return (pivot_pos, already_partitioned)\n\ndef _sort2(\n    arr: Array[T], i: int, j: int, keyf: CallableTrait[[T], S], T: type, S: type\n):\n    if keyf(arr[j]) < keyf(arr[i]):\n        arr[i], arr[j] = arr[j], arr[i]\n\ndef _sort3(\n    arr: Array[T], i: int, j: int, k: int, keyf: CallableTrait[[T], S], T: type, S: type\n):\n    _sort2(arr, i, j, keyf)\n    _sort2(arr, j, k, keyf)\n    _sort2(arr, i, j, keyf)\n\ndef _pdq_sort(\n    arr: Array[T],\n    begin: int,\n    end: int,\n    keyf: CallableTrait[[T], S],\n    bad_allowed: int,\n    leftmost: bool,\n    T: type,\n    S: type,\n):\n    while True:\n        size = end - begin\n        if size < INSERTION_SORT_THRESHOLD:\n            _insertion_sort(arr, begin, end, keyf)\n            return\n\n        size_2 = size // 2\n        if size > NINTHER_THRESHOLD:\n            _sort3(arr, begin, begin + size_2, end - 1, keyf)\n            _sort3(arr, begin + 1, begin + (size_2 - 1), end - 2, keyf)\n            _sort3(arr, begin + 2, begin + (size_2 + 1), end - 3, keyf)\n            _sort3(\n                arr, begin + (size_2 - 1), begin + size_2, begin + (size_2 + 1), keyf\n            )\n            arr[begin], arr[begin + size_2] = arr[begin + size_2], arr[begin]\n        else:\n            _sort3(arr, begin + size_2, begin, end - 1, keyf)\n\n        if not leftmost and not keyf(arr[begin - 1]) < keyf(arr[begin]):\n            begin = _partition_left(arr, begin, end, keyf) + 1\n            continue\n\n        part_result = _partition_right(arr, begin, end, keyf)\n        pivot_pos = part_result[0]\n        already_partitioned = part_result[1] == 1\n\n        l_size = pivot_pos - begin\n        r_size = end - (pivot_pos + 1)\n        highly_unbalanced = (l_size < (size // 8)) or (r_size < (size // 8))\n\n        if highly_unbalanced:\n            bad_allowed -= 1\n            if bad_allowed == 0:\n                _heap_sort(arr, begin, end, keyf)\n                return\n\n            if l_size >= INSERTION_SORT_THRESHOLD:\n                arr[begin], arr[begin + l_size // 4] = (\n                    arr[begin + l_size // 4],\n                    arr[begin],\n                )\n                arr[pivot_pos - 1], arr[pivot_pos - l_size // 4] = (\n                    arr[pivot_pos - l_size // 4],\n                    arr[pivot_pos - 1],\n                )\n\n                if l_size > NINTHER_THRESHOLD:\n                    arr[begin + 1], arr[begin + (l_size // 4 + 1)] = (\n                        arr[begin + (l_size // 4 + 1)],\n                        arr[begin + 1],\n                    )\n                    arr[begin + 2], arr[begin + (l_size // 4 + 2)] = (\n                        arr[begin + (l_size // 4 + 2)],\n                        arr[begin + 2],\n                    )\n                    arr[pivot_pos - 2], arr[pivot_pos - (l_size // 4 + 1)] = (\n                        arr[pivot_pos - (l_size // 4 + 1)],\n                        arr[pivot_pos - 2],\n                    )\n                    arr[pivot_pos - 3], arr[pivot_pos - (l_size // 4 + 2)] = (\n                        arr[pivot_pos - (l_size // 4 + 2)],\n                        arr[pivot_pos - 3],\n                    )\n\n            if r_size >= INSERTION_SORT_THRESHOLD:\n                arr[pivot_pos + 1], arr[pivot_pos + (1 + r_size // 4)] = (\n                    arr[pivot_pos + (1 + r_size // 4)],\n                    arr[pivot_pos + 1],\n                )\n                arr[end - 1], arr[end - r_size // 4] = (\n                    arr[end - r_size // 4],\n                    arr[end - 1],\n                )\n\n                if r_size > NINTHER_THRESHOLD:\n                    arr[pivot_pos + 2], arr[pivot_pos + (2 + r_size // 4)] = (\n                        arr[pivot_pos + (2 + r_size // 4)],\n                        arr[pivot_pos + 2],\n                    )\n                    arr[pivot_pos + 3], arr[pivot_pos + (3 + r_size // 4)] = (\n                        arr[pivot_pos + (3 + r_size // 4)],\n                        arr[pivot_pos + 3],\n                    )\n                    arr[end - 2], arr[end - (1 + r_size // 4)] = (\n                        arr[end - (1 + r_size // 4)],\n                        arr[end - 2],\n                    )\n                    arr[end - 3], arr[end - (2 + r_size // 4)] = (\n                        arr[end - (2 + r_size // 4)],\n                        arr[end - 3],\n                    )\n\n        else:\n            if (\n                already_partitioned\n                and _partial_insertion_sort(arr, begin, pivot_pos, keyf)\n                and _partial_insertion_sort(arr, pivot_pos + 1, end, keyf)\n            ):\n                return\n\n        _pdq_sort(arr, begin, pivot_pos, keyf, bad_allowed, leftmost)\n        begin = pivot_pos + 1\n        leftmost = False\n\ndef pdq_sort_array(\n    collection: Array[T], size: int, keyf: CallableTrait[[T], S], T: type, S: type\n):\n    _pdq_sort(collection, 0, size, keyf, _floor_log2(size), True)\n\ndef pdq_sort_inplace(\n    collection: List[T], keyf: CallableTrait[[T], S], T: type, S: type\n):\n    pdq_sort_array(collection.arr, collection.len, keyf)\n\ndef pdq_sort(collection: List[T], keyf: CallableTrait[[T], S], T: type, S: type) -> List[T]:\n    newlst = collection.__copy__()\n    pdq_sort_inplace(newlst, keyf)\n    return newlst\n"
  },
  {
    "path": "stdlib/algorithms/qsort.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n# Parts of this file: https://github.com/orlp/pdqsort\n# License:\n#    Copyright (c) 2021 Orson Peters <orsonpeters@gmail.com>\n#\n#    This software is provided 'as-is', without any express or implied warranty. In no event will the\n#    authors be held liable for any damages arising from the use of this software.\n#\n#    Permission is granted to anyone to use this software for any purpose, including commercial\n#    applications, and to alter it and redistribute it freely, subject to the following restrictions:\n#\n#    1. The origin of this software must not be misrepresented; you must not claim that you wrote the\n#    original software. If you use this software in a product, an acknowledgment in the product\n#    documentation would be appreciated but is not required.\n#\n#    2. Altered source versions must be plainly marked as such, and must not be misrepresented as\n#    being the original software.\n#\n#    3. This notice may not be removed or altered from any source distribution.\n\ndef _med3(\n    a: int, b: int, c: int, d: Array[T], k: CallableTrait[[T], S], T: type, S: type\n) -> int:\n    if k(d[a]) < k(d[b]):\n        return b if (k(d[b]) < k(d[c])) else (c if k(d[a]) < k(d[c]) else a)\n    else:\n        return (\n            b\n            if not (k(d[b]) < k(d[c]) or k(d[b]) == k(d[c]))\n            else (c if not (k(d[a]) < k(d[c]) or k(d[a]) == k(d[c])) else a)\n        )\n\ndef _swap(i: int, j: int, a: Array[T], T: type):\n    a[i], a[j] = a[j], a[i]\n\ndef _vecswap(i: int, j: int, n: int, a: Array[T], T: type):\n    while n > 0:\n        _swap(i, j, a)\n        i += 1\n        j += 1\n        n -= 1\n\ndef _qsort(\n    arr: Array[T], frm: int, cnt: int, key: CallableTrait[[T], S], T: type, S: type\n):\n    if cnt <= 7:\n        i = frm + 1\n        while i < frm + cnt:\n            j = i\n            while j > frm and not (\n                key(arr[j - 1]) < key(arr[j]) or key(arr[j - 1]) == key(arr[j])\n            ):\n                _swap(j, j - 1, arr)\n                j -= 1\n            i += 1\n        return\n\n    mid = cnt // 2\n    lo = frm\n    hi = frm + cnt - 1\n\n    if cnt > 40:\n        s = cnt // 8\n        lo = _med3(lo, lo + s, lo + 2 * s, arr, key)\n        mid = _med3(mid - s, mid, mid + s, arr, key)\n        hi = _med3(hi - 2 * s, hi - s, hi, arr, key)\n    mid = _med3(lo, mid, hi, arr, key)\n\n    _swap(frm, mid, arr)\n    a = frm\n    b = a\n    c = frm + cnt - 1\n    d = c\n\n    while True:\n        while b <= c and (\n            key(arr[b]) < key(arr[frm]) or key(arr[b]) == key(arr[frm])\n        ):\n            if key(arr[b]) == key(arr[frm]):\n                _swap(a, b, arr)\n                a += 1\n            b += 1\n\n        while c >= b and not key(arr[c]) < key(arr[frm]):\n            if key(arr[c]) == key(arr[frm]):\n                _swap(c, d, arr)\n                d -= 1\n            c -= 1\n\n        if b > c:\n            break\n        _swap(b, c, arr)\n        b += 1\n        c -= 1\n\n    hi = frm + cnt\n    span = min(a - frm, b - a)\n    _vecswap(frm, b - span, span, arr)\n\n    span = min(d - c, hi - d - 1)\n    _vecswap(b, hi - span, span, arr)\n\n    span = b - a\n    if span > 1:\n        _qsort(arr, frm, span, key)\n\n    span = d - c\n    if span > 1:\n        _qsort(arr, hi - span, span, key)\n\ndef qsort_array(\n    collection: Array[T], size: int, key: CallableTrait[[T], S], T: type, S: type\n):\n    _qsort(collection, 0, size, key)\n\ndef qsort_inplace(\n    collection: List[T], key: CallableTrait[[T], S], T: type, S: type\n):\n    qsort_array(collection.arr, collection.len, key)\n\ndef qsort(collection: List[T], key: CallableTrait[[T], S], T: type, S: type) -> List[T]:\n    collection = collection[:]\n    qsort_inplace(collection, key)\n    return collection\n"
  },
  {
    "path": "stdlib/algorithms/strings.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n# Implementation of vectorized Rabin-Karp string search.\n# See http://0x80.pl/articles/simd-strfind.html for\n# details. These implementations are modified to not\n# perform any out-of-bounds memory accesses.\n\n@pure\n@llvm\ndef cttz(n: UInt[N], N: Literal[int]) -> UInt[N]:\n    declare i{=N} @llvm.cttz.i{=N}(i{=N}, i1)\n    %0 = call i{=N} @llvm.cttz.i{=N}(i{=N} %n, i1 true)\n    ret i{=N} %0\n\n@pure\n@llvm\ndef ctlz(n: UInt[N], N: Literal[int]) -> UInt[N]:\n    declare i{=N} @llvm.ctlz.i{=N}(i{=N}, i1)\n    %0 = call i{=N} @llvm.ctlz.i{=N}(i{=N} %n, i1 true)\n    ret i{=N} %0\n\n@pure\n@llvm\ndef forward_mask(s: Ptr[byte], n: int, needle: Ptr[byte], k: int, i: int, firstb: byte, lastb: byte) -> u16:\n    %first0 = insertelement <16 x i8> undef, i8 %firstb, i64 0\n    %first = shufflevector <16 x i8> %first0, <16 x i8> poison, <16 x i32> zeroinitializer\n    %last0 = insertelement <16 x i8> undef, i8 %lastb, i64 0\n    %last = shufflevector <16 x i8> %last0, <16 x i8> poison, <16 x i32> zeroinitializer\n    %offset0 = add i64 %i, %k\n    %offset = sub i64 %offset0, 1\n    %ptr_first = getelementptr inbounds i8, ptr %s, i64 %i\n    %ptr_last = getelementptr inbounds i8, ptr %s, i64 %offset\n    %block_first = load <16 x i8>, ptr %ptr_first, align 1\n    %block_last = load <16 x i8>, ptr %ptr_last, align 1\n    %eq_first = icmp eq <16 x i8> %first, %block_first\n    %eq_last = icmp eq <16 x i8> %last, %block_last\n    %mask0 = and <16 x i1> %eq_first, %eq_last\n    %mask = bitcast <16 x i1> %mask0 to i16\n    ret i16 %mask\n\n@pure\n@llvm\ndef backward_mask(s: Ptr[byte], n: int, needle: Ptr[byte], k: int, i: int, firstb: byte, lastb: byte) -> u16:\n    %j0 = sub i64 %i, 16\n    %j = add i64 %j0, 1\n    %first0 = insertelement <16 x i8> undef, i8 %firstb, i64 0\n    %first = shufflevector <16 x i8> %first0, <16 x i8> poison, <16 x i32> zeroinitializer\n    %last0 = insertelement <16 x i8> undef, i8 %lastb, i64 0\n    %last = shufflevector <16 x i8> %last0, <16 x i8> poison, <16 x i32> zeroinitializer\n    %offset0 = sub i64 %j, %k\n    %offset = add i64 %offset0, 1\n    %ptr_first = getelementptr inbounds i8, ptr %s, i64 %offset\n    %ptr_last = getelementptr inbounds i8, ptr %s, i64 %j\n    %block_first = load <16 x i8>, ptr %ptr_first, align 1\n    %block_last = load <16 x i8>, ptr %ptr_last, align 1\n    %eq_first = icmp eq <16 x i8> %last, %block_last\n    %eq_last = icmp eq <16 x i8> %first, %block_first\n    %mask0 = and <16 x i1> %eq_first, %eq_last\n    %mask = bitcast <16 x i1> %mask0 to i16\n    ret i16 %mask\n\ndef forward_find(s: Ptr[byte], n: int, needle: Ptr[byte], k: int):\n    if k == 0:\n        return 0\n\n    if n < k:\n        return -1\n\n    if k == 1:\n        p = _C.memchr(s, i32(int(needle[0])), n)\n        return p - s if p else -1\n\n    firstb = needle[0]\n    lastb = needle[k - 1]\n    i = 0\n\n    while i + k + 16 - 1 <= n:\n        mask = forward_mask(s, n, needle, k, i, firstb, lastb)\n        while mask:\n            bitpos = int(cttz(mask))\n            if _C.memcmp(s + i + bitpos + 1, needle + 1, k - 2) == i32(0):\n                return i + bitpos\n            mask = mask & (mask - u16(1))\n        i += 16\n\n    # unrolled by hand\n    while True:\n        j = i + 0\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        j = i + 1\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        j = i + 2\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        j = i + 3\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        j = i + 4\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        j = i + 5\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        j = i + 6\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        j = i + 7\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        j = i + 8\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        j = i + 9\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        j = i + 10\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        j = i + 11\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        j = i + 12\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        j = i + 13\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        j = i + 14\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        j = i + 15\n        if j + k <= n:\n            if firstb == s[j] and lastb == s[j + k - 1] and _C.memcmp(s + j + 1, needle + 1, k - 2) == i32(0):\n                return j\n        else:\n            break\n\n        break\n\n    return -1\n\ndef backward_find(s: Ptr[byte], n: int, needle: Ptr[byte], k: int):\n    if k == 0:\n        return n\n\n    if n < k:\n        return -1\n\n    if k == 1:\n        i = n - 1\n        while i >= 0:\n            if s[i] == needle[0]:\n                return i\n            i -= 1\n        return -1\n\n    firstb = needle[0]\n    lastb = needle[k - 1]\n    i = n - 1\n\n    while i - (k - 1) - (16 - 1) >= 0:\n        mask = backward_mask(s, n, needle, k, i, firstb, lastb)\n        while mask:\n            bitpos = int(ctlz(mask))\n            if _C.memcmp(s + i - (k - 1) - bitpos + 1, needle + 1, k - 2) == i32(0):\n                return i - (k - 1) - bitpos\n            mask &= ~(u16(1) << u16(16 - 1 - bitpos))\n        i -= 16\n\n    # unrolled by hand\n    while True:\n        j = i - 0\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        j = i - 1\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        j = i - 2\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        j = i - 3\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        j = i - 4\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        j = i - 5\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        j = i - 6\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        j = i - 7\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        j = i - 8\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        j = i - 9\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        j = i + 10\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        j = i - 11\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        j = i - 12\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        j = i - 13\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        j = i - 14\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        j = i - 15\n        if j - k + 1 >= 0:\n            if lastb == s[j] and firstb == s[j - k + 1] and _C.memcmp(s + j - k + 2, needle + 1, k - 2) == i32(0):\n                return j - k + 1\n        else:\n            break\n\n        break\n\n    return -1\n\ndef find(haystack: str, needle: str):\n    return forward_find(haystack.ptr, haystack.len, needle.ptr, needle.len)\n\ndef rfind(haystack: str, needle: str):\n    return backward_find(haystack.ptr, haystack.len, needle.ptr, needle.len)\n\ndef count(haystack: str, needle: str):\n    occ = 0\n    tmp = haystack.ptr\n    n = haystack.len\n    k = needle.len\n\n    if k == 0:\n        return n + 1\n\n    while True:\n        pos = forward_find(tmp, n - (tmp - haystack.ptr), needle.ptr, k)\n        if pos == -1:\n            break\n        tmp += pos + k\n        occ += 1\n    return occ\n\ndef count_with_max(haystack: str, needle: str, maxcount: int):\n    occ = 0\n    tmp = haystack.ptr\n    n = haystack.len\n    k = needle.len\n\n    if maxcount == 0:\n        return 0\n\n    if k == 0:\n        return n + 1 if n + 1 < maxcount else maxcount\n\n    while True:\n        pos = forward_find(tmp, n - (tmp - haystack.ptr), needle.ptr, k)\n        if pos == -1:\n            break\n        tmp += pos + k\n        occ += 1\n        if occ == maxcount:\n            return occ\n    return occ\n"
  },
  {
    "path": "stdlib/algorithms/timsort.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n# Pats of this file: (c) 2022 Python Software Foundation. All right reserved.\n# License:\n#    1. This LICENSE AGREEMENT is between the Python Software Foundation (\"PSF\"), and\n#    the Individual or Organization (\"Licensee\") accessing and otherwise using Python\n#    3.10.2 software in source or binary form and its associated documentation.\n#\n#    2. Subject to the terms and conditions of this License Agreement, PSF hereby\n#    grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,\n#    analyze, test, perform and/or display publicly, prepare derivative works,\n#    distribute, and otherwise use Python 3.10.2 alone or in any derivative\n#    version, provided, however, that PSF's License Agreement and PSF's notice of\n#    copyright, i.e., \"Copyright © 2001-2022 Python Software Foundation; All Rights\n#    Reserved\" are retained in Python 3.10.2 alone or in any derivative version\n#    prepared by Licensee.\n#\n#    3. In the event Licensee prepares a derivative work that is based on or\n#    incorporates Python 3.10.2 or any part thereof, and wants to make the\n#    derivative work available to others as provided herein, then Licensee hereby\n#    agrees to include in any such work a brief summary of the changes made to Python\n#    3.10.2.\n#\n#    4. PSF is making Python 3.10.2 available to Licensee on an \"AS IS\" basis.\n#    PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.  BY WAY OF\n#    EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR\n#    WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE\n#    USE OF PYTHON 3.10.2 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.\n#\n#    5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.10.2\n#    FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF\n#    MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.10.2, OR ANY DERIVATIVE\n#    THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.\n#\n#    6. This License Agreement will automatically terminate upon a material breach of\n#    its terms and conditions.\n#\n#    7. Nothing in this License Agreement shall be deemed to create any relationship\n#    of agency, partnership, or joint venture between PSF and Licensee.  This License\n#    Agreement does not grant permission to use PSF trademarks or trade name in a\n#    trademark sense to endorse or promote products or services of Licensee, or any\n#    third party.\n#\n#    8. By copying, installing or otherwise using Python 3.10.2, Licensee agrees\n#    to be bound by the terms and conditions of this License Agreement.\n#\n# Timsort by Tim Peters, published at https://github.com/python/cpython/blob/master/Objects/listobject.c#L2187\n\nBLOCK_SIZE = 64\nCACHELINE_SIZE = 64\nMIN_GALLOP = 7\n\nfrom algorithms.insertionsort import _insertion_sort\n\ndef _count_run(\n    arr: Array[T], begin: int, end: int, keyf: CallableTrait[[T], S], T: type, S: type\n) -> Tuple[int, int]:\n    \"\"\"\n    Returns the # of elements in the next run and if the run is \"inorder\" or \"reversed\"\n    \"\"\"\n    inorder = 1\n\n    if end - begin == 1:\n        return 1, inorder\n\n    n = 2\n    i = begin + 1\n    if keyf(arr[i - 1]) >= keyf(arr[i]):\n        inorder = 0\n        i += 1\n        while i < end:\n            if keyf(arr[i - 1]) < keyf(arr[i]):\n                break\n            i += 1\n            n += 1\n\n    else:\n        i += 1\n        while i < end:\n            if keyf(arr[i - 1]) >= keyf(arr[i]):\n                break\n            i += 1\n            n += 1\n\n    return n, inorder\n\ndef _merge_compute_minrun(n: int) -> int:\n    \"\"\"\n    Computes the minrun for Timsort\n    \"\"\"\n    r = 0\n    while n >= 64:\n        r |= n & 1\n        n >>= 1\n    return n + r\n\ndef _reverse_sortslice(arr: Array[T], begin: int, end: int, T: type):\n    if end - begin < 2:\n        return\n    arr[begin], arr[end - 1] = arr[end - 1], arr[begin]\n    _reverse_sortslice(arr, begin + 1, end - 1)\n\ndef _modified_comp(\n    a: T, b: T, keyf: CallableTrait[[T], S], left: bool, T: type, S: type\n) -> bool:\n    \"\"\"\n    Abstracts the left or right compare in gallop\n    \"\"\"\n    if left:\n        return keyf(b) >= keyf(a)\n    else:\n        return keyf(a) < keyf(b)\n\ndef _gallop(\n    arr: Array[T],\n    a: Tuple[int, int],\n    b: Tuple[int, int],\n    keyf: CallableTrait[[T], S],\n    hint: int,\n    left: bool,\n    T: type,\n    S: type,\n) -> int:\n    \"\"\"\n    Gallop for Timsort\n    \"\"\"\n\n    key = arr[0]  # just to initialize k\n    if left:\n        key = arr[b[0] + b[1] - 1]\n    else:\n        key = arr[b[0]]\n\n    curr = a[0] + hint\n    ofs, lastofs = 1, 0\n\n    if _modified_comp(key, arr[curr], keyf, left):\n        # Gallop left\n        maxofs = hint + 1\n\n        while ofs < maxofs:\n            if _modified_comp(key, arr[curr - ofs], keyf, left):\n                lastofs = ofs\n                ofs = (ofs << 1) + 1\n            else:\n                break\n\n        if ofs > maxofs:\n            ofs = maxofs\n\n        ofs, lastofs = hint - lastofs, hint - ofs\n\n    else:\n        # Gallop right\n        maxofs = a[1] - hint\n\n        while ofs < maxofs:\n            if _modified_comp(key, arr[curr + ofs], keyf, left):\n                break\n            lastofs = ofs\n            ofs = (ofs << 1) + 1\n\n        if ofs > maxofs:\n            ofs = maxofs\n\n        lastofs += hint\n        ofs += hint\n\n    lastofs += 1\n    while lastofs < ofs:\n        m = lastofs + ((ofs - lastofs) >> 1)\n        if _modified_comp(key, arr[a[0] + m], keyf, left):\n            ofs = m\n        else:\n            lastofs = m + 1\n\n    return ofs\n\ndef _merge_with_gallop(\n    arr: Array[T],\n    a: Tuple[int, int],\n    b: Tuple[int, int],\n    keyf: CallableTrait[[T], S],\n    T: type,\n    S: type,\n):\n    min_gallop = MIN_GALLOP\n\n    combined = Array[T](a[1] + b[1])\n    a_copy = combined.slice(0, a[1])\n    b_copy = combined.slice(a[1], len(combined))\n\n    j = 0\n    for i in range(a[0], a[0] + a[1]):\n        combined[j] = arr[i]\n        j += 1\n    for i in range(b[0], b[0] + b[1]):\n        combined[j] = arr[i]\n        j += 1\n\n    i, j, k = 0, 0, a[0]\n\n    while i < len(a_copy) and j < len(b_copy):\n        acount, bcount = 0, 0\n\n        while i < len(a_copy) and j < len(b_copy):\n            if keyf(b_copy[j]) < keyf(a_copy[i]):\n                arr[k] = b_copy[j]\n                acount = 0\n                bcount += 1\n                j += 1\n                k += 1\n                if bcount >= min_gallop:\n                    break\n            else:\n                arr[k] = a_copy[i]\n                acount += 1\n                bcount = 0\n                i += 1\n                k += 1\n                if acount >= min_gallop:\n                    break\n\n        if i == len(a_copy) or j == len(b_copy):\n            break\n\n        min_gallop += 1\n\n        while i < len(a_copy) and j < len(b_copy):\n            if min_gallop > 1:\n                min_gallop -= 1\n\n            acount = _gallop(\n                combined, (0, len(a_copy)), (len(a_copy), len(b_copy)), keyf, i, False\n            )\n            if acount:\n                while i < acount:\n                    arr[k] = a_copy[i]\n                    i += 1\n                    k += 1\n                arr[k] = b_copy[j]\n                j += 1\n                k += 1\n\n            if i == len(a_copy) or j == len(b_copy):\n                break\n\n            b_end = _gallop(\n                combined, (len(a_copy), len(b_copy)), (0, len(a_copy)), keyf, j, True\n            )\n            bcount = len(b_copy) - b_end\n            if bcount:\n                while j < b_end:\n                    arr[k] = b_copy[j]\n                    j += 1\n                    k += 1\n                arr[k] = a_copy[i]\n                i += 1\n                k += 1\n\n            if acount < MIN_GALLOP and bcount < MIN_GALLOP:\n                break\n\n        min_gallop += 1\n\n    while i < len(a_copy):\n        arr[k] = a_copy[i]\n        i += 1\n        k += 1\n\n    while j < len(b_copy):\n        arr[k] = b_copy[j]\n        j += 1\n        k += 1\n\ndef _merge_at(\n    arr: Array[T],\n    a: Tuple[int, int],\n    b: Tuple[int, int],\n    keyf: CallableTrait[[T], S],\n    T: type,\n    S: type,\n):\n    start_a, len_a = a\n    start_b, len_b = b\n\n    # Where does b start in a?\n    k = _gallop(arr, a, b, keyf, 0, False)\n    start_a, len_a = start_a + k, len_a - k\n    if len_a == 0:\n        return\n\n    # Where does a end in b?\n    len_b = _gallop(arr, b, a, keyf, len_b - 1, True)\n    if len_b == 0:\n        return\n\n    _merge_with_gallop(arr, (start_a, len_a), (start_b, len_b), keyf)\n\ndef _merge_collapse(\n    arr: Array[T],\n    stack: List[Tuple[int, int]],\n    keyf: CallableTrait[[T], S],\n    T: type,\n    S: type,\n):\n    if len(stack) <= 1:\n        return\n\n    while len(stack) > 2:\n        X = stack[-3]\n        Y = stack[-2]\n        Z = stack[-1]\n\n        if X[1] > Y[1] + Z[1] and Y[1] > Z[1]:\n            break\n\n        C = stack.pop()\n        B = stack.pop()\n        A = stack.pop()\n\n        if A[1] <= B[1] + C[1]:\n            if A[1] < C[1]:\n                _merge_at(arr, A, B, keyf)\n                stack.append((A[0], A[1] + B[1]))\n                stack.append(C)\n            else:\n                _merge_at(arr, B, C, keyf)\n                stack.append(A)\n                stack.append((B[0], B[1] + C[1]))\n\n        else:\n            _merge_at(arr, B, C, keyf)\n            stack.append(A)\n            stack.append((B[0], B[1] + C[1]))\n\n    if len(stack) == 2:\n        X = stack[-2]\n        Y = stack[-1]\n\n        if X[1] <= Y[1]:\n            C = stack.pop()\n            B = stack.pop()\n            _merge_at(arr, B, C, keyf)\n            stack.append((B[0], B[1] + C[1]))\n            return\n\ndef _final_merge(\n    arr: Array[T],\n    stack: List[Tuple[int, int]],\n    keyf: CallableTrait[[T], S],\n    T: type,\n    S: type,\n):\n    while len(stack) > 1:\n        C = stack.pop()\n        B = stack.pop()\n        _merge_at(arr, B, C, keyf)\n        stack.append((B[0], B[1] + C[1]))\n\ndef _tim_sort(\n    arr: Array[T], begin: int, end: int, keyf: CallableTrait[[T], S], T: type, S: type\n):\n    if end - begin < 2:\n        return\n\n    merge_pending = List[Tuple[int, int]]()\n    minrun = _merge_compute_minrun(end - begin)\n    i = begin\n    while i < end:\n        n, inorder = _count_run(arr, i, end, keyf)\n        if not inorder:\n            _reverse_sortslice(arr, i, i + n)\n\n        if n < minrun:\n            force = min(minrun, end - i)\n            _insertion_sort(arr, i, i + force, keyf)\n            n = force\n\n        merge_pending.append((i, n))\n        _merge_collapse(arr, merge_pending, keyf)\n        i += n\n\n    _final_merge(arr, merge_pending, keyf)\n\ndef tim_sort_array(\n    collection: Array[T], size: int, keyf: CallableTrait[[T], S], T: type, S: type\n):\n    _tim_sort(collection, 0, size, keyf)\n\ndef tim_sort_inplace(\n    collection: List[T], keyf: CallableTrait[[T], S], T: type, S: type\n):\n    tim_sort_array(collection.arr, collection.len, keyf)\n\ndef tim_sort(collection: List[T], keyf: CallableTrait[[T], S], T: type, S: type) -> List[T]:\n    newlst = list(collection)\n    tim_sort_inplace(newlst, keyf)\n    return newlst\n"
  },
  {
    "path": "stdlib/asyncio.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom threading import Lock, ThreadLocal\nfrom time import time as _time, sleep as _sleep\nfrom sys import stderr as _stderr\nimport internal.gc as gc\nimport internal.static as static\n\n_FUTURE_STATE_PENDING:   Literal[int] = 0\n_FUTURE_STATE_FINISHED:  Literal[int] = 1\n_FUTURE_STATE_EXCEPTION: Literal[int] = 2\n_FUTURE_STATE_CANCELLED: Literal[int] = 3\n\n@pure\n@derives\n@llvm\ndef _bitcast(x: T, D: type, T: type) -> D:\n    %y = bitcast {=T} %x to {=D}\n    ret {=D} %y\n\n@tuple\nclass WorkItem:\n    coro: cobj        # Coroutine handle\n    task: cobj        # Raw Task pointer, or null\n    result_size: int  # Size in bytes of enclosed Future/Task result\n\nclass WorkNode:\n    work: WorkItem\n    prev: Optional[WorkNode]\n    next: Optional[WorkNode]\n\n    def __init__(self, work: WorkItem):\n        self.work = work\n        self.prev = None\n        self.next = None\n\n    def cancelled(self):\n        return self.work.cancelled()\n\nclass Timer:\n    work: WorkItem\n    when: float\n\n    def cancelled(self):\n        return self.work.cancelled()\n\nclass EventLoop:\n    _lock: Lock\n    _work_head: Optional[WorkNode]\n    _work_tail: Optional[WorkNode]\n    _work_curr: Optional[WorkItem]\n    _timers: Ptr[Timer]\n    _timers_len: int\n    _timers_cap: int\n    _running: bool\n    _closed: bool\n    _stop_flag: bool\n\n@tuple\nclass Handle:\n    _node: WorkNode\n    _loop: EventLoop\n\n    def cancel(self):\n        loop = self._loop\n        with loop._lock:\n            if not self.cancelled():\n                loop._cancel(self._node)\n\n    def cancelled(self):\n        return self._node.cancelled()\n\n@tuple\nclass TimerHandle:\n    _timer: Timer\n    _loop: EventLoop\n\n    def cancel(self):\n        loop = self._loop\n        with loop._lock:\n            if not self.cancelled():\n                loop._cancel(self._timer)\n\n    def cancelled(self):\n        return self._timer.cancelled()\n\n    def when(self):\n        return self._timer.when\n\nclass Future:\n    _result: R\n    _exception: Optional[BaseException]\n    _lock: Lock\n    _loop: EventLoop\n    _cancel_msg: str\n    _done_callbacks: Ptr[WorkItem]\n    _done_callbacks_len: int\n    _done_callbacks_cap: int\n    _state: int\n    R: type\n\nclass Task(Static[Future[R]]):\n    _name: str\n    _coro: cobj\n    _waiting_on_task: Optional[Task[None]]\n    _waiting_on_lock: cobj\n    R: type\n\ndef _raw_task(task: Task) -> Task[None]:\n    # Skip over `result` field\n    return _bitcast(task.__raw__() + gc.sizeof(task.R), Task[None])\n\n@extend\nclass WorkItem:\n    def __new__(coro: cobj):\n        return WorkItem(coro, cobj(), 0)\n\n    def __new__(coro: Coroutine):\n        return WorkItem(coro.__raw__(), cobj(), 0)\n\n    def __new__(task: Task):\n        data = task.__raw__()\n        coro = task._coro\n        result_size = gc.sizeof(task.R)\n        return WorkItem(coro, data, result_size)\n\n    def __new__():\n        return WorkItem(cobj())\n\n    def raw_task(self) -> Task[None]:\n        # Skip over `result` field\n        return _bitcast(self.task + self.result_size, Task[None])\n\n    def cancelled(self):\n        return not bool(self.coro)\n\nclass InvalidStateError(Exception):\n    def __init__(self, message: str = ''):\n        super().__init__(message)\n\nclass CancelledError(BaseException):\n    def __init__(self, message: str = ''):\n        super().__init__(message)\n\nasync def _callback_wrapper(callback, *args):\n    callback(*args)\n\nasync def _future_callback_wrapper(callback, future):\n    callback(future)\n\n_current_loop: ThreadLocal[Optional[EventLoop]] = None\n_running_loop: ThreadLocal[Optional[EventLoop]] = None\n\n@tuple\nclass _EnterLoop:\n    loop: EventLoop\n    old_current_loop: Optional[EventLoop]\n    old_running_loop: Optional[EventLoop]\n\n    def __new__(loop: EventLoop):\n        return _EnterLoop(loop, _current_loop, _running_loop)\n\n    def __enter__(self):\n        global _current_loop\n        global _running_loop\n        _current_loop = self.loop\n        _running_loop = self.loop\n\n    def __exit__(self):\n        global _current_loop\n        global _running_loop\n        _current_loop = self.old_current_loop\n        _running_loop = self.old_running_loop\n\n@extend\nclass EventLoop:\n    def __init__(self):\n        TIMERS_CAP_INIT: Literal[int] = 8  # must be power of 2\n        self._lock = Lock()\n        self._work_head = None\n        self._work_tail = None\n        self._work_curr = None\n        self._timers = Ptr[Timer](TIMERS_CAP_INIT)\n        self._timers_len = 0\n        self._timers_cap = TIMERS_CAP_INIT\n        self._running = False\n        self._closed = False\n        self._stop_flag = False\n\n    def _ensure_open(self):\n        if self._closed:\n            raise RuntimeError(\"Event loop is closed\")\n\n    def _ensure_not_running(self):\n        if self._running:\n            raise RuntimeError(\"This event loop is already running\")\n\n    def close(self):\n        with self._lock:\n            if not self._closed:\n                self._work_head = None\n                self._work_tail = None\n                self._work_curr = None\n                self._timers = Ptr[Timer]()\n                self._timers_len = 0\n                self._timers_cap = 0\n                self._running = False\n                self._closed = True\n                self._stop_flag = True\n\n    def time(self):\n        return _time()\n\n    def stop(self):\n        with self._lock:\n            self._stop_flag = True\n\n    def is_running(self):\n        return self._running\n\n    def is_closed(self):\n        return self._closed\n\n    def _call_soon(self, work: WorkItem, return_handle: Literal[bool] = False):\n        with self._lock:\n            self._ensure_open()\n            node = self._work_enqueue(work)\n        if return_handle:\n            return Handle(node, self)\n\n    def _call_soon_no_duplicate(self, work: WorkItem):\n        with self._lock:\n            self._ensure_open()\n            node = self._work_head\n            while node is not None:\n                if node.work.coro == work.coro:\n                    return False\n                node = node.next\n            self._work_enqueue(work)\n            return True\n\n    def call_soon(self, callback, *args):\n        item = WorkItem(_callback_wrapper(callback, *args))\n        return self._call_soon(item, return_handle=True)\n\n    def call_soon_threadsafe(self, callback, *args):\n        with self._lock:\n            return self.call_soon(callback, *args)\n\n    def _call_later(self, work: WorkItem, delay: float, return_handle: Literal[bool] = False):\n        timer = Timer(work, self.time() + delay)\n        with self._lock:\n            self._ensure_open()\n            self._timers_push(timer)\n        if return_handle:\n            return TimerHandle(timer, self)\n\n    def call_later(self, delay: float, callback, *args):\n        item = WorkItem(_callback_wrapper(callback, *args))\n        return self._call_later(item, delay, return_handle=True)\n\n    def call_at(self, delay: float, callback, *args):\n        return self.call_later(delay, callback, *args)\n\n    def _handle_exception(self, exc: BaseException):\n        rtti = _bitcast(Ptr[cobj](exc.__raw__())[1], TypeInfo)\n        _stderr.write(f'{rtti.nice_name}: {exc}\\n')\n\n    def _enqueue_timers(self, now: float):\n        while self._timers_len > 0 and self._timers[0].when <= now:\n            timer = self._timers_pop()\n            if not timer.cancelled():\n                self._work_enqueue(timer.work)\n\n    def _step(self):\n        work: Optional[WorkItem] = None\n        now = self.time()\n        stop = False\n\n        with self._lock:\n            self._enqueue_timers(now)\n\n            if not self._work_empty():\n                work = self._work_dequeue()\n                self._work_curr = work\n\n            stop = self._stop_flag\n\n        if stop:\n            return True\n\n        if work is not None:\n            g = Generator[None](work.coro)\n            exception: Optional[BaseException] = None\n\n            try:\n                g.__resume__()\n            except CancelledError:\n                pass\n            except SystemExit:\n                self._work_curr = None\n                raise\n            except AssertionError:\n                self._work_curr = None\n                raise\n            except BaseException as e:\n                exception = e\n\n            if work.task:\n                task = work.raw_task()\n                callbacks = Ptr[WorkItem]()\n                num_callbacks = 0\n\n                with task._lock:\n                    if not task.done():\n                        if exception is not None:\n                                callbacks, num_callbacks = \\\n                                    task._finish_with_exception(exception)\n                        elif g.__done__():\n                                str.memcpy(work.task,\n                                           g.__promise__().as_byte(),\n                                           work.result_size)\n                                callbacks, num_callbacks = task._finish()\n\n                task._schedule_callbacks(callbacks, num_callbacks)\n            elif exception is not None:\n                self._handle_exception(exception)\n\n            self._work_curr = None\n        else:\n            sleep_time = 0.01  # 10ms default\n            with self._lock:\n                if self._timers_len > 0:\n                    dt = self._timers[0].when - now\n                    if dt > 0 and dt < sleep_time:\n                        sleep_time = dt\n\n            if sleep_time > 0:\n                _sleep(sleep_time)\n\n        return False\n\n    def run_forever(self):\n        self._ensure_open()\n        self._ensure_not_running()\n        self._running = True\n        self._stop_flag = False\n\n        with _EnterLoop(self):\n            while True:\n                stop = self._step()\n                if stop:\n                    break\n            self._running = False\n\n    def run_until_complete(self, future):\n        self._ensure_open()\n        self._ensure_not_running()\n        self._running = True\n        self._stop_flag = False\n\n        with _EnterLoop(self):\n            while not future.done():\n                stop = self._step()\n                if stop:\n                    break\n            self._running = False\n\n        return future.result()\n\n    def _work_empty(self):\n        return self._work_tail is None\n\n    def _work_enqueue(self, work: WorkItem):\n        node = WorkNode(work)\n        tail = self._work_tail\n        if tail is None:\n            self._work_head = node\n            self._work_tail = node\n        else:\n            node.prev = tail\n            tail.next = node\n            self._work_tail = node\n        return node\n\n    def _work_dequeue(self):\n        # caller must ensure non-empty\n        head = self._work_head\n        self._work_head = head.next\n        if head.next is None:\n            self._work_tail = None\n        else:\n            head.next.prev = None\n            head.next = None\n        return head.work\n\n    def _cancel(self, node: WorkNode):\n        node.work = WorkItem()\n\n        if self._work_head is None or self._work_tail is None:\n            return\n\n        head: WorkNode = self._work_head\n        tail: WorkNode = self._work_tail\n\n        if node is head:\n            self._work_head = head.next\n            if head.next is None:\n                self._work_tail = None\n            else:\n                head.next.prev = None\n                head.next = None\n        elif node is tail:\n            self._work_tail = tail.prev\n            if tail.prev is None:\n                self._work_head = None\n            else:\n                tail.prev.next = None\n                tail.prev = None\n        else:\n            node.prev.next = node.next\n            node.next.prev = node.prev\n\n    def _cancel(self, timer: Timer):\n        timer.work = WorkItem()\n\n    def _timers_reserve(self, new_cap: int):\n        old_cap = self._timers_cap\n        if new_cap <= old_cap:\n            return\n\n        sz = gc.sizeof(Timer)\n        self._timers = Ptr[Timer](gc.realloc(\n                        self._timers.as_byte(),\n                        new_cap * sz, old_cap * sz))\n        self._timers_cap = new_cap\n\n    def _timers_swap(self, i: int, j: int):\n        timers = self._timers\n        tmp = timers[i]\n        timers[i] = timers[j]\n        timers[j] = tmp\n\n    def _timers_push(self, t: Timer):\n        if self._timers_len == self._timers_cap:\n            self._timers_reserve(self._timers_cap * 2)\n\n        i = self._timers_len\n        self._timers_len += 1\n        timers = self._timers\n        timers[i] = t\n\n        # Sift up\n        while i > 0:\n            parent = (i - 1) >> 1\n            if timers[parent].when <= timers[i].when:\n                break\n            self._timers_swap(parent, i)\n            i = parent\n\n    def _timers_pop(self):\n        # caller must ensure non-empty\n        timers = self._timers\n        out = timers[0]\n        self._timers_len -= 1\n        timers_len = self._timers_len\n\n        if timers_len > 0:\n            timers[0] = timers[timers_len]\n            i = 0\n            while True:\n                left = 2*i + 1\n                right = 2*i + 2\n                smallest = i\n\n                if (left < timers_len and timers[left].when < timers[smallest].when):\n                    smallest = left\n                if (right < timers_len and timers[right].when < timers[smallest].when):\n                    smallest = right\n                if smallest == i:\n                    break\n\n                self._timers_swap(i, smallest)\n                i = smallest\n\n        return out\n\n    def create_future(self, T: type = NoneType):\n        return Future[T](loop=self)\n\n    def create_task(self, coro: Coroutine, name: Optional[str] = None):\n        self._ensure_open()\n        task = Task(coro, loop=self, name=name)\n        work = WorkItem(task)\n        self._call_soon(work)\n        return task\n\n@extend\nclass Future:\n    def __init__(self, loop: Optional[EventLoop] = None):\n        self._exception = None\n        self._lock = Lock()\n        if loop is None:\n            if _running_loop is not None:\n                self._loop = _running_loop\n            else:\n                self._loop = get_event_loop()\n        else:\n            self._loop = loop\n        self._cancel_msg = ''\n        self._done_callbacks = Ptr[WorkItem]()\n        self._done_callbacks_len = 0\n        self._done_callbacks_cap = 0\n        self._state = _FUTURE_STATE_PENDING\n\n    def _result_size(self):\n        return gc.sizeof(R)\n\n    def _reset_callbacks(self):\n        self._done_callbacks = Ptr[WorkItem]()\n        self._done_callbacks_len = 0\n        self._done_callbacks_cap = 0\n\n    def add_done_callback(self, callback):\n        self._add_done_callback(\n          WorkItem(_future_callback_wrapper(callback, self)))\n\n    def _add_done_callback(self, work: WorkItem, add_if_done: Literal[bool] = True):\n        lock = self._lock\n        lock.acquire()\n\n        if self.done():\n            lock.release()\n            if add_if_done:\n                self._loop._call_soon(work)\n            return False\n\n        n = self._done_callbacks_len\n        m = self._done_callbacks_cap\n\n        if m == 0:\n            self._done_callbacks = Ptr[WorkItem](1)\n            self._done_callbacks_cap = 1\n        elif n >= m:\n            new_m = m * 2\n            sz = gc.sizeof(WorkItem)\n            self._done_callbacks = Ptr[WorkItem](\n                                     gc.realloc(\n                                       self._done_callbacks.as_byte(),\n                                       new_m * sz, m * sz))\n            self._done_callbacks_cap = new_m\n\n        self._done_callbacks[n] = work\n        self._done_callbacks_len += 1\n        lock.release()\n        return True\n\n    def _schedule_callbacks(self, callbacks: Ptr[WorkItem], num_callbacks: int):\n        for i in range(num_callbacks):\n            item = callbacks[i]\n            if item.task:\n                task = item.raw_task()\n                waiting_on_lock = task._waiting_on_lock\n                if waiting_on_lock != self._lock.p:\n                    continue\n                task._waiting_on_task = None\n                task._waiting_on_lock = cobj()\n            self._loop._call_soon(item)\n        if callbacks:\n            gc.free(callbacks.as_byte())\n\n    def result(self):\n        with self._lock:\n            state = self._state\n            if state == _FUTURE_STATE_CANCELLED:\n                raise CancelledError(self._cancel_msg)\n            elif state == _FUTURE_STATE_EXCEPTION:\n                raise self._exception.__val__()\n            elif state == _FUTURE_STATE_PENDING:\n                raise InvalidStateError(\"Result is not set.\")\n            else:\n                return self._result\n\n    def _finish(self):\n        if self.done():\n            raise InvalidStateError(\"Invalid state\")\n\n        self._state = _FUTURE_STATE_FINISHED\n        callbacks = self._done_callbacks\n        num_callbacks = self._done_callbacks_len\n        self._reset_callbacks()\n        return callbacks, num_callbacks\n\n    def _finish_with_exception(self, exception: BaseException):\n        if self.done():\n            raise InvalidStateError(\"Invalid state\")\n\n        self._state = _FUTURE_STATE_EXCEPTION\n        self._exception = exception\n        callbacks = self._done_callbacks\n        num_callbacks = self._done_callbacks_len\n        self._reset_callbacks()\n        return callbacks, num_callbacks\n\n    def set_result(self, result: R):\n        callbacks = Ptr[WorkItem]()\n        num_callbacks = 0\n\n        with self._lock:\n            if self.done():\n                raise InvalidStateError(\"Invalid state\")\n\n            self._result = result\n            self._state = _FUTURE_STATE_FINISHED\n\n            callbacks = self._done_callbacks\n            num_callbacks = self._done_callbacks_len\n            self._reset_callbacks()\n\n        self._schedule_callbacks(callbacks, num_callbacks)\n\n    def _set_result_if_not_done(self, result: R):\n        callbacks = Ptr[WorkItem]()\n        num_callbacks = 0\n\n        with self._lock:\n            if self.done():\n                return False\n\n            self._result = result\n            self._state = _FUTURE_STATE_FINISHED\n\n            callbacks = self._done_callbacks\n            num_callbacks = self._done_callbacks_len\n            self._reset_callbacks()\n\n        self._schedule_callbacks(callbacks, num_callbacks)\n        return True\n\n    def set_exception(self, exception: BaseException):\n        callbacks = Ptr[WorkItem]()\n        num_callbacks = 0\n\n        with self._lock:\n            if self.done():\n                raise InvalidStateError(\"Invalid state\")\n\n            self._exception = exception\n            self._state = _FUTURE_STATE_EXCEPTION\n\n            callbacks = self._done_callbacks\n            num_callbacks = self._done_callbacks_len\n            self._reset_callbacks()\n\n        self._schedule_callbacks(callbacks, num_callbacks)\n\n    def _set_exception_if_not_done(self, exception: BaseException):\n        callbacks = Ptr[WorkItem]()\n        num_callbacks = 0\n\n        with self._lock:\n            if self.done():\n                return False\n\n            self._exception = exception\n            self._state = _FUTURE_STATE_EXCEPTION\n\n            callbacks = self._done_callbacks\n            num_callbacks = self._done_callbacks_len\n            self._reset_callbacks()\n\n        self._schedule_callbacks(callbacks, num_callbacks)\n        return True\n\n    def cancelled(self):\n        return self._state == _FUTURE_STATE_CANCELLED\n\n    def done(self):\n        return self._state != _FUTURE_STATE_PENDING\n\n    def cancel(self, msg: Optional[str] = None):\n        callbacks = Ptr[WorkItem]()\n        num_callbacks = 0\n\n        with self._lock:\n            if self.done():\n                return False\n\n            self._state = _FUTURE_STATE_CANCELLED\n            if msg is not None:\n                self._cancel_msg = msg\n\n            callbacks = self._done_callbacks\n            num_callbacks = self._done_callbacks_len\n            self._reset_callbacks()\n\n        self._schedule_callbacks(callbacks, num_callbacks)\n        return True\n\n    def get_loop(self):\n        return self._loop\n\n    def exception(self) -> Optional[BaseException]:\n        with self._lock:\n            state = self._state\n            if state == _FUTURE_STATE_CANCELLED:\n                raise CancelledError(self._cancel_msg)\n            elif state == _FUTURE_STATE_EXCEPTION:\n                return self._exception.__val__()\n            elif state == _FUTURE_STATE_PENDING:\n                raise InvalidStateError(\"Exception is not set.\")\n            else:\n                return None\n\n    async def __await__(self):\n        return await self\n\n_default_task_name_counter = 1\ndef _default_task_name():\n    global _default_task_name_counter\n    n = _default_task_name_counter\n    _default_task_name_counter += 1\n    return f'Task-{n}'\n\n@extend\nclass Task:\n    def __init__(self,\n                 coro: Coroutine[R],\n                 loop: Optional[EventLoop] = None,\n                 name: Optional[str] = None):\n        super().__init__(loop)\n        if name is None:\n            self._name = _default_task_name()\n        else:\n            self._name = name\n        self._coro = coro.__raw__()\n        self._waiting_on_task = None\n        self._waiting_on_lock = cobj()\n\n    def get_name(self):\n        return self._name\n\n    def set_name(self, value: str):\n        self._name = value\n\n    def get_coro(self, T: type = NoneType) -> Coroutine[T]:\n        return Coroutine[T](self._coro)\n\n    def add_done_callback(self, callback):\n        super().add_done_callback(callback)\n\n    def _add_done_callback(self, work: WorkItem, add_if_done: Literal[bool] = True):\n        return super()._add_done_callback(work, add_if_done)\n\n    def _schedule_callbacks(self, callbacks: Ptr[WorkItem], num_callbacks: int):\n        super()._schedule_callbacks(callbacks, num_callbacks)\n\n    def get_loop(self):\n        return super().get_loop()\n\n    def done(self):\n        return super().done()\n\n    def cancelled(self):\n        return super().cancelled()\n\n    def result(self):\n        return super().result()\n\n    def _finish(self):\n        return super()._finish()\n\n    def _finish_with_exception(self, exception: BaseException):\n        return super()._finish_with_exception(exception)\n\n    def cancel(self, msg: Optional[str] = None):\n        if not super().cancel(msg):\n            return False\n\n        with self._lock:\n            waiting_on_task = self._waiting_on_task\n            if waiting_on_task is not None:\n                waiting_on_task.cancel(msg)\n                self._waiting_on_task = None\n                self._waiting_on_lock = cobj()\n            self._loop._call_soon_no_duplicate(WorkItem(self))\n\n        return True\n\n    async def __await__(self):\n        return await self\n\ndef _wait_on(future):\n    loop = get_running_loop()\n    if loop is not future.get_loop():\n        raise RuntimeError(\"running loop is not the same as task loop\")\n\n    work_curr = loop._work_curr\n    if work_curr is None:\n        return False\n\n    added = future._add_done_callback(work_curr, add_if_done=False)\n\n    if added and work_curr.task:\n        task = work_curr.raw_task()\n        task._waiting_on_lock = future._lock.p\n        if isinstance(future, Task):\n            task._waiting_on_task = _raw_task(future)\n\n    return added\n\ndef _requeue():\n    loop = get_running_loop()\n    loop._call_soon(loop._work_curr)\n\ndef _promise(coro):\n    g = Generator[coro.T](coro.__raw__())\n    return g.__promise__()[0]\n\ndef _done(coro):\n    g = Generator[coro.T](coro.__raw__())\n    return g.__done__()\n\ndef _resume(coro):\n    g = Generator[coro.T](coro.__raw__())\n    g.__resume__()\n\ndef _curr_task():\n    return get_running_loop()._work_curr.raw_task()\n\ndef _is_waiting(task):\n    return bool(task._waiting_on_lock)\n\ndef _cancel_checkpoint():\n    loop = get_running_loop()\n    work_curr = loop._work_curr\n    if work_curr is not None and work_curr.task:\n        task = work_curr.raw_task()\n        if task.cancelled():\n            raise CancelledError(task._cancel_msg)\n\ndef new_event_loop():\n    return EventLoop()\n\ndef set_event_loop(loop: EventLoop):\n    global _current_loop\n    _current_loop = loop\n\ndef get_event_loop():\n    global _current_loop\n    if _current_loop is not None:\n        return _current_loop\n\n    loop = new_event_loop()\n    _current_loop = loop\n    return loop\n\ndef get_running_loop() -> EventLoop:\n    if _running_loop is None:\n        raise RuntimeError(\"no running event loop\")\n    return _running_loop\n\ndef create_task(coro, name: Optional[str] = None):\n    return get_running_loop().create_task(coro, name=name)\n\ndef isfuture(obj) -> Literal[bool]:\n    return isinstance(obj, Future)\n\ndef iscoroutine(obj) -> Literal[bool]:\n    return isinstance(obj, Coroutine)\n\ndef ensure_future(obj, loop: Optional[EventLoop] = None):\n    if isfuture(obj):\n        return obj\n\n    if iscoroutine(obj):\n        return (loop if loop is not None else get_event_loop()).create_task(obj)\n\n    compile_error(\"An asyncio.Future, a coroutine or an awaitable is required\")\n\ndef current_task() -> Optional[Task[None]]:\n    loop = get_running_loop()\n    work_curr = loop._work_curr\n    if work_curr is not None and work_curr.task:\n        return work_curr.raw_task()\n    else:\n        return None\n\ndef run(coro, debug=None, loop_factory=None):\n    if loop_factory is not None:\n        loop = loop_factory()\n    else:\n        loop = get_event_loop()\n    task = loop.create_task(coro)\n    return loop.run_until_complete(task)\n\nasync def sleep(delay: float, result=None):\n    loop = get_running_loop()\n    future = loop.create_future(type(result))\n    if delay <= 0.0:\n        loop.call_soon(future.set_result, result)\n    else:\n        loop.call_later(delay, future.set_result, result)\n    return await future\n\ndef gather(*aws):\n    @pure\n    @llvm\n    def zero(T: type) -> T:\n        ret {=T} zeroinitializer\n\n    @pure\n    @derives\n    @llvm\n    def gep(p: Ptr[T], idx: Literal[int], R: type, T: type) -> Ptr[R]:\n        %q = getelementptr {=T}, ptr %p, i32 0, i32 {=idx}\n        ret ptr %q\n\n    @nocapture\n    @llvm\n    def atomic_decrement(i: Ptr[int]) -> int:\n        %j = atomicrmw sub ptr %i, i64 1 seq_cst\n        ret i64 %j\n\n    @tuple\n    class GatherCallback[T]:\n        payload: T\n\n        def __call__(self, future):\n            outer, results, my_result, remaining = self.payload\n\n            with future._lock:\n                if future._state == _FUTURE_STATE_FINISHED:\n                    my_result[0] = future._result\n                elif future._state == _FUTURE_STATE_EXCEPTION:\n                    outer._set_exception_if_not_done(future._exception)\n                    return\n\n            if atomic_decrement(remaining) == 1:\n                outer._set_result_if_not_done(results[0])\n\n    @tuple\n    class CancelCallback[T]:\n        futures: T\n\n        def __call__(self, future):\n            if future.cancelled():\n                for f in self.futures:\n                    f.cancel()\n\n    loop = get_running_loop()\n    futures = tuple(ensure_future(a, loop=loop) for a in aws)\n    ret_type = type(tuple(zero(f.R) for f in futures))\n    outer = loop.create_future(ret_type)\n    n: Literal[int] = static.len(futures)\n\n    if n == 0:\n        outer.set_result(())\n        return outer\n\n    results = Ptr[ret_type](1)\n    remaining = Ptr[int](1)\n    remaining[0] = n\n\n    for i in static.range(n):\n        my_future = futures[i]\n        my_result = gep(results, i, my_future.R)\n        payload = (outer, results, my_result, remaining)\n        my_future.add_done_callback(GatherCallback(payload))\n\n    outer.add_done_callback(CancelCallback(futures))\n    return outer\n"
  },
  {
    "path": "stdlib/bisect.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\ndef bisect_left(\n    a: List[T], x: S, lo: int = 0, hi: Optional[int] = None, T: type, S: type\n) -> int:\n    if lo < 0:\n        raise ValueError(\"lo must be non-negative\")\n    hi: int = len(a) if hi is None else hi\n    while lo < hi:\n        mid = (lo + hi) // 2\n        if a[mid] < x:\n            lo = mid + 1\n        else:\n            hi = mid\n    return lo\n\ndef bisect_right(\n    a: List[T], x: S, lo: int = 0, hi: Optional[int] = None, T: type, S: type\n) -> int:\n    if lo < 0:\n        raise ValueError(\"lo must be non-negative\")\n    hi: int = len(a) if hi is None else hi\n    while lo < hi:\n        mid = (lo + hi) // 2\n        if x < a[mid]:\n            hi = mid\n        else:\n            lo = mid + 1\n    return lo\n\ndef insort_left(\n    a: List[T], x: S, lo: int = 0, hi: Optional[int] = None, T: type, S: type\n):\n    lo = bisect_left(a, x, lo, hi)\n    a.insert(lo, x)\n\ndef insort_right(\n    a: List[T], x: S, lo: int = 0, hi: Optional[int] = None, T: type, S: type\n):\n    lo = bisect_right(a, x, lo, hi)\n\n    if lo == len(a):\n        a.append(x)\n    else:\n        a.insert(lo, x)\n\nbisect = bisect_right\ninsort = insort_right\n"
  },
  {
    "path": "stdlib/bz2.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom internal.file import bzFile\n\ndef open(path: str, mode: str = \"r\") -> bzFile:\n    return bzFile(path, mode)\n"
  },
  {
    "path": "stdlib/cmath.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport math\n\ne = math.e\npi = math.pi\ntau = math.tau\ninf = math.inf\nnan = math.nan\ninfj = complex(0.0, inf)\nnanj = complex(0.0, nan)\n\n# internal constants\n_FLT_RADIX = 2\n_M_LN2 = 0.6931471805599453094  # ln(2)\n_M_LN10 = 2.302585092994045684  # ln(10)\n\n@pure\n@llvm\ndef _max_float() -> float:\n    ret double 0x7FEFFFFFFFFFFFFF\n\n@pure\n@llvm\ndef _min_float() -> float:\n    ret double 0x10000000000000\n\n_DBL_MAX = _max_float()\n_DBL_MIN = _min_float()\n_DBL_MANT_DIG = 53\n\n_CM_LARGE_DOUBLE = _DBL_MAX/4.\n_CM_SQRT_LARGE_DOUBLE = math.sqrt(_CM_LARGE_DOUBLE)\n_CM_LOG_LARGE_DOUBLE = math.log(_CM_LARGE_DOUBLE)\n_CM_SQRT_DBL_MIN = math.sqrt(_DBL_MIN)\n_CM_SCALE_UP = (2*(_DBL_MANT_DIG // 2) + 1)\n_CM_SCALE_DOWN = (-(_CM_SCALE_UP+1)//2)\n\n# special types\n_ST_NINF  = 0  # negative infinity\n_ST_NEG   = 1  # negative finite number (nonzero)\n_ST_NZERO = 2  # -0.\n_ST_PZERO = 3  # +0.\n_ST_POS   = 4  # positive finite number (nonzero)\n_ST_PINF  = 5  # positive infinity\n_ST_NAN   = 6  # Not a Number\n\ndef _special_type(d: float):\n    if math.isfinite(d):\n        if d != 0:\n            if math.copysign(1., d) == 1.:\n                return _ST_POS\n            else:\n                return _ST_NEG\n        else:\n            if math.copysign(1., d) == 1.:\n                return _ST_PZERO\n            else:\n                return _ST_NZERO\n    if math.isnan(d):\n        return _ST_NAN\n    if math.copysign(1., d) == 1.:\n        return _ST_PINF\n    else:\n        return _ST_NINF\n\ndef _acos_special():\n    P = pi\n    P14 = 0.25*pi\n    P12 = 0.5*pi\n    P34 = 0.75*pi\n    INF = inf  # Py_HUGE_VAL\n    N = nan\n    U = -9.5426319407711027e33  # unlikely value, used as placeholder\n    def C(a,b): return complex(a, b)\n    v = (C(P34,INF), C(P,INF), C(P,INF), C(P,-INF), C(P,-INF), C(P34,-INF), C(N,INF),\n         C(P12,INF), C(U,U), C(U,U), C(U,U), C(U,U), C(P12,-INF), C(N,N), C(P12,INF),\n         C(U,U), C(P12,0.), C(P12,-0.), C(U,U), C(P12,-INF), C(P12,N), C(P12,INF), C(U,U),\n         C(P12,0.), C(P12,-0.), C(U,U), C(P12,-INF), C(P12,N), C(P12,INF), C(U,U), C(U,U),\n         C(U,U), C(U,U), C(P12,-INF), C(N,N), C(P14,INF), C(0.,INF), C(0.,INF), C(0.,-INF),\n         C(0.,-INF), C(P14,-INF), C(N,INF), C(N,INF), C(N,N), C(N,N), C(N,N), C(N,N),\n         C(N,-INF), C(N,N))\n    return v\n\ndef _acosh_special():\n    P = pi\n    P14 = 0.25*pi\n    P12 = 0.5*pi\n    P34 = 0.75*pi\n    INF = inf  # Py_HUGE_VAL\n    N = nan\n    U = -9.5426319407711027e33  # unlikely value, used as placeholder\n    def C(a,b): return complex(a, b)\n    v = (C(INF,-P34), C(INF,-P), C(INF,-P), C(INF,P), C(INF,P), C(INF,P34), C(INF,N),\n         C(INF,-P12), C(U,U), C(U,U), C(U,U), C(U,U), C(INF,P12), C(N,N), C(INF,-P12),\n         C(U,U), C(0.,-P12), C(0.,P12), C(U,U), C(INF,P12), C(N,N), C(INF,-P12), C(U,U),\n         C(0.,-P12), C(0.,P12), C(U,U), C(INF,P12), C(N,N), C(INF,-P12), C(U,U), C(U,U),\n         C(U,U), C(U,U), C(INF,P12), C(N,N), C(INF,-P14), C(INF,-0.), C(INF,-0.), C(INF,0.),\n         C(INF,0.), C(INF,P14), C(INF,N), C(INF,N), C(N,N), C(N,N), C(N,N), C(N,N), C(INF,N),\n         C(N,N))\n    return v\n\ndef _asinh_special():\n    P = pi\n    P14 = 0.25*pi\n    P12 = 0.5*pi\n    P34 = 0.75*pi\n    INF = inf  # Py_HUGE_VAL\n    N = nan\n    U = -9.5426319407711027e33  # unlikely value, used as placeholder\n    def C(a,b): return complex(a, b)\n    v = (C(-INF,-P14), C(-INF,-0.), C(-INF,-0.), C(-INF,0.), C(-INF,0.), C(-INF,P14), C(-INF,N),\n         C(-INF,-P12), C(U,U), C(U,U), C(U,U), C(U,U), C(-INF,P12), C(N,N), C(-INF,-P12), C(U,U),\n         C(-0.,-0.), C(-0.,0.), C(U,U), C(-INF,P12), C(N,N), C(INF,-P12), C(U,U), C(0.,-0.),\n         C(0.,0.), C(U,U), C(INF,P12), C(N,N), C(INF,-P12), C(U,U), C(U,U), C(U,U), C(U,U),\n         C(INF,P12), C(N,N), C(INF,-P14), C(INF,-0.), C(INF,-0.), C(INF,0.), C(INF,0.), C(INF,P14),\n         C(INF,N), C(INF,N), C(N,N), C(N,-0.), C(N,0.), C(N,N), C(INF,N), C(N,N))\n    return v\n\ndef _atanh_special():\n    P = pi\n    P14 = 0.25*pi\n    P12 = 0.5*pi\n    P34 = 0.75*pi\n    INF = inf  # Py_HUGE_VAL\n    N = nan\n    U = -9.5426319407711027e33  # unlikely value, used as placeholder\n    def C(a,b): return complex(a, b)\n    v = (C(-0.,-P12), C(-0.,-P12), C(-0.,-P12), C(-0.,P12), C(-0.,P12), C(-0.,P12), C(-0.,N),\n         C(-0.,-P12), C(U,U), C(U,U), C(U,U), C(U,U), C(-0.,P12), C(N,N), C(-0.,-P12), C(U,U),\n         C(-0.,-0.), C(-0.,0.), C(U,U), C(-0.,P12), C(-0.,N), C(0.,-P12), C(U,U), C(0.,-0.), C(0.,0.),\n         C(U,U), C(0.,P12), C(0.,N), C(0.,-P12), C(U,U), C(U,U), C(U,U), C(U,U), C(0.,P12), C(N,N),\n         C(0.,-P12), C(0.,-P12), C(0.,-P12), C(0.,P12), C(0.,P12), C(0.,P12), C(0.,N), C(0.,-P12),\n         C(N,N), C(N,N), C(N,N), C(N,N), C(0.,P12), C(N,N))\n    return v\n\ndef _cosh_special():\n    P = pi\n    P14 = 0.25*pi\n    P12 = 0.5*pi\n    P34 = 0.75*pi\n    INF = inf  # Py_HUGE_VAL\n    N = nan\n    U = -9.5426319407711027e33  # unlikely value, used as placeholder\n    def C(a,b): return complex(a, b)\n    v = (C(INF,N), C(U,U), C(INF,0.), C(INF,-0.), C(U,U), C(INF,N), C(INF,N), C(N,N), C(U,U), C(U,U),\n         C(U,U), C(U,U), C(N,N), C(N,N), C(N,0.), C(U,U), C(1.,0.), C(1.,-0.), C(U,U), C(N,0.), C(N,0.),\n         C(N,0.), C(U,U), C(1.,-0.), C(1.,0.), C(U,U), C(N,0.), C(N,0.), C(N,N), C(U,U), C(U,U), C(U,U),\n         C(U,U), C(N,N), C(N,N), C(INF,N), C(U,U), C(INF,-0.), C(INF,0.), C(U,U), C(INF,N), C(INF,N),\n         C(N,N), C(N,N), C(N,0.), C(N,0.), C(N,N), C(N,N), C(N,N))\n    return v\n\ndef _exp_special():\n    P = pi\n    P14 = 0.25*pi\n    P12 = 0.5*pi\n    P34 = 0.75*pi\n    INF = inf  # Py_HUGE_VAL\n    N = nan\n    U = -9.5426319407711027e33  # unlikely value, used as placeholder\n    def C(a,b): return complex(a, b)\n    v = (C(0.,0.), C(U,U), C(0.,-0.), C(0.,0.), C(U,U), C(0.,0.), C(0.,0.), C(N,N), C(U,U), C(U,U), C(U,U),\n         C(U,U), C(N,N), C(N,N), C(N,N), C(U,U), C(1.,-0.), C(1.,0.), C(U,U), C(N,N), C(N,N), C(N,N), C(U,U),\n         C(1.,-0.), C(1.,0.), C(U,U), C(N,N), C(N,N), C(N,N), C(U,U), C(U,U), C(U,U), C(U,U), C(N,N), C(N,N),\n         C(INF,N), C(U,U), C(INF,-0.), C(INF,0.), C(U,U), C(INF,N), C(INF,N), C(N,N), C(N,N), C(N,-0.),\n         C(N,0.), C(N,N), C(N,N), C(N,N))\n    return v\n\ndef _log_special():\n    P = pi\n    P14 = 0.25*pi\n    P12 = 0.5*pi\n    P34 = 0.75*pi\n    INF = inf  # Py_HUGE_VAL\n    N = nan\n    U = -9.5426319407711027e33  # unlikely value, used as placeholder\n    def C(a,b): return complex(a, b)\n    v = (C(INF,-P34), C(INF,-P), C(INF,-P), C(INF,P), C(INF,P), C(INF,P34), C(INF,N), C(INF,-P12), C(U,U),\n         C(U,U), C(U,U), C(U,U), C(INF,P12), C(N,N), C(INF,-P12), C(U,U), C(-INF,-P), C(-INF,P), C(U,U), C(INF,P12),\n         C(N,N), C(INF,-P12), C(U,U), C(-INF,-0.), C(-INF,0.), C(U,U), C(INF,P12), C(N,N), C(INF,-P12), C(U,U),\n         C(U,U), C(U,U), C(U,U), C(INF,P12), C(N,N), C(INF,-P14), C(INF,-0.), C(INF,-0.), C(INF,0.), C(INF,0.),\n         C(INF,P14), C(INF,N), C(INF,N), C(N,N), C(N,N), C(N,N), C(N,N), C(INF,N), C(N,N))\n    return v\n\ndef _sinh_special():\n    P = pi\n    P14 = 0.25*pi\n    P12 = 0.5*pi\n    P34 = 0.75*pi\n    INF = inf  # Py_HUGE_VAL\n    N = nan\n    U = -9.5426319407711027e33  # unlikely value, used as placeholder\n    def C(a,b): return complex(a, b)\n    v = (C(INF,N), C(U,U), C(-INF,-0.), C(-INF,0.), C(U,U), C(INF,N), C(INF,N), C(N,N), C(U,U), C(U,U), C(U,U),\n        C(U,U), C(N,N), C(N,N), C(0.,N), C(U,U), C(-0.,-0.), C(-0.,0.), C(U,U), C(0.,N), C(0.,N), C(0.,N),\n        C(U,U), C(0.,-0.), C(0.,0.), C(U,U), C(0.,N), C(0.,N), C(N,N), C(U,U), C(U,U), C(U,U), C(U,U), C(N,N),\n        C(N,N), C(INF,N), C(U,U), C(INF,-0.), C(INF,0.), C(U,U), C(INF,N), C(INF,N), C(N,N), C(N,N), C(N,-0.),\n        C(N,0.), C(N,N), C(N,N), C(N,N))\n    return v\n\ndef _sqrt_special():\n    P = pi\n    P14 = 0.25*pi\n    P12 = 0.5*pi\n    P34 = 0.75*pi\n    INF = inf  # Py_HUGE_VAL\n    N = nan\n    U = -9.5426319407711027e33  # unlikely value, used as placeholder\n    def C(a,b): return complex(a, b)\n    v = (C(INF,-INF), C(0.,-INF), C(0.,-INF), C(0.,INF), C(0.,INF), C(INF,INF), C(N,INF), C(INF,-INF), C(U,U), C(U,U),\n         C(U,U), C(U,U), C(INF,INF), C(N,N), C(INF,-INF), C(U,U), C(0.,-0.), C(0.,0.), C(U,U), C(INF,INF), C(N,N),\n         C(INF,-INF), C(U,U), C(0.,-0.), C(0.,0.), C(U,U), C(INF,INF), C(N,N), C(INF,-INF), C(U,U), C(U,U), C(U,U),\n         C(U,U), C(INF,INF), C(N,N), C(INF,-INF), C(INF,-0.), C(INF,-0.), C(INF,0.), C(INF,0.), C(INF,INF), C(INF,N),\n         C(INF,-INF), C(N,N), C(N,N), C(N,N), C(N,N), C(INF,INF), C(N,N))\n    return v\n\ndef _tanh_special():\n    P = pi\n    P14 = 0.25*pi\n    P12 = 0.5*pi\n    P34 = 0.75*pi\n    INF = inf  # Py_HUGE_VAL\n    N = nan\n    U = -9.5426319407711027e33  # unlikely value, used as placeholder\n    def C(a,b): return complex(a, b)\n    v = (C(-1.,0.), C(U,U), C(-1.,-0.), C(-1.,0.), C(U,U), C(-1.,0.), C(-1.,0.), C(N,N), C(U,U), C(U,U), C(U,U),\n         C(U,U), C(N,N), C(N,N), C(N,N), C(U,U), C(-0.,-0.), C(-0.,0.), C(U,U), C(N,N), C(N,N), C(N,N), C(U,U),\n         C(0.,-0.), C(0.,0.), C(U,U), C(N,N), C(N,N), C(N,N), C(U,U), C(U,U), C(U,U), C(U,U), C(N,N), C(N,N),\n         C(1.,0.), C(U,U), C(1.,-0.), C(1.,0.), C(U,U), C(1.,0.), C(1.,0.), C(N,N), C(N,N), C(N,-0.), C(N,0.),\n         C(N,N), C(N,N), C(N,N))\n    return v\n\ndef _rect_special():\n    P = pi\n    P14 = 0.25*pi\n    P12 = 0.5*pi\n    P34 = 0.75*pi\n    INF = inf  # Py_HUGE_VAL\n    N = nan\n    U = -9.5426319407711027e33  # unlikely value, used as placeholder\n    def C(a,b): return complex(a, b)\n    v = (C(INF,N), C(U,U), C(-INF,0.), C(-INF,-0.), C(U,U), C(INF,N), C(INF,N), C(N,N), C(U,U), C(U,U), C(U,U), C(U,U),\n         C(N,N), C(N,N), C(0.,0.), C(U,U), C(-0.,0.), C(-0.,-0.), C(U,U), C(0.,0.), C(0.,0.), C(0.,0.), C(U,U), C(0.,-0.),\n         C(0.,0.), C(U,U), C(0.,0.), C(0.,0.), C(N,N), C(U,U), C(U,U), C(U,U), C(U,U), C(N,N), C(N,N), C(INF,N), C(U,U),\n         C(INF,-0.), C(INF,0.), C(U,U), C(INF,N), C(INF,N), C(N,N), C(N,N), C(N,0.), C(N,0.), C(N,N), C(N,N), C(N,N))\n    return v\n\ndef _is_special(z):\n    return (not math.isfinite(z.real)) or (not math.isfinite(z.imag))\n\ndef _special_get(z, table):\n    t1 = _special_type(z.real)\n    t2 = _special_type(z.imag)\n    return table[7*t1 + t2]\n\ndef _sqrt_impl(z):\n    if _is_special(z):\n        return _special_get(z, _sqrt_special())\n\n    r_real = 0.\n    r_imag = 0.\n    if z.real == 0. and z.imag == 0.:\n        r_real = 0.\n        r_imag = z.imag\n        return complex(r_real, r_imag)\n\n    ax = math.fabs(z.real)\n    ay = math.fabs(z.imag)\n    s = 0.\n    if ax < _DBL_MIN and ay < _DBL_MIN and (ax > 0. or ay > 0.):\n        # here we catch cases where hypot(ax, ay) is subnormal\n        ax = math.ldexp(ax, _CM_SCALE_UP)\n        s = math.ldexp(math.sqrt(ax + math.hypot(ax, math.ldexp(ay, _CM_SCALE_UP))), _CM_SCALE_DOWN)\n    else:\n        ax /= 8.\n        s = 2.*math.sqrt(ax + math.hypot(ax, ay/8.))\n    d = ay/(2.*s)\n\n    if z.real >= 0.:\n        r_real = s\n        r_imag = math.copysign(d, z.imag)\n    else:\n        r_real = d\n        r_imag = math.copysign(s, z.imag)\n    # errno = 0\n    return complex(r_real, r_imag)\n\ndef _acos_impl(z):\n    if _is_special(z):\n        return _special_get(z, _acos_special())\n\n    r_real = 0.\n    r_imag = 0.\n    if math.fabs(z.real) > _CM_LARGE_DOUBLE or math.fabs(z.imag) > _CM_LARGE_DOUBLE:\n        # avoid unnecessary overflow for large arguments\n        r_real = math.atan2(math.fabs(z.imag), z.real)\n        # split into cases to make sure that the branch cut has the\n        # correct continuity on systems with unsigned zeros\n        if z.real < 0.:\n            r_imag = -math.copysign(math.log(math.hypot(z.real/2., z.imag/2.)) + _M_LN2*2, z.imag)\n        else:\n            r_imag = math.copysign(math.log(math.hypot(z.real/2., z.imag/2.)) + _M_LN2*2, -z.imag)\n    else:\n        s1 = _sqrt_impl(complex(1. - z.real, -z.imag))\n        s2 = _sqrt_impl(complex(1. + z.real, z.imag))\n        r_real = 2.*math.atan2(s1.real, s2.real)\n        r_imag = math.asinh(s2.real*s1.imag - s2.imag*s1.real)\n    return complex(r_real, r_imag)\n\ndef _acosh_impl(z):\n    if _is_special(z):\n        return _special_get(z, _acosh_special())\n\n    r_real = 0.\n    r_imag = 0.\n    if math.fabs(z.real) > _CM_LARGE_DOUBLE or math.fabs(z.imag) > _CM_LARGE_DOUBLE:\n        # avoid unnecessary overflow for large arguments\n        r_real = math.log(math.hypot(z.real/2., z.imag/2.)) + _M_LN2*2.\n        r_imag = math.atan2(z.imag, z.real)\n    else:\n        s1 = _sqrt_impl(complex(z.real - 1., z.imag))\n        s2 = _sqrt_impl(complex(z.real + 1., z.imag))\n        r_real = math.asinh(s1.real*s2.real + s1.imag*s2.imag)\n        r_imag = 2.*math.atan2(s1.imag, s2.real)\n    return complex(r_real, r_imag)\n\ndef _asinh_impl(z):\n    if _is_special(z):\n        return _special_get(z, _asinh_special())\n\n    r_real = 0.\n    r_imag = 0.\n    if math.fabs(z.real) > _CM_LARGE_DOUBLE or math.fabs(z.imag) > _CM_LARGE_DOUBLE:\n        if z.imag >= 0.:\n            r_real = math.copysign(math.log(math.hypot(z.real/2., z.imag/2.)) + _M_LN2*2, z.real)\n        else:\n            r_real = -math.copysign(math.log(math.hypot(z.real/2., z.imag/2.)) + _M_LN2*2, -z.real)\n        r_imag = math.atan2(z.imag, math.fabs(z.real))\n    else:\n        s1 = _sqrt_impl(complex(1. + z.imag, -z.real))\n        s2 = _sqrt_impl(complex(1. - z.imag, z.real))\n        r_real = math.asinh(s1.real*s2.imag - s2.real*s1.imag)\n        r_imag = math.atan2(z.imag, s1.real*s2.real - s1.imag*s2.imag)\n    return complex(r_real, r_imag)\n\ndef _asin_impl(z):\n    s = _asinh_impl(complex(-z.imag, z.real))\n    r_real = s.imag\n    r_imag = -s.real\n    return complex(r_real, r_imag)\n\ndef _atanh_impl(z):\n    if _is_special(z):\n        return _special_get(z, _atanh_special())\n\n    # Reduce to case where z.real >= 0., using atanh(z) = -atanh(-z).\n    if z.real < 0.:\n        return -_atanh_impl(-z)\n\n    r_real = 0.\n    r_imag = 0.\n    ay = math.fabs(z.imag)\n    if z.real > _CM_SQRT_LARGE_DOUBLE or ay > _CM_SQRT_LARGE_DOUBLE:\n        # if abs(z) is large then we use the approximation\n        # atanh(z) ~ 1/z +/- i*pi/2 (+/- depending on the sign\n        # of z.imag)\n        h = math.hypot(z.real/2., z.imag/2.)  # safe from overflow\n        r_real = z.real/4./h/h\n        # the two negations in the next line cancel each other out\n        # except when working with unsigned zeros: they're there to\n        # ensure that the branch cut has the correct continuity on\n        # systems that don't support signed zeros\n        r_imag = -math.copysign(pi/2., -z.imag)\n        # errno = 0\n    elif z.real == 1. and ay < _CM_SQRT_DBL_MIN:\n        # C99 standard says:  atanh(1+/-0.) should be inf +/- 0i\n        if ay == 0.:\n            r_real = inf\n            r_imag = z.imag\n            # errno = EDOM\n        else:\n            r_real = -math.log(math.sqrt(ay)/math.sqrt(math.hypot(ay, 2.)))\n            r_imag = math.copysign(math.atan2(2., -ay)/2, z.imag)\n            # errno = 0\n    else:\n        r_real = math.log1p(4.*z.real/((1-z.real)*(1-z.real) + ay*ay))/4.\n        r_imag = -math.atan2(-2.*z.imag, (1-z.real)*(1+z.real) - ay*ay)/2.\n        # errno = 0\n    return complex(r_real, r_imag)\n\ndef _atan_impl(z):\n    s = _atanh_impl(complex(-z.imag, z.real))\n    r_real = s.imag\n    r_imag = -s.real\n    return complex(r_real, r_imag)\n\ndef _cosh_impl(z):\n    r_real = 0.\n    r_imag = 0.\n    # special treatment for cosh(+/-inf + iy) if y is not a NaN\n    if (not math.isfinite(z.real)) or (not math.isfinite(z.imag)):\n        if math.isinf(z.real) and math.isfinite(z.imag) and z.imag != 0.:\n            if z.real > 0:\n                r_real = math.copysign(inf, math.cos(z.imag))\n                r_imag = math.copysign(inf, math.sin(z.imag))\n            else:\n                r_real = math.copysign(inf, math.cos(z.imag))\n                r_imag = -math.copysign(inf, math.sin(z.imag))\n        else:\n            r = _special_get(z, _cosh_special())\n            r_real = r.real\n            r_imag = r.imag\n        '''\n        /* need to set errno = EDOM if y is +/- infinity and x is not\n           a NaN */\n        if (Py_IS_INFINITY(z.imag) && !Py_IS_NAN(z.real))\n            errno = EDOM;\n        else\n            errno = 0;\n        '''\n        return complex(r_real, r_imag)\n\n    if math.fabs(z.real) > _CM_LOG_LARGE_DOUBLE:\n        # deal correctly with cases where cosh(z.real) overflows but\n        # cosh(z) does not.\n        x_minus_one = z.real - math.copysign(1., z.real)\n        r_real = math.cos(z.imag) * math.cosh(x_minus_one) * e\n        r_imag = math.sin(z.imag) * math.sinh(x_minus_one) * e\n    else:\n        r_real = math.cos(z.imag) * math.cosh(z.real)\n        r_imag = math.sin(z.imag) * math.sinh(z.real)\n    '''\n    /* detect overflow, and set errno accordingly */\n    if (Py_IS_INFINITY(r.real) || Py_IS_INFINITY(r.imag))\n        errno = ERANGE;\n    else\n        errno = 0;\n    '''\n    return complex(r_real, r_imag)\n\ndef _cos_impl(z):\n    r = _cosh_impl(complex(-z.imag, z.real))\n    return r\n\ndef _exp_impl(z):\n    r_real = 0.\n    r_imag = 0.\n    if (not math.isfinite(z.real)) or (not math.isfinite(z.imag)):\n        if math.isinf(z.real) and math.isfinite(z.imag) and z.imag != 0.:\n            if z.real > 0:\n                r_real = math.copysign(inf, math.cos(z.imag))\n                r_imag = math.copysign(inf, math.sin(z.imag))\n            else:\n                r_real = math.copysign(0., math.cos(z.imag))\n                r_imag = math.copysign(0., math.sin(z.imag))\n        else:\n            r = _special_get(z, _exp_special())\n            r_real = r.real\n            r_imag = r.imag\n        '''\n        /* need to set errno = EDOM if y is +/- infinity and x is not\n           a NaN and not -infinity */\n        if (Py_IS_INFINITY(z.imag) &&\n            (Py_IS_FINITE(z.real) ||\n             (Py_IS_INFINITY(z.real) && z.real > 0)))\n            errno = EDOM;\n        else\n            errno = 0;\n        '''\n        return complex(r_real, r_imag)\n\n    if z.real > _CM_LOG_LARGE_DOUBLE:\n        l = math.exp(z.real - 1.)\n        r_real = l*math.cos(z.imag)*e\n        r_imag = l*math.sin(z.imag)*e\n    else:\n        l = math.exp(z.real)\n        r_real = l*math.cos(z.imag)\n        r_imag = l*math.sin(z.imag)\n    '''\n    /* detect overflow, and set errno accordingly */\n    if (Py_IS_INFINITY(r.real) || Py_IS_INFINITY(r.imag))\n        errno = ERANGE;\n    else\n        errno = 0;\n    '''\n    return complex(r_real, r_imag)\n\ndef _c_log(z):\n    if _is_special(z):\n        return _special_get(z, _log_special())\n\n    ax = math.fabs(z.real)\n    ay = math.fabs(z.imag)\n\n    r_real = 0.\n    r_imag = 0.\n    if ax > _CM_LARGE_DOUBLE or ay > _CM_LARGE_DOUBLE:\n        r_real = math.log(math.hypot(ax/2., ay/2.)) + _M_LN2\n    elif ax < _DBL_MIN and ay < _DBL_MIN:\n        if ax > 0. or ay > 0.:\n            # catch cases where hypot(ax, ay) is subnormal\n            r_real = math.log(math.hypot(math.ldexp(ax, _DBL_MANT_DIG), math.ldexp(ay, _DBL_MANT_DIG))) - _DBL_MANT_DIG*_M_LN2\n        else:\n            # log(+/-0. +/- 0i)\n            r_real = -inf\n            r_imag = math.atan2(z.imag, z.real)\n            # errno = EDOM\n            return complex(r_real, r_imag)\n    else:\n        h = math.hypot(ax, ay)\n        if 0.71 <= h <= 1.73:\n            am = max(ax, ay)\n            an = min(ax, ay)\n            r_real = math.log1p((am-1)*(am+1) + an*an)/2.\n        else:\n            r_real = math.log(h)\n    r_imag = math.atan2(z.imag, z.real)\n    # errno = 0\n    return complex(r_real, r_imag)\n\ndef _log10_impl(z):\n    s = _c_log(z)\n    return complex(s.real / _M_LN10, s.imag / _M_LN10)\n\ndef _sinh_impl(z):\n    r_real = 0.\n    r_imag = 0.\n    if (not math.isfinite(z.real)) or (not math.isfinite(z.imag)):\n        if math.isinf(z.real) and math.isfinite(z.imag) and z.imag != 0.:\n            if z.real > 0:\n                r_real = math.copysign(inf, math.cos(z.imag))\n                r_imag = math.copysign(inf, math.sin(z.imag))\n            else:\n                r_real = -math.copysign(inf, math.cos(z.imag))\n                r_imag = math.copysign(inf, math.sin(z.imag))\n        else:\n            r = _special_get(z, _sinh_special())\n            r_real = r.real\n            r_imag = r.imag\n        '''\n        /* need to set errno = EDOM if y is +/- infinity and x is not\n           a NaN */\n        if (Py_IS_INFINITY(z.imag) && !Py_IS_NAN(z.real))\n            errno = EDOM;\n        else\n            errno = 0;\n        '''\n        return complex(r_real, r_imag)\n\n    if math.fabs(z.real) > _CM_LOG_LARGE_DOUBLE:\n        x_minus_one = z.real - math.copysign(1., z.real)\n        r_real = math.cos(z.imag) * math.sinh(x_minus_one) * e\n        r_imag = math.sin(z.imag) * math.cosh(x_minus_one) * e\n    else:\n        r_real = math.cos(z.imag) * math.sinh(z.real)\n        r_imag = math.sin(z.imag) * math.cosh(z.real)\n    '''\n    /* detect overflow, and set errno accordingly */\n    if (Py_IS_INFINITY(r.real) || Py_IS_INFINITY(r.imag))\n        errno = ERANGE;\n    else\n        errno = 0;\n    '''\n    return complex(r_real, r_imag)\n\ndef _sin_impl(z):\n    s = _sinh_impl(complex(-z.imag, z.real))\n    r = complex(s.imag, -s.real)\n    return r\n\ndef _tanh_impl(z):\n    r_real = 0.\n    r_imag = 0.\n    # special treatment for tanh(+/-inf + iy) if y is finite and\n    # nonzero\n    if (not math.isfinite(z.real)) or (not math.isfinite(z.imag)):\n        if math.isinf(z.real) and math.isfinite(z.imag) and z.imag != 0.:\n            if z.real > 0:\n                r_real = 1.0\n                r_imag = math.copysign(0., 2.*math.sin(z.imag)*math.cos(z.imag))\n            else:\n                r_real = -1.0\n                r_imag = math.copysign(0., 2.*math.sin(z.imag)*math.cos(z.imag))\n        else:\n            r = _special_get(z, _tanh_special())\n            r_real = r.real\n            r_imag = r.imag\n        '''\n        /* need to set errno = EDOM if z.imag is +/-infinity and\n           z.real is finite */\n        if (Py_IS_INFINITY(z.imag) && Py_IS_FINITE(z.real))\n            errno = EDOM;\n        else\n            errno = 0;\n        '''\n        return complex(r_real, r_imag)\n\n    # danger of overflow in 2.*z.imag !\n    if math.fabs(z.real) > _CM_LOG_LARGE_DOUBLE:\n        r_real = math.copysign(1., z.real)\n        r_imag = 4.*math.sin(z.imag)*math.cos(z.imag)*math.exp(-2.*math.fabs(z.real))\n    else:\n        tx = math.tanh(z.real)\n        ty = math.tan(z.imag)\n        cx = 1./math.cosh(z.real)\n        txty = tx*ty\n        denom = 1. + txty*txty\n        r_real = tx*(1. + ty*ty)/denom\n        r_imag = ((ty/denom)*cx)*cx\n    # errno = 0\n    return complex(r_real, r_imag)\n\ndef _tan_impl(z):\n    s = _tanh_impl(complex(-z.imag, z.real))\n    r = complex(s.imag, -s.real)\n    return r\n\ndef phase(x):\n    z = complex(x)\n    return z._phase()\n\ndef polar(x):\n    z = complex(x)\n    return complex(x)._polar()\n\ndef rect(r, phi):\n    z_real = 0.\n    z_imag = 0.\n    if (not math.isfinite(r)) or (not math.isfinite(phi)):\n        # if r is +/-infinity and phi is finite but nonzero then\n        # result is (+-INF +-INF i), but we need to compute cos(phi)\n        # and sin(phi) to figure out the signs.\n        if math.isinf(r) and (math.isfinite(phi) and phi != 0.):\n            if r > 0:\n                z_real = math.copysign(inf, math.cos(phi))\n                z_imag = math.copysign(inf, math.sin(phi))\n            else:\n                z_real = -math.copysign(inf, math.cos(phi))\n                z_imag = -math.copysign(inf, math.sin(phi))\n        else:\n            z = _special_get(complex(r, phi), _rect_special())\n            z_real = z.real\n            z_imag = z.imag\n        '''\n        /* need to set errno = EDOM if r is a nonzero number and phi\n           is infinite */\n        if (r != 0. && !Py_IS_NAN(r) && Py_IS_INFINITY(phi))\n            errno = EDOM;\n        else\n            errno = 0;\n        '''\n    elif phi == 0.0:\n        # Workaround for buggy results with phi=-0.0 on OS X 10.8.  See\n        # bugs.python.org/issue18513.\n        z_real = r\n        z_imag = r * phi\n        # errno = 0\n    else:\n        z_real = r * math.cos(phi)\n        z_imag = r * math.sin(phi)\n        # errno = 0\n    return complex(z_real, z_imag)\n\ndef exp(x):\n    z = complex(x)\n    return _exp_impl(z)\n\ndef log(x, base = e):\n    z = complex(x)\n    y = complex(base)\n    r = _c_log(z)\n    if y == complex(e, 0.0):\n        return r\n    else:\n        return r/_c_log(y)\n\ndef log10(x):\n    z = complex(x)\n    return _log10_impl(z)\n\ndef sqrt(x):\n    z = complex(x)\n    return _sqrt_impl(z)\n\ndef asin(x):\n    z = complex(x)\n    return _asin_impl(z)\n\ndef acos(x):\n    z = complex(x)\n    return _acos_impl(z)\n\ndef atan(x):\n    z = complex(x)\n    return _atan_impl(z)\n\ndef sin(x):\n    z = complex(x)\n    return _sin_impl(z)\n\ndef cos(x):\n    z = complex(x)\n    return _cos_impl(z)\n\ndef tan(x):\n    z = complex(x)\n    return _tan_impl(z)\n\ndef asinh(x):\n    z = complex(x)\n    return _asinh_impl(z)\n\ndef acosh(x):\n    z = complex(x)\n    return _acosh_impl(z)\n\ndef atanh(x):\n    z = complex(x)\n    return _atanh_impl(z)\n\ndef sinh(x):\n    z = complex(x)\n    return _sinh_impl(z)\n\ndef cosh(x):\n    z = complex(x)\n    return _cosh_impl(z)\n\ndef tanh(x):\n    z = complex(x)\n    return _tanh_impl(z)\n\ndef isfinite(x):\n    z = complex(x)\n    return math.isfinite(z.real) and math.isfinite(z.imag)\n\ndef isinf(x):\n    z = complex(x)\n    return math.isinf(z.real) or math.isinf(z.imag)\n\ndef isnan(x):\n    z = complex(x)\n    return math.isnan(z.real) or math.isnan(z.imag)\n\ndef isclose(a, b, rel_tol: float = 1e-09, abs_tol: float = 0.0):\n    if rel_tol < 0. or abs_tol < 0.:\n        raise ValueError(\"tolerances must be non-negative\")\n\n    x = complex(a)\n    y = complex(b)\n\n    if x.real == y.real and x.imag == y.imag:\n        return True\n\n    if (math.isinf(x.real) or math.isinf(x.imag) or\n        math.isinf(y.real) or math.isinf(y.imag)):\n        return False\n\n    diff = abs(x - y)\n    return (((diff <= rel_tol * abs(y)) or\n             (diff <= rel_tol * abs(x))) or\n             (diff <= abs_tol))\n"
  },
  {
    "path": "stdlib/codon/static.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom internal.static import *\n"
  },
  {
    "path": "stdlib/collections.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom internal.types.optional import unwrap\n\n@dataclass(init=False)\nclass deque:\n    _arr: Array[T]\n    _head: int\n    _tail: int\n    _maxlen: int\n    T: type\n\n    def __init__(self, arr: Array[T], head: int, tail: int, maxlen: int):\n        self._arr = arr\n        self._head = head\n        self._tail = tail\n        self._maxlen = maxlen\n\n    def __init__(self):\n        self._arr = Array[T](16)\n        self._head = 0\n        self._tail = 0\n        self._maxlen = -1\n\n    def __init__(self, maxlen: int):\n        cap = 1\n        while cap < maxlen:\n            cap *= 2\n        self._arr = Array[T](cap)\n        self._head = 0\n        self._tail = 0\n        self._maxlen = maxlen\n\n    def __init__(self, it: Generator[T]):\n        self._arr = Array[T](16)\n        self._head = 0\n        self._tail = 0\n        self._maxlen = -1\n        for i in it:\n            self.append(i)\n\n    @property\n    def maxlen(self) -> int:\n        return self._maxlen\n\n    def _double_cap(self):\n        p = self._head\n        n = len(self._arr)\n        r = n - p\n        new_cap = n * 2\n        new_arr = Array[T](new_cap)\n        for i in range(r):\n            new_arr[i] = self._arr[p + i]\n        for i in range(p):\n            new_arr[i + r] = self._arr[i]\n        self._arr = new_arr\n        self._head = 0\n        self._tail = n\n\n    def _check_not_empty(self):\n        if not self:\n            raise IndexError(\"pop from an empty deque\")\n\n    def __bool__(self) -> bool:\n        return self._head != self._tail\n\n    def __len__(self) -> int:\n        return (self._tail - self._head) & (len(self._arr) - 1)\n\n    def appendleft(self, x: T):\n        self._head = (self._head - 1) & (len(self._arr) - 1)\n        self._arr[self._head] = x\n        if self._maxlen >= 0 and len(self) > self._maxlen:\n            self.pop()\n        if self._head == self._tail:\n            self._double_cap()\n\n    def append(self, x: T):\n        self._arr[self._tail] = x\n        self._tail = (self._tail + 1) & (len(self._arr) - 1)\n        if self._maxlen >= 0 and len(self) > self._maxlen:\n            self.popleft()\n        if self._head == self._tail:\n            self._double_cap()\n\n    def popleft(self) -> T:\n        self._check_not_empty()\n        res = self._arr[self._head]\n        self._head = (self._head + 1) & (len(self._arr) - 1)\n        return res\n\n    def pop(self) -> T:\n        self._check_not_empty()\n        self._tail = (self._tail - 1) & (len(self._arr) - 1)\n        return self._arr[self._tail]\n\n    def clear(self):\n        self._head = 0\n        self._tail = 0\n\n    def __iter__(self) -> Generator[T]:\n        i = self._head\n        while i != self._tail:\n            yield self._arr[i]\n            i = (i + 1) & (len(self._arr) - 1)\n\n    def __contains__(self, x: T) -> bool:\n        for i in self:\n            if i == x:\n                return True\n        return False\n\n    def __deepcopy__(self) -> deque[T]:\n        return deque(i.__deepcopy__() for i in self)\n\n    def __copy__(self) -> deque[T]:\n        return deque[T](self._arr.__copy__(), self._head, self._tail, self._maxlen)\n\n    def copy(self) -> deque[T]:\n        return self.__copy__()\n\n    def __repr__(self) -> str:\n        return f\"deque({repr(List[T](iter(self)))})\"\n\n    def _idx_check(self, idx: int, msg: str):\n        if self._head == self._tail or idx >= len(self) or idx < 0:\n            raise IndexError(msg)\n\n    @property\n    def left(self) -> T:\n        self._idx_check(0, \"list index out of range\")\n        return self._arr[self._head]\n\n    def __getitem__(self, idx: int) -> T:\n        if idx < 0:\n            idx += len(self)\n        self._idx_check(idx, \"list index out of range\")\n        if self._head <= self._tail:\n            return self._arr[self._head + idx]\n        elif self._head + idx < len(self._arr):\n            return self._arr[self._head + idx]\n        else:\n            idx -= len(self._arr) - self._head\n            assert 0 <= idx < self._tail\n            return self._arr[idx]\n\n@tuple\nclass _CounterItem:\n    element: T\n    count: int\n    T: type\n\n    def __eq__(self, other: _CounterItem[T]) -> bool:\n        return self.count == other.count\n\n    def __ne__(self, other: _CounterItem[T]) -> bool:\n        return self.count != other.count\n\n    def __lt__(self, other: _CounterItem[T]) -> bool:\n        return self.count < other.count\n\n    def __gt__(self, other: _CounterItem[T]) -> bool:\n        return self.count > other.count\n\n    def __le__(self, other: _CounterItem[T]) -> bool:\n        return self.count <= other.count\n\n    def __ge__(self, other: _CounterItem[T]) -> bool:\n        return self.count >= other.count\n\nclass Counter(Static[Dict[T, int]]):\n    T: type\n\n    def __init__(self, elements: Generator[T]):\n        self._init()\n        self.update(elements)\n\n    def __init__(self, other: Counter[T]):\n        self._init_from(other)\n\n    def __init__(self, other: Dict[T, int]):\n        self._init_from(other)\n\n    def elements(self) -> Generator[T]:\n        for k, v in self.items():\n            for i in range(v):\n                yield k\n\n    def most_common(self, n: Optional[int] = None) -> List[Tuple[T, int]]:\n        if len(self) == 0:\n            return []\n\n        if n is None:\n            v = List(capacity=len(self))\n            for t in self.items():\n                v.append(t)\n            v.sort(reverse=True, key=lambda i: i[1])\n            return v\n        else:\n            from heapq import heapify, heapreplace\n\n            n: int = n\n\n            if n == 1:\n                top: Optional[Tuple[T, int]] = None\n                for t in self.items():\n                    if top is None or t[1] > top[1]:\n                        top = t\n                return [unwrap(top)]\n\n            if n <= 0:\n                return []\n\n            result = List[_CounterItem](capacity=n)\n            for t in self.items():\n                ct = _CounterItem(*t)\n                if len(result) < n:\n                    result.append(ct)\n                    if len(result) == n:\n                        heapify(result)\n                else:\n                    if result[0] < ct:\n                        heapreplace(result, ct)\n\n            result.sort(reverse=True)\n            return [tuple(i) for i in result]\n\n    def subtract(self, elements: Generator[T]):\n        for a in elements:\n            self.increment(a, -1)\n\n    def subtract(self, other: Counter[T]):\n        for k, v in other.items():\n            self.increment(k, -v)\n\n    def subtract(self, other: Dict[T, int]):\n        for k, v in other.items():\n            self.increment(k, -v)\n\n    def update(self, elements: Generator[T]):\n        for a in elements:\n            self.increment(a)\n\n    def update(self, other: Counter[T]):\n        for k, v in other.items():\n            self.increment(k, by=v)\n\n    def update(self, other: Dict[T, int]):\n        for k, v in other.items():\n            self.increment(k, by=v)\n\n    def update(self):\n        pass\n\n    def total(self) -> int:\n        m = 0\n        for v in self.values():\n            m += v\n        return m\n\n    def __getitem__(self, key: T) -> int:\n        return self.get(key, 0)\n\n    def __delitem__(self, key: T):\n        x = self._kh_get(key)\n        if x != self._kh_end():\n            self._kh_del(x)\n\n    def __eq__(self, other: Counter[T]) -> bool:\n        if self.__len__() != other.__len__():\n            return False\n        for k, v in self.items():\n            if k not in other or other[k] != v:\n                return False\n        return True\n\n    def __ne__(self, other: Counter[T]) -> bool:\n        return not (self == other)\n\n    def __copy__(self) -> Counter[T]:\n        return Counter[T](self)\n\n    def __iadd__(self, other: Counter[T]) -> Counter[T]:\n        for k, v in other.items():\n            self.increment(k, by=v)\n        self._del_non_positives()\n        return self\n\n    def __isub__(self, other: Counter[T]) -> Counter[T]:\n        for k, v in other.items():\n            self.increment(k, by=-v)\n        self._del_non_positives()\n        return self\n\n    def __iand__(self, other: Counter[T]) -> Counter[T]:\n        for k, v in other.items():\n            self[k] = min(self.get(k, 0), v)\n        self._del_non_positives()\n        return self\n\n    def __ior__(self, other: Counter[T]) -> Counter[T]:\n        self._del_non_positives()\n        for k, v in other.items():\n            self[k] = max(self.get(k, 0), v)\n        self._del_non_positives()\n        return self\n\n    def __pos__(self) -> Counter[T]:\n        result = Counter[T]()\n        result.resize(self._n_buckets)\n        for k, v in self.items():\n            if v > 0:\n                result[k] = v\n        return result\n\n    def __neg__(self) -> Counter[T]:\n        result = Counter[T]()\n        result.resize(self._n_buckets)\n        for k, v in self.items():\n            if v < 0:\n                result[k] = -v\n        return result\n\n    def __add__(self, other: Counter[T]) -> Counter[T]:\n        result = self.__copy__()\n        result += other\n        return result\n\n    def __sub__(self, other: Counter[T]) -> Counter[T]:\n        result = self.__copy__()\n        result -= other\n        return result\n\n    def __and__(self, other: Counter[T]) -> Counter[T]:\n        result = self.__copy__()\n        result &= other\n        return result\n\n    def __or__(self, other: Counter[T]) -> Counter[T]:\n        result = self.__copy__()\n        result |= other\n        return result\n\n    def __repr__(self):\n        return f\"Counter({super().__repr__()})\"\n\n    def __dict_do_op_throws__(self, key: T, other: Z, op: F, F: type, Z: type):\n        self.__dict_do_op__(key, other, 0, op)\n\n    def _del_non_positives(self):\n        for k, v in self.items():\n            if v <= 0:\n                del self[k]\n\n@extend\nclass Dict:\n    def __init__(self: Dict[K, int], other: Counter[K]):\n        self._init_from(other)\n\nclass defaultdict(Static[Dict[K,V]]):\n    default_factory: S\n    K: type\n    V: type\n    S: TypeTrait[CallableTrait[[], V]]\n\n    def __init__(self: defaultdict[K, VV, Function[[], V]], VV: TypeTrait[V]):\n        super().__init__()\n        self.default_factory = lambda: VV()\n\n    def __init__(self, f: S):\n        super().__init__()\n        self.default_factory = f\n\n    def __init__(self: defaultdict[K, VV, Function[[], V]], VV: TypeTrait[V], other: Dict[K, V]):\n        super().__init__(other)\n        self.default_factory = lambda: VV()\n\n    def __init__(self, f: S, other: Dict[K, V]):\n        super().__init__(other)\n        self.default_factory = f\n\n    def __missing__(self, key: K):\n        default_value = self.default_factory()\n        self.__setitem__(key, default_value)\n        return default_value\n\n    def __getitem__(self, key: K) -> V:\n        if key not in self:\n            return self.__missing__(key)\n        return super().__getitem__(key)\n\n    def __dict_do_op_throws__(self, key: K, other: Z, op: F, F: type, Z: type):\n        x = self._kh_get(key)\n        if x == self._kh_end():\n            self.__missing__(key)\n            x = self._kh_get(key)\n        self._vals[x] = op(self._vals[x], other)\n\n    def copy(self):\n        d = defaultdict[K,V,S](self.default_factory)\n        d._init_from(self)\n        return d\n\n    def __copy__(self):\n        return self.copy()\n\n    def __deepcopy__(self):\n        d = defaultdict[K,V,S](self.default_factory)\n        for k,v in self.items():\n            d[k.__deepcopy__()] = v.__deepcopy__()\n        return d\n\n    def __eq__(self, other: defaultdict[K,V,S]) -> bool:\n        if self.__len__() != other.__len__():\n            return False\n        for k, v in self.items():\n            if k not in other or other[k] != v:\n                return False\n        return True\n\n    def __ne__(self, other: defaultdict[K,V,S]) -> bool:\n        return not (self == other)\n\n    def __repr__(self):\n        return f\"defaultdict(<default factory of '{V.__name__}'>, {super().__repr__()})\"\n\n@extend\nclass Dict:\n    def __init__(self: Dict[K, V], other: defaultdict[K, V, S], S: type):\n        self._init_from(other)\n\ndef namedtuple(name: Literal[str], args):  # internal\n    pass\n"
  },
  {
    "path": "stdlib/copy.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nclass Error(Exception):\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n\ndef copy(x):\n\treturn x.__copy__()\n\ndef deepcopy(x):\n\treturn x.__deepcopy__()\n"
  },
  {
    "path": "stdlib/datetime.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n# Parts of this file: (c) 2022 Python Software Foundation. All right reserved.\n# - Currently does not support timezones\n# - Timedeltas use a pure-microseconds representations for efficiency, meaning they\n#   have a smaller range (+/- 292,471.2 years) but should be more than enough for\n#   all practical uses\n# License:\n#    1. This LICENSE AGREEMENT is between the Python Software Foundation (\"PSF\"), and\n#    the Individual or Organization (\"Licensee\") accessing and otherwise using Python\n#    3.10.2 software in source or binary form and its associated documentation.\n#\n#    2. Subject to the terms and conditions of this License Agreement, PSF hereby\n#    grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,\n#    analyze, test, perform and/or display publicly, prepare derivative works,\n#    distribute, and otherwise use Python 3.10.2 alone or in any derivative\n#    version, provided, however, that PSF's License Agreement and PSF's notice of\n#    copyright, i.e., \"Copyright © 2001-2022 Python Software Foundation; All Rights\n#    Reserved\" are retained in Python 3.10.2 alone or in any derivative version\n#    prepared by Licensee.\n#\n#    3. In the event Licensee prepares a derivative work that is based on or\n#    incorporates Python 3.10.2 or any part thereof, and wants to make the\n#    derivative work available to others as provided herein, then Licensee hereby\n#    agrees to include in any such work a brief summary of the changes made to Python\n#    3.10.2.\n#\n#    4. PSF is making Python 3.10.2 available to Licensee on an \"AS IS\" basis.\n#    PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.  BY WAY OF\n#    EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR\n#    WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE\n#    USE OF PYTHON 3.10.2 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.\n#\n#    5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.10.2\n#    FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF\n#    MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.10.2, OR ANY DERIVATIVE\n#    THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.\n#\n#    6. This License Agreement will automatically terminate upon a material breach of\n#    its terms and conditions.\n#\n#    7. Nothing in this License Agreement shall be deemed to create any relationship\n#    of agency, partnership, or joint venture between PSF and Licensee.  This License\n#    Agreement does not grant permission to use PSF trademarks or trade name in a\n#    trademark sense to endorse or promote products or services of Licensee, or any\n#    third party.\n#\n#    8. By copying, installing or otherwise using Python 3.10.2, Licensee agrees\n#    to be bound by the terms and conditions of this License Agreement.\n\nfrom time import localtime\nfrom time import struct_time\n\n#############\n# constants #\n#############\n\nMINYEAR = 1\nMAXYEAR = 9999\nMAXORDINAL = 3652059\nMAX_DELTA_DAYS = 999999999\n\n_DI4Y = 1461\n_DI100Y = 36524\n_DI400Y = 146097\n\n_ROUND_HALF_EVEN = 0\n_ROUND_CEILING = 1\n_ROUND_FLOOR = 2\n_ROUND_UP = 3\n\n#############\n# utilities #\n#############\n\ndef _signed_add_overflowed(result: int, i: int, j: int) -> bool:\n    return ((result ^ i) & (result ^ j)) < 0\n\ndef _divmod(x: int, y: int) -> Tuple[int, int]:\n    # assert y > 0\n    quo = x // y\n    r = x - quo * y\n    if r < 0:\n        quo -= 1\n        r += y\n    # assert 0 <= r < y\n    return quo, r\n\ndef _divide_nearest(m: int, n: int) -> int:\n    return m // n  # TODO\n\ndef _days_in_monthx(i: int) -> int:\n    return (0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[i]\n\ndef _days_before_monthx(i: int) -> int:\n    return (0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334)[i]\n\ndef _is_leap(year: int) -> bool:\n    return year % 4 == 0 and (year % 100 != 0 or year % 400 == 0)\n\ndef _days_in_month(year: int, month: int) -> int:\n    # assert 1 <= month <= 12\n    if month == 2 and _is_leap(year):\n        return 29\n    else:\n        return _days_in_monthx(month)\n\ndef _days_before_month(year: int, month: int) -> int:\n    # assert 1 <= month <= 12\n    days = _days_before_monthx(month)\n    if month > 2 and _is_leap(year):\n        days += 1\n    return days\n\ndef _days_before_year(year: int) -> int:\n    y = year - 1\n    # assert year >= 1\n    return y * 365 + y // 4 - y // 100 + y // 400\n\ndef _ord_to_ymd(ordinal: int) -> Tuple[int, int, int]:\n    ordinal -= 1\n    n400 = ordinal // _DI400Y\n    n = ordinal % _DI400Y\n    year = n400 * 400 + 1\n\n    n100 = n // _DI100Y\n    n = n % _DI100Y\n\n    n4 = n // _DI4Y\n    n = n % _DI4Y\n\n    n1 = n // 365\n    n = n % 365\n\n    year += n100 * 100 + n4 * 4 + n1\n    if n1 == 4 or n100 == 4:\n        # assert n == 0\n        year -= 1\n        return (year, 12, 31)\n\n    leapyear = (n1 == 3) and (n4 != 24 or n100 == 3)\n    # assert leapyear == is_leap(year)\n    month = (n + 50) >> 5\n    preceding = _days_before_monthx(month) + int(month > 2 and leapyear)\n    if preceding > n:\n        month -= 1\n        preceding -= _days_in_month(year, month)\n    n -= preceding\n    # assert 0 <= n\n    # assert n < _days_in_month(year, month)\n    day = n + 1\n    return (year, month, day)\n\ndef _ymd_to_ord(year: int, month: int, day: int) -> int:\n    return _days_before_year(year) + _days_before_month(year, month) + day\n\ndef _weekday(year: int, month: int, day: int) -> int:\n    return (_ymd_to_ord(year, month, day) + 6) % 7\n\ndef _iso_week1_monday(year: int) -> int:\n    first_day = _ymd_to_ord(year, 1, 1)\n    first_weekday = (first_day + 6) % 7\n    week1_monday = first_day - first_weekday\n    if first_weekday > 3:\n        week1_monday += 7\n    return week1_monday\n\ndef _check_delta_day_range(days: int):\n    if not (-MAX_DELTA_DAYS <= days <= MAX_DELTA_DAYS):\n        raise OverflowError(f\"days={days}; must have magnitude <= {MAX_DELTA_DAYS}\")\n\ndef _check_date_args(year: int, month: int, day: int):\n    if not (MINYEAR <= year <= MAXYEAR):\n        raise ValueError(f\"year {year} is out of range\")\n    if not (1 <= month <= 12):\n        raise ValueError(\"month must be in 1..12\")\n    if not (1 <= day <= _days_in_month(year, month)):\n        raise ValueError(\"day is out of range for month\")\n\ndef _check_time_args(hour: int, minute: int, second: int, microsecond: int):\n    if not (0 <= hour <= 23):\n        raise ValueError(\"hour must be in 0..23\")\n    if not (0 <= minute <= 59):\n        raise ValueError(\"minute must be in 0..59\")\n    if not (0 <= second <= 59):\n        raise ValueError(\"second must be in 0..59\")\n    if not (0 <= microsecond <= 999999):\n        raise ValueError(\"microsecond must be in 0..999999\")\n\ndef _normalize_pair(hi: int, lo: int, factor: int) -> Tuple[int, int]:\n    # assert factor > 0\n    if lo < 0 or lo >= factor:\n        num_hi, lo = _divmod(lo, factor)\n        new_hi = hi + num_hi\n        # assert not _signed_add_overflowed(new_hi, hi, num_hi)\n        hi = new_hi\n    # assert 0 <= lo < factor\n    return hi, lo\n\ndef _normalize_d_s_us(d: int, s: int, us: int) -> Tuple[int, int, int]:\n    if us < 0 or us >= 1000000:\n        s, us = _normalize_pair(s, us, 1000000)\n    if s < 0 or s >= 24 * 3600:\n        d, s = _normalize_pair(d, s, 24 * 3600)\n    # assert 0 <= s < 24*3600\n    # assert 0 <= us < 1000000\n    return d, s, us\n\ndef _normalize_y_m_d(y: int, m: int, d: int) -> Tuple[int, int, int]:\n    def error():\n        raise OverflowError(\"date value out of range\")\n\n    # assert 1 <= m <= 12\n    dim = _days_in_month(y, m)\n    if d < 1 or d > dim:\n        if d == 0:\n            m -= 1\n            if m > 0:\n                d = _days_in_month(y, m)\n            else:\n                y -= 1\n                m = 12\n                d = 31\n        elif d == dim + 1:\n            m += 1\n            d = 1\n            if m > 12:\n                m = 1\n                y += 1\n        else:\n            ordinal = _ymd_to_ord(y, m, 1) + d - 1\n            if ordinal < 1 or ordinal > MAXORDINAL:\n                error()\n            else:\n                return _ord_to_ymd(ordinal)\n    # assert m > 0\n    # assert d > 0\n    if not (MINYEAR <= y <= MAXYEAR):\n        error()\n    return y, m, d\n\ndef _normalize_date(year: int, month: int, day: int) -> Tuple[int, int, int]:\n    return _normalize_y_m_d(year, month, day)\n\ndef _normalize_datetime(\n    year: int,\n    month: int,\n    day: int,\n    hour: int,\n    minute: int,\n    second: int,\n    microsecond: int,\n) -> Tuple[int, int, int, int, int, int, int]:\n    second, microsecond = _normalize_pair(second, microsecond, 1000000)\n    minute, second = _normalize_pair(minute, second, 60)\n    hour, minute = _normalize_pair(hour, minute, 60)\n    day, hour = _normalize_pair(day, hour, 24)\n    year, month, day = _normalize_date(year, month, day)\n    return year, month, day, hour, minute, second, microsecond\n\n\ndef _parse_digits(digits: str, num_digits: int) -> Tuple[str, int]:\n    if len(digits) < num_digits:\n        return \"\", -1\n    p = digits.ptr\n    var = 0\n    for i in range(num_digits):\n        tmp = int(p[0]) - 48  # 48 == '0'\n        if not (0 <= tmp <= 9):\n            return \"\", -1\n        var *= 10\n        var += tmp\n        p += 1\n    return str(p, len(digits) - num_digits), var\n\ndef _isoformat_error(s: str):\n    raise ValueError(f\"Invalid isoformat string: {s}\")\n\ndef _parse_isoformat_date(dtstr: str) -> Tuple[int, int, int]:\n    p = dtstr\n    p, year = _parse_digits(p, 4)\n    if year < 0:\n        _isoformat_error(dtstr)\n\n    if not p or p[0] != \"-\":\n        _isoformat_error(dtstr)\n    p = p[1:]\n\n    p, month = _parse_digits(p, 2)\n    if month < 0:\n        _isoformat_error(dtstr)\n\n    if not p or p[0] != \"-\":\n        _isoformat_error(dtstr)\n    p = p[1:]\n\n    p, day = _parse_digits(p, 2)\n    if day < 0 or p:\n        _isoformat_error(dtstr)\n\n    return year, month, day\n\ndef _parse_hh_mm_ss_ff(tstr: str) -> Tuple[int, int, int, int]:\n    hour, minute, second, microsecond = 0, 0, 0, 0\n\n    p = tstr\n    for i in range(3):\n        p, val = _parse_digits(p, 2)\n        if val < 0:\n            _isoformat_error(tstr)\n\n        if i == 0:\n            hour = val\n        if i == 1:\n            minute = val\n        if i == 2:\n            second = val\n\n        if not p:\n            return hour, minute, second, microsecond\n        c = p[0]\n        p = p[1:]\n        if c == \":\":\n            continue\n        elif c == \".\":\n            break\n        else:\n            _isoformat_error(tstr)\n\n    len_remains = len(p)\n    if not (len_remains == 6 or len_remains == 3):\n        _isoformat_error(tstr)\n\n    p, microsecond = _parse_digits(p, len_remains)\n    if microsecond < 0:\n        _isoformat_error(tstr)\n\n    if len_remains == 3:\n        microsecond *= 1000\n\n    return hour, minute, second, microsecond\n\ndef _parse_isoformat_time(dtstr: str) -> Tuple[int, int, int, int, int, int]:\n    n = len(dtstr)\n    tzinfo_pos = 0\n    tzsign = 0\n    while tzinfo_pos < n:\n        c = dtstr[tzinfo_pos]\n        if c == \"+\":\n            tzsign = 1\n            break\n        if c == \"-\":\n            tzsign = -1\n            break\n        tzinfo_pos += 1\n\n    hour, minute, second, microsecond = _parse_hh_mm_ss_ff(dtstr[:tzinfo_pos])\n    if tzinfo_pos == n:\n        return hour, minute, second, microsecond, 0, 0\n\n    tzlen = n - tzinfo_pos\n    if not (tzlen == 6 or tzlen == 9 or tzlen == 16):\n        _isoformat_error(dtstr)\n\n    tzhour, tzminute, tzsecond, tzmicrosecond = _parse_hh_mm_ss_ff(\n        dtstr[tzinfo_pos + 1 :]\n    )\n    tzoffset = tzsign * ((tzhour * 3600) + (tzminute * 60) + tzsecond)\n    tzmicrosecond *= tzsign\n    return hour, minute, second, microsecond, tzoffset, tzmicrosecond\n\ndef _format_ctime(\n    year: int, month: int, day: int, hours: int, minutes: int, seconds: int\n) -> str:\n    DAY_NAMES = (\"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\", \"Sun\")\n    MONTH_NAMES = (\n        \"Jan\",\n        \"Feb\",\n        \"Mar\",\n        \"Apr\",\n        \"May\",\n        \"Jun\",\n        \"Jul\",\n        \"Aug\",\n        \"Sep\",\n        \"Oct\",\n        \"Nov\",\n        \"Dec\",\n    )\n    wday = _weekday(year, month, day)\n    return f\"{DAY_NAMES[wday]} {MONTH_NAMES[month - 1]} {str(day).rjust(2)} {str(hours).zfill(2)}:{str(minutes).zfill(2)}:{str(seconds).zfill(2)} {str(year).zfill(4)}\"\n\ndef _utc_to_seconds(\n    year: int, month: int, day: int, hour: int, minute: int, second: int\n) -> int:\n    if year < MINYEAR or year > MAXYEAR:\n        raise ValueError(f\"year {year} is out of range\")\n    ordinal = _ymd_to_ord(year, month, day)\n    return ((ordinal * 24 + hour) * 60 + minute) * 60 + second\n\ndef _round_half_even(x: float) -> float:\n    from math import fabs\n\n    rounded = x.__round__()\n    if fabs(x - rounded) == 0.5:\n        rounded = 2.0 * (x / 2.0).__round__()\n    return rounded\n\n################\n# core classes #\n################\n\n@tuple\nclass timedelta:\n    min: ClassVar[timedelta]\n    max: ClassVar[timedelta]\n    resolution: ClassVar[timedelta]\n\n    _microseconds: int\n\n    def _new(microseconds: int) -> timedelta:\n        return type._force_value_cast((microseconds,), timedelta)\n\n    @inline\n    def _accum(sofar: int, leftover: float, num: int, factor: int) -> Tuple[int, float]:\n        sofar += num * factor\n        return sofar, leftover\n\n    @inline\n    def _accum(\n        sofar: int, leftover: float, num: float, factor: int\n    ) -> Tuple[int, float]:\n        from math import modf\n\n        fracpart, intpart = modf(num)\n        prod = int(intpart) * factor\n        s = sofar + prod\n\n        if fracpart == 0.0:\n            return s, leftover\n        dnum = factor * fracpart\n        fracpart, intpart = modf(dnum)\n        y = s + int(intpart)\n        return y, leftover + fracpart\n\n    def __new__(\n        days: float = 0,\n        seconds: float = 0,\n        microseconds: float = 0,\n        milliseconds: float = 0,\n        minutes: float = 0,\n        hours: float = 0,\n        weeks: float = 0,\n    ) -> timedelta:\n        us = 0\n        leftover = 0.0\n\n        us, leftover = timedelta._accum(us, leftover, days, 24 * 60 * 60 * 1000000)\n        us, leftover = timedelta._accum(us, leftover, seconds, 1000000)\n        us, leftover = timedelta._accum(us, leftover, microseconds, 1)\n        us, leftover = timedelta._accum(us, leftover, milliseconds, 1000)\n        us, leftover = timedelta._accum(us, leftover, minutes, 60 * 1000000)\n        us, leftover = timedelta._accum(us, leftover, hours, 60 * 60 * 1000000)\n        us, leftover = timedelta._accum(us, leftover, weeks, 7 * 24 * 60 * 60 * 1000000)\n\n        if leftover:\n            from math import fabs\n\n            whole_us = leftover.__round__()\n            if fabs(whole_us - leftover) == 0.5:\n                is_odd = us & 1\n                whole_us = 2.0 * ((leftover + is_odd) * 0.5).__round__() - is_odd\n            us += int(whole_us)\n\n        return timedelta._new(us)\n\n    # override default constructor\n    def __new__(days: int) -> timedelta:\n        return timedelta(days, 0)\n\n    @property\n    def days(self) -> int:\n        days, seconds, microseconds = _normalize_d_s_us(0, 0, self._microseconds)\n        return days\n\n    @property\n    def seconds(self) -> int:\n        days, seconds, microseconds = _normalize_d_s_us(0, 0, self._microseconds)\n        return seconds\n\n    @property\n    def microseconds(self) -> int:\n        days, seconds, microseconds = _normalize_d_s_us(0, 0, self._microseconds)\n        return microseconds\n\n    def __repr__(self) -> str:\n        days, seconds, microseconds = _normalize_d_s_us(0, 0, self._microseconds)\n        if days == 0 and seconds == 0 and microseconds == 0:\n            return \"timedelta(0)\"\n        v = []\n        if days:\n            v.append(f\"days={days}\")\n        if seconds:\n            v.append(f\"seconds={seconds}\")\n        if microseconds:\n            v.append(f\"microseconds={microseconds}\")\n        return f\"timedelta({', '.join(v)})\"\n\n    def __str__(self) -> str:\n        days, seconds, us = _normalize_d_s_us(0, 0, self._microseconds)\n        minutes, seconds = _divmod(seconds, 60)\n        hours, minutes = _divmod(minutes, 60)\n\n        if days:\n            if us:\n                return f\"{days} day{'' if days == 1 or days == -1 else 's'}, {hours}:{str(minutes).zfill(2)}:{str(seconds).zfill(2)}.{str(us).zfill(6)}\"\n            else:\n                return f\"{days} day{'' if days == 1 or days == -1 else 's'}, {hours}:{str(minutes).zfill(2)}:{str(seconds).zfill(2)}\"\n        else:\n            if us:\n                return f\"{hours}:{str(minutes).zfill(2)}:{str(seconds).zfill(2)}.{str(us).zfill(6)}\"\n            else:\n                return f\"{hours}:{str(minutes).zfill(2)}:{str(seconds).zfill(2)}\"\n\n    def __add__(self, other: timedelta) -> timedelta:\n        return timedelta._new(self._microseconds + other._microseconds)\n\n    def __sub__(self, other: timedelta) -> timedelta:\n        return timedelta._new(self._microseconds - other._microseconds)\n\n    def __mul__(self, other: int) -> timedelta:\n        return timedelta._new(self._microseconds * other)\n\n    def __rmul__(self, other: int) -> timedelta:\n        return self * other\n\n    def __mul__(self, other: float) -> timedelta:\n        return timedelta._new(int(_round_half_even(self._microseconds * other)))\n\n    def __rmul__(self, other: float) -> timedelta:\n        return self * other\n\n    def __truediv__(self, other: timedelta) -> float:\n        return self._microseconds / other._microseconds\n\n    def __truediv__(self, other: float) -> timedelta:\n        return timedelta._new(int(_round_half_even(self._microseconds / other)))\n\n    def __truediv__(self, other: int) -> timedelta:\n        return self / float(other)\n\n    def __floordiv__(self, other: timedelta) -> int:\n        return int((self._microseconds / other._microseconds).__floor__())\n\n    def __floordiv__(self, other: int) -> timedelta:\n        return timedelta._new(self._microseconds // other)\n\n    def __mod__(self, other: timedelta) -> timedelta:\n        n = self._microseconds\n        M = other._microseconds\n        m = self._microseconds % other._microseconds\n        return timedelta._new(((n % M) + M) % M)\n\n    def __divmod__(self, other: timedelta) -> Tuple[int, timedelta]:\n        return self // other, self % other\n\n    def __pos__(self) -> timedelta:\n        return self\n\n    def __neg__(self) -> timedelta:\n        return timedelta._new(-self._microseconds)\n\n    def __abs__(self) -> timedelta:\n        return timedelta._new(abs(self._microseconds))\n\n    def __eq__(self, other: timedelta) -> bool:\n        return self._microseconds == other._microseconds\n\n    def __ne__(self, other: timedelta) -> bool:\n        return self._microseconds != other._microseconds\n\n    def __lt__(self, other: timedelta) -> bool:\n        return self._microseconds < other._microseconds\n\n    def __le__(self, other: timedelta) -> bool:\n        return self._microseconds <= other._microseconds\n\n    def __gt__(self, other: timedelta) -> bool:\n        return self._microseconds > other._microseconds\n\n    def __ge__(self, other: timedelta) -> bool:\n        return self._microseconds >= other._microseconds\n\n    def __bool__(self) -> bool:\n        return bool(self._microseconds)\n\n    def total_seconds(self) -> float:\n        return self._microseconds / 1e6\n\n\ntimedelta.min = timedelta._new(-9223372036854775808)\ntimedelta.max = timedelta._new(9223372036854775807)\ntimedelta.resolution = timedelta(microseconds=1)\n\n@tuple\nclass IsoCalendarDate:\n    year: int\n    week: int\n    weekday: int\n\n    def __repr__(self) -> str:\n        return f\"IsoCalendarDate(year={self.year}, week={self.week}, weekday={self.weekday})\"\n\n@tuple\nclass date:\n    min: ClassVar[date]\n    max: ClassVar[date]\n    resolution: ClassVar[timedelta] = timedelta(days=1)\n\n    _value: UInt[32]\n\n    def __new__(year: int, month: int, day: int) -> date:\n        _check_date_args(year, month, day)\n        v = (year << 16) | (month << 8) | day\n        return date(UInt[32](v))\n\n    @property\n    def year(self) -> int:\n        v = int(self._value)\n        return v >> 16\n\n    @property\n    def month(self) -> int:\n        v = int(self._value)\n        return (v >> 8) & 0xFF\n\n    @property\n    def day(self) -> int:\n        v = int(self._value)\n        return v & 0xFF\n\n    def __repr__(self) -> str:\n        return f\"date(year={self.year}, month={self.month}, day={self.day})\"\n\n    def today() -> date:\n        from time import time as ttime\n\n        return date.fromtimestamp(ttime())\n\n    def fromtimestamp(timestamp) -> date:\n        ts = int(timestamp)\n        tm = localtime(ts)\n        return date(tm.tm_year, tm.tm_mon, tm.tm_mday)\n\n    def fromordinal(ordinal: int) -> date:\n        return date(*_ord_to_ymd(ordinal))\n\n    def fromisoformat(date_string: str) -> date:\n        return date(*_parse_isoformat_date(date_string))\n\n    def fromisocalendar(year, week, day) -> date:\n        if year < MINYEAR or year > MAXYEAR:\n            raise ValueError(f\"Year is out of range: {year}\")\n\n        if week <= 0 or week >= 53:\n            out_of_range = True\n            if week == 53:\n                first_weekday = _weekday(year, 1, 1)\n                if first_weekday == 3 or (first_weekday == 2 and _is_leap(year)):\n                    out_of_range = False\n\n            if out_of_range:\n                raise ValueError(f\"Invalid week: {week}\")\n\n        if day <= 0 or day >= 8:\n            raise ValueError(f\"Invalid day: {day} (range is [1, 7])\")\n\n        day_1 = _iso_week1_monday(year)\n        month = week\n        day_offset = (month - 1) * 7 + day - 1\n        return date(*_ord_to_ymd(day_1 + day_offset))\n\n    def __add__(self, other: timedelta) -> date:\n        days, seconds, microseconds = _normalize_d_s_us(0, 0, other._microseconds)\n        day = self.day + days\n        return date(*_normalize_date(self.year, self.month, day))\n\n    def __sub__(self, other: timedelta) -> date:\n        days, seconds, microseconds = _normalize_d_s_us(0, 0, other._microseconds)\n        day = self.day - days\n        return date(*_normalize_date(self.year, self.month, day))\n\n    def __sub__(self, other: date) -> timedelta:\n        left_ord = _ymd_to_ord(self.year, self.month, self.day)\n        right_ord = _ymd_to_ord(other.year, other.month, other.day)\n        return timedelta(days=left_ord - right_ord)\n\n    def __eq__(self, other: date) -> bool:\n        return self._value == other._value\n\n    def __ne__(self, other: date) -> bool:\n        return self._value != other._value\n\n    def __lt__(self, other: date) -> bool:\n        return self._value < other._value\n\n    def __le__(self, other: date) -> bool:\n        return self._value <= other._value\n\n    def __gt__(self, other: date) -> bool:\n        return self._value > other._value\n\n    def __ge__(self, other: date) -> bool:\n        return self._value >= other._value\n\n    def __bool__(self) -> bool:\n        return True\n\n    def replace(self, year: int = -1, month: int = -1, day: int = -1) -> date:\n        if year == -1:\n            year = self.year\n        if month == -1:\n            month = self.month\n        if day == -1:\n            day = self.day\n        return date(year, month, day)\n\n    def timetuple(self) -> struct_time:\n        yday = self.toordinal() - date(self.year, 1, 1).toordinal() + 1\n        return struct_time(\n            self.year, self.month, self.day, 0, 0, 0, self.weekday(), yday, -1\n        )\n\n    def toordinal(self) -> int:\n        return _ymd_to_ord(self.year, self.month, self.day)\n\n    def weekday(self) -> int:\n        return _weekday(self.year, self.month, self.day)\n\n    def isoweekday(self) -> int:\n        return self.weekday() + 1\n\n    def isocalendar(self) -> IsoCalendarDate:\n        year = self.year\n        week1_monday = _iso_week1_monday(year)\n        today = _ymd_to_ord(year, self.month, self.day)\n        week, day = _divmod(today - week1_monday, 7)\n        if week < 0:\n            year -= 1\n            week1_monday = _iso_week1_monday(year)\n            week, day = _divmod(today - week1_monday, 7)\n        elif week >= 52 and today >= _iso_week1_monday(year + 1):\n            year += 1\n            week = 0\n        return IsoCalendarDate(year, week + 1, day + 1)\n\n    def isoformat(self) -> str:\n        return f\"{str(self.year).zfill(4)}-{str(self.month).zfill(2)}-{str(self.day).zfill(2)}\"\n\n    def __str__(self) -> str:\n        return self.isoformat()\n\n    def ctime(self) -> str:\n        return _format_ctime(self.year, self.month, self.day, 0, 0, 0)\n\n    # strftime() / __format__() not supported\n\n\ndate.min = date(MINYEAR, 1, 1)\ndate.max = date(MAXYEAR, 12, 31)\n\n\n@tuple\nclass time:\n    min: ClassVar[time]\n    max: ClassVar[time]\n    resolution: ClassVar[timedelta] = timedelta(microseconds=1)\n\n    _value: int\n\n    def __new__(\n        hour: int = 0, minute: int = 0, second: int = 0, microsecond: int = 0\n    ) -> time:\n        _check_time_args(hour, minute, second, microsecond)\n        v = (hour << 40) | (minute << 32) | (second << 24) | microsecond\n        return superf(v)\n\n    @property\n    def hour(self) -> int:\n        v = self._value\n        return v >> 40\n\n    @property\n    def minute(self) -> int:\n        v = self._value\n        return (v >> 32) & 0xFF\n\n    @property\n    def second(self) -> int:\n        v = self._value\n        return (v >> 24) & 0xFF\n\n    @property\n    def microsecond(self) -> int:\n        v = self._value\n        return v & 0xFFFFFF\n\n    def __repr__(self) -> str:\n        h, m, s, us = self.hour, self.minute, self.second, self.microsecond\n        v = []\n        v.append(f\"hour={h}\")\n        v.append(f\"minute={m}\")\n        if s or us:\n            v.append(f\"second={s}\")\n        if us:\n            v.append(f\"microsecond={us}\")\n        return f\"time({', '.join(v)})\"\n\n    def __str__(self) -> str:\n        return self.isoformat()\n\n    def __bool__(self) -> bool:\n        return True\n\n    def fromisoformat(time_string: str) -> time:\n        (\n            hour,\n            minute,\n            second,\n            microsecond,\n            tzoffset,\n            tzmicrosecond,\n        ) = _parse_isoformat_time(time_string)\n        # TODO: deal with timezone\n        return time(hour, minute, second, microsecond)\n\n    def replace(\n        self, hour: int = -1, minute: int = -1, second: int = -1, microsecond: int = -1\n    ) -> time:\n        if hour == -1:\n            hour = self.hour\n        if second == -1:\n            second = self.second\n        if minute == -1:\n            minute = self.minute\n        if microsecond == -1:\n            microsecond = self.microsecond\n        return time(hour, minute, second, microsecond)\n\n    def isoformat(self, timespec: Literal[str] = \"auto\") -> str:\n        hh = str(self.hour).zfill(2)\n        mm = str(self.minute).zfill(2)\n        ss = str(self.second).zfill(2)\n        us = str(self.microsecond).zfill(6)\n        ms = str(self.microsecond // 1000).zfill(3)\n\n        if timespec == \"auto\":\n            if self.microsecond:\n                return f\"{hh}:{mm}:{ss}.{us}\"\n            else:\n                return f\"{hh}:{mm}:{ss}\"\n        elif timespec == \"hours\":\n            return hh\n        elif timespec == \"minutes\":\n            return f\"{hh}:{mm}\"\n        elif timespec == \"seconds\":\n            return f\"{hh}:{mm}:{ss}\"\n        elif timespec == \"milliseconds\":\n            return f\"{hh}:{mm}:{ss}.{ms}\"\n        elif timespec == \"microseconds\":\n            return f\"{hh}:{mm}:{ss}.{us}\"\n        else:\n            compile_error(\n                \"invalid timespec; valid ones are 'auto', 'hours', 'minutes', 'seconds', 'milliseconds' and 'microseconds'\"\n            )\n\n\ntime.min = time(0, 0, 0, 0)\ntime.max = time(23, 59, 59, 999999)\n@tuple\nclass datetime:\n    min: ClassVar[datetime]\n    max: ClassVar[datetime]\n    resolution: ClassVar[timedelta] = timedelta(microseconds=1)\n\n    _time: time\n    _date: date\n\n    def __new__(\n        year: int,\n        month: int,\n        day: int,\n        hour: int = 0,\n        minute: int = 0,\n        second: int = 0,\n        microsecond: int = 0,\n    ) -> datetime:\n        return datetime(time(hour, minute, second, microsecond), date(year, month, day))\n\n    def date(self) -> date:\n        return self._date\n\n    def time(self) -> time:\n        return self._time\n\n    @property\n    def year(self) -> int:\n        return self.date().year\n\n    @property\n    def month(self) -> int:\n        return self.date().month\n\n    @property\n    def day(self) -> int:\n        return self.date().day\n\n    @property\n    def hour(self) -> int:\n        return self.time().hour\n\n    @property\n    def minute(self) -> int:\n        return self.time().minute\n\n    @property\n    def second(self) -> int:\n        return self.time().second\n\n    @property\n    def microsecond(self) -> int:\n        return self.time().microsecond\n\n    def __repr__(self) -> str:\n        return f\"datetime(year={self.year}, month={self.month}, day={self.day}, hour={self.hour}, minute={self.minute}, second={self.second}, microsecond={self.microsecond})\"\n\n    def __str__(self) -> str:\n        return self.isoformat(sep=\" \")\n\n    def _from_timet_and_us(timet, us) -> datetime:\n        tm = localtime(timet)\n        year = tm.tm_year\n        month = tm.tm_mon\n        day = tm.tm_mday\n        hour = tm.tm_hour\n        minute = tm.tm_min\n        second = min(59, tm.tm_sec)\n        # TODO: timezone adjustments\n        return datetime(year, month, day, hour, minute, second, us)\n\n    def today() -> datetime:\n        from time import time as ttime\n\n        return datetime.fromtimestamp(ttime())\n\n    # TODO: support timezone\n    def now() -> datetime:\n        return datetime.today()\n\n    def utcnow() -> datetime:\n        return datetime.now()\n\n    # TODO: support timezone\n    def fromtimestamp(timestamp) -> datetime:\n        from time import _time_to_timeval, _ROUND_HALF_EVEN\n\n        timet, us = _time_to_timeval(float(timestamp), _ROUND_HALF_EVEN)\n        return datetime._from_timet_and_us(timet, us)\n\n    def utcfromtimestamp(timestamp) -> datetime:\n        return datetime.fromtimestamp(timestamp)\n\n    def fromordinal(ordinal: int) -> datetime:\n        return datetime.combine(date.fromordinal(ordinal), time())\n\n    # TODO: support timezone\n    def combine(date: date, time: time) -> datetime:\n        return datetime(time, date)\n\n    def fromisoformat(date_string: str) -> datetime:\n        time_string = \"\" if len(date_string) < 10 else date_string[:10]\n        year, month, day = _parse_isoformat_date(time_string)\n        if len(date_string) == 10:\n            return datetime(year=year, month=month, day=day)\n        date_string = \"\" if len(date_string) < 12 else date_string[11:]\n        hour, minute, second, microsecond = _parse_hh_mm_ss_ff(date_string)\n        return datetime(\n            year=year,\n            month=month,\n            day=day,\n            hour=hour,\n            minute=minute,\n            second=second,\n            microsecond=microsecond,\n        )\n\n    def fromisocalendar(year: int, week: int, day: int) -> datetime:\n        return datetime.combine(date.fromisocalendar(year, week, day), time())\n\n    def __add__(self, other: timedelta) -> datetime:\n        td_days, td_seconds, td_microseconds = _normalize_d_s_us(\n            0, 0, other._microseconds\n        )\n        year = self.year\n        month = self.month\n        day = self.day + td_days\n        hour = self.hour\n        minute = self.minute\n        second = self.second + td_seconds\n        microsecond = self.microsecond + td_microseconds\n        return datetime(\n            *_normalize_datetime(year, month, day, hour, minute, second, microsecond)\n        )\n\n    def __sub__(self, other: timedelta) -> datetime:\n        td_days, td_seconds, td_microseconds = _normalize_d_s_us(\n            0, 0, other._microseconds\n        )\n        year = self.year\n        month = self.month\n        day = self.day - td_days\n        hour = self.hour\n        minute = self.minute\n        second = self.second - td_seconds\n        microsecond = self.microsecond - td_microseconds\n        return datetime(\n            *_normalize_datetime(year, month, day, hour, minute, second, microsecond)\n        )\n\n    def __sub__(self, other: datetime) -> timedelta:\n        delta_d = _ymd_to_ord(self.year, self.month, self.day) - _ymd_to_ord(\n            other.year, other.month, other.day\n        )\n        delta_s = (\n            (self.hour - other.hour) * 3600\n            + (self.minute - other.minute) * 60\n            + (self.second - other.second)\n        )\n        delta_us = self.microsecond - other.microsecond\n        return timedelta(days=delta_d, seconds=delta_s, microseconds=delta_us)\n\n    def __eq__(self, other: datetime) -> bool:\n        return self.date() == other.date() and self.time() == other.time()\n\n    def __ne__(self, other: datetime) -> bool:\n        return not (self == other)\n\n    def __lt__(self, other: datetime) -> bool:\n        return (self.date(), self.time()) < (other.date(), other.time())\n\n    def __le__(self, other: datetime) -> bool:\n        return (self.date(), self.time()) <= (other.date(), other.time())\n\n    def __gt__(self, other: datetime) -> bool:\n        return (self.date(), self.time()) > (other.date(), other.time())\n\n    def __ge__(self, other: datetime) -> bool:\n        return (self.date(), self.time()) >= (other.date(), other.time())\n\n    def __bool__(self) -> bool:\n        return True\n\n    def replace(\n        self,\n        year: int = -1,\n        month: int = -1,\n        day: int = -1,\n        hour: int = -1,\n        minute: int = -1,\n        second: int = -1,\n        microsecond: int = -1,\n    ) -> datetime:\n        return datetime(\n            self.time().replace(hour, minute, second, microsecond),\n            self.date().replace(year, month, day),\n        )\n\n    def timetuple(self) -> struct_time:\n        yday = self.toordinal() - date(self.year, 1, 1).toordinal() + 1\n        return struct_time(\n            self.year,\n            self.month,\n            self.day,\n            self.hour,\n            self.minute,\n            self.second,\n            self.weekday(),\n            yday,\n            -1,\n        )\n\n    def utctimetuple(self) -> struct_time:\n        return self.timetuple()\n\n    def toordinal(self) -> int:\n        return self.date().toordinal()\n\n    def timestamp(self) -> float:\n        return (self - datetime(1970, 1, 1)).total_seconds()\n\n    def weekday(self) -> int:\n        return self.date().weekday()\n\n    def isoweekday(self) -> int:\n        return self.date().isoweekday()\n\n    def isocalendar(self) -> IsoCalendarDate:\n        return self.date().isocalendar()\n\n    def isoformat(self, sep: str = \"T\", timespec: Literal[str] = \"auto\") -> str:\n        date_part = str(self.date())\n        time_part = self.time().isoformat(timespec=timespec)\n        return f\"{date_part}{sep}{time_part}\"\n\n    def ctime(self) -> str:\n        date = self.date()\n        time = self.time()\n        return _format_ctime(\n            date.year, date.month, date.day, time.hour, time.minute, time.second\n        )\n\ndatetime.min = datetime(MINYEAR, 1, 1)\ndatetime.max = datetime(MAXYEAR, 12, 31, 23, 59, 59, 999999)\n@extend\nclass timedelta:\n    def __add__(self, other: date) -> date:\n        return other + self\n\n    def __add__(self, other: datetime) -> datetime:\n        return other + self\n"
  },
  {
    "path": "stdlib/functools.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n@no_arg_reorder\ndef partial():  # internal\n    pass\n"
  },
  {
    "path": "stdlib/getopt.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n# Parts of this file: (c) 2022 Python Software Foundation. All right reserved.\n# License:\n#    1. This LICENSE AGREEMENT is between the Python Software Foundation (\"PSF\"), and\n#    the Individual or Organization (\"Licensee\") accessing and otherwise using Python\n#    3.10.2 software in source or binary form and its associated documentation.\n#\n#    2. Subject to the terms and conditions of this License Agreement, PSF hereby\n#    grants Licensee a nonexclusive, royalty-free, world-wide license to reproduce,\n#    analyze, test, perform and/or display publicly, prepare derivative works,\n#    distribute, and otherwise use Python 3.10.2 alone or in any derivative\n#    version, provided, however, that PSF's License Agreement and PSF's notice of\n#    copyright, i.e., \"Copyright © 2001-2022 Python Software Foundation; All Rights\n#    Reserved\" are retained in Python 3.10.2 alone or in any derivative version\n#    prepared by Licensee.\n#\n#    3. In the event Licensee prepares a derivative work that is based on or\n#    incorporates Python 3.10.2 or any part thereof, and wants to make the\n#    derivative work available to others as provided herein, then Licensee hereby\n#    agrees to include in any such work a brief summary of the changes made to Python\n#    3.10.2.\n#\n#    4. PSF is making Python 3.10.2 available to Licensee on an \"AS IS\" basis.\n#    PSF MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.  BY WAY OF\n#    EXAMPLE, BUT NOT LIMITATION, PSF MAKES NO AND DISCLAIMS ANY REPRESENTATION OR\n#    WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE\n#    USE OF PYTHON 3.10.2 WILL NOT INFRINGE ANY THIRD PARTY RIGHTS.\n#\n#    5. PSF SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF PYTHON 3.10.2\n#    FOR ANY INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF\n#    MODIFYING, DISTRIBUTING, OR OTHERWISE USING PYTHON 3.10.2, OR ANY DERIVATIVE\n#    THEREOF, EVEN IF ADVISED OF THE POSSIBILITY THEREOF.\n#\n#    6. This License Agreement will automatically terminate upon a material breach of\n#    its terms and conditions.\n#\n#    7. Nothing in this License Agreement shall be deemed to create any relationship\n#    of agency, partnership, or joint venture between PSF and Licensee.  This License\n#    Agreement does not grant permission to use PSF trademarks or trade name in a\n#    trademark sense to endorse or promote products or services of Licensee, or any\n#    third party.\n#\n#    8. By copying, installing or otherwise using Python 3.10.2, Licensee agrees\n#    to be bound by the terms and conditions of this License Agreement.\n\nimport os\n\nclass GetoptError(Exception):\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n\ndef long_has_args(opt: str, longopts: List[str]) -> Tuple[bool, str]:\n    possibilities = [o for o in longopts if o.startswith(opt)]\n    if not possibilities:\n        raise GetoptError(f\"option --{opt} not recognized\")\n    # Is there an exact match?\n    if opt in possibilities:\n        return False, opt\n    elif opt + \"=\" in possibilities:\n        return True, opt\n    # No exact match, so better be unique.\n    if len(possibilities) > 1:\n        # XXX since possibilities contains all valid continuations, might be\n        # nice to work them into the error msg\n        raise GetoptError(f\"option --{opt} not a unique prefix\")\n    assert len(possibilities) == 1\n    unique_match = possibilities[0]\n    has_arg = unique_match.endswith(\"=\")\n    if has_arg:\n        unique_match = unique_match[:-1]\n    return has_arg, unique_match\n\ndef do_longs(\n    opts: List[Tuple[str, str]], opt: str, longopts: List[str], args: List[str]\n) -> Tuple[List[Tuple[str, str]], List[str]]:\n    optarg = \"\"\n    try:\n        i = opt.index(\"=\")\n        opt, optarg = opt[:i], opt[i + 1 :]\n    except ValueError:\n        pass\n\n    has_arg, opt = long_has_args(opt, longopts)\n    if has_arg:\n        if optarg == \"\":\n            if not args:\n                raise GetoptError(f\"option --{opt} requires argument\")\n            optarg, args = args[0], args[1:]\n    elif optarg != \"\":\n        raise GetoptError(f\"option --{opt} must not have an argument\")\n    opts.append((f\"--{opt}\", optarg))\n    return opts, args\n\ndef short_has_arg(opt: str, shortopts: str) -> bool:\n    for i in range(len(shortopts)):\n        if opt == shortopts[i] != \":\":\n            return shortopts.startswith(\":\", i + 1)\n    raise GetoptError(f\"option -{opt} not recognized\")\n\ndef do_shorts(\n    opts: List[Tuple[str, str]], optstring: str, shortopts: str, args: List[str]\n) -> Tuple[List[Tuple[str, str]], List[str]]:\n    while optstring != \"\":\n        opt, optstring = optstring[0], optstring[1:]\n        optarg = \"\"\n        if short_has_arg(opt, shortopts):\n            if optstring == \"\":\n                if not args:\n                    raise GetoptError(f\"option -{opt} requires argument\")\n                optstring, args = args[0], args[1:]\n            optarg, optstring = optstring, \"\"\n        opts.append((f\"-{opt}\", optarg))\n    return opts, args\n\ndef getopt(\n    args: List[str], shortopts: str, longopts: List[str] = []\n) -> Tuple[List[Tuple[str, str]], List[str]]:\n    opts = []\n    prog_args = []\n\n    # Allow options after non-option arguments?\n    all_options_first = False\n    if shortopts.startswith(\"+\"):\n        shortopts = shortopts[1:]\n        all_options_first = True\n    elif \"POSIXLY_CORRECT\" in os.environ:\n        all_options_first = True\n\n    while args:\n        if args[0] == \"--\":\n            prog_args += args[1:]\n            break\n\n        if args[0][:2] == \"--\":\n            opts, args = do_longs(opts, args[0][2:], longopts, args[1:])\n        elif args[0][:1] == \"-\" and args[0] != \"-\":\n            opts, args = do_shorts(opts, args[0][1:], shortopts, args[1:])\n        else:\n            if all_options_first:\n                prog_args += args\n                break\n            else:\n                prog_args.append(args[0])\n                args = args[1:]\n\n    return opts, prog_args\n"
  },
  {
    "path": "stdlib/gpu.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom internal.gpu import *\n\ncuda_init()\n"
  },
  {
    "path": "stdlib/gzip.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom internal.file import gzFile\n\ndef open(path: str, mode: str = \"r\") -> gzFile:\n    return gzFile(path, mode)\n"
  },
  {
    "path": "stdlib/heapq.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n# 'heap' is a heap at all indices >= startpos, except possibly for pos.  pos\n# is the index of a leaf with a possibly out-of-order value.  Restore the\n# heap invariant.\ndef _siftdown(heap: List[T], startpos: int, pos: int, T: type):\n    newitem = heap[pos]\n    # Follow the path to the root, moving parents down until finding a place\n    # newitem fits.\n    while pos > startpos:\n        parentpos = (pos - 1) >> 1\n        parent = heap[parentpos]\n        if newitem < parent:\n            heap[pos] = parent\n            pos = parentpos\n            continue\n        break\n    heap[pos] = newitem\n\ndef _siftup(heap: List[T], pos: int, T: type):\n    endpos = len(heap)\n    startpos = pos\n    newitem = heap[pos]\n    # Bubble up the smaller child until hitting a leaf.\n    childpos = 2 * pos + 1  # leftmost child position\n    while childpos < endpos:\n        # Set childpos to index of smaller child.\n        rightpos = childpos + 1\n        if rightpos < endpos and not heap[childpos] < heap[rightpos]:\n            childpos = rightpos\n        # Move the smaller child up.\n        heap[pos] = heap[childpos]\n        pos = childpos\n        childpos = 2 * pos + 1\n    # The leaf at pos is empty now.  Put newitem there, and bubble it up\n    # to its final resting place (by sifting its parents down).\n    heap[pos] = newitem\n    _siftdown(heap, startpos, pos)\n\ndef _siftdown_max(heap: List[T], startpos: int, pos: int, T: type):\n    \"Maxheap variant of _siftdown\"\n    newitem = heap[pos]\n    # Follow the path to the root, moving parents down until finding a place\n    # newitem fits.\n    while pos > startpos:\n        parentpos = (pos - 1) >> 1\n        parent = heap[parentpos]\n        if parent < newitem:\n            heap[pos] = parent\n            pos = parentpos\n            continue\n        break\n    heap[pos] = newitem\n\ndef _siftup_max(heap: List[T], pos: int, T: type):\n    \"Maxheap variant of _siftup\"\n    endpos = len(heap)\n    startpos = pos\n    newitem = heap[pos]\n    # Bubble up the larger child until hitting a leaf.\n    childpos = 2 * pos + 1  # leftmost child position\n    while childpos < endpos:\n        # Set childpos to index of larger child.\n        rightpos = childpos + 1\n        if rightpos < endpos and not heap[rightpos] < heap[childpos]:\n            childpos = rightpos\n        # Move the larger child up.\n        heap[pos] = heap[childpos]\n        pos = childpos\n        childpos = 2 * pos + 1\n    # The leaf at pos is empty now.  Put newitem there, and bubble it up\n    # to its final resting place (by sifting its parents down).\n    heap[pos] = newitem\n    _siftdown_max(heap, startpos, pos)\n\ndef heappush(heap: List[T], item: T, T: type):\n    heap.append(item)\n    _siftdown(heap, 0, len(heap) - 1)\n\ndef heappop(heap: List[T], T: type) -> T:\n    lastelt = heap.pop()  # raises appropriate IndexError if heap is empty\n    if heap:\n        returnitem = heap[0]\n        heap[0] = lastelt\n        _siftup(heap, 0)\n        return returnitem\n    return lastelt\n\ndef heapreplace(heap: List[T], item: T, T: type) -> T:\n    returnitem = heap[0]  # raises appropriate IndexError if heap is empty\n    heap[0] = item\n    _siftup(heap, 0)\n    return returnitem\n\ndef heappushpop(heap: List[T], item: T, T: type) -> T:\n    if heap and heap[0] < item:\n        item, heap[0] = heap[0], item\n        _siftup(heap, 0)\n    return item\n\ndef heapify(x: List[T], T: type):\n    n = len(x)\n    # Transform bottom-up.  The largest index there's any point to looking at\n    # is the largest with a child index in-range, so must have 2*i + 1 < n,\n    # or i < (n-1)/2.  If n is even = 2*j, this is (2*j-1)/2 = j-1/2 so\n    # j-1 is the largest, which is n//2 - 1.  If n is odd = 2*j+1, this is\n    # (2*j+1-1)/2 = j so j-1 is the largest, and that's again n//2-1.\n    for i in reversed(range(n // 2)):\n        _siftup(x, i)\n\ndef _heappop_max(heap: List[T], T: type) -> T:\n    lastelt = heap.pop()  # raises appropriate IndexError if heap is empty\n    if heap:\n        returnitem = heap[0]\n        heap[0] = lastelt\n        _siftup_max(heap, 0)\n        return returnitem\n    return lastelt\n\ndef _heapreplace_max(heap: List[T], item: T, T: type) -> T:\n    returnitem = heap[0]  # raises appropriate IndexError if heap is empty\n    heap[0] = item\n    _siftup_max(heap, 0)\n    return returnitem\n\ndef _heapify_max(x: List[T], T: type):\n    n = len(x)\n    for i in reversed(range(n // 2)):\n        _siftup_max(x, i)\n\ndef nsmallest(n: int, iterable: Generator[T], key=Optional[int](), T: type) -> List[T]:\n    if n == 1:\n        v = List(1)\n        for a in iterable:\n            if not v:\n                v.append(a)\n            else:\n                if not isinstance(key, Optional):\n                    if key(a) < key(v[0]):\n                        v[0] = a\n                elif a < v[0]:\n                    v[0] = a\n        return v\n\n    # When key is none, use simpler decoration\n    if isinstance(key, Optional):\n        it = iter(iterable)\n        # put the range(n) first so that zip() doesn't\n        # consume one too many elements from the iterator\n        result = List(n)\n        done = False\n        for i in range(n):\n            if it.done():\n                done = True\n                break\n            result.append((it.__next__(), i))\n        if not result:\n            it.destroy()\n            return []\n        _heapify_max(result)\n        top = result[0][0]\n        order = n\n        if not done:\n            for elem in it:\n                if elem < top:\n                    _heapreplace_max(result, (elem, order))\n                    top, _order = result[0]\n                    order += 1\n        else:\n            it.destroy()\n        result.sort()\n        return [elem for elem, order in result]\n    else:\n        # General case, slowest method\n        it = iter(iterable)\n        result = List(n)\n        done = False\n        for i in range(n):\n            if it.done():\n                done = True\n                break\n            elem = it.__next__()\n            result.append((key(elem), i, elem))\n        if not result:\n            it.destroy()\n            return []\n        _heapify_max(result)\n        top = result[0][0]\n        order = n\n        if not done:\n            for elem in it:\n                k = key(elem)\n                if k < top:\n                    _heapreplace_max(result, (k, order, elem))\n                    top, _order, _elem = result[0]\n                    order += 1\n        else:\n            it.destroy()\n        result.sort()\n        return [elem for k, order, elem in result]\n\ndef nlargest(n: int, iterable: Generator[T], key=Optional[int](), T: type) -> List[T]:\n    if n == 1:\n        v = List(1)\n        for a in iterable:\n            if not v:\n                v.append(a)\n            else:\n                if not isinstance(key, Optional):\n                    if key(a) > key(v[0]):\n                        v[0] = a\n                elif a > v[0]:\n                    v[0] = a\n        return v\n\n    # When key is none, use simpler decoration\n    if isinstance(key, Optional):\n        it = iter(iterable)\n        result = List(n)\n        done = False\n        for i in range(0, -n, -1):\n            if it.done():\n                done = True\n                break\n            result.append((it.__next__(), i))\n        if not result:\n            it.destroy()\n            return []\n        heapify(result)\n        top = result[0][0]\n        order = -n\n        if not done:\n            for elem in it:\n                if top < elem:\n                    heapreplace(result, (elem, order))\n                    top, _order = result[0]\n                    order -= 1\n        else:\n            it.destroy()\n        result.sort()\n        return [elem for elem, order in reversed(result)]\n    else:\n        # General case, slowest method\n        it = iter(iterable)\n        result = List(n)\n        done = False\n        for i in range(0, -n, -1):\n            if it.done():\n                done = True\n                break\n            elem = it.__next__()\n            result.append((key(elem), i, elem))\n        if not result:\n            return []\n        heapify(result)\n        top = result[0][0]\n        order = -n\n        if not done:\n            for elem in it:\n                k = key(elem)\n                if top < k:\n                    heapreplace(result, (k, order, elem))\n                    top, _order, _elem = result[0]\n                    order -= 1\n        else:\n            it.destroy()\n        result.sort()\n        return [elem for k, order, elem in reversed(result)]\n\n@tuple\nclass _MergeItem:\n    value: T\n    order: int\n    gen: Generator[T]\n    key: S\n    T: type\n    S: type\n\n    def __lt__(self, other):\n        if isinstance(self.key, Optional):\n            return (self.value, self.order) < (other.value, other.order)\n        else:\n            return (self.key(self.value), self.order, self.value) < (\n                other.key(other.value),\n                other.order,\n                other.value,\n            )\n\ndef merge(*iterables, key=Optional[int](), reverse: bool = False):\n    items = []\n\n    # TODO: unify types of different compatible functions\n    # TODO: lambdas with void?\n    def _heapify(x):\n        if reverse:\n            _heapify_max(x)\n        else:\n            heapify(x)\n\n    _heappop = lambda x: _heappop_max(x) if reverse else heappop(x)\n    _heapreplace = lambda x, s: _heapreplace_max(x, s) if reverse else heapreplace(x, s)\n    direction = -1 if reverse else 1\n\n    order = 0\n    for it in iterables:\n        gen = iter(it)\n        if not gen.done():\n            items.append(_MergeItem(gen.__next__(), order * direction, gen, key))\n        order += 1\n    _heapify(items)\n    while len(items) > 1:\n        while True:\n            # TODO: @tuple unpacking does not work\n            value, order, gen = items[0].value, items[0].order, items[0].gen\n            yield value\n            if gen.done():\n                _heappop(items)\n                break\n            _heapreplace(items, _MergeItem(gen.__next__(), order, gen, key))\n    if items:\n        # fast case when only a single iterator remains\n        value, order, gen = items[0].value, items[0].order, items[0].gen\n        yield value\n        yield from gen\n"
  },
  {
    "path": "stdlib/internal/__init__.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n# Core library\n# from internal.core import *  # done automatically by compiler\nfrom internal.attributes import *\nimport internal.static as static\nfrom internal.static import print as __print__\nfrom internal.types.ptr import *\nfrom internal.types.str import *\nfrom internal.types.int import *\nfrom internal.types.bool import *\nfrom internal.types.array import *\nfrom internal.types.error import *\nfrom internal.types.intn import *\nfrom internal.types.float import *\nfrom internal.types.byte import *\nfrom internal.types.type import *\nfrom internal.types.tuple import *\nfrom internal.types.rtti import *\nfrom internal.types.function import *\nfrom internal.types.union import *\nfrom internal.types.any import *\nfrom internal.types.ellipsis import *\nfrom internal.types.import_ import *\nfrom internal.types.ptr import *\nfrom internal.types.generator import *\nfrom internal.types.optional import *\nimport internal.c_stubs as _C\nfrom internal.format import *\nfrom internal.internal import *\nfrom internal.types.slice import *\nfrom internal.types.range import *\nfrom internal.types.complex import *\n\n__argv__ = Array[str](0)\n\nfrom internal.types.strbuf import strbuf as _strbuf\nfrom internal.types.collections.list import *\nfrom internal.types.collections.set import *\nfrom internal.types.collections.dict import *\nfrom internal.types.collections.tuple import *\n\nfrom internal.builtin import *\nfrom internal.builtin import _jit_display\nfrom internal.str import *\n\nfrom internal.sort import sorted\n\nfrom openmp import Ident as __OMPIdent, for_par, for_par as par\nfrom internal.file import File, gzFile, open, gzopen\nfrom internal.gpu import _gpu_loop_outline_template\nfrom pickle import pickle, unpickle\nfrom internal.dlopen import dlsym as _dlsym\nimport internal.python\nfrom internal.python import PyError\nfrom asyncio import Future as _Future\n\nif __py_numerics__:\n    import internal.pynumerics\nif __py_extension__:\n    internal.python.ensure_initialized()\n"
  },
  {
    "path": "stdlib/internal/__init_test__.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n# Core library\n\nfrom internal.attributes import *\nfrom internal.static import print as __print__\n\nfrom internal.types.ptr import *\nfrom internal.types.str import *\nfrom internal.types.int import *\nfrom internal.types.bool import *\nfrom internal.types.array import *\nfrom internal.types.error import *\nfrom internal.types.intn import *\nfrom internal.types.float import *\nfrom internal.types.byte import *\nfrom internal.types.type import *\nfrom internal.types.tuple import *\nfrom internal.types.rtti import *\nfrom internal.types.function import *\nfrom internal.types.union import *\nfrom internal.types.any import *\nfrom internal.types.ellipsis import *\nfrom internal.types.import_ import *\nfrom internal.types.ptr import *\nfrom internal.types.generator import *\nfrom internal.types.optional import *\nfrom internal.internal import *\nfrom internal.types.slice import *\nfrom internal.types.range import *\nfrom internal.types.complex import *\nfrom internal.types.strbuf import strbuf as _strbuf\nfrom internal.types.collections.list import *\nimport internal.c_stubs as _C\nfrom internal.format import *\n\ndef next(g: Generator[T], default: Optional[T] = None, T: type) -> T:\n    if g.done():\n        if default:\n            return unwrap(default)\n        else:\n            raise StopIteration()\n    return g.__next__()\n\nfrom C import seq_print_full(str, cobj)\n\nclass Set:\n    items: List[T]\n    T: type = NoneType\n\n    def __init__(self):\n        self.items = []\n\n    def __iter__(self) -> Generator[T]:\n        yield from self.items\n\n    def add(self, what: T):\n        if what not in self.items:\n            self.items.append(what)\n\n    def __repr__(self) -> str:\n        if len(self.items) == 0:\n            return \"set()\"\n        s = self.items.__repr__()\n        s.ptr[0] = \"{\".ptr[0]\n        s.ptr[s.len - 1] = \"}\".ptr[0]\n        return s\n\nclass Dict:\n    keys: List[K]\n    values: List[V]\n    K: type = NoneType\n    V: type = NoneType\n\n    def __init__(self):\n        self.keys = []\n        self.values = []\n\n    def __iter__(self) -> Generator[K]:\n        yield from self.keys\n\n    def items(self) -> Generator[Tuple[K, V]]:\n        for i in range(self.keys.len):\n            yield (self.keys[i], self.values[i])\n\n    def __contains__(self, key: K) -> bool:\n        return self.keys.find(key) != -1\n\n    def __getitem__(self, key: K) -> V:\n        i = self.keys.find(key)\n        return self.values[i]\n\n    def __setitem__(self, key: K, val: V):\n        i = self.keys.find(key)\n        if i != -1:\n            self.values[i] = val\n        else:\n            self.keys.append(key)\n            self.values.append(val)\n\n    def __len__(self) -> int:\n        return self.keys.len\n\n    def __repr__(self) -> str:\n        n = self.__len__()\n        if n == 0:\n            return \"{}\"\n        else:\n            lst = []\n            lst.append(\"{\")\n            first = True\n            for k, v in self.items():\n                if not first:\n                    lst.append(\", \")\n                else:\n                    first = False\n                lst.append(k.__repr__())\n                lst.append(\": \")\n                lst.append(v.__repr__())\n            lst.append(\"}\")\n            return str.cat(lst)\n\n@extend\nclass str:\n    def __getitem__(self, idx: int) -> str:\n        if idx < 0:\n            idx += self.len\n        if not (0 <= idx < self.len):\n            raise IndexError(\"string index out of range\")\n        return str(self.ptr + idx, 1)\n\n    def __getitem__(self, s: Slice) -> str:\n        if s.start is None and s.stop is None and s.step is None:\n            return self.__copy__()\n        elif s.step is None:\n            start, stop, step, length = s.adjust_indices(self.len)\n            return str(self.ptr + start, length)\n        else:\n            raise ValueError(\"nope\")\n\n    def strip(self):\n        if self.__len__() == 0:\n            return \"\"\n\n        i = 0\n        while i < self.__len__() and _C.isspace(i32(int(self.ptr[i]))):\n            i += 1\n\n        j = self.__len__() - 1\n        while j >= 0 and _C.isspace(i32(int(self.ptr[j]))):\n            j -= 1\n        j += 1\n\n        if j <= i:\n            return \"\"\n\n        return str(self.ptr + i, j - i)\n\n    def join(self, l: Generator[str]) -> str:\n        buf = _strbuf()\n        if len(self) == 0:\n            for a in l:\n                buf.append(a)\n        else:\n            first = True\n            for a in l:\n                if first:\n                    first = False\n                else:\n                    buf.append(self)\n                buf.append(a)\n        return buf.__str__()\n\n    def __repr__(self) -> str:\n        return f\"'{self}'\"\n\n    def _isdigit(a: byte) -> bool:\n        return _C.isdigit(i32(int(a))) != i32(0)\n\nset = Set\ndict = Dict\n\nfrom internal.builtin import *\n\n# from openmp import Ident as __OMPIdent, for_par\nfrom internal.dlopen import dlsym as _dlsym\n"
  },
  {
    "path": "stdlib/internal/attributes.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n@__attribute__\ndef test():\n    pass\n\n@__attribute__\ndef export():\n    pass\n\n@__attribute__\ndef inline():\n    pass\n\n@__attribute__\ndef noinline():\n    pass\n\n@__attribute__\ndef nonpure():\n    pass\n\n@__attribute__\ndef no_side_effect():\n    pass\n\n@__attribute__\ndef nocapture():\n    pass\n\n@__attribute__\ndef pycapture():\n    pass\n\n@__attribute__\ndef self_captures():\n    pass\n\n@__attribute__\ndef commutative():\n    pass\n\n@__attribute__\ndef associative():\n    pass\n\n@__attribute__\ndef distributive():\n    pass\n\n@__attribute__\ndef realize_without_self():\n    pass\n\n@__attribute__\ndef virtual():\n    pass\n\n@__attribute__\ndef no_argument_wrap():\n    pass\n\n@__attribute__\ndef no_arg_reorder():\n    pass\n"
  },
  {
    "path": "stdlib/internal/builtin.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\nclass object:\n    def __init__(self):\n        pass\n\n    def __repr__(self) -> str:\n        return f\"<{self.__class__.__name__} object at {self.__raw__()}>\"\n\ndef id(x) -> int:\n    if isinstance(x, ByRef):\n        return int(x.__raw__())\n    else:\n        return 0\n\ndef print(*args, sep: str = \" \", end: str = \"\\n\", file=_C.seq_stdout(), flush: bool = False):\n    fp = cobj()\n    if isinstance(file, cobj):\n        fp = file\n    else:\n        fp = file.fp\n    i = 0\n    for a in args:\n        if i and sep:\n            _C.seq_print_full(sep, fp)\n        _C.seq_print_full(str(a), fp)\n        i += 1\n    _C.seq_print_full(end, fp)\n    if flush:\n        _C.fflush(fp)\n\ndef input(prompt: str = \"\"):\n    stdout = _C.seq_stdout()\n    stderr = _C.seq_stderr()\n    stdin = _C.seq_stdin()\n    _C.fflush(stderr)\n    _C.fflush(stdout)\n    print(prompt, end=\"\")\n    buf = cobj()\n    n = 0\n    s = _C.getline(__ptr__(buf), __ptr__(n), stdin)\n    if s > 0:\n        if buf[s - 1] == byte(10):\n            s -= 1  # skip trailing '\\n'\n        if s != 0 and buf[s - 1] == byte(13):\n            s -= 1  # skip trailing '\\r'\n        ans = str(buf, s).__ptrcopy__()\n        _C.free(buf)\n        return ans\n    else:\n        _C.free(buf)\n        raise EOFError(\"EOF when reading a line\")\n\n@extend\nclass __internal__:\n    def print(*args):\n        print(*args, flush=True, file=_C.seq_stdout())\n\ndef min(*args, key=None, default=None):\n    if static.len(args) == 0:\n        compile_error(\"min() expected at least 1 argument, got 0\")\n    elif static.len(args) > 1 and default is not None:\n        compile_error(\"min() 'default' argument only allowed for iterables\")\n    elif static.len(args) == 1:\n        x = args[0].__iter__()\n        if not x.done():\n            s = x.__next__()\n            while not x.done():\n                i = x.__next__()\n                if key is None:\n                    if i < s:\n                        s = i\n                else:\n                    if key(i) < key(s):\n                        s = i\n            x.destroy()\n            return s\n        else:\n            x.destroy()\n        if default is None:\n            raise ValueError(\"min() arg is an empty sequence\")\n        else:\n            return default\n    elif static.len(args) == 2:\n        a, b = args\n        if key is None:\n            return a if a <= b else b\n        else:\n            return a if key(a) <= key(b) else b\n    else:\n        m = args[0]\n        for i in args[1:]:\n            if key is None:\n                if i < m:\n                    m = i\n            else:\n                if key(i) < key(m):\n                    m = i\n        return m\n\ndef max(*args, key=None, default=None):\n    if static.len(args) == 0:\n        compile_error(\"max() expected at least 1 argument, got 0\")\n    elif static.len(args) > 1 and default is not None:\n        compile_error(\"max() 'default' argument only allowed for iterables\")\n    elif static.len(args) == 1:\n        x = args[0].__iter__()\n        if not x.done():\n            s = x.__next__()\n            while not x.done():\n                i = x.__next__()\n                if key is None:\n                    if i > s:\n                        s = i\n                else:\n                    if key(i) > key(s):\n                        s = i\n            x.destroy()\n            return s\n        else:\n            x.destroy()\n        if default is None:\n            raise ValueError(\"max() arg is an empty sequence\")\n        else:\n            return default\n    elif static.len(args) == 2:\n        a, b = args\n        if key is None:\n            return a if a >= b else b\n        else:\n            return a if key(a) >= key(b) else b\n    else:\n        m = args[0]\n        for i in args[1:]:\n            if key is None:\n                if i > m:\n                    m = i\n            else:\n                if key(i) > key(m):\n                    m = i\n        return m\n\ndef len(x) -> int:\n    return x.__len__()\n\ndef iter(x):\n    return x.__iter__()\n\ndef abs(x):\n    return x.__abs__()\n\ndef hash(x) -> int:\n    return x.__hash__()\n\ndef ord(s: str) -> int:\n    if len(s) != 1:\n        raise TypeError(\n            f\"ord() expected a character, but string of length {len(s)} found\"\n        )\n    return int(s.ptr[0])\n\ndef divmod(a, b):\n    if hasattr(a, \"__divmod__\"):\n        return a.__divmod__(b)\n    else:\n        return (a // b, a % b)\n\ndef chr(i: int) -> str:\n    p = cobj(1)\n    p[0] = byte(i)\n    return str(p, 1)\n\ndef next(g: Generator[T], default: Optional[T] = None, T: type) -> T:\n    if g.done():\n        if default is not None:\n            return default.__val__()\n        else:\n            raise StopIteration()\n    return g.__next__()\n\ndef any(x: Generator[T], T: type) -> bool:\n    for a in x:\n        if a:\n            return True\n    return False\n\ndef all(x: Generator[T], T: type) -> bool:\n    for a in x:\n        if not a:\n            return False\n    return True\n\ndef zip(*args):\n    if static.len(args) == 0:\n        yield from List[int]()\n    else:\n        iters = tuple(iter(i) for i in args)\n        done = False\n        while not done:\n            for i in iters:\n                if i.done():\n                    done = True\n            if not done:\n                yield tuple(i.__next__() for i in iters)\n        for i in iters:\n            i.destroy()\n\ndef filter(f: CallableTrait[[T], bool], x: Generator[T], T: type) -> Generator[T]:\n    for a in x:\n        if f(a):\n            yield a\n\ndef map(f, *args):\n    if static.len(args) == 0:\n        compile_error(\"map() expects at least one iterator\")\n    elif static.len(args) == 1:\n        for a in args[0]:\n            yield f(a)\n    else:\n        for a in zip(*args):\n            yield f(*a)\n\ndef enumerate(x, start: int = 0):\n    i = start\n    for a in x:\n        yield (i, a)\n        i += 1\n\ndef echo(x):\n    print x\n    return x\n\ndef reversed(x):\n    if hasattr(x, \"__reversed__\"):\n        return x.__reversed__()\n\n\n    def _rev(x):\n        i = x.__len__() - 1\n        while i >= 0:\n            yield x[i]\n            i -= 1\n\n    return _rev(x)\n\ndef round(x, n=0):\n    nx = float.__pow__(10.0, n)\n    return float.__round__(x * nx) / nx\n\ndef sum(x, start=0):\n    if ((isinstance(x, List[int]) and isinstance(start, int)) or\n        (isinstance(x, List[int]) and isinstance(start, float)) or\n        (isinstance(x, List[float]) and isinstance(start, float))):\n        p = x.arr.ptr\n        s = start\n        for i in range(len(x)):\n            s += p[i]\n        return s\n    elif isinstance(x, List[float]) and isinstance(start, int):\n        p = x.arr.ptr\n        s = float(start)\n        for i in range(len(x)):\n            s += p[i]\n        return s\n    elif isinstance(x, List[bool]) and isinstance(start, int):\n        p = x.arr.ptr\n        s = start\n        for i in range(len(x)):\n            s += 1 if p[i] else 0\n        return s\n    if ((isinstance(x, Generator[int]) and isinstance(start, int)) or\n        (isinstance(x, Generator[int]) and isinstance(start, float)) or\n        (isinstance(x, Generator[float]) and isinstance(start, float))):\n        s = start\n        for a in x:\n            s += a\n        return s\n    elif isinstance(x, Generator[float]) and isinstance(start, int):\n        s = float(start)\n        for a in x:\n            s += a\n        return s\n    elif isinstance(x, Generator[bool]) and isinstance(start, int):\n        s = start\n        for a in x:\n            s += 1 if a else 0\n        return s\n    else:\n        itr = x.__iter__()\n        S = type(start + itr.__promise__()[0])\n        if isinstance(start, S):\n            s = start\n        else:\n            s = S(start)\n\n        for a in x:\n            s = s + a\n\n        return s\n\ndef repr(x):\n    if not hasattr(x, \"__repr__\") and hasattr(x, \"__repr_default__\"):\n        return x.__repr_default__()\n    return x.__repr__()\n\ndef _int_format(a: int, base: int, prefix: str = \"\"):\n    assert base == 2 or base == 8 or base == 10 or base == 16\n    chars = \"0123456789abcdef-\"\n\n    b = a\n    digits = 0\n    while b != 0:\n        digits += 1\n        b //= base\n\n    sz = digits + (1 if a <= 0 else 0) + len(prefix)\n    p = Ptr[byte](sz)\n    q = p\n\n    if a < 0:\n        q[0] = chars[-1].ptr[0]\n        q += 1\n\n    if prefix:\n        str.memcpy(q, prefix.ptr, len(prefix))\n        q += len(prefix)\n\n    if digits != 0:\n        b = a\n        q += digits - 1\n        i = 1\n        while b != 0:\n            i += 1\n            q[0] = chars.ptr[abs(b % base)]\n            q += -1\n            b //= base\n    else:\n        q[0] = chars.ptr[0]\n\n    return str(p, sz)\n\ndef bin(n):\n    return _int_format(n.__index__(), 2, \"0b\")\n\ndef oct(n):\n    return _int_format(n.__index__(), 8, \"0o\")\n\ndef hex(n):\n    return _int_format(n.__index__(), 16, \"0x\")\n\ndef pow(base: float, exp: float):\n    return base ** exp\n\n@overload\ndef pow(base: int, exp: int, mod: Optional[int] = None):\n    if exp < 0:\n        raise ValueError(\"pow() negative int exponent not supported\")\n\n    if mod is not None:\n        if mod == 0:\n            raise ValueError(\"pow() 3rd argument cannot be 0\")\n        base %= mod\n\n    result = 1\n    while exp > 0:\n        if exp & 1:\n            x = result * base\n            result = x % mod if mod is not None else x\n        y = base * base\n        base = y % mod if mod is not None else y\n        exp >>= 1\n    return result % mod if mod is not None else result\n\n@extend\nclass int:\n    def _from_str(s: str, base: int):\n        def parse_error(s: str, base: int):\n            raise ValueError(\n                f\"invalid literal for int() with base {base}: {s.__repr__()}\"\n            )\n\n        if base < 0 or base > 36 or base == 1:\n            raise ValueError(\"int() base must be >= 2 and <= 36, or 0\")\n\n        s0 = s\n        base0 = base\n        s = s.strip()\n        n = len(s)\n        negate = False\n\n        if base == 0:\n            # skip leading sign\n            o = 0\n            if n >= 1 and (s.ptr[0] == byte(43) or s.ptr[0] == byte(45)):\n                o = 1\n\n            # detect base from prefix\n            if n >= o + 1 and s.ptr[o] == byte(48):  # '0'\n                if n < o + 2:\n                    parse_error(s0, base)\n\n                if s.ptr[o + 1] == byte(98) or s.ptr[o + 1] == byte(66):  # 'b'/'B'\n                    base = 2\n                elif s.ptr[o + 1] == byte(111) or s.ptr[o + 1] == byte(79):  # 'o'/'O'\n                    base = 8\n                elif s.ptr[o + 1] == byte(120) or s.ptr[o + 1] == byte(88):  # 'x'/'X'\n                    base = 16\n                else:\n                    parse_error(s0, base)\n            else:\n                base = 10\n\n        if base == 2 or base == 8 or base == 16:\n            if base == 2:\n                C_LOWER = byte(98)  # 'b'\n                C_UPPER = byte(66)  # 'B'\n            elif base == 8:\n                C_LOWER = byte(111)  # 'o'\n                C_UPPER = byte(79)   # 'O'\n            else:\n                C_LOWER = byte(120)  # 'x'\n                C_UPPER = byte(88)   # 'X'\n\n            def check_digit(d: byte, base: int):\n                if base == 2:\n                    return d == byte(48) or d == byte(49)\n                elif base == 8:\n                    return byte(48) <= d <= byte(55)\n                elif base == 16:\n                    return ((byte(48) <= d <= byte(57)) or\n                            (byte(97) <= d <= byte(102)) or\n                            (byte(65) <= d <= byte(70)))\n                return False\n\n            if (n >= 4 and\n                (s.ptr[0] == byte(43) or s.ptr[0] == byte(45)) and\n                s.ptr[1] == byte(48) and\n                (s.ptr[2] == C_LOWER or s.ptr[2] == C_UPPER)):  # '+0b' etc.\n                if not check_digit(s.ptr[3], base):\n                    parse_error(s0, base0)\n                negate = (s.ptr[0] == byte(45))\n                s = str(s.ptr + 3, n - 3)\n            elif (n >= 3 and\n                  s.ptr[0] == byte(48) and\n                  (s.ptr[1] == C_LOWER or s.ptr[1] == C_UPPER)):  # '0b' etc.\n                if not check_digit(s.ptr[3], base):\n                    parse_error(s0, base0)\n                s = str(s.ptr + 2, n - 2)\n\n        end = cobj()\n        result = _C.seq_int_from_str(s, __ptr__(end), i32(base))\n        n = len(s)\n\n        if n == 0 or end != s.ptr + n:\n            parse_error(s0, base0)\n\n        if negate:\n            result = -result\n\n        return result\n\n@extend\nclass float:\n    def _from_str(s: str) -> float:\n        s0 = s\n        s = s.rstrip()\n        n = len(s)\n        end = cobj()\n        result = _C.seq_float_from_str(s, __ptr__(end))\n\n        if n == 0 or end != s.ptr + n:\n            raise ValueError(f\"could not convert string to float: {s0.__repr__()}\")\n\n        return result\n\n@extend\nclass complex:\n    def _from_str(v: str) -> complex:\n        def parse_error():\n            raise ValueError(\"complex() arg is a malformed string\")\n\n        n = len(v)\n        s = v.ptr\n        x = 0.0\n        y = 0.0\n        z = 0.0\n        got_bracket = False\n        end = cobj()\n        i = 0\n\n        while i < n and str._isspace(s[i]):\n            i += 1\n\n        if i < n and s[i] == byte(40):  # '('\n            got_bracket = True\n            i += 1\n            while i < n and str._isspace(s[i]):\n                i += 1\n\n        z = _C.seq_float_from_str(str(s + i, n - i), __ptr__(end))\n\n        if end != s + i:\n            i = end - s\n\n            if i < n and (s[i] == byte(43) or s[i] == byte(45)):  # '+' '-'\n                x = z\n                y = _C.seq_float_from_str(str(s + i, n - i), __ptr__(end))\n\n                if end != s + i:\n                    i = end - s\n                else:\n                    y = 1.0 if s[i] == byte(43) else -1.0\n                    i += 1\n\n                if not (i < n and (s[i] == byte(106) or s[i] == byte(74))):  # 'j' 'J'\n                    parse_error()\n\n                i += 1\n            elif i < n and (s[i] == byte(106) or s[i] == byte(74)):  # 'j' 'J'\n                i += 1\n                y = z\n            else:\n                x = z\n        else:\n            if i < n and (s[i] == byte(43) or s[i] == byte(45)):  # '+' '-'\n                y = 1.0 if s[i] == byte(43) else -1.0\n                i += 1\n            else:\n                y = 1.0\n\n            if not (i < n and (s[i] == byte(106) or s[i] == byte(74))):  # 'j' 'J'\n                parse_error()\n\n            i += 1\n\n        while i < n and str._isspace(s[i]):\n            i += 1\n\n        if got_bracket:\n            if i < n and s[i] != byte(41):  # ')'\n                parse_error()\n            i += 1\n            while i < n and str._isspace(s[i]):\n                i += 1\n\n        if i != n:\n            parse_error()\n\n        return complex(x, y)\n\n@extend\nclass float32:\n    def _from_str(s: str) -> float32:\n        return float32(float._from_str(s))\n\n@extend\nclass float16:\n    def _from_str(s: str) -> float16:\n        return float16(float._from_str(s))\n\n@extend\nclass bfloat16:\n    def _from_str(s: str) -> bfloat16:\n        return bfloat16(float._from_str(s))\n\n@extend\nclass complex64:\n    def _from_str(s: str) -> complex64:\n        return complex64(complex._from_str(s))\n\ndef _jit_display(x, s: Literal[str], bundle: Optional[Set[str]] = None):\n    if isinstance(x, None):\n        return\n    if hasattr(x, \"_repr_mimebundle_\") and s == \"jupyter\":\n        d = x._repr_mimebundle_(bundle)\n        # TODO: pick appropriate mime\n        mime = next(d.keys()) # just pick first\n        print(f\"\\x00\\x00__codon/mime__\\x00{mime}\\x00{d[mime]}\", end='')\n    elif hasattr(x, \"__repr__\"):\n        print(x.__repr__(), end='')\n    elif hasattr(x, \"__str__\"):\n        print(x.__str__(), end='')\n"
  },
  {
    "path": "stdlib/internal/c_stubs.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n# runtime functions\nfrom C import seq_print(str)\nfrom C import seq_print_full(str, cobj)\n\n@nocapture\n@C\ndef seq_str_int(a: int, fmt: str, error: Ptr[bool]) -> str:\n    pass\n\n@nocapture\n@C\ndef seq_str_uint(a: int, fmt: str, error: Ptr[bool]) -> str:\n    pass\n\n@nocapture\n@C\ndef seq_str_float(a: float, fmt: str, error: Ptr[bool]) -> str:\n    pass\n\n@nocapture\n@C\ndef seq_str_str(a: str, fmt: str, error: Ptr[bool]) -> str:\n    pass\n\n@nocapture\n@C\ndef seq_str_ptr(a: cobj, fmt: str, error: Ptr[bool]) -> str:\n    pass\n\n@nocapture\n@C\ndef seq_int_from_str(a: str, b: Ptr[cobj], c: i32) -> int:\n    pass\n\n@nocapture\n@C\ndef seq_float_from_str(a: str, b: Ptr[cobj]) -> float:\n    pass\n\n@pure\n@C\ndef seq_strdup(a: cobj) -> str:\n    pass\n\n@pure\n@C\ndef seq_stdin() -> cobj:\n    pass\n\n@pure\n@C\ndef seq_stdout() -> cobj:\n    pass\n\n@pure\n@C\ndef seq_stderr() -> cobj:\n    pass\n\n@no_side_effect\n@C\ndef seq_env() -> Ptr[cobj]:\n    pass\n\n@no_side_effect\n@C\ndef seq_time() -> int:\n    pass\n\n@no_side_effect\n@C\ndef seq_time_monotonic() -> int:\n    pass\n\n@no_side_effect\n@C\ndef seq_time_highres() -> int:\n    pass\n\n@no_side_effect\n@C\ndef seq_localtime(a: int, b: cobj) -> bool:\n    pass\n\n@no_side_effect\n@C\ndef seq_gmtime(a: int, b: cobj) -> bool:\n    pass\n\n@pure\n@C\ndef seq_mktime(a: cobj) -> int:\n    pass\n\nfrom C import seq_sleep(float)\n\n@pure\n@C\ndef seq_pid() -> int:\n    pass\n\n@pure\n@C\ndef seq_lock_new() -> cobj:\n    pass\n\n@nocapture\n@C\ndef seq_lock_acquire(a: cobj, b: bool, c: float) -> bool:\n    pass\n\n@nocapture\n@C\ndef seq_lock_release(a: cobj) -> None:\n    pass\n\n@pure\n@C\ndef seq_rlock_new() -> cobj:\n    pass\n\n@nocapture\n@C\ndef seq_rlock_acquire(a: cobj, b: bool, c: float) -> bool:\n    pass\n\n@nocapture\n@C\ndef seq_rlock_release(a: cobj) -> None:\n    pass\n\n@pure\n@C\ndef seq_i32_to_float(a: i32) -> float:\n    pass\n\n# <ctype.h>\n@pure\n@C\ndef isdigit(a: i32) -> i32:\n    pass\n\n@pure\n@C\ndef isspace(a: i32) -> i32:\n    pass\n\n@pure\n@C\ndef isupper(a: i32) -> i32:\n    pass\n\n@pure\n@C\ndef islower(a: i32) -> i32:\n    pass\n\n@pure\n@C\ndef toupper(a: i32) -> i32:\n    pass\n\n@pure\n@C\ndef tolower(a: i32) -> i32:\n    pass\n\n@pure\n@C\ndef isalnum(a: i32) -> i32:\n    pass\n\n@pure\n@C\ndef isalpha(a: i32) -> i32:\n    pass\n\n# <math.h>\n@pure\n@C\ndef ceil(a: float) -> float:\n    pass\n\n@pure\n@C\ndef floor(a: float) -> float:\n    pass\n\n@pure\n@C\ndef fabs(a: float) -> float:\n    pass\n\n@pure\n@C\ndef fmod(a: float, b: float) -> float:\n    pass\n\n@pure\n@C\ndef exp(a: float) -> float:\n    pass\n\n@pure\n@C\ndef expm1(a: float) -> float:\n    pass\n\n@pure\n@C\ndef ldexp(a: float, b: i32) -> float:\n    pass\n\n@pure\n@C\ndef log(a: float) -> float:\n    pass\n\n@pure\n@C\ndef log10(a: float) -> float:\n    pass\n\n@pure\n@C\ndef sqrt(a: float) -> float:\n    pass\n\n@pure\n@C\ndef cbrt(a: float) -> float:\n    pass\n\n@pure\n@C\ndef pow(a: float, b: float) -> float:\n    pass\n\n@pure\n@C\ndef round(a: float) -> float:\n    pass\n\n@pure\n@C\ndef nextafter(a: float, b: float) -> float:\n    pass\n\n@pure\n@C\ndef acos(a: float) -> float:\n    pass\n\n@pure\n@C\ndef asin(a: float) -> float:\n    pass\n\n@pure\n@C\ndef atan(a: float) -> float:\n    pass\n\n@pure\n@C\ndef atan2(a: float, b: float) -> float:\n    pass\n\n@pure\n@C\ndef cos(a: float) -> float:\n    pass\n\n@pure\n@C\ndef sin(a: float) -> float:\n    pass\n\n@pure\n@C\ndef tan(a: float) -> float:\n    pass\n\n@pure\n@C\ndef cosh(a: float) -> float:\n    pass\n\n@pure\n@C\ndef sinh(a: float) -> float:\n    pass\n\n@pure\n@C\ndef tanh(a: float) -> float:\n    pass\n\n@pure\n@C\ndef acosh(a: float) -> float:\n    pass\n\n@pure\n@C\ndef asinh(a: float) -> float:\n    pass\n\n@pure\n@C\ndef atanh(a: float) -> float:\n    pass\n\n@pure\n@C\ndef copysign(a: float, b: float) -> float:\n    pass\n\n@pure\n@C\ndef log1p(a: float) -> float:\n    pass\n\n@pure\n@C\ndef trunc(a: float) -> float:\n    pass\n\n@pure\n@C\ndef log2(a: float) -> float:\n    pass\n\n@pure\n@C\ndef erf(a: float) -> float:\n    pass\n\n@pure\n@C\ndef erfc(a: float) -> float:\n    pass\n\n@pure\n@C\ndef tgamma(a: float) -> float:\n    pass\n\n@pure\n@C\ndef lgamma(a: float) -> float:\n    pass\n\n@pure\n@C\ndef remainder(a: float, b: float) -> float:\n    pass\n\n@pure\n@C\ndef hypot(a: float, b: float) -> float:\n    pass\n\n@nocapture\n@C\ndef frexp(a: float, b: Ptr[Int[32]]) -> float:\n    pass\n\n@nocapture\n@C\ndef modf(a: float, b: Ptr[float]) -> float:\n    pass\n\n@pure\n@C\ndef ceilf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef floorf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef fabsf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef fmodf(a: float32, b: float32) -> float32:\n    pass\n\n@pure\n@C\ndef expf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef expm1f(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef ldexpf(a: float32, b: i32) -> float32:\n    pass\n\n@pure\n@C\ndef logf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef log2f(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef log10f(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef sqrtf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef cbrtf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef powf(a: float32, b: float32) -> float32:\n    pass\n\n@pure\n@C\ndef roundf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef nextafterf(a: float32, b: float32) -> float32:\n    pass\n\n@pure\n@C\ndef acosf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef asinf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef atanf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef atan2f(a: float32, b: float32) -> float32:\n    pass\n\n@pure\n@C\ndef cosf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef sinf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef tanf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef coshf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef sinhf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef tanhf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef acoshf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef asinhf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef atanhf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef copysignf(a: float32, b: float32) -> float32:\n    pass\n\n@pure\n@C\ndef log1pf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef truncf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef erff(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef erfcf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef tgammaf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef lgammaf(a: float32) -> float32:\n    pass\n\n@pure\n@C\ndef remainderf(a: float32, b: float32) -> float32:\n    pass\n\n@pure\n@C\ndef hypotf(a: float32, b: float32) -> float32:\n    pass\n\n@nocapture\n@C\ndef frexpf(a: float32, b: Ptr[Int[32]]) -> float32:\n    pass\n\n@nocapture\n@C\ndef modff(a: float32, b: Ptr[float32]) -> float32:\n    pass\n\n# <stdio.h>\n@pure\n@C\ndef ferror(a: cobj) -> i32:\n    pass\n\n@nocapture\n@C\ndef fgetc(a: cobj) -> i32:\n    pass\n\n@nocapture\n@C\ndef fopen(a: cobj, b: cobj) -> cobj:\n    pass\n\n@nocapture\n@C\ndef fdopen(a: int, b: cobj) -> cobj:\n    pass\n\n@nocapture\n@C\ndef fclose(a: cobj) -> int:\n    pass\n\n@nocapture\n@C\ndef fread(a: cobj, b: int, c: int, d: cobj) -> int:\n    pass\n\n@nocapture\n@C\ndef fwrite(a: cobj, b: int, c: int, d: cobj) -> int:\n    pass\n\n@nocapture\n@C\ndef ftell(a: cobj) -> int:\n    pass\n\n@nocapture\n@C\ndef fseek(a: cobj, b: int, c: i32) -> i32:\n    pass\n\n@nocapture\n@C\ndef fgets(a: cobj, b: int, c: cobj) -> cobj:\n    pass\n\n@nocapture\n@C\ndef fflush(a: cobj) -> None:\n    pass\n\n@nocapture\n@C\ndef getline(a: Ptr[cobj], b: Ptr[int], c: cobj) -> int:\n    pass\n\n# <stdlib.h>\nfrom C import exit(int)\n\n@nocapture\n@C\ndef system(a: cobj) -> int:\n    pass\n\n@nocapture\n@C\ndef free(a: cobj) -> None:\n    pass\n\n@pure\n@C\ndef atoi(a: cobj) -> int:\n    pass\n\n# <zlib.h>\n@nocapture\n@C\ndef gzopen(a: cobj, b: cobj) -> cobj:\n    pass\n\n@nocapture\n@C\ndef gzerror(a: cobj, b: Ptr[i32]) -> cobj:\n    pass\n\n@nocapture\n@C\ndef gzgetc(a: cobj) -> i32:\n    pass\n\n@nocapture\n@C\ndef gzgets(a: cobj, b: cobj, c: i32) -> cobj:\n    pass\n\n@nocapture\n@C\ndef gzclose(a: cobj) -> int:\n    pass\n\n@nocapture\n@C\ndef gzread(a: cobj, b: cobj, c: u32) -> i32:\n    pass\n\n@nocapture\n@C\ndef gzwrite(a: cobj, b: cobj, c: u32) -> i32:\n    pass\n\n@nocapture\n@C\ndef gztell(a: cobj) -> int:\n    pass\n\n@nocapture\n@C\ndef gzseek(a: cobj, b: int, c: i32) -> int:\n    pass\n\n@nocapture\n@C\ndef gzflush(a: cobj, b: i32) -> i32:\n    pass\n\n# <bzlib.h>\n@nocapture\n@C\ndef BZ2_bzopen(a: cobj, b: cobj) -> cobj:\n    pass\n\n@nocapture\n@C\ndef BZ2_bzread(a: cobj, b: cobj, c: i32) -> i32:\n    pass\n\n@nocapture\n@C\ndef BZ2_bzwrite(a: cobj, b: cobj, c: i32) -> i32:\n    pass\n\n@nocapture\n@C\ndef BZ2_bzflush(a: cobj) -> i32:\n    pass\n\n@nocapture\n@C\ndef BZ2_bzclose(a: cobj) -> None:\n    pass\n\n@nocapture\n@C\ndef BZ2_bzerror(a: cobj, b: Ptr[i32]) -> cobj:\n    pass\n\n# <string.h>\n@pure\n@C\ndef memcmp(lhs: Ptr[byte], rhs: Ptr[byte], count: int) -> i32:\n    pass\n\n@pure\n@C\ndef memchr(p: Ptr[byte], ch: i32, count: int) -> Ptr[byte]:\n    pass\n"
  },
  {
    "path": "stdlib/internal/core.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n@tuple\n@__internal__\n@__noextend__\nclass type[T]:\n    #  __new__ / __init__: always done internally\n    pass\n\n@tuple\n@__internal__\n@__noextend__\nclass unrealized_type[T]:\n    pass\n\n@tuple\n@__internal__\nclass TypeWrap[T]:\n    pass\n\n@__internal__\nclass __internal__:\n    pass\n\n@__internal__\nclass __magic__:\n    pass\n\n@tuple\n@__internal__\n@__notuple__\nclass bool:\n    pass\n\n@tuple\n@__internal__\n@__notuple__\nclass byte:\n    pass\n\n@tuple\n@__internal__\n@__notuple__\nclass int:\n    MAX = 9223372036854775807\n    pass\n\n@tuple\n@__internal__\n@__notuple__\nclass float:\n    MIN_10_EXP = -307\n    pass\n\n@tuple\n@__internal__\n@__notuple__\nclass float32:\n    MIN_10_EXP = -37\n    pass\n\n@tuple\n@__internal__\n@__notuple__\nclass float16:\n    MIN_10_EXP = -4\n    pass\n\n@tuple\n@__internal__\n@__notuple__\nclass bfloat16:\n    MIN_10_EXP = -37\n    pass\n\n@tuple\n@__internal__\n@__notuple__\nclass float128:\n    MIN_10_EXP = -4931\n    pass\n\n@tuple\n@__internal__\n@__notuple__\nclass Function[T, TR]:\n    pass\n\n@tuple\n@__internal__\nclass CallableTrait[T, TR]:\n    pass\n\n@tuple\n@__internal__\nclass NoneType:\n    pass\n\n@tuple\n@__internal__\n@__notuple__\nclass Ptr[T]:\n    pass\ncobj = Ptr[byte]\n\n@tuple\n@__internal__\n@__notuple__\nclass Capsule[T]:\n    val: Ptr[T]\n\n@tuple\n@__internal__\n@__notuple__\nclass Generator[T]:\n    pass\n\n@tuple\n@__internal__\n@__notuple__\nclass Optional:\n    T: type = NoneType\n\n@tuple\n@__internal__\n@__notuple__\nclass Int[N: Literal[int]]:\n    pass\n\n@tuple\n@__internal__\n@__notuple__\nclass UInt[N: Literal[int]]:\n    pass\n\n@__internal__\nclass pyobj:\n    p: Ptr[byte]\n\n@tuple\n@__internal__\nclass str:\n    ptr: Ptr[byte]\n    len: int\n\n@tuple\n@__internal__\n@__noextend__\nclass Tuple:\n    @llvm\n    def __new__() -> Tuple:\n        ret {} {}\n    def __add__(self: __SELF__, obj, __SELF__: type):\n        return __magic__.add(self, obj)\n    def __mul__(self: __SELF__, n: Literal[int], __SELF__: type):\n        return __magic__.mul(self, n)\n    def __contains__(self: __SELF__, obj, __SELF__: type) -> bool:\n        return __magic__.contains(self, obj)\n    def __getitem__(self: __SELF__, idx: int, __SELF__: type):\n        return __magic__.getitem(self, idx)\n    def __iter__(self: __SELF__, __SELF__: type):\n        yield from __magic__.iter(self)\n    def __hash__(self: __SELF__, __SELF__: type) -> int:\n        return __magic__.hash(self)\n    def __repr__(self: __SELF__, __SELF__: type) -> str:\n        return __magic__.repr(self)\n    def __len__(self: __SELF__, __SELF__: type) -> int:\n        return __magic__.len(self)\n    def __eq__(self: __SELF__, obj: __SELF__, __SELF__: type) -> bool:\n        return __magic__.eq(self, obj)\n    def __ne__(self: __SELF__, obj: __SELF__, __SELF__: type) -> bool:\n        return __magic__.ne(self, obj)\n    def __gt__(self: __SELF__, obj: __SELF__, __SELF__: type) -> bool:\n        return __magic__.gt(self, obj)\n    def __ge__(self: __SELF__, obj: __SELF__, __SELF__: type) -> bool:\n        return __magic__.ge(self, obj)\n    def __lt__(self: __SELF__, obj: __SELF__, __SELF__: type) -> bool:\n        return __magic__.lt(self, obj)\n    def __le__(self: __SELF__, obj: __SELF__, __SELF__: type) -> bool:\n        return __magic__.le(self, obj)\n    def __pickle__(self: __SELF__, dest: Ptr[byte], __SELF__: type):\n        return __magic__.pickle(self, dest)\n    def __unpickle__(src: Ptr[byte], __SELF__: type) -> __SELF__:\n        return __magic__.unpickle(src)\n    def __to_py__(self: __SELF__, __SELF__: type) -> Ptr[byte]:\n        return __magic__.to_py(self)\n    def __from_py__(src: Ptr[byte], __SELF__: type) -> __SELF__:\n        return __magic__.from_py(src)\n    def __to_gpu__(self: __SELF__, cache, __SELF__: type) -> __SELF__:\n        return __magic__.to_gpu(self, cache)\n    def __from_gpu__(self: __SELF__, other: __SELF__, __SELF__: type):\n        return __magic__.from_gpu(self, other)\n    def __from_gpu_new__(other: __SELF__, __SELF__: type) -> __SELF__:\n        return __magic__.from_gpu_new(other)\n    def __tuplesize__(self: __SELF__, __SELF__: type) -> int:\n        return __magic__.tuplesize(self)\ntuple = Tuple\n\n@tuple\n@__internal__\nclass __NTuple__[N: Literal[int], T]:\n    pass\n\n@__attribute__\ndef pure():\n    pass\n\n@__attribute__\ndef derives():\n    pass\n\n@extend\nclass NoneType:\n    @pure\n    @derives\n    @llvm\n    def __new__() -> NoneType:\n        ret {} {}\nNoneType.__new__()  # always have this realization around\n\n@tuple\n@__internal__\nclass Array:\n    len: int\n    ptr: Ptr[T]\n    T: type\n\n@extend\nclass type:\n    def __new__(obj):\n        pass\nfunction = Function\n\n@tuple\n@__internal__\n@__notuple__\nclass Union[TU]:\n    # compiler-generated\n    def __new__(val):\n        TU\n\n@extend\nclass Function:\n    @pure\n    @derives\n    @llvm\n    def __new__() -> Function[T, TR]:\n        ret ptr null\n\n# dummy\n@__internal__\nclass TypeTrait[T]:\n    pass\n\n@__internal__\nclass ByVal:\n    pass\n\n@__internal__\nclass ByRef:\n    pass\n\n@__internal__\nclass ClassVar[T]:\n    pass\n\n@__internal__\nclass RTTIType:\n    data: Ptr[byte]  # pointer to the data\n    typeinfo: Ptr[byte]  # TypeInfo type information\n\n\n@__internal__\n@tuple\nclass ellipsis:\n    @pure\n    @derives\n    @llvm\n    def __new__() -> ellipsis:\n        ret {} {}\nEllipsis = ellipsis()\n\n@tuple\n@__internal__\nclass __array__:\n    T: type\n    def __new__(sz: Literal[int]) -> Array[T]:\n        pass\n\n@tuple\n@__internal__\nclass Import:\n    loaded: bool\n    file: Literal[str]\n    name: Literal[str]\n\n    @pure\n    @derives\n    @llvm\n    def __new__(loaded: bool, file: Literal[str], name: Literal[str]) -> Import[file, name]:\n        %0 = insertvalue { {=bool} } undef, {=bool} %loaded, 0\n        ret { {=bool} } %0\n\n\ndef __ptr__(var):\n    pass\n\ndef compile_error(msg: Literal[str]):\n    pass\n\ndef isinstance(obj, what):\n    # isinstance(obj, type) -> Literal[bool]: true if obj is type[T]\n    # isinstance(obj, Tuple) -> Literal[bool]: true if obj is Tuple\n    # isinstance(obj, ByVal) -> Literal[bool]: true if obj is @tuple\n    # isinstance(obj, ByRef) -> Literal[bool]: true if obj is ref. type (class)\n    # isinstance(obj, Union) -> Literal[bool]: true if obj is union\n    # isinstance(union, T) -> bool: true if union's current content is T (runtime)\n    # isinstance(pyobj, pyobj_T) -> bool: true if pyobj's type is pyobj_T (runtime)\n    # isinstance(non_pyobj, pyobj) -> Literal[bool]: always false\n    # isinstance(obj, T) -> Literal[bool]: true if T is base type (static or RTTI) of type(obj)\n    # isinstance(obj, T) -> bool: true if type(obj) is base type (static or RTTI) of T and RTTI info matches\n    # otherwise: static[False]\n    pass\n\n@__attribute__\ndef overload():\n    pass\n\ndef hasattr(obj, attr: Literal[str], *args, **kwargs):\n    pass\n\ndef getattr(obj, attr: Literal[str]):\n    pass\n\ndef setattr(obj, attr: Literal[str], what):\n    pass\n\n@tuple\n@__internal__\nclass Super:\n    __obj__: __T__\n    __T__: type\n\ndef super():\n    pass\n\ndef superf(*args):\n    \"\"\"Special handling\"\"\"\n    pass\n\n#(init=False, repr=False, eq=False, order=False, hash=False, pickle=False, python=False, gpu=False, container=False)\n@__internal__\n@tuple\nclass NamedTuple:\n    args: T\n    N: Literal[int]  # name cache ID\n    T: type\n\n    @pure\n    @derives\n    @llvm\n    def __new__(args: T = (), N: Literal[int] = 0, T: type) -> NamedTuple[N, T]:\n        %0 = insertvalue { {=T} } undef, {=T} %args, 0\n        ret { {=T} } %0\n\n\n@__internal__\n@tuple\nclass Partial:\n    args: T  # format: (arg1, arg2, ..., (star_args...))\n    kwargs: K\n\n    M: Literal[str] # mask\n    T: type # Tuple\n    K: type # NamedTuple\n    F: type # must be unrealized_type\n\n    @pure\n    @derives\n    @llvm\n    def __new__(args: T, kwargs: K, M: Literal[str], F: type, T: type, K: type) -> Partial[M, T, K, F]:\n        %0 = insertvalue { {=T}, {=K} } undef, {=T} %args, 0\n        %1 = insertvalue { {=T}, {=K} } %0, {=K} %kwargs, 1\n        ret { {=T}, {=K} } %1\n\n\n@__internal__\n@tuple\nclass Callable:\n    fn: Function[[Ptr[byte], T], TR]\n    data: Ptr[byte]\n    T: type\n    TR: type\n\n\n@tuple\n@__internal__\nclass Coroutine[T]:\n    @pure\n    @derives\n    @llvm\n    def __raw__(self) -> Ptr[byte]:\n        ret ptr %self\n\n    @pure\n    @derives\n    @llvm\n    def __await__(self) -> Generator[T]:\n        ret ptr %self\n\n@tuple\n@__internal__\nclass AsyncGenerator[T]:\n    @pure\n    @derives\n    @llvm\n    def __raw__(self) -> Ptr[byte]:\n        ret ptr %self\n\n__codon__: Literal[bool] = True\n"
  },
  {
    "path": "stdlib/internal/dlopen.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n# <dlfcn.h>\n@tuple\nclass DL_info:\n    dli_fname: cobj\n    dli_fbase: cobj\n    dli_sname: cobj\n    dli_saddr: cobj\n    dli_version: i32\n    dli_reserved1: i32\n    dli_reserved: Ptr[i32]\n\nfrom C import dlerror() -> cobj as c_dlerror\nfrom C import dlopen(cobj, int) -> cobj as c_dlopen\nfrom C import dlsym(cobj, cobj) -> cobj as c_dlsym\nfrom C import dlclose(cobj) -> i32 as c_dlclose\nfrom C import dladdr(cobj, Ptr[DL_info]) -> i32\n\nRTLD_LAZY: Literal[int] = 1\nRTLD_NOW: Literal[int] = 2\n\nRTLD_GLOBAL: Literal[int] = 8 if __apple__ else 256\nRTLD_LOCAL: Literal[int] = 0 if __apple__ else 256\n\ndef dlext() -> Literal[str]:\n    if __apple__:\n        return \"dylib\"\n    else:\n        return \"so\"\n\n@pure\ndef dlerror() -> str:\n    return str.from_ptr(c_dlerror())\n\ndef dlopen(name: str, flag: int = RTLD_NOW | RTLD_GLOBAL) -> cobj:\n    h = c_dlopen(cobj() if name == \"\" else name.c_str(), flag)\n    if h == cobj():\n        raise CError(dlerror())\n    return h\n\ndef dlsym(lib, name: str, Fn: type) -> Fn:\n    h = cobj()\n    if isinstance(lib, str):\n        h = dlopen(lib)\n    else:\n        h = lib\n    fn = c_dlsym(h, name.c_str())\n    if fn == cobj():\n        raise CError(dlerror())\n    return Fn(fn)\n\ndef dlclose(handle: cobj):\n    if c_dlclose(handle) != i32(0):\n        raise CError(dlerror())\n\ndef get_library_file_name(handle: cobj):\n    info: DL_info\n    if dladdr(handle, __ptr__(info)):\n        return str.from_ptr(info.dli_fname)\n    else:\n        raise CError(dlerror())\n"
  },
  {
    "path": "stdlib/internal/file.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom internal.gc import realloc, free\n\nclass File:\n    sz: int\n    buf: Ptr[byte]\n    fp: cobj\n\n    def __init__(self, fp: cobj):\n        self.fp = fp\n        self._reset()\n\n    def __init__(self, path: str, mode: str):\n        self.fp = _C.fopen(path.c_str(), mode.c_str())\n        if not self.fp:\n            raise IOError(f\"file {path} could not be opened\")\n        self._reset()\n\n    def __init__(self, fd: int, mode: str):\n        self.fp = _C.fdopen(fd, mode.c_str())\n        if not self.fp:\n            raise IOError(f\"file descriptor {fd} could not be opened\")\n        self._reset()\n\n    def _errcheck(self, msg: str):\n        err = int(_C.ferror(self.fp))\n        if err:\n            raise IOError(f\"file I/O error: {msg}\")\n\n    def __enter__(self):\n        pass\n\n    def __exit__(self):\n        self.close()\n\n    def __iter__(self) -> Generator[str]:\n        for a in self._iter():\n            yield a.__ptrcopy__()\n\n    def readlines(self) -> List[str]:\n        return [l for l in self]\n\n    def write(self, s: str):\n        self._ensure_open()\n        _C.fwrite(s.ptr, 1, len(s), self.fp)\n        self._errcheck(\"error in write\")\n\n    def __file_write_gen__(self, g: Generator[T], T: type):\n        for s in g:\n            self.write(str(s))\n\n    def read(self, sz: int = -1) -> str:\n        self._ensure_open()\n        if sz < 0:\n            SEEK_SET = 0\n            SEEK_END = 2\n            cur = _C.ftell(self.fp)\n            _C.fseek(self.fp, 0, i32(SEEK_END))\n            sz = _C.ftell(self.fp) - cur\n            _C.fseek(self.fp, cur, i32(SEEK_SET))\n        buf = Ptr[byte](sz)\n        ret = _C.fread(buf, 1, sz, self.fp)\n        self._errcheck(\"error in read\")\n        return str(buf, ret)\n\n    def tell(self) -> int:\n        self._ensure_open()\n        ret = _C.ftell(self.fp)\n        self._errcheck(\"error in tell\")\n        return ret\n\n    def seek(self, offset: int, whence: int):\n        self._ensure_open()\n        _C.fseek(self.fp, offset, i32(whence))\n        self._errcheck(\"error in seek\")\n\n    def flush(self):\n        self._ensure_open()\n        _C.fflush(self.fp)\n\n    def close(self):\n        if self.fp:\n            _C.fclose(self.fp)\n            self.fp = cobj()\n        if self.buf:\n            _C.free(self.buf)\n            self._reset()\n\n    def _ensure_open(self):\n        if not self.fp:\n            raise IOError(\"I/O operation on closed file\")\n\n    def _reset(self):\n        self.buf = Ptr[byte]()\n        self.sz = 0\n\n    def _iter(self) -> Generator[str]:\n        self._ensure_open()\n        while True:\n            rd = _C.getline(\n                Ptr[Ptr[byte]](self.__raw__() + 8), Ptr[int](self.__raw__()), self.fp\n            )\n            if rd != -1:\n                yield str(self.buf, rd)\n            else:\n                break\n\n    def _iter_trim_newline(self) -> Generator[str]:\n        self._ensure_open()\n        while True:\n            rd = _C.getline(\n                Ptr[Ptr[byte]](self.__raw__() + 8), Ptr[int](self.__raw__()), self.fp\n            )\n            if rd != -1:\n                if self.buf[rd - 1] == byte(10):\n                    rd -= 1\n                yield str(self.buf, rd)\n            else:\n                break\n\ndef _gz_errcheck(stream: cobj):\n    errnum = i32(0)\n    msg = _C.gzerror(stream, __ptr__(errnum))\n    if msg and msg[0]:\n        raise IOError(f\"zlib error: {str(msg, _C.strlen(msg))}\")\n\nclass gzFile:\n    sz: int\n    buf: Ptr[byte]\n    fp: cobj\n\n    def __init__(self, fp: cobj):\n        self.fp = fp\n        self._reset()\n\n    def __init__(self, path: str, mode: str):\n        self.fp = _C.gzopen(path.c_str(), mode.c_str())\n        if not self.fp:\n            raise IOError(f\"file {path} could not be opened\")\n        self._reset()\n\n    def _getline(self) -> int:\n        if not self.buf:\n            self.sz = 128\n            self.buf = Ptr[byte](self.sz)\n\n        offset = 0\n        while True:\n            if not _C.gzgets(self.fp, self.buf + offset, i32(self.sz - offset)):\n                _gz_errcheck(self.fp)\n                if offset == 0:\n                    return -1\n                break\n\n            offset += _C.strlen(self.buf + offset)\n\n            if self.buf[offset - 1] == byte(10):  # '\\n'\n                break\n\n            oldsz = self.sz\n            self.sz *= 2\n            self.buf = realloc(self.buf, self.sz, oldsz)\n\n        return offset\n\n    def __iter__(self) -> Generator[str]:\n        for a in self._iter():\n            yield a.__ptrcopy__()\n\n    def __enter__(self):\n        pass\n\n    def __exit__(self):\n        self.close()\n\n    def close(self):\n        if self.fp:\n            _C.gzclose(self.fp)\n            self.fp = cobj()\n        if self.buf:\n            free(self.buf)\n            self._reset()\n\n    def readlines(self) -> List[str]:\n        return [l for l in self]\n\n    def write(self, s: str):\n        self._ensure_open()\n        _C.gzwrite(self.fp, s.ptr, u32(len(s)))\n        _gz_errcheck(self.fp)\n\n    def __file_write_gen__(self, g: Generator[T], T: type):\n        for s in g:\n            self.write(str(s))\n\n    def read(self, sz: int = -1) -> str:\n        self._ensure_open()\n        if sz < 0:\n            buf = _strbuf()\n            for a in self._iter():\n                buf.append(a)\n            return buf.__str__()\n        buf = Ptr[byte](sz)\n        ret = _C.gzread(self.fp, buf, u32(sz))\n        _gz_errcheck(self.fp)\n        return str(buf, int(ret))\n\n    def tell(self) -> int:\n        self._ensure_open()\n        ret = _C.gztell(self.fp)\n        _gz_errcheck(self.fp)\n        return ret\n\n    def seek(self, offset: int, whence: int):\n        self._ensure_open()\n        _C.gzseek(self.fp, offset, i32(whence))\n        _gz_errcheck(self.fp)\n\n    def flush(self):\n        Z_FINISH = 4\n        self._ensure_open()\n        _C.gzflush(self.fp, i32(Z_FINISH))\n        _gz_errcheck(self.fp)\n\n    def _iter(self) -> Generator[str]:\n        self._ensure_open()\n        while True:\n            rd = self._getline()\n            if rd != -1:\n                yield str(self.buf, rd)\n            else:\n                break\n\n    def _iter_trim_newline(self) -> Generator[str]:\n        self._ensure_open()\n        while True:\n            rd = self._getline()\n            if rd != -1:\n                if self.buf[rd - 1] == byte(10):\n                    rd -= 1\n                yield str(self.buf, rd)\n            else:\n                break\n\n    def _ensure_open(self):\n        if not self.fp:\n            raise IOError(\"I/O operation on closed file\")\n\n    def _reset(self):\n        self.buf = cobj()\n        self.sz = 0\n\ndef _bz_errcheck(stream: cobj):\n    errnum = i32(0)\n    msg = _C.BZ2_bzerror(stream, __ptr__(errnum))\n    if errnum < i32(0):\n        raise IOError(f\"bzip2 error: {str(msg, _C.strlen(msg))}\")\n\nclass bzFile:\n    sz: int\n    buf: Ptr[byte]\n    fp: cobj\n\n    def __init__(self, fp: cobj):\n        self.fp = fp\n        self._reset()\n\n    def __init__(self, path: str, mode: str):\n        self.fp = _C.BZ2_bzopen(path.c_str(), mode.c_str())\n        if not self.fp:\n            raise IOError(f\"file {path} could not be opened\")\n        self._reset()\n\n    def _getline(self) -> int:\n        if not self.buf:\n            self.sz = 128\n            self.buf = Ptr[byte](self.sz)\n\n        offset = 0\n        while True:\n            m = self._gets(self.buf + offset, self.sz - offset)\n            if m == 0:\n                if offset == 0:\n                    return -1\n                break\n            elif m < 0:\n                _bz_errcheck(self.fp)\n                if offset == 0:\n                    return -1\n                break\n\n            offset += m\n\n            if self.buf[offset - 1] == byte(10):  # '\\n'\n                break\n\n            oldsz = self.sz\n            self.sz *= 2\n            self.buf = realloc(self.buf, self.sz, oldsz)\n\n        return offset\n\n    def __iter__(self) -> Generator[str]:\n        for a in self._iter():\n            yield a.__ptrcopy__()\n\n    def __enter__(self):\n        pass\n\n    def __exit__(self):\n        self.close()\n\n    def close(self):\n        if self.fp:\n            _C.BZ2_bzclose(self.fp)\n            self.fp = cobj()\n        if self.buf:\n            free(self.buf)\n            self._reset()\n\n    def readlines(self) -> List[str]:\n        return [l for l in self]\n\n    def write(self, s: str):\n        self._ensure_open()\n        _C.BZ2_bzwrite(self.fp, s.ptr, i32(len(s)))\n        _bz_errcheck(self.fp)\n\n    def __file_write_gen__(self, g: Generator[T], T: type):\n        for s in g:\n            self.write(str(s))\n\n    def read(self, sz: int = -1) -> str:\n        self._ensure_open()\n        if sz < 0:\n            buf = _strbuf()\n            for a in self._iter():\n                buf.append(a)\n            return buf.__str__()\n        buf = Ptr[byte](sz)\n        ret = _C.BZ2_bzread(self.fp, buf, i32(sz))\n        _bz_errcheck(self.fp)\n        return str(buf, int(ret))\n\n    def flush(self):\n        self._ensure_open()\n        _C.BZ2_bzflush(self.fp)\n        _bz_errcheck(self.fp)\n\n    def _iter(self) -> Generator[str]:\n        self._ensure_open()\n        while True:\n            rd = self._getline()\n            if rd != -1:\n                yield str(self.buf, rd)\n            else:\n                break\n\n    def _iter_trim_newline(self) -> Generator[str]:\n        self._ensure_open()\n        while True:\n            rd = self._getline()\n            if rd != -1:\n                if self.buf[rd - 1] == byte(10):\n                    rd -= 1\n                yield str(self.buf, rd)\n            else:\n                break\n\n    def _ensure_open(self):\n        if not self.fp:\n            raise IOError(\"I/O operation on closed file\")\n\n    def _reset(self):\n        self.buf = cobj()\n        self.sz = 0\n\n    def _gets(self, buf: cobj, sz: int):\n        if sz <= 0:\n            return 0\n\n        fp = self.fp\n        b = byte(0)\n        i = 0\n\n        while i < sz - 1:\n            status = _C.BZ2_bzread(fp, __ptr__(b), i32(1))\n            if status == i32(0):\n                break\n            elif status < i32(0):\n                return -1\n\n            buf[i] = b\n            i += 1\n            if b == byte(10):\n                break\n\n        return i\n\ndef open(path, mode: str = \"r\") -> File:\n    return File(path, mode)\n\ndef gzopen(path: str, mode: str = \"r\") -> gzFile:\n    return gzFile(path, mode)\n\ndef bzopen(path: str, mode: str = \"r\") -> bzFile:\n    return bzFile(path, mode)\n\ndef is_binary(path: str) -> bool:\n    # https://stackoverflow.com/questions/898669/how-can-i-detect-if-a-file-is-binary-non-text-in-python/7392391#7392391\n    # Can get both false positive and false negatives, but still is a\n    # clever approach that works for the large majority of files\n    textchars = {7, 8, 9, 10, 12, 13, 27} | set(iter(range(0x20, 0x100))) - {0x7F}\n    with open(path, \"rb\") as f:\n        header = f.read(1024)\n        return any(ord(c) not in textchars for c in header)\n"
  },
  {
    "path": "stdlib/internal/format.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\ndef _format_error(ret: str):\n\traise ValueError(f\"invalid format specifier: {ret}\")\n\ndef _python_to_fmt_format(s):\n    if not s:\n        return s\n\n    i = 0\n    fill, align = None, None\n    if (i + 1 < len(s) and\n        (s[i + 1] == '<' or\n         s[i + 1] == '>' or\n         s[i + 1] == '=' or\n         s[i + 1] == '^')):\n        fill = s[i]\n        align = s[i + 1]\n        i += 2\n    elif (i < len(s) and\n          (s[i] == '<' or\n           s[i] == '>' or\n           s[i] == '=' or\n           s[i] == '^')):\n        align = s[i]\n        i += 1\n    if align == '=':\n        raise NotImplementedError(\"'=' alignment not yet supported\")\n\n    sign = None\n    if i < len(s) and (s[i] == '+' or s[i] == '-' or s[i] == ' '):\n        sign = s[i]\n        i += 1\n\n    coerce_negative_float = False\n    if i < len(s) and s[i] == 'z':\n        coerce_negative_float = True\n        i += 1\n    if coerce_negative_float:\n        raise NotImplementedError(\"'z' not yet supported\")\n\n    alternate_form = False\n    if i < len(s) and s[i] == '#':\n        alternate_form = True\n        i += 1\n\n    width_pre_zero = False\n    if i < len(s) and s[i] == '#':\n        width_pre_zero = True\n        i += 1\n    width = 0\n    while i < len(s) and str._isdigit(s.ptr[i]):\n        width = 10 * width + ord(s[i]) - ord('0')\n        i += 1\n\n    grouping = None\n    if i < len(s) and (s[i] == '_' or s[i] == ','):\n        grouping = s[i]\n        i += 1\n    if grouping == '_':\n        raise NotImplementedError(\"'_' grouping not yet supported\")\n\n    precision = None\n    if i < len(s) and s[i] == '.':\n        i += 1\n        precision = 0\n        while i < len(s) and str._isdigit(s.ptr[i]):\n            precision = 10 * precision + ord(s[i]) - ord('0')\n            i += 1\n\n    type = None\n    if i < len(s):\n        type = s[i]\n        i += 1\n\n    if i != len(s):\n        raise ValueError(\"bad format string\")\n\n    # Construct fmt::format-compatible string\n    ns = _strbuf()\n    if align:\n        if fill: ns.append(fill)\n        ns.append(align)\n    if sign: ns.append(sign)\n    if alternate_form: ns.append(\"#\")\n    if width_pre_zero: ns.append(\"0\")\n    if width: ns.append(str(width))\n    if precision is not None:\n        ns.append(\".\")\n        ns.append(str(precision))\n    if grouping: ns.append(\"L\")\n    if type: ns.append(type)\n    return str(ns)\n\n@extend\nclass int:\n    def __format__(self, format_spec: str) -> str:\n        err = False\n        ret = _C.seq_str_int(self, _python_to_fmt_format(format_spec), __ptr__(err))\n        if format_spec and err:\n            _format_error(ret)\n        return ret\n\n@extend\nclass Int:\n    def __format__(self, format_spec: str) -> str:\n        err = False\n        ret = _C.seq_str_int(self.__int__(), _python_to_fmt_format(format_spec), __ptr__(err))\n        if format_spec and err:\n            _format_error(ret)\n        return ret\n\n@extend\nclass UInt:\n    def __format__(self, format_spec: str) -> str:\n        err = False\n        ret = _C.seq_str_uint(self.__int__(), _python_to_fmt_format(format_spec), __ptr__(err))\n        if format_spec and err:\n            _format_error(ret)\n        return ret\n\n@extend\nclass float:\n    def __format__(self, format_spec: str) -> str:\n        err = False\n        ret = _C.seq_str_float(self, _python_to_fmt_format(format_spec), __ptr__(err))\n        if format_spec and err:\n            _format_error(ret)\n        return ret if ret != \"-nan\" else \"nan\"\n\n@extend\nclass str:\n    def __format__(self, format_spec: str) -> str:\n        err = False\n        ret = _C.seq_str_str(self, _python_to_fmt_format(format_spec), __ptr__(err))\n        if format_spec and err:\n            _format_error(ret)\n        return ret\n\n@extend\nclass Ptr:\n    def __format__(self, format_spec: str) -> str:\n        err = False\n        ret = _C.seq_str_ptr(self.as_byte(), _python_to_fmt_format(format_spec), __ptr__(err))\n        if format_spec and err:\n            _format_error(ret)\n        return ret\n\ndef _divmod_10(dividend, N: Literal[int]):\n    T = type(dividend)\n    zero, one = T(0), T(1)\n    neg = dividend < zero\n    dvd = dividend.__abs__()\n\n    remainder = 0\n    quotient = zero\n\n    # Euclidean division\n    for bit_idx in range(N - 1, -1, -1):\n        mask = int((dvd & (one << T(bit_idx))) != zero)\n        remainder = (remainder << 1) + mask\n        if remainder >= 10:\n            quotient = (quotient << one) + one\n            remainder -= 10\n        else:\n            quotient = quotient << one\n\n    if neg:\n        quotient = -quotient\n        remainder = -remainder\n\n    return quotient, remainder\n\n@extend\nclass Int:\n    def __str__(self) -> str:\n        if N <= 64:\n            return str(int(self))\n\n        if not self:\n            return '0'\n\n        s = _strbuf()\n        d = self\n\n        if d >= Int[N](0):\n            while True:\n                d, m = _divmod_10(d, N)\n                b = byte(48 + m)  # 48 == ord('0')\n                s.append(str(__ptr__(b), 1))\n                if not d:\n                    break\n        else:\n            while True:\n                d, m = _divmod_10(d, N)\n                b = byte(48 - m)  # 48 == ord('0')\n                s.append(str(__ptr__(b), 1))\n\n                if not d:\n                    break\n            s.append('-')\n\n        s.reverse()\n        return s.__str__()\n\n@extend\nclass UInt:\n    def __str__(self) -> str:\n        if N <= 64:\n            return self.__format__(\"\")\n\n        s = _strbuf()\n        d = self\n\n        while True:\n            d, m = _divmod_10(d, N)\n            b = byte(48 + int(m))  # 48 == ord('0')\n            s.append(str(__ptr__(b), 1))\n            if not d:\n                break\n\n        s.reverse()\n        return s.__str__()\n\n@extend\nclass __magic__:\n    def repr_partial(slf) -> str:\n        h = slf.__class__.__name__\n        return f\"partial({h})\"\n"
  },
  {
    "path": "stdlib/internal/gc.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n# Primarily for internal use. Regular users should not use this module.\n\n@pure\n@C\ndef seq_alloc(a: int) -> cobj:\n    pass\n\n@pure\n@C\ndef seq_alloc_atomic(a: int) -> cobj:\n    pass\n\n@pure\n@C\ndef seq_alloc_uncollectable(a: int) -> cobj:\n    pass\n\n@pure\n@C\ndef seq_alloc_atomic_uncollectable(a: int) -> cobj:\n    pass\n\n@nocapture\n@derives\n@C\ndef seq_realloc(p: cobj, newsize: int, oldsize: int) -> cobj:\n    pass\n\n@nocapture\n@C\ndef seq_free(p: cobj) -> None:\n    pass\n\n@nocapture\n@C\ndef seq_register_finalizer(p: cobj, f: cobj) -> None:\n    pass\n\n@nocapture\n@C\ndef seq_gc_add_roots(p: cobj, q: cobj) -> None:\n    pass\n\n@nocapture\n@C\ndef seq_gc_remove_roots(p: cobj, q: cobj) -> None:\n    pass\n\n@C\ndef seq_gc_clear_roots() -> None:\n    pass\n\n@nocapture\n@C\ndef seq_gc_exclude_static_roots(p: cobj, q: cobj) -> None:\n    pass\n\ndef sizeof(T: type):\n    return T.__elemsize__\n\ndef atomic(T: type):\n    return T.__atomic__\n\ndef alloc(sz: int):\n    return seq_alloc(sz)\n\n# Allocates a block of memory via GC, where the\n# caller guarantees that this block will not store\n# pointers to other GC-allocated data.\ndef alloc_atomic(sz: int):\n    return seq_alloc_atomic(sz)\n\n# Allocates a block of memory via GC that is scanned,\n# but not collected itself. Should be free'd explicitly.\ndef alloc_uncollectable(sz: int):\n    return seq_alloc_uncollectable(sz)\n\n# Allocates a block of memory via GC that is scanned,\n# but not collected itself. Should be free'd explicitly.\ndef alloc_atomic_uncollectable(sz: int):\n    return seq_alloc_atomic_uncollectable(sz)\n\ndef realloc(p: cobj, newsz: int, oldsz: int):\n    return seq_realloc(p, newsz, oldsz)\n\ndef free(p: cobj):\n    seq_free(p)\n\ndef add_roots(start: cobj, end: cobj):\n    seq_gc_add_roots(start, end)\n\ndef remove_roots(start: cobj, end: cobj):\n    seq_gc_remove_roots(start, end)\n\ndef clear_roots():\n    seq_gc_clear_roots()\n\ndef exclude_static_roots(start: cobj, end: cobj):\n    seq_gc_exclude_static_roots(start, end)\n\ndef register_finalizer(p):\n    def f(x: cobj, data: cobj, T: type):\n        Ptr[T](__ptr__(x).as_byte())[0].__del__()\n\n    if hasattr(p, \"__del__\"):\n        seq_register_finalizer(p.__raw__(), f(T=type(p), ...).F.T.__raw__())\n\ndef construct_ref[T](args) -> T:\n    p = T.__new__()\n    p.__init__(*args)\n    return p\n"
  },
  {
    "path": "stdlib/internal/gpu.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom internal.gc import sizeof as _sizeof\nfrom internal.dlopen import *\n\nimport internal.static as static\n\nCUDA_SUCCESS: Literal[int] = 0\nCUDA_ERROR_NOT_FOUND: Literal[int] = 500\n\nCUresult = i32\nCUdevice = i32\nCUdeviceptr = u64\nCUmodule = cobj\nCUcontext = cobj\nCUfunction = cobj\n\nmodules: List[CUmodule] = []\n\n# We use 3020 (v2; 64-bit address space) CUDA API here\ncuCtxCreate = Function[[Ptr[cobj], u32, CUdevice], CUresult](cobj())\ncuMemAlloc = Function[[Ptr[CUdeviceptr], i64], CUresult](cobj())\ncuMemcpyDtoH = Function[[cobj, CUdeviceptr, u64], CUresult](cobj())\ncuMemcpyHtoD = Function[[CUdeviceptr, cobj, u64], CUresult](cobj())\ncuMemFree = Function[[CUdeviceptr], CUresult](cobj())\n\ncuDeviceComputeCapability = Function[[Ptr[i32], Ptr[i32], CUdevice], CUresult](cobj())\ncuDeviceGet = Function[[Ptr[CUdevice], i32], CUresult](cobj())\ncuDeviceGetCount = Function[[Ptr[i32]], CUresult](cobj())\ncuDeviceGetName = Function[[Ptr[byte], i32, CUdevice], CUresult](cobj())\ncuGetErrorName = Function[[CUresult, Ptr[cobj]], CUresult](cobj())\ncuGetErrorString = Function[[CUresult, Ptr[cobj]], CUresult](cobj())\ncuInit = Function[[u32], CUresult](cobj())\ncuLaunchKernel = Function[[CUfunction, u32, u32, u32, u32, u32, u32, u32, cobj, cobj, cobj], CUresult](cobj())\ncuModuleGetFunction = Function[[Ptr[CUfunction], CUmodule, cobj], CUresult](cobj())\ncuModuleLoadData = Function[[Ptr[CUmodule], cobj], CUresult](cobj())\n\n\nclass CUDAError(Exception):\n    result: int\n    def __init__(self, result: int, message: str = \"\"):\n        super().__init__(message)\n        self.result = result\n\n\n@tuple\nclass Device:\n    _device: i32\n\n    def __new__(idx: int):\n        device = CUdevice()\n        cuda_check(cuDeviceGet(__ptr__(device), i32(idx)))\n        return superf(device)\n\n    @staticmethod\n    def count():\n        count = 0i32\n        cuda_check(cuDeviceGetCount(__ptr__(count)))\n        return int(count)\n\n    def __str__(self):\n        name = Ptr[byte](128)\n        cuda_check(cuDeviceGetName(name, 127i32, self._device))\n        return str.from_ptr(name)\n\n    def __index__(self):\n        return int(self._device)\n\n    def __bool__(self):\n        return True\n\n    @property\n    def compute_capability(self):\n        devMajor, devMinor = 0i32, 0i32\n        cuda_check(cuDeviceComputeCapability(__ptr__(devMajor), __ptr__(devMinor), self._device))\n        return int(devMajor), int(devMinor)\n\n\n\n_CUDA_INITIALIZED = False\n\ndef cuda_init_handles(cuda_handle: cobj):\n    global cuCtxCreate\n    global cuDeviceComputeCapability\n    global cuDeviceGet\n    global cuDeviceGetCount\n    global cuDeviceGetName\n    global cuGetErrorName\n    global cuGetErrorString\n    global cuInit\n    global cuLaunchKernel\n    global cuMemAlloc\n    global cuMemcpyDtoH\n    global cuMemcpyHtoD\n    global cuMemFree\n    global cuModuleGetFunction\n    global cuModuleLoadData\n\n    cuCtxCreate = dlsym(cuda_handle, \"cuCtxCreate_v2\")\n    cuMemAlloc = dlsym(cuda_handle, \"cuMemAlloc_v2\")\n    cuMemcpyDtoH = dlsym(cuda_handle, \"cuMemcpyDtoH_v2\")\n    cuMemcpyHtoD = dlsym(cuda_handle, \"cuMemcpyHtoD_v2\")\n    cuMemFree = dlsym(cuda_handle, \"cuMemFree_v2\")\n\n    cuDeviceComputeCapability = dlsym(cuda_handle, \"cuDeviceComputeCapability\")\n    cuDeviceGet = dlsym(cuda_handle, \"cuDeviceGet\")\n    cuDeviceGetCount = dlsym(cuda_handle, \"cuDeviceGetCount\")\n    cuDeviceGetName = dlsym(cuda_handle, \"cuDeviceGetName\")\n    cuGetErrorName = dlsym(cuda_handle, \"cuGetErrorName\")\n    cuGetErrorString = dlsym(cuda_handle, \"cuGetErrorString\")\n    cuInit = dlsym(cuda_handle, \"cuInit\")\n    cuLaunchKernel = dlsym(cuda_handle, \"cuLaunchKernel\")\n    cuModuleGetFunction = dlsym(cuda_handle, \"cuModuleGetFunction\")\n    cuModuleLoadData = dlsym(cuda_handle, \"cuModuleLoadData\")\n\n\ndef cuda_check(err):\n    if err != i32(CUDA_SUCCESS):\n        msg = cobj()\n        cuGetErrorName(err, __ptr__(msg))\n        errname = str.from_ptr(msg)\n        cuGetErrorString(err, __ptr__(msg))\n        msg = str.from_ptr(msg)\n        raise CUDAError(int(err), f\"{msg} ({errname})\")\n\n\ndef nvptx_init():\n    cuda_check(cuInit(0u32))\n    device = CUdevice()\n    cuda_check(cuDeviceGet(__ptr__(device), 0i32))\n    context = CUcontext()\n    cuda_check(cuCtxCreate(__ptr__(context), 0u32, device))\n    return device, context\n\n\ndef nvptx_load_module():\n    from C import __codon_ptx__() -> cobj\n    module = CUmodule()\n    # NOTE: 2nd argument to cuModuleLoadData() will\n    #       be replaced by Codon's GPU pass\n    ptx = __codon_ptx__()\n    if ptx:\n        cuda_check(cuModuleLoadData(__ptr__(module), ptx))\n        modules.append(module)\n\n\ndef cuda_init(debug: bool = False):\n    global _CUDA_INITIALIZED\n    if _CUDA_INITIALIZED:\n        return\n\n    import sys, os\n\n    cuda_handle = cobj()\n    LD = os.getenv(\"CODON_CUDA\", default=\"libcuda.\" + dlext())\n\n    cuda_handle = dlopen(LD, RTLD_LOCAL | RTLD_LAZY)\n    cuda_init_handles(cuda_handle)\n\n    device, context = nvptx_init()\n    if debug:\n        cuCtxGetApiVersion: Function[[CUcontext, Ptr[u32]], CUresult] = dlsym(cuda_handle, \"cuCtxGetApiVersion\")\n        api_version: u32 = 0u32\n        cuCtxGetApiVersion(context, __ptr__(api_version))\n        print(f\"[CUDA] Loaded API version {api_version}\", file=sys.stderr)\n\n    nvptx_load_module()\n\n    if debug:\n        d = Device(0)\n        print(\n            \"[CUDA] Initialized: library=\", get_library_file_name(cuModuleLoadData.__raw__()),\n            \", device=\", d, \", capability=\", d.compute_capability,\n            sep='', file=sys.stderr\n        )\n\n    _CUDA_INITIALIZED = True\n\n\n@tuple\nclass Memory[T]:\n    _ptr: CUdeviceptr\n\n    def __new__(ptr: cobj):\n        return Memory[T](CUdeviceptr(ptr.__int__()))\n\n    def _alloc(n: int, T: type):\n        if n == 0:\n            return Memory[T](0u64)\n        devp = CUdeviceptr()\n        cuda_check(cuMemAlloc(__ptr__(devp), n * _sizeof(T)))\n        return Memory[T](devp)\n\n    def _read(self, p: Ptr[T], n: int):\n        if n:\n            cuda_check(cuMemcpyDtoH(p.as_byte(), self._ptr, u64(n * _sizeof(T))))\n\n    def _write(self, p: Ptr[T], n: int):\n        if n:\n            cuda_check(cuMemcpyHtoD(self._ptr, p.as_byte(), u64(n * _sizeof(T))))\n\n    def _free(self):\n        if self._ptr:\n            cuda_check(cuMemFree(self._ptr))\n\n    @pure\n    @llvm\n    def to_ptr(p: CUdeviceptr) -> cobj:\n        %0 = inttoptr i64 %p to ptr\n        ret ptr %0\n\n    @property\n    def ptr(self) -> cobj:\n        return Memory.to_ptr(self._ptr)\n\n@llvm\ndef syncthreads() -> None:\n    declare void @llvm.nvvm.barrier0()\n    call void @llvm.nvvm.barrier0()\n    ret {} {}\n\n@tuple\nclass Dim3:\n    _x: u32\n    _y: u32\n    _z: u32\n\n    def __new__(x: int, y: int, z: int):\n        return Dim3(u32(x), u32(y), u32(z))\n\n    @property\n    def x(self):\n        return int(self._x)\n\n    @property\n    def y(self):\n        return int(self._y)\n\n    @property\n    def z(self):\n        return int(self._z)\n\n@tuple\nclass Thread:\n    @property\n    def x(self):\n        @pure\n        @llvm\n        def get_x() -> u32:\n            declare i32 @llvm.nvvm.read.ptx.sreg.tid.x()\n            %res = call i32 @llvm.nvvm.read.ptx.sreg.tid.x()\n            ret i32 %res\n\n        return int(get_x())\n\n    @property\n    def y(self):\n        @pure\n        @llvm\n        def get_y() -> u32:\n            declare i32 @llvm.nvvm.read.ptx.sreg.tid.y()\n            %res = call i32 @llvm.nvvm.read.ptx.sreg.tid.y()\n            ret i32 %res\n\n        return int(get_y())\n\n    @property\n    def z(self):\n        @pure\n        @llvm\n        def get_z() -> u32:\n            declare i32 @llvm.nvvm.read.ptx.sreg.tid.z()\n            %res = call i32 @llvm.nvvm.read.ptx.sreg.tid.z()\n            ret i32 %res\n\n        return int(get_z())\n\n@tuple\nclass Block:\n    @property\n    def x(self):\n        @pure\n        @llvm\n        def get_x() -> u32:\n            declare i32 @llvm.nvvm.read.ptx.sreg.ctaid.x()\n            %res = call i32 @llvm.nvvm.read.ptx.sreg.ctaid.x()\n            ret i32 %res\n\n        return int(get_x())\n\n    @property\n    def y(self):\n        @pure\n        @llvm\n        def get_y() -> u32:\n            declare i32 @llvm.nvvm.read.ptx.sreg.ctaid.y()\n            %res = call i32 @llvm.nvvm.read.ptx.sreg.ctaid.y()\n            ret i32 %res\n\n        return int(get_y())\n\n    @property\n    def z(self):\n        @pure\n        @llvm\n        def get_z() -> u32:\n            declare i32 @llvm.nvvm.read.ptx.sreg.ctaid.z()\n            %res = call i32 @llvm.nvvm.read.ptx.sreg.ctaid.z()\n            ret i32 %res\n\n        return int(get_z())\n\n    @property\n    def dim(self):\n        @pure\n        @llvm\n        def get_x() -> u32:\n            declare i32 @llvm.nvvm.read.ptx.sreg.ntid.x()\n            %res = call i32 @llvm.nvvm.read.ptx.sreg.ntid.x()\n            ret i32 %res\n\n        @pure\n        @llvm\n        def get_y() -> u32:\n            declare i32 @llvm.nvvm.read.ptx.sreg.ntid.y()\n            %res = call i32 @llvm.nvvm.read.ptx.sreg.ntid.y()\n            ret i32 %res\n\n        @pure\n        @llvm\n        def get_z() -> u32:\n            declare i32 @llvm.nvvm.read.ptx.sreg.ntid.z()\n            %res = call i32 @llvm.nvvm.read.ptx.sreg.ntid.z()\n            ret i32 %res\n\n        return Dim3(get_x(), get_y(), get_z())\n\n@tuple\nclass Grid:\n    @property\n    def dim(self):\n        @pure\n        @llvm\n        def get_x() -> u32:\n            declare i32 @llvm.nvvm.read.ptx.sreg.nctaid.x()\n            %res = call i32 @llvm.nvvm.read.ptx.sreg.nctaid.x()\n            ret i32 %res\n\n        @pure\n        @llvm\n        def get_y() -> u32:\n            declare i32 @llvm.nvvm.read.ptx.sreg.nctaid.y()\n            %res = call i32 @llvm.nvvm.read.ptx.sreg.nctaid.y()\n            ret i32 %res\n\n        @pure\n        @llvm\n        def get_z() -> u32:\n            declare i32 @llvm.nvvm.read.ptx.sreg.nctaid.z()\n            %res = call i32 @llvm.nvvm.read.ptx.sreg.nctaid.z()\n            ret i32 %res\n\n        return Dim3(get_x(), get_y(), get_z())\n\n@tuple\nclass Warp:\n    def __len__(self):\n        @pure\n        @llvm\n        def get_warpsize() -> u32:\n            declare i32 @llvm.nvvm.read.ptx.sreg.warpsize()\n            %res = call i32 @llvm.nvvm.read.ptx.sreg.warpsize()\n            ret i32 %res\n\n        return int(get_warpsize())\n\nthread = Thread()\nblock = Block()\ngrid = Grid()\nwarp = Warp()\n\ndef _catch():\n    return (thread, block, grid, warp)\n\n_catch()\n\n@tuple\nclass AllocCache:\n    v: List[Ptr[byte]]\n\n    def add(self, p: Ptr[byte]):\n        self.v.append(p)\n\n    def free(self):\n        for p in self.v:\n            Memory[byte](p)._free()\n\n\ndef _tuple_from_gpu(args, gpu_args):\n    if static.len(args) > 0:\n        a = args[0]\n        g = gpu_args[0]\n        a.__from_gpu__(g)\n        _tuple_from_gpu(args[1:], gpu_args[1:])\n\n\ndef nvptx_function(name: Literal[str]) -> CUfunction:\n    function = CUfunction()\n    result = CUresult()\n\n    if name.isalnum():\n        name_clean = name.ptr\n    else:\n        name_len = len(name)\n        name_clean = Ptr[byte](name_len + 1)\n        for i in range(name_len):\n            c = name[i]\n            name_clean[i] = c.ptr[0] if c.isalnum() else byte(95)  # '_'\n        name_clean[name_len] = byte(0)\n\n    for i in range(len(modules) - 1, -1, -1):\n        m = modules[i]\n        result = cuModuleGetFunction(__ptr__(function), m, name_clean)\n        if result == i32(CUDA_SUCCESS):\n            return function\n        elif result == i32(CUDA_ERROR_NOT_FOUND):\n            continue\n        else:\n            break\n\n    cuda_check(result)  # this will raise an error\n    return CUfunction()\n\n\ndef canonical_dim(dim):\n    if isinstance(dim, NoneType):\n        return (1, 1, 1)\n    elif isinstance(dim, int):\n        return (dim, 1, 1)\n    elif isinstance(dim, Tuple[int,int]):\n        return (dim[0], dim[1], 1)\n    elif isinstance(dim, Tuple[int,int,int]):\n        return dim\n    elif isinstance(dim, Dim3):\n        return (dim.x, dim.y, dim.z)\n    else:\n        compile_error(\"bad dimension argument\")\n\n\ndef offsets(t):\n    @pure\n    @llvm\n    def offsetof(t: T, i: Literal[int], T: type, S: type) -> int:\n        %p = getelementptr {=T}, ptr null, i64 0, i32 {=i}\n        %s = ptrtoint ptr %p to i64\n        ret i64 %s\n\n    if static.len(t) == 0:\n        return ()\n    else:\n        T = type(t)\n        S = type(t[-1])\n        return (*offsets(t[:-1]), offsetof(t, static.len(t) - 1, T, S))\n\n\ndef kernel_wrapper(*args, grid, block, fn):\n    grid = canonical_dim(grid)\n    block = canonical_dim(block)\n    cache = AllocCache([])\n    shared_mem = 0\n    gpu_args = tuple(arg.__to_gpu__(cache) for arg in args)\n    kernel_ptr = nvptx_function(static.function.realized(fn, *gpu_args).__llvm_name__)\n    p = __ptr__(gpu_args).as_byte()\n    arg_ptrs = tuple((p + offset) for offset in offsets(gpu_args))\n    cuda_check(cuLaunchKernel(kernel_ptr,\n                                u32(grid[0]), u32(grid[1]), u32(grid[2]),\n                                u32(block[0]), u32(block[1]), u32(block[2]),\n                                u32(shared_mem), cobj(),\n                                __ptr__(arg_ptrs).as_byte(), cobj()))\n    _tuple_from_gpu(args, gpu_args)\n    cache.free()\n\n\ndef kernel(fn):\n    return kernel_wrapper(fn=fn, ...)\n\n\ndef _ptr_to_gpu(p: Ptr[T], n: int, cache: AllocCache, index_filter = lambda i: True, T: type):\n    from internal.gc import atomic\n\n    if not atomic(T):\n        tmp = Ptr[T](n)\n        for i in range(n):\n            if index_filter(i):\n                tmp[i] = p[i].__to_gpu__(cache)\n        p = tmp\n\n    mem = Memory._alloc(n, T)\n    cache.add(mem.ptr)\n    mem._write(p, n)\n    return Ptr[T](mem.ptr)\n\ndef _ptr_from_gpu(p: Ptr[T], q: Ptr[T], n: int, index_filter = lambda i: True, T: type):\n    from internal.gc import atomic\n\n    mem = Memory[T](q.as_byte())\n    if not atomic(T):\n        tmp = Ptr[T](n)\n        mem._read(tmp, n)\n        for i in range(n):\n            if index_filter(i):\n                p[i] = T.__from_gpu_new__(tmp[i])\n    else:\n        mem._read(p, n)\n\n@pure\n@llvm\ndef _ptr_to_type(p: cobj, T: type) -> T:\n    ret ptr %p\n\n\n@pure\n@llvm\ndef _type_to_ptr(p: cobj, T: type) -> T:\n    ret ptr %p\n\n\ndef _object_to_gpu(obj: T, cache: AllocCache, T: type):\n    s = tuple(obj)\n    gpu_mem = Memory._alloc(1, type(s))\n    cache.add(gpu_mem.ptr)\n    gpu_mem._write(__ptr__(s), 1)\n    return _ptr_to_type(gpu_mem.ptr, T)\n\ndef _object_from_gpu(obj):\n    T = type(obj)\n    S = type(tuple(obj))\n\n    tmp = T.__new__()\n    p = Ptr[S](tmp.__raw__())\n    q = Ptr[S](obj.__raw__())\n\n    mem = Memory[S](q.as_byte())\n    mem._read(p, 1)\n    return tmp\n\n@tuple\nclass Pointer[T]:\n    _ptr: Ptr[T]\n    _len: int\n\n    def __to_gpu__(self, cache: AllocCache):\n        return _ptr_to_gpu(self._ptr, self._len, cache)\n\n    def __from_gpu__(self, other: Ptr[T]):\n        _ptr_from_gpu(self._ptr, other, self._len)\n\n    def __from_gpu_new__(other: Ptr[T]):\n        return other\n\ndef raw(v):\n    from numpy import ndarray\n    if isinstance(v, List):\n        return Pointer(v.arr.ptr, len(v))\n    elif isinstance(v, ndarray):\n        if not v._is_contig:\n            raise ValueError(\"gpu.raw() array argument must be contiguous\")\n        return Pointer(v.data, v.size)\n    else:\n        compile_error(\"gpu.raw() argument must be an ndarray or a list\")\n\n@extend\nclass Ptr:\n    def __to_gpu__(self, cache: AllocCache):\n        return self\n\n    def __from_gpu__(self, other: Ptr[T]):\n        pass\n\n    def __from_gpu_new__(other: Ptr[T]):\n        return other\n\n@extend\nclass NoneType:\n    def __to_gpu__(self, cache: AllocCache):\n        return self\n\n    def __from_gpu__(self, other: NoneType):\n        pass\n\n    def __from_gpu_new__(other: NoneType):\n        return other\n\n@extend\nclass int:\n    def __to_gpu__(self, cache: AllocCache):\n        return self\n\n    def __from_gpu__(self, other: int):\n        pass\n\n    def __from_gpu_new__(other: int):\n        return other\n\n@extend\nclass float:\n    def __to_gpu__(self, cache: AllocCache):\n        return self\n\n    def __from_gpu__(self, other: float):\n        pass\n\n    def __from_gpu_new__(other: float):\n        return other\n\n@extend\nclass float32:\n    def __to_gpu__(self, cache: AllocCache):\n        return self\n\n    def __from_gpu__(self, other: float32):\n        pass\n\n    def __from_gpu_new__(other: float32):\n        return other\n\n@extend\nclass bool:\n    def __to_gpu__(self, cache: AllocCache):\n        return self\n\n    def __from_gpu__(self, other: bool):\n        pass\n\n    def __from_gpu_new__(other: bool):\n        return other\n\n@extend\nclass byte:\n    def __to_gpu__(self, cache: AllocCache):\n        return self\n\n    def __from_gpu__(self, other: byte):\n        pass\n\n    def __from_gpu_new__(other: byte):\n        return other\n\n@extend\nclass Int:\n    def __to_gpu__(self, cache: AllocCache):\n        return self\n\n    def __from_gpu__(self, other: Int[N]):\n        pass\n\n    def __from_gpu_new__(other: Int[N]):\n        return other\n\n@extend\nclass UInt:\n    def __to_gpu__(self, cache: AllocCache):\n        return self\n\n    def __from_gpu__(self, other: UInt[N]):\n        pass\n\n    def __from_gpu_new__(other: UInt[N]):\n        return other\n\n@extend\nclass str:\n    def __to_gpu__(self, cache: AllocCache):\n        n = self.len\n        return str(_ptr_to_gpu(self.ptr, n, cache), n)\n\n    def __from_gpu__(self, other: str):\n        pass\n\n    def __from_gpu_new__(other: str):\n        n = other.len\n        p = Ptr[byte](n)\n        _ptr_from_gpu(p, other.ptr, n)\n        return str(p, n)\n\n@extend\nclass List:\n    @inline\n    def __to_gpu__(self, cache: AllocCache):\n        mem = List[T].__new__()\n        n = self.len\n        gpu_ptr = _ptr_to_gpu(self.arr.ptr, n, cache)\n        mem.arr = Array[T](gpu_ptr, n)\n        mem.len = n\n        return _object_to_gpu(mem, cache)\n\n    @inline\n    def __from_gpu__(self, other: List[T]):\n        mem = _object_from_gpu(other)\n        my_cap = self.arr.len\n        other_cap = mem.arr.len\n\n        if other_cap > my_cap:\n            self._resize(other_cap)\n\n        _ptr_from_gpu(self.arr.ptr, mem.arr.ptr, mem.len)\n        self.len = mem.len\n\n    @inline\n    def __from_gpu_new__(other: List[T]):\n        mem = _object_from_gpu(other)\n        arr = Array[T](mem.arr.len)\n        _ptr_from_gpu(arr.ptr, mem.arr.ptr, arr.len)\n        mem.arr = arr\n        return mem\n\n@extend\nclass DynamicTuple:\n    @inline\n    def __to_gpu__(self, cache: AllocCache):\n        n = self._len\n        gpu_ptr = _ptr_to_gpu(self._ptr, n, cache)\n        return DynamicTuple(gpu_ptr, n)\n\n    @inline\n    def __from_gpu__(self, other: DynamicTuple[T]):\n        _ptr_from_gpu(self._ptr, other._ptr, self._len)\n\n    @inline\n    def __from_gpu_new__(other: DynamicTuple[T]):\n        n = other._len\n        p = Ptr[T](n)\n        _ptr_from_gpu(p, other._ptr, n)\n        return DynamicTuple(p, n)\n\n@extend\nclass Dict:\n    def __to_gpu__(self, cache: AllocCache):\n        from internal.khash import __ac_fsize\n        mem = Dict[K,V].__new__()\n        n = self._n_buckets\n        f = __ac_fsize(n) if n else 0\n\n        mem._n_buckets = n\n        mem._size = self._size\n        mem._n_occupied = self._n_occupied\n        mem._upper_bound = self._upper_bound\n        mem._flags = _ptr_to_gpu(self._flags, f, cache)\n        mem._keys = _ptr_to_gpu(self._keys, n, cache, lambda i: self._kh_exist(i))\n        mem._vals = _ptr_to_gpu(self._vals, n, cache, lambda i: self._kh_exist(i))\n\n        return _object_to_gpu(mem, cache)\n\n    def __from_gpu__(self, other: Dict[K,V]):\n        from internal.khash import __ac_fsize\n        mem = _object_from_gpu(other)\n        my_n = self._n_buckets\n        n = mem._n_buckets\n        f = __ac_fsize(n) if n else 0\n\n        if my_n != n:\n            self._flags = Ptr[u32](f)\n            self._keys = Ptr[K](n)\n            self._vals = Ptr[V](n)\n\n        _ptr_from_gpu(self._flags, mem._flags, f)\n        _ptr_from_gpu(self._keys, mem._keys, n, lambda i: self._kh_exist(i))\n        _ptr_from_gpu(self._vals, mem._vals, n, lambda i: self._kh_exist(i))\n\n        self._n_buckets = n\n        self._size = mem._size\n        self._n_occupied = mem._n_occupied\n        self._upper_bound = mem._upper_bound\n\n    def __from_gpu_new__(other: Dict[K,V]):\n        from internal.khash import __ac_fsize\n        mem = _object_from_gpu(other)\n\n        n = mem._n_buckets\n        f = __ac_fsize(n) if n else 0\n        flags = Ptr[u32](f)\n        keys = Ptr[K](n)\n        vals = Ptr[V](n)\n\n        _ptr_from_gpu(flags, mem._flags, f)\n        mem._flags = flags\n        _ptr_from_gpu(keys, mem._keys, n, lambda i: mem._kh_exist(i))\n        mem._keys = keys\n        _ptr_from_gpu(vals, mem._vals, n, lambda i: mem._kh_exist(i))\n        mem._vals = vals\n        return mem\n\n@extend\nclass Set:\n    def __to_gpu__(self, cache: AllocCache):\n        from internal.khash import __ac_fsize\n        mem = Set[K].__new__()\n        n = self._n_buckets\n        f = __ac_fsize(n) if n else 0\n\n        mem._n_buckets = n\n        mem._size = self._size\n        mem._n_occupied = self._n_occupied\n        mem._upper_bound = self._upper_bound\n        mem._flags = _ptr_to_gpu(self._flags, f, cache)\n        mem._keys = _ptr_to_gpu(self._keys, n, cache, lambda i: self._kh_exist(i))\n\n        return _object_to_gpu(mem, cache)\n\n    def __from_gpu__(self, other: Set[K]):\n        from internal.khash import __ac_fsize\n        mem = _object_from_gpu(other)\n\n        my_n = self._n_buckets\n        n = mem._n_buckets\n        f = __ac_fsize(n) if n else 0\n\n        if my_n != n:\n            self._flags = Ptr[u32](f)\n            self._keys = Ptr[K](n)\n\n        _ptr_from_gpu(self._flags, mem._flags, f)\n        _ptr_from_gpu(self._keys, mem._keys, n, lambda i: self._kh_exist(i))\n\n        self._n_buckets = n\n        self._size = mem._size\n        self._n_occupied = mem._n_occupied\n        self._upper_bound = mem._upper_bound\n\n    def __from_gpu_new__(other: Set[K]):\n        from internal.khash import __ac_fsize\n        mem = _object_from_gpu(other)\n\n        n = mem._n_buckets\n        f = __ac_fsize(n) if n else 0\n        flags = Ptr[u32](f)\n        keys = Ptr[K](n)\n\n        _ptr_from_gpu(flags, mem._flags, f)\n        mem._flags = flags\n        _ptr_from_gpu(keys, mem._keys, n, lambda i: mem._kh_exist(i))\n        mem._keys = keys\n        return mem\n\n@extend\nclass Optional:\n    def __to_gpu__(self, cache: AllocCache):\n        if self is None:\n            return self\n        else:\n            return Optional[T](self.__val__().__to_gpu__(cache))\n\n    def __from_gpu__(self, other: Optional[T]):\n        if self is not None and other is not None:\n            self.__val__().__from_gpu__(other.__val__())\n\n    def __from_gpu_new__(other: Optional[T]):\n        if other is None:\n            return Optional[T]()\n        else:\n            return Optional[T](T.__from_gpu_new__(other.__val__()))\n\n@extend\nclass type:\n    def _to_gpu(obj, cache: AllocCache):\n        if isinstance(obj, Tuple):\n            return tuple(a.__to_gpu__(cache) for a in obj)\n        elif isinstance(obj, ByVal):\n            T = type(obj)\n            return T(*tuple(a.__to_gpu__(cache) for a in tuple(obj)))\n        else:\n            T = type(obj)\n            S = type(tuple(obj))\n            mem = T.__new__()\n            Ptr[S](mem.__raw__())[0] = tuple(obj).__to_gpu__(cache)\n            return _object_to_gpu(mem, cache)\n\n    def _from_gpu(obj, other):\n        if isinstance(obj, Tuple):\n            _tuple_from_gpu(obj, other)\n        elif isinstance(obj, ByVal):\n            _tuple_from_gpu(tuple(obj), tuple(other))\n        else:\n            S = type(tuple(obj))\n            Ptr[S](obj.__raw__())[0] = S.__from_gpu_new__(tuple(_object_from_gpu(other)))\n\n    def _from_gpu_new(other):\n        if isinstance(other, Tuple):\n            return tuple(type(a).__from_gpu_new__(a) for a in other)\n        elif isinstance(other, ByVal):\n            T = type(other)\n            return T(*tuple(type(a).__from_gpu_new__(a) for a in tuple(other)))\n        else:\n            S = type(tuple(other))\n            mem = _object_from_gpu(other)\n            Ptr[S](mem.__raw__())[0] = S.__from_gpu_new__(tuple(mem))\n            return mem\n\n# @par(gpu=True) support\n\n@pure\n@llvm\ndef _gpu_thread_x() -> u32:\n    declare i32 @llvm.nvvm.read.ptx.sreg.tid.x()\n    %res = call i32 @llvm.nvvm.read.ptx.sreg.tid.x()\n    ret i32 %res\n\n@pure\n@llvm\ndef _gpu_block_x() -> u32:\n    declare i32 @llvm.nvvm.read.ptx.sreg.ctaid.x()\n    %res = call i32 @llvm.nvvm.read.ptx.sreg.ctaid.x()\n    ret i32 %res\n\n@pure\n@llvm\ndef _gpu_block_dim_x() -> u32:\n    declare i32 @llvm.nvvm.read.ptx.sreg.ntid.x()\n    %res = call i32 @llvm.nvvm.read.ptx.sreg.ntid.x()\n    ret i32 %res\n\ndef _gpu_loop_outline_template(start, stop, args, instance: Literal[int]):\n    @nonpure\n    def _loop_step():\n        return 1\n\n    @kernel\n    def _kernel_stub(start: int, count: int, args):\n        @nonpure\n        def _gpu_loop_body_stub(idx, args):\n            pass\n\n        @nonpure\n        def _dummy_use(n):\n            pass\n\n        _dummy_use(instance)\n        idx = (int(_gpu_block_dim_x()) * int(_gpu_block_x())) + int(_gpu_thread_x())\n        step = _loop_step()\n        if idx < count:\n            _gpu_loop_body_stub(start + (idx * step), args)\n\n    step = _loop_step()\n    loop = range(start, stop, step)\n\n    MAX_BLOCK = 1024\n    MAX_GRID = 2147483647\n    G = MAX_BLOCK * MAX_GRID\n    n = len(loop)\n\n    if n == 0:\n        return\n    elif n > G:\n        raise ValueError(f'loop exceeds GPU iteration limit of {G}')\n\n    block = n\n    grid = 1\n    if n > MAX_BLOCK:\n        block = MAX_BLOCK\n        grid = (n // MAX_BLOCK) + (0 if n % MAX_BLOCK == 0 else 1)\n\n    _kernel_stub(start, n, args, grid=grid, block=block)\n\n"
  },
  {
    "path": "stdlib/internal/internal.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\n@extend\nclass __internal__:\n    def _print(a):\n        from C import seq_print(str)\n        if hasattr(a, \"__repr__\"):\n            seq_print(a.__repr__())\n        else:\n            seq_print(a.__str__())\n\n    def seq_assert(file: str, line: int, msg: str) -> AssertionError:\n        s = f\": {msg}\" if msg else \"\"\n        s = f\"Assert failed{s} ({file}:{line.__repr__()})\"\n        return AssertionError(s)\n\n    def seq_assert_test(file: str, line: int, msg: str):\n        from C import seq_print(str)\n        s = f\": {msg}\" if msg else \"\"\n        s = f\"\\033[1;31mTEST FAILED:\\033[0m {file} (line {line}){s}\\n\"\n        seq_print(s)\n\n    def undef(v, s):\n        if not v:\n            raise NameError(f\"name '{s}' is not defined\")\n\n\n@extend\nclass __magic__:\n    # always present\n    def tuplesize(T: type) -> int:\n        return (t() for t in static.vars_types(T)).__elemsize__\n\n    # @dataclass parameter: init=True for tuples;\n    # always present for reference types only\n    def new(T: type) -> T:\n        \"\"\"Create a new reference (class) type\"\"\"\n        return type._ref_new(T)\n\n    # init is compiler-generated when init=True for reference types\n    # def init(self, a1, ..., aN): ...\n\n    # always present for reference types only\n    def raw(obj) -> Ptr[byte]:\n        return type._ref_raw(obj)\n\n    # always present for reference types only\n    def dict(slf) -> List[str]:\n        d = List[str](static.len(slf))\n        for k, _ in static.vars(slf):\n            d.append(k)\n        return d\n\n    # always present for tuple types only\n    def len(slf) -> int:\n        return static.len(slf)\n\n    # always present for tuple types only\n    def add(slf, obj):\n        if not isinstance(obj, Tuple):\n            compile_error(\"can only concatenate tuple to tuple\")\n        return (*slf, *obj)\n\n    # always present for tuple types only\n    def mul(slf, i: Literal[int]):\n        if i < 1:\n            return ()\n        elif i == 1:\n            return slf\n        else:\n            return (*(__magic__.mul(slf, i - 1)), *slf)\n\n    # always present for tuples\n    def contains(slf, what) -> bool:\n        for _, v in static.vars(slf):\n            if isinstance(what, type(v)):\n                if what == v:\n                    return True\n        return False\n\n    # @dataclass parameter: container=True\n    def getitem(slf, index: int):\n        if static.len(slf) == 0:\n            Tuple._fix_index(index, 0)  # raise exception\n        else:\n            return Tuple._getitem(slf, index, type(slf), static.tuple_type(slf, 0))\n\n    # @dataclass parameter: container=True\n    def iter(slf):\n        if static.len(slf) == 0:\n            if int(0): yield 0  # set generator type without yielding anything\n        for _, v in static.vars(slf):\n            yield v\n\n    # @dataclass parameter: order=True or eq=True\n    def eq(slf, obj) -> bool:\n        for k, v in static.vars(slf):\n            if not (v == getattr(obj, k)):\n                return False\n        return True\n\n    # @dataclass parameter: order=True or eq=True\n    def ne(slf, obj) -> bool:\n        return not (slf == obj)\n\n    # @dataclass parameter: order=True\n    def lt(slf, obj) -> bool:\n        for k, v in static.vars(slf):\n            z = getattr(obj, k)\n            if v < z:\n                return True\n            if not (v == z):\n                return False\n        return False\n\n    # @dataclass parameter: order=True\n    def le(slf, obj) -> bool:\n        for k, v in static.vars(slf):\n            z = getattr(obj, k)\n            if v < z:\n                return True\n            if not (v == z):\n                return False\n        return True\n\n    # @dataclass parameter: order=True\n    def gt(slf, obj) -> bool:\n        for k, v in static.vars(slf):\n            z = getattr(obj, k)\n            if z < v:\n                return True\n            if not (v == z):\n                return False\n        return False\n\n    # @dataclass parameter: order=True\n    def ge(slf, obj) -> bool:\n        for k, v in static.vars(slf):\n            z = getattr(obj, k)\n            if z < v:\n                return True\n            if not (v == z):\n                return False\n        return True\n\n    # @dataclass parameter: hash=True\n    def hash(slf) -> int:\n        seed = 0\n        for _, v in static.vars(slf):\n            seed = seed ^ ((v.__hash__() + 2_654_435_769) + ((seed << 6) + (seed >> 2)))\n        return seed\n\n    # @dataclass parameter: pickle=True\n    def pickle(slf, dest: Ptr[byte]) -> None:\n        for _, v in static.vars(slf):\n            v.__pickle__(dest)\n\n    # @dataclass parameter: pickle=True\n    def unpickle(src: Ptr[byte], T: type) -> T:\n        if isinstance(T, ByVal):\n            return type._force_value_cast(tuple(type(t).__unpickle__(src) for t in static.vars_types(T)), T)\n        else:\n            obj = T.__new__()\n            for k, v in static.vars(obj):\n                setattr(obj, k, type(v).__unpickle__(src))\n            return obj\n\n    # @dataclass parameter: python=True\n    def to_py(slf) -> Ptr[byte]:\n        o = pyobj._tuple_new(static.len(tuple(slf)))\n        for i, _, v in static.vars(slf, with_index=True):\n            pyobj._tuple_set(o, i, v.__to_py__())\n        return o\n\n    # @dataclass parameter: python=True\n    def from_py(src: Ptr[byte], T: type) -> T:\n        if isinstance(T, ByVal):\n            return type._force_value_cast(tuple(\n                type(t).__from_py__(pyobj._tuple_get(src, i))\n                for i, t in static.vars_types(T, with_index=True)\n            ), T)\n        else:\n            obj = T.__new__()\n            for i, k, v in static.vars(obj, with_index=True):\n                setattr(obj, k, type(v).__from_py__(pyobj._tuple_get(src, i)))\n            return obj\n\n    # @dataclass parameter: gpu=True\n    def to_gpu(slf, cache):\n        return type._to_gpu(slf, cache)\n\n    # @dataclass parameter: gpu=True\n    def from_gpu(slf: T, other: T, T: type):\n        type._from_gpu(slf, other)\n\n    # @dataclass parameter: gpu=True\n    def from_gpu_new(other: T, T: type) -> T:\n        return type._from_gpu_new(other)\n\n    # @dataclass parameter: repr=True\n    def repr(slf) -> str:\n        l: Literal[int] = static.len(tuple(slf))\n        if l == 0:\n            return \"()\"\n        a = __array__[str](l)\n        n = __array__[str](l)\n        for i, k, v in static.vars(slf, with_index=True):\n            a[i] = v.__repr__()\n            if isinstance(slf, Tuple):\n                n[i] = \"\"\n            else:\n                n[i] = k\n        return Tuple._str(a.ptr, n.ptr, l)\n\n    # @dataclass parameter: repr=False\n    def repr_default(slf) -> str:\n        return f'<{type(slf).__class__.__name__} object at {slf.__raw__()}>'\n\n    # @dataclass parameter: repr=True\n    def str(slf) -> str:\n        if not hasattr(slf, \"__repr__\") and hasattr(slf, \"__repr_default__\"):\n            return slf.__repr_default__()\n        return slf.__repr__()\n\n@tuple\nclass PyObject:\n    refcnt: int\n    pytype: Ptr[byte]\n\n@tuple\nclass PyWrapper[T]:\n    head: PyObject\n    data: T\n\nRTTIType._init_vtables()\n"
  },
  {
    "path": "stdlib/internal/khash.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\ndef __ac_isempty(flag: Ptr[u32], i: int) -> int:\n    return int(flag[i >> 4] >> u32((i & 0xF) << 1)) & 2\n\ndef __ac_isdel(flag: Ptr[u32], i: int) -> int:\n    return int(flag[i >> 4] >> u32((i & 0xF) << 1)) & 1\n\ndef __ac_iseither(flag: Ptr[u32], i: int) -> int:\n    return int(flag[i >> 4] >> u32((i & 0xF) << 1)) & 3\n\ndef __ac_set_isdel_false(flag: Ptr[u32], i: int):\n    flag[i >> 4] &= u32(~(1 << ((i & 0xF) << 1)))\n\ndef __ac_set_isempty_false(flag: Ptr[u32], i: int):\n    flag[i >> 4] &= u32(~(2 << ((i & 0xF) << 1)))\n\ndef __ac_set_isboth_false(flag: Ptr[u32], i: int):\n    flag[i >> 4] &= u32(~(3 << ((i & 0xF) << 1)))\n\ndef __ac_set_isdel_true(flag: Ptr[u32], i: int):\n    flag[i >> 4] |= u32(1 << ((i & 0xF) << 1))\n\ndef __ac_fsize(m) -> int:\n    return 1 if m < 16 else m >> 4\n"
  },
  {
    "path": "stdlib/internal/pynumerics.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n@pure\n@llvm\ndef _floordiv_int_float(self: int, other: float) -> float:\n    declare double @llvm.floor.f64(double)\n    %0 = sitofp i64 %self to double\n    %1 = fdiv double %0, %other\n    %2 = call double @llvm.floor.f64(double %1)\n    ret double %2\n\n@pure\n@llvm\ndef _floordiv_int_int(self: int, other: int) -> int:\n    %0 = sdiv i64 %self, %other\n    ret i64 %0\n\n@pure\n@llvm\ndef _truediv_int_float(self: int, other: float) -> float:\n    %0 = sitofp i64 %self to double\n    %1 = fdiv double %0, %other\n    ret double %1\n\n@pure\n@llvm\ndef _truediv_int_int(self: int, other: int) -> float:\n    %0 = sitofp i64 %self to double\n    %1 = sitofp i64 %other to double\n    %2 = fdiv double %0, %1\n    ret double %2\n\n@pure\n@llvm\ndef _mod_int_float(self: int, other: float) -> float:\n    %0 = sitofp i64 %self to double\n    %1 = frem double %0, %other\n    ret double %1\n\n@pure\n@llvm\ndef _mod_int_int(self: int, other: int) -> int:\n    %0 = srem i64 %self, %other\n    ret i64 %0\n\n@pure\n@llvm\ndef _truediv_float_float(self: float, other: float) -> float:\n    %0 = fdiv double %self, %other\n    ret double %0\n\n@pure\n@llvm\ndef _mod_float_float(self: float, other: float) -> float:\n    %0 = frem double %self, %other\n    ret double %0\n\ndef _divmod_int_int(self: int, other: int):\n    d = _floordiv_int_int(self, other)\n    m = self - d * other\n    if m and ((other ^ m) < 0):\n        m += other\n        d -= 1\n    return (d, m)\n\ndef _divmod_float_float(self: float, other: float):\n    mod = _mod_float_float(self, other)\n    div = _truediv_float_float(self - mod, other)\n    if mod:\n        if (other < 0) != (mod < 0):\n            mod += other\n            div -= 1.0\n    else:\n        mod = (0.0).copysign(other)\n\n    floordiv = 0.0\n    if div:\n        floordiv = div.__floor__()\n        if div - floordiv > 0.5:\n            floordiv += 1.0\n    else:\n        floordiv = (0.0).copysign(self / other)\n\n    return (floordiv, mod)\n\n@extend\nclass int:\n    def __floordiv__(self, other: float):\n        if other == 0.0:\n            raise ZeroDivisionError(\"float floor division by zero\")\n        return _divmod_float_float(float(self), other)[0]\n\n    def __floordiv__(self, other: int) -> int:\n        # Need a return type signature here because ZeroDivisionError\n        # realizes the same __floordiv__ later on\n        if other == 0:\n            raise ZeroDivisionError(\"integer division or modulo by zero\")\n        return _divmod_int_int(self, other)[0]\n\n    def __truediv__(self, other: float):\n        if other == 0.0:\n            raise ZeroDivisionError(\"float division by zero\")\n        return _truediv_int_float(self, other)\n\n    def __truediv__(self, other: int):\n        if other == 0:\n            raise ZeroDivisionError(\"division by zero\")\n        return _truediv_int_int(self, other)\n\n    def __mod__(self, other: float):\n        if other == 0.0:\n            raise ZeroDivisionError(\"float modulo\")\n        return _divmod_float_float(self, other)[1]\n\n    def __mod__(self, other: int):\n        if other == 0:\n            raise ZeroDivisionError(\"integer division or modulo by zero\")\n        return _divmod_int_int(self, other)[1]\n\n    def __divmod__(self, other: float):\n        if other == 0.0:\n            raise ZeroDivisionError(\"float divmod()\")\n        return _divmod_float_float(float(self), other)\n\n    def __divmod__(self, other: int):\n        if other == 0:\n            raise ZeroDivisionError(\"integer division or modulo by zero\")\n        return _divmod_int_int(self, other)\n\n@extend\nclass float:\n    def __floordiv__(self, other: float):\n        if other == 0.0:\n            raise ZeroDivisionError(\"float floor division by zero\")\n        return _divmod_float_float(self, other)[0]\n\n    def __floordiv__(self, other: int):\n        if other == 0:\n            raise ZeroDivisionError(\"float floor division by zero\")\n        return _divmod_float_float(self, float(other))[0]\n\n    def __truediv__(self, other: float):\n        if other == 0.0:\n            raise ZeroDivisionError(\"float division by zero\")\n        return _truediv_float_float(self, other)\n\n    def __truediv__(self, other: int):\n        if other == 0:\n            raise ZeroDivisionError(\"float division by zero\")\n        return _truediv_float_float(self, float(other))\n\n    def __mod__(self, other: float):\n        if other == 0.0:\n            raise ZeroDivisionError(\"float modulo\")\n        return _divmod_float_float(self, other)[1]\n\n    def __mod__(self, other: int):\n        if other == 0:\n            raise ZeroDivisionError(\"float modulo\")\n        return _divmod_float_float(self, float(other))[1]\n\n    def __divmod__(self, other: float):\n        if other == 0.0:\n            raise ZeroDivisionError(\"float divmod()\")\n        return _divmod_float_float(self, other)\n\n    def __divmod__(self, other: int):\n        if other == 0:\n            raise ZeroDivisionError(\"float divmod()\")\n        return _divmod_float_float(self, float(other))\n"
  },
  {
    "path": "stdlib/internal/python.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport os\n\nfrom gc import atomic, alloc_uncollectable\nfrom internal.dlopen import *\nimport internal.static as static\n\n# general\nPy_DecRef = Function[[cobj], NoneType](cobj())\nPy_IncRef = Function[[cobj], NoneType](cobj())\nPy_Initialize = Function[[], NoneType](cobj())\nPyImport_AddModule = Function[[cobj], cobj](cobj())\nPyImport_AddModuleObject = Function[[cobj], cobj](cobj())\nPyImport_ImportModule = Function[[cobj], cobj](cobj())\nPyRun_SimpleString = Function[[cobj], NoneType](cobj())\nPyEval_GetGlobals = Function[[], cobj](cobj())\nPyEval_GetBuiltins = Function[[], cobj](cobj())\nPyOS_setsig = Function[[i32, cobj], cobj](cobj())\n\n# conversions\nPyLong_AsLong = Function[[cobj], int](cobj())\nPyLong_FromLong = Function[[int], cobj](cobj())\nPyFloat_AsDouble = Function[[cobj], float](cobj())\nPyFloat_FromDouble = Function[[float], cobj](cobj())\nPyBool_FromLong = Function[[int], cobj](cobj())\nPyBytes_AsString = Function[[cobj], cobj](cobj())\nPyBytes_Size = Function[[cobj], int](cobj())\nPyList_New = Function[[int], cobj](cobj())\nPyList_Size = Function[[cobj], int](cobj())\nPyList_GetItem = Function[[cobj, int], cobj](cobj())\nPyList_SetItem = Function[[cobj, int, cobj], cobj](cobj())\nPyDict_New = Function[[], cobj](cobj())\nPyDict_Next = Function[[cobj, Ptr[int], Ptr[cobj], Ptr[cobj]], int](cobj())\nPyDict_GetItem = Function[[cobj, cobj], cobj](cobj())\nPyDict_GetItemString = Function[[cobj, cobj], cobj](cobj())\nPyDict_SetItem = Function[[cobj, cobj, cobj], cobj](cobj())\nPyDict_Size = Function[[cobj], int](cobj())\nPySet_Add = Function[[cobj, cobj], cobj](cobj())\nPySet_New = Function[[cobj], cobj](cobj())\nPyTuple_New = Function[[int], cobj](cobj())\nPyTuple_Size = Function[[cobj], int](cobj())\nPyTuple_GetItem = Function[[cobj, int], cobj](cobj())\nPyTuple_SetItem = Function[[cobj, int, cobj], i32](cobj())\nPyUnicode_AsEncodedString = Function[[cobj, cobj, cobj], cobj](cobj())\nPyUnicode_DecodeFSDefaultAndSize = Function[[cobj, int], cobj](cobj())\nPyUnicode_FromString = Function[[cobj], cobj](cobj())\nPyComplex_FromDoubles = Function[[float, float], cobj](cobj())\nPyComplex_RealAsDouble = Function[[cobj], float](cobj())\nPyComplex_ImagAsDouble = Function[[cobj], float](cobj())\nPyIter_Next = Function[[cobj], cobj](cobj())\nPySlice_New = Function[[cobj, cobj, cobj], cobj](cobj())\nPySlice_Unpack = Function[[cobj, Ptr[int], Ptr[int], Ptr[int]], int](cobj())\nPyCapsule_New = Function[[cobj, cobj, cobj], cobj](cobj())\nPyCapsule_GetPointer = Function[[cobj, cobj], cobj](cobj())\n\n# number\nPyNumber_Add = Function[[cobj, cobj], cobj](cobj())\nPyNumber_Subtract = Function[[cobj, cobj], cobj](cobj())\nPyNumber_Multiply = Function[[cobj, cobj], cobj](cobj())\nPyNumber_MatrixMultiply = Function[[cobj, cobj], cobj](cobj())\nPyNumber_FloorDivide = Function[[cobj, cobj], cobj](cobj())\nPyNumber_TrueDivide = Function[[cobj, cobj], cobj](cobj())\nPyNumber_Remainder = Function[[cobj, cobj], cobj](cobj())\nPyNumber_Divmod = Function[[cobj, cobj], cobj](cobj())\nPyNumber_Power = Function[[cobj, cobj, cobj], cobj](cobj())\nPyNumber_Negative = Function[[cobj], cobj](cobj())\nPyNumber_Positive = Function[[cobj], cobj](cobj())\nPyNumber_Absolute = Function[[cobj], cobj](cobj())\nPyNumber_Invert = Function[[cobj], cobj](cobj())\nPyNumber_Lshift = Function[[cobj, cobj], cobj](cobj())\nPyNumber_Rshift = Function[[cobj, cobj], cobj](cobj())\nPyNumber_And = Function[[cobj, cobj], cobj](cobj())\nPyNumber_Xor = Function[[cobj, cobj], cobj](cobj())\nPyNumber_Or = Function[[cobj, cobj], cobj](cobj())\nPyNumber_InPlaceAdd = Function[[cobj, cobj], cobj](cobj())\nPyNumber_InPlaceSubtract = Function[[cobj, cobj], cobj](cobj())\nPyNumber_InPlaceMultiply = Function[[cobj, cobj], cobj](cobj())\nPyNumber_InPlaceMatrixMultiply = Function[[cobj, cobj], cobj](cobj())\nPyNumber_InPlaceFloorDivide = Function[[cobj, cobj], cobj](cobj())\nPyNumber_InPlaceTrueDivide = Function[[cobj, cobj], cobj](cobj())\nPyNumber_InPlaceRemainder = Function[[cobj, cobj], cobj](cobj())\nPyNumber_InPlacePower = Function[[cobj, cobj, cobj], cobj](cobj())\nPyNumber_InPlaceLshift = Function[[cobj, cobj], cobj](cobj())\nPyNumber_InPlaceRshift = Function[[cobj, cobj], cobj](cobj())\nPyNumber_InPlaceAnd = Function[[cobj, cobj], cobj](cobj())\nPyNumber_InPlaceXor = Function[[cobj, cobj], cobj](cobj())\nPyNumber_InPlaceOr = Function[[cobj, cobj], cobj](cobj())\nPyNumber_Long = Function[[cobj], cobj](cobj())\nPyNumber_Float = Function[[cobj], cobj](cobj())\nPyNumber_Index = Function[[cobj], cobj](cobj())\n\n# object\nPyObject_Call = Function[[cobj, cobj, cobj], cobj](cobj())\nPyObject_CallNoArgs = Function[[cobj], cobj](cobj())\nPyObject_CallOneArg = Function[[cobj, cobj], cobj](cobj())\nPyObject_GetAttr = Function[[cobj, cobj], cobj](cobj())\nPyObject_GetAttrString = Function[[cobj, cobj], cobj](cobj())\nPyObject_GetIter = Function[[cobj], cobj](cobj())\nPyObject_HasAttrString = Function[[cobj, cobj], int](cobj())\nPyObject_IsTrue = Function[[cobj], i32](cobj())\nPyObject_Length = Function[[cobj], int](cobj())\nPyObject_LengthHint = Function[[cobj, int], int](cobj())\nPyObject_SetAttrString = Function[[cobj, cobj, cobj], cobj](cobj())\nPyObject_Str = Function[[cobj], cobj](cobj())\nPyObject_Repr = Function[[cobj], cobj](cobj())\nPyObject_Hash = Function[[cobj], int](cobj())\nPyObject_GetItem = Function[[cobj, cobj], cobj](cobj())\nPyObject_SetItem = Function[[cobj, cobj, cobj], int](cobj())\nPyObject_DelItem = Function[[cobj, cobj], int](cobj())\nPyObject_RichCompare = Function[[cobj, cobj, i32], cobj](cobj())\nPyObject_IsInstance = Function[[cobj, cobj], i32](cobj())\n\n# error handling\nPyErr_Fetch = Function[[Ptr[cobj], Ptr[cobj], Ptr[cobj]], NoneType](cobj())\nPyErr_NormalizeException = Function[[Ptr[cobj], Ptr[cobj], Ptr[cobj]], NoneType](cobj())\nPyErr_SetString = Function[[cobj, cobj], NoneType](cobj())\n\n# constants\nPy_None = cobj()\nPy_True = cobj()\nPy_False = cobj()\nPy_Ellipsis = cobj()\nPy_NotImplemented = cobj()\nPy_LT = 0\nPy_LE = 1\nPy_EQ = 2\nPy_NE = 3\nPy_GT = 4\nPy_GE = 5\n\n# types\nPyLong_Type = cobj()\nPyFloat_Type = cobj()\nPyBool_Type = cobj()\nPyUnicode_Type = cobj()\nPyBytes_Type = cobj()\nPyComplex_Type = cobj()\nPyList_Type = cobj()\nPyDict_Type = cobj()\nPySet_Type = cobj()\nPyTuple_Type = cobj()\nPySlice_Type = cobj()\nPyCapsule_Type = cobj()\n\n# exceptions\nPyExc_BaseException = cobj()\nPyExc_Exception = cobj()\nPyExc_NameError = cobj()\nPyExc_OSError = cobj()\nPyExc_IOError = cobj()\nPyExc_ValueError = cobj()\nPyExc_LookupError = cobj()\nPyExc_IndexError = cobj()\nPyExc_KeyError = cobj()\nPyExc_TypeError = cobj()\nPyExc_ArithmeticError = cobj()\nPyExc_ZeroDivisionError = cobj()\nPyExc_OverflowError = cobj()\nPyExc_AttributeError = cobj()\nPyExc_RuntimeError = cobj()\nPyExc_NotImplementedError = cobj()\nPyExc_StopIteration = cobj()\nPyExc_AssertionError = cobj()\nPyExc_EOFError = cobj()\nPyExc_SystemExit = cobj()\n\n_PY_MODULE_CACHE = Dict[str, pyobj]()\n\n_PY_INIT = \"\"\"\nimport io\n\nclsf = None\nclsa = None\nplt = None\ntry:\n    import matplotlib.figure\n    import matplotlib.pyplot\n    plt = matplotlib.pyplot\n    clsf = matplotlib.figure.Figure\n    clsa = matplotlib.artist.Artist\nexcept ModuleNotFoundError:\n    pass\n\ndef __codon_repr__(fig):\n    if clsf and isinstance(fig, clsf):\n        stream = io.StringIO()\n        fig.savefig(stream, format=\"svg\")\n        return 'image/svg+xml', stream.getvalue()\n    elif clsa and isinstance(fig, list) and all(\n        isinstance(i, clsa) for i in fig\n    ):\n        stream = io.StringIO()\n        plt.gcf().savefig(stream, format=\"svg\")\n        return 'image/svg+xml', stream.getvalue()\n    elif hasattr(fig, \"_repr_html_\"):\n        return 'text/html', fig._repr_html_()\n    else:\n        return 'text/plain', fig.__repr__()\n\"\"\"\n\n_PY_INITIALIZED = False\n\ndef init_handles_dlopen(py_handle: cobj):\n    global Py_DecRef\n    global Py_IncRef\n    global Py_Initialize\n    global PyImport_AddModule\n    global PyImport_AddModuleObject\n    global PyImport_ImportModule\n    global PyRun_SimpleString\n    global PyEval_GetGlobals\n    global PyEval_GetBuiltins\n    global PyOS_setsig\n    global PyLong_AsLong\n    global PyLong_FromLong\n    global PyFloat_AsDouble\n    global PyFloat_FromDouble\n    global PyBool_FromLong\n    global PyBytes_AsString\n    global PyBytes_Size\n    global PyList_New\n    global PyList_Size\n    global PyList_GetItem\n    global PyList_SetItem\n    global PyDict_New\n    global PyDict_Next\n    global PyDict_GetItem\n    global PyDict_GetItemString\n    global PyDict_SetItem\n    global PyDict_Size\n    global PySet_Add\n    global PySet_New\n    global PyTuple_New\n    global PyTuple_Size\n    global PyTuple_GetItem\n    global PyTuple_SetItem\n    global PyUnicode_AsEncodedString\n    global PyUnicode_DecodeFSDefaultAndSize\n    global PyUnicode_FromString\n    global PyComplex_FromDoubles\n    global PyComplex_RealAsDouble\n    global PyComplex_ImagAsDouble\n    global PyIter_Next\n    global PySlice_New\n    global PySlice_Unpack\n    global PyCapsule_New\n    global PyCapsule_GetPointer\n    global PyNumber_Add\n    global PyNumber_Subtract\n    global PyNumber_Multiply\n    global PyNumber_MatrixMultiply\n    global PyNumber_FloorDivide\n    global PyNumber_TrueDivide\n    global PyNumber_Remainder\n    global PyNumber_Divmod\n    global PyNumber_Power\n    global PyNumber_Negative\n    global PyNumber_Positive\n    global PyNumber_Absolute\n    global PyNumber_Invert\n    global PyNumber_Lshift\n    global PyNumber_Rshift\n    global PyNumber_And\n    global PyNumber_Xor\n    global PyNumber_Or\n    global PyNumber_InPlaceAdd\n    global PyNumber_InPlaceSubtract\n    global PyNumber_InPlaceMultiply\n    global PyNumber_InPlaceMatrixMultiply\n    global PyNumber_InPlaceFloorDivide\n    global PyNumber_InPlaceTrueDivide\n    global PyNumber_InPlaceRemainder\n    global PyNumber_InPlacePower\n    global PyNumber_InPlaceLshift\n    global PyNumber_InPlaceRshift\n    global PyNumber_InPlaceAnd\n    global PyNumber_InPlaceXor\n    global PyNumber_InPlaceOr\n    global PyNumber_Long\n    global PyNumber_Float\n    global PyNumber_Index\n    global PyObject_Call\n    global PyObject_CallNoArgs\n    global PyObject_CallOneArg\n    global PyObject_GetAttr\n    global PyObject_GetAttrString\n    global PyObject_GetIter\n    global PyObject_HasAttrString\n    global PyObject_IsTrue\n    global PyObject_Length\n    global PyObject_LengthHint\n    global PyObject_SetAttrString\n    global PyObject_Str\n    global PyObject_Repr\n    global PyObject_Hash\n    global PyObject_GetItem\n    global PyObject_SetItem\n    global PyObject_DelItem\n    global PyObject_RichCompare\n    global PyObject_IsInstance\n    global PyErr_Fetch\n    global PyErr_NormalizeException\n    global PyErr_SetString\n    global Py_None\n    global Py_True\n    global Py_False\n    global Py_Ellipsis\n    global Py_NotImplemented\n    global PyLong_Type\n    global PyFloat_Type\n    global PyBool_Type\n    global PyUnicode_Type\n    global PyBytes_Type\n    global PyComplex_Type\n    global PyList_Type\n    global PyDict_Type\n    global PySet_Type\n    global PyTuple_Type\n    global PySlice_Type\n    global PyCapsule_Type\n    global PyExc_BaseException\n    global PyExc_Exception\n    global PyExc_NameError\n    global PyExc_OSError\n    global PyExc_IOError\n    global PyExc_ValueError\n    global PyExc_LookupError\n    global PyExc_IndexError\n    global PyExc_KeyError\n    global PyExc_TypeError\n    global PyExc_ArithmeticError\n    global PyExc_ZeroDivisionError\n    global PyExc_OverflowError\n    global PyExc_AttributeError\n    global PyExc_RuntimeError\n    global PyExc_NotImplementedError\n    global PyExc_StopIteration\n    global PyExc_AssertionError\n    global PyExc_EOFError\n    global PyExc_SystemExit\n\n    Py_DecRef = dlsym(py_handle, \"Py_DecRef\")\n    Py_IncRef = dlsym(py_handle, \"Py_IncRef\")\n    Py_Initialize = dlsym(py_handle, \"Py_Initialize\")\n    PyImport_AddModule = dlsym(py_handle, \"PyImport_AddModule\")\n    PyImport_AddModuleObject = dlsym(py_handle, \"PyImport_AddModuleObject\")\n    PyImport_ImportModule = dlsym(py_handle, \"PyImport_ImportModule\")\n    PyRun_SimpleString = dlsym(py_handle, \"PyRun_SimpleString\")\n    PyEval_GetGlobals = dlsym(py_handle, \"PyEval_GetGlobals\")\n    PyEval_GetBuiltins = dlsym(py_handle, \"PyEval_GetBuiltins\")\n    PyOS_setsig = dlsym(py_handle, \"PyOS_setsig\")\n    PyLong_AsLong = dlsym(py_handle, \"PyLong_AsLong\")\n    PyLong_FromLong = dlsym(py_handle, \"PyLong_FromLong\")\n    PyFloat_AsDouble = dlsym(py_handle, \"PyFloat_AsDouble\")\n    PyFloat_FromDouble = dlsym(py_handle, \"PyFloat_FromDouble\")\n    PyBool_FromLong = dlsym(py_handle, \"PyBool_FromLong\")\n    PyBytes_AsString = dlsym(py_handle, \"PyBytes_AsString\")\n    PyBytes_Size = dlsym(py_handle, \"PyBytes_Size\")\n    PyList_New = dlsym(py_handle, \"PyList_New\")\n    PyList_Size = dlsym(py_handle, \"PyList_Size\")\n    PyList_GetItem = dlsym(py_handle, \"PyList_GetItem\")\n    PyList_SetItem = dlsym(py_handle, \"PyList_SetItem\")\n    PyDict_New = dlsym(py_handle, \"PyDict_New\")\n    PyDict_Next = dlsym(py_handle, \"PyDict_Next\")\n    PyDict_GetItem = dlsym(py_handle, \"PyDict_GetItem\")\n    PyDict_GetItemString = dlsym(py_handle, \"PyDict_GetItemString\")\n    PyDict_SetItem = dlsym(py_handle, \"PyDict_SetItem\")\n    PyDict_Size = dlsym(py_handle, \"PyDict_Size\")\n    PySet_Add = dlsym(py_handle, \"PySet_Add\")\n    PySet_New = dlsym(py_handle, \"PySet_New\")\n    PyTuple_New = dlsym(py_handle, \"PyTuple_New\")\n    PyTuple_Size = dlsym(py_handle, \"PyTuple_Size\")\n    PyTuple_GetItem = dlsym(py_handle, \"PyTuple_GetItem\")\n    PyTuple_SetItem = dlsym(py_handle, \"PyTuple_SetItem\")\n    PyUnicode_AsEncodedString = dlsym(py_handle, \"PyUnicode_AsEncodedString\")\n    PyUnicode_DecodeFSDefaultAndSize = dlsym(py_handle, \"PyUnicode_DecodeFSDefaultAndSize\")\n    PyUnicode_FromString = dlsym(py_handle, \"PyUnicode_FromString\")\n    PyComplex_FromDoubles = dlsym(py_handle, \"PyComplex_FromDoubles\")\n    PyComplex_RealAsDouble = dlsym(py_handle, \"PyComplex_RealAsDouble\")\n    PyComplex_ImagAsDouble = dlsym(py_handle, \"PyComplex_ImagAsDouble\")\n    PyIter_Next = dlsym(py_handle, \"PyIter_Next\")\n    PySlice_New = dlsym(py_handle, \"PySlice_New\")\n    PySlice_Unpack = dlsym(py_handle, \"PySlice_Unpack\")\n    PyCapsule_New = dlsym(py_handle, \"PyCapsule_New\")\n    PyCapsule_GetPointer = dlsym(py_handle, \"PyCapsule_GetPointer\")\n    PyNumber_Add = dlsym(py_handle, \"PyNumber_Add\")\n    PyNumber_Subtract = dlsym(py_handle, \"PyNumber_Subtract\")\n    PyNumber_Multiply = dlsym(py_handle, \"PyNumber_Multiply\")\n    PyNumber_MatrixMultiply = dlsym(py_handle, \"PyNumber_MatrixMultiply\")\n    PyNumber_FloorDivide = dlsym(py_handle, \"PyNumber_FloorDivide\")\n    PyNumber_TrueDivide = dlsym(py_handle, \"PyNumber_TrueDivide\")\n    PyNumber_Remainder = dlsym(py_handle, \"PyNumber_Remainder\")\n    PyNumber_Divmod = dlsym(py_handle, \"PyNumber_Divmod\")\n    PyNumber_Power = dlsym(py_handle, \"PyNumber_Power\")\n    PyNumber_Negative = dlsym(py_handle, \"PyNumber_Negative\")\n    PyNumber_Positive = dlsym(py_handle, \"PyNumber_Positive\")\n    PyNumber_Absolute = dlsym(py_handle, \"PyNumber_Absolute\")\n    PyNumber_Invert = dlsym(py_handle, \"PyNumber_Invert\")\n    PyNumber_Lshift = dlsym(py_handle, \"PyNumber_Lshift\")\n    PyNumber_Rshift = dlsym(py_handle, \"PyNumber_Rshift\")\n    PyNumber_And = dlsym(py_handle, \"PyNumber_And\")\n    PyNumber_Xor = dlsym(py_handle, \"PyNumber_Xor\")\n    PyNumber_Or = dlsym(py_handle, \"PyNumber_Or\")\n    PyNumber_InPlaceAdd = dlsym(py_handle, \"PyNumber_InPlaceAdd\")\n    PyNumber_InPlaceSubtract = dlsym(py_handle, \"PyNumber_InPlaceSubtract\")\n    PyNumber_InPlaceMultiply = dlsym(py_handle, \"PyNumber_InPlaceMultiply\")\n    PyNumber_InPlaceMatrixMultiply = dlsym(py_handle, \"PyNumber_InPlaceMatrixMultiply\")\n    PyNumber_InPlaceFloorDivide = dlsym(py_handle, \"PyNumber_InPlaceFloorDivide\")\n    PyNumber_InPlaceTrueDivide = dlsym(py_handle, \"PyNumber_InPlaceTrueDivide\")\n    PyNumber_InPlaceRemainder = dlsym(py_handle, \"PyNumber_InPlaceRemainder\")\n    PyNumber_InPlacePower = dlsym(py_handle, \"PyNumber_InPlacePower\")\n    PyNumber_InPlaceLshift = dlsym(py_handle, \"PyNumber_InPlaceLshift\")\n    PyNumber_InPlaceRshift = dlsym(py_handle, \"PyNumber_InPlaceRshift\")\n    PyNumber_InPlaceAnd = dlsym(py_handle, \"PyNumber_InPlaceAnd\")\n    PyNumber_InPlaceXor = dlsym(py_handle, \"PyNumber_InPlaceXor\")\n    PyNumber_InPlaceOr = dlsym(py_handle, \"PyNumber_InPlaceOr\")\n    PyNumber_Long = dlsym(py_handle, \"PyNumber_Long\")\n    PyNumber_Float = dlsym(py_handle, \"PyNumber_Float\")\n    PyNumber_Index = dlsym(py_handle, \"PyNumber_Index\")\n    PyObject_Call = dlsym(py_handle, \"PyObject_Call\")\n    PyObject_CallNoArgs = dlsym(py_handle, \"PyObject_CallNoArgs\")\n    PyObject_CallOneArg = dlsym(py_handle, \"PyObject_CallOneArg\")\n    PyObject_GetAttr = dlsym(py_handle, \"PyObject_GetAttr\")\n    PyObject_GetAttrString = dlsym(py_handle, \"PyObject_GetAttrString\")\n    PyObject_GetIter = dlsym(py_handle, \"PyObject_GetIter\")\n    PyObject_HasAttrString = dlsym(py_handle, \"PyObject_HasAttrString\")\n    PyObject_IsTrue = dlsym(py_handle, \"PyObject_IsTrue\")\n    PyObject_Length = dlsym(py_handle, \"PyObject_Length\")\n    PyObject_LengthHint = dlsym(py_handle, \"PyObject_LengthHint\")\n    PyObject_SetAttrString = dlsym(py_handle, \"PyObject_SetAttrString\")\n    PyObject_Str = dlsym(py_handle, \"PyObject_Str\")\n    PyObject_Repr = dlsym(py_handle, \"PyObject_Repr\")\n    PyObject_Hash = dlsym(py_handle, \"PyObject_Hash\")\n    PyObject_GetItem = dlsym(py_handle, \"PyObject_GetItem\")\n    PyObject_SetItem = dlsym(py_handle, \"PyObject_SetItem\")\n    PyObject_DelItem = dlsym(py_handle, \"PyObject_DelItem\")\n    PyObject_RichCompare = dlsym(py_handle, \"PyObject_RichCompare\")\n    PyObject_IsInstance = dlsym(py_handle, \"PyObject_IsInstance\")\n    PyErr_Fetch = dlsym(py_handle, \"PyErr_Fetch\")\n    PyErr_NormalizeException = dlsym(py_handle, \"PyErr_NormalizeException\")\n    PyErr_SetString = dlsym(py_handle, \"PyErr_SetString\")\n    Py_None = dlsym(py_handle, \"_Py_NoneStruct\")\n    Py_True = dlsym(py_handle, \"_Py_TrueStruct\")\n    Py_False = dlsym(py_handle, \"_Py_FalseStruct\")\n    Py_Ellipsis = dlsym(py_handle, \"_Py_EllipsisObject\")\n    Py_NotImplemented = dlsym(py_handle, \"_Py_NotImplementedStruct\")\n    PyLong_Type = dlsym(py_handle, \"PyLong_Type\")\n    PyFloat_Type = dlsym(py_handle, \"PyFloat_Type\")\n    PyBool_Type = dlsym(py_handle, \"PyBool_Type\")\n    PyUnicode_Type = dlsym(py_handle, \"PyUnicode_Type\")\n    PyBytes_Type = dlsym(py_handle, \"PyBytes_Type\")\n    PyComplex_Type = dlsym(py_handle, \"PyComplex_Type\")\n    PyList_Type = dlsym(py_handle, \"PyList_Type\")\n    PyDict_Type = dlsym(py_handle, \"PyDict_Type\")\n    PySet_Type = dlsym(py_handle, \"PySet_Type\")\n    PyTuple_Type = dlsym(py_handle, \"PyTuple_Type\")\n    PySlice_Type = dlsym(py_handle, \"PySlice_Type\")\n    PyCapsule_Type = dlsym(py_handle, \"PyCapsule_Type\")\n    PyExc_BaseException = Ptr[cobj](dlsym(py_handle, \"PyExc_BaseException\", cobj))[0]\n    PyExc_Exception = Ptr[cobj](dlsym(py_handle, \"PyExc_Exception\", cobj))[0]\n    PyExc_NameError = Ptr[cobj](dlsym(py_handle, \"PyExc_NameError\", cobj))[0]\n    PyExc_OSError = Ptr[cobj](dlsym(py_handle, \"PyExc_OSError\", cobj))[0]\n    PyExc_IOError = Ptr[cobj](dlsym(py_handle, \"PyExc_IOError\", cobj))[0]\n    PyExc_ValueError = Ptr[cobj](dlsym(py_handle, \"PyExc_ValueError\", cobj))[0]\n    PyExc_LookupError = Ptr[cobj](dlsym(py_handle, \"PyExc_LookupError\", cobj))[0]\n    PyExc_IndexError = Ptr[cobj](dlsym(py_handle, \"PyExc_IndexError\", cobj))[0]\n    PyExc_KeyError = Ptr[cobj](dlsym(py_handle, \"PyExc_KeyError\", cobj))[0]\n    PyExc_TypeError = Ptr[cobj](dlsym(py_handle, \"PyExc_TypeError\", cobj))[0]\n    PyExc_ArithmeticError = Ptr[cobj](dlsym(py_handle, \"PyExc_ArithmeticError\", cobj))[0]\n    PyExc_ZeroDivisionError = Ptr[cobj](dlsym(py_handle, \"PyExc_ZeroDivisionError\", cobj))[0]\n    PyExc_OverflowError = Ptr[cobj](dlsym(py_handle, \"PyExc_OverflowError\", cobj))[0]\n    PyExc_AttributeError = Ptr[cobj](dlsym(py_handle, \"PyExc_AttributeError\", cobj))[0]\n    PyExc_RuntimeError = Ptr[cobj](dlsym(py_handle, \"PyExc_RuntimeError\", cobj))[0]\n    PyExc_NotImplementedError = Ptr[cobj](dlsym(py_handle, \"PyExc_NotImplementedError\", cobj))[0]\n    PyExc_StopIteration = Ptr[cobj](dlsym(py_handle, \"PyExc_StopIteration\", cobj))[0]\n    PyExc_AssertionError = Ptr[cobj](dlsym(py_handle, \"PyExc_AssertionError\", cobj))[0]\n    PyExc_EOFError = Ptr[cobj](dlsym(py_handle, \"PyExc_EOFError\", cobj))[0]\n    PyExc_SystemExit = Ptr[cobj](dlsym(py_handle, \"PyExc_SystemExit\", cobj))[0]\n\ndef init_handles_static():\n    from C import Py_DecRef(cobj) as _Py_DecRef\n    from C import Py_IncRef(cobj) as _Py_IncRef\n    from C import Py_Initialize() as _Py_Initialize\n    from C import PyImport_AddModule(cobj) -> cobj as _PyImport_AddModule\n    from C import PyImport_AddModuleObject(cobj) -> cobj as _PyImport_AddModuleObject\n    from C import PyImport_ImportModule(cobj) -> cobj as _PyImport_ImportModule\n    from C import PyRun_SimpleString(cobj) as _PyRun_SimpleString\n    from C import PyEval_GetGlobals() -> cobj as _PyEval_GetGlobals\n    from C import PyEval_GetBuiltins() -> cobj as _PyEval_GetBuiltins\n    from C import PyOS_setsig(i32, cobj) -> cobj as _PyOS_setsig\n    from C import PyLong_AsLong(cobj) -> int as _PyLong_AsLong\n    from C import PyLong_FromLong(int) -> cobj as _PyLong_FromLong\n    from C import PyFloat_AsDouble(cobj) -> float as _PyFloat_AsDouble\n    from C import PyFloat_FromDouble(float) -> cobj as _PyFloat_FromDouble\n    from C import PyBool_FromLong(int) -> cobj as _PyBool_FromLong\n    from C import PyBytes_AsString(cobj) -> cobj as _PyBytes_AsString\n    from C import PyBytes_Size(cobj) -> int as _PyBytes_Size\n    from C import PyList_New(int) -> cobj as _PyList_New\n    from C import PyList_Size(cobj) -> int as _PyList_Size\n    from C import PyList_GetItem(cobj, int) -> cobj as _PyList_GetItem\n    from C import PyList_SetItem(cobj, int, cobj) -> cobj as _PyList_SetItem\n    from C import PyDict_New() -> cobj as _PyDict_New\n    from C import PyDict_Next(cobj, Ptr[int], Ptr[cobj], Ptr[cobj]) -> int as _PyDict_Next\n    from C import PyDict_GetItem(cobj, cobj) -> cobj as _PyDict_GetItem\n    from C import PyDict_GetItemString(cobj, cobj) -> cobj as _PyDict_GetItemString\n    from C import PyDict_SetItem(cobj, cobj, cobj) -> cobj as _PyDict_SetItem\n    from C import PyDict_Size(cobj) -> int as _PyDict_Size\n    from C import PySet_Add(cobj, cobj) -> cobj as _PySet_Add\n    from C import PySet_New(cobj) -> cobj as _PySet_New\n    from C import PyTuple_New(int) -> cobj as _PyTuple_New\n    from C import PyTuple_Size(cobj) -> int as _PyTuple_Size\n    from C import PyTuple_GetItem(cobj, int) -> cobj as _PyTuple_GetItem\n    from C import PyTuple_SetItem(cobj, int, cobj) -> i32 as _PyTuple_SetItem\n    from C import PyUnicode_AsEncodedString(cobj, cobj, cobj) -> cobj as _PyUnicode_AsEncodedString\n    from C import PyUnicode_DecodeFSDefaultAndSize(cobj, int) -> cobj as _PyUnicode_DecodeFSDefaultAndSize\n    from C import PyUnicode_FromString(cobj) -> cobj as _PyUnicode_FromString\n    from C import PyComplex_FromDoubles(float, float) -> cobj as _PyComplex_FromDoubles\n    from C import PyComplex_RealAsDouble(cobj) -> float as _PyComplex_RealAsDouble\n    from C import PyComplex_ImagAsDouble(cobj) -> float as _PyComplex_ImagAsDouble\n    from C import PyIter_Next(cobj) -> cobj as _PyIter_Next\n    from C import PySlice_New(cobj, cobj, cobj) -> cobj as _PySlice_New\n    from C import PySlice_Unpack(cobj, Ptr[int], Ptr[int], Ptr[int]) -> int as _PySlice_Unpack\n    from C import PyCapsule_New(cobj, cobj, cobj) -> cobj as _PyCapsule_New\n    from C import PyCapsule_GetPointer(cobj, cobj) -> cobj as _PyCapsule_GetPointer\n    from C import PyNumber_Add(cobj, cobj) -> cobj as _PyNumber_Add\n    from C import PyNumber_Subtract(cobj, cobj) -> cobj as _PyNumber_Subtract\n    from C import PyNumber_Multiply(cobj, cobj) -> cobj as _PyNumber_Multiply\n    from C import PyNumber_MatrixMultiply(cobj, cobj) -> cobj as _PyNumber_MatrixMultiply\n    from C import PyNumber_FloorDivide(cobj, cobj) -> cobj as _PyNumber_FloorDivide\n    from C import PyNumber_TrueDivide(cobj, cobj) -> cobj as _PyNumber_TrueDivide\n    from C import PyNumber_Remainder(cobj, cobj) -> cobj as _PyNumber_Remainder\n    from C import PyNumber_Divmod(cobj, cobj) -> cobj as _PyNumber_Divmod\n    from C import PyNumber_Power(cobj, cobj, cobj) -> cobj as _PyNumber_Power\n    from C import PyNumber_Negative(cobj) -> cobj as _PyNumber_Negative\n    from C import PyNumber_Positive(cobj) -> cobj as _PyNumber_Positive\n    from C import PyNumber_Absolute(cobj) -> cobj as _PyNumber_Absolute\n    from C import PyNumber_Invert(cobj) -> cobj as _PyNumber_Invert\n    from C import PyNumber_Lshift(cobj, cobj) -> cobj as _PyNumber_Lshift\n    from C import PyNumber_Rshift(cobj, cobj) -> cobj as _PyNumber_Rshift\n    from C import PyNumber_And(cobj, cobj) -> cobj as _PyNumber_And\n    from C import PyNumber_Xor(cobj, cobj) -> cobj as _PyNumber_Xor\n    from C import PyNumber_Or(cobj, cobj) -> cobj as _PyNumber_Or\n    from C import PyNumber_InPlaceAdd(cobj, cobj) -> cobj as _PyNumber_InPlaceAdd\n    from C import PyNumber_InPlaceSubtract(cobj, cobj) -> cobj as _PyNumber_InPlaceSubtract\n    from C import PyNumber_InPlaceMultiply(cobj, cobj) -> cobj as _PyNumber_InPlaceMultiply\n    from C import PyNumber_InPlaceMatrixMultiply(cobj, cobj) -> cobj as _PyNumber_InPlaceMatrixMultiply\n    from C import PyNumber_InPlaceFloorDivide(cobj, cobj) -> cobj as _PyNumber_InPlaceFloorDivide\n    from C import PyNumber_InPlaceTrueDivide(cobj, cobj) -> cobj as _PyNumber_InPlaceTrueDivide\n    from C import PyNumber_InPlaceRemainder(cobj, cobj) -> cobj as _PyNumber_InPlaceRemainder\n    from C import PyNumber_InPlacePower(cobj, cobj, cobj) -> cobj as _PyNumber_InPlacePower\n    from C import PyNumber_InPlaceLshift(cobj, cobj) -> cobj as _PyNumber_InPlaceLshift\n    from C import PyNumber_InPlaceRshift(cobj, cobj) -> cobj as _PyNumber_InPlaceRshift\n    from C import PyNumber_InPlaceAnd(cobj, cobj) -> cobj as _PyNumber_InPlaceAnd\n    from C import PyNumber_InPlaceXor(cobj, cobj) -> cobj as _PyNumber_InPlaceXor\n    from C import PyNumber_InPlaceOr(cobj, cobj) -> cobj as _PyNumber_InPlaceOr\n    from C import PyNumber_Long(cobj) -> cobj as _PyNumber_Long\n    from C import PyNumber_Float(cobj) -> cobj as _PyNumber_Float\n    from C import PyNumber_Index(cobj) -> cobj as _PyNumber_Index\n    from C import PyObject_Call(cobj, cobj, cobj) -> cobj as _PyObject_Call\n    from C import PyObject_CallNoArgs(cobj) -> cobj as _PyObject_CallNoArgs\n    from C import PyObject_CallOneArg(cobj, cobj) -> cobj as _PyObject_CallOneArg\n    from C import PyObject_GetAttr(cobj, cobj) -> cobj as _PyObject_GetAttr\n    from C import PyObject_GetAttrString(cobj, cobj) -> cobj as _PyObject_GetAttrString\n    from C import PyObject_GetIter(cobj) -> cobj as _PyObject_GetIter\n    from C import PyObject_HasAttrString(cobj, cobj) -> int as _PyObject_HasAttrString\n    from C import PyObject_IsTrue(cobj) -> i32 as _PyObject_IsTrue\n    from C import PyObject_Length(cobj) -> int as _PyObject_Length\n    from C import PyObject_LengthHint(cobj, int) -> int as _PyObject_LengthHint\n    from C import PyObject_SetAttrString(cobj, cobj, cobj) -> cobj as _PyObject_SetAttrString\n    from C import PyObject_Str(cobj) -> cobj as _PyObject_Str\n    from C import PyObject_Repr(cobj) -> cobj as _PyObject_Repr\n    from C import PyObject_Hash(cobj) -> int as _PyObject_Hash\n    from C import PyObject_GetItem(cobj, cobj) -> cobj as _PyObject_GetItem\n    from C import PyObject_SetItem(cobj, cobj, cobj) -> int as _PyObject_SetItem\n    from C import PyObject_DelItem(cobj, cobj) -> int as _PyObject_DelItem\n    from C import PyObject_RichCompare(cobj, cobj, i32) -> cobj as _PyObject_RichCompare\n    from C import PyObject_IsInstance(cobj, cobj) -> i32 as _PyObject_IsInstance\n    from C import PyErr_Fetch(Ptr[cobj], Ptr[cobj], Ptr[cobj]) as _PyErr_Fetch\n    from C import PyErr_NormalizeException(Ptr[cobj], Ptr[cobj], Ptr[cobj]) as _PyErr_NormalizeException\n    from C import PyErr_SetString(cobj, cobj) as _PyErr_SetString\n    from C import _Py_NoneStruct: cobj\n    from C import _Py_TrueStruct: cobj\n    from C import _Py_FalseStruct: cobj\n    from C import _Py_EllipsisObject: cobj\n    from C import _Py_NotImplementedStruct: cobj\n    from C import PyLong_Type: cobj as _PyLong_Type\n    from C import PyFloat_Type: cobj as _PyFloat_Type\n    from C import PyBool_Type: cobj as _PyBool_Type\n    from C import PyUnicode_Type: cobj as _PyUnicode_Type\n    from C import PyBytes_Type: cobj as _PyBytes_Type\n    from C import PyComplex_Type: cobj as _PyComplex_Type\n    from C import PyList_Type: cobj as _PyList_Type\n    from C import PyDict_Type: cobj as _PyDict_Type\n    from C import PySet_Type: cobj as _PySet_Type\n    from C import PyTuple_Type: cobj as _PyTuple_Type\n    from C import PySlice_Type: cobj as _PySlice_Type\n    from C import PyCapsule_Type: cobj as _PyCapsule_Type\n    from C import PyExc_BaseException: cobj as _PyExc_BaseException\n    from C import PyExc_Exception: cobj as _PyExc_Exception\n    from C import PyExc_NameError: cobj as _PyExc_NameError\n    from C import PyExc_OSError: cobj as _PyExc_OSError\n    from C import PyExc_IOError: cobj as _PyExc_IOError\n    from C import PyExc_ValueError: cobj as _PyExc_ValueError\n    from C import PyExc_LookupError: cobj as _PyExc_LookupError\n    from C import PyExc_IndexError: cobj as _PyExc_IndexError\n    from C import PyExc_KeyError: cobj as _PyExc_KeyError\n    from C import PyExc_TypeError: cobj as _PyExc_TypeError\n    from C import PyExc_ArithmeticError: cobj as _PyExc_ArithmeticError\n    from C import PyExc_ZeroDivisionError: cobj as _PyExc_ZeroDivisionError\n    from C import PyExc_OverflowError: cobj as _PyExc_OverflowError\n    from C import PyExc_AttributeError: cobj as _PyExc_AttributeError\n    from C import PyExc_RuntimeError: cobj as _PyExc_RuntimeError\n    from C import PyExc_NotImplementedError: cobj as _PyExc_NotImplementedError\n    from C import PyExc_StopIteration: cobj as _PyExc_StopIteration\n    from C import PyExc_AssertionError: cobj as _PyExc_AssertionError\n    from C import PyExc_EOFError: cobj as _PyExc_EOFError\n    from C import PyExc_SystemExit: cobj as _PyExc_SystemExit\n\n    global Py_DecRef\n    global Py_IncRef\n    global Py_Initialize\n    global PyImport_AddModule\n    global PyImport_AddModuleObject\n    global PyImport_ImportModule\n    global PyRun_SimpleString\n    global PyEval_GetGlobals\n    global PyEval_GetBuiltins\n    global PyOS_setsig\n    global PyLong_AsLong\n    global PyLong_FromLong\n    global PyFloat_AsDouble\n    global PyFloat_FromDouble\n    global PyBool_FromLong\n    global PyBytes_AsString\n    global PyBytes_Size\n    global PyList_New\n    global PyList_Size\n    global PyList_GetItem\n    global PyList_SetItem\n    global PyDict_New\n    global PyDict_Next\n    global PyDict_GetItem\n    global PyDict_GetItemString\n    global PyDict_SetItem\n    global PyDict_Size\n    global PySet_Add\n    global PySet_New\n    global PyTuple_New\n    global PyTuple_Size\n    global PyTuple_GetItem\n    global PyTuple_SetItem\n    global PyUnicode_AsEncodedString\n    global PyUnicode_DecodeFSDefaultAndSize\n    global PyUnicode_FromString\n    global PyComplex_FromDoubles\n    global PyComplex_RealAsDouble\n    global PyComplex_ImagAsDouble\n    global PyIter_Next\n    global PySlice_New\n    global PySlice_Unpack\n    global PyCapsule_New\n    global PyCapsule_GetPointer\n    global PyNumber_Add\n    global PyNumber_Subtract\n    global PyNumber_Multiply\n    global PyNumber_MatrixMultiply\n    global PyNumber_FloorDivide\n    global PyNumber_TrueDivide\n    global PyNumber_Remainder\n    global PyNumber_Divmod\n    global PyNumber_Power\n    global PyNumber_Negative\n    global PyNumber_Positive\n    global PyNumber_Absolute\n    global PyNumber_Invert\n    global PyNumber_Lshift\n    global PyNumber_Rshift\n    global PyNumber_And\n    global PyNumber_Xor\n    global PyNumber_Or\n    global PyNumber_InPlaceAdd\n    global PyNumber_InPlaceSubtract\n    global PyNumber_InPlaceMultiply\n    global PyNumber_InPlaceMatrixMultiply\n    global PyNumber_InPlaceFloorDivide\n    global PyNumber_InPlaceTrueDivide\n    global PyNumber_InPlaceRemainder\n    global PyNumber_InPlacePower\n    global PyNumber_InPlaceLshift\n    global PyNumber_InPlaceRshift\n    global PyNumber_InPlaceAnd\n    global PyNumber_InPlaceXor\n    global PyNumber_InPlaceOr\n    global PyNumber_Long\n    global PyNumber_Float\n    global PyNumber_Index\n    global PyObject_Call\n    global PyObject_CallNoArgs\n    global PyObject_CallOneArg\n    global PyObject_GetAttr\n    global PyObject_GetAttrString\n    global PyObject_GetIter\n    global PyObject_HasAttrString\n    global PyObject_IsTrue\n    global PyObject_Length\n    global PyObject_LengthHint\n    global PyObject_SetAttrString\n    global PyObject_Str\n    global PyObject_Repr\n    global PyObject_Hash\n    global PyObject_GetItem\n    global PyObject_SetItem\n    global PyObject_DelItem\n    global PyObject_RichCompare\n    global PyObject_IsInstance\n    global PyErr_Fetch\n    global PyErr_NormalizeException\n    global PyErr_SetString\n    global Py_None\n    global Py_True\n    global Py_False\n    global Py_Ellipsis\n    global Py_NotImplemented\n    global PyLong_Type\n    global PyFloat_Type\n    global PyBool_Type\n    global PyUnicode_Type\n    global PyBytes_Type\n    global PyComplex_Type\n    global PyList_Type\n    global PyDict_Type\n    global PySet_Type\n    global PyTuple_Type\n    global PySlice_Type\n    global PyCapsule_Type\n    global PyExc_BaseException\n    global PyExc_Exception\n    global PyExc_NameError\n    global PyExc_OSError\n    global PyExc_IOError\n    global PyExc_ValueError\n    global PyExc_LookupError\n    global PyExc_IndexError\n    global PyExc_KeyError\n    global PyExc_TypeError\n    global PyExc_ArithmeticError\n    global PyExc_ZeroDivisionError\n    global PyExc_OverflowError\n    global PyExc_AttributeError\n    global PyExc_RuntimeError\n    global PyExc_NotImplementedError\n    global PyExc_StopIteration\n    global PyExc_AssertionError\n    global PyExc_EOFError\n    global PyExc_SystemExit\n\n    Py_DecRef = _Py_DecRef\n    Py_IncRef = _Py_IncRef\n    Py_Initialize = _Py_Initialize\n    PyImport_AddModule = _PyImport_AddModule\n    PyImport_AddModuleObject = _PyImport_AddModuleObject\n    PyImport_ImportModule = _PyImport_ImportModule\n    PyRun_SimpleString = _PyRun_SimpleString\n    PyEval_GetGlobals = _PyEval_GetGlobals\n    PyEval_GetBuiltins = _PyEval_GetBuiltins\n    PyOS_setsig = _PyOS_setsig\n    PyLong_AsLong = _PyLong_AsLong\n    PyLong_FromLong = _PyLong_FromLong\n    PyFloat_AsDouble = _PyFloat_AsDouble\n    PyFloat_FromDouble = _PyFloat_FromDouble\n    PyBool_FromLong = _PyBool_FromLong\n    PyBytes_AsString = _PyBytes_AsString\n    PyBytes_Size = _PyBytes_Size\n    PyList_New = _PyList_New\n    PyList_Size = _PyList_Size\n    PyList_GetItem = _PyList_GetItem\n    PyList_SetItem = _PyList_SetItem\n    PyDict_New = _PyDict_New\n    PyDict_Next = _PyDict_Next\n    PyDict_GetItem = _PyDict_GetItem\n    PyDict_GetItemString = _PyDict_GetItemString\n    PyDict_SetItem = _PyDict_SetItem\n    PyDict_Size = _PyDict_Size\n    PySet_Add = _PySet_Add\n    PySet_New = _PySet_New\n    PyTuple_New = _PyTuple_New\n    PyTuple_Size = _PyTuple_Size\n    PyTuple_GetItem = _PyTuple_GetItem\n    PyTuple_SetItem = _PyTuple_SetItem\n    PyUnicode_AsEncodedString = _PyUnicode_AsEncodedString\n    PyUnicode_DecodeFSDefaultAndSize = _PyUnicode_DecodeFSDefaultAndSize\n    PyUnicode_FromString = _PyUnicode_FromString\n    PyComplex_FromDoubles = _PyComplex_FromDoubles\n    PyComplex_RealAsDouble = _PyComplex_RealAsDouble\n    PyComplex_ImagAsDouble = _PyComplex_ImagAsDouble\n    PyIter_Next = _PyIter_Next\n    PySlice_New = _PySlice_New\n    PySlice_Unpack = _PySlice_Unpack\n    PyCapsule_New = _PyCapsule_New\n    PyCapsule_GetPointer = _PyCapsule_GetPointer\n    PyNumber_Add = _PyNumber_Add\n    PyNumber_Subtract = _PyNumber_Subtract\n    PyNumber_Multiply = _PyNumber_Multiply\n    PyNumber_MatrixMultiply = _PyNumber_MatrixMultiply\n    PyNumber_FloorDivide = _PyNumber_FloorDivide\n    PyNumber_TrueDivide = _PyNumber_TrueDivide\n    PyNumber_Remainder = _PyNumber_Remainder\n    PyNumber_Divmod = _PyNumber_Divmod\n    PyNumber_Power = _PyNumber_Power\n    PyNumber_Negative = _PyNumber_Negative\n    PyNumber_Positive = _PyNumber_Positive\n    PyNumber_Absolute = _PyNumber_Absolute\n    PyNumber_Invert = _PyNumber_Invert\n    PyNumber_Lshift = _PyNumber_Lshift\n    PyNumber_Rshift = _PyNumber_Rshift\n    PyNumber_And = _PyNumber_And\n    PyNumber_Xor = _PyNumber_Xor\n    PyNumber_Or = _PyNumber_Or\n    PyNumber_InPlaceAdd = _PyNumber_InPlaceAdd\n    PyNumber_InPlaceSubtract = _PyNumber_InPlaceSubtract\n    PyNumber_InPlaceMultiply = _PyNumber_InPlaceMultiply\n    PyNumber_InPlaceMatrixMultiply = _PyNumber_InPlaceMatrixMultiply\n    PyNumber_InPlaceFloorDivide = _PyNumber_InPlaceFloorDivide\n    PyNumber_InPlaceTrueDivide = _PyNumber_InPlaceTrueDivide\n    PyNumber_InPlaceRemainder = _PyNumber_InPlaceRemainder\n    PyNumber_InPlacePower = _PyNumber_InPlacePower\n    PyNumber_InPlaceLshift = _PyNumber_InPlaceLshift\n    PyNumber_InPlaceRshift = _PyNumber_InPlaceRshift\n    PyNumber_InPlaceAnd = _PyNumber_InPlaceAnd\n    PyNumber_InPlaceXor = _PyNumber_InPlaceXor\n    PyNumber_InPlaceOr = _PyNumber_InPlaceOr\n    PyNumber_Long = _PyNumber_Long\n    PyNumber_Float = _PyNumber_Float\n    PyNumber_Index = _PyNumber_Index\n    PyObject_Call = _PyObject_Call\n    PyObject_CallNoArgs = _PyObject_CallNoArgs\n    PyObject_CallOneArg = _PyObject_CallOneArg\n    PyObject_GetAttr = _PyObject_GetAttr\n    PyObject_GetAttrString = _PyObject_GetAttrString\n    PyObject_GetIter = _PyObject_GetIter\n    PyObject_HasAttrString = _PyObject_HasAttrString\n    PyObject_IsTrue = _PyObject_IsTrue\n    PyObject_Length = _PyObject_Length\n    PyObject_LengthHint = _PyObject_LengthHint\n    PyObject_SetAttrString = _PyObject_SetAttrString\n    PyObject_Str = _PyObject_Str\n    PyObject_Repr = _PyObject_Repr\n    PyObject_Hash = _PyObject_Hash\n    PyObject_GetItem = _PyObject_GetItem\n    PyObject_SetItem = _PyObject_SetItem\n    PyObject_DelItem = _PyObject_DelItem\n    PyObject_RichCompare = _PyObject_RichCompare\n    PyObject_IsInstance = _PyObject_IsInstance\n    PyErr_Fetch = _PyErr_Fetch\n    PyErr_NormalizeException = _PyErr_NormalizeException\n    PyErr_SetString = _PyErr_SetString\n    Py_None = __ptr__(_Py_NoneStruct).as_byte()\n    Py_True = __ptr__(_Py_TrueStruct).as_byte()\n    Py_False = __ptr__(_Py_FalseStruct).as_byte()\n    Py_Ellipsis = __ptr__(_Py_EllipsisObject).as_byte()\n    Py_NotImplemented = __ptr__(_Py_NotImplementedStruct).as_byte()\n    PyLong_Type = __ptr__(_PyLong_Type).as_byte()\n    PyFloat_Type = __ptr__(_PyFloat_Type).as_byte()\n    PyBool_Type = __ptr__(_PyBool_Type).as_byte()\n    PyUnicode_Type = __ptr__(_PyUnicode_Type).as_byte()\n    PyBytes_Type = __ptr__(_PyBytes_Type).as_byte()\n    PyComplex_Type = __ptr__(_PyComplex_Type).as_byte()\n    PyList_Type = __ptr__(_PyList_Type).as_byte()\n    PyDict_Type = __ptr__(_PyDict_Type).as_byte()\n    PySet_Type = __ptr__(_PySet_Type).as_byte()\n    PyTuple_Type = __ptr__(_PyTuple_Type).as_byte()\n    PySlice_Type = __ptr__(_PySlice_Type).as_byte()\n    PyCapsule_Type = __ptr__(_PyCapsule_Type).as_byte()\n    PyExc_BaseException = _PyExc_BaseException\n    PyExc_Exception = _PyExc_Exception\n    PyExc_NameError = _PyExc_NameError\n    PyExc_OSError = _PyExc_OSError\n    PyExc_IOError = _PyExc_IOError\n    PyExc_ValueError = _PyExc_ValueError\n    PyExc_LookupError = _PyExc_LookupError\n    PyExc_IndexError = _PyExc_IndexError\n    PyExc_KeyError = _PyExc_KeyError\n    PyExc_TypeError = _PyExc_TypeError\n    PyExc_ArithmeticError = _PyExc_ArithmeticError\n    PyExc_ZeroDivisionError = _PyExc_ZeroDivisionError\n    PyExc_OverflowError = _PyExc_OverflowError\n    PyExc_AttributeError = _PyExc_AttributeError\n    PyExc_RuntimeError = _PyExc_RuntimeError\n    PyExc_NotImplementedError = _PyExc_NotImplementedError\n    PyExc_StopIteration = _PyExc_StopIteration\n    PyExc_AssertionError = _PyExc_AssertionError\n    PyExc_EOFError = _PyExc_EOFError\n    PyExc_SystemExit = _PyExc_SystemExit\n\ndef init_error_py_types():\n    BaseException._pytype = PyExc_BaseException\n    Exception._pytype = PyExc_Exception\n    NameError._pytype = PyExc_NameError\n    OSError._pytype = PyExc_OSError\n    IOError._pytype = PyExc_IOError\n    ValueError._pytype = PyExc_ValueError\n    LookupError._pytype = PyExc_LookupError\n    IndexError._pytype = PyExc_IndexError\n    KeyError._pytype = PyExc_KeyError\n    TypeError._pytype = PyExc_TypeError\n    ArithmeticError._pytype = PyExc_ArithmeticError\n    ZeroDivisionError._pytype = PyExc_ZeroDivisionError\n    OverflowError._pytype = PyExc_OverflowError\n    AttributeError._pytype = PyExc_AttributeError\n    RuntimeError._pytype = PyExc_RuntimeError\n    NotImplementedError._pytype = PyExc_NotImplementedError\n    StopIteration._pytype = PyExc_StopIteration\n    AssertionError._pytype = PyExc_AssertionError\n    EOFError._pytype = PyExc_EOFError\n    SystemExit._pytype = PyExc_SystemExit\n\ndef setup_python(python_loaded: bool):\n    global _PY_INITIALIZED\n    if _PY_INITIALIZED:\n        return\n\n    py_handle = cobj()\n    if python_loaded:\n        py_handle = dlopen(\"\", RTLD_LOCAL | RTLD_NOW)\n    else:\n        LD = os.getenv(\"CODON_PYTHON\", default=\"libpython.\" + dlext())\n        py_handle = dlopen(LD, RTLD_LOCAL | RTLD_NOW)\n\n    init_handles_dlopen(py_handle)\n    init_error_py_types()\n\n    if not python_loaded:\n        Py_Initialize()\n\n    PyOS_setsig(i32(2), cobj())  # disable CPython's SIGINT handler\n    _PY_INITIALIZED = True\n\ndef ensure_initialized(python_loaded: bool = False):\n    if __py_extension__:\n        init_handles_static()\n        init_error_py_types()\n    else:\n        setup_python(python_loaded)\n        PyRun_SimpleString(_PY_INIT.c_str())\n\ndef setup_decorator():\n    setup_python(True)\n\n@tuple\nclass _PyArg_Parser:\n    initialized: i32\n    format: cobj\n    keywords: Ptr[cobj]\n    fname: cobj\n    custom_msg: cobj\n    pos: i32\n    min: i32\n    max: i32\n    kwtuple: cobj\n    next: cobj\n\n    def __new__(fname: cobj, keywords: Ptr[cobj], format: cobj):\n        z = i32(0)\n        o = cobj()\n        return _PyArg_Parser(z, format, keywords, fname, o, z, z, z, o, o)\n\n@dataclass(init=False)\nclass PyError(Exception):\n    pytype: pyobj\n\n    def __init__(self, message: str):\n        super().__init__(message)\n        self.pytype = pyobj(cobj(), steal=True)\n\n    @overload\n    def __init__(self, message: str, pytype: pyobj):\n        super().__init__(message)\n        self.pytype = pytype\n\ndef _apply1(func, obj: cobj, *args):\n    return pyobj(pyobj.exc_wrap(func(obj, *args)), steal=True)\n\ndef _apply2(func, obj: cobj, other, *args):\n    if isinstance(other, pyobj):\n        return pyobj(pyobj.exc_wrap(func(obj, other.p, *args)), steal=True)\n    else:\n        other_py = other.__to_py__()\n        try:\n            return pyobj(pyobj.exc_wrap(func(obj, other_py, *args)), steal=True)\n        finally:\n            pyobj.decref(other_py)\n\ndef _rapply2(func, obj: cobj, other, *args):\n    if isinstance(other, pyobj):\n        return pyobj(pyobj.exc_wrap(func(other.p, obj, *args)), steal=True)\n    else:\n        other_py = other.__to_py__()\n        try:\n            return pyobj(pyobj.exc_wrap(func(other_py, obj, *args)), steal=True)\n        finally:\n            pyobj.decref(other_py)\n\ndef _apply3(func, obj: cobj, other1, other2, *args):\n    if isinstance(other1, pyobj) and isinstance(other2):\n        return pyobj(pyobj.exc_wrap(func(obj, other1.p, other2.p, *args)), steal=True)\n    else:\n        other1_py = other1.__to_py__()\n        other2_py = other2.__to_py__()\n        try:\n            ret = pyobj.exc_wrap(func(obj, other1_py, other2_py, *args))\n            if isinstance(ret, int):\n                return\n            else:\n                return pyobj(ret, steal=True)\n        finally:\n            pyobj.decref(other1_py)\n            pyobj.decref(other2_py)\n\n@extend\nclass pyobj:\n    def __new__() -> pyobj:\n        return type._ref_new(pyobj)\n\n    def __raw__(self) -> Ptr[byte]:\n        return type._ref_raw(self)\n\n    def __init__(self, p: Ptr[byte], steal: bool = False):\n        self.p = p\n        if not steal:\n            self.incref()\n\n    def __del__(self):\n        self.decref()\n\n    def _getattr(self, name: str) -> pyobj:\n        return pyobj(pyobj.exc_wrap(PyObject_GetAttrString(self.p, name.c_str())), steal=True)\n\n    def exc_wrap(retval: T, T: type) -> T:\n        if T is cobj:\n            if not retval:\n                pyobj.exc_check()\n        elif T is int:\n            if retval < 0:\n                pyobj.exc_check()\n        elif T is i32:\n            if retval < i32(0):\n                pyobj.exc_check()\n        else:\n            pyobj.exc_check()\n        return retval\n\n    def __add__(self, other):\n        return _apply2(PyNumber_Add, self.p, other)\n\n    def __radd__(self, other):\n        return _rapply2(PyNumber_Add, self.p, other)\n\n    def __sub__(self, other):\n        return _apply2(PyNumber_Subtract, self.p, other)\n\n    def __rsub__(self, other):\n        return _rapply2(PyNumber_Subtract, self.p, other)\n\n    def __mul__(self, other):\n        return _apply2(PyNumber_Multiply, self.p, other)\n\n    def __rmul__(self, other):\n        return _rapply2(PyNumber_Multiply, self.p, other)\n\n    def __matmul__(self, other):\n        return _apply2(PyNumber_MatrixMultiply, self.p, other)\n\n    def __rmatmul__(self, other):\n        return _rapply2(PyNumber_MatrixMultiply, self.p, other)\n\n    def __floordiv__(self, other):\n        return _apply2(PyNumber_FloorDivide, self.p, other)\n\n    def __rfloordiv__(self, other):\n        return _rapply2(PyNumber_FloorDivide, self.p, other)\n\n    def __truediv__(self, other):\n        return _apply2(PyNumber_TrueDivide, self.p, other)\n\n    def __rtruediv__(self, other):\n        return _rapply2(PyNumber_TrueDivide, self.p, other)\n\n    def __mod__(self, other):\n        return _apply2(PyNumber_Remainder, self.p, other)\n\n    def __rmod__(self, other):\n        return _rapply2(PyNumber_Remainder, self.p, other)\n\n    def __divmod__(self, other):\n        return _apply2(PyNumber_Divmod, self.p, other)\n\n    def __rdivmod__(self, other):\n        return _rapply2(PyNumber_Divmod, self.p, other)\n\n    def __pow__(self, other):\n        return _apply2(PyNumber_Power, self.p, other, Py_None)\n\n    def __rpow__(self, other):\n        return _rapply2(PyNumber_Power, self.p, other, Py_None)\n\n    def __neg__(self):\n        return _apply1(PyNumber_Negative, self.p)\n\n    def __pos__(self):\n        return _apply1(PyNumber_Positive, self.p)\n\n    def __invert__(self):\n        return _apply1(PyNumber_Invert, self.p)\n\n    def __lshift__(self, other):\n        return _apply2(PyNumber_Lshift, self.p, other)\n\n    def __rlshift__(self, other):\n        return _rapply2(PyNumber_Lshift, self.p, other)\n\n    def __rshift__(self, other):\n        return _apply2(PyNumber_Rshift, self.p, other)\n\n    def __rrshift__(self, other):\n        return _rapply2(PyNumber_Rshift, self.p, other)\n\n    def __and__(self, other):\n        return _apply2(PyNumber_And, self.p, other)\n\n    def __rand__(self, other):\n        return _rapply2(PyNumber_And, self.p, other)\n\n    def __xor__(self, other):\n        return _apply2(PyNumber_Xor, self.p, other)\n\n    def __rxor__(self, other):\n        return _rapply2(PyNumber_Xor, self.p, other)\n\n    def __or__(self, other):\n        return _apply2(PyNumber_Or, self.p, other)\n\n    def __ror__(self, other):\n        return _rapply2(PyNumber_Or, self.p, other)\n\n    def __iadd__(self, other):\n        return _apply2(PyNumber_InPlaceAdd, self.p, other)\n\n    def __isub__(self, other):\n        return _apply2(PyNumber_InPlaceSubtract, self.p, other)\n\n    def __imul__(self, other):\n        return _apply2(PyNumber_InPlaceMultiply, self.p, other)\n\n    def __imatmul__(self, other):\n        return _apply2(PyNumber_InPlaceMatrixMultiply, self.p, other)\n\n    def __ifloordiv__(self, other):\n        return _apply2(PyNumber_InPlaceFloorDivide, self.p, other)\n\n    def __itruediv__(self, other):\n        return _apply2(PyNumber_InPlaceTrueDivide, self.p, other)\n\n    def __imod__(self, other):\n        return _apply2(PyNumber_InPlaceRemainder, self.p, other)\n\n    def __ipow__(self, other):\n        return _apply2(PyNumber_InPlacePower, self.p, other, Py_None)\n\n    def __ilshift__(self, other):\n        return _apply2(PyNumber_InPlaceLshift, self.p, other)\n\n    def __irshift__(self, other):\n        return _apply2(PyNumber_InPlaceRshift, self.p, other)\n\n    def __iand__(self, other):\n        return _apply2(PyNumber_InPlaceAnd, self.p, other)\n\n    def __ixor__(self, other):\n        return _apply2(PyNumber_InPlaceXor, self.p, other)\n\n    def __ior__(self, other):\n        return _apply2(PyNumber_InPlaceOr, self.p, other)\n\n    def __int__(self):\n        o = pyobj.exc_wrap(PyNumber_Long(self.p))\n        x = int.__from_py__(o)\n        pyobj.decref(o)\n        return x\n\n    def __float__(self):\n        o = pyobj.exc_wrap(PyNumber_Float(self.p))\n        x = float.__from_py__(o)\n        pyobj.decref(o)\n        return x\n\n    def __index__(self):\n        o = pyobj.exc_wrap(PyNumber_Index(self.p))\n        x = int.__from_py__(o)\n        pyobj.decref(o)\n        return x\n\n    def __len__(self) -> int:\n        return pyobj.exc_wrap(PyObject_Length(self.p))\n\n    def __length_hint__(self) -> int:\n        return pyobj.exc_wrap(PyObject_LengthHint(self.p))\n\n    def __getitem__(self, key):\n        return _apply2(PyObject_GetItem, self.p, key)\n\n    def __setitem__(self, key, v):\n        return _apply3(PyObject_SetItem, self.p, key, v)\n\n    def __delitem__(self, key):\n        return _apply2(PyObject_DelItem, self.p, key)\n\n    def __lt__(self, other):\n        return _apply2(PyObject_RichCompare, self.p, other, i32(Py_LT))\n\n    def __le__(self, other):\n        return _apply2(PyObject_RichCompare, self.p, other, i32(Py_LE))\n\n    def __eq__(self, other):\n        return _apply2(PyObject_RichCompare, self.p, other, i32(Py_EQ))\n\n    def __ne__(self, other):\n        return _apply2(PyObject_RichCompare, self.p, other, i32(Py_NE))\n\n    def __gt__(self, other):\n        return _apply2(PyObject_RichCompare, self.p, other, i32(Py_GT))\n\n    def __ge__(self, other):\n        return _apply2(PyObject_RichCompare, self.p, other, i32(Py_GE))\n\n    def __to_py__(self) -> cobj:\n        self.incref()\n        return self.p\n\n    def __from_py__(p: cobj) -> pyobj:\n        return pyobj(p)\n\n    def __str__(self) -> str:\n        o = pyobj.exc_wrap(PyObject_Str(self.p))\n        return str.__from_py__(o)\n\n    def __repr__(self) -> str:\n        o = pyobj.exc_wrap(PyObject_Repr(self.p))\n        return str.__from_py__(o)\n\n    def __hash__(self) -> int:\n        return pyobj.exc_wrap(PyObject_Hash(self.p))\n\n    def __iter__(self) -> Generator[pyobj]:\n        it = PyObject_GetIter(self.p)\n        if not it:\n            raise TypeError(\"Python object is not iterable\")\n        try:\n            while i := PyIter_Next(it):\n                yield pyobj(pyobj.exc_wrap(i), steal=True)\n        finally:\n            pyobj.decref(it)\n        pyobj.exc_check()\n\n    def to_str(self, errors: str, empty: str = \"\") -> str:\n        return pyobj.to_str(self.p, errors, empty)\n\n    def to_str(p: cobj, errors: str, empty: str = \"\") -> str:\n        obj = PyUnicode_AsEncodedString(p, \"utf-8\".ptr, errors.c_str() if errors else \"\".ptr)\n        if obj == cobj():\n            return empty\n        bts = PyBytes_AsString(obj)\n        res = str.from_ptr(bts)\n        pyobj.decref(obj)\n        return res\n\n    def exc_check():\n        ptype, pvalue, ptraceback = cobj(), cobj(), cobj()\n        PyErr_Fetch(__ptr__(ptype), __ptr__(pvalue), __ptr__(ptraceback))\n        PyErr_NormalizeException(__ptr__(ptype), __ptr__(pvalue), __ptr__(ptraceback))\n        if ptype != cobj():\n            py_msg = PyObject_Str(pvalue) if pvalue != cobj() else pvalue\n            msg = pyobj.to_str(py_msg, \"ignore\", \"<empty Python message>\")\n\n            pyobj.decref(ptype)\n            pyobj.decref(ptraceback)\n            pyobj.decref(py_msg)\n\n            raise PyError(msg, pyobj(pvalue))\n\n    def incref(self):\n        Py_IncRef(self.p)\n        return self\n\n    def incref(ptr: Ptr[byte]):\n        Py_IncRef(ptr)\n\n    def decref(self):\n        Py_DecRef(self.p)\n        return self\n\n    def decref(ptr: Ptr[byte]):\n        Py_DecRef(ptr)\n\n    def __call__(self, *args, **kwargs):\n        if static.len(args) == 0 and static.len(kwargs.args) == 0:\n            return pyobj(pyobj.exc_wrap(PyObject_CallNoArgs(self.p)), steal=True)\n        elif static.len(args) == 1 and static.len(kwargs.args) == 0:\n            arg_py = args[0].__to_py__()\n            try:\n                return pyobj(pyobj.exc_wrap(PyObject_CallOneArg(self.p, arg_py)), steal=True)\n            finally:\n                pyobj.decref(arg_py)\n        else:\n            args_py = args.__to_py__()\n            kws_py = cobj()\n\n            if static.len(kwargs.args) > 0:\n                keys = kwargs.__keys__()\n                values = [pyobj(v.__to_py__(), steal=True) for v in kwargs.args]\n                kws_py = {keys[i]: values[i] for i in range(len(keys))}.__to_py__()\n\n            try:\n                return pyobj(pyobj.exc_wrap(PyObject_Call(self.p, args_py, kws_py)), steal=True)\n            finally:\n                pyobj.decref(args_py)\n                if static.len(kwargs.args) > 0:\n                    pyobj.decref(kws_py)\n\n    def _tuple_new(length: int):\n        return pyobj.exc_wrap(PyTuple_New(length))\n\n    def _tuple_size(p: cobj):\n        return pyobj.exc_wrap(PyTuple_Size(p))\n\n    def _tuple_set(p: cobj, idx: int, val: cobj):\n        pyobj.exc_wrap(PyTuple_SetItem(p, idx, val))\n\n    def _tuple_get(p: cobj, idx: int) -> cobj:\n        return pyobj.exc_wrap(PyTuple_GetItem(p, idx))\n\n    def _import(name: str) -> pyobj:\n        ensure_initialized()\n        if name in _PY_MODULE_CACHE:\n            return _PY_MODULE_CACHE[name]\n        m = pyobj(pyobj.exc_wrap(PyImport_ImportModule(name.c_str())), steal=True)\n        _PY_MODULE_CACHE[name] = m\n        return m\n\n    def _exec(code: str):\n        ensure_initialized()\n        PyRun_SimpleString(code.c_str())\n\n    def _globals() -> pyobj:\n        p = PyEval_GetGlobals()\n        if p == cobj():\n            Py_IncRef(Py_None)\n            return pyobj(Py_None)\n        return pyobj(p)\n\n    def _builtins() -> pyobj:\n        return pyobj(PyEval_GetBuiltins())\n\n    def _get_module(name: str) -> pyobj:\n        return pyobj(pyobj.exc_wrap(PyImport_AddModule(name.c_str())))\n\n    def _main_module() -> pyobj:\n        return pyobj._get_module(\"__main__\")\n\n    def _repr_mimebundle_(self, bundle: Optional[Set[str]] = None) -> Dict[str, str]:\n        fn = pyobj._main_module()._getattr(\"__codon_repr__\")\n        assert fn.p != cobj(), \"cannot find python.__codon_repr__\"\n        mime, txt = Tuple[str, str].__from_py__(fn.__call__(self).p)\n        return {mime: txt}\n\n    def __bool__(self):\n        return bool(pyobj.exc_wrap(PyObject_IsTrue(self.p) == i32(1)))\n\ndef _get_identifier(typ: str) -> pyobj:\n    t = pyobj._builtins()[typ]\n    if t.p == cobj():\n        t = pyobj._main_module()[typ]\n    return t\n\ndef _isinstance(what: pyobj, typ: pyobj) -> bool:\n    return bool(pyobj.exc_wrap(PyObject_IsInstance(what.p, typ.p)))\n\n@tuple\nclass _PyObject_Struct:\n    refcnt: int\n    pytype: cobj\n\ndef _conversion_error(name: Literal[str]):\n    raise PyError(\"conversion error: Python object did not have type '\" + name + \"'\")\n\ndef _get_type(o: cobj):\n    return Ptr[_PyObject_Struct](o)[0].pytype\n\ndef _ensure_type(o: cobj, t: cobj, name: Literal[str]):\n    if _get_type(o) != t:\n        _conversion_error(name)\n\n\n# Type conversions\n\n@extend\nclass NoneType:\n    def __to_py__(self) -> cobj:\n        Py_IncRef(Py_None)\n        return Py_None\n\n    def __from_py__(x: cobj) -> None:\n        if x != Py_None:\n            _conversion_error(\"NoneType\")\n        return\n\n@extend\nclass int:\n    def __to_py__(self) -> cobj:\n        return pyobj.exc_wrap(PyLong_FromLong(self))\n\n    def __from_py__(i: cobj) -> int:\n        _ensure_type(i, PyLong_Type, \"int\")\n        return PyLong_AsLong(i)\n\n@extend\nclass float:\n    def __to_py__(self) -> cobj:\n        return pyobj.exc_wrap(PyFloat_FromDouble(self))\n\n    def __from_py__(d: cobj) -> float:\n        return pyobj.exc_wrap(PyFloat_AsDouble(d))\n\n@extend\nclass bool:\n    def __to_py__(self) -> cobj:\n        return pyobj.exc_wrap(PyBool_FromLong(int(self)))\n\n    def __from_py__(b: cobj) -> bool:\n        _ensure_type(b, PyBool_Type, \"bool\")\n        return PyObject_IsTrue(b) != i32(0)\n\n@extend\nclass byte:\n    def __to_py__(self) -> cobj:\n        return str.__to_py__(str(__ptr__(self), 1))\n\n    def __from_py__(c: cobj) -> byte:\n        return str.__from_py__(c).ptr[0]\n\n@extend\nclass str:\n    def __to_py__(self) -> cobj:\n        return pyobj.exc_wrap(PyUnicode_DecodeFSDefaultAndSize(self.ptr, self.len))\n\n    def __from_py__(s: cobj) -> str:\n        if _get_type(s) == PyBytes_Type:\n            n = PyBytes_Size(s)\n            p0 = PyBytes_AsString(s)\n            p1 = cobj(n)\n            str.memcpy(p1, p0, n)\n            return str(p1, n)\n        else:\n            return pyobj.exc_wrap(pyobj.to_str(s, \"strict\"))\n\n@extend\nclass complex:\n    def __to_py__(self) -> cobj:\n        return pyobj.exc_wrap(PyComplex_FromDoubles(self.real, self.imag))\n\n    def __from_py__(c: cobj) -> complex:\n        _ensure_type(c, PyComplex_Type, \"complex\")\n        real = PyComplex_RealAsDouble(c)\n        imag = PyComplex_ImagAsDouble(c)\n        return complex(real, imag)\n\n@extend\nclass List:\n    def __to_py__(self) -> cobj:\n        pylist = PyList_New(len(self))\n        pyobj.exc_check()\n        idx = 0\n        for a in self:\n            PyList_SetItem(pylist, idx, a.__to_py__())\n            pyobj.exc_check()\n            idx += 1\n        return pylist\n\n    def __from_py__(v: cobj) -> List[T]:\n        _ensure_type(v, PyList_Type, \"list\")\n        n = PyList_Size(v)\n        t = List[T](n)\n        for i in range(n):\n            elem = PyList_GetItem(v, i)\n            t.append(T.__from_py__(elem))\n        return t\n\n@extend\nclass Dict:\n    def __to_py__(self) -> cobj:\n        pydict = PyDict_New()\n        pyobj.exc_check()\n        for k, v in self.items():\n            PyDict_SetItem(pydict, k.__to_py__(), v.__to_py__())\n            pyobj.exc_check()\n        return pydict\n\n    def __from_py__(d: cobj) -> Dict[K, V]:\n        _ensure_type(d, PyDict_Type, \"dict\")\n        b = dict[K, V]()\n        pos = 0\n        k_ptr = cobj()\n        v_ptr = cobj()\n        while PyDict_Next(d, __ptr__(pos), __ptr__(k_ptr), __ptr__(v_ptr)):\n            k = K.__from_py__(k_ptr)\n            v = V.__from_py__(v_ptr)\n            b[k] = v\n        return b\n\n@extend\nclass Set:\n    def __to_py__(self) -> cobj:\n        pyset = PySet_New(cobj())\n        pyobj.exc_check()\n        for a in self:\n            PySet_Add(pyset, a.__to_py__())\n            pyobj.exc_check()\n        return pyset\n\n    def __from_py__(s: cobj) -> Set[K]:\n        _ensure_type(s, PySet_Type, \"set\")\n        b = set[K]()\n        s_iter = PyObject_GetIter(s)\n        while True:\n            k_ptr = pyobj.exc_wrap(PyIter_Next(s_iter))\n            if not k_ptr:\n                break\n            k = K.__from_py__(k_ptr)\n            pyobj.decref(k_ptr)\n            b.add(k)\n        pyobj.decref(s_iter)\n        return b\n\n@extend\nclass DynamicTuple:\n    def __to_py__(self) -> cobj:\n        pytup = PyTuple_New(len(self))\n        i = 0\n        for a in self:\n            PyTuple_SetItem(pytup, i, a.__to_py__())\n            pyobj.exc_check()\n            i += 1\n        return pytup\n\n    def __from_py__(t: cobj) -> DynamicTuple[T]:\n        _ensure_type(t, PyTuple_Type, \"tuple\")\n        n = PyTuple_Size(t)\n        p = Ptr[T](n)\n        for i in range(n):\n            p[i] = T.__from_py__(PyTuple_GetItem(t, i))\n        return DynamicTuple(p, n)\n\n@extend\nclass Slice:\n    def __to_py__(self) -> cobj:\n        start = self.start\n        stop = self.stop\n        step = self.step\n        start_py = start.__to_py__() if start is not None else cobj()\n        stop_py = stop.__to_py__() if stop is not None else cobj()\n        step_py = step.__to_py__() if step is not None else cobj()\n        return PySlice_New(start_py, stop_py, step_py)\n\n    def __from_py__(s: cobj) -> Slice:\n        _ensure_type(s, PySlice_Type, \"slice\")\n        start = 0\n        stop = 0\n        step = 0\n        PySlice_Unpack(s, __ptr__(start), __ptr__(stop), __ptr__(step))\n        return Slice(Optional(start), Optional(stop), Optional(step))\n\n@extend\nclass Optional:\n    def __to_py__(self) -> cobj:\n        if self is None:\n            return Py_None\n        else:\n            return self.__val__().__to_py__()\n\n    def __from_py__(o: cobj) -> Optional[T]:\n        if o == Py_None:\n            return Optional[T]()\n        else:\n            return Optional[T](T.__from_py__(o))\n\n@extend\nclass ellipsis:\n    def __to_py__(self) -> cobj:\n        return Py_Ellipsis\n\n    def __from_py__(e: cobj) -> ellipsis:\n        if e == Py_Ellipsis:\n            return Ellipsis\n        else:\n            _conversion_error(\"ellipsis\")\n\n__pyenv__: Optional[pyobj] = None\ndef _____(): __pyenv__  # make it global!\n\n\nclass _PyWrap:\n    def _dispatch_error(F: Literal[str], args):\n        if isinstance(args, cobj):\n            s = str(pyobj(args, steal=False))\n        else:\n            s = str([pyobj(i, steal=False) for i in args])\n        raise TypeError(\"could not find callable method '\" + F + \"' for given arguments \" + s)\n\n    def _wrap(args, T: type, F: Literal[str], map):\n        for fn in static.function.overloads(T, F):\n            a = _PyWrap._args_from_py(fn, args)\n            if a is None:\n                continue\n            if static.function.can_call(fn, *a):\n                try:\n                    return map(fn, a)\n                except PyError as e:\n                    pass\n        _PyWrap._dispatch_error(F, args)\n\n    def _wrap_unary(obj: cobj, T: type, F: Literal[str]) -> cobj:\n        return _PyWrap._wrap(\n            (obj,), T=T, F=F,\n            map=lambda f, a: f(*a).__to_py__()\n        )\n\n    def _args_from_py(fn, args):\n        def err(fail: Ptr[bool], T: type = NoneType) -> T:\n            fail[0] = True\n            # auto-return zero-initialized T\n\n        def get_arg(F, p, k, fail: Ptr[bool], i: Literal[int]):\n            if static.function.has_type(F, i):\n                return static.function.get_type(F, i).__from_py__(p[i]) if p[i] != cobj() else (\n                    static.function.get_default(F, i) if static.function.has_default(F, i)\n                    else err(fail, static.function.get_type(F, i))\n                )\n            else:\n                return pyobj(p[i], steal=False) if p[i] != cobj() else (\n                    static.function.get_default(F, i) if static.function.has_default(F, i) else err(fail)\n                )\n\n        fail = False\n        pargs = Ptr[cobj](__ptr__(args).as_byte())\n        try:\n            ta = tuple(get_arg(fn, pargs, k, __ptr__(fail), i) for i, k in static.enumerate(static.function.args(fn)))\n            if fail:\n                return None\n            return static.function.wrap_args(fn, *ta)\n        except PyError as e:\n            return None\n\n    def _reorder_args(fn, self: cobj, args: cobj, kwargs: cobj, M: Literal[int] = 1):\n        nargs = PyTuple_Size(args)\n        nkwargs = PyDict_Size(kwargs) if kwargs != cobj() else 0\n\n        args_ordered = tuple(cobj() for _ in static.function.args(fn))\n        pargs = Ptr[cobj](__ptr__(args_ordered).as_byte())\n\n        if nargs + nkwargs + M > len(args_ordered):\n            return None\n\n        if M:\n            pargs[0] = self\n\n        for i in range(nargs):\n            pargs[i + M] = PyTuple_GetItem(args, i)\n\n        kwused = 0\n        for i, k in static.enumerate(static.function.args(fn)):\n            if i < nargs + M:\n                continue\n\n            p = PyDict_GetItemString(kwargs, k.ptr) if nkwargs else cobj()\n            if p != cobj():\n                pargs[i] = p\n                kwused += 1\n\n        if kwused != nkwargs:\n            return None\n\n        return _PyWrap._args_from_py(fn, args_ordered)\n\n    def _reorder_args_fastcall(\n        fn, self: cobj, args: Ptr[cobj], nargs: int,\n        kwds: Ptr[str], nkw: int, M: Literal[int] = 1\n    ):\n        args_ordered = tuple(cobj() for _ in static.function.args(fn))\n        pargs = Ptr[cobj](__ptr__(args_ordered).as_byte())\n\n        if nargs + M > len(args_ordered):\n            return None\n\n        if M:\n            pargs[0] = self\n\n        for i in range(nargs):\n            pargs[i + M] = args[i]\n\n        for i in range(nargs, nargs + nkw):\n            kw = kwds[i - nargs]\n            o = args[i]\n\n            found = False\n            j = M\n            for i, k in static.enumerate(static.function.args(fn)):\n                if M and i == 0:\n                    continue\n                if kw == k:\n                    if not pargs[j]:\n                        pargs[j] = o\n                    else:\n                        return None\n                    found = True\n                    break\n                j += 1\n            if not found:\n                return None\n\n        return _PyWrap._args_from_py(fn, args_ordered)\n\n    def wrap_magic_abs(obj: cobj, T: type):\n        return _PyWrap._wrap_unary(obj, T, \"__abs__\")\n\n    def wrap_magic_pos(obj: cobj, T: type):\n        return _PyWrap._wrap_unary(obj, T, \"__pos__\")\n\n    def wrap_magic_neg(obj: cobj, T: type):\n        return _PyWrap._wrap_unary(obj, T, \"__neg__\")\n\n    def wrap_magic_invert(obj: cobj, T: type):\n        return _PyWrap._wrap_unary(obj, T, \"__invert__\")\n\n    def wrap_magic_int(obj: cobj, T: type):\n        return _PyWrap._wrap_unary(obj, T, \"__int__\")\n\n    def wrap_magic_float(obj: cobj, T: type):\n        return _PyWrap._wrap_unary(obj, T, \"__float__\")\n\n    def wrap_magic_index(obj: cobj, T: type):\n        return _PyWrap._wrap_unary(obj, T, \"__index__\")\n\n    def wrap_magic_repr(obj: cobj, T: type):\n        return _PyWrap._wrap_unary(obj, T, \"__repr__\")\n\n    def wrap_magic_str(obj: cobj, T: type):\n        return _PyWrap._wrap_unary(obj, T, \"__str__\")\n\n    def _wrap_binary(obj: cobj, obj2: cobj, T: type, F: Literal[str]) -> cobj:\n        return _PyWrap._wrap(\n            (obj, obj2), T=T, F=F,\n            map=lambda f, a: f(*a).__to_py__()\n        )\n\n    def wrap_magic_add(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__add__\")\n\n    def wrap_magic_radd(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__radd__\")\n\n    def wrap_magic_iadd(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__iadd__\")\n\n    def wrap_magic_sub(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__sub__\")\n\n    def wrap_magic_rsub(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__rsub__\")\n\n    def wrap_magic_isub(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__isub__\")\n\n    def wrap_magic_mul(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__mul__\")\n\n    def wrap_magic_rmul(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__rmul__\")\n\n    def wrap_magic_imul(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__imul__\")\n\n    def wrap_magic_mod(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__mod__\")\n\n    def wrap_magic_rmod(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__rmod__\")\n\n    def wrap_magic_imod(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__imod__\")\n\n    def wrap_magic_divmod(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__divmod__\")\n\n    def wrap_magic_rdivmod(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__rdivmod__\")\n\n    def wrap_magic_lshift(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__lshift__\")\n\n    def wrap_magic_rlshift(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__rlshift__\")\n\n    def wrap_magic_ilshift(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__ilshift__\")\n\n    def wrap_magic_rshift(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__rshift__\")\n\n    def wrap_magic_rrshift(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__rrshift__\")\n\n    def wrap_magic_irshift(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__irshift__\")\n\n    def wrap_magic_and(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__and__\")\n\n    def wrap_magic_rand(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__rand__\")\n\n    def wrap_magic_iand(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__iand__\")\n\n    def wrap_magic_xor(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__xor__\")\n\n    def wrap_magic_rxor(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__rxor__\")\n\n    def wrap_magic_ixor(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__ixor__\")\n\n    def wrap_magic_or(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__or__\")\n\n    def wrap_magic_ror(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__ror__\")\n\n    def wrap_magic_ior(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__ior__\")\n\n    def wrap_magic_floordiv(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__floordiv__\")\n\n    def wrap_magic_ifloordiv(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__ifloordiv__\")\n\n    def wrap_magic_truediv(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__truediv__\")\n\n    def wrap_magic_itruediv(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__itruediv__\")\n\n    def wrap_magic_matmul(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__matmul__\")\n\n    def wrap_magic_rmatmul(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__rmatmul__\")\n\n    def wrap_magic_imatmul(obj: cobj, obj2: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__imatmul__\")\n\n    def wrap_magic_pow(obj: cobj, obj2: cobj, obj3: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__pow__\")\n\n    def wrap_magic_rpow(obj: cobj, obj2: cobj, obj3: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__rpow__\")\n\n    def wrap_magic_ipow(obj: cobj, obj2: cobj, obj3: cobj, T: type):\n        return _PyWrap._wrap_binary(obj, obj2, T, \"__ipow__\")\n\n    def _wrap_hash(obj: cobj, T: type, F: Literal[str]) -> i64:\n        return _PyWrap._wrap(\n            (obj,), T=T, F=F,\n            map=lambda f, a: f(*a)\n        )\n\n    def wrap_magic_len(obj: cobj, T: type):\n        return _PyWrap._wrap_hash(obj, T, \"__len__\")\n\n    def wrap_magic_hash(obj: cobj, T: type):\n        return _PyWrap._wrap_hash(obj, T, \"__hash__\")\n\n    def wrap_magic_bool(obj: cobj, T: type) -> i32:\n        return _PyWrap._wrap(\n            (obj,), T=T, F=\"__bool__\",\n            map=lambda f, a: i32(1) if f(*a) else i32(0)\n        )\n\n    def wrap_magic_del(obj: cobj, T: type):\n        _PyWrap._wrap(\n            (obj,), T=T, F=\"__del__\",\n            map=lambda f, a: f(*a)\n        )\n\n    def wrap_magic_contains(obj: cobj, arg: cobj, T: type) -> i32:\n        return _PyWrap._wrap(\n            (obj, arg,), T=T, F=\"__contains__\",\n            map=lambda f, a: i32(1) if f(*a) else i32(0)\n        )\n\n    def wrap_magic_init(obj: cobj, args: cobj, kwargs: cobj, T: type) -> i32:\n        if isinstance(T, ByRef):\n            F: Literal[str] = \"__init__\"\n            for fn in static.function.overloads(T, F):\n                a = _PyWrap._reorder_args(fn, obj, args, kwargs, M=1)\n                if a is not None and static.function.can_call(fn, *a):\n                    fn(*a)\n                    return i32(0)\n            _PyWrap._dispatch_error(F, args)\n        else:\n            F: Literal[str] = \"__new__\"\n            for fn in static.function.overloads(T, F):\n                a = _PyWrap._reorder_args(fn, obj, args, kwargs, M=0)\n                if a is not None and static.function.can_call(fn, *a):\n                    x = fn(*a)\n                    p = Ptr[PyObject](obj) + 1\n                    Ptr[T](p.as_byte())[0] = x\n                    return i32(0)\n            _PyWrap._dispatch_error(F, args)\n\n    def wrap_magic_call(obj: cobj, args: cobj, kwargs: cobj, T: type) -> cobj:\n        F: Literal[str] = \"__call__\"\n        for fn in static.function.overloads(T, F):\n            a = _PyWrap._reorder_args(fn, obj, args, kwargs, M=1)\n            if a is not None and static.function.can_call(fn, *a):\n                return fn(*a).__to_py__()\n        _PyWrap._dispatch_error(F, args)\n\n    def _wrap_cmp(obj: cobj, other: cobj, T: type, F: Literal[str]) -> cobj:\n        return _PyWrap._wrap(\n            (obj, other), T=T, F=F,\n            map=lambda f, a: f(*a).__to_py__()\n        )\n\n    def wrap_magic_lt(obj: cobj, other: cobj, T: type):\n        return _PyWrap._wrap_cmp(obj, other, T, \"__lt__\")\n\n    def wrap_magic_le(obj: cobj, other: cobj, T: type):\n        return _PyWrap._wrap_cmp(obj, other, T, \"__le__\")\n\n    def wrap_magic_eq(obj: cobj, other: cobj, T: type):\n        return _PyWrap._wrap_cmp(obj, other, T, \"__eq__\")\n\n    def wrap_magic_ne(obj: cobj, other: cobj, T: type):\n        return _PyWrap._wrap_cmp(obj, other, T, \"__ne__\")\n\n    def wrap_magic_gt(obj: cobj, other: cobj, T: type):\n        return _PyWrap._wrap_cmp(obj, other, T, \"__gt__\")\n\n    def wrap_magic_ge(obj: cobj, other: cobj, T: type):\n        return _PyWrap._wrap_cmp(obj, other, T, \"__ge__\")\n\n    def wrap_cmp(obj: cobj, other: cobj, op: i32, C: type) -> cobj:\n        if hasattr(C, \"__lt__\") and op == 0i32:\n            return _PyWrap.wrap_magic_lt(obj, other, C)\n        elif hasattr(C, \"__le__\") and op == 1i32:\n            return _PyWrap.wrap_magic_le(obj, other, C)\n        elif hasattr(C, \"__eq__\") and op == 2i32:\n            return _PyWrap.wrap_magic_eq(obj, other, C)\n        elif hasattr(C, \"__ne__\") and op == 3i32:\n            return _PyWrap.wrap_magic_ne(obj, other, C)\n        elif hasattr(C, \"__gt__\") and op == 4i32:\n            return _PyWrap.wrap_magic_gt(obj, other, C)\n        elif hasattr(C, \"__ge__\") and op == 5i32:\n            return _PyWrap.wrap_magic_ge(obj, other, C)\n        else:\n            Py_IncRef(Py_NotImplemented)\n            return Py_NotImplemented\n\n    def wrap_magic_getitem(obj: cobj, idx: cobj, T: type):\n        return _PyWrap._wrap(\n            (obj, idx), T=T, F=\"__getitem__\",\n            map=lambda f, a: f(*a).__to_py__()\n        )\n\n    def wrap_magic_setitem(obj: cobj, idx: cobj, val: cobj, T: type) -> i32:\n        if val == cobj():\n            _PyWrap._wrap(\n                (obj, idx), T=T, F=\"__delitem__\",\n                map=lambda f, a: f(*a)\n            )\n        else:\n            _PyWrap._wrap(\n                (obj, idx, val), T=T, F=\"__setitem__\",\n                map=lambda f, a: f(*a)\n            )\n        return i32(0)\n\n    class IterWrap:\n        _gen: cobj\n        T: type\n\n        def _init(obj: cobj, T: type) -> cobj:\n            return _PyWrap.IterWrap(T.__from_py__(obj)).__to_py__()\n\n        def __init__(self, it: T):\n            self._gen = it.__iter__().__raw__()\n\n        def _iter(obj: cobj) -> cobj:\n            T  # need separate fn for each instantiation\n            p = Ptr[PyObject](obj)\n            o = p[0]\n            p[0] = PyObject(o.refcnt + 1, o.pytype)\n            return obj\n\n        def _iternext(self: cobj, TO: type) -> cobj:\n            pt = _PyWrap.IterWrap[T].__from_py__(self)\n            if pt._gen == cobj():\n                return cobj()\n\n            gt = TO(pt._gen)\n            if gt.done():\n                pt._gen = cobj()\n                return cobj()\n            else:\n                return gt.__next__().__to_py__()\n\n        def __to_py__(self):\n            return _PyWrap.wrap_to_py(self)\n\n        def __from_py__(obj: cobj):\n            return _PyWrap.wrap_from_py(obj, _PyWrap.IterWrap[T])\n\n    def wrap_magic_iter(obj: cobj, T: type) -> cobj:\n        return _PyWrap.IterWrap._init(obj, T)\n\n    def wrap_multiple(\n        obj: cobj, args: Ptr[cobj], nargs: int, _kwds: cobj, T: type, F: Literal[str],\n        M: Literal[int] = 1\n    ):\n        kwds = Ptr[str]()\n        nkw = 0\n        if _kwds:\n            nkw = PyTuple_Size(_kwds)\n            kwds = Ptr[str](nkw)\n            for i in range(nkw):\n                kwds[i] = str.__from_py__(PyTuple_GetItem(_kwds, i))\n\n        for fn in static.function.overloads(T, F):\n            a = _PyWrap._reorder_args_fastcall(fn, obj, args, nargs, kwds, nkw, M)\n            if a is not None and static.function.can_call(fn, *a):\n                return fn(*a).__to_py__()\n\n        _PyWrap._dispatch_error(F, [args[i] for i in range(nargs)])\n\n    def wrap_get(obj: cobj, closure: cobj, T: type, S: Literal[str]):\n        return getattr(T.__from_py__(obj), S).__to_py__()\n\n    def wrap_set(obj: cobj, what: cobj, closure: cobj, T: type, S: Literal[str]) -> i32:\n        t = T.__from_py__(obj)\n        val = type(getattr(t, S)).__from_py__(what)\n        setattr(t, S, val)\n        return i32(0)\n\n    def py_type(T: type) -> cobj:\n        return cobj()\n\n    def wrap_to_py(o) -> cobj:\n        from internal.gc import alloc_atomic_uncollectable\n\n        O = type(o)\n        P = PyWrapper[O]\n        sz = sizeof(P)\n        pytype = _PyWrap.py_type(O)\n        mem = alloc_atomic_uncollectable(sz) if atomic(O) else alloc_uncollectable(sz)\n        obj = Ptr[P](mem.as_byte())\n        obj[0] = PyWrapper(PyObject(1, pytype), o)\n        return obj.as_byte()\n\n    def wrap_from_py(o: cobj, T: type) -> T:\n        obj = Ptr[PyWrapper[T]](o)[0]\n        pytype = _PyWrap.py_type(T)\n        if obj.head.pytype != pytype:\n            _conversion_error(T.__name__)\n        return obj.data\n"
  },
  {
    "path": "stdlib/internal/sort.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom algorithms.pdqsort import pdq_sort_inplace\nfrom algorithms.insertionsort import insertion_sort_inplace\nfrom algorithms.heapsort import heap_sort_inplace\nfrom algorithms.qsort import qsort_inplace\nfrom algorithms.timsort import tim_sort_inplace\n\ndef sorted(\n    v: Generator[T],\n    key=Optional[int](),\n    reverse: bool = False,\n    algorithm: Literal[str] = \"auto\",\n    T: type,\n) -> List[T]:\n    newlist = [a for a in v]\n    if not isinstance(key, Optional):\n        newlist.sort(key, reverse, algorithm)\n    else:\n        newlist.sort(reverse=reverse, algorithm=algorithm)\n    return newlist\n\ndef _is_pdq_compatible(x):\n    if (isinstance(x, int) or\n        isinstance(x, float) or\n        isinstance(x, bfloat16) or\n        isinstance(x, float16) or\n        isinstance(x, float32) or\n        isinstance(x, float128) or\n        isinstance(x, bool) or\n        isinstance(x, byte) or\n        isinstance(x, str) or\n        isinstance(x, Int) or\n        isinstance(x, UInt)):\n        return True\n    elif isinstance(x, Tuple):\n        for a in x:\n            if not _is_pdq_compatible(a):\n                return False\n        return True\n    else:\n        return False\n\ndef _sort_list(\n    self: List[T], key: CallableTrait[[T], S], algorithm: Literal[str], T: type, S: type\n):\n    if algorithm == \"tim\" or algorithm == \"auto\":\n        tim_sort_inplace(self, key)\n    elif algorithm == \"pdq\":\n        pdq_sort_inplace(self, key)\n    elif algorithm == \"insertion\":\n        insertion_sort_inplace(self, key)\n    elif algorithm == \"heap\":\n        heap_sort_inplace(self, key)\n    elif algorithm == \"quick\":\n        qsort_inplace(self, key)\n    else:\n        compile_error(\"invalid sort algorithm\")\n\n@extend\nclass List:\n    def sort(\n        self,\n        key=Optional[int](),\n        reverse: bool = False,\n        algorithm: Literal[str] = \"auto\",\n    ):\n        if isinstance(key, Optional):\n            if algorithm == \"auto\":\n                # Python uses Timsort in all cases, but if we\n                # know stability does not matter (i.e. sorting\n                # primitive type with no key), we will use\n                # faster PDQ instead. PDQ is ~50% faster than\n                # Timsort for sorting 1B 64-bit ints.\n                if self:\n                    if _is_pdq_compatible(self[0]):\n                        pdq_sort_inplace(self, lambda x: x)\n                    else:\n                        tim_sort_inplace(self, lambda x: x)\n            else:\n                _sort_list(self, lambda x: x, algorithm)\n        else:\n            _sort_list(self, key, algorithm)\n        if reverse:\n            self.reverse()\n"
  },
  {
    "path": "stdlib/internal/static.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n# Methods for static reflection. Implemented within special.cpp, call.cpp and/or loops.cpp.\n# !! Not intended for public use !!\n\ndef len(obj):\n    pass\n\n@no_argument_wrap\ndef print(*args):\n    pass\n\ndef range(start: Literal[int], stop: Literal[int], step: Literal[int] = 1):\n    import internal.types.range\n\n    if step == 0:\n        compile_error(\"range() step argument must not be zero\")\n    # Avoid exception raising method here as exception raising depends on this method\n    return type._force_value_cast((start, stop, step), internal.types.range.range)\n\n@overload\ndef range(stop: Literal[int]):\n    import internal.types.range\n\n    # Avoid exception raising method here as exception raising depends on this method\n    return type._force_value_cast((0, stop, 1), internal.types.range.range)\n\ndef enumerate(tup):\n    i = -1\n    return tuple(((i := i + 1), t) for t in tup)\n    i\n\ndef tuple(*args):\n    return args\n\ndef has_rtti(T: type):\n    pass\n\nclass function:\n    def realized(fn, *args):\n        pass\n\n    def overloads(T: type, F: Literal[str]):\n        pass\n\n    def args(F):  # function: (i, name)\n        pass\n\n    def has_type(F, i: Literal[int]):\n        pass\n\n    def get_type(F, i: Literal[int]):\n        pass\n\n    @no_argument_wrap\n    def can_call(F, *args, **kwargs):\n        pass\n\n    def wrap_args(F, *args, **kwargs):\n        pass\n\n    def has_default(F, i: Literal[int]):\n        pass\n\n    def get_default(F, i: Literal[int]):\n        pass\n\ndef vars(obj, with_index: Literal[bool] = False):\n    pass\n\ndef vars_types(T: type, with_index: Literal[bool] = False):\n    pass\n\ndef tuple_type(T: type, N: Literal[int]):\n    pass\n\n# Compile-time generated\ndef format(fmt: Literal[str], arg: Literal[str]) -> Literal[str]:\n    \"\"\"%% is used instead of {} for arguments.\"\"\"\n    pass\n\n# Compile-time generated\ndef int_to_string(val: Literal[int]) -> Literal[str]:\n    pass\n\ndef loop_string(\n    start: Literal[int], stop: Literal[int],\n    val: Literal[str], delim: Literal[str]\n) -> Literal[str]:\n    if start >= stop:\n        return \"\"\n    elif start + 1 == stop:\n        return static.format(val, int_to_string(start))\n    else:\n        return static.format(val, int_to_string(start)) + delim + loop_string(start + 1, stop, val, delim)\n"
  },
  {
    "path": "stdlib/internal/str.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n_MAX: Literal[int] = 0x7FFFFFFFFFFFFFFF\n\n@extend\nclass str:\n    # Magic methods\n\n    def __hash__(self) -> int:\n        h = 0\n        p, n = self.ptr, self.len\n        i = 0\n        while i < n:\n            h = 31 * h + int(p[i])\n            i += 1\n        return h\n\n    def __lt__(self, other: str) -> bool:\n        return self._cmp(other) < 0\n\n    def __le__(self, other: str) -> bool:\n        return self._cmp(other) <= 0\n\n    def __gt__(self, other: str) -> bool:\n        return self._cmp(other) > 0\n\n    def __ge__(self, other: str) -> bool:\n        return self._cmp(other) >= 0\n\n    def __repr__(self) -> str:\n        v = _strbuf(len(self) + 2)\n\n        q, qe = \"'\", \"\\\\'\"\n        found_single = False\n        found_double = False\n        for c in self:\n            if c == \"'\":\n                found_single = True\n            elif c == '\"':\n                found_double = True\n\n        if found_single and not found_double:\n            q, qe = '\"', '\\\\\"'\n\n        v.append(q)\n        for c in self:\n            d = c\n            if c == \"\\n\":\n                d = \"\\\\n\"\n            elif c == \"\\r\":\n                d = \"\\\\r\"\n            elif c == \"\\t\":\n                d = \"\\\\t\"\n            elif c == \"\\\\\":\n                d = \"\\\\\\\\\"\n            elif c == q:\n                d = qe\n            else:\n                b = int(c.ptr[0])\n                if not (32 <= b <= 126):\n                    h = \"0123456789abcdef\"\n                    v.append(\"\\\\x\")\n                    v.append(h[b // 16])\n                    v.append(h[b % 16])\n                    d = \"\"\n            if d:\n                v.append(d)\n        v.append(q)\n        return v.__str__()\n\n    def __getitem__(self, idx: int) -> str:\n        if idx < 0:\n            idx += len(self)\n        if not (0 <= idx < len(self)):\n            raise IndexError(\"string index out of range\")\n        return str(self.ptr + idx, 1)\n\n    def __getitem__(self, s: Slice) -> str:\n        if s.start is None and s.stop is None and s.step is None:\n            return self.__copy__()\n        elif s.step is None:\n            start, stop, step, length = s.adjust_indices(len(self))\n            return str(self.ptr + start, length)\n        else:\n            start, stop, step, length = s.adjust_indices(len(self))\n            return self._make_from_range(start, stop, step, length)\n\n    def _make_from_range(self, start: int, stop: int, step: int, length: int) -> str:\n        p = Ptr[byte](length)\n        j = 0\n        for i in range(start, stop, step):\n            p[j] = self.ptr[i]\n            j += 1\n        return str(p, length)\n\n    def __iter__(self) -> Generator[str]:\n        i = 0\n        n = len(self)\n        while i < n:\n            yield str(self.ptr + i, 1)\n            i += 1\n\n    def __reversed__(self) -> Generator[str]:\n        i = len(self) - 1\n        while i >= 0:\n            yield str(self.ptr + i, 1)\n            i -= 1\n\n    def __mul__(self, x: int) -> str:\n        total = x * self.len\n        p = Ptr[byte](total)\n        n = 0\n        for _ in range(x):\n            str.memcpy(p + n, self.ptr, self.len)\n            n += self.len\n        return str(p, total)\n\n    def _cmp(self, other: str) -> int:\n        n = min(self.len, other.len)\n        i = 0\n        while i < n:\n            c1 = self.ptr[i]\n            c2 = other.ptr[i]\n            if c1 != c2:\n                return int(c1) - int(c2)\n            i += 1\n        return self.len - other.len\n\nimport algorithms.strings as algorithms\n\n@extend\nclass str:\n    def __contains__(self, pattern: str) -> bool:\n        return self.find(pattern) >= 0\n\n    # Helper methods\n\n    def _isdigit(a: byte) -> bool:\n        return _C.isdigit(i32(int(a))) != i32(0)\n\n    def _isspace(a: byte) -> bool:\n        return _C.isspace(i32(int(a))) != i32(0)\n\n    def _isupper(a: byte) -> bool:\n        return _C.isupper(i32(int(a))) != i32(0)\n\n    def _islower(a: byte) -> bool:\n        return _C.islower(i32(int(a))) != i32(0)\n\n    def _isalpha(a: byte) -> bool:\n        return _C.isalpha(i32(int(a))) != i32(0)\n\n    def _isalnum(a: byte) -> bool:\n        return _C.isalnum(i32(int(a))) != i32(0)\n\n    def _toupper(a: byte) -> byte:\n        return byte(int(_C.toupper(i32(int(a)))))\n\n    def _tolower(a: byte) -> byte:\n        return byte(int(_C.tolower(i32(int(a)))))\n\n    def _slice(self, i: int, j: int) -> str:\n        return str(self.ptr + i, j - i)\n\n    def _at(self, i: int) -> str:\n        return str(self.ptr + i, 1)\n\n    def join(self, l: Generator[str]) -> str:\n        buf = _strbuf()\n        if len(self) == 0:\n            for a in l:\n                buf.append(a)\n        else:\n            first = True\n            for a in l:\n                if first:\n                    first = False\n                else:\n                    buf.append(self)\n                buf.append(a)\n        return buf.__str__()\n\n    def join(self, l: List[str]) -> str:\n        if len(l) == 0:\n            return \"\"\n        if len(l) == 1:\n            return l[0]\n        if len(self) == 0:\n            return str.cat(l)\n\n        # compute length\n        n = 0\n        i = 0\n        while i < len(l):\n            n += len(l[i])\n            if i < len(l) - 1:\n                n += len(self)\n            i += 1\n\n        # copy to new buffer\n        p = Ptr[byte](n)\n        r = 0\n        i = 0\n        while i < len(l):\n            str.memcpy(p + r, l[i].ptr, len(l[i]))\n            r += len(l[i])\n            if i < len(l) - 1:\n                str.memcpy(p + r, self.ptr, len(self))\n                r += len(self)\n            i += 1\n\n        return str(p, n)\n\n    def isdigit(self) -> bool:\n        if len(self) == 0:\n            return False\n\n        for i in range(len(self)):\n            if not str._isdigit(self.ptr[i]):\n                return False\n        return True\n\n    def islower(self) -> bool:\n        cased = False\n\n        # For empty strings\n        if len(self) == 0:\n            return False\n\n        # For single character strings\n        if len(self) == 1:\n            return str._islower(self.ptr[0])\n\n        for i in range(len(self)):\n            if str._isupper(self.ptr[i]):\n                return False\n            elif not cased and str._islower(self.ptr[i]):\n                cased = True\n        return cased\n\n    def isupper(self) -> bool:\n        cased = False\n\n        # For empty strings\n        if len(self) == 0:\n            return False\n\n        # For single character strings\n        if len(self) == 1:\n            return str._isupper(self.ptr[0])\n\n        for i in range(len(self)):\n            if str._islower(self.ptr[i]):\n                return False\n            elif not cased and str._isupper(self.ptr[i]):\n                cased = True\n        return cased\n\n    def isalnum(self) -> bool:\n        if len(self) == 0:\n            return False\n\n        for i in range(len(self)):\n            if not str._isalnum(self.ptr[i]):\n                return False\n        return True\n\n    def isalpha(self) -> bool:\n        if len(self) == 0:\n            return False\n\n        for i in range(len(self)):\n            if not str._isalpha(self.ptr[i]):\n                return False\n        return True\n\n    def isspace(self) -> bool:\n        if len(self) == 0:\n            return False\n\n        for i in range(len(self)):\n            if not str._isspace(self.ptr[i]):\n                return False\n        return True\n\n    def istitle(self) -> bool:\n\n        # For empty strings\n        if len(self) == 0:\n            return False\n\n        # For single character strings\n        if len(self) == 1:\n            return str._isupper(self.ptr[0])\n\n        cased = False\n        prev_is_cased = False\n        for i in range(len(self)):\n            if str._isupper(self.ptr[i]):\n                if prev_is_cased:\n                    return False\n                prev_is_cased = True\n                cased = True\n            elif str._islower(self.ptr[i]):\n                if not prev_is_cased:\n                    return False\n                prev_is_cased = True\n                cased = True\n            else:\n                prev_is_cased = False\n        return cased\n\n    def capitalize(self) -> str:\n        n = len(self)\n        if n > 0:\n            p = Ptr[byte](n)\n            p[0] = str._toupper(self.ptr[0])\n            for i in range(1, n):\n                p[i] = str._tolower(self.ptr[i])\n            return str(p, n)\n        return \"\"\n\n    def isdecimal(self) -> bool:\n        if len(self) == 0:\n            return False\n\n        for i in range(len(self)):\n            # test ascii values 48-57 == 0-9\n            if not (48 <= int(self.ptr[i]) <= 57):\n                return False\n        return True\n\n    def lower(self) -> str:\n        # Empty string\n        n = len(self)\n        if n == 0:\n            return \"\"\n        p = Ptr[byte](n)\n        for i in range(n):\n            p[i] = str._tolower(self.ptr[i])\n        return str(p, n)\n\n    def upper(self) -> str:\n        # Empty string\n        n = len(self)\n        if n == 0:\n            return \"\"\n        p = Ptr[byte](n)\n        for i in range(n):\n            p[i] = str._toupper(self.ptr[i])\n        return str(p, n)\n\n    def isascii(self) -> bool:\n        for i in range(len(self)):\n            if int(self.ptr[i]) >= 128:\n                return False\n        return True\n\n    def casefold(self) -> str:\n        return self.lower()\n\n    def swapcase(self) -> str:\n        # Empty string\n        n = len(self)\n        if n == 0:\n            return \"\"\n        p = Ptr[byte](n)\n        for i in range(n):\n            if str._islower(self.ptr[i]):\n                p[i] = str._toupper(self.ptr[i])\n            elif str._isupper(self.ptr[i]):\n                p[i] = str._tolower(self.ptr[i])\n            else:\n                p[i] = self.ptr[i]\n        return str(p, n)\n\n    def title(self) -> str:\n        prev_is_cased = False\n\n        n = len(self)\n        if n == 0:\n            return \"\"\n\n        p = Ptr[byte](n)\n        for i in range(n):\n            if str._islower(self.ptr[i]):\n                # lowercase to uppercase\n                if not prev_is_cased:\n                    p[i] = str._toupper(self.ptr[i])\n                else:\n                    p[i] = self.ptr[i]\n                prev_is_cased = True\n            elif str._isupper(self.ptr[i]):\n                # uppercase to lowercase\n                if prev_is_cased:\n                    p[i] = str._tolower(self.ptr[i])\n                else:\n                    p[i] = self.ptr[i]\n                prev_is_cased = True\n            else:\n                p[i] = self.ptr[i]\n                prev_is_cased = False\n        return str(p, n)\n\n    def isnumeric(self) -> bool:\n        return self.isdecimal()\n\n    def _build(*args):\n        total = 0\n        for t in args:\n            if isinstance(t, str):\n                total += len(t)\n            else:\n                total += len(t[0]) * t[1]\n        p = Ptr[byte](total)\n        i = 0\n\n        for t in args:\n            if isinstance(t, str):\n                str.memcpy(p + i, t.ptr, t.len)\n                i += t.len\n            else:\n                s, n = t\n                for _ in range(n):\n                    str.memcpy(p + i, s.ptr, s.len)\n                    i += s.len\n\n        return str(p, total)\n\n    def ljust(self, width: int, fillchar: str = \" \") -> str:\n        if len(fillchar) != 1:\n            raise ValueError(\"The fill character must be exactly one character long\")\n        if width <= len(self):\n            return self\n        return str._build(self, (fillchar, width - len(self)))\n\n    def rjust(self, width: int, fillchar: str = \" \") -> str:\n        if len(fillchar) != 1:\n            raise ValueError(\"The fill character must be exactly one character long\")\n        if width <= len(self):\n            return self\n        return str._build((fillchar, width - len(self)), self)\n\n    def center(self, width: int, fillchar: str = \" \") -> str:\n        if len(fillchar) != 1:\n            raise ValueError(\"The fill character must be exactly one character long\")\n        if width <= len(self):\n            return self\n\n        pad = width - len(self)\n        left_pad = pad // 2\n        right_pad = width - len(self) - left_pad\n        return str._build((fillchar, left_pad), self, (fillchar, right_pad))\n\n    def zfill(self, width: int) -> str:\n        if len(self) >= width:\n            return self\n\n        plus = byte(43)   # +\n        minus = byte(45)  # -\n        zero = byte(48)   # 0\n\n        zf = self.rjust(width, '0')\n        fill = width - len(self)\n        p = zf.ptr\n\n        if len(self) > 0 and (p[fill] == plus or p[fill] == minus):\n            p[0] = p[fill]\n            p[fill] = zero\n\n        return zf\n\n    def count(self, sub: str, start: int = 0, end: Optional[int] = None) -> int:\n        end: int = end if end is not None else len(self)\n        start, end = self._correct_indices(start, end)\n        if end - start < len(sub):\n            return 0\n        return algorithms.count(self._slice(start, end), sub)\n\n    def find(self, sub: str, start: int = 0, end: Optional[int] = None) -> int:\n        end: int = end if end is not None else len(self)\n        start, end = self._correct_indices(start, end)\n        if end - start < len(sub):\n            return -1\n        pos = algorithms.find(self._slice(start, end), sub)\n        return pos if pos < 0 else pos + start\n\n    def rfind(self, sub: str, start: int = 0, end: Optional[int] = None) -> int:\n        end: int = end if end is not None else len(self)\n        start, end = self._correct_indices(start, end)\n        if end - start < len(sub):\n            return -1\n        pos = algorithms.rfind(self._slice(start, end), sub)\n        return pos if pos < 0 else pos + start\n\n    def isidentifier(self) -> bool:\n        # empty string\n        if len(self) == 0:\n            return False\n\n        # is not a letter or _\n        first = self._at(0)\n        if not first.isalpha():\n            if first != \"_\":\n                return False\n\n        if first.isalpha() or first == \"_\":\n            for i in range(1, len(self)):\n                ith = self._at(i)\n                if not ith.isalpha():\n                    if not ith.isdecimal():\n                        if ith != \"_\":\n                            return False\n\n        return True\n\n    def isprintable(self) -> bool:\n        for i in range(len(self)):\n            if not (31 < int(self.ptr[i]) < 128):\n                return False\n        return True\n\n    def _has_char(self, chars: str) -> bool:\n        s = self._at(0)\n        if chars:\n            for c in chars:\n                if s == c:\n                    return True\n            return False\n        else:\n            return s.isspace()\n\n    def lstrip(self, chars: str = \"\") -> str:\n        i = 0\n        while i < len(self) and self._at(i)._has_char(chars):\n            i += 1\n        return self._slice(i, len(self))\n\n    def rstrip(self, chars: str = \"\") -> str:\n        i = len(self) - 1\n        while i >= 0 and self._at(i)._has_char(chars):\n            i -= 1\n        return self._slice(0, i + 1)\n\n    def strip(self, chars: str = \"\") -> str:\n        return self.lstrip(chars).rstrip(chars)\n\n    def partition(self, sep: str) -> Tuple[str, str, str]:\n        if not sep:\n            raise ValueError(\"empty separator\")\n        pos = algorithms.find(self, sep)\n        if pos < 0:\n            return self, \"\", \"\"\n        return self._slice(0, pos), sep, self._slice(pos + len(sep), len(self))\n\n    def rpartition(self, sep: str) -> Tuple[str, str, str]: # XXX\n        if not sep:\n            raise ValueError(\"empty separator\")\n        pos = algorithms.rfind(self, sep)\n        if pos < 0:\n            return \"\", \"\", self\n        return self._slice(0, pos), sep, self._slice(pos + len(sep), len(self))\n\n    def split(self, sep: Optional[str] = None, maxsplit: int = -1) -> List[str]:\n        if sep is None:\n            return self._split_whitespace(\n                maxsplit if maxsplit >= 0 else _MAX\n            )\n        sep: str = sep\n\n        if len(sep) == 0:\n            raise ValueError(\"empty separator\")\n\n        # special case for length-1 pattern\n        if len(sep) == 1:\n            return self._split_char(sep.ptr[0], maxsplit if maxsplit >= 0 else _MAX)\n\n        MAX_PREALLOC = 12\n        maxsplit = maxsplit if maxsplit >= 0 else _MAX\n        prealloc_size = MAX_PREALLOC if maxsplit >= MAX_PREALLOC else maxsplit + 1\n        v = List[str](capacity=prealloc_size)\n        i = 0\n        j = 0\n        n = len(self)\n\n        while maxsplit > 0:\n            maxsplit -= 1\n            pos = algorithms.find(self._slice(i, n), sep)\n            if pos < 0:\n                break\n            j = i + pos\n            v.append(self._slice(i, j))\n            i = j + len(sep)\n\n        v.append(self._slice(i, n))\n        return v\n\n    def rsplit(self, sep: Optional[str] = None, maxsplit: int = -1) -> List[str]:\n        if sep is None:\n            return self._rsplit_whitespace(\n                maxsplit if maxsplit >= 0 else _MAX\n            )\n        sep: str = sep\n\n        if len(sep) == 0:\n            raise ValueError(\"empty separator\")\n\n        # special case for length-1 pattern\n        if len(sep) == 1:\n            return self._rsplit_char(sep.ptr[0], maxsplit if maxsplit >= 0 else _MAX)\n\n        MAX_PREALLOC = 12\n        maxsplit = maxsplit if maxsplit >= 0 else _MAX\n        prealloc_size = MAX_PREALLOC if maxsplit >= MAX_PREALLOC else maxsplit + 1\n        v = List[str](capacity=prealloc_size)\n        i = 0\n        j = len(self)\n        n = j\n\n        while maxsplit > 0:\n            maxsplit -= 1\n            pos = algorithms.rfind(self._slice(0, j), sep)\n            if pos < 0:\n                break\n            v.append(self._slice(pos + len(sep), j))\n            j = pos\n\n        v.append(self._slice(0, j))\n        v.reverse()\n        return v\n\n    def splitlines(self, keepends: bool = False) -> List[str]:\n        v = []\n        i = 0\n        j = 0\n        n = len(self)\n\n        break_r = byte(13)  # \\r\n        break_n = byte(10)  # \\n\n\n        while i < n:\n            while i < n and not (self.ptr[i] == break_r or self.ptr[i] == break_n):\n                i += 1\n\n            eol = i\n            if i < n:\n                if self.ptr[i] == break_r and i + 1 < n and self.ptr[i + 1] == break_n:\n                    i += 2\n                else:\n                    i += 1\n                if keepends:\n                    eol = i\n\n            if j == 0 and eol == n:\n                v.append(self)\n                break\n\n            v.append(self._slice(j, eol))\n            j = i\n\n        return v\n\n    def startswith(\n        self, prefix: str, start: int = 0, end: Optional[int] = None\n    ) -> bool:\n        end: int = end if end is not None else len(self)\n        if end < 0:\n            end += len(self)\n        elif start < 0:\n            start += len(self)\n\n        # length prefix is longer than range of string being compared to\n        if start + len(prefix) > len(self):\n            return False\n\n        # length of prefix is longer than range of string[start:end]\n        if end - start < len(prefix):\n            return False\n\n        # prefix is an empty string\n        if not prefix:\n            return True\n\n        return prefix == self._slice(start, start + len(prefix))\n\n    def endswith(self, suffix: str, start: int = 0, end: Optional[int] = None) -> bool:\n        end: int = end if end is not None else len(self)\n        if end < 0:\n            end += len(self)\n        elif start < 0:\n            start += len(self)\n        if end > len(self):\n            end = len(self)\n\n        # length prefix is longer than range of string being compared to\n        if end - start < len(suffix) or start > len(self):\n            return False\n\n        if end - len(suffix) > start:\n            start = end - len(suffix)\n\n        # length of prefix is longer than range of string[start:end]\n        if end - start < len(suffix):\n            return False\n\n        # prefix is an empty string\n        if not suffix:\n            return True\n\n        return suffix == self._slice(start, start + len(suffix))\n\n    def index(self, sub: str, start: int = 0, end: Optional[int] = None) -> int:\n        i = self.find(sub, start, end)\n        if i == -1:\n            raise ValueError(\"substring not found\")\n        else:\n            return i\n\n    def rindex(self, sub: str, start: int = 0, end: Optional[int] = None) -> int:\n        i = self.rfind(sub, start, end)\n        if i == -1:\n            raise ValueError(\"substring not found\")\n        else:\n            return i\n\n    def replace(self, old: str, new: str, maxcount: int = -1) -> str:\n        return self._replace(old, new, maxcount)\n\n    def expandtabs(self, tabsize: int = 8) -> str:\n        i = 0\n        j = 0\n        p = self.ptr\n        e = p + len(self)\n\n        break_r = byte(13)  # \\r\n        break_n = byte(10)  # \\n\n        tab = byte(9)       # \\t\n        space = byte(32)    # ' '\n\n        def overflow():\n            raise OverflowError(\"result too long\")\n\n        while p < e:\n            if p[0] == tab:\n                if tabsize > 0:\n                    incr = tabsize - (j % tabsize)\n                    if j > _MAX - incr:\n                        overflow()\n                    j += incr\n            else:\n                if j > _MAX - 1:\n                    overflow()\n                j += 1\n                if p[0] == break_n or p[0] == break_r:\n                    if i > _MAX - j:\n                        overflow()\n                    i += j\n                    j = 0\n            p += 1\n\n        if i > _MAX - j:\n            overflow()\n\n        u_len = i + j\n        u = Ptr[byte](u_len)\n        j = 0\n        q = u\n        p = self.ptr\n\n        while p < e:\n            if p[0] == tab:\n                if tabsize > 0:\n                    i = tabsize - (j % tabsize)\n                    j += i\n                    while True:\n                        k = i\n                        i -= 1\n                        if k == 0:\n                            break\n                        q[0] = space\n                        q += 1\n            else:\n                j += 1\n                q[0] = p[0]\n                q += 1\n                if p[0] == break_n or p[0] == break_r:\n                    j = 0\n            p += 1\n\n        return str(u, u_len)\n\n    def translate(self, map) -> str:\n        n = len(self)\n        m = 0\n\n        for i in range(n):\n            key = int(self.ptr[i])\n            if key in map:\n                val = map[key]\n                if val is not None:\n                    m += len(val)\n            else:\n                m += 1\n\n        p = Ptr[byte](m)\n        q = p\n\n        for i in range(n):\n            key = int(self.ptr[i])\n            if key in map:\n                val = map[key]\n                if val is not None:\n                    str.memcpy(q, val.ptr, len(val))\n                    q += len(val)\n            else:\n                q[0] = self.ptr[i]\n                q += 1\n\n        return str(p, m)\n\n\n    # Internal helpers\n\n    def _correct_indices(self, start: int, end: int) -> Tuple[int, int]:\n        n = len(self)\n\n        if end > n:\n            end = n\n        elif end < 0:\n            end += n\n            if end < 0:\n                end = 0\n\n        if start < 0:\n            start += n\n            if start < 0:\n                start = 0\n\n        return (start, end)\n\n    def _split_whitespace(self, maxcount: int) -> List[str]:\n        PREALLOC_MAX = 12\n        l = List[str](PREALLOC_MAX if maxcount >= PREALLOC_MAX else maxcount + 1)\n\n        str_len = len(self)\n        i = 0\n        j = 0\n        while maxcount > 0:\n            maxcount -= 1\n            while i < str_len and str._isspace(self.ptr[i]):\n                i += 1\n            if i == str_len:\n                break\n            j = i\n            i += 1\n            while i < str_len and not str._isspace(self.ptr[i]):\n                i += 1\n            l.append(self._slice(j, i))\n\n        if i < str_len:\n            while i < str_len and str._isspace(self.ptr[i]):\n                i += 1\n            if i != str_len:\n                l.append(self._slice(i, str_len))\n\n        return l\n\n    def _rsplit_whitespace(self, maxcount: int) -> List[str]:\n        PREALLOC_MAX = 12\n        l = List[str](PREALLOC_MAX if maxcount >= PREALLOC_MAX else maxcount + 1)\n\n        str_len = len(self)\n        i = str_len - 1\n        j = str_len - 1\n        while maxcount > 0:\n            maxcount -= 1\n            while i >= 0 and str._isspace(self.ptr[i]):\n                i -= 1\n            if i < 0:\n                break\n            j = i\n            i -= 1\n            while i >= 0 and not str._isspace(self.ptr[i]):\n                i -= 1\n            l.append(self._slice(i + 1, j + 1))\n\n        if i >= 0:\n            while i >= 0 and str._isspace(self.ptr[i]):\n                i -= 1\n            if i >= 0:\n                l.append(self._slice(0, i + 1))\n\n        l.reverse()\n        return l\n\n    def _split_char(self, char: byte, maxcount: int) -> List[str]:\n        PREALLOC_MAX = 12\n        l = List[str](PREALLOC_MAX if maxcount >= PREALLOC_MAX else maxcount + 1)\n\n        str_len = len(self)\n        i = 0\n        j = 0\n\n        while i < str_len and maxcount > 0:\n            if self.ptr[i] == char:\n                l.append(self._slice(j, i))\n                j = i + 1\n                maxcount -= 1\n            i += 1\n\n        l.append(self._slice(j, str_len))\n        return l\n\n    def _rsplit_char(self, char: byte, maxcount: int) -> List[str]:\n        PREALLOC_MAX = 12\n        l = List[str](PREALLOC_MAX if maxcount >= PREALLOC_MAX else maxcount + 1)\n\n        str_len = len(self)\n        i = str_len - 1\n        j = str_len - 1\n\n        while i >= 0 and maxcount > 0:\n            if self.ptr[i] == char:\n                l.append(self._slice(i + 1, j + 1))\n                j = i - 1\n                maxcount -= 1\n            i -= 1\n\n        l.append(self._slice(0, j + 1))\n        l.reverse()\n        return l\n\n    def _findchar(self, c: byte):\n        return _C.memchr(self.ptr, i32(int(c)), len(self))\n\n    def _countchar(self, c: byte, maxcount: int):\n        count = 0\n        start = self.ptr\n        end = start + len(self)\n\n        while True:\n            start = str(start, end - start)._findchar(c)\n            if not start:\n                break\n            count += 1\n            if count >= maxcount:\n                break\n            start += 1\n        return count\n\n    def _replace_interleave(self, to: str, maxcount: int):\n        self_s = self.ptr\n        self_len = len(self)\n        to_len = len(to)\n        to_s = to.ptr\n        count = 0\n        i = 0\n\n        if maxcount <= self_len:\n            count = maxcount\n        else:\n            count = self_len + 1\n\n        # assert count > 0\n        if to_len > (_MAX - self_len) // count:\n            raise OverflowError(\"replace bytes is too long\")\n\n        result_len = count * to_len + self_len\n        result_s = Ptr[byte](result_len)\n        result_s0 = result_s\n\n        if to_len > 1:\n            str.memcpy(result_s, to_s, to_len)\n            result_s += to_len\n            count -= 1\n\n            while i < count:\n                result_s[0] = self_s[0]\n                result_s += 1\n                self_s += 1\n                str.memcpy(result_s, to_s, to_len)\n                result_s += to_len\n                i += 1\n        else:\n            result_s[0] = to_s[0]\n            result_s += to_len\n            count -= 1\n\n            while i < count:\n                result_s[0] = self_s[0]\n                result_s += 1\n                self_s += 1\n                result_s[0] = to_s[0]\n                result_s += to_len\n                i += 1\n\n        str.memcpy(result_s, self_s, self_len - i)\n        return str(result_s0, result_len)\n\n    def _replace_delete_single_character(self, from_c: byte, maxcount: int):\n        self_len = len(self)\n        self_s = self.ptr\n\n        count = self._countchar(from_c, maxcount)\n        if count == 0:\n            return self\n\n        result_len = self_len - count\n        # assert result_len >= 0\n        result_s = Ptr[byte](result_len)\n        result_s0 = result_s\n\n        start = self_s\n        end = self_s + self_len\n        while count > 0:\n            count -= 1\n            nxt = str(start, end - start)._findchar(from_c)\n            if not nxt:\n                break\n            str.memcpy(result_s, start, nxt - start)\n            result_s += nxt - start\n            start = nxt + 1\n\n        str.memcpy(result_s, start, end - start)\n        return str(result_s0, result_len)\n\n    def _replace_delete_substring(self, from_s: str, maxcount: int):\n        self_len = len(self)\n        self_s = self.ptr\n        from_len = len(from_s)\n\n        count = algorithms.count_with_max(self, from_s, maxcount)\n        if count == 0:\n            return self\n\n        result_len = self_len - (count * from_len)\n        # assert result_len >= 0\n        result_s = Ptr[byte](result_len)\n        result_s0 = result_s\n\n        start = self_s\n        end = self_s + self_len\n        while count > 0:\n            count -= 1\n            offset = algorithms.find(str(start, end - start), from_s)\n            if offset == -1:\n                break\n            nxt = start + offset\n            str.memcpy(result_s, start, nxt - start)\n            result_s += nxt - start\n            start = nxt + from_len\n\n        str.memcpy(result_s, start, end - start)\n        return str(result_s0, result_len)\n\n    def _replace_single_character_in_place(self, from_c: byte, to_c: byte, maxcount: int):\n        self_s = self.ptr\n        self_len = len(self)\n\n        nxt = self._findchar(from_c)\n        if not nxt:\n            return self\n\n        result_s = Ptr[byte](self_len)\n        str.memcpy(result_s, self_s, self_len)\n\n        start = result_s + (nxt - self_s)\n        start[0] = to_c\n        start += 1\n        end = result_s + self_len\n        maxcount -= 1\n\n        while maxcount > 0:\n            maxcount -= 1\n            nxt = str(start, end - start)._findchar(from_c)\n            if not nxt:\n                break\n            nxt[0] = to_c\n            start = nxt + 1\n\n        return str(result_s, self_len)\n\n    def _replace_substring_in_place(self, from_s: str, to: str, maxcount: int):\n        self_s = self.ptr\n        self_len = len(self)\n        from_len = len(from_s)\n        to_s = to.ptr\n\n        offset = algorithms.find(self, from_s)\n        if offset == -1:\n            return self\n\n        result_s = Ptr[byte](self_len)\n        str.memcpy(result_s, self_s, self_len)\n\n        start = result_s + offset\n        str.memcpy(start, to_s, from_len)\n        start += from_len\n        end = result_s + self_len\n        maxcount -= 1\n\n        while maxcount > 0:\n            maxcount -= 1\n            offset = algorithms.find(str(start, end - start), from_s)\n            if offset == -1:\n                break\n            str.memcpy(start + offset, to_s, from_len)\n            start += offset + from_len\n\n        return str(result_s, self_len)\n\n    def _replace_single_character(self, from_c: byte, to_s: str, maxcount: int):\n        self_s = self.ptr\n        self_len = len(self)\n        to_len = len(to_s)\n\n        count = self._countchar(from_c, maxcount)\n        if count == 0:\n            return self\n\n        # assert count > 0\n        if to_len - 1 > (_MAX - self_len) // count:\n            raise OverflowError(\"replace bytes is too long\")\n\n        result_len = self_len + count * (to_len - 1)\n        result_s = Ptr[byte](result_len)\n        result_s0 = result_s\n\n        start = self_s\n        end = self_s + self_len\n        while count > 0:\n            count -= 1\n            nxt = str(start, end - start)._findchar(from_c)\n            if not nxt:\n                break\n\n            if nxt == start:\n                str.memcpy(result_s, to_s.ptr, to_len)\n                result_s += to_len\n                start += 1\n            else:\n                str.memcpy(result_s, start, nxt - start)\n                result_s += (nxt - start)\n                str.memcpy(result_s, to_s.ptr, to_len)\n                result_s += to_len\n                start = nxt + 1\n\n        str.memcpy(result_s, start, end - start)\n        return str(result_s0, result_len)\n\n    def _replace_substring(self, from_s: str, to_s: str, maxcount: int):\n        self_s = self.ptr\n        self_len = len(self)\n        from_len = len(from_s)\n        to_len = len(to_s)\n\n        count = algorithms.count_with_max(self, from_s, maxcount)\n        if count == 0:\n            return self\n\n        # assert count > 0\n        if to_len - from_len > (_MAX - self_len) // count:\n            raise OverflowError(\"replace bytes is too long\")\n\n        result_len = self_len + count * (to_len - from_len)\n        result_s = Ptr[byte](result_len)\n        result_s0 = result_s\n\n        start = self_s\n        end = self_s + self_len\n        while count > 0:\n            count -= 1\n            offset = algorithms.find(str(start, end - start), from_s)\n            if offset == -1:\n                break\n\n            nxt = start + offset\n            if nxt == start:\n                str.memcpy(result_s, to_s.ptr, to_len)\n                result_s += to_len\n                start += from_len\n            else:\n                str.memcpy(result_s, start, nxt - start)\n                result_s += (nxt - start)\n                str.memcpy(result_s, to_s.ptr, to_len)\n                result_s += to_len\n                start = nxt + from_len\n\n        str.memcpy(result_s, start, end - start)\n        return str(result_s0, result_len)\n\n    def _replace(self, from_s: str, to_s: str, maxcount: int):\n        self_len = len(self)\n        from_len = len(from_s)\n        to_len = len(to_s)\n\n        if self_len < from_len:\n            return self\n\n        if maxcount < 0:\n            maxcount = _MAX\n        elif maxcount == 0:\n            return self\n\n        if from_len == 0:\n            if to_len == 0:\n                return self\n            return self._replace_interleave(to_s, maxcount)\n\n        if to_len == 0:\n            if from_len == 1:\n                return self._replace_delete_single_character(from_s.ptr[0], maxcount)\n            return self._replace_delete_substring(from_s, maxcount)\n\n        if from_len == to_len:\n            if from_len == 1:\n                return self._replace_single_character_in_place(from_s.ptr[0], to_s.ptr[0], maxcount)\n            return self._replace_substring_in_place(from_s, to_s, maxcount)\n\n        if from_len == 1:\n            return self._replace_single_character(from_s.ptr[0], to_s, maxcount)\n        else:\n            return self._replace_substring(from_s, to_s, maxcount)\n"
  },
  {
    "path": "stdlib/internal/types/any.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\n\n@__internal__\nclass Any:\n    _data: Ptr[byte]\n    _typeinfo: TypeInfo\n\n    def __str__(self):\n        if self._typeinfo.repr.__raw__() != cobj():\n            return f\"Any({self._typeinfo.repr(self._data)}, {self._typeinfo})\"\n        return f\"Any({self._typeinfo})\"\n\n    def __new__() -> Any:\n        return __magic__.new(Any)\n\n    def __init__(self, obj):\n        T = type(obj)\n        self._typeinfo = TypeInfo(T)\n        if isinstance(T, ByVal):\n            p = Ptr[T](1)\n            p[0] = obj\n            self._data = p.as_byte()\n        else:\n            self._data = obj.__raw__().as_byte()\n\n    def unwrap(self, T: type) -> T:\n        if TypeInfo(T) != self._typeinfo:\n            raise TypeError(f\"Any.unwrap failed: requested {T}, but got {self._typeinfo.nice_name} instead\")\n        if isinstance(T, ByVal):\n            p = type._force_cast(self._data, Ptr[T])\n            return p[0]\n        else:\n            return type._force_cast(self._data, T)\n\n\n@extend\nclass Capsule:\n    @pure\n    @derives\n    @llvm\n    def _make(val: Ptr[T], T: type) -> Capsule[T]:\n        %0 = insertvalue { ptr } undef, ptr %val, 0\n        ret { ptr } %0\n\n    def make(val: T, T: type) -> Capsule[T]:\n        p = Ptr[T](1)\n        p[0] = val\n        return Capsule._make(p)\n\n    @pure\n    @derives\n    @llvm\n    def _ptr(ref: Capsule[T], T: type) -> Ptr[T]:\n        %0 = extractvalue { ptr } %ref, 0\n        %1 = getelementptr {=T}, ptr %0, i64 0\n        ret ptr %1\n\n    @pure\n    @derives\n    @llvm\n    def _get(ref: Capsule[T], T: type) -> T:\n        %0 = extractvalue { ptr } %ref, 0\n        %1 = getelementptr {=T}, ptr %0, i64 0\n        %2 = load {=T}, ptr %1\n        ret {=T} %2\n\n    def __init__(self, val: T):\n        self.val[0] = val\n"
  },
  {
    "path": "stdlib/internal/types/array.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom internal.gc import sizeof\n\n@extend\nclass Array:\n    @llvm\n    @pure\n    @derives\n    def __new__(ptr: Ptr[T], sz: int) -> Array[T]:\n        %0 = insertvalue { i64, ptr } undef, i64 %sz, 0\n        %1 = insertvalue { i64, ptr } %0, ptr %ptr, 1\n        ret { i64, ptr } %1\n\n    @overload\n    def __new__(sz: int) -> Array[T]:\n        return Array[T](Ptr[T](sz), sz)\n\n    def __copy__(self) -> Array[T]:\n        p = Ptr[T](self.len)\n        str.memcpy(p.as_byte(), self.ptr.as_byte(), self.len * sizeof(T))\n        return Array[T](p, self.len)\n\n    def __deepcopy__(self) -> Array[T]:\n        p = Ptr[T](self.len)\n        i = 0\n        while i < self.len:\n            p[i] = self.ptr[i].__deepcopy__()\n            i += 1\n        return Array[T](p, self.len)\n\n    def __len__(self) -> int:\n        return self.len\n\n    def __bool__(self) -> bool:\n        return bool(self.len)\n\n    def __getitem__(self, index: int) -> T:\n        return self.ptr[index]\n\n    def __setitem__(self, index: int, what: T):\n        self.ptr[index] = what\n\n    def slice(self, s: int, e: int) -> Array[T]:\n        return Array[T](self.ptr + s, e - s)\n\narray = Array\n\n# Forward declarations\n@dataclass(init=False)\nclass List:\n    len: int\n    arr: Array[T]\n    T: type = NoneType\n"
  },
  {
    "path": "stdlib/internal/types/bool.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom internal.attributes import commutative, associative\n\n@extend\nclass bool:\n    def __new__() -> bool:\n        return False\n\n    @overload\n    def __new__(what) -> bool:\n        return what.__bool__()\n\n    def __repr__(self) -> str:\n        return \"True\" if self else \"False\"\n\n    def __copy__(self) -> bool:\n        return self\n\n    def __deepcopy__(self) -> bool:\n        return self\n\n    def __bool__(self) -> bool:\n        return self\n\n    def __hash__(self) -> int:\n        return int(self)\n\n    @pure\n    @llvm\n    def __invert__(self) -> bool:\n        %0 = trunc i8 %self to i1\n        %1 = xor i1 %0, true\n        %2 = zext i1 %1 to i8\n        ret i8 %2\n\n    @pure\n    @llvm\n    def __eq__(self, other: bool) -> bool:\n        %0 = icmp eq i8 %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __ne__(self, other: bool) -> bool:\n        %0 = icmp ne i8 %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __lt__(self, other: bool) -> bool:\n        %0 = icmp ult i8 %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __gt__(self, other: bool) -> bool:\n        %0 = icmp ugt i8 %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __le__(self, other: bool) -> bool:\n        %0 = icmp ule i8 %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __ge__(self, other: bool) -> bool:\n        %0 = icmp uge i8 %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @commutative\n    @associative\n    @llvm\n    def __and__(self, other: bool) -> bool:\n        %0 = and i8 %self, %other\n        ret i8 %0\n\n    @pure\n    @commutative\n    @associative\n    @llvm\n    def __or__(self, other: bool) -> bool:\n        %0 = or i8 %self, %other\n        ret i8 %0\n\n    @pure\n    @commutative\n    @associative\n    @llvm\n    def __xor__(self, other: bool) -> bool:\n        %0 = xor i8 %self, %other\n        ret i8 %0\n\n    @pure\n    @llvm\n    def __int__(self) -> int:\n        %0 = zext i8 %self to i64\n        ret i64 %0\n\n    @pure\n    @llvm\n    def __float__(self) -> float:\n        %0 = uitofp i8 %self to double\n        ret double %0\n"
  },
  {
    "path": "stdlib/internal/types/byte.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n@extend\nclass byte:\n    @pure\n    @llvm\n    def __new__() -> byte:\n        ret i8 0\n\n    @overload\n    def __new__(b: byte) -> byte:\n        return b\n\n    @overload\n    def __new__(s: str) -> byte:\n        if s.__len__() != 1:\n            raise ValueError(\"str length must be 1 in byte constructor\")\n        return s.ptr[0]\n\n    @pure\n    @overload\n    @llvm\n    def __new__(i: int) -> byte:\n        %0 = trunc i64 %i to i8\n        ret i8 %0\n\n    def __copy__(self) -> byte:\n        return self\n\n    def __deepcopy__(self) -> byte:\n        return self\n\n    @pure\n    @llvm\n    def __bool__(self) -> bool:\n        %0 = icmp ne i8 %self, 0\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __eq__(self, other: byte) -> bool:\n        %0 = icmp eq i8 %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @overload\n    def __eq__(self, other: int) -> bool:\n        return self == byte(other)\n\n    @pure\n    @llvm\n    def __ne__(self, other: byte) -> bool:\n        %0 = icmp ne i8 %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __lt__(self, other: byte) -> bool:\n        %0 = icmp ult i8 %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __gt__(self, other: byte) -> bool:\n        %0 = icmp ugt i8 %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __le__(self, other: byte) -> bool:\n        %0 = icmp ule i8 %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __ge__(self, other: byte) -> bool:\n        %0 = icmp uge i8 %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    def __str__(self) -> str:\n        p = Ptr[byte](1)\n        p[0] = self\n        return str(p, 1)\n\n    def __repr__(self) -> str:\n        return f\"byte({str(__ptr__(self), 1).__repr__()})\"\n\n    @pure\n    @llvm\n    def __int__(self) -> int:\n        %0 = zext i8 %self to i64\n        ret i64 %0\n\n    @pure\n    @llvm\n    def __float__(self) -> float:\n        %0 = uitofp i8 %self to double\n        ret double %0\n"
  },
  {
    "path": "stdlib/internal/types/collections/dict.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n# dict implementation based on klib's khash\n\nimport internal.khash as khash\nimport internal.gc as gc\n\ndef _dict_hash(key) -> int:\n    k = key.__hash__()\n    return (k >> 33) ^ k ^ (k << 11)\n\nclass Dict:\n    _n_buckets: int\n    _size: int\n    _n_occupied: int\n    _upper_bound: int\n\n    _flags: Ptr[u32]\n    _keys: Ptr[K]\n    _vals: Ptr[V]\n\n    K: type = NoneType\n    V: type = NoneType\n\n    # Magic methods\n\n    def _init(self):\n        self._n_buckets = 0\n        self._size = 0\n        self._n_occupied = 0\n        self._upper_bound = 0\n        self._flags = Ptr[u32]()\n        self._keys = Ptr[K]()\n        self._vals = Ptr[V]()\n\n    def _init_from(self, other):\n        n = other._n_buckets\n\n        if n == 0:\n            self._init()\n            return\n\n        f = khash.__ac_fsize(n)\n        self._n_buckets = n\n        self._size = other._size\n        self._n_occupied = other._n_occupied\n        self._upper_bound = other._upper_bound\n\n        flags_copy = Ptr[u32](f)\n        keys_copy = Ptr[K](n)\n        vals_copy = Ptr[V](n)\n        str.memcpy(flags_copy.as_byte(), other._flags.as_byte(), f * gc.sizeof(u32))\n        str.memcpy(keys_copy.as_byte(), other._keys.as_byte(), n * gc.sizeof(K))\n        str.memcpy(vals_copy.as_byte(), other._vals.as_byte(), n * gc.sizeof(V))\n\n        self._flags = flags_copy\n        self._keys = keys_copy\n        self._vals = vals_copy\n\n    def __init__(self):\n        self._init()\n\n    def __init__(self, g: Generator[Tuple[K, V]]):\n        self._init()\n        for k, v in g:\n            self[k] = v\n\n    def __init__(self, other: Dict[K, V]):\n        self._init_from(other)\n\n    def __getitem__(self, key: K) -> V:\n        x = self._kh_get(key)\n        if x != self._kh_end():\n            return self._vals[x]\n        raise KeyError(str(key))\n\n    def __setitem__(self, key: K, val: V):\n        ret, x = self._kh_put(key)\n        self._vals[x] = val\n\n    def __delitem__(self, key: K):\n        x = self._kh_get(key)\n        if x != self._kh_end():\n            self._kh_del(x)\n        else:\n            raise KeyError(str(key))\n\n    def __contains__(self, key: K) -> bool:\n        return self._kh_get(key) != self._kh_end()\n\n    def __eq__(self, other: Dict[K, V]) -> bool:\n        if self.__len__() != other.__len__():\n            return False\n        for k, v in self.items():\n            if k not in other or other[k] != v:\n                return False\n        return True\n\n    def __ne__(self, other: Dict[K, V]) -> bool:\n        return not (self == other)\n\n    def __iter__(self) -> Generator[K]:\n        return self.keys()\n\n    def __len__(self) -> int:\n        return self._size\n\n    def __or__(self, other):\n        new = self.__copy__()\n        new.update(other)\n        return new\n\n    def __ior__(self, other):\n        self.update(other)\n        return self\n\n    def __copy__(self):\n        if self.__len__() == 0:\n            return Dict[K, V]()\n        n = self._n_buckets\n        f = khash.__ac_fsize(n)\n        flags_copy = Ptr[u32](f)\n        keys_copy = Ptr[K](n)\n        vals_copy = Ptr[V](n)\n        str.memcpy(flags_copy.as_byte(), self._flags.as_byte(), f * gc.sizeof(u32))\n        str.memcpy(keys_copy.as_byte(), self._keys.as_byte(), n * gc.sizeof(K))\n        str.memcpy(vals_copy.as_byte(), self._vals.as_byte(), n * gc.sizeof(V))\n        return Dict[K, V](\n            n,\n            self._size,\n            self._n_occupied,\n            self._upper_bound,\n            flags_copy,\n            keys_copy,\n            vals_copy,\n        )\n\n    def __deepcopy__(self) -> Dict[K, V]:\n        return {k.__deepcopy__(): v.__deepcopy__() for k, v in self.items()}\n\n    def __repr__(self) -> str:\n        n = self.__len__()\n        if n == 0:\n            return \"{}\"\n        else:\n            buf = _strbuf()\n            buf.append(\"{\")\n            first = True\n            for k, v in self.items():\n                if not first:\n                    buf.append(\", \")\n                else:\n                    first = False\n                buf.append(k.__repr__())\n                buf.append(\": \")\n                buf.append(v.__repr__())\n            buf.append(\"}\")\n            return buf.__str__()\n\n    # Helper methods\n\n    def __bool__(self):\n        return self.__len__() > 0\n\n    def resize(self, new_n_buckets: int):\n        self._kh_resize(new_n_buckets)\n\n    def get(self, key: K, s: V) -> V:\n        x = self._kh_get(key)\n        return self._vals[x] if x != self._kh_end() else s\n\n    def get(self, key: K) -> Optional[V]:\n        x = self._kh_get(key)\n        return self._vals[x] if x != self._kh_end() else None\n\n    def setdefault(self, key: K, val: V) -> V:\n        ret, x = self._kh_put(key)\n        if ret != 0:  # i.e. key not present\n            self._vals[x] = val\n            return val\n        return self._vals[x]\n\n    def increment(self, key: K, by: T = 1, T: type):\n        ret, x = self._kh_put(key)\n        if ret != 0:  # i.e. key not present\n            self._vals[x] = by\n        else:\n            self._vals[x] += by\n\n    def __dict_do_op_throws__(self, key: K, other: Z, op: F, F: type, Z: type):\n        x = self._kh_get(key)\n        if x == self._kh_end():\n            raise KeyError(str(key))\n        else:\n            self._vals[x] = op(self._vals[x], other)\n\n    def __dict_do_op__(self, key: K, other: Z, dflt: V, op: F, F: type, Z: type):\n        ret, x = self._kh_put(key)\n        self._vals[x] = op(dflt if ret != 0 else self._vals[x], other)\n\n    def update(self, other):\n        if isinstance(other, Dict[K, V]):\n            for k, v in other.items():\n                self[k] = v\n        else:\n            for k, v in other:\n                self[k] = v\n\n    def update(self, other: Dict[K, V]):\n        for k, v in other.items():\n            self[k] = v\n\n    def pop(self, key: K) -> V:\n        x = self._kh_get(key)\n        if x != self._kh_end():\n            v = self._vals[x]\n            self._kh_del(x)\n            return v\n        raise KeyError(str(key))\n\n    def popitem(self) -> Tuple[K, V]:\n        for k in self:\n            return (k, self.pop(k))\n        raise KeyError(\"dictionary is empty\")\n\n    def clear(self):\n        self._kh_clear()\n\n    def items(self) -> Generator[Tuple[K, V]]:\n        i = self._kh_begin()\n        while i < self._kh_end():\n            if self._kh_exist(i):\n                yield self._keys[i], self._vals[i]\n            i += 1\n\n    def keys(self) -> Generator[K]:\n        for k, _ in self.items():\n            yield k\n\n    def values(self) -> Generator[V]:\n        for _, v in self.items():\n            yield v\n\n    def copy(self):\n        return self.__copy__()\n\n    def fromkeys(ks: Generator[K], v: V, K: type, V: type) -> Dict[K, V]:\n        return {k: v for k in ks}\n\n    # Internal helpers\n\n    def _kh_clear(self):\n        if self._flags:\n            i = 0\n            n = khash.__ac_fsize(self._n_buckets)\n            while i < n:\n                self._flags[i] = u32(0xAAAAAAAA)\n                i += 1\n            self._size = 0\n            self._n_occupied = 0\n\n    def _kh_get(self, key: K) -> int:\n        if self._n_buckets:\n            step = 0\n            mask = self._n_buckets - 1\n            k = _dict_hash(key)\n            i = k & mask\n            last = i\n            while not khash.__ac_isempty(self._flags, i) and (\n                khash.__ac_isdel(self._flags, i) or self._keys[i] != key\n            ):\n                step += 1\n                i = (i + step) & mask\n                if i == last:\n                    return self._n_buckets\n            return self._n_buckets if khash.__ac_iseither(self._flags, i) else i\n        else:\n            return 0\n\n    def _kh_resize(self, new_n_buckets: int):\n        HASH_UPPER = 0.77\n        new_flags = Ptr[u32]()\n        j = 1\n\n        # round up to next power of 2\n        new_n_buckets -= 1\n        new_n_buckets |= new_n_buckets >> 1\n        new_n_buckets |= new_n_buckets >> 2\n        new_n_buckets |= new_n_buckets >> 4\n        new_n_buckets |= new_n_buckets >> 8\n        new_n_buckets |= new_n_buckets >> 16\n        new_n_buckets |= new_n_buckets >> 32\n        new_n_buckets += 1\n\n        if new_n_buckets < 4:\n            new_n_buckets = 4\n\n        if self._size >= int(new_n_buckets * HASH_UPPER + 0.5):\n            j = 0\n        else:\n            fsize = khash.__ac_fsize(new_n_buckets)\n            new_flags = Ptr[u32](fsize)\n            i = 0\n            while i < fsize:\n                new_flags[i] = u32(0xAAAAAAAA)\n                i += 1\n\n            if self._n_buckets < new_n_buckets:\n                self._keys = Ptr[K](\n                    gc.realloc(self._keys.as_byte(),\n                               new_n_buckets * gc.sizeof(K),\n                               self._n_buckets * gc.sizeof(K))\n                )\n                self._vals = Ptr[V](\n                    gc.realloc(self._vals.as_byte(),\n                               new_n_buckets * gc.sizeof(V),\n                               self._n_buckets * gc.sizeof(V))\n                )\n\n        if j:\n            j = 0\n            while j != self._n_buckets:\n                if khash.__ac_iseither(self._flags, j) == 0:\n                    key = self._keys[j]\n                    val = self._vals[j]\n                    new_mask = new_n_buckets - 1\n                    khash.__ac_set_isdel_true(self._flags, j)\n\n                    while True:\n                        step = 0\n                        k = _dict_hash(key)\n                        i = k & new_mask\n\n                        while not khash.__ac_isempty(new_flags, i):\n                            step += 1\n                            i = (i + step) & new_mask\n\n                        khash.__ac_set_isempty_false(new_flags, i)\n                        if (\n                            i < self._n_buckets\n                            and khash.__ac_iseither(self._flags, i) == 0\n                        ):\n                            self._keys[i], key = key, self._keys[i]\n                            self._vals[i], val = val, self._vals[i]\n                            khash.__ac_set_isdel_true(self._flags, i)\n                        else:\n                            self._keys[i] = key\n                            self._vals[i] = val\n                            break\n                j += 1\n\n            if self._n_buckets > new_n_buckets:\n                self._keys = Ptr[K](\n                    gc.realloc(self._keys.as_byte(),\n                               new_n_buckets * gc.sizeof(K),\n                               self._n_buckets * gc.sizeof(K))\n                )\n                self._vals = Ptr[V](\n                    gc.realloc(self._vals.as_byte(),\n                               new_n_buckets * gc.sizeof(V),\n                               self._n_buckets * gc.sizeof(V))\n                )\n\n            self._flags = new_flags\n            self._n_buckets = new_n_buckets\n            self._n_occupied = self._size\n            self._upper_bound = int(self._n_buckets * HASH_UPPER + 0.5)\n\n    def _kh_put(self, key: K) -> Tuple[int, int]:\n        if self._n_occupied >= self._upper_bound:\n            if self._n_buckets > (self._size << 1):\n                self._kh_resize(self._n_buckets - 1)\n            else:\n                self._kh_resize(self._n_buckets + 1)\n\n        mask = self._n_buckets - 1\n        step = 0\n        site = self._n_buckets\n        x = site\n        k = _dict_hash(key)\n        i = k & mask\n        if khash.__ac_isempty(self._flags, i):\n            x = i\n        else:\n            last = i\n            while not khash.__ac_isempty(self._flags, i) and (\n                khash.__ac_isdel(self._flags, i) or self._keys[i] != key\n            ):\n                if khash.__ac_isdel(self._flags, i):\n                    site = i\n                step += 1\n                i = (i + step) & mask\n                if i == last:\n                    x = site\n                    break\n\n            if x == self._n_buckets:\n                if khash.__ac_isempty(self._flags, i) and site != self._n_buckets:\n                    x = site\n                else:\n                    x = i\n\n        ret = 0\n        if khash.__ac_isempty(self._flags, x):\n            self._keys[x] = key\n            khash.__ac_set_isboth_false(self._flags, x)\n            self._size += 1\n            self._n_occupied += 1\n            ret = 1\n        elif khash.__ac_isdel(self._flags, x):\n            self._keys[x] = key\n            khash.__ac_set_isboth_false(self._flags, x)\n            self._size += 1\n            ret = 2\n\n        return (ret, x)\n\n    def _kh_del(self, x: int):\n        if x != self._n_buckets and not khash.__ac_iseither(self._flags, x):\n            khash.__ac_set_isdel_true(self._flags, x)\n            self._size -= 1\n\n    def _kh_begin(self) -> int:\n        return 0\n\n    def _kh_end(self) -> int:\n        return self._n_buckets\n\n    def _kh_exist(self, x: int) -> bool:\n        return not khash.__ac_iseither(self._flags, x)\n\ndict = Dict\n"
  },
  {
    "path": "stdlib/internal/types/collections/list.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.gc as gc\n\n@extend\nclass List:\n    @staticmethod\n    def as_list(x):\n        if isinstance(x, List):\n            return x\n        elif isinstance(x, Tuple):\n            return x\n        else:\n            return List(x)\n\n    def __init__(self):\n        self.arr = Array[T](10)\n        self.len = 0\n\n    def __init__(self, capacity: int):\n        if capacity < 0:\n            capacity = 0\n        self.arr = Array[T](capacity)\n        self.len = 0\n\n    def __init__(self, other: List[T]):\n        self.arr = Array[T](other.len)\n        self.len = 0\n        for i in other:\n            self.append(i)\n\n    def __init__(self, it: Generator[T]):\n        self.arr = Array[T](10)\n        self.len = 0\n        for i in it:\n            self.append(i)\n\n    def __init__(self, arr: Array[T], len: int):\n        self.arr = arr\n        self.len = len\n\n    def _get(self, idx: int):\n        return self.arr.ptr[idx]\n\n    def _set(self, idx: int, val: T):\n        self.arr.ptr[idx] = val\n\n    def __len__(self) -> int:\n        return self.len\n\n    def __bool__(self) -> bool:\n        return self.__len__() > 0\n\n    def __getitem__(self, idx: int) -> T:\n        if idx < 0:\n            idx += self.__len__()\n        self._idx_check(idx, \"list index out of range\")\n        return self._get(idx)\n\n    def __setitem__(self, idx: int, val: T):\n        if idx < 0:\n            idx += self.__len__()\n        self._idx_check(idx, \"list assignment index out of range\")\n        self._set(idx, val)\n\n    def __delitem__(self, idx: int):\n        if idx < 0:\n            idx += self.__len__()\n        self._idx_check(idx, \"list assignment index out of range\")\n        while idx < self.len - 1:\n            self._set(idx, self._get(idx + 1))\n            idx += 1\n        self.len -= 1\n\n    def __eq__(self, other: List[T]) -> bool:\n        if self.__len__() != other.__len__():\n            return False\n        for i in range(self.__len__()):\n            if self._get(i) != other._get(i):\n                return False\n        return True\n\n    def __ne__(self, other: List[T]) -> bool:\n        return not (self == other)\n\n    def __getitem__(self, s: Slice) -> List[T]:\n        if s.start is None and s.stop is None and s.step is None:\n            return self.__copy__()\n        if s.step is None:\n            start, stop, step, length = s.adjust_indices(self.__len__())\n            return List[T](self._copy_arr(start, stop, length), length)\n        else:\n            start, stop, step, length = s.adjust_indices(self.__len__())\n            other = List[T](length)\n            for i in range(start, stop, step):\n                other.append(self._get(i))\n            return other\n\n    def __setitem__(self, s: Slice, other: Generator[T]):\n        return self.__setitem__(s, [a for a in other])\n\n    def __setitem__(self, s: Slice, other: List[T]):\n        if s.start is None and s.stop is None and s.step is None:\n            self.clear()\n            for a in other:\n                self.append(a)\n            return\n\n        start, stop, step, length = s.adjust_indices(self.__len__())\n        if s.step is None or step == 1:\n            if other.__raw__() == self.__raw__():\n                other = other.__copy__()\n            self._assign_slice(start, stop, other.arr.ptr, other.__len__())\n        else:\n            if (step < 0 and start < stop) or (step > 0 and start > stop):\n                stop = start\n\n            seq: Optional[List[T]] = None\n            if other.__raw__() == self.__raw__():\n                seq = other.__copy__()\n            else:\n                seq = other\n\n            seq_len = seq.__len__()\n            if seq_len != length:\n                raise ValueError(\n                    f\"attempt to assign sequence of size {seq_len} to extended slice of size {length}\"\n                )\n\n            if length == 0:\n                return\n\n            cur = start\n            i = 0\n            while i < length:\n                self._set(cur, seq._get(i))\n                cur += step\n                i += 1\n\n    def __delitem__(self, s: Slice):\n        if s.start is None and s.stop is None and s.step is None:\n            self.clear()\n        else:\n            start, stop, step, length = s.adjust_indices(self.__len__())\n            if s.step is None or step == 1:\n                self._assign_slice(start, stop, Ptr[T](), 0)\n            else:\n                if length < 0:\n                    return\n\n                if step < 0:\n                    stop = start + 1\n                    start = stop + step * (length - 1) - 1\n                    step = -step\n\n                cur = start\n                i = 0\n                while cur < stop:\n                    lim = step - 1\n                    if cur + step > self.__len__():\n                        lim = self.__len__() - cur - 1\n                    str.memmove(\n                        (self.arr.ptr + (cur - i)).as_byte(),\n                        (self.arr.ptr + (cur + 1)).as_byte(),\n                        lim * gc.sizeof(T),\n                    )\n                    cur += step\n                    i += 1\n\n                cur = start + length * step\n                if cur < self.__len__():\n                    str.memmove(\n                        (self.arr.ptr + (cur - length)).as_byte(),\n                        (self.arr.ptr + cur).as_byte(),\n                        (self.__len__() - cur) * gc.sizeof(T),\n                    )\n\n                self.len -= length\n                # self._resize(self.__len__())\n\n    def __contains__(self, x: T) -> bool:\n        for a in self:\n            if a == x:\n                return True\n        return False\n\n    def __copy__(self) -> List[T]:\n        return List[T](self.arr.__copy__(), self.len)\n\n    def __deepcopy__(self) -> List[T]:\n        return [l.__deepcopy__() for l in self]\n\n    def __iter__(self) -> Generator[T]:\n        i = 0\n        N = self.len\n        p = self.arr.ptr\n        while i < N:\n            yield p[i]\n            i += 1\n\n    def __reversed__(self) -> Generator[T]:\n        i = self.len - 1\n        while i >= 0:\n            yield self._get(i)\n            i -= 1\n\n    def __add__(self, other: List[T]) -> List[T]:\n        n = self.len + other.len\n        v = List[T](n)\n        v.len = n\n        p = v.arr.ptr\n        str.memcpy(p.as_byte(),\n                   self.arr.ptr.as_byte(),\n                   self.len * gc.sizeof(T))\n        str.memcpy((p + self.len).as_byte(),\n                   other.arr.ptr.as_byte(),\n                   other.len * gc.sizeof(T))\n        return v\n\n    def __iadd__(self, other: List[T]) -> List[T]:\n        n = self.len + other.len\n        if self.arr.len < n:\n            self._resize(n)\n        str.memcpy((self.arr.ptr + self.len).as_byte(),\n                   other.arr.ptr.as_byte(),\n                   other.len * gc.sizeof(T))\n        self.len = n\n        return self\n\n    def __mul__(self, n: int) -> List[T]:\n        if n <= 0:\n            return List[T]()\n\n        new_len = self.len * n\n        v = List[T](new_len)\n        i = 0\n        while i < n:\n            j = 0\n            while j < self.len:\n                v.append(self._get(j))\n                j += 1\n            i += 1\n        return v\n\n    def __rmul__(self, n: int) -> List[T]:\n        return self.__mul__(n)\n\n    def __imul__(self, n: int) -> List[T]:\n        if n == 1:\n            return self\n\n        if n <= 0:\n            self.clear()\n            return self\n\n        len0 = self.__len__()\n        new_cap = n * len0\n        if self.arr.len < new_cap:\n            p = Ptr[T](gc.realloc(self.arr.ptr.as_byte(),\n                                  new_cap * gc.sizeof(T),\n                                  self.arr.len * gc.sizeof(T)))\n            self.arr = Array[T](p, new_cap)\n\n        idx = len0\n        i = 0\n        while i < n - 1:\n            j = 0\n            while j < len0:\n                self._set(idx, self._get(j))\n                idx += 1\n                j += 1\n            i += 1\n\n        self.len = new_cap\n        return self\n\n    def __repr__(self) -> str:\n        n = self.__len__()\n        if n == 0:\n            return \"[]\"\n        else:\n            buf = _strbuf()\n            buf.append(\"[\")\n            buf.append(self._get(0).__repr__())\n            for i in range(1, n):\n                buf.append(\", \")\n                buf.append(self._get(i).__repr__())\n            buf.append(\"]\")\n            return buf.__str__()\n\n    # Helper functions\n\n    def append(self, x: T):\n        self._resize_if_full()\n        self._set(self.len, x)\n        self.len += 1\n\n    def extend(self, itr: Generator[T]):\n        for a in itr:\n            self.append(a)\n\n    def insert(self, idx: int, x: T):\n        n = self.__len__()\n        if idx < 0:\n            idx += n\n            if idx < 0:\n                idx = 0\n        if idx > n:\n            idx = n\n        self._resize_if_full()\n        i = n\n        while i > idx:\n            self._set(i, self._get(i - 1))\n            i -= 1\n        self._set(idx, x)\n        self.len += 1\n\n    def pop(self, idx: int = -1) -> T:\n        if self.__len__() == 0:\n            raise IndexError(\"pop from empty list\")\n        if idx < 0:\n            idx += self.__len__()\n        self._idx_check(idx, \"pop index out of range\")\n        x = self._get(idx)\n        del self[idx]\n        return x\n\n    def remove(self, x: T) -> bool:\n        i = 0\n        for a in self:\n            if a == x:\n                del self[i]\n                return True\n            i += 1\n        return False\n\n    def clear(self):\n        self.len = 0\n\n    def index(self, x: T) -> int:\n        i = 0\n        for a in self:\n            if a == x:\n                return i\n            i += 1\n        raise ValueError(f\"{x} is not in list\")\n\n    def find(self, x: T) -> int:\n        i = 0\n        for a in self:\n            if a == x:\n                return i\n            i += 1\n        return -1\n\n    def count(self, x: T) -> int:\n        count = 0\n        for a in self:\n            if a == x:\n                count += 1\n        return count\n\n    def reverse(self):\n        i = 0\n        while i < self.len // 2:\n            j = self.len - i - 1\n            x = self._get(i)\n            self._set(i, self._get(j))\n            self._set(j, x)\n            i += 1\n\n    def copy(self) -> List[T]:\n        return self.__copy__()\n\n    # Internal helpers\n\n    def _idx_check(self, idx: int, msg: str):\n        if idx >= self.len or idx < 0:\n            raise IndexError(msg)\n\n    def _resize(self, new_cap: int):\n        p = Ptr[T](gc.realloc(self.arr.ptr.as_byte(),\n                              new_cap * gc.sizeof(T),\n                              self.arr.len * gc.sizeof(T)))\n        self.arr = Array[T](p, new_cap)\n\n    def _resize_if_full(self):\n        if self.len == self.arr.len:\n            new_cap = (1 + 3 * self.len) // 2\n            if new_cap <= 0:\n                new_cap = 1\n            self._resize(new_cap)\n\n    def __hash__(self) -> int:\n        # https://www.boost.org/doc/libs/1_35_0/doc/html/boost/hash_combine_id241013.html\n        seed = 0\n        for v in self:\n            seed ^= v.__hash__() + 0x9E3779B9 + (seed << 6) + (seed >> 2)\n        return seed\n\n    def _assign_slice(self, ilow: int, ihigh: int, v: Ptr[T], n: int):\n        a = self\n        L = a.len\n\n        if ilow < 0:\n            ilow = 0\n        elif ilow > L:\n            ilow = L\n\n        if ihigh < ilow:\n            ihigh = ilow\n        elif ihigh > L:\n            ihigh = L\n\n        norig = ihigh - ilow\n        assert norig >= 0\n        d = n - norig\n        if L + d == 0:\n            a.clear()\n            return\n\n        if d < 0:\n            tail = L - ihigh\n            str.memmove(\n                (a.arr.ptr + (ihigh + d)).as_byte(),\n                (a.arr.ptr + ihigh).as_byte(),\n                tail * gc.sizeof(T),\n            )\n            a._resize(L + d)\n        elif d > 0:\n            k = L\n            a._resize(k + d)\n            str.memmove(\n                (a.arr.ptr + (ihigh + d)).as_byte(),\n                (a.arr.ptr + ihigh).as_byte(),\n                (k - ihigh) * gc.sizeof(T),\n            )\n\n        k = 0\n        while k < n:\n            a._set(ilow, v[k])\n            k += 1\n            ilow += 1\n        a.len += d\n\n    def _copy_arr(self, start: int, stop: int, length: int) -> Array[T]:\n        if length <= 0:\n            return Array[T](Ptr[T](), 0)\n        return self.arr.slice(start, stop).__copy__()\n\n    def _cmp(self, other: List[T]):\n        n1 = self.__len__()\n        n2 = other.__len__()\n        nmin = n1 if n1 < n2 else n2\n        for i in range(nmin):\n            a = self._get(i)\n            b = other._get(i)\n\n            if a < b:\n                return -1\n            elif a == b:\n                continue\n            else:\n                return 1\n        if n1 < n2:\n            return -1\n        elif n1 == n2:\n            return 0\n        else:\n            return 1\n\n    def __lt__(self, other: List[T]):\n        return self._cmp(other) < 0\n\n    def __gt__(self, other: List[T]):\n        return self._cmp(other) > 0\n\n    def __le__(self, other: List[T]):\n        return self._cmp(other) <= 0\n\n    def __ge__(self, other: List[T]):\n        return self._cmp(other) >= 0\n\n    # list addition optimization helpers\n\n    def _list_add_opt_default_len(v: List[T]):\n        return v.__len__()\n\n    def _list_add_opt_default_append(ans: List[T], v: List[T]):\n        from internal.gc import sizeof\n        n = v.__len__()\n        str.memcpy((ans.arr.ptr + ans.len).as_byte(), v.arr.ptr.as_byte(), n * sizeof(T))\n        ans.len += n\n\n    def _list_add_opt_slice_len(v: List[T], s: Slice):\n        if s.start is None and s.stop is None and s.step is None:\n            return v.__len__()\n        start, stop, step, length = s.adjust_indices(v.__len__())\n        return length\n\n    def _list_add_opt_slice_append(ans: List[T], v: List[T], s: Slice):\n        from internal.gc import sizeof\n        if s.start is None and s.stop is None and s.step is None:\n            n = v.__len__()\n            str.memcpy((ans.arr.ptr + ans.len).as_byte(), v.arr.ptr.as_byte(), n * sizeof(T))\n            ans.len += n\n        elif s.step is None:\n            start, stop, step, length = s.adjust_indices(v.__len__())\n            n = stop - start\n            str.memcpy((ans.arr.ptr + ans.len).as_byte(), (v.arr.ptr + start).as_byte(), n * sizeof(T))\n            ans.len += n\n        else:\n            start, stop, step, length = s.adjust_indices(v.__len__())\n            for i in range(start, stop, step):\n                ans.arr.ptr[ans.len] = v._get(i)\n                ans.len += 1\n\n    def _list_add_opt_literal_append(ans: List[T], elem: T):\n        ans.arr.ptr[ans.len] = elem\n        ans.len += 1\n\n    def _list_add_opt_opt_new(capacity: int):\n        return List[T](capacity=capacity)\n\nlist = List\n"
  },
  {
    "path": "stdlib/internal/types/collections/set.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n# set implementation based on klib's khash\n\nfrom internal.attributes import commutative, associative\nimport internal.khash as khash\nimport internal.gc as gc\n\ndef _set_hash(key) -> int:\n    k = key.__hash__()\n    return (k >> 33) ^ k ^ (k << 11)\n\nclass Set:\n    _n_buckets: int\n    _size: int\n    _n_occupied: int\n    _upper_bound: int\n\n    _flags: Ptr[u32]\n    _keys: Ptr[K]\n\n    K: type = NoneType\n\n    # Magic methods\n    def _init(self):\n        self._n_buckets = 0\n        self._size = 0\n        self._n_occupied = 0\n        self._upper_bound = 0\n        self._flags = Ptr[u32]()\n        self._keys = Ptr[K]()\n\n    def __init__(self):\n        self._init()\n\n    def __init__(self, g: Generator[K]):\n        self._init()\n        for a in g:\n            self.add(a)\n\n    def __sub__(self, other: Set[K]) -> Set[K]:\n        return self.difference(other)\n\n    def __isub__(self, other: Set[K]) -> Set[K]:\n        self.difference_update(other)\n        return self\n\n    @commutative\n    @associative\n    def __and__(self, other: Set[K]) -> Set[K]:\n        return self.intersection(other)\n\n    def __iand__(self, other: Set[K]) -> Set[K]:\n        self.intersection_update(other)\n        return self\n\n    @commutative\n    @associative\n    def __or__(self, other: Set[K]) -> Set[K]:\n        return self.union(other)\n\n    def __ior__(self, other: Set[K]) -> Set[K]:\n        for a in other:\n            self.add(a)\n        return self\n\n    @commutative\n    @associative\n    def __xor__(self, other: Set[K]) -> Set[K]:\n        return self.symmetric_difference(other)\n\n    def __ixor__(self, other: Set[K]) -> Set[K]:\n        self.symmetric_difference_update(other)\n        return self\n\n    def __contains__(self, key: K) -> bool:\n        return self._kh_get(key) != self._kh_end()\n\n    def __eq__(self, other: Set[K]) -> bool:\n        if self.__len__() != other.__len__():\n            return False\n        for k in self:\n            if k not in other:\n                return False\n        return True\n\n    def __ne__(self, other: Set[K]) -> bool:\n        return not (self == other)\n\n    def __le__(self, other: Set[K]) -> bool:\n        return self.issubset(other)\n\n    def __ge__(self, other: Set[K]) -> bool:\n        return self.issuperset(other)\n\n    def __lt__(self, other: Set[K]) -> bool:\n        return self != other and self <= other\n\n    def __gt__(self, other: Set[K]) -> bool:\n        return self != other and self >= other\n\n    def __iter__(self) -> Generator[K]:\n        i = self._kh_begin()\n        while i < self._kh_end():\n            if self._kh_exist(i):\n                yield self._keys[i]\n            i += 1\n\n    def __len__(self) -> int:\n        return self._size\n\n    def __bool__(self) -> bool:\n        return self.__len__() != 0\n\n    def __copy__(self) -> Set[K]:\n        if self.__len__() == 0:\n            return Set[K]()\n        n = self._n_buckets\n        f = khash.__ac_fsize(n)\n        flags_copy = Ptr[u32](f)\n        keys_copy = Ptr[K](n)\n        str.memcpy(flags_copy.as_byte(), self._flags.as_byte(), f * gc.sizeof(u32))\n        str.memcpy(keys_copy.as_byte(), self._keys.as_byte(), n * gc.sizeof(K))\n        return Set[K](\n            n, self._size, self._n_occupied, self._upper_bound, flags_copy, keys_copy\n        )\n\n    def __deepcopy__(self) -> Set[K]:\n        return {s.__deepcopy__() for s in self}\n\n    def __repr__(self) -> str:\n        n = self.__len__()\n        if n == 0:\n            return \"set()\"\n        else:\n            buf = _strbuf()\n            buf.append(\"{\")\n            first = True\n            for k in self:\n                if not first:\n                    buf.append(\", \")\n                else:\n                    first = False\n                buf.append(k.__repr__())\n            buf.append(\"}\")\n            return buf.__str__()\n\n    # Helper methods\n\n    def resize(self, new_n_buckets: int):\n        self._kh_resize(new_n_buckets)\n\n    def add(self, key: K):\n        self._kh_put(key)\n\n    def update(self, other: Generator[K]):\n        for k in other:\n            self.add(k)\n\n    def remove(self, key: K):\n        x = self._kh_get(key)\n        if x != self._kh_end():\n            self._kh_del(x)\n        else:\n            raise KeyError(str(key))\n\n    def pop(self) -> K:\n        if self.__len__() == 0:\n            raise ValueError(\"empty set\")\n        for a in self:\n            self.remove(a)\n            return a\n\n    def discard(self, key: K):\n        x = self._kh_get(key)\n        if x != self._kh_end():\n            self._kh_del(x)\n\n    def difference(self, other: Set[K]) -> Set[K]:\n        s = Set[K]()\n        for a in self:\n            if a not in other:\n                s.add(a)\n        return s\n\n    def difference_update(self, other: Set[K]):\n        for a in other:\n            self.discard(a)\n\n    def intersection(self, other: Set[K]) -> Set[K]:\n        if other.__len__() < self.__len__():\n            self, other = other, self\n        s = Set[K]()\n        for a in self:\n            if a in other:\n                s.add(a)\n        return s\n\n    def intersection_update(self, other: Set[K]):\n        for a in self:\n            if a not in other:\n                self.discard(a)\n\n    def symmetric_difference(self, other: Set[K]) -> Set[K]:\n        s = Set[K]()\n        for a in self:\n            if a not in other:\n                s.add(a)\n        for a in other:\n            if a not in self:\n                s.add(a)\n        return s\n\n    def symmetric_difference_update(self, other: Set[K]):\n        for a in other:\n            if a in self:\n                self.discard(a)\n        for a in self:\n            if a in other:\n                self.discard(a)\n\n    def union(self, other: Set[K]) -> Set[K]:\n        s = Set[K]()\n        s.resize(\n            self._n_buckets if self._n_buckets >= other._n_buckets else other._n_buckets\n        )\n        for a in self:\n            s.add(a)\n        for a in other:\n            s.add(a)\n        return s\n\n    def isdisjoint(self, other: Set[K]) -> bool:\n        if other.__len__() < self.__len__():\n            self, other = other, self\n        for a in self:\n            if a in other:\n                return False\n        return True\n\n    def issubset(self, other: Set[K]) -> bool:\n        if other.__len__() < self.__len__():\n            return False\n        for a in self:\n            if a not in other:\n                return False\n        return True\n\n    def issuperset(self, other: Set[K]) -> bool:\n        return other.issubset(self)\n\n    def clear(self):\n        self._kh_clear()\n\n    def copy(self) -> Set[K]:\n        return self.__copy__()\n\n    # Internal helpers\n\n    def _kh_clear(self):\n        if self._flags:\n            i = 0\n            n = khash.__ac_fsize(self._n_buckets)\n            while i < n:\n                self._flags[i] = u32(0xAAAAAAAA)\n                i += 1\n            self._size = 0\n            self._n_occupied = 0\n\n    def _kh_get(self, key: K) -> int:\n        if self._n_buckets:\n            step = 0\n            mask = self._n_buckets - 1\n            k = _set_hash(key)\n            i = k & mask\n            last = i\n            while not khash.__ac_isempty(self._flags, i) and (\n                khash.__ac_isdel(self._flags, i) or self._keys[i] != key\n            ):\n                step += 1\n                i = (i + step) & mask\n                if i == last:\n                    return self._n_buckets\n            return self._n_buckets if khash.__ac_iseither(self._flags, i) else i\n        else:\n            return 0\n\n    def _kh_resize(self, new_n_buckets: int):\n        HASH_UPPER = 0.77\n        new_flags = Ptr[u32]()\n        j = 1\n\n        # round up to next power of 2\n        new_n_buckets -= 1\n        new_n_buckets |= new_n_buckets >> 1\n        new_n_buckets |= new_n_buckets >> 2\n        new_n_buckets |= new_n_buckets >> 4\n        new_n_buckets |= new_n_buckets >> 8\n        new_n_buckets |= new_n_buckets >> 16\n        new_n_buckets |= new_n_buckets >> 32\n        new_n_buckets += 1\n\n        if new_n_buckets < 4:\n            new_n_buckets = 4\n\n        if self._size >= int(new_n_buckets * HASH_UPPER + 0.5):\n            j = 0\n        else:\n            fsize = khash.__ac_fsize(new_n_buckets)\n            new_flags = Ptr[u32](fsize)\n            i = 0\n            while i < fsize:\n                new_flags[i] = u32(0xAAAAAAAA)\n                i += 1\n\n            if self._n_buckets < new_n_buckets:\n                self._keys = Ptr[K](\n                    gc.realloc(self._keys.as_byte(),\n                               new_n_buckets * gc.sizeof(K),\n                               self._n_buckets * gc.sizeof(K))\n                )\n\n        if j:\n            j = 0\n            while j != self._n_buckets:\n                if khash.__ac_iseither(self._flags, j) == 0:\n                    key = self._keys[j]\n                    new_mask = new_n_buckets - 1\n                    khash.__ac_set_isdel_true(self._flags, j)\n\n                    while True:\n                        step = 0\n                        k = _set_hash(key)\n                        i = k & new_mask\n\n                        while not khash.__ac_isempty(new_flags, i):\n                            step += 1\n                            i = (i + step) & new_mask\n\n                        khash.__ac_set_isempty_false(new_flags, i)\n                        if (\n                            i < self._n_buckets\n                            and khash.__ac_iseither(self._flags, i) == 0\n                        ):\n                            self._keys[i], key = key, self._keys[i]\n                            khash.__ac_set_isdel_true(self._flags, i)\n                        else:\n                            self._keys[i] = key\n                            break\n                j += 1\n\n            if self._n_buckets > new_n_buckets:\n                self._keys = Ptr[K](\n                    gc.realloc(self._keys.as_byte(),\n                               new_n_buckets * gc.sizeof(K),\n                               self._n_buckets * gc.sizeof(K))\n                )\n\n            self._flags = new_flags\n            self._n_buckets = new_n_buckets\n            self._n_occupied = self._size\n            self._upper_bound = int(self._n_buckets * HASH_UPPER + 0.5)\n\n    def _kh_put(self, key: K) -> Tuple[int, int]:\n        if self._n_occupied >= self._upper_bound:\n            if self._n_buckets > (self._size << 1):\n                self._kh_resize(self._n_buckets - 1)\n            else:\n                self._kh_resize(self._n_buckets + 1)\n\n        mask = self._n_buckets - 1\n        step = 0\n        site = self._n_buckets\n        x = site\n        k = _set_hash(key)\n        i = k & mask\n        if khash.__ac_isempty(self._flags, i):\n            x = i\n        else:\n            last = i\n            while not khash.__ac_isempty(self._flags, i) and (\n                khash.__ac_isdel(self._flags, i) or self._keys[i] != key\n            ):\n                if khash.__ac_isdel(self._flags, i):\n                    site = i\n                step += 1\n                i = (i + step) & mask\n                if i == last:\n                    x = site\n                    break\n\n            if x == self._n_buckets:\n                if khash.__ac_isempty(self._flags, i) and site != self._n_buckets:\n                    x = site\n                else:\n                    x = i\n\n        ret = 0\n        if khash.__ac_isempty(self._flags, x):\n            self._keys[x] = key\n            khash.__ac_set_isboth_false(self._flags, x)\n            self._size += 1\n            self._n_occupied += 1\n            ret = 1\n        elif khash.__ac_isdel(self._flags, x):\n            self._keys[x] = key\n            khash.__ac_set_isboth_false(self._flags, x)\n            self._size += 1\n            ret = 2\n\n        return (ret, x)\n\n    def _kh_del(self, x: int):\n        if x != self._n_buckets and not khash.__ac_iseither(self._flags, x):\n            khash.__ac_set_isdel_true(self._flags, x)\n            self._size -= 1\n\n    def _kh_begin(self) -> int:\n        return 0\n\n    def _kh_end(self) -> int:\n        return self._n_buckets\n\n    def _kh_exist(self, x: int) -> bool:\n        return not khash.__ac_iseither(self._flags, x)\n\nset = Set\n"
  },
  {
    "path": "stdlib/internal/types/collections/tuple.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\n@tuple\nclass DynamicTuple:\n    _ptr: Ptr[T]\n    _len: int\n    T: type\n\n    def __new__(x: List[T]):\n        from internal.gc import sizeof\n        n = x.__len__()\n        p = Ptr[T](n)\n        str.memcpy(p.as_byte(), x.arr.ptr.as_byte(), n * sizeof(T))\n        return DynamicTuple(p, n)\n\n    def __new__(x: Generator[T]):\n        from internal.gc import realloc, sizeof\n        n = 0\n        m = 0\n        if hasattr(x, \"__len__\"):\n            m = x.__len__()\n        elif hasattr(x, \"__length_hint__\"):\n            m = x.__length_hint__()\n        else:\n            m = 10\n        p = Ptr[T](m)\n        for a in x:\n            if n == m:\n                new_m = (1 + 3*m) // 2\n                p = Ptr[T](realloc(p.as_byte(), new_m * sizeof(T), m * sizeof(T)))\n                m = new_m\n            p[n] = a\n            n += 1\n\n        return DynamicTuple(p, n)\n\n    def __new__() -> DynamicTuple[T]:\n        return DynamicTuple(Ptr[T](), 0)\n\n    def __len__(self):\n        return self._len\n\n    def __bool__(self):\n        return self._len > 0\n\n    def _ensure_tuple(x):\n        if not isinstance(x, Tuple):\n            compile_error(\"expected tuple type\")\n\n    def __eq__(self, other):\n        DynamicTuple._ensure_tuple(other)\n\n        p = self._ptr\n        n = self._len\n\n        if static.len(other) == 0:\n            return n == 0\n\n        if n != static.len(other):\n            return False\n\n        for i in range(n):\n            if p[i] != other[i]:\n                return False\n\n        return True\n\n    def __eq__(self, other: DynamicTuple[T]):\n        p = self._ptr\n        n = self._len\n        q = other._ptr\n        m = other._len\n\n        if n != m:\n            return False\n\n        for i in range(n):\n            if p[i] != q[i]:\n                return False\n\n        return True\n\n    def __ne__(self, other):\n        DynamicTuple._ensure_tuple(other)\n        return not (self == other)\n\n    def __ne__(self, other: DynamicTuple[T]):\n        return not (self == other)\n\n    def _cmp(self, other):\n        DynamicTuple._ensure_tuple(other)\n        p = self._ptr\n        n = self._len\n\n        if static.len(other) == 0:\n            return (1 if n > 0 else 0)\n\n        m = static.len(other)\n\n        for i in range(n if n < m else m):\n            a = p[i]\n            b = other[i]\n\n            if a < b:\n                return -1\n            elif a == b:\n                pass\n            else:\n                return 1\n\n        if n < m:\n            return -1\n        elif n == m:\n            return 0\n        else:\n            return 1\n\n    def _cmp(self, other: DynamicTuple[T]):\n        p = self._ptr\n        n = self._len\n        q = other._ptr\n        m = other._len\n\n        for i in range(n if n < m else m):\n            a = p[i]\n            b = q[i]\n\n            if a < b:\n                return -1\n            elif a == b:\n                pass\n            else:\n                return 1\n\n        if n < m:\n            return -1\n        elif n == m:\n            return 0\n        else:\n            return 1\n\n    def __lt__(self, other):\n        return self._cmp(other) < 0\n\n    def __gt__(self, other):\n        return self._cmp(other) > 0\n\n    def __le__(self, other):\n        return self._cmp(other) <= 0\n\n    def __ge__(self, other):\n        return self._cmp(other) >= 0\n\n    def __lt__(self, other: DynamicTuple[T]):\n        return self._cmp(other) < 0\n\n    def __gt__(self, other: DynamicTuple[T]):\n        return self._cmp(other) > 0\n\n    def __le__(self, other: DynamicTuple[T]):\n        return self._cmp(other) <= 0\n\n    def __ge__(self, other: DynamicTuple[T]):\n        return self._cmp(other) >= 0\n\n    def __hash__(self):\n        p = self._ptr\n        n = self._len\n        seed = 0\n        for i in range(n):\n            seed = seed ^ ((p[i].__hash__() + 2654435769) + ((seed << 6) + (seed >> 2)))\n        return seed\n\n    def __iter__(self):\n        p = self._ptr\n        n = self._len\n        for i in range(n):\n            yield p[i]\n\n    def __contains__(self, item: T):\n        p = self._ptr\n        n = self._len\n        for i in range(n):\n            if p[i] == item:\n                return True\n        return False\n\n    def __getitem__(self, idx: int):\n        p = self._ptr\n        n = self._len\n        if idx < 0:\n            idx += n\n        if idx < 0 or idx >= n:\n            raise IndexError(f\"tuple index {idx} out of range 0..{n}\")\n        return p[idx]\n\n    def __getitem__(self, s: Slice):\n        p = self._ptr\n        n = self._len\n\n        if s.start is None and s.stop is None and s.step is None:\n            return self\n        if s.step is None:\n            start, stop, step, length = s.adjust_indices(n)\n            return DynamicTuple(p + start, length)\n        else:\n            start, stop, step, length = s.adjust_indices(n)\n            q = Ptr[T](length)\n            n = 0\n            for i in range(start, stop, step):\n                q[n] = self[i]\n                n += 1\n            return DynamicTuple(q, length)\n\n    def __repr__(self):\n        return f\"({', '.join(a.__repr__() for a in self)})\"\n"
  },
  {
    "path": "stdlib/internal/types/complex.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n@tuple(python=False)\nclass complex64:\n    real: float32\n    imag: float32\n\n@tuple\nclass complex:\n    real: float\n    imag: float\n\n    def __new__() -> complex:\n        return complex(0.0, 0.0)\n\n    @overload\n    def __new__(what):\n        # do not overload! (needed to avoid pyobj conversion)\n        if isinstance(what, str) or isinstance(what, Optional[str]):\n            return complex._from_str(what)\n        else:\n            return what.__complex__()\n\n    @overload\n    def __new__(real, imag) -> complex:\n        return superf(float(real), float(imag))\n\n    def __complex__(self) -> complex:\n        return self\n\n    def __bool__(self) -> bool:\n        return self.real != 0.0 or self.imag != 0.0\n\n    def __pos__(self) -> complex:\n        return self\n\n    def __neg__(self) -> complex:\n        return complex(-self.real, -self.imag)\n\n    def __abs__(self) -> float:\n        @pure\n        @C\n        def hypot(a: float, b: float) -> float:\n            pass\n\n        return hypot(self.real, self.imag)\n\n    def __copy__(self) -> complex:\n        return self\n\n    def __hash__(self) -> int:\n        return self.real.__hash__() + self.imag.__hash__() * 1000003\n\n    def __add__(self, other: float) -> complex:\n        return self + complex(other)\n\n    def __sub__(self, other: float) -> complex:\n        return self - complex(other)\n\n    def __mul__(self, other: float) -> complex:\n        return self * complex(other)\n\n    def __truediv__(self, other: float) -> complex:\n        return self / complex(other)\n\n    def __eq__(self, other: float) -> bool:\n        return self == complex(other)\n\n    def __ne__(self, other: float) -> bool:\n        return self != complex(other)\n\n    def __pow__(self, other: float) -> complex:\n        return self ** complex(other)\n\n    def __add__(self, other: int) -> complex:\n        return self + complex(other)\n\n    def __sub__(self, other: int) -> complex:\n        return self - complex(other)\n\n    def __mul__(self, other: int) -> complex:\n        return self * complex(other)\n\n    def __truediv__(self, other: int) -> complex:\n        return self / complex(other)\n\n    def __eq__(self, other: int) -> bool:\n        return self == complex(other)\n\n    def __ne__(self, other: int) -> bool:\n        return self != complex(other)\n\n    def __radd__(self, other: float) -> complex:\n        return complex(other) + self\n\n    def __rsub__(self, other: float) -> complex:\n        return complex(other) - self\n\n    def __rmul__(self, other: float) -> complex:\n        return complex(other) * self\n\n    def __rtruediv__(self, other: float) -> complex:\n        return complex(other) / self\n\n    def __rpow__(self, other: float) -> complex:\n        return complex(other) ** self\n\n    def __radd__(self, other: int) -> complex:\n        return complex(other) + self\n\n    def __rsub__(self, other: int) -> complex:\n        return complex(other) - self\n\n    def __rmul__(self, other: int) -> complex:\n        return complex(other) * self\n\n    def __rtruediv__(self, other: int) -> complex:\n        return complex(other) / self\n\n    def __rpow__(self, other: int) -> complex:\n        return complex(other) ** self\n\n    def __add__(self, other: complex) -> complex:\n        return complex(self.real + other.real, self.imag + other.imag)\n\n    def __sub__(self, other: complex) -> complex:\n        return complex(self.real - other.real, self.imag - other.imag)\n\n    def __mul__(self, other: complex) -> complex:\n        a = (self.real * other.real) - (self.imag * other.imag)\n        b = (self.real * other.imag) + (self.imag * other.real)\n        return complex(a, b)\n\n    def __truediv__(self, other: complex) -> complex:\n        a = self\n        b = other\n        abs_breal = (-b.real) if b.real < 0 else b.real\n        abs_bimag = (-b.imag) if b.imag < 0 else b.imag\n\n        if abs_breal >= abs_bimag:\n            # divide tops and bottom by b.real\n            if abs_breal == 0.0:\n                # errno = EDOM\n                return complex(0.0, 0.0)\n            else:\n                ratio = b.imag / b.real\n                denom = b.real + b.imag * ratio\n                return complex(\n                    (a.real + a.imag * ratio) / denom, (a.imag - a.real * ratio) / denom\n                )\n        elif abs_bimag >= abs_breal:\n            # divide tops and bottom by b.imag\n            ratio = b.real / b.imag\n            denom = b.real * ratio + b.imag\n            # assert b.imag != 0.0\n            return complex(\n                (a.real * ratio + a.imag) / denom, (a.imag * ratio - a.real) / denom\n            )\n        else:\n            nan = 0.0 / 0.0\n            return complex(nan, nan)\n\n    def __eq__(self, other: complex) -> bool:\n        return self.real == other.real and self.imag == other.imag\n\n    def __ne__(self, other: complex) -> bool:\n        return not (self == other)\n\n    def __pow__(self, other: int) -> complex:\n        def powu(x: complex, n: int) -> complex:\n            mask = 1\n            r = complex(1.0, 0.0)\n            p = x\n            while mask > 0 and n >= mask:\n                if n & mask:\n                    r = r * p\n                mask <<= 1\n                p = p * p\n            return r\n\n        if other > 0:\n            return powu(self, other)\n        else:\n            return complex(1.0, 0.0) / powu(self, -other)\n\n    def __pow__(self, other: complex) -> complex:\n        @pure\n        @C\n        def hypot(a: float, b: float) -> float:\n            pass\n\n        @pure\n        @C\n        def atan2(a: float, b: float) -> float:\n            pass\n\n        @pure\n        @llvm\n        def exp(x: float) -> float:\n            declare double @llvm.exp.f64(double)\n            %y = call double @llvm.exp.f64(double %x)\n            ret double %y\n\n        @pure\n        @llvm\n        def pow(x: float, y: float) -> float:\n            declare double @llvm.pow.f64(double, double)\n            %z = call double @llvm.pow.f64(double %x, double %y)\n            ret double %z\n\n        @pure\n        @llvm\n        def log(x: float) -> float:\n            declare double @llvm.log.f64(double)\n            %y = call double @llvm.log.f64(double %x)\n            ret double %y\n\n        @pure\n        @llvm\n        def sin(x: float) -> float:\n            declare double @llvm.sin.f64(double)\n            %y = call double @llvm.sin.f64(double %x)\n            ret double %y\n\n        @pure\n        @llvm\n        def cos(x: float) -> float:\n            declare double @llvm.cos.f64(double)\n            %y = call double @llvm.cos.f64(double %x)\n            ret double %y\n\n        @pure\n        @llvm\n        def floor(x: float) -> float:\n            declare double @llvm.floor.f64(double)\n            %y = call double @llvm.floor.f64(double %x)\n            ret double %y\n\n        @pure\n        @llvm\n        def fabs(x: float) -> float:\n            declare double @llvm.fabs.f64(double)\n            %y = call double @llvm.fabs.f64(double %x)\n            ret double %y\n\n        if other.imag == 0.0 and other.real == floor(other.real) and fabs(other.real) <= 100.0:\n            return self ** int(other.real)\n        elif other.real == 0.0 and other.imag == 0.0:\n            return complex(1.0, 0.0)\n        elif self.real == 0.0 and self.imag == 0.0:\n            # if other.imag != 0. or other.real < 0.: errno = EDOM\n            return complex(0.0, 0.0)\n        else:\n            vabs = hypot(self.real, self.imag)\n            len = pow(vabs, other.real)\n            at = atan2(self.imag, self.real)\n            phase = at * other.real\n            if other.imag != 0.0:\n                len /= exp(at * other.imag)\n                phase += other.imag * log(vabs)\n            return complex(len * cos(phase), len * sin(phase))\n\n    def __repr__(self) -> str:\n        @pure\n        @llvm\n        def copysign(x: float, y: float) -> float:\n            declare double @llvm.copysign.f64(double, double)\n            %z = call double @llvm.copysign.f64(double %x, double %y)\n            ret double %z\n\n        @pure\n        @llvm\n        def fabs(x: float) -> float:\n            declare double @llvm.fabs.f64(double)\n            %y = call double @llvm.fabs.f64(double %x)\n            ret double %y\n\n        if self.real == 0.0 and copysign(1.0, self.real) == 1.0:\n            return f\"{self.imag}j\"\n        else:\n            sign = \"+\"\n            if self.imag < 0.0 or (\n                self.imag == 0.0 and copysign(1.0, self.imag) == -1.0\n            ):\n                sign = \"-\"\n            return f\"({self.real}{sign}{fabs(self.imag)}j)\"\n\n    def conjugate(self) -> complex:\n        return complex(self.real, -self.imag)\n\n    # helpers\n    def _phase(self) -> float:\n        @pure\n        @C\n        def atan2(a: float, b: float) -> float:\n            pass\n\n        return atan2(self.imag, self.real)\n\n    def _polar(self) -> Tuple[float, float]:\n        return (self.__abs__(), self._phase())\n\n    @pure\n    @llvm\n    def _exp(x: float) -> float:\n        declare double @llvm.exp.f64(double)\n        %y = call double @llvm.exp.f64(double %x)\n        ret double %y\n\n    @pure\n    @llvm\n    def _sqrt(x: float) -> float:\n        declare double @llvm.sqrt.f64(double)\n        %y = call double @llvm.sqrt.f64(double %x)\n        ret double %y\n\n    @pure\n    @llvm\n    def _cos(x: float) -> float:\n        declare double @llvm.cos.f64(double)\n        %y = call double @llvm.cos.f64(double %x)\n        ret double %y\n\n    @pure\n    @llvm\n    def _sin(x: float) -> float:\n        declare double @llvm.sin.f64(double)\n        %y = call double @llvm.sin.f64(double %x)\n        ret double %y\n\n    @pure\n    @llvm\n    def _log(x: float) -> float:\n        declare double @llvm.log.f64(double)\n        %y = call double @llvm.log.f64(double %x)\n        ret double %y\n\n@extend\nclass int:\n    def __suffix_j__(x: int) -> complex:\n        return complex(0, x)\n\n@extend\nclass float:\n    def __complex__(self) -> complex:\n        return complex(self, 0.0)\n\n    def __suffix_j__(x: float) -> complex:\n        return complex(0, x)\n\nf32 = float32\n\n@extend\nclass complex64:\n    def __new__() -> complex64:\n        return complex64(f32(0.0), f32(0.0))\n\n    def __new__(other):\n        if isinstance(other, str):\n            return complex64._from_str(other)\n        else:\n            return complex64(other.__complex__())\n\n    def __new__(real: f32):\n        return complex64(real, f32(0.0))\n\n    def __new__(other: complex) -> complex64:\n        return complex64(f32(other.real), f32(other.imag))\n\n    def __new__(real, imag) -> complex64:\n        return superf(f32(float(real)), f32(float(imag)))\n\n    def __complex__(self) -> complex:\n        return complex(float(self.real), float(self.imag))\n\n    def __bool__(self) -> bool:\n        return self.real != f32(0.0) or self.imag != f32(0.0)\n\n    def __pos__(self) -> complex64:\n        return self\n\n    def __neg__(self) -> complex64:\n        return complex64(-self.real, -self.imag)\n\n    def __abs__(self) -> f32:\n        @pure\n        @C\n        def hypotf(a: f32, b: f32) -> f32:\n            pass\n\n        return hypotf(self.real, self.imag)\n\n    def __copy__(self) -> complex64:\n        return self\n\n    def __hash__(self) -> int:\n        return self.real.__hash__() + self.imag.__hash__() * 1000003\n\n    def __add__(self, other: complex) -> complex:\n        return complex(self) + other\n\n    def __sub__(self, other: complex) -> complex:\n        return complex(self) - other\n\n    def __mul__(self, other: complex) -> complex:\n        return complex(self) * other\n\n    def __truediv__(self, other: complex) -> complex:\n        return complex(self) / other\n\n    def __pow__(self, other: complex) -> complex:\n        return complex(self) ** other\n\n    def __eq__(self, other: complex) -> bool:\n        return complex(self) == other\n\n    def __ne__(self, other: complex) -> bool:\n        return complex(self) != other\n\n    def __radd__(self, other: complex) -> complex:\n        return other + complex(self)\n\n    def __rsub__(self, other: complex) -> complex:\n        return other - complex(self)\n\n    def __rmul__(self, other: complex) -> complex:\n        return other * complex(self)\n\n    def __rtruediv__(self, other: complex) -> complex:\n        return other / complex(self)\n\n    def __rpow__(self, other: complex) -> complex:\n        return other ** complex(self)\n\n    def __add__(self, other: float32) -> complex64:\n        return self + complex64(other)\n\n    def __sub__(self, other: float32) -> complex64:\n        return self - complex64(other)\n\n    def __mul__(self, other: float32) -> complex64:\n        return self * complex64(other)\n\n    def __truediv__(self, other: float32) -> complex64:\n        return self / complex64(other)\n\n    def __pow__(self, other: float32) -> complex64:\n        return self ** complex64(other)\n\n    def __eq__(self, other: float32) -> bool:\n        return self == complex64(other)\n\n    def __ne__(self, other: float32) -> bool:\n        return self != complex64(other)\n\n    def __radd__(self, other: float32) -> complex64:\n        return complex64(other) + self\n\n    def __rsub__(self, other: float32) -> complex64:\n        return complex64(other) - self\n\n    def __rmul__(self, other: float32) -> complex64:\n        return complex64(other) * self\n\n    def __rtruediv__(self, other: float32) -> complex64:\n        return complex64(other) / self\n\n    def __rpow__(self, other: float32) -> complex64:\n        return complex64(other) ** self\n\n    def __add__(self, other: float) -> complex:\n        return complex(self) + other\n\n    def __sub__(self, other: float) -> complex:\n        return complex(self) - other\n\n    def __mul__(self, other: float) -> complex:\n        return complex(self) * other\n\n    def __truediv__(self, other: float) -> complex:\n        return complex(self) / other\n\n    def __pow__(self, other: float) -> complex:\n        return complex(self) ** other\n\n    def __eq__(self, other: float) -> bool:\n        return complex(self) == other\n\n    def __ne__(self, other: float) -> bool:\n        return complex(self) != other\n\n    def __radd__(self, other: float) -> complex:\n        return other + complex(self)\n\n    def __rsub__(self, other: float) -> complex:\n        return other - complex(self)\n\n    def __rmul__(self, other: float) -> complex:\n        return other * complex(self)\n\n    def __rtruediv__(self, other: float) -> complex:\n        return other / complex(self)\n\n    def __rpow__(self, other: float) -> complex:\n        return other ** complex(self)\n\n    def __add__(self, other: int) -> complex:\n        return complex(self) + other\n\n    def __sub__(self, other: int) -> complex:\n        return complex(self) - other\n\n    def __mul__(self, other: int) -> complex:\n        return complex(self) * other\n\n    def __truediv__(self, other: int) -> complex:\n        return complex(self) / other\n\n    # def __pow__(self, other: int) -> complex:\n    #     return complex(self) ** other\n\n    def __eq__(self, other: int) -> bool:\n        return complex(self) == other\n\n    def __ne__(self, other: int) -> bool:\n        return complex(self) != other\n\n    def __radd__(self, other: int) -> complex:\n        return other + complex(self)\n\n    def __rsub__(self, other: int) -> complex:\n        return other - complex(self)\n\n    def __rmul__(self, other: int) -> complex:\n        return other * complex(self)\n\n    def __rtruediv__(self, other: int) -> complex:\n        return other / complex(self)\n\n    def __rpow__(self, other: int) -> complex:\n        return other ** complex(self)\n\n    def __add__(self, other: complex64) -> complex64:\n        return complex64(self.real + other.real, self.imag + other.imag)\n\n    def __sub__(self, other: complex64) -> complex64:\n        return complex64(self.real - other.real, self.imag - other.imag)\n\n    def __mul__(self, other: complex64) -> complex64:\n        a = (self.real * other.real) - (self.imag * other.imag)\n        b = (self.real * other.imag) + (self.imag * other.real)\n        return complex64(a, b)\n\n    def __truediv__(self, other: complex64) -> complex64:\n        a = self\n        b = other\n        abs_breal = (-b.real) if b.real < f32(0) else b.real\n        abs_bimag = (-b.imag) if b.imag < f32(0) else b.imag\n\n        if abs_breal >= abs_bimag:\n            # divide tops and bottom by b.real\n            if abs_breal == f32(0.0):\n                # errno = EDOM\n                return complex64(0.0, 0.0)\n            else:\n                ratio = b.imag / b.real\n                denom = b.real + b.imag * ratio\n                return complex64(\n                    (a.real + a.imag * ratio) / denom, (a.imag - a.real * ratio) / denom\n                )\n        elif abs_bimag >= abs_breal:\n            # divide tops and bottom by b.imag\n            ratio = b.real / b.imag\n            denom = b.real * ratio + b.imag\n            # assert b.imag != 0.0\n            return complex64(\n                (a.real * ratio + a.imag) / denom, (a.imag * ratio - a.real) / denom\n            )\n        else:\n            nan = 0.0 / 0.0\n            return complex64(nan, nan)\n\n    def __eq__(self, other: complex64) -> bool:\n        return self.real == other.real and self.imag == other.imag\n\n    def __ne__(self, other: complex64) -> bool:\n        return not (self == other)\n\n    def __pow__(self, other: int) -> complex64:\n        def powu(x: complex64, n: int) -> complex64:\n            mask = 1\n            r = complex64(1.0, 0.0)\n            p = x\n            while mask > 0 and n >= mask:\n                if n & mask:\n                    r = r * p\n                mask <<= 1\n                p = p * p\n            return r\n\n        if other > 0:\n            return powu(self, other)\n        else:\n            return complex64(1.0, 0.0) / powu(self, -other)\n\n    def __pow__(self, other: complex64) -> complex64:\n        @pure\n        @C\n        def hypotf(a: f32, b: f32) -> f32:\n            pass\n\n        @pure\n        @C\n        def atan2f(a: f32, b: f32) -> f32:\n            pass\n\n        @pure\n        @llvm\n        def exp(x: f32) -> f32:\n            declare float @llvm.exp.f32(float)\n            %y = call float @llvm.exp.f32(float %x)\n            ret float %y\n\n        @pure\n        @llvm\n        def pow(x: f32, y: f32) -> f32:\n            declare float @llvm.pow.f32(float, float)\n            %z = call float @llvm.pow.f32(float %x, float %y)\n            ret float %z\n\n        @pure\n        @llvm\n        def log(x: f32) -> f32:\n            declare float @llvm.log.f32(float)\n            %y = call float @llvm.log.f32(float %x)\n            ret float %y\n\n        @pure\n        @llvm\n        def sin(x: f32) -> f32:\n            declare float @llvm.sin.f32(float)\n            %y = call float @llvm.sin.f32(float %x)\n            ret float %y\n\n        @pure\n        @llvm\n        def cos(x: f32) -> f32:\n            declare float @llvm.cos.f32(float)\n            %y = call float @llvm.cos.f32(float %x)\n            ret float %y\n\n        @pure\n        @llvm\n        def floor(x: f32) -> f32:\n            declare float @llvm.floor.f32(float)\n            %y = call float @llvm.floor.f32(float %x)\n            ret float %y\n\n        @pure\n        @llvm\n        def fabs(x: f32) -> f32:\n            declare float @llvm.fabs.f64(float)\n            %y = call float @llvm.fabs.f64(float %x)\n            ret float %y\n\n        if other.imag == f32(0.0) and other.real == floor(other.real) and fabs(other.real) <= f32(100.0):\n            return self ** int(other.real)\n        elif other.real == f32(0.0) and other.imag == f32(0.0):\n            return complex64(1.0, 0.0)\n        elif self.real == f32(0.0) and self.imag == f32(0.0):\n            # if other.imag != 0. or other.real < 0.: errno = EDOM\n            return complex64(0.0, 0.0)\n        else:\n            vabs = hypotf(self.real, self.imag)\n            len = pow(vabs, other.real)\n            at = atan2f(self.imag, self.real)\n            phase = at * other.real\n            if other.imag != f32(0.0):\n                len /= exp(at * other.imag)\n                phase += other.imag * log(vabs)\n            return complex64(len * cos(phase), len * sin(phase))\n\n    def _str(self, include_type: bool) -> str:\n        @pure\n        @llvm\n        def copysign(x: f32, y: f32) -> f32:\n            declare float @llvm.copysign.f32(float, float)\n            %z = call float @llvm.copysign.f32(float %x, float %y)\n            ret float %z\n\n        @pure\n        @llvm\n        def fabs(x: f32) -> f32:\n            declare float @llvm.fabs.f32(float)\n            %y = call float @llvm.fabs.f32(float %x)\n            ret float %y\n\n        if self.real == f32(0.0) and copysign(f32(1.0), self.real) == f32(1.0):\n            if include_type:\n                return f\"complex64({self.imag}j)\"\n            else:\n                return f\"{self.imag}j\"\n        else:\n            sign = \"+\"\n            if self.imag < f32(0.0) or (\n                self.imag == f32(0.0) and copysign(f32(1.0), self.imag) == f32(-1.0)\n            ):\n                sign = \"-\"\n\n            if include_type:\n                return f\"complex64({self.real}{sign}{fabs(self.imag)}j)\"\n            else:\n                return f\"({self.real}{sign}{fabs(self.imag)}j)\"\n\n    def __repr__(self) -> str:\n        return self._str(include_type=True)\n\n    def __str__(self) -> str:\n        return self._str(include_type=False)\n\n    def conjugate(self) -> complex64:\n        return complex64(self.real, -self.imag)\n\n    # helpers\n    def _phase(self) -> f32:\n        @pure\n        @C\n        def atan2f(a: f32, b: f32) -> f32:\n            pass\n\n        return atan2f(self.imag, self.real)\n\n    def _polar(self) -> Tuple[f32, f32]:\n        return (self.__abs__(), self._phase())\n\n    @pure\n    @llvm\n    def _exp(x: f32) -> f32:\n        declare float @llvm.exp.f32(float)\n        %y = call float @llvm.exp.f32(float %x)\n        ret float %y\n\n    @pure\n    @llvm\n    def _sqrt(x: f32) -> f32:\n        declare float @llvm.sqrt.f32(float)\n        %y = call float @llvm.sqrt.f32(float %x)\n        ret float %y\n\n    @pure\n    @llvm\n    def _cos(x: f32) -> f32:\n        declare float @llvm.cos.f32(float)\n        %y = call float @llvm.cos.f32(float %x)\n        ret float %y\n\n    @pure\n    @llvm\n    def _sin(x: f32) -> f32:\n        declare float @llvm.sin.f32(float)\n        %y = call float @llvm.sin.f32(float %x)\n        ret float %y\n\n    @pure\n    @llvm\n    def _log(x: f32) -> f32:\n        declare float @llvm.log.f32(float)\n        %y = call float @llvm.log.f32(float %x)\n        ret float %y\n\n@extend\nclass int:\n    def __complex__(self) -> complex:\n        return complex(float(self), 0.0)\n"
  },
  {
    "path": "stdlib/internal/types/ellipsis.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n@extend\nclass ellipsis:\n    def __repr__(self):\n        return 'Ellipsis'\n    def __eq__(self, other: ellipsis):\n        return True\n    def __ne__(self, other: ellipsis):\n        return False\n    def __hash__(self):\n        return 269626442  # same as CPython\n"
  },
  {
    "path": "stdlib/internal/types/error.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n# Warning(!): This type must be consistent with the exception\n# header type defined in runtime/exc.cpp.\nclass BaseException:\n    _pytype: ClassVar[cobj] = cobj()\n    message: str\n    func: str\n    file: str\n    line: int\n    col: int\n    python_type: cobj\n    cause: Optional[BaseException]\n\n    def __init__(self, message: str = \"\"):\n        self.message = message\n        self.func = \"\"\n        self.file = \"\"\n        self.line = 0\n        self.col = 0\n        self.python_type = BaseException._pytype\n        self.cause = Optional._ref_new(T=BaseException)\n\n    def __str__(self):\n        return self.message\n\n    def __repr__(self):\n        return f'{self.typename}({self.message.__repr__()})'\n\n    @property\n    def typename(self):\n        p = Ptr[Tuple[cobj, TypeInfo]](self.__raw__())\n        return p[0][1].nice_name\n\n    @property\n    def __cause__(self):\n        return self.cause\n\n    @__hidden__\n    def _set_header(e, func, file, line, col, cause):\n        if not isinstance(e, BaseException):\n            compile_error(\"exceptions must derive from BaseException\")\n\n        e.func = func\n        e.file = file\n        e.line = line\n        e.col = col\n        if cause is not None:\n            e.cause = cause\n        return e\n\n\nclass Exception(BaseException):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        if hasattr(self.__class__, \"_pytype\"):\n            self.python_type = self.__class__._pytype\n\nclass NameError(Exception):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass OSError(Exception):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass IOError(Exception):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass ValueError(Exception):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass LookupError(Exception):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass IndexError(LookupError):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass KeyError(LookupError):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass CError(Exception):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass TypeError(Exception):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass ArithmeticError(Exception):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass ZeroDivisionError(ArithmeticError):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass OverflowError(ArithmeticError):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass AttributeError(Exception):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass RuntimeError(Exception):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass NotImplementedError(RuntimeError):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass StopIteration(Exception):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass AssertionError(Exception):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass EOFError(Exception):\n    _pytype: ClassVar[cobj] = cobj()\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n        self.python_type = self.__class__._pytype\n\nclass SystemExit(BaseException):\n    _pytype: ClassVar[cobj] = cobj()\n    _status: int\n\n    def __init__(self, message: str = \"\", status: int = 0):\n        super().__init__(message)\n        self._status = status\n        self.python_type = self.__class__._pytype\n\n    def __init__(self, status: int):\n        self.__init__(\"\", status)\n\n    @property\n    def status(self):\n        return self._status\n\nclass StaticCompileError(Exception):\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n"
  },
  {
    "path": "stdlib/internal/types/float.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom internal.attributes import commutative\nfrom internal.gc import alloc_atomic, free\n\ndef _float_int_pow(a: F, b: int, F: type) -> F:\n    abs_exp = b.__abs__()\n    result = F(1)\n    factor = a\n\n    while abs_exp:\n        if abs_exp & 1:\n            result *= factor\n        factor *= factor\n        abs_exp >>= 1\n\n    if b < 0:\n        result = F(1) / result\n\n    return result\n\n@extend\nclass float:\n    def __new__() -> float:\n        return 0.0\n\n    @overload\n    def __new__(what) -> float:\n        # do not overload! (needed to avoid pyobj conversion)\n        if isinstance(what, str) or isinstance(what, Optional[str]):\n            return float._from_str(what)\n        else:\n            return what.__float__()\n\n    def __repr__(self) -> str:\n        return self.__format__(\"\")\n\n    def __copy__(self) -> float:\n        return self\n\n    def __deepcopy__(self) -> float:\n        return self\n\n    @pure\n    @llvm\n    def __int__(self) -> int:\n        %0 = fptosi double %self to i64\n        ret i64 %0\n\n    def __float__(self) -> float:\n        return self\n\n    @pure\n    @llvm\n    def __bool__(self) -> bool:\n        %0 = fcmp une double %self, 0.000000e+00\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    def __pos__(self) -> float:\n        return self\n\n    @pure\n    @llvm\n    def __neg__(self) -> float:\n        %0 = fneg double %self\n        ret double %0\n\n    @pure\n    @commutative\n    @llvm\n    def __add__(a: float, b: float) -> float:\n        %tmp = fadd double %a, %b\n        ret double %tmp\n\n    @pure\n    @llvm\n    def __sub__(a: float, b: float) -> float:\n        %tmp = fsub double %a, %b\n        ret double %tmp\n\n    @pure\n    @commutative\n    @llvm\n    def __mul__(a: float, b: float) -> float:\n        %tmp = fmul double %a, %b\n        ret double %tmp\n\n    def __floordiv__(self, other: float) -> float:\n        return self.__truediv__(other).__floor__()\n\n    @pure\n    @llvm\n    def __truediv__(a: float, b: float) -> float:\n        %tmp = fdiv double %a, %b\n        ret double %tmp\n\n    @pure\n    @llvm\n    def __mod__(a: float, b: float) -> float:\n        %tmp = frem double %a, %b\n        ret double %tmp\n\n    def __divmod__(self, other: float) -> Tuple[float, float]:\n        mod = self % other\n        div = (self - mod) / other\n        if mod:\n            if (other < 0.0) != (mod < 0.0):\n                mod += other\n                div -= 1.0\n        else:\n            mod = (0.0).copysign(other)\n\n        floordiv = 0.0\n        if div:\n            floordiv = div.__floor__()\n            if div - floordiv > 0.5:\n                floordiv += 1.0\n        else:\n            floordiv = (0.0).copysign(self / other)\n\n        return (floordiv, mod)\n\n    @pure\n    @llvm\n    def __eq__(a: float, b: float) -> bool:\n        %tmp = fcmp oeq double %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __ne__(a: float, b: float) -> bool:\n        %tmp = fcmp une double %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __lt__(a: float, b: float) -> bool:\n        %tmp = fcmp olt double %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __gt__(a: float, b: float) -> bool:\n        %tmp = fcmp ogt double %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __le__(a: float, b: float) -> bool:\n        %tmp = fcmp ole double %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __ge__(a: float, b: float) -> bool:\n        %tmp = fcmp oge double %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def sqrt(a: float) -> float:\n        declare double @llvm.sqrt.f64(double %a)\n        %tmp = call double @llvm.sqrt.f64(double %a)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def sin(a: float) -> float:\n        declare double @llvm.sin.f64(double %a)\n        %tmp = call double @llvm.sin.f64(double %a)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def cos(a: float) -> float:\n        declare double @llvm.cos.f64(double %a)\n        %tmp = call double @llvm.cos.f64(double %a)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def exp(a: float) -> float:\n        declare double @llvm.exp.f64(double %a)\n        %tmp = call double @llvm.exp.f64(double %a)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def exp2(a: float) -> float:\n        declare double @llvm.exp2.f64(double %a)\n        %tmp = call double @llvm.exp2.f64(double %a)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def log(a: float) -> float:\n        declare double @llvm.log.f64(double %a)\n        %tmp = call double @llvm.log.f64(double %a)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def log10(a: float) -> float:\n        declare double @llvm.log10.f64(double %a)\n        %tmp = call double @llvm.log10.f64(double %a)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def log2(a: float) -> float:\n        declare double @llvm.log2.f64(double %a)\n        %tmp = call double @llvm.log2.f64(double %a)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def __abs__(a: float) -> float:\n        declare double @llvm.fabs.f64(double %a)\n        %tmp = call double @llvm.fabs.f64(double %a)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def __floor__(a: float) -> float:\n        declare double @llvm.floor.f64(double %a)\n        %tmp = call double @llvm.floor.f64(double %a)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def __ceil__(a: float) -> float:\n        declare double @llvm.ceil.f64(double %a)\n        %tmp = call double @llvm.ceil.f64(double %a)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def __trunc__(a: float) -> float:\n        declare double @llvm.trunc.f64(double %a)\n        %tmp = call double @llvm.trunc.f64(double %a)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def rint(a: float) -> float:\n        declare double @llvm.rint.f64(double %a)\n        %tmp = call double @llvm.rint.f64(double %a)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def nearbyint(a: float) -> float:\n        declare double @llvm.nearbyint.f64(double %a)\n        %tmp = call double @llvm.nearbyint.f64(double %a)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def __round__(a: float) -> float:\n        declare double @llvm.round.f64(double %a)\n        %tmp = call double @llvm.round.f64(double %a)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def __pow__(a: float, b: float) -> float:\n        declare double @llvm.pow.f64(double %a, double %b)\n        %tmp = call double @llvm.pow.f64(double %a, double %b)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def min(a: float, b: float) -> float:\n        declare double @llvm.minnum.f64(double %a, double %b)\n        %tmp = call double @llvm.minnum.f64(double %a, double %b)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def max(a: float, b: float) -> float:\n        declare double @llvm.maxnum.f64(double %a, double %b)\n        %tmp = call double @llvm.maxnum.f64(double %a, double %b)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def copysign(a: float, b: float) -> float:\n        declare double @llvm.copysign.f64(double %a, double %b)\n        %tmp = call double @llvm.copysign.f64(double %a, double %b)\n        ret double %tmp\n\n    @pure\n    @llvm\n    def fma(a: float, b: float, c: float) -> float:\n        declare double @llvm.fma.f64(double %a, double %b, double %c)\n        %tmp = call double @llvm.fma.f64(double %a, double %b, double %c)\n        ret double %tmp\n\n    @nocapture\n    @llvm\n    def __atomic_xchg__(d: Ptr[float], b: float) -> None:\n        %tmp = atomicrmw xchg ptr %d, double %b seq_cst\n        ret {} {}\n\n    @nocapture\n    @llvm\n    def __atomic_add__(d: Ptr[float], b: float) -> float:\n        0:\n        %1 = load atomic i64, ptr %d monotonic, align 8\n        %2 = bitcast i64 %1 to double\n        %3 = fadd double %2, %b\n        %4 = bitcast double %3 to i64\n        %5 = cmpxchg weak ptr %d, i64 %1, i64 %4 seq_cst monotonic, align 8\n        %6 = extractvalue { i64, i1 } %5, 1\n        br i1 %6, label %15, label %7\n        7:                                                ; preds = %0, %7\n        %8 = phi { i64, i1 } [ %13, %7 ], [ %5, %0 ]\n        %9 = extractvalue { i64, i1 } %8, 0\n        %10 = bitcast i64 %9 to double\n        %11 = fadd double %10, %b\n        %12 = bitcast double %11 to i64\n        %13 = cmpxchg weak ptr %d, i64 %9, i64 %12 seq_cst monotonic, align 8\n        %14 = extractvalue { i64, i1 } %13, 1\n        br i1 %14, label %15, label %7\n        15:                                               ; preds = %7, %0\n        %16 = phi double [ %2, %0 ], [ %10, %7 ]\n        ret double %16\n\n    @nocapture\n    @llvm\n    def __atomic_sub__(d: Ptr[float], b: float) -> float:\n        0:\n        %1 = load atomic i64, ptr %d monotonic, align 8\n        %2 = bitcast i64 %1 to double\n        %3 = fsub double %2, %b\n        %4 = bitcast double %3 to i64\n        %5 = cmpxchg weak ptr %d, i64 %1, i64 %4 seq_cst monotonic, align 8\n        %6 = extractvalue { i64, i1 } %5, 1\n        br i1 %6, label %15, label %7\n        7:                                                ; preds = %0, %7\n        %8 = phi { i64, i1 } [ %13, %7 ], [ %5, %0 ]\n        %9 = extractvalue { i64, i1 } %8, 0\n        %10 = bitcast i64 %9 to double\n        %11 = fsub double %10, %b\n        %12 = bitcast double %11 to i64\n        %13 = cmpxchg weak ptr %d, i64 %9, i64 %12 seq_cst monotonic, align 8\n        %14 = extractvalue { i64, i1 } %13, 1\n        br i1 %14, label %15, label %7\n        15:                                               ; preds = %7, %0\n        %16 = phi double [ %2, %0 ], [ %10, %7 ]\n        ret double %16\n\n    def __hash__(self) -> int:\n        @nocapture\n        @C\n        def frexp(a: float, b: Ptr[Int[32]]) -> float: pass\n\n        HASH_BITS = 61\n        HASH_MODULUS = (1 << HASH_BITS) - 1\n        HASH_INF = 314159\n        HASH_NAN = 0\n        INF = 1.0 / 0.0\n        NAN = 0.0 / 0.0\n        v = self\n\n        if v == INF or v == -INF:\n            return HASH_INF if v > 0 else -HASH_INF\n        if v == NAN:\n            return HASH_NAN\n\n        _e = i32(0)\n        m = frexp(v, __ptr__(_e))\n        e = int(_e)\n\n        sign = 1\n        if m < 0:\n            sign = -1\n            m = -m\n\n        x = 0\n        while m:\n            x = ((x << 28) & HASH_MODULUS) | x >> (HASH_BITS - 28)\n            m *= 268435456.0  # 2**28\n            e -= 28\n            y = int(m)\n            m -= y\n            x += y\n            if x >= HASH_MODULUS:\n                x -= HASH_MODULUS\n\n        e = e % HASH_BITS if e >= 0 else HASH_BITS - 1 - ((-1 - e) % HASH_BITS)\n        x = ((x << e) & HASH_MODULUS) | x >> (HASH_BITS - e)\n\n        x = x * sign\n        if x == -1:\n            x = -2\n        return x\n\n    def __match__(self, obj: float) -> bool:\n        return self == obj\n\n    @property\n    def real(self) -> float:\n        return self\n\n    @property\n    def imag(self) -> float:\n        return 0.0\n\n    @commutative\n    @overload\n    def __add__(self: float, b: int) -> float:\n        return self + float(b)\n\n    @commutative\n    @overload\n    def __add__(self: float, b: bool) -> float:\n        return self + float(b)\n\n    @overload\n    def __sub__(self: float, b: int) -> float:\n        return self - float(b)\n\n    @commutative\n    @overload\n    def __mul__(self: float, b: int) -> float:\n        return self * float(b)\n\n    @overload\n    def __floordiv__(self, b: int) -> float:\n        return self // float(b)\n\n    @overload\n    def __truediv__(self: float, b: int) -> float:\n        return self / float(b)\n\n    @overload\n    def __mod__(self: float, b: int) -> float:\n        return self % float(b)\n\n    @overload\n    def __divmod__(self, b: int):\n        return self.__divmod__(float(b))\n\n    @overload\n    def __eq__(self: float, b: int) -> bool:\n        return self == float(b)\n\n    @overload\n    def __ne__(self: float, b: int) -> bool:\n        return self != float(b)\n\n    @overload\n    def __lt__(self: float, b: int) -> bool:\n        return self < float(b)\n\n    @overload\n    def __gt__(self: float, b: int) -> bool:\n        return self > float(b)\n\n    @overload\n    def __le__(self: float, b: int) -> bool:\n        return self <= float(b)\n\n    @overload\n    def __ge__(self: float, b: int) -> bool:\n        return self >= float(b)\n\n    @overload\n    def __pow__(self: float, b: int) -> float:\n        return _float_int_pow(self, b)\n\n@extend\nclass float32:\n    @pure\n    @llvm\n    def __new__(self: float) -> float32:\n        %0 = fptrunc double %self to float\n        ret float %0\n\n    @overload\n    def __new__(what: float32) -> float32:\n        return what\n\n    @overload\n    def __new__(what: str) -> float32:\n        return float32._from_str(what)\n\n    @overload\n    def __new__() -> float32:\n        return float32.__new__(0.0)\n\n    def __repr__(self) -> str:\n        return self.__float__().__repr__()\n\n    def __format__(self, format_spec: str) -> str:\n        return self.__float__().__format(format_spec)\n\n    def __copy__(self) -> float32:\n        return self\n\n    def __deepcopy__(self) -> float32:\n        return self\n\n    @pure\n    @llvm\n    def __int__(self) -> int:\n        %0 = fptosi float %self to i64\n        ret i64 %0\n\n    @pure\n    @llvm\n    def __float__(self) -> float:\n        %0 = fpext float %self to double\n        ret double %0\n\n    @pure\n    @llvm\n    def __bool__(self) -> bool:\n        %0 = fcmp une float %self, 0.000000e+00\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    def __pos__(self) -> float32:\n        return self\n\n    @pure\n    @llvm\n    def __neg__(self) -> float32:\n        %0 = fneg float %self\n        ret float %0\n\n    @pure\n    @commutative\n    @llvm\n    def __add__(a: float32, b: float32) -> float32:\n        %tmp = fadd float %a, %b\n        ret float %tmp\n\n    @pure\n    @llvm\n    def __sub__(a: float32, b: float32) -> float32:\n        %tmp = fsub float %a, %b\n        ret float %tmp\n\n    @pure\n    @commutative\n    @llvm\n    def __mul__(a: float32, b: float32) -> float32:\n        %tmp = fmul float %a, %b\n        ret float %tmp\n\n    def __floordiv__(self, other: float32) -> float32:\n        return self.__truediv__(other).__floor__()\n\n    @pure\n    @llvm\n    def __truediv__(a: float32, b: float32) -> float32:\n        %tmp = fdiv float %a, %b\n        ret float %tmp\n\n    @pure\n    @llvm\n    def __mod__(a: float32, b: float32) -> float32:\n        %tmp = frem float %a, %b\n        ret float %tmp\n\n    def __divmod__(self, other: float32) -> Tuple[float32, float32]:\n        mod = self % other\n        div = (self - mod) / other\n        if mod:\n            if (other < float32(0.0)) != (mod < float32(0.0)):\n                mod += other\n                div -= float32(1.0)\n        else:\n            mod = float32(0.0).copysign(other)\n\n        floordiv = float32(0.0)\n        if div:\n            floordiv = div.__floor__()\n            if div - floordiv > float32(0.5):\n                floordiv += float32(1.0)\n        else:\n            floordiv = float32(0.0).copysign(self / other)\n\n        return (floordiv, mod)\n\n    @pure\n    @llvm\n    def __eq__(a: float32, b: float32) -> bool:\n        %tmp = fcmp oeq float %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __ne__(a: float32, b: float32) -> bool:\n        %tmp = fcmp une float %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __lt__(a: float32, b: float32) -> bool:\n        %tmp = fcmp olt float %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __gt__(a: float32, b: float32) -> bool:\n        %tmp = fcmp ogt float %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __le__(a: float32, b: float32) -> bool:\n        %tmp = fcmp ole float %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __ge__(a: float32, b: float32) -> bool:\n        %tmp = fcmp oge float %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def sqrt(a: float32) -> float32:\n        declare float @llvm.sqrt.f32(float %a)\n        %tmp = call float @llvm.sqrt.f32(float %a)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def sin(a: float32) -> float32:\n        declare float @llvm.sin.f32(float %a)\n        %tmp = call float @llvm.sin.f32(float %a)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def cos(a: float32) -> float32:\n        declare float @llvm.cos.f32(float %a)\n        %tmp = call float @llvm.cos.f32(float %a)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def exp(a: float32) -> float32:\n        declare float @llvm.exp.f32(float %a)\n        %tmp = call float @llvm.exp.f32(float %a)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def exp2(a: float32) -> float32:\n        declare float @llvm.exp2.f32(float %a)\n        %tmp = call float @llvm.exp2.f32(float %a)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def log(a: float32) -> float32:\n        declare float @llvm.log.f32(float %a)\n        %tmp = call float @llvm.log.f32(float %a)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def log10(a: float32) -> float32:\n        declare float @llvm.log10.f32(float %a)\n        %tmp = call float @llvm.log10.f32(float %a)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def log2(a: float32) -> float32:\n        declare float @llvm.log2.f32(float %a)\n        %tmp = call float @llvm.log2.f32(float %a)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def __abs__(a: float32) -> float32:\n        declare float @llvm.fabs.f32(float %a)\n        %tmp = call float @llvm.fabs.f32(float %a)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def __floor__(a: float32) -> float32:\n        declare float @llvm.floor.f32(float %a)\n        %tmp = call float @llvm.floor.f32(float %a)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def __ceil__(a: float32) -> float32:\n        declare float @llvm.ceil.f32(float %a)\n        %tmp = call float @llvm.ceil.f32(float %a)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def __trunc__(a: float32) -> float32:\n        declare float @llvm.trunc.f32(float %a)\n        %tmp = call float @llvm.trunc.f32(float %a)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def rint(a: float32) -> float32:\n        declare float @llvm.rint.f32(float %a)\n        %tmp = call float @llvm.rint.f32(float %a)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def nearbyint(a: float32) -> float32:\n        declare float @llvm.nearbyint.f32(float %a)\n        %tmp = call float @llvm.nearbyint.f32(float %a)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def __round__(a: float32) -> float32:\n        declare float @llvm.round.f32(float %a)\n        %tmp = call float @llvm.round.f32(float %a)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def __pow__(a: float32, b: float32) -> float32:\n        declare float @llvm.pow.f32(float %a, float %b)\n        %tmp = call float @llvm.pow.f32(float %a, float %b)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def min(a: float32, b: float32) -> float32:\n        declare float @llvm.minnum.f32(float %a, float %b)\n        %tmp = call float @llvm.minnum.f32(float %a, float %b)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def max(a: float32, b: float32) -> float32:\n        declare float @llvm.maxnum.f32(float %a, float %b)\n        %tmp = call float @llvm.maxnum.f32(float %a, float %b)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def copysign(a: float32, b: float32) -> float32:\n        declare float @llvm.copysign.f32(float %a, float %b)\n        %tmp = call float @llvm.copysign.f32(float %a, float %b)\n        ret float %tmp\n\n    @pure\n    @llvm\n    def fma(a: float32, b: float32, c: float32) -> float32:\n        declare float @llvm.fma.f32(float %a, float %b, float %c)\n        %tmp = call float @llvm.fma.f32(float %a, float %b, float %c)\n        ret float %tmp\n\n    @nocapture\n    @llvm\n    def __atomic_xchg__(d: Ptr[float32], b: float32) -> None:\n        %tmp = atomicrmw xchg ptr %d, float %b seq_cst\n        ret {} {}\n\n    @nocapture\n    @llvm\n    def __atomic_add__(d: Ptr[float32], b: float32) -> float32:\n        0:\n        %1 = load atomic i32, ptr %d monotonic, align 4\n        %2 = bitcast i32 %1 to float\n        %3 = fadd float %2, %b\n        %4 = bitcast float %3 to i32\n        %5 = cmpxchg weak ptr %d, i32 %1, i32 %4 seq_cst monotonic, align 4\n        %6 = extractvalue { i32, i1 } %5, 1\n        br i1 %6, label %15, label %7\n        7:                                                ; preds = %0, %7\n        %8 = phi { i32, i1 } [ %13, %7 ], [ %5, %0 ]\n        %9 = extractvalue { i32, i1 } %8, 0\n        %10 = bitcast i32 %9 to float\n        %11 = fadd float %10, %b\n        %12 = bitcast float %11 to i32\n        %13 = cmpxchg weak ptr %d, i32 %9, i32 %12 seq_cst monotonic, align 4\n        %14 = extractvalue { i32, i1 } %13, 1\n        br i1 %14, label %15, label %7\n        15:                                               ; preds = %7, %0\n        %16 = phi float [ %2, %0 ], [ %10, %7 ]\n        ret float %16\n\n    @nocapture\n    @llvm\n    def __atomic_sub__(d: Ptr[float32], b: float32) -> float32:\n        0:\n        %1 = load atomic i32, ptr %d monotonic, align 4\n        %2 = bitcast i32 %1 to float\n        %3 = fsub float %2, %b\n        %4 = bitcast float %3 to i32\n        %5 = cmpxchg weak ptr %d, i32 %1, i32 %4 seq_cst monotonic, align 4\n        %6 = extractvalue { i32, i1 } %5, 1\n        br i1 %6, label %15, label %7\n        7:                                                ; preds = %0, %7\n        %8 = phi { i32, i1 } [ %13, %7 ], [ %5, %0 ]\n        %9 = extractvalue { i32, i1 } %8, 0\n        %10 = bitcast i32 %9 to float\n        %11 = fsub float %10, %b\n        %12 = bitcast float %11 to i32\n        %13 = cmpxchg weak ptr %d, i32 %9, i32 %12 seq_cst monotonic, align 4\n        %14 = extractvalue { i32, i1 } %13, 1\n        br i1 %14, label %15, label %7\n        15:                                               ; preds = %7, %0\n        %16 = phi float [ %2, %0 ], [ %10, %7 ]\n        ret float %16\n\n    def __hash__(self) -> int:\n        return self.__float__().__hash__()\n\n    def __match__(self, obj: float32) -> bool:\n        return self == obj\n\n    @commutative\n    @overload\n    def __add__(self: float32, b: int) -> float32:\n        return self + float32(b)\n\n    @overload\n    def __sub__(self: float32, b: int) -> float32:\n        return self - float32(b)\n\n    @commutative\n    @overload\n    def __mul__(self: float32, b: int) -> float32:\n        return self * float32(b)\n\n    @overload\n    def __floordiv__(self, b: int) -> float32:\n        return self // float32(b)\n\n    @overload\n    def __truediv__(self: float32, b: int) -> float32:\n        return self / float32(b)\n\n    @overload\n    def __mod__(self: float32, b: int) -> float32:\n        return self % float32(b)\n\n    @overload\n    def __divmod__(self, b: int):\n        return self.__divmod__(float32(b))\n\n    @overload\n    def __eq__(self: float32, b: int) -> bool:\n        return self == float32(b)\n\n    @overload\n    def __ne__(self: float32, b: int) -> bool:\n        return self != float32(b)\n\n    @overload\n    def __lt__(self: float32, b: int) -> bool:\n        return self < float32(b)\n\n    @overload\n    def __gt__(self: float32, b: int) -> bool:\n        return self > float32(b)\n\n    @overload\n    def __le__(self: float32, b: int) -> bool:\n        return self <= float32(b)\n\n    @overload\n    def __ge__(self: float32, b: int) -> bool:\n        return self >= float32(b)\n\n    @overload\n    def __pow__(self: float32, b: int) -> float32:\n        return _float_int_pow(self, b)\n\n@extend\nclass float16:\n    @pure\n    @llvm\n    def __new__(self: float) -> float16:\n        %0 = fptrunc double %self to half\n        ret half %0\n\n    @overload\n    def __new__(what: float16) -> float16:\n        return what\n\n    @overload\n    def __new__(what: str) -> float16:\n        return float16._from_str(what)\n\n    @overload\n    def __new__() -> float16:\n        return float16.__new__(0.0)\n\n    def __repr__(self) -> str:\n        return self.__float__().__repr__()\n\n    def __format__(self, format_spec: str) -> str:\n        return self.__float__().__format(format_spec)\n\n    def __copy__(self) -> float16:\n        return self\n\n    def __deepcopy__(self) -> float16:\n        return self\n\n    @pure\n    @llvm\n    def __int__(self) -> int:\n        %0 = fptosi half %self to i64\n        ret i64 %0\n\n    @pure\n    @llvm\n    def __float__(self) -> float:\n        %0 = fpext half %self to double\n        ret double %0\n\n    @pure\n    @llvm\n    def __bool__(self) -> bool:\n        %0 = fcmp une half %self, 0.000000e+00\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    def __pos__(self) -> float16:\n        return self\n\n    @pure\n    @llvm\n    def __neg__(self) -> float16:\n        %0 = fneg half %self\n        ret half %0\n\n    @pure\n    @commutative\n    @llvm\n    def __add__(a: float16, b: float16) -> float16:\n        %tmp = fadd half %a, %b\n        ret half %tmp\n\n    @pure\n    @llvm\n    def __sub__(a: float16, b: float16) -> float16:\n        %tmp = fsub half %a, %b\n        ret half %tmp\n\n    @pure\n    @commutative\n    @llvm\n    def __mul__(a: float16, b: float16) -> float16:\n        %tmp = fmul half %a, %b\n        ret half %tmp\n\n    def __floordiv__(self, other: float16) -> float16:\n        return self.__truediv__(other).__floor__()\n\n    @pure\n    @llvm\n    def __truediv__(a: float16, b: float16) -> float16:\n        %tmp = fdiv half %a, %b\n        ret half %tmp\n\n    @pure\n    @llvm\n    def __mod__(a: float16, b: float16) -> float16:\n        %tmp = frem half %a, %b\n        ret half %tmp\n\n    def __divmod__(self, other: float16) -> Tuple[float16, float16]:\n        mod = self % other\n        div = (self - mod) / other\n        if mod:\n            if (other < float16(0.0)) != (mod < float16(0.0)):\n                mod += other\n                div -= float16(1.0)\n        else:\n            mod = float16(0.0).copysign(other)\n\n        floordiv = float16(0.0)\n        if div:\n            floordiv = div.__floor__()\n            if div - floordiv > float16(0.5):\n                floordiv += float16(1.0)\n        else:\n            floordiv = float16(0.0).copysign(self / other)\n\n        return (floordiv, mod)\n\n    @pure\n    @llvm\n    def __eq__(a: float16, b: float16) -> bool:\n        %tmp = fcmp oeq half %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __ne__(a: float16, b: float16) -> bool:\n        %tmp = fcmp une half %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __lt__(a: float16, b: float16) -> bool:\n        %tmp = fcmp olt half %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __gt__(a: float16, b: float16) -> bool:\n        %tmp = fcmp ogt half %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __le__(a: float16, b: float16) -> bool:\n        %tmp = fcmp ole half %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __ge__(a: float16, b: float16) -> bool:\n        %tmp = fcmp oge half %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def sqrt(a: float16) -> float16:\n        declare half @llvm.sqrt.f16(half %a)\n        %tmp = call half @llvm.sqrt.f16(half %a)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def sin(a: float16) -> float16:\n        declare half @llvm.sin.f16(half %a)\n        %tmp = call half @llvm.sin.f16(half %a)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def cos(a: float16) -> float16:\n        declare half @llvm.cos.f16(half %a)\n        %tmp = call half @llvm.cos.f16(half %a)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def exp(a: float16) -> float16:\n        declare half @llvm.exp.f16(half %a)\n        %tmp = call half @llvm.exp.f16(half %a)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def exp2(a: float16) -> float16:\n        declare half @llvm.exp2.f16(half %a)\n        %tmp = call half @llvm.exp2.f16(half %a)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def log(a: float16) -> float16:\n        declare half @llvm.log.f16(half %a)\n        %tmp = call half @llvm.log.f16(half %a)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def log10(a: float16) -> float16:\n        declare half @llvm.log10.f16(half %a)\n        %tmp = call half @llvm.log10.f16(half %a)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def log2(a: float16) -> float16:\n        declare half @llvm.log2.f16(half %a)\n        %tmp = call half @llvm.log2.f16(half %a)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def __abs__(a: float16) -> float16:\n        declare half @llvm.fabs.f16(half %a)\n        %tmp = call half @llvm.fabs.f16(half %a)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def __floor__(a: float16) -> float16:\n        declare half @llvm.floor.f16(half %a)\n        %tmp = call half @llvm.floor.f16(half %a)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def __ceil__(a: float16) -> float16:\n        declare half @llvm.ceil.f16(half %a)\n        %tmp = call half @llvm.ceil.f16(half %a)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def __trunc__(a: float16) -> float16:\n        declare half @llvm.trunc.f16(half %a)\n        %tmp = call half @llvm.trunc.f16(half %a)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def rint(a: float16) -> float16:\n        declare half @llvm.rint.f16(half %a)\n        %tmp = call half @llvm.rint.f16(half %a)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def nearbyint(a: float16) -> float16:\n        declare half @llvm.nearbyint.f16(half %a)\n        %tmp = call half @llvm.nearbyint.f16(half %a)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def __round__(a: float16) -> float16:\n        declare half @llvm.round.f16(half %a)\n        %tmp = call half @llvm.round.f16(half %a)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def __pow__(a: float16, b: float16) -> float16:\n        declare half @llvm.pow.f16(half %a, half %b)\n        %tmp = call half @llvm.pow.f16(half %a, half %b)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def min(a: float16, b: float16) -> float16:\n        declare half @llvm.minnum.f16(half %a, half %b)\n        %tmp = call half @llvm.minnum.f16(half %a, half %b)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def max(a: float16, b: float16) -> float16:\n        declare half @llvm.maxnum.f16(half %a, half %b)\n        %tmp = call half @llvm.maxnum.f16(half %a, half %b)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def copysign(a: float16, b: float16) -> float16:\n        declare half @llvm.copysign.f16(half %a, half %b)\n        %tmp = call half @llvm.copysign.f16(half %a, half %b)\n        ret half %tmp\n\n    @pure\n    @llvm\n    def fma(a: float16, b: float16, c: float16) -> float16:\n        declare half @llvm.fma.f16(half %a, half %b, half %c)\n        %tmp = call half @llvm.fma.f16(half %a, half %b, half %c)\n        ret half %tmp\n\n    def __hash__(self) -> int:\n        return self.__float__().__hash__()\n\n    def __match__(self, obj: float16) -> bool:\n        return self == obj\n\n    @commutative\n    @overload\n    def __add__(self: float16, b: int) -> float16:\n        return self + float16(b)\n\n    @overload\n    def __sub__(self: float16, b: int) -> float16:\n        return self - float16(b)\n\n    @commutative\n    @overload\n    def __mul__(self: float16, b: int) -> float16:\n        return self * float16(b)\n\n    @overload\n    def __floordiv__(self, b: int) -> float16:\n        return self // float16(b)\n\n    @overload\n    def __truediv__(self: float16, b: int) -> float16:\n        return self / float16(b)\n\n    @overload\n    def __mod__(self: float16, b: int) -> float16:\n        return self % float16(b)\n\n    @overload\n    def __divmod__(self, b: int):\n        return self.__divmod__(float16(b))\n\n    @overload\n    def __eq__(self: float16, b: int) -> bool:\n        return self == float16(b)\n\n    @overload\n    def __ne__(self: float16, b: int) -> bool:\n        return self != float16(b)\n\n    @overload\n    def __lt__(self: float16, b: int) -> bool:\n        return self < float16(b)\n\n    @overload\n    def __gt__(self: float16, b: int) -> bool:\n        return self > float16(b)\n\n    @overload\n    def __le__(self: float16, b: int) -> bool:\n        return self <= float16(b)\n\n    @overload\n    def __ge__(self: float16, b: int) -> bool:\n        return self >= float16(b)\n\n    @overload\n    def __pow__(self: float16, b: int) -> float16:\n        return _float_int_pow(self, b)\n\n@extend\nclass bfloat16:\n    @pure\n    @llvm\n    def __new__(self: float) -> bfloat16:\n        %0 = fptrunc double %self to bfloat\n        ret bfloat %0\n\n    @overload\n    def __new__(what: bfloat16) -> bfloat16:\n        return what\n\n    @overload\n    def __new__(what: str) -> bfloat16:\n        return bfloat16._from_str(what)\n\n    @overload\n    def __new__() -> bfloat16:\n        return bfloat16.__new__(0.0)\n\n    def __repr__(self) -> str:\n        return self.__float__().__repr__()\n\n    def __format__(self, format_spec: str) -> str:\n        return self.__float__().__format(format_spec)\n\n    def __copy__(self) -> bfloat16:\n        return self\n\n    def __deepcopy__(self) -> bfloat16:\n        return self\n\n    @pure\n    @llvm\n    def __int__(self) -> int:\n        %0 = fptosi bfloat %self to i64\n        ret i64 %0\n\n    @pure\n    @llvm\n    def __float__(self) -> float:\n        %0 = fpext bfloat %self to double\n        ret double %0\n\n    @pure\n    @llvm\n    def __bool__(self) -> bool:\n        %0 = fcmp une bfloat %self, 0.000000e+00\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    def __pos__(self) -> bfloat16:\n        return self\n\n    @pure\n    @llvm\n    def __neg__(self) -> bfloat16:\n        %0 = fneg bfloat %self\n        ret bfloat %0\n\n    @pure\n    @commutative\n    @llvm\n    def __add__(a: bfloat16, b: bfloat16) -> bfloat16:\n        %tmp = fadd bfloat %a, %b\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def __sub__(a: bfloat16, b: bfloat16) -> bfloat16:\n        %tmp = fsub bfloat %a, %b\n        ret bfloat %tmp\n\n    @pure\n    @commutative\n    @llvm\n    def __mul__(a: bfloat16, b: bfloat16) -> bfloat16:\n        %tmp = fmul bfloat %a, %b\n        ret bfloat %tmp\n\n    def __floordiv__(self, other: bfloat16) -> bfloat16:\n        return self.__truediv__(other).__floor__()\n\n    @pure\n    @llvm\n    def __truediv__(a: bfloat16, b: bfloat16) -> bfloat16:\n        %tmp = fdiv bfloat %a, %b\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def __mod__(a: bfloat16, b: bfloat16) -> bfloat16:\n        %tmp = frem bfloat %a, %b\n        ret bfloat %tmp\n\n    def __divmod__(self, other: bfloat16) -> Tuple[bfloat16, bfloat16]:\n        mod = self % other\n        div = (self - mod) / other\n        if mod:\n            if (other < bfloat16(0.0)) != (mod < bfloat16(0.0)):\n                mod += other\n                div -= bfloat16(1.0)\n        else:\n            mod = bfloat16(0.0).copysign(other)\n\n        floordiv = bfloat16(0.0)\n        if div:\n            floordiv = div.__floor__()\n            if div - floordiv > bfloat16(0.5):\n                floordiv += bfloat16(1.0)\n        else:\n            floordiv = bfloat16(0.0).copysign(self / other)\n\n        return (floordiv, mod)\n\n    @pure\n    @llvm\n    def __eq__(a: bfloat16, b: bfloat16) -> bool:\n        %tmp = fcmp oeq bfloat %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __ne__(a: bfloat16, b: bfloat16) -> bool:\n        %tmp = fcmp une bfloat %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __lt__(a: bfloat16, b: bfloat16) -> bool:\n        %tmp = fcmp olt bfloat %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __gt__(a: bfloat16, b: bfloat16) -> bool:\n        %tmp = fcmp ogt bfloat %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __le__(a: bfloat16, b: bfloat16) -> bool:\n        %tmp = fcmp ole bfloat %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __ge__(a: bfloat16, b: bfloat16) -> bool:\n        %tmp = fcmp oge bfloat %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def sqrt(a: bfloat16) -> bfloat16:\n        declare bfloat @llvm.sqrt.bf16(bfloat %a)\n        %tmp = call bfloat @llvm.sqrt.bf16(bfloat %a)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def sin(a: bfloat16) -> bfloat16:\n        declare bfloat @llvm.sin.bf16(bfloat %a)\n        %tmp = call bfloat @llvm.sin.bf16(bfloat %a)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def cos(a: bfloat16) -> bfloat16:\n        declare bfloat @llvm.cos.bf16(bfloat %a)\n        %tmp = call bfloat @llvm.cos.bf16(bfloat %a)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def exp(a: bfloat16) -> bfloat16:\n        declare bfloat @llvm.exp.bf16(bfloat %a)\n        %tmp = call bfloat @llvm.exp.bf16(bfloat %a)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def exp2(a: bfloat16) -> bfloat16:\n        declare bfloat @llvm.exp2.bf16(bfloat %a)\n        %tmp = call bfloat @llvm.exp2.bf16(bfloat %a)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def log(a: bfloat16) -> bfloat16:\n        declare bfloat @llvm.log.bf16(bfloat %a)\n        %tmp = call bfloat @llvm.log.bf16(bfloat %a)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def log10(a: bfloat16) -> bfloat16:\n        declare bfloat @llvm.log10.bf16(bfloat %a)\n        %tmp = call bfloat @llvm.log10.bf16(bfloat %a)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def log2(a: bfloat16) -> bfloat16:\n        declare bfloat @llvm.log2.bf16(bfloat %a)\n        %tmp = call bfloat @llvm.log2.bf16(bfloat %a)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def __abs__(a: bfloat16) -> bfloat16:\n        declare bfloat @llvm.fabs.bf16(bfloat %a)\n        %tmp = call bfloat @llvm.fabs.bf16(bfloat %a)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def __floor__(a: bfloat16) -> bfloat16:\n        declare bfloat @llvm.floor.bf16(bfloat %a)\n        %tmp = call bfloat @llvm.floor.bf16(bfloat %a)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def __ceil__(a: bfloat16) -> bfloat16:\n        declare bfloat @llvm.ceil.bf16(bfloat %a)\n        %tmp = call bfloat @llvm.ceil.bf16(bfloat %a)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def __trunc__(a: bfloat16) -> bfloat16:\n        declare bfloat @llvm.trunc.bf16(bfloat %a)\n        %tmp = call bfloat @llvm.trunc.bf16(bfloat %a)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def rint(a: bfloat16) -> bfloat16:\n        declare bfloat @llvm.rint.bf16(bfloat %a)\n        %tmp = call bfloat @llvm.rint.bf16(bfloat %a)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def nearbyint(a: bfloat16) -> bfloat16:\n        declare bfloat @llvm.nearbyint.bf16(bfloat %a)\n        %tmp = call bfloat @llvm.nearbyint.bf16(bfloat %a)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def __round__(a: bfloat16) -> bfloat16:\n        declare bfloat @llvm.round.bf16(bfloat %a)\n        %tmp = call bfloat @llvm.round.bf16(bfloat %a)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def __pow__(a: bfloat16, b: bfloat16) -> bfloat16:\n        declare bfloat @llvm.pow.bf16(bfloat %a, bfloat %b)\n        %tmp = call bfloat @llvm.pow.bf16(bfloat %a, bfloat %b)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def min(a: bfloat16, b: bfloat16) -> bfloat16:\n        declare bfloat @llvm.minnum.bf16(bfloat %a, bfloat %b)\n        %tmp = call bfloat @llvm.minnum.bf16(bfloat %a, bfloat %b)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def max(a: bfloat16, b: bfloat16) -> bfloat16:\n        declare bfloat @llvm.maxnum.bf16(bfloat %a, bfloat %b)\n        %tmp = call bfloat @llvm.maxnum.bf16(bfloat %a, bfloat %b)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def copysign(a: bfloat16, b: bfloat16) -> bfloat16:\n        declare bfloat @llvm.copysign.bf16(bfloat %a, bfloat %b)\n        %tmp = call bfloat @llvm.copysign.bf16(bfloat %a, bfloat %b)\n        ret bfloat %tmp\n\n    @pure\n    @llvm\n    def fma(a: bfloat16, b: bfloat16, c: bfloat16) -> bfloat16:\n        declare bfloat @llvm.fma.bf16(bfloat %a, bfloat %b, bfloat %c)\n        %tmp = call bfloat @llvm.fma.bf16(bfloat %a, bfloat %b, bfloat %c)\n        ret bfloat %tmp\n\n    def __hash__(self) -> int:\n        return self.__float__().__hash__()\n\n    def __match__(self, obj: bfloat16) -> bool:\n        return self == obj\n\n    @commutative\n    @overload\n    def __add__(self: bfloat16, b: int) -> bfloat16:\n        return self + bfloat16(b)\n\n    @overload\n    def __sub__(self: bfloat16, b: int) -> bfloat16:\n        return self - bfloat16(b)\n\n    @commutative\n    @overload\n    def __mul__(self: bfloat16, b: int) -> bfloat16:\n        return self * bfloat16(b)\n\n    @overload\n    def __floordiv__(self, b: int) -> bfloat16:\n        return self // bfloat16(b)\n\n    @overload\n    def __truediv__(self: bfloat16, b: int) -> bfloat16:\n        return self / bfloat16(b)\n\n    @overload\n    def __mod__(self: bfloat16, b: int) -> bfloat16:\n        return self % bfloat16(b)\n\n    @overload\n    def __divmod__(self, b: int):\n        return self.__divmod__(bfloat16(b))\n\n    @overload\n    def __eq__(self: bfloat16, b: int) -> bool:\n        return self == bfloat16(b)\n\n    @overload\n    def __ne__(self: bfloat16, b: int) -> bool:\n        return self != bfloat16(b)\n\n    @overload\n    def __lt__(self: bfloat16, b: int) -> bool:\n        return self < bfloat16(b)\n\n    @overload\n    def __gt__(self: bfloat16, b: int) -> bool:\n        return self > bfloat16(b)\n\n    @overload\n    def __le__(self: bfloat16, b: int) -> bool:\n        return self <= bfloat16(b)\n\n    @overload\n    def __ge__(self: bfloat16, b: int) -> bool:\n        return self >= bfloat16(b)\n\n    @overload\n    def __pow__(self: bfloat16, b: int) -> bfloat16:\n        return _float_int_pow(self, b)\n\n@extend\nclass float128:\n    @pure\n    @llvm\n    def __new__(self: float) -> float128:\n        %0 = fpext double %self to fp128\n        ret fp128 %0\n\n    @overload\n    def __new__(what: float128) -> float128:\n        return what\n\n    @overload\n    def __new__() -> float128:\n        return float128.__new__(0.0)\n\n    def __repr__(self) -> str:\n        return self.__float__().__repr__()\n\n    def __format__(self, format_spec: str) -> str:\n        return self.__float__().__format(format_spec)\n\n    def __copy__(self) -> float128:\n        return self\n\n    def __deepcopy__(self) -> float128:\n        return self\n\n    @pure\n    @llvm\n    def __int__(self) -> int:\n        %0 = fptosi fp128 %self to i64\n        ret i64 %0\n\n    @pure\n    @llvm\n    def __float__(self) -> float:\n        %0 = fptrunc fp128 %self to double\n        ret double %0\n\n    @pure\n    @llvm\n    def __bool__(self) -> bool:\n        %0 = fcmp une fp128 %self, 0xL00000000000000000000000000000000\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    def __pos__(self) -> float128:\n        return self\n\n    @pure\n    @llvm\n    def __neg__(self) -> float128:\n        %0 = fneg fp128 %self\n        ret fp128 %0\n\n    @pure\n    @commutative\n    @llvm\n    def __add__(a: float128, b: float128) -> float128:\n        %tmp = fadd fp128 %a, %b\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def __sub__(a: float128, b: float128) -> float128:\n        %tmp = fsub fp128 %a, %b\n        ret fp128 %tmp\n\n    @pure\n    @commutative\n    @llvm\n    def __mul__(a: float128, b: float128) -> float128:\n        %tmp = fmul fp128 %a, %b\n        ret fp128 %tmp\n\n    def __floordiv__(self, other: float128) -> float128:\n        return self.__truediv__(other).__floor__()\n\n    @pure\n    @llvm\n    def __truediv__(a: float128, b: float128) -> float128:\n        %tmp = fdiv fp128 %a, %b\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def __mod__(a: float128, b: float128) -> float128:\n        %tmp = frem fp128 %a, %b\n        ret fp128 %tmp\n\n    def __divmod__(self, other: float128) -> Tuple[float128, float128]:\n        mod = self % other\n        div = (self - mod) / other\n        if mod:\n            if (other < float128(0.0)) != (mod < float128(0)):\n                mod += other\n                div -= float128(1.0)\n        else:\n            mod = float128(0.0).copysign(other)\n\n        floordiv = float128(0.0)\n        if div:\n            floordiv = div.__floor__()\n            if div - floordiv > float128(0.5):\n                floordiv += float128(1.0)\n        else:\n            floordiv = float128(0.0).copysign(self / other)\n\n        return (floordiv, mod)\n\n    @pure\n    @llvm\n    def __eq__(a: float128, b: float128) -> bool:\n        %tmp = fcmp oeq fp128 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __ne__(a: float128, b: float128) -> bool:\n        %tmp = fcmp une fp128 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __lt__(a: float128, b: float128) -> bool:\n        %tmp = fcmp olt fp128 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __gt__(a: float128, b: float128) -> bool:\n        %tmp = fcmp ogt fp128 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __le__(a: float128, b: float128) -> bool:\n        %tmp = fcmp ole fp128 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __ge__(a: float128, b: float128) -> bool:\n        %tmp = fcmp oge fp128 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def sqrt(a: float128) -> float128:\n        declare fp128 @llvm.sqrt.f128(fp128 %a)\n        %tmp = call fp128 @llvm.sqrt.f128(fp128 %a)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def sin(a: float128) -> float128:\n        declare fp128 @llvm.sin.f128(fp128 %a)\n        %tmp = call fp128 @llvm.sin.f128(fp128 %a)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def cos(a: float128) -> float128:\n        declare fp128 @llvm.cos.f128(fp128 %a)\n        %tmp = call fp128 @llvm.cos.f128(fp128 %a)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def exp(a: float128) -> float128:\n        declare fp128 @llvm.exp.f128(fp128 %a)\n        %tmp = call fp128 @llvm.exp.f128(fp128 %a)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def exp2(a: float128) -> float128:\n        declare fp128 @llvm.exp2.f128(fp128 %a)\n        %tmp = call fp128 @llvm.exp2.f128(fp128 %a)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def log(a: float128) -> float128:\n        declare fp128 @llvm.log.f128(fp128 %a)\n        %tmp = call fp128 @llvm.log.f128(fp128 %a)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def log10(a: float128) -> float128:\n        declare fp128 @llvm.log10.f128(fp128 %a)\n        %tmp = call fp128 @llvm.log10.f128(fp128 %a)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def log2(a: float128) -> float128:\n        declare fp128 @llvm.log2.f128(fp128 %a)\n        %tmp = call fp128 @llvm.log2.f128(fp128 %a)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def __abs__(a: float128) -> float128:\n        declare fp128 @llvm.fabs.f128(fp128 %a)\n        %tmp = call fp128 @llvm.fabs.f128(fp128 %a)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def __floor__(a: float128) -> float128:\n        declare fp128 @llvm.floor.f128(fp128 %a)\n        %tmp = call fp128 @llvm.floor.f128(fp128 %a)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def __ceil__(a: float128) -> float128:\n        declare fp128 @llvm.ceil.f128(fp128 %a)\n        %tmp = call fp128 @llvm.ceil.f128(fp128 %a)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def __trunc__(a: float128) -> float128:\n        declare fp128 @llvm.trunc.f128(fp128 %a)\n        %tmp = call fp128 @llvm.trunc.f128(fp128 %a)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def rint(a: float128) -> float128:\n        declare fp128 @llvm.rint.f128(fp128 %a)\n        %tmp = call fp128 @llvm.rint.f128(fp128 %a)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def nearbyint(a: float128) -> float128:\n        declare fp128 @llvm.nearbyint.f128(fp128 %a)\n        %tmp = call fp128 @llvm.nearbyint.f128(fp128 %a)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def __round__(a: float128) -> float128:\n        declare fp128 @llvm.round.f128(fp128 %a)\n        %tmp = call fp128 @llvm.round.f128(fp128 %a)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def __pow__(a: float128, b: float128) -> float128:\n        declare fp128 @llvm.pow.f128(fp128 %a, fp128 %b)\n        %tmp = call fp128 @llvm.pow.f128(fp128 %a, fp128 %b)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def min(a: float128, b: float128) -> float128:\n        declare fp128 @llvm.minnum.f128(fp128 %a, fp128 %b)\n        %tmp = call fp128 @llvm.minnum.f128(fp128 %a, fp128 %b)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def max(a: float128, b: float128) -> float128:\n        declare fp128 @llvm.maxnum.f128(fp128 %a, fp128 %b)\n        %tmp = call fp128 @llvm.maxnum.f128(fp128 %a, fp128 %b)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def copysign(a: float128, b: float128) -> float128:\n        declare fp128 @llvm.copysign.f128(fp128 %a, fp128 %b)\n        %tmp = call fp128 @llvm.copysign.f128(fp128 %a, fp128 %b)\n        ret fp128 %tmp\n\n    @pure\n    @llvm\n    def fma(a: float128, b: float128, c: float128) -> float128:\n        declare fp128 @llvm.fma.f128(fp128 %a, fp128 %b, fp128 %c)\n        %tmp = call fp128 @llvm.fma.f128(fp128 %a, fp128 %b, fp128 %c)\n        ret fp128 %tmp\n\n    def __hash__(self) -> int:\n        return self.__float__().__hash__()\n\n    def __match__(self, obj: float128) -> bool:\n        return self == obj\n\n    @commutative\n    @overload\n    def __add__(self: float128, b: int) -> float128:\n        return self + float128(b)\n\n    @overload\n    def __sub__(self: float128, b: int) -> float128:\n        return self - float128(b)\n\n    @commutative\n    @overload\n    def __mul__(self: float128, b: int) -> float128:\n        return self * float128(b)\n\n    @overload\n    def __floordiv__(self, b: int) -> float128:\n        return self // float128(b)\n\n    @overload\n    def __truediv__(self: float128, b: int) -> float128:\n        return self / float128(b)\n\n    @overload\n    def __mod__(self: float128, b: int) -> float128:\n        return self % float128(b)\n\n    @overload\n    def __divmod__(self, b: int):\n        return self.__divmod__(float128(b))\n\n    @overload\n    def __eq__(self: float128, b: int) -> bool:\n        return self == float128(b)\n\n    @overload\n    def __ne__(self: float128, b: int) -> bool:\n        return self != float128(b)\n\n    @overload\n    def __lt__(self: float128, b: int) -> bool:\n        return self < float128(b)\n\n    @overload\n    def __gt__(self: float128, b: int) -> bool:\n        return self > float128(b)\n\n    @overload\n    def __le__(self: float128, b: int) -> bool:\n        return self <= float128(b)\n\n    @overload\n    def __ge__(self: float128, b: int) -> bool:\n        return self >= float128(b)\n\n    @overload\n    def __pow__(self: float128, b: int) -> float128:\n        return _float_int_pow(self, b)\n\n@extend\nclass float:\n    def __suffix_f32__(double) -> float32:\n        return float32.__new__(double)\n\n    def __suffix_f16__(double) -> float16:\n        return float16.__new__(double)\n\n    def __suffix_bf16__(double) -> bfloat16:\n        return bfloat16.__new__(double)\n\n    def __suffix_f128__(double) -> float128:\n        return float128.__new__(double)\n\n@extend\nclass int:\n    @commutative\n    @overload\n    def __add__(self, b: float32) -> float32:\n        return float32(self) + b\n\n    @overload\n    def __sub__(self, b: float32) -> float32:\n        return float32(self) - b\n\n    @commutative\n    @overload\n    def __mul__(self, b: float32) -> float32:\n        return float32(self) * b\n\n    @overload\n    def __floordiv__(self, b: float32) -> float32:\n        return float32(self) // b\n\n    @overload\n    def __truediv__(self, b: float32) -> float32:\n        return float32(self) / b\n\n    @overload\n    def __mod__(self, b: float32) -> float32:\n        return float32(self) % b\n\n    @overload\n    def __divmod__(self, b: float32):\n        return float32(self).__divmod__(b)\n\n    @overload\n    def __pow__(self, b: float32) -> float32:\n        return float32(self) ** b\n\n    @overload\n    def __eq__(self, b: float32) -> bool:\n        return float32(self) == b\n\n    @overload\n    def __ne__(self, b: float32) -> bool:\n        return float32(self) != b\n\n    @overload\n    def __lt__(self, b: float32) -> bool:\n        return float32(self) < b\n\n    @overload\n    def __gt__(self, b: float32) -> bool:\n        return float32(self) > b\n\n    @overload\n    def __le__(self, b: float32) -> bool:\n        return float32(self) <= b\n\n    @overload\n    def __ge__(self, b: float32) -> bool:\n        return float32(self) >= b\n\n    @commutative\n    @overload\n    def __add__(self, b: float16) -> float16:\n        return float16(self) + b\n\n    @overload\n    def __sub__(self, b: float16) -> float16:\n        return float16(self) - b\n\n    @commutative\n    @overload\n    def __mul__(self, b: float16) -> float16:\n        return float16(self) * b\n\n    @overload\n    def __floordiv__(self, b: float16) -> float16:\n        return float16(self) // b\n\n    @overload\n    def __truediv__(self, b: float16) -> float16:\n        return float16(self) / b\n\n    @overload\n    def __mod__(self, b: float16) -> float16:\n        return float16(self) % b\n\n    @overload\n    def __divmod__(self, b: float16):\n        return float16(self).__divmod__(b)\n\n    @overload\n    def __pow__(self, b: float16) -> float16:\n        return float16(self) ** b\n\n    @overload\n    def __eq__(self, b: float16) -> bool:\n        return float16(self) == b\n\n    @overload\n    def __ne__(self, b: float16) -> bool:\n        return float16(self) != b\n\n    @overload\n    def __lt__(self, b: float16) -> bool:\n        return float16(self) < b\n\n    @overload\n    def __gt__(self, b: float16) -> bool:\n        return float16(self) > b\n\n    @overload\n    def __le__(self, b: float16) -> bool:\n        return float16(self) <= b\n\n    @overload\n    def __ge__(self, b: float16) -> bool:\n        return float16(self) >= b\n\n    @commutative\n    @overload\n    def __add__(self, b: bfloat16) -> bfloat16:\n        return bfloat16(self) + b\n\n    @overload\n    def __sub__(self, b: bfloat16) -> bfloat16:\n        return bfloat16(self) - b\n\n    @commutative\n    @overload\n    def __mul__(self, b: bfloat16) -> bfloat16:\n        return bfloat16(self) * b\n\n    @overload\n    def __floordiv__(self, b: bfloat16) -> bfloat16:\n        return bfloat16(self) // b\n\n    @overload\n    def __truediv__(self, b: bfloat16) -> bfloat16:\n        return bfloat16(self) / b\n\n    @overload\n    def __mod__(self, b: bfloat16) -> bfloat16:\n        return bfloat16(self) % b\n\n    @overload\n    def __divmod__(self, b: bfloat16):\n        return bfloat16(self).__divmod__(b)\n\n    @overload\n    def __pow__(self, b: bfloat16) -> bfloat16:\n        return bfloat16(self) ** b\n\n    @overload\n    def __eq__(self, b: bfloat16) -> bool:\n        return bfloat16(self) == b\n\n    @overload\n    def __ne__(self, b: bfloat16) -> bool:\n        return bfloat16(self) != b\n\n    @overload\n    def __lt__(self, b: bfloat16) -> bool:\n        return bfloat16(self) < b\n\n    @overload\n    def __gt__(self, b: bfloat16) -> bool:\n        return bfloat16(self) > b\n\n    @overload\n    def __le__(self, b: bfloat16) -> bool:\n        return bfloat16(self) <= b\n\n    @overload\n    def __ge__(self, b: bfloat16) -> bool:\n        return bfloat16(self) >= b\n\n    @commutative\n    @overload\n    def __add__(self, b: float128) -> float128:\n        return float128(self) + b\n\n    @overload\n    def __sub__(self, b: float128) -> float128:\n        return float128(self) - b\n\n    @commutative\n    @overload\n    def __mul__(self, b: float128) -> float128:\n        return float128(self) * b\n\n    @overload\n    def __floordiv__(self, b: float128) -> float128:\n        return float128(self) // b\n\n    @overload\n    def __truediv__(self, b: float128) -> float128:\n        return float128(self) / b\n\n    @overload\n    def __mod__(self, b: float128) -> float128:\n        return float128(self) % b\n\n    @overload\n    def __divmod__(self, b: float128):\n        return float128(self).__divmod__(b)\n\n    @overload\n    def __pow__(self, b: float128) -> float128:\n        return float128(self) ** b\n\n    @overload\n    def __eq__(self, b: float128) -> bool:\n        return float128(self) == b\n\n    @overload\n    def __ne__(self, b: float128) -> bool:\n        return float128(self) != b\n\n    @overload\n    def __lt__(self, b: float128) -> bool:\n        return float128(self) < b\n\n    @overload\n    def __gt__(self, b: float128) -> bool:\n        return float128(self) > b\n\n    @overload\n    def __le__(self, b: float128) -> bool:\n        return float128(self) <= b\n\n    @overload\n    def __ge__(self, b: float128) -> bool:\n        return float128(self) >= b\n\nf16 = float16\nbf16 = bfloat16\nf32 = float32\nf64 = float\nf128 = float128\n"
  },
  {
    "path": "stdlib/internal/types/function.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\n@extend\nclass Function:\n    @pure\n    @overload\n    @llvm\n    def __new__(what: Ptr[byte]) -> Function[T, TR]:\n        ret ptr %what\n\n    @overload\n    def __new__(what: Function[T, TR]) -> Function[T, TR]:\n       return what\n\n    @pure\n    @llvm\n    def __raw__(self) -> Ptr[byte]:\n        ret ptr %self\n\n    def __repr__(self) -> str:\n       return Ptr._ptr_to_str(self.__raw__(), \"function\")\n\n    @llvm\n    def __call_internal__(self: Function[T, TR], args: T) -> TR:\n        noop  # compiler will populate this one\n\n    def __call__(self, *args) -> TR:\n        return Function.__call_internal__(self, args)\n\n\n@extend\nclass Callable:\n    def __new__(fn: Function[[Ptr[byte], T], TR], data: Ptr[byte]) -> Callable[T, TR]:\n        return type._force_value_cast((fn, data), Callable[T, TR])\n\n    @overload\n    def __new__(fn: Function[[Ptr[byte], T], TR], data: Partial[M,PT,K,F],\n                T: type, TR: type,\n                M: Literal[str], PT: type, F: type, K: type) -> Callable[T, TR]:\n        p = Ptr[Partial[M,PT,K,F]](1)\n        p[0] = data\n        return Callable(fn, p.as_byte())\n\n    @overload\n    def __new__(fn: Function[[Ptr[byte], T], TR], data: Function[T, TR]) -> Callable[T, TR]:\n        return Callable(fn, data.__raw__())\n\n    @overload\n    def __new__(fn: Function[T, TR]) -> Callable[T, TR]:\n        def _wrap(data: Ptr[byte], args, f: type):\n            return f(data)(*args)\n        return Callable(\n            static.function.realized(_wrap(f=Function[T, TR], ...), Ptr[byte], T),\n            fn.__raw__()\n        )\n\n    def __call__(self, *args):\n        return self.fn.__call__(self.data, args)\n\n\n@extend\nclass Partial:\n    def __repr__(self):\n        return __magic__.repr_partial(self)\n\n    def __call__(self, *args, **kwargs):\n        return self(*args, **kwargs)\n\n    @property\n    def __fn_name__(self):\n        return F.__name__[16:-1]  # chop off unrealized_type\n\n    def __raw__(self):\n        # TODO: better error message\n        return F.T.__raw__()\n"
  },
  {
    "path": "stdlib/internal/types/generator.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n@extend\nclass Generator:\n    def _yield_final(val):\n        \"\"\"Compiler-generated.\"\"\"\n        pass\n\n    def _yield_in_no_suspend(T: type) -> T:\n        \"\"\"Compiler-generated.\"\"\"\n        pass\n\n    @__internal__\n    def __promise__(self) -> Ptr[T]:\n        pass\n\n    def done(self) -> bool:\n        self.__resume__()\n        return self.__done__()\n\n    def __next__(self: Generator[T]) -> T:\n        if isinstance(T, None):\n            pass\n        else:\n            return self.__promise__()[0]\n\n    def __iter__(self) -> Generator[T]:\n        return self\n\n    @pure\n    @derives\n    @llvm\n    def __raw__(self) -> Ptr[byte]:\n        ret ptr %self\n\n    @pure\n    @derives\n    @llvm\n    def __new__(ptr: cobj) -> Generator[T]:\n        ret ptr %ptr\n\n    @overload\n    def __new__() -> Generator[T]:\n        compile_error(\"invalid generator\")\n\n    @pure\n    @llvm\n    def __done__(self) -> bool:\n        declare i1 @llvm.coro.done(ptr nocapture readonly)\n        %0 = call i1 @llvm.coro.done(ptr %self)\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @nocapture\n    @llvm\n    def __resume__(self) -> None:\n        declare void @llvm.coro.resume(ptr)\n        call void @llvm.coro.resume(ptr %self)\n        ret {} {}\n\n    def __repr__(self) -> str:\n        return Ptr._ptr_to_str(self.__raw__(), \"generator\")\n\n    def send(self, what: T) -> T:\n        p = self.__promise__()\n        p[0] = what\n        self.__resume__()\n        return p[0]\n\n    @nocapture\n    @llvm\n    def destroy(self) -> None:\n        declare void @llvm.coro.destroy(ptr)\n        call void @llvm.coro.destroy(ptr %self)\n        ret {} {}\n\ngenerator = Generator\n"
  },
  {
    "path": "stdlib/internal/types/import_.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n\n@extend\nclass Import:\n    def _set_loaded(i: Ptr[Import]):\n        Ptr[bool](i.as_byte())[0] = True\n\n    def __repr__(self) -> str:\n        return f\"<module '{self.name}' from '{self.file}'>\"\n"
  },
  {
    "path": "stdlib/internal/types/int.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom internal.attributes import commutative, associative, distributive\n\n@extend\nclass int:\n    @pure\n    @llvm\n    def __new__() -> int:\n        ret i64 0\n\n    @overload\n    def __new__(s: str, base: int) -> int:\n        return int._from_str(s, base)\n\n    @overload\n    def __new__(what) -> int:\n        # do not overload! (needed to avoid pyobj conversion)\n        if isinstance(what, str) or isinstance(what, Optional[str]):\n            return int._from_str(what, 10)\n        else:\n            return what.__int__()\n\n    def __int__(self) -> int:\n        return self\n\n    @pure\n    @llvm\n    def __float__(self) -> float:\n        %tmp = sitofp i64 %self to double\n        ret double %tmp\n\n    def __index__(self) -> int:\n        return self\n\n    def __repr__(self) -> str:\n        return self.__format__(\"\")\n\n    def __copy__(self) -> int:\n        return self\n\n    def __deepcopy__(self) -> int:\n        return self\n\n    def __hash__(self) -> int:\n        return self\n\n    @pure\n    @llvm\n    def __bool__(self) -> bool:\n        %0 = icmp ne i64 %self, 0\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    def __pos__(self) -> int:\n        return self\n\n    def __neg__(self) -> int:\n        return 0 - self\n\n    @pure\n    @llvm\n    def __abs__(self) -> int:\n        declare i64 @llvm.abs.i64(i64, i1)\n        %0 = call i64 @llvm.abs.i64(i64 %self, i1 false)\n        ret i64 %0\n\n    @pure\n    @llvm\n    def __lshift__(self, other: int) -> int:\n        %0 = shl i64 %self, %other\n        ret i64 %0\n\n    @pure\n    @llvm\n    def __rshift__(self, other: int) -> int:\n        %0 = ashr i64 %self, %other\n        ret i64 %0\n\n    @pure\n    @commutative\n    @associative\n    @llvm\n    def __add__(self, b: bool) -> int:\n        %0 = sext {=bool} %b to i64\n        %1 = add i64 %self, %0\n        ret i64 %1\n\n    @pure\n    @commutative\n    @overload\n    @llvm\n    def __add__(self, other: float) -> float:\n        %0 = sitofp i64 %self to double\n        %1 = fadd double %0, %other\n        ret double %1\n\n    @pure\n    @commutative\n    @associative\n    @overload\n    @llvm\n    def __add__(self, b: int) -> int:\n        %tmp = add i64 %self, %b\n        ret i64 %tmp\n\n    @pure\n    @llvm\n    def __sub__(self, other: float) -> float:\n        %0 = sitofp i64 %self to double\n        %1 = fsub double %0, %other\n        ret double %1\n\n    @pure\n    @overload\n    @llvm\n    def __sub__(self, b: int) -> int:\n        %tmp = sub i64 %self, %b\n        ret i64 %tmp\n\n    @pure\n    @commutative\n    @llvm\n    def __mul__(self, other: float) -> float:\n        %0 = sitofp i64 %self to double\n        %1 = fmul double %0, %other\n        ret double %1\n\n    @pure\n    @commutative\n    @associative\n    @distributive\n    @overload\n    @llvm\n    def __mul__(self, b: int) -> int:\n        %tmp = mul i64 %self, %b\n        ret i64 %tmp\n\n    @pure\n    @llvm\n    def __floordiv__(self, other: float) -> float:\n        declare double @llvm.floor.f64(double)\n        %0 = sitofp i64 %self to double\n        %1 = fdiv double %0, %other\n        %2 = call double @llvm.floor.f64(double %1)\n        ret double %2\n\n    @pure\n    @overload\n    @llvm\n    def __floordiv__(self, b: int) -> int:\n        %tmp = sdiv i64 %self, %b\n        ret i64 %tmp\n\n    @pure\n    @llvm\n    def __truediv__(self, other: float) -> float:\n        %0 = sitofp i64 %self to double\n        %1 = fdiv double %0, %other\n        ret double %1\n\n    @pure\n    @overload\n    @llvm\n    def __truediv__(self, other: int) -> float:\n        %0 = sitofp i64 %self to double\n        %1 = sitofp i64 %other to double\n        %2 = fdiv double %0, %1\n        ret double %2\n\n    @pure\n    @llvm\n    def __mod__(self, other: float) -> float:\n        %0 = sitofp i64 %self to double\n        %1 = frem double %0, %other\n        ret double %1\n\n    @pure\n    @overload\n    @llvm\n    def __mod__(a: int, b: int) -> int:\n        %tmp = srem i64 %a, %b\n        ret i64 %tmp\n\n    def __divmod__(self, other: float) -> Tuple[float, float]:\n        return float(self).__divmod__(other)\n\n    @overload\n    def __divmod__(self, other: int) -> Tuple[int, int]:\n        d = self // other\n        m = self - d * other\n        if m and ((other ^ m) < 0):\n            m += other\n            d -= 1\n        return (d, m)\n\n    @pure\n    @llvm\n    def __invert__(a: int) -> int:\n        %tmp = xor i64 %a, -1\n        ret i64 %tmp\n\n    @pure\n    @commutative\n    @associative\n    @llvm\n    def __and__(a: int, b: int) -> int:\n        %tmp = and i64 %a, %b\n        ret i64 %tmp\n\n    @pure\n    @commutative\n    @associative\n    @llvm\n    def __or__(a: int, b: int) -> int:\n        %tmp = or i64 %a, %b\n        ret i64 %tmp\n\n    @pure\n    @commutative\n    @associative\n    @llvm\n    def __xor__(a: int, b: int) -> int:\n        %tmp = xor i64 %a, %b\n        ret i64 %tmp\n\n    @pure\n    @llvm\n    def __bitreverse__(a: int) -> int:\n        declare i64 @llvm.bitreverse.i64(i64 %a)\n        %tmp = call i64 @llvm.bitreverse.i64(i64 %a)\n        ret i64 %tmp\n\n    @pure\n    @llvm\n    def __bswap__(a: int) -> int:\n        declare i64 @llvm.bswap.i64(i64 %a)\n        %tmp = call i64 @llvm.bswap.i64(i64 %a)\n        ret i64 %tmp\n\n    @pure\n    @llvm\n    def __ctpop__(a: int) -> int:\n        declare i64 @llvm.ctpop.i64(i64 %a)\n        %tmp = call i64 @llvm.ctpop.i64(i64 %a)\n        ret i64 %tmp\n\n    @pure\n    @llvm\n    def __ctlz__(a: int) -> int:\n        declare i64 @llvm.ctlz.i64(i64 %a, i1 %is_zero_undef)\n        %tmp = call i64 @llvm.ctlz.i64(i64 %a, i1 false)\n        ret i64 %tmp\n\n    @pure\n    @llvm\n    def __cttz__(a: int) -> int:\n        declare i64 @llvm.cttz.i64(i64 %a, i1 %is_zero_undef)\n        %tmp = call i64 @llvm.cttz.i64(i64 %a, i1 false)\n        ret i64 %tmp\n\n    @pure\n    @llvm\n    def __eq__(self, b: float) -> bool:\n        %0 = sitofp i64 %self to double\n        %1 = fcmp oeq double %0, %b\n        %2 = zext i1 %1 to i8\n        ret i8 %2\n\n    @pure\n    @overload\n    @llvm\n    def __eq__(a: int, b: int) -> bool:\n        %tmp = icmp eq i64 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __ne__(self, b: float) -> bool:\n        %0 = sitofp i64 %self to double\n        %1 = fcmp one double %0, %b\n        %2 = zext i1 %1 to i8\n        ret i8 %2\n\n    @pure\n    @overload\n    @llvm\n    def __ne__(a: int, b: int) -> bool:\n        %tmp = icmp ne i64 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __lt__(self, b: float) -> bool:\n        %0 = sitofp i64 %self to double\n        %1 = fcmp olt double %0, %b\n        %2 = zext i1 %1 to i8\n        ret i8 %2\n\n    @pure\n    @overload\n    @llvm\n    def __lt__(a: int, b: int) -> bool:\n        %tmp = icmp slt i64 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __gt__(self, b: float) -> bool:\n        %0 = sitofp i64 %self to double\n        %1 = fcmp ogt double %0, %b\n        %2 = zext i1 %1 to i8\n        ret i8 %2\n\n    @pure\n    @overload\n    @llvm\n    def __gt__(a: int, b: int) -> bool:\n        %tmp = icmp sgt i64 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __le__(self, b: float) -> bool:\n        %0 = sitofp i64 %self to double\n        %1 = fcmp ole double %0, %b\n        %2 = zext i1 %1 to i8\n        ret i8 %2\n\n    @pure\n    @overload\n    @llvm\n    def __le__(a: int, b: int) -> bool:\n        %tmp = icmp sle i64 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @pure\n    @llvm\n    def __ge__(self, b: float) -> bool:\n        %0 = sitofp i64 %self to double\n        %1 = fcmp oge double %0, %b\n        %2 = zext i1 %1 to i8\n        ret i8 %2\n\n    @pure\n    @overload\n    @llvm\n    def __ge__(a: int, b: int) -> bool:\n        %tmp = icmp sge i64 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    def __pow__(self, exp: float) -> float:\n        return float(self) ** exp\n\n    def _ipow(self, exp: int) -> int:\n        if exp < 0:\n            return 0\n        result = 1\n        while True:\n            if exp & 1:\n                result *= self\n            exp >>= 1\n            if not exp:\n                break\n            self *= self\n        return result\n\n    @overload\n    def __pow__(self, exp: int) -> int:\n        return self._ipow(exp)\n\n    def __pow__(self, exp: Literal[int]):\n        if exp == 0:\n            return 1\n        elif exp > 0:\n            return self._ipow(exp)\n        else:\n            return self ** float(exp)\n\n    def popcnt(self) -> int:\n        return Int[64](self).popcnt()\n\n    def bit_length(self) -> int:\n        return Int[64](self).bit_length()\n\n    @nocapture\n    @llvm\n    def __atomic_xchg__(d: Ptr[int], b: int) -> None:\n        %tmp = atomicrmw xchg ptr %d, i64 %b seq_cst\n        ret {} {}\n\n    @nocapture\n    @llvm\n    def __atomic_add__(d: Ptr[int], b: int) -> int:\n        %tmp = atomicrmw add ptr %d, i64 %b seq_cst\n        ret i64 %tmp\n\n    @nocapture\n    @llvm\n    def __atomic_sub__(d: Ptr[int], b: int) -> int:\n        %tmp = atomicrmw sub ptr %d, i64 %b seq_cst\n        ret i64 %tmp\n\n    @nocapture\n    @llvm\n    def __atomic_and__(d: Ptr[int], b: int) -> int:\n        %tmp = atomicrmw and ptr %d, i64 %b seq_cst\n        ret i64 %tmp\n\n    @nocapture\n    @llvm\n    def __atomic_nand__(d: Ptr[int], b: int) -> int:\n        %tmp = atomicrmw nand ptr %d, i64 %b seq_cst\n        ret i64 %tmp\n\n    @nocapture\n    @llvm\n    def __atomic_or__(d: Ptr[int], b: int) -> int:\n        %tmp = atomicrmw or ptr %d, i64 %b seq_cst\n        ret i64 %tmp\n\n    @nocapture\n    @llvm\n    def __atomic_xor__(d: Ptr[int], b: int) -> int:\n        %tmp = atomicrmw xor ptr %d, i64 %b seq_cst\n        ret i64 %tmp\n\n    @nocapture\n    @llvm\n    def __atomic_min__(d: Ptr[int], b: int) -> int:\n        %tmp = atomicrmw min ptr %d, i64 %b seq_cst\n        ret i64 %tmp\n\n    @nocapture\n    @llvm\n    def __atomic_max__(d: Ptr[int], b: int) -> int:\n        %tmp = atomicrmw max ptr %d, i64 %b seq_cst\n        ret i64 %tmp\n\n    def __match__(self, obj: int) -> bool:\n        return self == obj\n\n    @property\n    def real(self) -> int:\n        return self\n\n    @property\n    def imag(self) -> int:\n        return 0\n"
  },
  {
    "path": "stdlib/internal/types/intn.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom internal.attributes import commutative, associative, distributive\n\ndef _check_bitwidth(N: Literal[int]):\n    if N <= 0:\n        compile_error(\"integer bit-width must be greater than 0\")\n    pass\n\n@extend\nclass Int:\n    @pure\n    @llvm\n    def _sext(what, F: Literal[int], T: Literal[int]) -> Int[T]:\n        %0 = sext i{=F} %what to i{=T}\n        ret i{=T} %0\n\n    @pure\n    @llvm\n    def _zext(what, F: Literal[int], T: Literal[int]) -> Int[T]:\n        %0 = zext i{=F} %what to i{=T}\n        ret i{=T} %0\n\n    @pure\n    @llvm\n    def _trunc(what, F: Literal[int], T: Literal[int]) -> Int[T]:\n        %0 = trunc i{=F} %what to i{=T}\n        ret i{=T} %0\n\n    def __new__() -> Int[N]:\n        _check_bitwidth(N)\n        return Int[N](0)\n\n    @overload\n    def __new__(what: float) -> Int[N]:\n        @pure\n        @llvm\n        def convert(what: float, N: Literal[int]) -> Int[N]:\n            %n = fptosi double %what to i{=N}\n            ret i{=N} %n\n\n        _check_bitwidth(N)\n        return convert(what)\n\n    @overload\n    def __new__(what: Int[M], M: Literal[int]) -> Int[N]:\n        _check_bitwidth(N)\n        if N < M:\n            return Int._trunc(what, M, N)\n        elif N == M:\n            return what\n        else:\n            return Int._sext(what, M, N)\n\n    @overload\n    def __new__(what: UInt[M], M: Literal[int]) -> Int[N]:\n        _check_bitwidth(N)\n        if N < M:\n            return Int._trunc(what, M, N)\n        elif N == M:\n            return Int[N](what)\n        else:\n            return Int._sext(what, M, N)\n\n    @overload\n    def __new__(what: UInt[N]) -> Int[N]:\n        @pure\n        @llvm\n        def convert(what: UInt[N], N: Literal[int]) -> Int[N]:\n            ret i{=N} %what\n\n        _check_bitwidth(N)\n        return convert(what)\n\n    @overload\n    def __new__(what: int) -> Int[N]:\n        _check_bitwidth(N)\n        if N < 64:\n            return Int._trunc(what, 64, N)\n        elif N == 64:\n            return what\n        else:\n            return Int._sext(what, 64, N)\n\n    @overload\n    def __new__(what: str) -> Int[N]:\n        _check_bitwidth(N)\n        ret = Int[N]()\n        i = 0\n        sign = Int[N](1)\n        if i < what.len and what.ptr[0] == byte(45):\n            sign = Int[N](-1)\n            i += 1\n        while i < what.len:\n            if what.ptr[i] < byte(48) or what.ptr[i] >= byte(58):\n                raise ValueError(\"Invalid integer string\")\n            ret = ret * Int[N](10) + Int[N](int(what.ptr[i]) - 48)\n            i += 1\n        return sign * ret\n\n    def __int__(self) -> int:\n        if N > 64:\n            return Int._trunc(self, N, 64)\n        elif N == 64:\n            return self\n        else:\n            return Int._sext(self, N, 64)\n\n    def __index__(self) -> int:\n        return int(self)\n\n    def __copy__(self) -> Int[N]:\n        return self\n\n    def __deepcopy__(self) -> Int[N]:\n        return self\n\n    def __hash__(self) -> int:\n        return int(self)\n\n    @pure\n    @llvm\n    def __float__(self) -> float:\n        %0 = sitofp i{=N} %self to double\n        ret double %0\n\n    @pure\n    @llvm\n    def __bool__(self) -> bool:\n        %0 = icmp ne i{=N} %self, 0\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    def __pos__(self) -> Int[N]:\n        return self\n\n    @pure\n    @llvm\n    def __neg__(self) -> Int[N]:\n        %0 = sub i{=N} 0, %self\n        ret i{=N} %0\n\n    @pure\n    @llvm\n    def __invert__(self) -> Int[N]:\n        %0 = xor i{=N} %self, -1\n        ret i{=N} %0\n\n    @pure\n    @llvm\n    def __abs__(self) -> Int[N]:\n        declare i{=N} @llvm.abs.i{=N}(i{=N}, i1)\n        %0 = call i{=N} @llvm.abs.i{=N}(i{=N} %self, i1 false)\n        ret i{=N} %0\n\n    @pure\n    @commutative\n    @associative\n    @llvm\n    def __add__(self, other: Int[N]) -> Int[N]:\n        %0 = add i{=N} %self, %other\n        ret i{=N} %0\n\n    @pure\n    @llvm\n    def __sub__(self, other: Int[N]) -> Int[N]:\n        %0 = sub i{=N} %self, %other\n        ret i{=N} %0\n\n    @pure\n    @commutative\n    @associative\n    @distributive\n    @llvm\n    def __mul__(self, other: Int[N]) -> Int[N]:\n        %0 = mul i{=N} %self, %other\n        ret i{=N} %0\n\n    @pure\n    @llvm\n    def _floordiv(self, other: Int[N]) -> Int[N]:\n        %0 = sdiv i{=N} %self, %other\n        ret i{=N} %0\n    def __floordiv__(self, other: Int[N]) -> Int[N]:\n        if N > 128:\n            compile_error(\"division is not supported on Int[N] when N > 128\")\n        return self._floordiv(other)\n\n    @pure\n    @llvm\n    def __truediv__(self, other: Int[N]) -> float:\n        %0 = sitofp i{=N} %self to double\n        %1 = sitofp i{=N} %other to double\n        %2 = fdiv double %0, %1\n        ret double %2\n\n    @pure\n    @llvm\n    def _mod(self, other: Int[N]) -> Int[N]:\n        %0 = srem i{=N} %self, %other\n        ret i{=N} %0\n\n    def __mod__(self, other: Int[N]) -> Int[N]:\n        if N > 128:\n            compile_error(\"modulus is not supported on Int[N] when N > 128\")\n        return self._mod(other)\n\n    def __divmod__(self, other: Int[N]) -> Tuple[Int[N], Int[N]]:\n        d = self // other\n        m = self - d * other\n        if m and ((other ^ m) < Int[N](0)):\n            m += other\n            d -= Int[N](1)\n        return (d, m)\n\n    @pure\n    @llvm\n    def __lshift__(self, other: Int[N]) -> Int[N]:\n        %0 = shl i{=N} %self, %other\n        ret i{=N} %0\n\n    @pure\n    @llvm\n    def __rshift__(self, other: Int[N]) -> Int[N]:\n        %0 = ashr i{=N} %self, %other\n        ret i{=N} %0\n\n    @pure\n    @llvm\n    def __eq__(self, other: Int[N]) -> bool:\n        %0 = icmp eq i{=N} %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __ne__(self, other: Int[N]) -> bool:\n        %0 = icmp ne i{=N} %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __lt__(self, other: Int[N]) -> bool:\n        %0 = icmp slt i{=N} %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __gt__(self, other: Int[N]) -> bool:\n        %0 = icmp sgt i{=N} %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __le__(self, other: Int[N]) -> bool:\n        %0 = icmp sle i{=N} %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __ge__(self, other: Int[N]) -> bool:\n        %0 = icmp sge i{=N} %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @commutative\n    @associative\n    @llvm\n    def __and__(self, other: Int[N]) -> Int[N]:\n        %0 = and i{=N} %self, %other\n        ret i{=N} %0\n\n    @pure\n    @commutative\n    @associative\n    @llvm\n    def __or__(self, other: Int[N]) -> Int[N]:\n        %0 = or i{=N} %self, %other\n        ret i{=N} %0\n\n    @pure\n    @commutative\n    @associative\n    @llvm\n    def __xor__(self, other: Int[N]) -> Int[N]:\n        %0 = xor i{=N} %self, %other\n        ret i{=N} %0\n\n    def __pow__(self, exp: Int[N]) -> Int[N]:\n        zero = Int[N](0)\n        one = Int[N](1)\n\n        if exp < zero:\n            return zero\n        result = one\n        while True:\n            if exp & one:\n                result *= self\n            exp >>= one\n            if not exp:\n                break\n            self *= self\n        return result\n\n    def __repr__(self) -> str:\n        return f\"Int[{N}]({self.__str__()})\"\n\n    @pure\n    @llvm\n    def _popcnt(self) -> Int[N]:\n        declare i{=N} @llvm.ctpop.i{=N}(i{=N})\n        %0 = call i{=N} @llvm.ctpop.i{=N}(i{=N} %self)\n        ret i{=N} %0\n\n    def popcnt(self) -> int:\n        return int(self._popcnt())\n\n    @pure\n    @llvm\n    def _ctlz(self) -> Int[N]:\n        declare i{=N} @llvm.ctlz.i{=N}(i{=N}, i1)\n        %0 = call i{=N} @llvm.ctlz.i{=N}(i{=N} %self, i1 true)\n        ret i{=N} %0\n\n    def bit_length(self) -> int:\n        if not self:\n            return 0\n        else:\n            return N - int(self.__abs__()._ctlz())\n\n    def len() -> int:\n        return N\n\n\n@extend\nclass UInt:\n\n    def __new__() -> UInt[N]:\n        _check_bitwidth(N)\n        return UInt[N](0)\n\n    @overload\n    def __new__(what: UInt[N]) -> UInt[N]:\n        _check_bitwidth(N)\n        return what\n\n    @overload\n    def __new__(what: float) -> UInt[N]:\n        @pure\n        @llvm\n        def convert(what: float, N: Literal[int]) -> UInt[N]:\n            %n = fptoui double %what to i{=N}\n            ret i{=N} %n\n\n        return convert(what)\n\n    @overload\n    def __new__(what: UInt[M], M: Literal[int]) -> UInt[N]:\n        _check_bitwidth(N)\n        if N < M:\n            return UInt[N](Int._trunc(what, M, N))\n        elif N == M:\n            return what\n        else:\n            return UInt[N](Int._zext(what, M, N))\n\n    @overload\n    def __new__(what: Int[M], M: Literal[int]) -> UInt[N]:\n        _check_bitwidth(N)\n        if N < M:\n            return UInt[N](Int._trunc(what, M, N))\n        elif N == M:\n            return UInt[N](what)\n        else:\n            return UInt[N](Int._sext(what, M, N))\n\n    @overload\n    def __new__(what: Int[N]) -> UInt[N]:\n        @pure\n        @llvm\n        def convert(what: Int[N], N: Literal[int]) -> UInt[N]:\n            ret i{=N} %what\n\n        _check_bitwidth(N)\n        return convert(what)\n\n    @pure\n    @llvm\n    def __new__(what: Int[N]) -> UInt[N]:\n        ret i{=N} %what\n\n    def __new__(what: int) -> UInt[N]:\n        @pure\n        @llvm\n        def convert(what: int) -> UInt[64]:\n            ret i64 %what\n\n        _check_bitwidth(N)\n        if N < 64:\n            return UInt[N](Int._trunc(what, 64, N))\n        elif N == 64:\n            return convert(what)\n        else:\n            return UInt[N](Int._sext(what, 64, N))\n\n    def __new__(what: str) -> UInt[N]:\n        _check_bitwidth(N)\n        return UInt[N](Int[N](what))\n\n    def __int__(self) -> int:\n        if N > 64:\n            return Int._trunc(self, N, 64)\n        elif N == 64:\n            return Int[64](self)\n        else:\n            return Int._zext(self, N, 64)\n\n    def __index__(self) -> int:\n        return int(self)\n\n    def __copy__(self) -> UInt[N]:\n        return self\n\n    def __deepcopy__(self) -> UInt[N]:\n        return self\n\n    def __hash__(self) -> int:\n        return int(self)\n\n    @pure\n    @llvm\n    def __float__(self) -> float:\n        %0 = uitofp i{=N} %self to double\n        ret double %0\n\n    @pure\n    @llvm\n    def __bool__(self) -> bool:\n        %0 = icmp ne i{=N} %self, 0\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    def __pos__(self) -> UInt[N]:\n        return self\n\n    @pure\n    @llvm\n    def __neg__(self) -> UInt[N]:\n        %0 = sub i{=N} 0, %self\n        ret i{=N} %0\n\n    @pure\n    @llvm\n    def __invert__(self) -> UInt[N]:\n        %0 = xor i{=N} %self, -1\n        ret i{=N} %0\n\n    def __abs__(self) -> UInt[N]:\n        return self\n\n    @pure\n    @commutative\n    @associative\n    @llvm\n    def __add__(self, other: UInt[N]) -> UInt[N]:\n        %0 = add i{=N} %self, %other\n        ret i{=N} %0\n\n    @pure\n    @llvm\n    def __sub__(self, other: UInt[N]) -> UInt[N]:\n        %0 = sub i{=N} %self, %other\n        ret i{=N} %0\n\n    @pure\n    @commutative\n    @associative\n    @distributive\n    @llvm\n    def __mul__(self, other: UInt[N]) -> UInt[N]:\n        %0 = mul i{=N} %self, %other\n        ret i{=N} %0\n\n    @pure\n    @llvm\n    def _floordiv(self, other: UInt[N]) -> UInt[N]:\n        %0 = udiv i{=N} %self, %other\n        ret i{=N} %0\n    def __floordiv__(self, other: UInt[N]) -> UInt[N]:\n        if N > 128:\n            compile_error(\"division is not supported on UInt[N] when N > 128\")\n        return self._floordiv(other)\n\n    @pure\n    @llvm\n    def __truediv__(self, other: UInt[N]) -> float:\n        %0 = uitofp i{=N} %self to double\n        %1 = uitofp i{=N} %other to double\n        %2 = fdiv double %0, %1\n        ret double %2\n\n    @pure\n    @llvm\n    def _mod(self, other: UInt[N]) -> UInt[N]:\n        %0 = urem i{=N} %self, %other\n        ret i{=N} %0\n    def __mod__(self, other: UInt[N]) -> UInt[N]:\n        if N > 128:\n            compile_error(\"modulus is not supported on UInt[N] when N > 128\")\n        return self._mod(other)\n\n    def __divmod__(self, other: UInt[N]) -> Tuple[UInt[N], UInt[N]]:\n        return (self // other, self % other)\n\n    @pure\n    @llvm\n    def __lshift__(self, other: UInt[N]) -> UInt[N]:\n        %0 = shl i{=N} %self, %other\n        ret i{=N} %0\n\n    @pure\n    @llvm\n    def __rshift__(self, other: UInt[N]) -> UInt[N]:\n        %0 = lshr i{=N} %self, %other\n        ret i{=N} %0\n\n    @pure\n    @llvm\n    def __eq__(self, other: UInt[N]) -> bool:\n        %0 = icmp eq i{=N} %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __ne__(self, other: UInt[N]) -> bool:\n        %0 = icmp ne i{=N} %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __lt__(self, other: UInt[N]) -> bool:\n        %0 = icmp ult i{=N} %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __gt__(self, other: UInt[N]) -> bool:\n        %0 = icmp ugt i{=N} %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __le__(self, other: UInt[N]) -> bool:\n        %0 = icmp ule i{=N} %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __ge__(self, other: UInt[N]) -> bool:\n        %0 = icmp uge i{=N} %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @commutative\n    @associative\n    @llvm\n    def __and__(self, other: UInt[N]) -> UInt[N]:\n        %0 = and i{=N} %self, %other\n        ret i{=N} %0\n\n    @pure\n    @commutative\n    @associative\n    @llvm\n    def __or__(self, other: UInt[N]) -> UInt[N]:\n        %0 = or i{=N} %self, %other\n        ret i{=N} %0\n\n    @pure\n    @commutative\n    @associative\n    @llvm\n    def __xor__(self, other: UInt[N]) -> UInt[N]:\n        %0 = xor i{=N} %self, %other\n        ret i{=N} %0\n\n    def __pow__(self, exp: UInt[N]) -> UInt[N]:\n        zero = UInt[N](0)\n        one = UInt[N](1)\n\n        if exp < zero:\n            return zero\n        result = one\n        while True:\n            if exp & one:\n                result *= self\n            exp >>= one\n            if not exp:\n                break\n            self *= self\n        return result\n\n    def __repr__(self) -> str:\n        return f\"UInt[{N}]({self.__str__()})\"\n\n    def popcnt(self) -> int:\n        return int(Int[N](self)._popcnt())\n\n    @pure\n    @llvm\n    def _ctlz(self) -> UInt[N]:\n        declare i{=N} @llvm.ctlz.i{=N}(i{=N}, i1)\n        %0 = call i{=N} @llvm.ctlz.i{=N}(i{=N} %self, i1 true)\n        ret i{=N} %0\n\n    def bit_length(self) -> int:\n        if not self:\n            return 0\n        else:\n            return N - int(self._ctlz())\n\n    def len() -> int:\n        return N\n\ni1 = Int[1]\ni8 = Int[8]\ni16 = Int[16]\ni32 = Int[32]\ni64 = Int[64]\ni128 = Int[128]\n\nu1 = UInt[1]\nu8 = UInt[8]\nu16 = UInt[16]\nu32 = UInt[32]\nu64 = UInt[64]\nu128 = UInt[128]\n"
  },
  {
    "path": "stdlib/internal/types/optional.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n@extend\nclass Optional:\n    @pure\n    @llvm\n    def _tuple_new(T: type) -> Optional[T]:\n        ret { i1, {=T} } { i1 false, {=T} undef }\n\n    @pure\n    @llvm\n    def _ref_new(T: type) -> Optional[T]:\n        ret ptr null\n\n    def __new__() -> Optional[T]:\n        if isinstance(T, ByVal):\n            return Optional._tuple_new(T)\n        else:\n            return Optional._ref_new(T)\n\n    @pure\n    @derives\n    @llvm\n    def _tuple_new_arg(what: T, T: type) -> Optional[T]:\n        %0 = insertvalue { i1, {=T} } { i1 true, {=T} undef }, {=T} %what, 1\n        ret { i1, {=T} } %0\n\n    @pure\n    @derives\n    @llvm\n    def _ref_new_arg(what: T, T: type) -> Optional[T]:\n        ret ptr %what\n\n    @overload\n    def __new__(what: T) -> Optional[T]:\n        if isinstance(T, ByVal):\n            return Optional._tuple_new_arg(what, T)\n        else:\n            return Optional._ref_new_arg(what, T)\n\n    @pure\n    @llvm\n    def _tuple_bool(what: Optional[T], T: type) -> bool:\n        %0 = extractvalue { i1, {=T} } %what, 0\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def _ref_bool(what: Optional[T], T: type) -> bool:\n        %0 = icmp ne ptr %what, null\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    def __has__(self) -> bool:\n        if isinstance(T, ByVal):\n            return Optional._tuple_bool(self, T)\n        else:\n            return Optional._ref_bool(self, T)\n\n    @pure\n    @derives\n    @llvm\n    def _tuple_invert(what: Optional[T], T: type) -> T:\n        %0 = extractvalue { i1, {=T} } %what, 1\n        ret {=T} %0\n\n    @pure\n    @derives\n    @llvm\n    def _ref_invert(what: Optional[T], T: type) -> T:\n        ret ptr %what\n\n    def __val__(self) -> T:\n        if isinstance(T, ByVal):\n            return Optional._tuple_invert(self, T)\n        else:\n            return Optional._ref_invert(self, T)\n\n    def __val_or__(self, default: T):\n        if self.__has__():\n            return self.__val__()\n        return default\n\n    def __bool__(self) -> bool:\n        if not self.__has__():\n            return False\n        if hasattr(self.__val__(), \"__bool__\"):\n            return self.__val__().__bool__()\n        else:\n            return True\n\n    def __eq__(self, other: T) -> bool:\n        if self is None:\n            return False\n        return self.__val__() == other\n\n    @overload\n    def __eq__(self, other: Optional[T]) -> bool:\n        if (self is None) or (other is None):\n            return (self is None) and (other is None)\n        return self.__val__() == other.__val__()\n\n    def __ne__(self, other: T) -> bool:\n        if self is None:\n            return True\n        return self.__val__() != other\n\n    @overload\n    def __ne__(self, other: Optional[T]) -> bool:\n        if (self is None) or (other is None):\n            return not ((self is None) and (other is None))\n        return self.__val__() != other.__val__()\n\n    def __str__(self) -> str:\n        return \"None\" if self is None else str(self.__val__())\n\n    def __repr__(self) -> str:\n        return \"None\" if self is None else self.__val__().__repr__()\n\n    def __is_optional__(self, other: Optional[T]) -> bool:\n        self_has = self.__has__()\n        other_has = other.__has__()\n        if (not self_has) or (not other_has):\n            return (not self_has) and (not other_has)\n        return self.__val__() is other.__val__()\n\noptional = Optional\n\ndef unwrap(opt: Optional[T], T: type) -> T:\n    if opt.__has__():\n        return opt.__val__()\n    raise ValueError(f\"optional unpack failed: expected {T.__class__.__name__}, got None\")\n"
  },
  {
    "path": "stdlib/internal/types/ptr.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n@extend\nclass Ptr:\n    @pure\n    @llvm\n    def __new__() -> Ptr[T]:\n        ret ptr null\n\n    @overload\n    @__internal__\n    def __new__(sz: int) -> Ptr[T]:\n        pass\n\n    @pure\n    @derives\n    @overload\n    @llvm\n    def __new__(other: Ptr[T]) -> Ptr[T]:\n        ret ptr %other\n\n    @pure\n    @derives\n    @overload\n    @llvm\n    def __new__(other: Ptr) -> Ptr[T]:\n        ret ptr %other\n\n    @pure\n    @llvm\n    def __int__(self) -> int:\n        %0 = ptrtoint ptr %self to i64\n        ret i64 %0\n\n    @pure\n    @llvm\n    def __copy__(self) -> Ptr[T]:\n        ret ptr %self\n\n    @pure\n    @llvm\n    def __bool__(self) -> bool:\n        %0 = icmp ne ptr %self, null\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @derives\n    @llvm\n    def __getitem__(self, index: int) -> T:\n        %0 = getelementptr {=T}, ptr %self, i64 %index\n        %1 = load {=T}, ptr %0\n        ret {=T} %1\n\n    @self_captures\n    @llvm\n    def __setitem__(self, index: int, what: T) -> None:\n        %0 = getelementptr {=T}, ptr %self, i64 %index\n        store {=T} %what, ptr %0\n        ret {} {}\n\n    @pure\n    @derives\n    @llvm\n    def __add__(self, other: int) -> Ptr[T]:\n        %0 = getelementptr {=T}, ptr %self, i64 %other\n        ret ptr %0\n\n    @pure\n    @llvm\n    def __sub__(self, other: Ptr[T]) -> int:\n        %0 = ptrtoint ptr %self to i64\n        %1 = ptrtoint ptr %other to i64\n        %2 = sub i64 %0, %1\n        %3 = sdiv exact i64 %2, ptrtoint (ptr getelementptr ({=T}, {=T}* null, i32 1) to i64)\n        ret i64 %3\n\n    @pure\n    @derives\n    @llvm\n    def __sub__(self, other: int) -> Ptr[T]:\n        %0 = sub i64 0, %other\n        %1 = getelementptr {=T}, {=T}* %self, i64 %0\n        ret {=T}* %1\n\n    @pure\n    @llvm\n    def __eq__(self, other: Ptr[T]) -> bool:\n        %0 = icmp eq ptr %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __ne__(self, other: Ptr[T]) -> bool:\n        %0 = icmp ne ptr %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __lt__(self, other: Ptr[T]) -> bool:\n        %0 = icmp slt ptr %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __gt__(self, other: Ptr[T]) -> bool:\n        %0 = icmp sgt ptr %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __le__(self, other: Ptr[T]) -> bool:\n        %0 = icmp sle ptr %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @pure\n    @llvm\n    def __ge__(self, other: Ptr[T]) -> bool:\n        %0 = icmp sge ptr %self, %other\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    @nocapture\n    @llvm\n    def __prefetch_r0__(self) -> None:\n        declare void @llvm.prefetch(ptr nocapture readonly, i32, i32, i32)\n        call void @llvm.prefetch(ptr %self, i32 0, i32 0, i32 1)\n        ret {} {}\n\n    @nocapture\n    @llvm\n    def __prefetch_r1__(self) -> None:\n        declare void @llvm.prefetch(ptr nocapture readonly, i32, i32, i32)\n        call void @llvm.prefetch(ptr %self, i32 0, i32 1, i32 1)\n        ret {} {}\n\n    @nocapture\n    @llvm\n    def __prefetch_r2__(self) -> None:\n        declare void @llvm.prefetch(ptr nocapture readonly, i32, i32, i32)\n        call void @llvm.prefetch(ptr %self, i32 0, i32 2, i32 1)\n        ret {} {}\n\n    @nocapture\n    @llvm\n    def __prefetch_r3__(self) -> None:\n        declare void @llvm.prefetch(ptr nocapture readonly, i32, i32, i32)\n        call void @llvm.prefetch(ptr %self, i32 0, i32 3, i32 1)\n        ret {} {}\n\n    @nocapture\n    @llvm\n    def __prefetch_w0__(self) -> None:\n        declare void @llvm.prefetch(ptr nocapture readonly, i32, i32, i32)\n        call void @llvm.prefetch(ptr %self, i32 1, i32 0, i32 1)\n        ret {} {}\n\n    @nocapture\n    @llvm\n    def __prefetch_w1__(self) -> None:\n        declare void @llvm.prefetch(ptr nocapture readonly, i32, i32, i32)\n        call void @llvm.prefetch(ptr %self, i32 1, i32 1, i32 1)\n        ret {} {}\n\n    @nocapture\n    @llvm\n    def __prefetch_w2__(self) -> None:\n        declare void @llvm.prefetch(ptr nocapture readonly, i32, i32, i32)\n        call void @llvm.prefetch(ptr %self, i32 1, i32 2, i32 1)\n        ret {} {}\n\n    @nocapture\n    @llvm\n    def __prefetch_w3__(self) -> None:\n        declare void @llvm.prefetch(ptr nocapture readonly, i32, i32, i32)\n        call void @llvm.prefetch(ptr %self, i32 1, i32 3, i32 1)\n        ret {} {}\n\n    @pure\n    @derives\n    @llvm\n    def as_byte(self) -> Ptr[byte]:\n        ret ptr %self\n\n    def __repr__(self) -> str:\n        return self.__format__(\"\")\n\n    def _ptr_to_str(p: Ptr[byte], name: str) -> str:\n        pstr = p.__repr__()\n        # '<[name] at [pstr]>'\n        total = 1 + name.len + 4 + pstr.len + 1\n        buf = Ptr[byte](total)\n        where = 0\n        buf[where] = byte(60)  # '<'\n        where += 1\n        str.memcpy(buf + where, name.ptr, name.len)\n        where += name.len\n        buf[where] = byte(32)  # ' '\n        where += 1\n        buf[where] = byte(97)  # 'a'\n        where += 1\n        buf[where] = byte(116)  # 't'\n        where += 1\n        buf[where] = byte(32)  # ' '\n        where += 1\n        str.memcpy(buf + where, pstr.ptr, pstr.len)\n        where += pstr.len\n        buf[where] = byte(62)  # '>'\n        free(pstr.ptr)\n        return str(buf, total)\n\n\nptr = Ptr\nJar = Ptr[byte]\n\n@extend\nclass NoneType:\n    def __eq__(self, other: NoneType):\n        return True\n\n    def __ne__(self, other: NoneType):\n        return False\n\n    def __bool__(self) -> bool:\n        return False\n\n    def __repr__(self) -> str:\n        return \"None\"\n"
  },
  {
    "path": "stdlib/internal/types/range.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n@tuple\nclass range:\n    start: int\n    stop: int\n    step: int\n\n    # Magic methods\n\n    @overload\n    def __new__(start: int, stop: int) -> range:\n        return range(start, stop, 1)\n\n    @overload\n    def __new__(stop: int) -> range:\n        return range(0, stop, 1)\n\n    @overload\n    def __new__(start: int, stop: int, step: int) -> range:\n        if step == 0:\n            raise ValueError(\"range() step argument must not be zero\")\n        return superf(start, stop, step)\n\n    def _get(self, idx: int) -> int:\n        return self.start + (idx * self.step)\n\n    @overload\n    def __getitem__(self, idx: int) -> int:\n        n = self.__len__()\n        if idx < 0:\n            idx += n\n        if idx < 0 or idx >= n:\n            raise IndexError(\"range object index out of range\")\n        return self._get(idx)\n\n    @overload\n    def __getitem__(self, s: Slice) -> range:\n        if s.start is None and s.stop is None and s.step is None:\n            return self\n        else:\n            start, stop, step, length = s.adjust_indices(self.__len__())\n            substep = self.step * step\n            substart = self._get(start)\n            substop = self._get(stop)\n            return range(substart, substop, substep)\n\n    def __contains__(self, idx: int) -> bool:\n        start, stop, step = self.start, self.stop, self.step\n        if (step > 0 and not (start <= idx < stop)) or (\n            step < 0 and not (stop < idx <= start)\n        ):\n            return False\n        return (idx - start) % step == 0\n\n    def _index(self, n: int) -> int:\n        return (n - self.start) // self.step\n\n    def index(self, n: int) -> int:\n        if n in self:\n            return self._index(n)\n        else:\n            raise ValueError(str(n) + \" is not in range\")\n\n    def count(self, n: int) -> int:\n        return int(n in self)\n\n    @overload\n    def __iter__(self) -> Generator[int]:\n        start, stop, step = self.start, self.stop, self.step\n        i = start\n        if step > 0:\n            while i < stop:\n                yield i\n                i += step\n        else:\n            while i > stop:\n                yield i\n                i += step\n\n    @overload\n    def __len__(self) -> int:\n        start, stop, step = self.start, self.stop, self.step\n        if step > 0 and start < stop:\n            return 1 + (stop - 1 - start) // step\n        elif step < 0 and start > stop:\n            return 1 + (start - 1 - stop) // (-step)\n        else:\n            return 0\n\n    def __bool__(self) -> bool:\n        return self.__len__() > 0\n\n    def __reversed__(self) -> Generator[int]:\n        start, stop, step = self.start, self.stop, self.step\n        n = self.__len__()\n        return range(start + (n - 1) * step, start - step, -step).__iter__()\n\n    @overload\n    def __repr__(self) -> str:\n        if self.step == 1:\n            return f\"range({self.start}, {self.stop})\"\n        else:\n            return f\"range({self.start}, {self.stop}, {self.step})\"\n"
  },
  {
    "path": "stdlib/internal/types/rtti.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\n\n__vtables__ = Ptr[Ptr[cobj]]()\n__vtable_size__ = 0\n\n\n@extend\nclass RTTIType:\n    def _new(data: Ptr[byte], typeinfo: Ptr[byte], T: type) -> T:\n        \"\"\"\n        Creates a new RTTIType wrapper for data and casts it to T.\n\n        Internal use only. No type checks are performed.\n        \"\"\"\n\n        p = type._ref_new(RTTIType)\n        p.data = data\n        p.typeinfo = typeinfo\n        return type._force_cast(p, T)\n\n    @inline\n    def _dist(B: type, D: type) -> int:\n        \"\"\"Calculates the byte distance of base class B and derived class D. Compiler generated.\"\"\"\n        return 0\n\n    @inline\n    def _to_derived(b: B, B: type, D: type) -> D:\n        if not (static.has_rtti(D) and static.has_rtti(B)):\n            compile_error(\"classes are not polymorphic\")\n        off = RTTIType._dist(B, D)\n        rtti = type._force_cast(b, RTTIType)\n        return RTTIType._new(rtti.data - off, rtti.typeinfo, D)\n\n\n    ### vTable & thunk setup. Mostly compiler-generated.\n\n    def _init_vtables():\n        \"\"\"\n        Create a global vtable.\n        \"\"\"\n\n        from internal.gc import alloc_atomic_uncollectable, sizeof\n\n        global __vtables__\n        sz = __vtable_size__ + 1\n        p = alloc_atomic_uncollectable(sz * sizeof(Ptr[Ptr[byte]]))\n        __vtables__ = Ptr[Ptr[Ptr[byte]]](p)\n        RTTIType._populate_vtables()\n\n    def _populate_vtables():\n        \"\"\"\n        Populate content of vtables. Compiler generated.\n        Corresponds to:\n            for each realized class C:\n                _init_vtable(<C's realization ID>, <C's vtable size> + 1, T=C)\n                for each fn F in C's vtable:\n                    _set_vtable_fn(\n                        <C's realization ID>, <F's vtable ID>, Function(<instantiated F>).__raw__(), T=C\n                    )\n        \"\"\"\n        pass\n\n    def _init_vtable(sz: int, T: type):\n        from internal.gc import alloc_atomic_uncollectable, sizeof\n\n        if not static.has_rtti(T):\n            compile_error(\"class is not polymorphic\")\n        p = alloc_atomic_uncollectable((sz + 1) * sizeof(Ptr[byte]))\n        id = T.__id__\n        __vtables__[id] = Ptr[Ptr[byte]](p)\n        # Set typeinfo\n        p = TypeInfo(T)\n        __vtables__[id][0] = p.__raw__().as_byte()\n\n    def _set_vtable_fn(id: int, fid: int, f: Ptr[byte], T: type):\n        if not static.has_rtti(T):\n            compile_error(\"class is not polymorphic\")\n        __vtables__[id][fid] = f\n\n    def _get_thunk_id(F: type, T: type) -> int:\n        \"\"\"Compiler-generated\"\"\"\n        return 0\n\n    def _thunk_debug(base, func, sig, *args):\n        # print(\"![thunk]!\", base, func, sig, args[0].__raw__())\n        pass\n\n    @no_argument_wrap\n    def _thunk_dispatch(slf, cls_id, *args, F: type):\n        if not static.has_rtti(type(slf)):\n            compile_error(\"class is not polymorphic\")\n\n        FR = type(static.function.realized(F, slf, *args))\n        T = type(slf)\n        thunk_id = RTTIType._get_thunk_id(FR, T)\n\n        # Get RTTI table\n        if cls_id == 0:\n            cls_id = type._force_cast(type._force_cast(slf, RTTIType).typeinfo, TypeInfo).id\n        fptr = __vtables__[cls_id][thunk_id]\n        f = FR(fptr)\n        return f(slf, *args)\n\n    def _get_typeinfo(obj: T, T: type) -> TypeInfo:\n        if static.has_rtti(T):\n            ti = type._force_cast(obj, RTTIType).typeinfo\n            return type._force_cast(ti, TypeInfo)\n        else:\n            compile_error(\"classes are not polymorphic\")\n\n    def _isinstance(obj, TW: type) -> bool:\n        obj_info = RTTIType._get_typeinfo(obj)\n        return obj_info.id == TW.__id__ or obj_info.is_parent(TW)\n\n\n@extend\nclass Super:\n    def __repr__(self):\n        return f'<super: {__T__} {self.__obj__}>'\n\n    def _super(obj, B: type, use_super_type: Literal[int] = 0):\n        D = type(obj)\n        if not static.has_rtti(D):  # static inheritance\n            return type._force_cast(obj, B)\n        else:\n            if not static.has_rtti(B):\n                compile_error(\"classes are not polymorphic\")\n            off = RTTIType._dist(B, D)\n            rtti = type._force_cast(obj, RTTIType)\n            res = RTTIType._new(rtti.data + off, rtti.typeinfo, B)\n            if use_super_type:\n                # This is explicit super()\n                return type._force_value_cast((res, ), Super[B])\n            else:\n                # Implicit super() just used for casting\n                return res\n\n    def _unwrap(obj: Super[B], B: type) -> B:\n        rtti = type._force_cast(obj.__obj__, RTTIType)\n        return RTTIType._new(rtti.data, TypeInfo(B).__raw__(), B)\n"
  },
  {
    "path": "stdlib/internal/types/slice.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n@tuple(init=False)\nclass Slice:\n    start: Optional[T]\n    stop: Optional[U]\n    step: Optional[V]\n    T: type\n    U: type\n    V: type\n\n    def __new__(stop: Optional[U], U: type = int) -> Slice[int, U, int]:\n        return Slice(None, stop, None)\n\n    @overload\n    def __new__(\n            start: Optional[T],\n            stop: Optional[U],\n            T: type = int,\n            U: type = int) -> Slice[T, U, int]:\n        return Slice(start, stop, None)\n\n    @overload\n    def __new__(\n            start: Optional[T],\n            stop: Optional[U],\n            step: Optional[V],\n            T: type = int,\n            U: type = int,\n            V: type = int) -> Slice[T, U, V]:\n        return type._force_value_cast((start, stop, step), Slice[T, U, V])\n\n    def adjust_indices(self, length: int) -> Tuple[int, int, int, int]:\n        if not (T is int or T is None) or not (U is int or U is None) or not (V is int or V is None):\n            compile_error(\"slice indices must be integers or None\")\n\n        step: int = self.step if self.step is not None else 1\n        start: int = 0\n        stop: int = 0\n        if step == 0:\n            raise ValueError(\"slice step cannot be zero\")\n        if step > 0:\n            start = self.start if self.start is not None else 0\n            stop = self.stop if self.stop is not None else length\n        else:\n            start = self.start if self.start is not None else length - 1\n            stop = self.stop if self.stop is not None else -(length + 1)\n\n        return Slice.adjust_indices_helper(length, start, stop, step)\n\n    def adjust_indices_helper(\n        length: int, start: int, stop: int, step: int\n    ) -> Tuple[int, int, int, int]:\n        if start < 0:\n            start += length\n            if start < 0:\n                start = -1 if step < 0 else 0\n        elif start >= length:\n            start = length - 1 if step < 0 else length\n\n        if stop < 0:\n            stop += length\n            if stop < 0:\n                stop = -1 if step < 0 else 0\n        elif stop >= length:\n            stop = length - 1 if step < 0 else length\n\n        if step < 0:\n            if stop < start:\n                return start, stop, step, (start - stop - 1) // (-step) + 1\n        else:\n            if start < stop:\n                return start, stop, step, (stop - start - 1) // step + 1\n\n        return start, stop, step, 0\n\n    def indices(self, length: int):\n        if length < 0:\n            raise ValueError(\"length should not be negative\")\n        return self.adjust_indices(length)[:-1]\n\n    @overload\n    def __repr__(self):\n        return f\"slice({self.start}, {self.stop}, {self.step})\"\n\n    @overload\n    def __eq__(self, other: Slice):\n        return self.start == other.start and self.step == other.step and self.stop == other.stop\n\n    @overload\n    def __ne__(self, other: Slice):\n        return not self.__eq__(other)\n\nslice = Slice\n"
  },
  {
    "path": "stdlib/internal/types/str.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\n@pure\n@C\ndef strlen(a: cobj) -> int:\n    pass\n\n@extend\nclass str:\n    @__internal__\n    def __new__(l: int, p: Ptr[byte]) -> str:\n        pass\n\n    @overload\n    def __new__(p: Ptr[byte], l: int) -> str:\n        return str(l, p)\n\n    @overload\n    def __new__() -> str:\n        return str(Ptr[byte](), 0)\n\n    @overload\n    def __new__(what) -> str:\n        if isinstance(what, Union):\n            return Union._str(what)\n        elif isinstance(what, type):\n            return what.__repr__()\n        elif hasattr(what, \"__str__\"):\n            return what.__str__()\n        else:\n            if not hasattr(what, \"__repr__\") and hasattr(what, \"__repr_default__\"):\n                return what.__repr_default__()\n            return what.__repr__()\n\n    def __str__(what: str) -> str:\n        return what\n\n    def __len__(self) -> int:\n        return self.len\n\n    def __bool__(self) -> bool:\n        return self.len != 0\n\n    def __copy__(self) -> str:\n        return self\n\n    def __deepcopy__(self) -> str:\n        return self\n\n    def __ptrcopy__(self) -> str:\n        n = self.len\n        p = cobj(n)\n        str.memcpy(p, self.ptr, n)\n        return str(p, n)\n\n    @llvm\n    def memcpy(dest: Ptr[byte], src: Ptr[byte], len: int) -> None:\n        declare void @llvm.memcpy.p0i8.p0i8.i64(ptr %dest, ptr %src, i64 %len, i32 %align, i1 %isvolatile)\n        call void @llvm.memcpy.p0i8.p0i8.i64(ptr %dest, ptr %src, i64 %len, i32 0, i1 false)\n        ret {} {}\n\n    @llvm\n    def memmove(dest: Ptr[byte], src: Ptr[byte], len: int) -> None:\n        declare void @llvm.memmove.p0i8.p0i8.i64(ptr %dest, ptr %src, i64 %len, i32 %align, i1 %isvolatile)\n        call void @llvm.memmove.p0i8.p0i8.i64(ptr %dest, ptr %src, i64 %len, i32 0, i1 false)\n        ret {} {}\n\n    @llvm\n    def memset(dest: Ptr[byte], val: byte, len: int) -> None:\n        declare void @llvm.memset.p0i8.i64(ptr %dest, i8 %val, i64 %len, i32 %align, i1 %isvolatile)\n        call void @llvm.memset.p0i8.i64(ptr %dest, i8 %val, i64 %len, i32 0, i1 false)\n        ret {} {}\n\n    def __add__(self, other: str) -> str:\n        len1 = self.len\n        len2 = other.len\n        len3 = len1 + len2\n        p = Ptr[byte](len3)\n        str.memcpy(p, self.ptr, len1)\n        str.memcpy(p + len1, other.ptr, len2)\n        return str(p, len3)\n\n    def c_str(self) -> cobj:\n        n = self.__len__()\n        p = cobj(n + 1)\n        str.memcpy(p, self.ptr, n)\n        p[n] = byte(0)\n        return p\n\n    def from_ptr(t: cobj) -> str:\n        n = strlen(t)\n        p = Ptr[byte](n)\n        str.memcpy(p, t, n)\n        return str(p, n)\n\n    def __eq__(self, other: str) -> bool:\n        if self.len != other.len:\n            return False\n        i = 0\n        while i < self.len:\n            if self.ptr[i] != other.ptr[i]:\n                return False\n            i += 1\n        return True\n\n    def __match__(self, obj: str) -> bool:\n        return self.__eq__(obj)\n\n    def __ne__(self, other: str) -> bool:\n        return not self.__eq__(other)\n\n    def cat(*args) -> str:\n        total = 0\n        if (\n            static.len(args) == 1\n            and hasattr(args[0], \"__iter__\")\n            and hasattr(args[0], \"__len__\")\n        ):\n            for s in args[0]:\n                if not isinstance(s, str):\n                    compile_error(\"not a string\")\n                total += s.len\n            p = cobj(total)\n            n = 0\n            for s in args[0]:\n                str.memcpy(p + n, s.ptr, s.len)\n                n += s.len\n            return str(p, total)\n        elif static.len(args) == 1 and hasattr(args[0], \"__iter__\"):\n            sz = 10\n            p = cobj(sz)\n            n = 0\n            for s in args[0]:\n                if not isinstance(s, str):\n                    compile_error(\"not a string\")\n                if n + s.len > sz:\n                    sz = 1 + 3 * (n + s.len) // 2\n                    pp = cobj(sz)\n                    str.memcpy(pp, p, n)\n                    p = pp\n                str.memcpy(p + n, s.ptr, s.len)\n                n += s.len\n            return str(p, n)\n        else:\n            for i in static.range(static.len(args)):\n                s = args[i]\n                if not isinstance(s, str):\n                    compile_error(\"not a string\")\n                total += s.len\n            p = cobj(total)\n            n = 0\n            for i in static.range(static.len(args)):\n                s = args[i]\n                str.memcpy(p + n, s.ptr, s.len)\n                n += s.len\n            return str(p, total)\n\n    def __prefix_b__(s: str, N: Literal[int]):\n        # Currently a no-op, as Codon str's are just bytes\n        return s\n"
  },
  {
    "path": "stdlib/internal/types/strbuf.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nclass strbuf:\n    data: Ptr[byte]\n    n: int\n    m: int\n\n    def __init__(self, capacity: int = 16):\n        self.data = Ptr[byte](capacity)\n        self.n = 0\n        self.m = capacity\n\n    def append(self, s: str):\n        from internal.gc import realloc\n        adding = s.__len__()\n        needed = self.n + adding\n        if needed > self.m:\n            m = self.m\n            while m < needed:\n                m *= 2\n            self.data = realloc(self.data, m, self.m)\n            self.m = m\n        str.memcpy(self.data + self.n, s.ptr, adding)\n        self.n = needed\n\n    def reverse(self):\n        a = 0\n        b = self.n - 1\n        p = self.data\n        while a < b:\n            p[a], p[b] = p[b], p[a]\n            a += 1\n            b -= 1\n\n    def __str__(self):\n        return str(self.data, self.n)\n"
  },
  {
    "path": "stdlib/internal/types/tuple.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\n@extend\nclass Tuple:\n    def _fix_index(idx: int, len: int) -> int:\n        if idx < 0:\n            idx += len\n        if idx < 0 or idx >= len:\n            raise IndexError(\"tuple index out of range\")\n        return idx\n\n    def _getitem(t: T, idx: int, T: type, E: type) -> E:\n        @pure\n        @derives\n        @llvm\n        def llvm_helper(t: T, idx: int, T: type, E: type) -> E:\n            %x = alloca {=T}\n            store {=T} %t, ptr %x\n            %p = getelementptr {=E}, ptr %x, i64 %idx\n            %v = load {=E}, ptr %p\n            ret {=E} %v\n\n        return llvm_helper(t, Tuple._fix_index(idx, static.len(t)), T, E)\n\n    def _offsetof(x, field: Literal[int]) -> int:\n        @pure\n        @llvm\n        def llvm_helper(T: type, idx: Literal[int], TE: type) -> int:\n            %a = alloca {=T}\n            %b = getelementptr inbounds {=T}, ptr %a, i64 0, i32 {=idx}\n            %base = ptrtoint ptr %a to i64\n            %elem = ptrtoint ptr %b to i64\n            %offset = sub i64 %elem, %base\n            ret i64 %offset\n\n        return llvm_helper(type(x), field, type(x[field]))\n\n    def _str(strs: Ptr[str], names: Ptr[str], n: int) -> str:\n        # special case of 1-element plain tuple: format as \"(x,)\"\n        if n == 1 and names[0].len == 0:\n            total = strs[0].len + 3\n            buf = Ptr[byte](total)\n            buf[0] = byte(40)  # '('\n            str.memcpy(buf + 1, strs[0].ptr, strs[0].len)\n            buf[total - 2] = byte(44)  # ','\n            buf[total - 1] = byte(41)  # ')'\n            return str(buf, total)\n\n        total = 2  # one for each of '(' and ')'\n        i = 0\n        while i < n:\n            total += strs[i].len\n            if names[i].len:\n                total += names[i].len + 2  # extra : and space\n            if i < n - 1:\n                total += 2  # \", \"\n            i += 1\n        buf = Ptr[byte](total)\n        where = 0\n        buf[where] = byte(40)  # '('\n        where += 1\n        i = 0\n        while i < n:\n            s = names[i]\n            l = s.len\n            if l:\n                str.memcpy(buf + where, s.ptr, l)\n                where += l\n                buf[where] = byte(58)  # ':'\n                where += 1\n                buf[where] = byte(32)  # ' '\n                where += 1\n            s = strs[i]\n            l = s.len\n            str.memcpy(buf + where, s.ptr, l)\n            where += l\n            if i < n - 1:\n                buf[where] = byte(44)  # ','\n                where += 1\n                buf[where] = byte(32)  # ' '\n                where += 1\n            i += 1\n        buf[where] = byte(41)  # ')'\n        return str(buf, total)\n\n\n@extend\nclass NamedTuple:\n    def __getitem__(self, key: Literal[str]):\n        return getattr(self, key)\n\n    def __contains__(self, key: Literal[str]):\n        return hasattr(self, key)\n\n    def _get(kw, key: Literal[str], default):\n        if hasattr(kw, key):\n            return getattr(kw, key)\n        else:\n            return default\n\n    def get(self, key: Literal[str], default = None):\n        return NamedTuple._get(self, key, default)\n\n    def _namedkeys(N: Literal[int]):\n        # Compiler generated\n        pass\n\n    def __keys__(self):\n        return NamedTuple._namedkeys(N)\n\n    def __repr__(self):\n        keys = self.__keys__()\n        values = [v.__repr__() for v in self.args]\n        s = ', '.join(f\"{keys[i]}: {values[i]}\" for i in range(len(keys)))\n        return f\"({s})\"\n"
  },
  {
    "path": "stdlib/internal/types/type.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\n\ndef __type_repr__(T: type):\n    return f\"<class '{T.__name__}'>\"\n\n\n@extend\nclass type:\n    @pure\n    @derives\n    @llvm\n    def _force_cast(p, T: type) -> T:\n        \"\"\"\n        Casts p of any reference type to the reference type T.\n\n        This method is intented for the internal typechecking usage and is completely unsafe.\n        No checks are performed; T is assumed to be a reference type.\n        Any violations will result in LLVM errors.\n        \"\"\"\n        ret ptr %p\n\n    @pure\n    @derives\n    @llvm\n    def _force_value_cast(t, U: type) -> U:\n        \"\"\"\n        Casts t of any type to U. Used for casting tuples into named tuple types.\n\n        This method is intented for the internal typechecking usage and is completely unsafe.\n        No checks are performed; t is assumed to be byte-compatible with U.\n        Any violations will result in LLVM errors.\n        \"\"\"\n        ret {=U} %t\n\n    @pure\n    @derives\n    @llvm\n    def _ref_raw(obj) -> Ptr[byte]:\n        \"\"\"\n        Casts the reference type to a pointer.\n        \"\"\"\n\n        ret ptr %obj\n\n    def _ref_new(T: type) -> T:\n        \"\"\"\n        Allocates a new reference (class) object.\n        \"\"\"\n\n        from internal.gc import alloc, alloc_atomic, sizeof, register_finalizer\n\n        sz = sizeof(tuple(T))\n        obj = alloc_atomic(sz) if T.__contents_atomic__ else alloc(sz)\n        register_finalizer(type._force_cast(obj, T))\n        if static.has_rtti(T):\n            obj = RTTIType._new(obj, TypeInfo(T).__raw__(), Ptr[byte])\n        return type._force_cast(obj, T)\n\n    def _construct(T: type, *args, **kwargs) -> T:\n        \"\"\"\n        Shorthand for `t = T.__new__(); t.__init__(*args, **kwargs); t`\n        \"\"\"\n\n        return T(*args, **kwargs)\n\n\n@__internal__\nclass TypeInfo:\n    id: int\n    _parent_ids: Ptr[int]\n    _n_parent_ids: int\n    raw_name: str\n    nice_name: str\n    repr: Function[[Ptr[byte]], str]\n\n    def __new__() -> TypeInfo:\n        return __magic__.new(TypeInfo)\n\n    def __raw__(self) -> Ptr[byte]:\n        return __magic__.raw(self)\n\n    def __init__(self, T: type):\n        if isinstance(T, TypeWrap):\n            self.__init__(T.T)\n            return\n\n        self.id = T.__id__\n        self.raw_name = T.__name__\n        self.nice_name = f\"{T}\"[8:-2]\n\n        self.repr = Function[[Ptr[byte]], str](cobj())\n        # if hasattr(T, \"__repr__\") or hasattr(T, \"__str__\"):\n        #     fn = static.function.realized(TypeInfo.wrap(T=T, ...), Ptr[byte])\n        #     if isinstance(fn.TR, str):\n        #         self.repr = fn\n\n        mro = T.__mro__\n        num_mro: Literal[int] = static.len(mro)\n        self._n_parent_ids = num_mro\n\n        if num_mro > 0:\n            self._parent_ids = Ptr[int](num_mro + 1)\n            for i in static.range(num_mro):\n                self._parent_ids[i] = mro[i].T.__id__\n            self._parent_ids[num_mro] = 0\n        else:\n            self._parent_ids = Ptr[int]()\n\n    def is_parent(self, T: type) -> bool:\n        for i in range(self._n_parent_ids):\n            if self._parent_ids[i] == T.__id__:\n                return True\n        return False\n\n    def wrap(arg: Ptr[byte], T: type) -> str:\n        if isinstance(T, ByVal):\n            p = type._force_cast(arg, Ptr[T])\n            return repr(p[0])\n        else:\n            obj = type._force_cast(arg, T)\n            return repr(obj)\n\n    def __str__(self):\n        return self.nice_name\n\n    def __repr__(self):\n        return self.__str__()\n\n    def __eq__(self, other: TypeInfo):\n        return self.id == other.id\n\n    def __ne__(self, other: TypeInfo):\n        return self.id != other.id\n\n\n@extend\nclass TypeWrap:\n    def __new__(T: type) -> TypeWrap[T]:\n        return type._force_value_cast((), TypeWrap[T])\n\n    def __call_no_self__(*args, **kwargs) -> T:\n        return T(*args, **kwargs)\n\n    def __call__(self, *args, **kwargs) -> T:\n        return T(*args, **kwargs)\n\n    def __repr__(self):\n        return __type_repr__(T)\n\n    @property\n    def __name__(self):\n        return T.__name__\n\n\nclass __cast__:\n    def cast(obj: T, T: type) -> Generator[T]:\n        return obj.__iter__()\n\n    @overload\n    def cast(obj: int) -> float:\n        return float(obj)\n\n    @overload\n    def cast(obj: T, T: type) -> Optional[T]:\n        return Optional[T](obj)\n\n    @overload\n    def cast(obj: Optional[T], T: type) -> T:\n        return obj.unwrap()\n\n    @overload\n    def cast(obj: T, T: type) -> pyobj:\n        return obj.__to_py__()\n\n    @overload\n    def cast(obj: pyobj, T: type) -> T:\n        return T.__from_py__(obj)\n\n    # Function[[T...], R]\n        # ExternFunction[[T...], R]\n        # CodonFunction[[T...], R]\n        # Partial[foo, [T...], R]\n\n    # function into partial (if not Function) / fn(foo) -> fn(foo(...))\n    # empty partial (!!) into Function[]\n    # union extract\n    # any into Union[]\n    # derived to base\n\n    def conv_float(obj: float) -> int:\n        return int(obj)\n"
  },
  {
    "path": "stdlib/internal/types/union.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\n\n@extend\nclass Union:\n    def _tag(u, tag: Literal[int]):  # compiler-generated\n        pass\n\n    @llvm\n    def _set_tag(tag: byte, U: type) -> U:\n        %0 = insertvalue {=U} undef, i8 %tag, 0\n        ret {=U} %0\n\n    @llvm\n    def _get_data_ptr(ptr: Ptr[U], U: type, T: type) -> Ptr[T]:\n        %0 = getelementptr inbounds {=U}, ptr %ptr, i64 0, i32 1\n        ret ptr %0\n\n    @llvm\n    def _get_tag(u: U, U: type) -> byte:\n        %0 = extractvalue {=U} %u, 0\n        ret i8 %0\n\n    def _get_data(u, T: type) -> T:\n        return Union._get_data_ptr(__ptr__(u), T=T)[0]\n\n    def _make(tag: int, value, U: type) -> U:\n        u = Union._set_tag(byte(tag), U)\n        Union._get_data_ptr(__ptr__(u), T=type(value))[0] = value\n        return u\n\n    def _new(value, U: type) -> U:\n        for tag, T in static.vars_types(U, with_index=True):\n            if isinstance(value, T):\n                return Union._make(tag, value, U)\n            if isinstance(value, Union[T]):\n                return Union._make(tag, Union._get(value, T), U)\n        # TODO: make this static!\n        raise TypeError(\"invalid union constructor\")\n\n    def _get(union, T: type) -> T:\n        for tag, TU in static.vars_types(union, with_index=True):\n            if isinstance(TU, T):\n                if Union._get_tag(union) == tag:\n                    return Union._get_data(union, TU)\n        raise TypeError(f\"invalid union getter for type '{T.__class__.__name__}'\")\n\n    def _member_helper(union, member: Literal[str]) -> Union:\n        for tag, T in static.vars_types(union, with_index=True):\n            if hasattr(T, member):\n                if Union._get_tag(union) == tag:\n                    return getattr(Union._get_data(union, T), member)\n        raise TypeError(f\"invalid union call '{member}'\")\n\n    def _member(union, member: Literal[str]):\n        t = Union._member_helper(union, member)\n        if static.len(t) == 1:\n            return Union._tag(t, 0)\n        else:\n            return t\n\n    def _call_helper(union, args, kwargs) -> Union:\n        for tag, T in static.vars_types(union, with_index=True):\n            if static.function.can_call(T, *args, **kwargs):\n                if Union._get_tag(union) == tag:\n                    return Union._get_data(union, T)(*args, **kwargs)\n            elif hasattr(T, '__call__'):\n                if static.function.can_call(T.__call__, *args, **kwargs):\n                    if Union._get_tag(union) == tag:\n                        return Union._get_data(union, T).__call__(*args, **kwargs)\n        raise TypeError(\"cannot call union \" + union.__class__.__name__)\n\n    def _call(union, args, kwargs):\n        t = Union._call_helper(union, args, kwargs)\n        if static.len(t) == 1:\n            return Union._tag(t, 0)\n        else:\n            return t\n\n    def __call__(self, *args, **kwargs):\n        return Union._call(self, args, kwargs)\n\n    def _str(union):\n        for tag, T in static.vars_types(union, with_index=True):\n            if hasattr(T, '__str__'):\n                if Union._get_tag(union) == tag:\n                    return Union._get_data(union, T).__str__()\n            elif hasattr(T, '__repr__'):\n                if Union._get_tag(union) == tag:\n                    return Union._get_data(union, T).__repr__()\n        return ''\n"
  },
  {
    "path": "stdlib/itertools.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom internal.types.optional import unwrap\nimport internal.static as static\n\n# Infinite iterators\n\n@inline\ndef count(start: T = 0, step: T = 1, T: type) -> Generator[T]:\n    n = start\n    while True:\n        yield n\n        n += step\n\n@inline\ndef cycle(iterable: Generator[T], T: type) -> Generator[T]:\n    saved = []\n    for element in iterable:\n        yield element\n        saved.append(element)\n    while saved:\n        for element in saved:\n            yield element\n\n@inline\ndef repeat(object: T, times: Optional[int] = None, T: type) -> Generator[T]:\n    if times is None:\n        while True:\n            yield object\n    else:\n        for i in range(times):\n            yield object\n\n# Iterators terminating on the shortest input sequence\n\n@inline\ndef accumulate(iterable: Generator[T], func=lambda a, b: a + b, initial=0, T: type):\n    total = initial\n    yield total\n    for element in iterable:\n        total = func(total, element)\n        yield total\n\n@inline\n@overload\ndef accumulate(iterable: Generator[T], func=lambda a, b: a + b, T: type):\n    total: Optional[T] = None\n    for element in iterable:\n        total = element if total is None else func(unwrap(total), element)\n        yield unwrap(total)\n\n@tuple\nclass chain:\n\n    @inline\n    def __new__(*iterables):\n        for it in iterables:\n            for element in it:\n                yield element\n\n    @inline\n    def from_iterable(iterables):\n        for it in iterables:\n            for element in it:\n                yield element\n\n@inline\ndef compress(\n    data: Generator[T], selectors: Generator[B], T: type, B: type\n) -> Generator[T]:\n    for d, s in zip(data, selectors):\n        if s:\n            yield d\n\n@inline\ndef dropwhile(\n    predicate: CallableTrait[[T], bool], iterable: Generator[T], T: type\n) -> Generator[T]:\n    b = False\n    for x in iterable:\n        if not b and not predicate(x):\n            b = True\n        if b:\n            yield x\n\n@inline\ndef filterfalse(\n    predicate: CallableTrait[[T], bool], iterable: Generator[T], T: type\n) -> Generator[T]:\n    for x in iterable:\n        if not predicate(x):\n            yield x\n\n# TODO: fix key\n@inline\ndef groupby(iterable, key=Optional[int]()):\n    currkey = None\n    group = []\n\n    for currvalue in iterable:\n        k = currvalue if isinstance(key, Optional) else key(currvalue)\n        if currkey is None:\n            currkey = k\n        if k != unwrap(currkey):\n            yield unwrap(currkey), group\n            currkey = k\n            group = []\n        group.append(currvalue)\n    if currkey is not None:\n        yield unwrap(currkey), group\n\ndef islice(iterable: Generator[T], stop: Optional[int], T: type) -> Generator[T]:\n    if stop is not None and stop.__val__() < 0:\n        raise ValueError(\n            \"Indices for islice() must be None or an integer: 0 <= x <= sys.maxsize.\"\n        )\n    i = 0\n    for x in iterable:\n        if stop is not None and i >= stop.__val__():\n            break\n        yield x\n        i += 1\n\n@overload\ndef islice(\n    iterable: Generator[T],\n    start: Optional[int],\n    stop: Optional[int],\n    step: Optional[int] = None,\n    T: type,\n) -> Generator[T]:\n    from sys import maxsize\n\n    start: int = 0 if start is None else start\n    stop: int = maxsize if stop is None else stop\n    step: int = 1 if step is None else step\n    have_stop = False\n\n    if start < 0 or stop < 0:\n        raise ValueError(\n            \"Indices for islice() must be None or an integer: 0 <= x <= sys.maxsize.\"\n        )\n    elif step < 0:\n        raise ValueError(\"Step for islice() must be a positive integer or None.\")\n\n    it = range(start, stop, step)\n    N = len(it)\n    idx = 0\n    b = -1\n\n    if N == 0:\n        for i, element in zip(range(start), iterable):\n            pass\n        return\n\n    nexti = it[0]\n    for i, element in enumerate(iterable):\n        if i == nexti:\n            yield element\n            idx += 1\n            if idx >= N:\n                b = i\n                break\n            nexti = it[idx]\n\n    if b >= 0:\n        for i, element in zip(range(b + 1, stop), iterable):\n            pass\n\n@inline\ndef starmap(function, iterable):\n    for args in iterable:\n        yield function(*args)\n\n@inline\ndef takewhile(\n    predicate: CallableTrait[[T], bool], iterable: Generator[T], T: type\n) -> Generator[T]:\n    for x in iterable:\n        if predicate(x):\n            yield x\n        else:\n            break\n\ndef tee(iterable: Generator[T], n: int = 2, T: type) -> List[Generator[T]]:\n    from collections import deque\n\n    it = iter(iterable)\n    deques = [deque[T]() for i in range(n)]\n\n    def gen(mydeque: deque[T], T: type) -> Generator[T]:\n        while True:\n            if not mydeque:  # when the local deque is empty\n                if it.__done__():\n                    return\n                it.__resume__()\n                if it.__done__():\n                    return\n                newval = it.__next__()\n                for d in deques:  # load it to all the deques\n                    d.append(newval)\n            yield mydeque.popleft()\n\n    return [gen(d) for d in deques]\n\n@inline\ndef zip_longest(*iterables, fillvalue):\n    if static.len(iterables) == 2:\n        a = iter(iterables[0])\n        b = iter(iterables[1])\n        a_done = False\n        b_done = False\n\n        while not a.done():\n            a_val = a.__next__()\n            b_val = fillvalue\n            if not b_done:\n                b_done = b.done()\n            if not b_done:\n                b_val = b.__next__()\n            yield a_val, b_val\n\n        if not b_done:\n            while not b.done():\n                yield fillvalue, b.__next__()\n\n        a.destroy()\n        b.destroy()\n    else:\n        iterators = tuple(iter(it) for it in iterables)\n        num_active = len(iterators)\n        if not num_active:\n            return\n        while True:\n            values = []\n            for it in iterators:\n                if it.__done__():  # already done\n                    values.append(fillvalue)\n                elif it.done():  # resume and check\n                    num_active -= 1\n                    if not num_active:\n                        return\n                    values.append(fillvalue)\n                else:\n                    values.append(it.__next__())\n            yield values\n\n@inline\n@overload\ndef zip_longest(*args):\n\n    def get_next(it):\n        if it.__done__() or it.done():\n            return None\n        return it.__next__()\n\n    iters = tuple(iter(arg) for arg in args)\n    while True:\n        done_count = 0\n        result = tuple(get_next(it) for it in iters)\n        all_none = True\n        for a in result:\n            if a is not None:\n                all_none = False\n        if all_none:\n            return\n        yield result\n    for it in iters:\n        it.destroy()\n\n# Combinatoric iterators\n\ndef product(*iterables, repeat: int):\n    if repeat < 0:\n        raise ValueError(\"repeat must be non-negative\")\n\n    if repeat == 0:\n        nargs = 0\n    else:\n        nargs = len(iterables)\n\n    npools = nargs * repeat\n    indices = Ptr[int](npools)\n\n    pools = list(capacity=npools)\n    i = 0\n\n    while i < nargs:\n        p = List.as_list(iterables[i])\n        if len(p) == 0:\n            return\n        pools.append(p)\n        indices[i] = 0\n        i += 1\n\n    while i < npools:\n        pools.append(pools[i - nargs])\n        indices[i] = 0\n        i += 1\n\n    result = list(capacity=npools)\n    for i in range(npools):\n        result.append(pools[i][0])\n\n    while True:\n        yield result\n\n        result = result.copy()\n        i = npools - 1\n        while i >= 0:\n            pool = pools[i]\n            indices[i] += 1\n\n            if indices[i] == len(pool):\n                indices[i] = 0\n                result[i] = pool[0]\n            else:\n                result[i] = pool[indices[i]]\n                break\n\n            i -= 1\n\n        if i < 0:\n            break\n\n@overload\ndef product(*iterables, repeat: Literal[int] = 1):\n    if repeat < 0:\n        compile_error(\"repeat must be non-negative\")\n\n    # handle some common cases\n    if repeat == 0:\n        yield ()\n    elif repeat == 1 and static.len(iterables) == 1:\n        it0 = iterables[0]\n        for a in it0:\n            yield (a,)\n    elif repeat == 1 and static.len(iterables) == 2:\n        it0 = iterables[0]\n        it1 = iterables[1]\n        for a in it0:\n            for b in it1:\n                yield (a, b)\n    elif repeat == 1 and static.len(iterables) == 3:\n        it0 = iterables[0]\n        it1 = iterables[1]\n        it2 = iterables[2]\n        for a in it0:\n            for b in it1:\n                for c in it2:\n                    yield (a, b, c)\n    else:\n        nargs: Literal[int] = static.len(iterables)\n        npools: Literal[int] = nargs * repeat\n        indices_tuple = (0,) * npools\n        indices = Ptr[int](__ptr__(indices_tuple).as_byte())\n        pools = tuple(List.as_list(it) for it in iterables) * repeat\n\n        for i in static.range(nargs):\n            if len(pools[i]) == 0:\n                return\n\n        result = tuple(pool[0] for pool in pools)\n\n        while True:\n            yield result\n\n            i = npools - 1\n            while i >= 0:\n                pool = pools[i]\n                indices[i] += 1\n\n                if indices[i] == len(pool):\n                    indices[i] = 0\n                else:\n                    break\n\n                i -= 1\n\n            if i < 0:\n                break\n\n            result = tuple(pools[i][indices[i]] for i in static.range(npools))\n\ndef combinations(pool, r: int):\n    if r < 0:\n        raise ValueError(\"r must be non-negative\")\n\n    pool_list = List.as_list(pool)\n    n = len(pool)\n\n    if r > n:\n        return\n\n    pool = pool_list.arr.ptr\n    indices = Ptr[int](r)\n    result = list(capacity=r)\n\n    for i in range(r):\n        indices[i] = i\n        result.append(pool[i])\n\n    while True:\n        yield result\n\n        i = r - 1\n        while i >= 0 and indices[i] == i + n - r:\n            i -= 1\n\n        if i < 0:\n            break\n\n        indices[i] += 1\n\n        for j in range(i + 1, r):\n            indices[j] = indices[j-1] + 1\n\n        result = result.copy()\n        while i < r:\n            result[i] = pool[indices[i]]\n            i += 1\n\n@overload\ndef combinations(pool, r: Literal[int]):\n    def empty(T: type) -> T:\n        pass\n\n    if r < 0:\n        compile_error(\"r must be non-negative\")\n\n    if isinstance(pool, list):\n        pool_list = pool\n    else:\n        pool_list = list(pool)\n\n    n = len(pool)\n\n    if r > n:\n        return\n\n    pool = pool_list.arr.ptr\n    indices_tuple = (0,) * r\n    indices = Ptr[int](__ptr__(indices_tuple).as_byte())\n    result_tuple = (empty(pool.T),) * r\n    result = Ptr[pool.T](__ptr__(result_tuple).as_byte())\n\n    for i in range(r):\n        indices[i] = i\n        result[i] = pool[i]\n\n    while True:\n        yield result_tuple\n\n        i = r - 1\n        while i >= 0 and indices[i] == i + n - r:\n            i -= 1\n\n        if i < 0:\n            break\n\n        indices[i] += 1\n\n        for j in range(i + 1, r):\n            indices[j] = indices[j-1] + 1\n\n        while i < r:\n            result[i] = pool[indices[i]]\n            i += 1\n\ndef combinations_with_replacement(pool, r: int):\n    if r < 0:\n        raise ValueError(\"r must be non-negative\")\n\n    pool_list = List.as_list(pool)\n    n = len(pool)\n\n    if n == 0:\n        if r == 0:\n            yield List[pool_list.T](capacity=0)\n        return\n\n    pool = pool_list.arr.ptr\n    indices = Ptr[int](r)\n    result = list(capacity=r)\n\n    for i in range(r):\n        indices[i] = 0\n        result.append(pool[0])\n\n    while True:\n        yield result\n\n        i = r - 1\n        while i >= 0 and indices[i] == n - 1:\n            i -= 1\n\n        if i < 0:\n            break\n\n        result = result.copy()\n        index = indices[i] + 1\n        elem = pool[index]\n\n        while i < r:\n            indices[i] = index\n            result[i] = elem\n            i += 1\n\n@overload\ndef combinations_with_replacement(pool, r: Literal[int]):\n    def empty(T: type) -> T:\n        pass\n\n    if r < 0:\n        compile_error(\"r must be non-negative\")\n\n    if r == 0:\n        yield ()\n        return\n\n    if isinstance(pool, list):\n        pool_list = pool\n    else:\n        pool_list = list(pool)\n\n    n = len(pool)\n\n    if n == 0:\n        return\n\n    pool = pool_list.arr.ptr\n    indices_tuple = (0,) * r\n    indices = Ptr[int](__ptr__(indices_tuple).as_byte())\n    result_tuple = (empty(pool.T),) * r\n    result = Ptr[pool.T](__ptr__(result_tuple).as_byte())\n\n    for i in range(r):\n        result[i] = pool[0]\n\n    while True:\n        yield result_tuple\n\n        i = r - 1\n        while i >= 0 and indices[i] == n - 1:\n            i -= 1\n\n        if i < 0:\n            break\n\n        index = indices[i] + 1\n        elem = pool[index]\n\n        while i < r:\n            indices[i] = index\n            result[i] = elem\n            i += 1\n\ndef _permutations_non_static(pool, r = None):\n    pool_list = List.as_list(pool)\n    n = len(pool)\n\n    if r is None:\n        yield from _permutations_non_static(pool_list, n)\n        return\n    elif not isinstance(r, int):\n        compile_error(\"Expected int as r\")\n\n    if r < 0:\n        raise ValueError(\"r must be non-negative\")\n\n    if r > n:\n        return\n\n    indices = Ptr[int](n)\n    cycles = Ptr[int](r)\n\n    for i in range(n):\n        indices[i] = i\n\n    for i in range(r):\n        cycles[i] = n - i\n\n    pool = pool_list.arr.ptr\n    result = list(capacity=r)\n\n    for i in range(r):\n        result.append(pool[i])\n\n    while True:\n        yield result\n\n        if n == 0:\n            break\n\n        result = result.copy()\n        i = r - 1\n        while i >= 0:\n            cycles[i] -= 1\n            if cycles[i] == 0:\n                index = indices[i]\n                for j in range(i, n - 1):\n                    indices[j] = indices[j+1]\n                indices[n-1] = index\n                cycles[i] = n - i\n            else:\n                j = cycles[i]\n                index = indices[i]\n                indices[i] = indices[n - j]\n                indices[n - j] = index\n\n                for k in range(i, r):\n                    index = indices[k]\n                    result[k] = pool[index]\n\n                break\n            i -= 1\n\n        if i < 0:\n            break\n\ndef _permutations_static(pool, r: Literal[int]):\n    def empty(T: type) -> T:\n        pass\n\n    pool_list = List.as_list(pool)\n    n = len(pool)\n\n    if r < 0:\n        raise compile_error(\"r must be non-negative\")\n\n    if r > n:\n        return\n\n    indices = Ptr[int](n)\n    cycles_tuple = (0,) * r\n    cycles = Ptr[int](__ptr__(cycles_tuple).as_byte())\n\n    for i in range(n):\n        indices[i] = i\n\n    for i in range(r):\n        cycles[i] = n - i\n\n    pool = pool_list.arr.ptr\n    result_tuple = (empty(pool.T),) * r\n    result = Ptr[pool.T](__ptr__(result_tuple).as_byte())\n\n    for i in range(r):\n        result[i] = pool[i]\n\n    while True:\n        yield result_tuple\n\n        if n == 0:\n            break\n\n        i = r - 1\n        while i >= 0:\n            cycles[i] -= 1\n            if cycles[i] == 0:\n                index = indices[i]\n                for j in range(i, n - 1):\n                    indices[j] = indices[j+1]\n                indices[n-1] = index\n                cycles[i] = n - i\n            else:\n                j = cycles[i]\n                index = indices[i]\n                indices[i] = indices[n - j]\n                indices[n - j] = index\n\n                for k in range(i, r):\n                    index = indices[k]\n                    result[k] = pool[index]\n\n                break\n            i -= 1\n\n        if i < 0:\n            break\n\ndef permutations(pool, r = None) -> Generator:\n    if isinstance(pool, Tuple) and r is None:\n        return _permutations_static(pool, static.len(pool))\n    else:\n        return _permutations_non_static(pool, r)\n\n@overload\ndef permutations(pool, r: Literal[int]) -> Generator:\n    return _permutations_static(pool, r)\n"
  },
  {
    "path": "stdlib/math.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\n@pure\n@llvm\ndef _inf() -> float:\n    ret double 0x7FF0000000000000\n\n@pure\n@llvm\ndef _nan() -> float:\n    ret double 0x7FF8000000000000\n\ne = 2.7182818284590452354\npi = 3.14159265358979323846\ntau = 6.28318530717958647693\ninf = _inf()\nnan = _nan()\n\ndef factorial(x: int) -> int:\n    _F = (\n        1,\n        1,\n        2,\n        6,\n        24,\n        120,\n        720,\n        5040,\n        40320,\n        362880,\n        3628800,\n        39916800,\n        479001600,\n        6227020800,\n        87178291200,\n        1307674368000,\n        20922789888000,\n        355687428096000,\n        6402373705728000,\n        121645100408832000,\n        2432902008176640000,\n    )\n    if not (0 <= x <= 20):\n        raise ValueError(\"factorial is only supported for 0 <= x <= 20\")\n    return _F[x]\n\ndef isnan(x: float) -> bool:\n    @pure\n    @llvm\n    def f(x: float) -> bool:\n        %y = fcmp uno double %x, 0.000000e+00\n        %z = zext i1 %y to i8\n        ret i8 %z\n\n    return f(x)\n\ndef isinf(x: float) -> bool:\n    @pure\n    @llvm\n    def f(x: float) -> bool:\n        declare double @llvm.fabs.f64(double)\n        %a = call double @llvm.fabs.f64(double %x)\n        %b = fcmp oeq double %a, 0x7FF0000000000000\n        %c = zext i1 %b to i8\n        ret i8 %c\n\n    return f(x)\n\ndef isfinite(x: float) -> bool:\n    return not (isnan(x) or isinf(x))\n\ndef _check1(arg: float, r: float, can_overflow: bool = False):\n    if __py_numerics__:\n        if isnan(r) and not isnan(arg):\n            raise ValueError(\"math domain error\")\n\n        if isinf(r) and isfinite(arg):\n            if can_overflow:\n                raise OverflowError(\"math range error\")\n            else:\n                raise ValueError(\"math domain error\")\n\n    return r\n\ndef _check2(x: float, y: float, r: float, can_overflow: bool = False):\n    if __py_numerics__:\n        if isnan(r) and not isnan(x) and not isnan(y):\n            raise ValueError(\"math domain error\")\n\n        if isinf(r) and isfinite(x) and isfinite(y):\n            if can_overflow:\n                raise OverflowError(\"math range error\")\n            else:\n                raise ValueError(\"math domain error\")\n\n    return r\n\ndef ceil(x: float) -> int:\n    @pure\n    @llvm\n    def f(x: float) -> float:\n        declare double @llvm.ceil.f64(double)\n        %y = call double @llvm.ceil.f64(double %x)\n        ret double %y\n\n    return int(f(x))\n\ndef floor(x: float) -> int:\n    @pure\n    @llvm\n    def f(x: float) -> float:\n        declare double @llvm.floor.f64(double)\n        %y = call double @llvm.floor.f64(double %x)\n        ret double %y\n\n    return int(f(x))\n\ndef fabs(x: float) -> float:\n    @pure\n    @llvm\n    def f(x: float) -> float:\n        declare double @llvm.fabs.f64(double)\n        %y = call double @llvm.fabs.f64(double %x)\n        ret double %y\n\n    return f(x)\n\ndef fmod(x: float, y: float) -> float:\n    @pure\n    @llvm\n    def f(x: float, y: float) -> float:\n        %z = frem double %x, %y\n        ret double %z\n\n    return f(x, y)\n\ndef exp(x: float) -> float:\n    @pure\n    @llvm\n    def f(x: float) -> float:\n        declare double @llvm.exp.f64(double)\n        %y = call double @llvm.exp.f64(double %x)\n        ret double %y\n\n    return _check1(x, f(x), True)\n\ndef expm1(x: float) -> float:\n    return _check1(x, _C.expm1(x), True)\n\ndef ldexp(x: float, i: int) -> float:\n    return _check1(x, _C.ldexp(x, i32(i)), True)\n\ndef log(x: float, base: float = e) -> float:\n    @pure\n    @llvm\n    def f(x: float) -> float:\n        declare double @llvm.log.f64(double)\n        %y = call double @llvm.log.f64(double %x)\n        ret double %y\n\n    if base == e:\n        return _check1(x, f(x))\n    else:\n        return _check1(x, f(x)) / _check1(base, f(base))\n\ndef log2(x: float) -> float:\n    @pure\n    @llvm\n    def f(x: float) -> float:\n        declare double @llvm.log2.f64(double)\n        %y = call double @llvm.log2.f64(double %x)\n        ret double %y\n\n    return _check1(x, f(x))\n\ndef log10(x: float) -> float:\n    @pure\n    @llvm\n    def f(x: float) -> float:\n        declare double @llvm.log10.f64(double)\n        %y = call double @llvm.log10.f64(double %x)\n        ret double %y\n\n    return _check1(x, f(x))\n\ndef degrees(x: float) -> float:\n    radToDeg = 180.0 / pi\n    return x * radToDeg\n\ndef radians(x: float) -> float:\n    degToRad = pi / 180.0\n    return x * degToRad\n\ndef sqrt(x: float) -> float:\n    @pure\n    @llvm\n    def f(x: float) -> float:\n        declare double @llvm.sqrt.f64(double)\n        %y = call double @llvm.sqrt.f64(double %x)\n        ret double %y\n\n    return _check1(x, f(x))\n\ndef pow(x: float, y: float) -> float:\n    @pure\n    @llvm\n    def f(x: float, y: float) -> float:\n        declare double @llvm.pow.f64(double, double)\n        %z = call double @llvm.pow.f64(double %x, double %y)\n        ret double %z\n\n    return _check2(x, y, f(x, y), True)\n\ndef acos(x: float) -> float:\n    return _check1(x, _C.acos(x))\n\ndef asin(x: float) -> float:\n    return _check1(x, _C.asin(x))\n\ndef atan(x: float) -> float:\n    return _check1(x, _C.atan(x))\n\ndef atan2(y: float, x: float) -> float:\n    return _check2(x, y, _C.atan2(y, x))\n\ndef cos(x: float) -> float:\n    @pure\n    @llvm\n    def f(x: float) -> float:\n        declare double @llvm.cos.f64(double)\n        %y = call double @llvm.cos.f64(double %x)\n        ret double %y\n\n    return _check1(x, f(x))\n\ndef sin(x: float) -> float:\n    @pure\n    @llvm\n    def f(x: float) -> float:\n        declare double @llvm.sin.f64(double)\n        %y = call double @llvm.sin.f64(double %x)\n        ret double %y\n\n    return _check1(x, f(x))\n\ndef hypot(x: float, y: float) -> float:\n    return _check2(x, y, _C.hypot(x, y), True)\n\ndef tan(x: float) -> float:\n    return _check1(x, _C.tan(x))\n\ndef cosh(x: float) -> float:\n    return _check1(x, _C.cosh(x), True)\n\ndef sinh(x: float) -> float:\n    return _check1(x, _C.sinh(x), True)\n\ndef tanh(x: float) -> float:\n    return _check1(x, _C.tanh(x))\n\ndef acosh(x: float) -> float:\n    return _check1(x, _C.acosh(x))\n\ndef asinh(x: float) -> float:\n    return _check1(x, _C.asinh(x))\n\ndef atanh(x: float) -> float:\n    return _check1(x, _C.atanh(x))\n\ndef copysign(x: float, y: float) -> float:\n    @pure\n    @llvm\n    def f(x: float, y: float) -> float:\n        declare double @llvm.copysign.f64(double, double)\n        %z = call double @llvm.copysign.f64(double %x, double %y)\n        ret double %z\n\n    return _check2(x, y, f(x, y))\n\ndef log1p(x: float) -> float:\n    return _check1(x, _C.log1p(x))\n\ndef trunc(x: float) -> int:\n    @pure\n    @llvm\n    def f(x: float) -> float:\n        declare double @llvm.trunc.f64(double)\n        %y = call double @llvm.trunc.f64(double %x)\n        ret double %y\n\n    return int(_check1(x, f(x)))\n\ndef erf(x: float) -> float:\n    return _check1(x, _C.erf(x))\n\ndef erfc(x: float) -> float:\n    return _check1(x, _C.erfc(x))\n\ndef gamma(x: float) -> float:\n    return _check1(x, _C.tgamma(x), True)\n\ndef lgamma(x: float) -> float:\n    return _check1(x, _C.lgamma(x), True)\n\ndef remainder(x: float, y: float) -> float:\n    return _check2(x, y, _C.remainder(x, y))\n\ndef _gcd2(a, b):\n    a = abs(a)\n    b = abs(b)\n    while a:\n        a, b = b % a, a\n    return b\n\ndef gcd(*args):\n    if static.len(args) == 0:\n        return 0\n\n    res = args[0]\n    T = type(res)\n\n    if static.len(args) == 1:\n        return abs(res)\n\n    for i in range(1, len(args)):\n        if res == T(1):\n            break\n        res = _gcd2(res, args[i])\n\n    return res\n\ndef lcm(*args):\n    def lcm2(a, b):\n        return abs((a // _gcd2(a, b)) * b)\n\n    if static.len(args) == 0:\n        return 1\n\n    res = args[0]\n    T = type(res)\n\n    if static.len(args) == 1:\n        return abs(res)\n\n    for i in range(1, len(args)):\n        if not res:\n            break\n        res = lcm2(res, args[i])\n\n    return res\n\n@pure\ndef frexp(x: float) -> Tuple[float, int]:\n    tmp = i32(0)\n    res = _C.frexp(float(x), __ptr__(tmp))\n    return (res, int(tmp))\n\n@pure\ndef modf(x: float) -> Tuple[float, float]:\n    tmp = 0.0\n    res = _C.modf(float(x), __ptr__(tmp))\n    return (res, tmp)\n\ndef isclose(a: float, b: float, rel_tol: float = 1e-09, abs_tol: float = 0.0) -> bool:\n    # short circuit exact equality -- needed to catch two\n    # infinities of the same sign. And perhaps speeds things\n    # up a bit sometimes.\n    if a == b:\n        return True\n\n    # This catches the case of two infinities of opposite sign, or\n    # one infinity and one finite number. Two infinities of opposite\n    # sign would otherwise have an infinite relative tolerance.\n    # Two infinities of the same sign are caught by the equality check\n    # above.\n    if a == inf or b == inf:\n        return False\n\n    # NAN is not close to anything, not even itself\n    if a == nan or b == nan:\n        return False\n\n    # regular computation\n    diff = fabs(b - a)\n\n    return ((diff <= fabs(rel_tol * b)) or (diff <= fabs(rel_tol * a))) or (\n        diff <= abs_tol\n    )\n\ndef fsum(seq):\n    def _fsum_realloc(p: Ptr[float], ps: Ptr[float], n: int, m: int):\n        from internal.gc import realloc, sizeof\n        v = Ptr[float]()\n        m += m\n        if n < m:\n            if p == ps:\n                v = Ptr[float](m)\n                str.memcpy(v.as_byte(), ps.as_byte(), n * sizeof(float))\n            else:\n                v = Ptr[float](realloc(p.as_byte(), m * sizeof(float), n * sizeof(float)))\n        return v, m\n\n    _NUM_PARTIALS: Literal[int] = 32\n    ps_arr = __array__[float](_NUM_PARTIALS)\n    ps = ps_arr.ptr\n    p = ps\n    n, m = 0, _NUM_PARTIALS\n    xsave, special_sum, inf_sum = 0.0, 0.0, 0.0\n    hi, yr, lo = 0.0, 0.0, 0.0\n\n    for item in seq:\n        x = float(item)\n        xsave = x\n        i = 0\n\n        for j in range(n):  # for y in partials\n            y = p[j]\n            if fabs(x) < fabs(y):\n                x, y = y, x\n            hi = x + y\n            yr = hi - x\n            lo = y - yr\n            if lo != 0.0:\n                p[i] = lo\n                i += 1\n            x = hi\n\n        n = i\n        if x != 0.0:\n            if not isfinite(x):\n                # a nonfinite x could arise either as\n                # a result of intermediate overflow, or\n                if isfinite(xsave):\n                    raise OverflowError(\"intermediate overflow in fsum\")\n                if isinf(xsave):\n                    inf_sum += xsave\n                special_sum += xsave\n                # reset partials\n                n = 0\n            else:\n                if n >= m:\n                    p, m = _fsum_realloc(p, ps, n, m)\n                p[n] = x\n                n += 1\n\n    if special_sum != 0.0:\n        if isnan(inf_sum):\n            raise ValueError(\"-inf + inf in fsum\")\n        else:\n            return special_sum\n\n    hi = 0.0\n    if n > 0:\n        hi = p[n - 1]\n        n -= 1\n        # sum_exact(ps, hi) from the top, stop when the sum becomes inexact\n        while n > 0:\n            x = hi\n            y = p[n - 1]\n            n -= 1\n            # assert fabs(y) < fabs(x)\n            hi = x + y\n            yr = hi - x\n            lo = y - yr\n            if lo != 0.0:\n                break\n\n        # Make half-even rounding work across multiple partials.\n        # Needed so that sum([1e-16, 1, 1e16]) will round-up the last\n        # digit to two instead of down to zero (the 1e-16 makes the 1\n        # slightly closer to two).  With a potential 1 ULP rounding\n        # error fixed-up, math.fsum() can guarantee commutativity.\n        if n > 0 and ((lo < 0.0 and p[n-1] < 0.0) or (lo > 0.0 and p[n-1] > 0.0)):\n            y = lo * 2.0\n            x = hi + y\n            yr = x - hi\n            if y == yr:\n                hi = x\n\n    return hi\n\ndef prod(iterable, start = 1):\n    def prod_generator(iterable: Generator[T], start, T: type):\n        if T is float:\n            result = float(start)\n        else:\n            result = start\n\n        for a in iterable:\n            result *= a\n        return result\n\n    def prod_tuple(iterable, start):\n        if static.len(iterable) == 0:\n            return start\n        else:\n            return prod(iterable[1:], start=(start * iterable[0]))\n\n    if isinstance(iterable, Tuple):\n        return prod_tuple(iterable, start)\n    else:\n        return prod_generator(iterable, start)\n\n# 32-bit float ops\n\ne32 = float32(e)\npi32 = float32(pi)\ntau32 = float32(tau)\n\ninf32 = float32(inf)\nnan32 = float32(nan)\n\n@overload\ndef isnan(x: float32) -> bool:\n    \"\"\"\n    `float32` version of `math.isnan()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32) -> bool:\n        %y = fcmp uno float %x, 0.000000e+00\n        %z = zext i1 %y to i8\n        ret i8 %z\n\n    return f(x)\n\n@overload\ndef isinf(x: float32) -> bool:\n    \"\"\"\n    `float32` version of `math.isinf()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32) -> bool:\n        declare float @llvm.fabs.f32(float)\n        %a = call float @llvm.fabs.f32(float %x)\n        %b = fcmp oeq float %a, 0x7FF0000000000000\n        %c = zext i1 %b to i8\n        ret i8 %c\n\n    return f(x)\n\n@overload\ndef isfinite(x: float32) -> bool:\n    \"\"\"\n    `float32` version of `math.isfinite()`\n    \"\"\"\n    return not (isnan(x) or isinf(x))\n\n@overload\ndef ceil(x: float32) -> int:\n    \"\"\"\n    `float32` version of `math.ceil()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32) -> float32:\n        declare float @llvm.ceil.f32(float)\n        %y = call float @llvm.ceil.f32(float %x)\n        ret float %y\n\n    return int(f(x))\n\n@overload\ndef floor(x: float32) -> int:\n    \"\"\"\n    `float32` version of `math.floor()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32) -> float32:\n        declare float @llvm.floor.f32(float)\n        %y = call float @llvm.floor.f32(float %x)\n        ret float %y\n\n    return int(f(x))\n\n@overload\ndef fabs(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.fabs()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32) -> float32:\n        declare float @llvm.fabs.f32(float)\n        %y = call float @llvm.fabs.f32(float %x)\n        ret float %y\n\n    return f(x)\n\n@overload\ndef fmod(x: float32, y: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.fmod()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32, y: float32) -> float32:\n        %z = frem float %x, %y\n        ret float %z\n\n    return f(x, y)\n\n@overload\ndef exp(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.exp()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32) -> float32:\n        declare float @llvm.exp.f32(float)\n        %y = call float @llvm.exp.f32(float %x)\n        ret float %y\n\n    return f(x)\n\n@overload\ndef expm1(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.expm1()`\n    \"\"\"\n    return _C.expm1f(x)\n\n@overload\ndef ldexp(x: float32, i: int) -> float32:\n    \"\"\"\n    `float32` version of `math.ldexp()`\n    \"\"\"\n    return _C.ldexpf(x, i32(i))\n\n@overload\ndef log(x: float32, base: float32 = e32) -> float32:\n    \"\"\"\n    `float32` version of `math.log()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32) -> float32:\n        declare float @llvm.log.f32(float)\n        %y = call float @llvm.log.f32(float %x)\n        ret float %y\n\n    if base == e32:\n        return f(x)\n    else:\n        return f(x) / f(base)\n\n@overload\ndef log2(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.log2()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32) -> float32:\n        declare float @llvm.log2.f32(float)\n        %y = call float @llvm.log2.f32(float %x)\n        ret float %y\n\n    return f(x)\n\n@overload\ndef log10(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.log10()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32) -> float32:\n        declare float @llvm.log10.f32(float)\n        %y = call float @llvm.log10.f32(float %x)\n        ret float %y\n\n    return f(x)\n\n@overload\ndef degrees(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.degrees()`\n    \"\"\"\n    radToDeg = float32(180.0) / pi32\n    return x * radToDeg\n\n@overload\ndef radians(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.radians()`\n    \"\"\"\n    degToRad = pi32 / float32(180.0)\n    return x * degToRad\n\n@overload\ndef sqrt(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.sqrt()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32) -> float32:\n        declare float @llvm.sqrt.f32(float)\n        %y = call float @llvm.sqrt.f32(float %x)\n        ret float %y\n\n    return f(x)\n\n@overload\ndef pow(x: float32, y: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.pow()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32, y: float32) -> float32:\n        declare float @llvm.pow.f32(float, float)\n        %z = call float @llvm.pow.f32(float %x, float %y)\n        ret float %z\n\n    return f(x, y)\n\n@overload\ndef acos(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.acos()`\n    \"\"\"\n    return _C.acosf(x)\n\n@overload\ndef asin(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.asin()`\n    \"\"\"\n    return _C.asinf(x)\n\n@overload\ndef atan(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.atan()`\n    \"\"\"\n    return _C.atanf(x)\n\n@overload\ndef atan2(y: float32, x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.atan2()`\n    \"\"\"\n    return _C.atan2f(y, x)\n\n@overload\ndef cos(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.cos()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32) -> float32:\n        declare float @llvm.cos.f32(float)\n        %y = call float @llvm.cos.f32(float %x)\n        ret float %y\n\n    return f(x)\n\n@overload\ndef sin(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.sin()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32) -> float32:\n        declare float @llvm.sin.f32(float)\n        %y = call float @llvm.sin.f32(float %x)\n        ret float %y\n\n    return f(x)\n\n@overload\ndef hypot(x: float32, y: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.hypot()`\n    \"\"\"\n    return _C.hypotf(x, y)\n\n@overload\ndef tan(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.tan()`\n    \"\"\"\n    return _C.tanf(x)\n\n@overload\ndef cosh(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.cosh()`\n    \"\"\"\n    return _C.coshf(x)\n\n@overload\ndef sinh(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.sinh()`\n    \"\"\"\n    return _C.sinhf(x)\n\n@overload\ndef tanh(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.tanh()`\n    \"\"\"\n    return _C.tanhf(x)\n\n@overload\ndef acosh(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.acosh()`\n    \"\"\"\n    return _C.acoshf(x)\n\n@overload\ndef asinh(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.asinh()`\n    \"\"\"\n    return _C.asinhf(x)\n\n@overload\ndef atanh(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.atanh()`\n    \"\"\"\n    return _C.atanhf(x)\n\n@overload\ndef copysign(x: float32, y: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.copysign()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32, y: float32) -> float32:\n        declare float @llvm.copysign.f32(float, float)\n        %z = call float @llvm.copysign.f32(float %x, float %y)\n        ret float %z\n\n    return f(x, y)\n\n@overload\ndef log1p(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.log1p()`\n    \"\"\"\n    return _C.log1pf(x)\n\n@overload\ndef trunc(x: float32) -> int:\n    \"\"\"\n    `float32` version of `math.trunc()`\n    \"\"\"\n    @pure\n    @llvm\n    def f(x: float32) -> float32:\n        declare float @llvm.trunc.f32(float)\n        %y = call float @llvm.trunc.f32(float %x)\n        ret float %y\n\n    return int(f(x))\n\n@overload\ndef erf(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.erf()`\n    \"\"\"\n    return _C.erff(x)\n\n@overload\ndef erfc(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.erfc()`\n    \"\"\"\n    return _C.erfcf(x)\n\n@overload\ndef gamma(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.gamma()`\n    \"\"\"\n    return _C.tgammaf(x)\n\n@overload\ndef lgamma(x: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.lgamma()`\n    \"\"\"\n    return _C.lgammaf(x)\n\n@overload\ndef remainder(x: float32, y: float32) -> float32:\n    \"\"\"\n    `float32` version of `math.remainder()`\n    \"\"\"\n    return _C.remainderf(x, y)\n\n@overload\n@pure\ndef frexp(x: float32) -> Tuple[float32, int]:\n    \"\"\"\n    `float32` version of `math.frexp()`\n    \"\"\"\n    tmp = i32(0)\n    res = _C.frexpf(float32(x), __ptr__(tmp))\n    return (res, int(tmp))\n\n@overload\n@pure\ndef modf(x: float32) -> Tuple[float32, float32]:\n    \"\"\"\n    `float32` version of `math.modf()`\n    \"\"\"\n    tmp = float32(0.0)\n    res = _C.modff(float32(x), __ptr__(tmp))\n    return (res, tmp)\n\n@overload\ndef isclose(a: float32, b: float32, rel_tol: float32 = float32(1e-09), abs_tol: float32 = float32(0.0)) -> bool:\n    \"\"\"\n    `float32` version of `math.isclose()`\n    \"\"\"\n    # short circuit exact equality -- needed to catch two\n    # infinities of the same sign. And perhaps speeds things\n    # up a bit sometimes.\n    if a == b:\n        return True\n\n    # This catches the case of two infinities of opposite sign, or\n    # one infinity and one finite number. Two infinities of opposite\n    # sign would otherwise have an infinite relative tolerance.\n    # Two infinities of the same sign are caught by the equality check\n    # above.\n    if a == inf32 or b == inf32:\n        return False\n\n    # NAN is not close to anything, not even itself\n    if a == nan32 or b == nan32:\n        return False\n\n    # regular computation\n    diff = fabs(b - a)\n\n    return ((diff <= fabs(rel_tol * b)) or (diff <= fabs(rel_tol * a))) or (\n        diff <= abs_tol\n    )\n"
  },
  {
    "path": "stdlib/numpy/__init__.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .dragon4 import format_float_positional, format_float_scientific\nfrom .npdatetime import *\nfrom .dtype import *\nfrom .ndarray import *\nfrom .indexing import *\nfrom .routines import *\nfrom .operators import *\nfrom .functional import *\nfrom .ndmath import *\nfrom .interp import *\nfrom .linalg_sym import *\nfrom .const import *\nfrom .window import *\nfrom .sorting import *\nfrom .ndgpu import *\nfrom .format import *\nfrom .misc import *\nfrom .lib.arraysetops import *\nfrom .statistics import *\nfrom .npio import *\nfrom .reductions import *\nfrom .util import AxisError, TooHardError\n\nimport linalg\nimport fft\nimport fusion\n"
  },
  {
    "path": "stdlib/numpy/const.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n@pure\n@llvm\ndef _inf() -> float:\n    ret double 0x7FF0000000000000\n\n@pure\n@llvm\ndef _ninf() -> float:\n    ret double 0xFFF0000000000000\n\n@pure\n@llvm\ndef _nan() -> float:\n    ret double 0x7FF8000000000000\n\n@pure\n@llvm\ndef _pzero() -> float:\n    ret double 0.000000e+00\n\n@pure\n@llvm\ndef _nzero() -> float:\n    ret double -0.000000e+00\n\n@pure\n@llvm\ndef _pi() -> float:\n    ret double 0x400921FB54442D18\n\n@pure\n@llvm\ndef _e() -> float:\n    ret double 0x4005BF0A8B145769\n\ninf = _inf()\ninfty = _inf()\nInf = _inf()\nInfinity = _inf()\nPINF = _inf()\nNINF = _ninf()\nPZERO = _pzero()\nNZERO = _nzero()\nnan = _nan()\nNaN = _nan()\nNAN = _nan()\npi = _pi()\ne = _e()\neuler_gamma = 0.577215664901532860606512090082402431\nnewaxis = None\n"
  },
  {
    "path": "stdlib/numpy/dragon4.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport util\nimport internal.static as static\n\n_DIGIT_MODE_UNIQUE: Literal[int] = 0\n_DIGIT_MODE_EXACT: Literal[int] = 1\n\n_CUTOFF_MODE_TOTAL_LENGTH: Literal[int] = 0\n_CUTOFF_MODE_FRACTION_LENGTH: Literal[int] = 1\n\n_TRIM_MODE_NONE: Literal[int] = 0\n_TRIM_MODE_LEAVE_ONE_ZERO: Literal[int] = 1\n_TRIM_MODE_ZEROS: Literal[int] = 2\n_TRIM_MODE_DPT_ZEROS: Literal[int] = 3\n\ndef _bitmask_u64(n):\n    return ~(~u64(0) << u64(n))\n\ndef _bitmask_u32(n):\n    return ~(~u32(0) << u32(n))\n\n@pure\n@llvm\ndef _log_tab(idx: u32) -> u32:\n    @data = private unnamed_addr constant [256 x i8] c\"\\00\\00\\01\\01\\02\\02\\02\\02\\03\\03\\03\\03\\03\\03\\03\\03\\04\\04\\04\\04\\04\\04\\04\\04\\04\\04\\04\\04\\04\\04\\04\\04\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\05\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\06\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\\07\"\n    %i = zext i32 %idx to i64\n    %p = getelementptr inbounds [256 x i8], ptr @data, i64 0, i64 %i\n    %x = load i8, ptr %p, align 1\n    %y = zext i8 %x to i32\n    ret i32 %y\n\n@pure\n@llvm\ndef _pow10_tab(idx: u32) -> u32:\n    @data = private unnamed_addr constant [8 x i32] [i32 1, i32 10, i32 100, i32 1000, i32 10000, i32 100000, i32 1000000, i32 10000000], align 16\n    %i = zext i32 %idx to i64\n    %p = getelementptr inbounds [8 x i32], ptr @data, i64 0, i64 %i\n    %x = load i32, ptr %p, align 4\n    ret i32 %x\n\n@pure\n@llvm\ndef _pow10_8_blocks() -> Ptr[u32]:\n    @data = private unnamed_addr constant [1 x i32] [i32 100000000], align 4\n    ret ptr @data\n\n@pure\n@llvm\ndef _pow10_16_blocks() -> Ptr[u32]:\n    @data = private unnamed_addr constant [2 x i32] [i32 1874919424, i32 2328306], align 4\n    ret ptr @data\n\n@pure\n@llvm\ndef _pow10_32_blocks() -> Ptr[u32]:\n    @data = private unnamed_addr constant [4 x i32] [i32 0, i32 -2052264063, i32 762134875, i32 1262], align 16\n    ret ptr @data\n\n@pure\n@llvm\ndef _pow10_64_blocks() -> Ptr[u32]:\n    @data = private unnamed_addr constant [7 x i32] [i32 0, i32 0, i32 -1083564287, i32 1849224548, i32 -626550803, i32 -381683212, i32 1593091], align 16\n    ret ptr @data\n\n@pure\n@llvm\ndef _pow10_128_blocks() -> Ptr[u32]:\n    @data = private unnamed_addr constant [14 x i32] [i32 0, i32 0, i32 0, i32 0, i32 781532673, i32 64985353, i32 253049085, i32 594863151, i32 -741345812, i32 -1006314488, i32 -1127370534, i32 -1506574567, i32 -383834621, i32 590], align 16\n    ret ptr @data\n\n@pure\n@llvm\ndef _pow10_256_blocks() -> Ptr[u32]:\n    @data = private unnamed_addr constant [27 x i32] [i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 -1741784063, i32 -1093433509, i32 -656826510, i32 303378311, i32 1809731782, i32 -817205648, i32 -711600113, i32 649228654, i32 -1379506512, i32 487929380, i32 1011012442, i32 1677677582, i32 -866815040, i32 1710878487, i32 1438394610, i32 -2133014537, i32 -194056740, i32 1608314830, i32 349175], align 16\n    ret ptr @data\n\n@pure\n@llvm\ndef _pow10_512_blocks() -> Ptr[u32]:\n    @data = private unnamed_addr constant [54 x i32] [i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 -59967487, i32 2012377703, i32 -1886042404, i32 1570150255, i32 -1204122985, i32 -1021437223, i32 1187251475, i32 -1796843705, i32 -930515263, i32 1148564857, i32 687371067, i32 -1440898625, i32 1883165473, i32 505794538, i32 -1306906846, i32 -1135477970, i32 -1763618979, i32 -1079775828, i32 849106862, i32 -402886317, i32 -1006893419, i32 -2052515548, i32 -111189154, i32 -1299149088, i32 -1817465372, i32 325481258, i32 -1807124644, i32 1774082830, i32 1933815724, i32 -1332102015, i32 1168579910, i32 -1570138296, i32 -1934593277, i32 -1978982637, i32 -1934914921, i32 -1043187495, i32 1664357844, i32 28], align 16\n    ret ptr @data\n\n@pure\n@llvm\ndef _pow10_1024_blocks() -> Ptr[u32]:\n    @data = private unnamed_addr constant [107 x i32] [i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 689565697, i32 -178574478, i32 1853628763, i32 516071302, i32 -1726198137, i32 365238920, i32 336250165, i32 1283268122, i32 -869476327, i32 248595470, i32 -1989790482, i32 2111925499, i32 507770399, i32 -1613855875, i32 589114268, i32 591287751, i32 1708941527, i32 -196009589, i32 475844916, i32 -916235898, i32 -1842627681, i32 -1477929935, i32 -1616958969, i32 1656645978, i32 -1911536956, i32 73103988, i32 448667107, i32 -1965546843, i32 -1170947055, i32 -669731579, i32 -1086333261, i32 -1882908138, i32 -1313302852, i32 -177344788, i32 838560765, i32 -1225497269, i32 270153238, i32 1802868219, i32 -602257410, i32 -2133229431, i32 -2135054939, i32 -1709168510, i32 837488486, i32 -57729136, i32 -1754647792, i32 -496338050, i32 -546818422, i32 1021550776, i32 -1908251954, i32 1973637538, i32 1823520457, i32 1146713475, i32 833971519, i32 -1017715830, i32 905620390, i32 26278816, i32 -1614484142, i32 -2000926437, i32 373297482, i32 5996609, i32 -185392290, i32 512575049, i32 917036550, i32 1942311753, i32 -1478050518, i32 -1046046964, i32 1192784020, i32 -757380625, i32 -1838399653, i32 -1369306668, i32 759380297, i32 888447942, i32 -735027820, i32 -640280059, i32 805], align 16\n    ret ptr @data\n\n@pure\n@llvm\ndef _pow10_2048_blocks() -> Ptr[u32]:\n    @data = private unnamed_addr constant [213 x i32] [i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 322166785, i32 -485922715, i32 -1300411073, i32 1239584207, i32 -332511455, i32 -293084332, i32 -1241090684, i32 915114683, i32 -1511677551, i32 785739093, i32 -41781389, i32 -363802302, i32 1370983858, i32 -1741411170, i32 -934225220, i32 -2039556367, i32 422849554, i32 -1837545081, i32 -755471934, i32 1720790602, i32 1908931983, i32 1470596141, i32 592794347, i32 -75502132, i32 -209314592, i32 941661409, i32 -1760316343, i32 885063988, i32 -1939057442, i32 -1482151780, i32 767256131, i32 -473209613, i32 -2139816191, i32 -477548823, i32 281116564, i32 -1460572270, i32 -1473765674, i32 -1770341453, i32 1511330880, i32 -1722614803, i32 330571332, i32 -1343878717, i32 -1564695530, i32 -250510817, i32 -82680652, i32 -1850029708, i32 -691546453, i32 -1907818699, i32 1142537539, i32 -995731867, i32 1751012624, i32 861228086, i32 -1421244777, i32 230498814, i32 1023297821, i32 -1741839258, i32 -873837401, i32 -1643049861, i32 2042981258, i32 1606787143, i32 -2066215378, i32 447345732, i32 1930371132, i32 1784132011, i32 -682428506, i32 -2019042206, i32 -1807399425, i32 1080427616, i32 2009179183, i32 -911460515, i32 -395913233, i32 1950782960, i32 -2126345083, i32 -1577292906, i32 -678331269, i32 2079341593, i32 1530129217, i32 1461057425, i32 -1888702881, i32 -620295939, i32 -1322931058, i32 2019354295, i32 1455849819, i32 1866918619, i32 1324269294, i32 424891864, i32 -1572544964, i32 -1653372480, i32 1400249021, i32 -812003303, i32 -560020917, i32 225889849, i32 1891545473, i32 777383150, i32 -705142663, i32 -177365685, i32 -74938629, i32 334453379, i32 1083130821, i32 1060342180, i32 -86804157, i32 1489826908, i32 -131205050, i32 1096580926, i32 689301528, i32 -1958912780, i32 1782865703, i32 -119818886, i32 -896597904, i32 -1965554708, i32 -1293386700, i32 59740741, i32 -1092777364, i32 -943071520, i32 246185302, i32 718535188, i32 -522319808, i32 -143300740, i32 -239269163, i32 -1833033186, i32 -2013651015, i32 -828570460, i32 -758943831, i32 1064267812, i32 -1339510942, i32 -1871161874, i32 -667006506, i32 1325057500, i32 -418047317, i32 2009959531, i32 175455101, i32 184092852, i32 -1936181725, i32 -451989465, i32 -1809701007, i32 487121622, i32 -135714586, i32 -219259738, i32 459389244, i32 300652075, i32 -1773620708, i32 -835990623, i32 888631636, i32 2076098096, i32 -450452711, i32 -1931269716, i32 -565545774, i32 -1243851819, i32 649395], align 16\n    ret ptr @data\n\n@pure\n@llvm\ndef _pow10_4096_blocks() -> Ptr[u32]:\n    @data = private unnamed_addr constant [426 x i32] [i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 0, i32 711442433, i32 -730706291, i32 -1895925017, i32 -124117360, i32 -284671721, i32 1423987028, i32 330414929, i32 1349249065, i32 -81153678, i32 -442935474, i32 -254123706, i32 -2140401965, i32 -1200953922, i32 1159028371, i32 -1067901758, i32 2115927092, i32 2085102554, i32 488590542, i32 -1685347864, i32 -692068491, i32 -482230768, i32 -1025528200, i32 23816114, i32 253984538, i32 1035905997, i32 -1351998092, i32 -894179625, i32 338562688, i32 1637191975, i32 740509713, i32 -2030004479, i32 -884213374, i32 -132735868, i32 -2012926068, i32 1759373012, i32 -1139599519, i32 -16054011, i32 1420532801, i32 1981002276, i32 438054990, i32 1006507643, i32 1142697287, i32 1332538012, i32 2029019521, i32 -345661512, i32 818392641, i32 -1803678450, i32 -1578382633, i32 -646081194, i32 556814413, i32 444795339, i32 -223554297, i32 1066321706, i32 -41797830, i32 -1784134980, i32 672091442, i32 -211711296, i32 -2128982268, i32 1841538484, i32 -745113061, i32 364431512, i32 -587319153, i32 1162785440, i32 -2026325751, i32 281340310, i32 735693841, i32 848809228, i32 1700785200, i32 -1375263311, i32 -200732952, i32 58530286, i32 965505005, i32 1000010347, i32 -913005488, i32 -1254877373, i32 1973852082, i32 -1403995711, i32 1019960210, i32 -2072059, i32 -1473079455, i32 -538291646, i32 -343684389, i32 -409096713, i32 1008791145, i32 503998487, i32 1881258362, i32 1949332730, i32 392996726, i32 2012973814, i32 -324953109, i32 -1833242146, i32 -1352419566, i32 -566900597, i32 -1528066164, i32 -516434455, i32 1085564064, i32 -2016293400, i32 1116879805, i32 -846241025, i32 774279411, i32 157211670, i32 1506320155, i32 531168605, i32 1362654525, i32 956967721, i32 -2146095336, i32 769186085, i32 -108734402, i32 2055679604, i32 -1046601809, i32 -313699283, i32 -319179312, i32 -1805456779, i32 -985920801, i32 212771124, i32 933418041, i32 -923128182, i32 562115198, i32 1853601831, i32 757336096, i32 1354633440, i32 1486083256, i32 -1422840903, i32 522920738, i32 1141587749, i32 -1084064034, i32 1926940553, i32 -1240942443, i32 2021162538, i32 -2032225296, i32 1877899947, i32 -1147964428, i32 669840763, i32 -136792706, i32 -56464737, i32 1023731922, i32 -908127285, i32 829588074, i32 -845247108, i32 -1459824416, i32 -1295805289, i32 813056473, i32 482949569, i32 638108879, i32 -1227765825, i32 1026714238, i32 -290514458, i32 -1911299489, i32 -295489493, i32 771648919, i32 630660440, i32 -467845948, i32 176185980, i32 -1416776294, i32 -1628817464, i32 -385156233, i32 -1865803313, i32 -1629276884, i32 907266128, i32 -25635198, i32 2022665808, i32 1527122180, i32 -1222913628, i32 1072477492, i32 -1288944372, i32 549664855, i32 -1494626342, i32 37352654, i32 1212772743, i32 -1583686763, i32 -1265439350, i32 -1783847256, i32 1305308377, i32 -820305072, i32 -68636374, i32 442988428, i32 954940108, i32 -1020419197, i32 -82679119, i32 -1606467416, i32 -312740538, i32 -372357340, i32 1279948029, i32 1939943640, i32 -644477395, i32 -1561602367, i32 -1800704021, i32 1864579964, i32 1225941120, i32 -1904502157, i32 1267503249, i32 -761726567, i32 904410805, i32 -1452417281, i32 -1777231055, i32 1796069820, i32 -959692915, i32 673539835, i32 1924694759, i32 -696869061, i32 -1502333891, i32 16535707, i32 -591431799, i32 -702125505, i32 -1365884419, i32 1317622811, i32 294990855, i32 1396706563, i32 -1911695526, i32 -441109691, i32 277813677, i32 277580220, i32 1101318484, i32 -532993181, i32 1132150143, i32 -1750274674, i32 -875141520, i32 743770306, i32 1695464553, i32 1548693232, i32 -1873807681, i32 -1719295265, i32 -1615995490, i32 1591267897, i32 626546738, i32 -471524167, i32 267710932, i32 1455435162, i32 -1940981756, i32 -1046443501, i32 335348168, i32 -422414735, i32 -1480444684, i32 -1660848436, i32 -791200270, i32 1301019273, i32 1414467789, i32 722985138, i32 -1224057731, i32 -41484727, i32 -550027455, i32 558142907, i32 -2065147907, i32 13833173, i32 77003966, i32 -1531295932, i32 -389363326, i32 -1362977170, i32 -2014547912, i32 1879090457, i32 -1360121029, i32 -10034132, i32 -1963103451, i32 62191163, i32 -1116106276, i32 1522063815, i32 785672270, i32 1215568492, i32 -1358523379, i32 802972489, i32 -1338147123, i32 -378234513, i32 -1401395207, i32 1391232801, i32 -1126326966, i32 -1898107648, i32 894950918, i32 1103583736, i32 961991865, i32 -1487664654, i32 305977505, i32 -1240461397, i32 1048256994, i32 781017659, i32 -1835688542, i32 -1130143881, i32 537658277, i32 905753687, i32 464963300, i32 -145835736, i32 1029507924, i32 -2016666335, i32 1231291503, i32 414073408, i32 -664227211, i32 -1949125482, i32 475358196, i32 -1036723979, i32 -127342224, i32 -116056065, i32 -1367612254, i32 655438830, i32 -1156589278, i32 623200562, i32 -1509253184, i32 273403236, i32 807993669, i32 98], align 16\n    ret ptr @data\n\ndef _pow10_blocks(i: u32):\n    if i == u32(0):\n        return (1, _pow10_8_blocks())\n    elif i == u32(1):\n        return (2, _pow10_16_blocks())\n    elif i == u32(2):\n        return (4, _pow10_32_blocks())\n    elif i == u32(3):\n        return (7, _pow10_64_blocks())\n    elif i == u32(4):\n        return (14, _pow10_128_blocks())\n    elif i == u32(5):\n        return (27, _pow10_256_blocks())\n    elif i == u32(6):\n        return (54, _pow10_512_blocks())\n    elif i == u32(7):\n        return (107, _pow10_1024_blocks())\n    elif i == u32(8):\n        return (213, _pow10_2048_blocks())\n    elif i == u32(9):\n        return (426, _pow10_4096_blocks())\n    else:\n        return (0, Ptr[u32]())\n\ndef _log_base2_32(val: u32):\n    temp = val >> u32(24)\n    if temp:\n        return u32(24) + _log_tab(temp)\n\n    temp = val >> u32(16)\n    if temp:\n        return u32(16) + _log_tab(temp)\n\n    temp = val >> u32(8)\n    if temp:\n        return u32(8) + _log_tab(temp)\n\n    return _log_tab(val)\n\ndef _log_base2_64(val: u64):\n    temp = val >> u64(32)\n    if temp:\n        return u64(32) + util.zext(_log_base2_32(util.itrunc(temp, u32)), u64)\n\n    return util.zext(_log_base2_32(util.itrunc(val, u32)), u64)\n\ndef _log_base2_128(hi: u64, lo: u64):\n    if hi:\n        return u64(64) + _log_base2_64(hi)\n\n    return _log_base2_64(lo)\n\n_BIGINT_MAX_BLOCKS: Literal[int] = 1023\n_BIGINT_DRAGON4_GROUPSIZE: Literal[int] = 7\n\nclass BigInt:\n    length: int\n    blocks: Ptr[u32]\n\n    def __init__(self, length: int, blocks: Ptr[u32]):\n        self.length = length\n        self.blocks = blocks\n\n    def __init__(self):\n        self.__init__(0, Ptr[u32](_BIGINT_MAX_BLOCKS))\n\n    def __str__(self):\n        return f'BigInt: len={len(self)} blocks=[{\", \".join(str(self[i]) for i in range(len(self)))}]'\n\n    def make_pow10(i: u32):\n        return BigInt(*_pow10_blocks(i))\n\n    def copy(self, dst: BigInt):\n        length = len(self)\n        srcp = self.blocks\n        dstp = dst.blocks\n        while srcp != self.blocks + length:\n            dstp[0] = srcp[0]\n            dstp += 1\n            srcp += 1\n        dst.length = length\n\n    def __getitem__(self, idx: int):\n        return self.blocks[idx]\n\n    def __setitem__(self, idx: int, val: u32):\n        self.blocks[idx] = val\n\n    def __setitem__(self, idx: int, val: u64):\n        self.blocks[idx] = util.itrunc(val, u32)\n\n    def __len__(self):\n        return self.length\n\n    def __bool__(self):\n        return self.length != 0\n\n    def set(self, val: u64):\n        if val > _bitmask_u64(32):\n            self[0] = val & _bitmask_u64(32)\n            self[1] = (val >> u64(32)) & _bitmask_u64(32)\n            self.length = 2\n        elif val:\n            self[0] = val & _bitmask_u64(32)\n            self.length = 1\n        else:\n            self.length = 0\n\n    def set(self, hi: u64, lo: u64):\n        if hi > _bitmask_u64(32):\n            self.length = 4\n        elif hi:\n            self.length = 3\n        elif lo > _bitmask_u64(32):\n            self.length = 2\n        elif lo:\n            self.length = 1\n        else:\n            self.length = 0\n\n        n = self.length\n\n        if n >= 4:\n            self[3] = (hi >> u64(32)) & _bitmask_u64(32)\n        if n >= 3:\n            self[2] = hi & _bitmask_u64(32)\n        if n >= 2:\n            self[1] = (lo >> u64(32)) & _bitmask_u64(32)\n        if n >= 1:\n            self[0] = lo & _bitmask_u64(32)\n\n    def set(self, val: u32):\n        if val:\n            self[0] = val\n            self.length = 1\n        else:\n            self.length = 0\n\n    @property\n    def even(self):\n        return len(self) == 0 or (self[0] % u32(2) == u32(0))\n\n    def compare(self, rhs: BigInt):\n        lhs = self\n        ldiff = len(lhs) - len(rhs)\n        if ldiff != 0:\n            return ldiff\n\n        i = len(lhs) - 1\n        while i >= 0:\n            if lhs[i] == rhs[i]:\n                i -= 1\n            elif lhs[i] > rhs[i]:\n                return 1\n            else:\n                return -1\n        return 0\n\n    def __eq__(self, other: BigInt):\n        return self.compare(other) == 0\n\n    def __ne__(self, other: BigInt):\n        return self.compare(other) != 0\n\n    def __lt__(self, other: BigInt):\n        return self.compare(other) < 0\n\n    def __le__(self, other: BigInt):\n        return self.compare(other) <= 0\n\n    def __gt__(self, other: BigInt):\n        return self.compare(other) > 0\n\n    def __ge__(self, other: BigInt):\n        return self.compare(other) >= 0\n\n    def add(self, lhs: BigInt, rhs: BigInt):\n        result = self\n        if len(lhs) < len(rhs):\n            small = lhs\n            large = rhs\n        else:\n            small = rhs\n            large = lhs\n\n        result.length = len(large)\n\n        large_cur = large.blocks\n        large_end = large_cur + len(large)\n        small_cur = small.blocks\n        small_end = small_cur + len(small)\n        result_cur = result.blocks\n        carry = u64(0)\n\n        while small_cur != small_end:\n            s = carry + util.zext(large_cur[0], u64) + util.zext(small_cur[0], u64)\n            carry = s >> u64(32)\n            result_cur[0] = util.itrunc(s & _bitmask_u64(32), u32)\n            large_cur += 1\n            small_cur += 1\n            result_cur += 1\n\n        while large_cur != large_end:\n            s = carry + util.zext(large_cur[0], u64)\n            carry = s >> u64(32)\n            result_cur[0] = util.itrunc(s & _bitmask_u64(32), u32)\n            large_cur += 1\n            result_cur += 1\n\n        if carry:\n            result_cur[0] = u32(1)\n            result.length = len(large) + 1\n        else:\n            result.length = len(large)\n\n    def mul(self, lhs: BigInt, rhs: BigInt):\n        result = self\n        if len(lhs) < len(rhs):\n            small = lhs\n            large = rhs\n        else:\n            small = rhs\n            large = lhs\n\n        max_result_len = len(large) + len(small)\n        cur = result.blocks\n        end = cur + max_result_len\n        while cur != end:\n            cur[0] = u32(0)\n            cur += 1\n\n        result_start = result.blocks\n        small_cur = small.blocks\n\n        while small_cur != small.blocks + len(small):\n            multiplier = small_cur[0]\n            if multiplier:\n                large_cur = large.blocks\n                result_cur = result_start\n                carry = u64(0)\n                while True:\n                    product = (util.zext(result_cur[0], u64) +\n                               (util.zext(large_cur[0], u64) *\n                                util.zext(multiplier, u64)) + carry)\n                    carry = product >> u64(32)\n                    result_cur[0] = util.itrunc(product & _bitmask_u64(32), u32)\n                    large_cur += 1\n                    result_cur += 1\n\n                    if large_cur == large.blocks + len(large):\n                        break\n\n                result_cur[0] = util.itrunc(carry & _bitmask_u64(32), u32)\n\n            small_cur += 1\n            result_start += 1\n\n        if max_result_len > 0 and not result[max_result_len - 1]:\n            result.length = max_result_len - 1\n        else:\n            result.length = max_result_len\n\n    def mul(self, lhs: BigInt, rhs: u32):\n        result = self\n        carry = u32(0)\n        result_cur = result.blocks\n        p_lhs_cur = lhs.blocks\n        p_lhs_end = lhs.blocks + len(lhs)\n\n        while p_lhs_cur != p_lhs_end:\n            product = (util.zext(p_lhs_cur[0], u64) *\n                        util.zext(rhs, u64) +\n                         util.zext(carry, u64))\n            result_cur[0] = util.itrunc(product & _bitmask_u64(32), u32)\n            carry = util.itrunc(product >> u64(32), u32)\n            p_lhs_cur += 1\n            result_cur += 1\n\n        if carry:\n            result_cur[0] = carry\n            result.length = len(lhs) + 1\n        else:\n            result.length = len(lhs)\n\n    def mul2(self, in_: BigInt):\n        result = self\n        carry = u32(0)\n        result_cur = result.blocks\n        p_lhs_cur = in_.blocks\n        p_lhs_end = in_.blocks + len(in_)\n\n        while p_lhs_cur != p_lhs_end:\n            cur = p_lhs_cur[0]\n            result_cur[0] = (cur << u32(1)) | carry\n            carry = cur >> u32(31)\n            p_lhs_cur += 1\n            result_cur += 1\n\n        if carry:\n            result_cur[0] = carry\n            result.length = len(in_) + 1\n        else:\n            result.length = len(in_)\n\n    def mul2(self):\n        result = self\n        carry = u32(0)\n        cur = result.blocks\n        end = result.blocks + len(result)\n\n        while cur != end:\n            tmpcur = cur[0]\n            cur[0] = (tmpcur << u32(1)) | carry\n            carry = tmpcur >> u32(31)\n            cur += 1\n\n        if carry:\n            cur[0] = carry\n            result.length = len(result) + 1\n\n    def mul10(self):\n        result = self\n        carry = u64(0)\n        cur = result.blocks\n        end = result.blocks + len(result)\n\n        while cur != end:\n            product = util.zext(cur[0], u64) * u64(10) + carry\n            cur[0] = util.itrunc(product & _bitmask_u64(32), u32)\n            carry = product >> u64(32)\n            cur += 1\n\n        if carry:\n            cur[0] = util.itrunc(carry, u32)\n            result.length = len(result) + 1\n\n    def pow10(self, exponent: u32, temp: BigInt):\n        result = self\n        cur_temp = result\n        p_next_temp = temp\n        table_idx = u32(0)\n\n        small_exponent = exponent & _bitmask_u32(3)\n        cur_temp.set(_pow10_tab(small_exponent))\n        exponent >>= u32(3)\n\n        while exponent:\n            if exponent & u32(1):\n                p_next_temp.mul(cur_temp, BigInt.make_pow10(table_idx))\n                p_swap = cur_temp\n                cur_temp = p_next_temp\n                p_next_temp = p_swap\n\n            table_idx += u32(1)\n            exponent >>= u32(1)\n\n        if cur_temp is not result:\n            cur_temp.copy(result)\n\n    def mul_pow10(self, exponent: u32, temp: BigInt):\n        in_ = self\n        table_idx = u32(0)\n\n        small_exponent = exponent & _bitmask_u32(3)\n        if small_exponent:\n            temp.mul(in_, _pow10_tab(small_exponent))\n            cur_temp = temp\n            p_next_temp = in_\n        else:\n            cur_temp = in_\n            p_next_temp = temp\n\n        exponent >>= u32(3)\n\n        while exponent:\n            if exponent & u32(1):\n                p_next_temp.mul(cur_temp, BigInt.make_pow10(table_idx))\n                p_swap = cur_temp\n                cur_temp = p_next_temp\n                p_next_temp = p_swap\n\n            table_idx += u32(1)\n            exponent >>= u32(1)\n\n        if cur_temp is not in_:\n            cur_temp.copy(in_)\n\n    def pow2(self, exponent: u32):\n        result = self\n        block_idx = int(exponent) >> 5\n\n        for i in range(block_idx + 1):\n            result[i] = u32(0)\n        result.length = block_idx + 1\n\n        bit_idx = int(exponent) & (32 - 1)\n        self[block_idx] |= (u32(1) << u32(bit_idx))\n\n    def div_with_rem_maxq9(self, divisor: BigInt):\n        dividend = self\n        length = len(divisor)\n        if len(dividend) < len(divisor):\n            return u32(0)\n\n        final_divisor_block = divisor.blocks + (length - 1)\n        final_dividend_block = dividend.blocks + (length - 1)\n        quotient = final_dividend_block[0] // (final_divisor_block[0] + u32(1))\n\n        if quotient:\n            divisor_cur = divisor.blocks\n            dividend_cur = dividend.blocks\n            borrow = u64(0)\n            carry = u64(0)\n\n            while True:\n                product = util.zext(divisor_cur[0], u64) * util.zext(quotient, u64) + carry\n                carry = product >> u64(32)\n\n                difference = util.zext(dividend_cur[0], u64) - (product & _bitmask_u64(32)) - borrow\n                borrow = (difference >> u64(32)) & u64(1)\n\n                dividend_cur[0] = util.itrunc(difference & _bitmask_u64(32), u32)\n                divisor_cur += 1\n                dividend_cur += 1\n\n                if divisor_cur > final_divisor_block:\n                    break\n\n            while length > 0 and dividend[length - 1] == u32(0):\n                length -= 1\n\n            dividend.length = length\n\n        if dividend >= divisor:\n            divisor_cur = divisor.blocks\n            dividend_cur = dividend.blocks\n            borrow = u64(0)\n\n            quotient += u32(1)\n\n            while True:\n                difference = util.zext(dividend_cur[0], u64) - util.zext(divisor_cur[0], u64) - borrow\n                borrow = (difference >> u64(32)) & u64(1)\n\n                dividend_cur[0] = util.itrunc(difference & _bitmask_u64(32), u32)\n                divisor_cur += 1\n                dividend_cur += 1\n\n                if divisor_cur > final_divisor_block:\n                    break\n\n            while length > 0 and dividend[length - 1] == u32(0):\n                length -= 1\n\n            dividend.length = length\n\n        return quotient\n\n    def lshift(self, shift: u32):\n        result = self\n        shift_blocks = int(shift // u32(32))\n        shift_bits = int(shift % u32(32))\n\n        p_in_blocks = result.blocks\n        in_length = len(result)\n\n        if not shift_bits:\n            p_in_cur = result.blocks + len(result)\n            p_out_cur = p_in_cur + shift_blocks\n            while p_in_cur >= p_in_blocks:\n                p_out_cur[0] = p_in_cur[0]\n                p_in_cur -= 1\n                p_out_cur -= 1\n\n            for i in range(shift_blocks):\n                result[i] = u32(0)\n\n            result.length += shift_blocks\n        else:\n            in_block_idx = in_length - 1\n            out_block_idx = in_length + shift_blocks\n\n            low_bits_shift = u32(32 - shift_bits)\n            high_bits = u32(0)\n            block = result[in_block_idx]\n            low_bits = block >> low_bits_shift\n\n            result.length = out_block_idx + 1\n\n            while in_block_idx > 0:\n                result[out_block_idx] = high_bits | low_bits\n                high_bits = block << u32(shift_bits)\n                in_block_idx -= 1\n                out_block_idx -= 1\n                block = result[in_block_idx]\n                low_bits = block >> low_bits_shift\n\n            result[out_block_idx] = high_bits | low_bits\n            result[out_block_idx - 1] = block << u32(shift_bits)\n\n            for i in range(shift_blocks):\n                result[i] = u32(0)\n\n            if result[len(result) - 1] == u32(0):\n                result.length -= 1\n\ndef dragon4(bigints,\n            exponent: i32,\n            mantissa_bit: u32,\n            has_unequal_margins: bool,\n            digit_mode: int,\n            cutoff_mode: int,\n            cutoff_max: int,\n            cutoff_min: int,\n            out_buffer: cobj,\n            buffer_size: int,\n            out_exponent: Ptr[i32]):\n\n    cur_digit = out_buffer\n    mantissa = bigints[0]\n    scale = bigints[1]\n    scaled_value = bigints[2]\n    scaled_margin_low = bigints[3]\n    optional_margin_high = bigints[4]\n    temp1 = bigints[5]\n    temp2 = bigints[6]\n\n    log10_2 = 0.30102999566398119521373889472449\n    is_even = mantissa.even\n\n    if not mantissa:\n        cur_digit[0] = byte(48)\n        out_exponent[0] = i32(0)\n        return 1\n\n    mantissa.copy(scaled_value)\n    exponent_u32 = util.bitcast(exponent, u32)\n\n    if has_unequal_margins:\n        if exponent > i32(0):\n            scaled_value.lshift(exponent_u32 + u32(2))\n            scale.set(u32(4))\n            scaled_margin_low.pow2(exponent_u32)\n            optional_margin_high.pow2(exponent_u32 + u32(1))\n        else:\n            scaled_value.lshift(u32(2))\n            scale.pow2(util.bitcast(-exponent + i32(2), u32))\n            scaled_margin_low.set(u32(1))\n            optional_margin_high.set(u32(2))\n\n        scaled_margin_high = optional_margin_high\n    else:\n        if exponent > i32(0):\n            scaled_value.lshift(exponent_u32 + u32(1))\n            scale.set(u32(2))\n            scaled_margin_low.pow2(exponent_u32)\n        else:\n            scaled_value.lshift(u32(1))\n            scale.pow2(util.bitcast(-exponent + i32(1), u32))\n            scaled_margin_low.set(u32(1))\n\n        scaled_margin_high = scaled_margin_low\n\n    digit_exponent = util.fptosi(util.ceil64(util.sitofp(util.bitcast(mantissa_bit, i32) + exponent, float) * log10_2 - 0.69), int)\n\n    if cutoff_max >= 0 and cutoff_mode == _CUTOFF_MODE_FRACTION_LENGTH and digit_exponent <= -cutoff_max:\n        digit_exponent = -cutoff_max + 1\n\n    if digit_exponent > 0:\n        scale.mul_pow10(u32(digit_exponent), temp1)\n    elif digit_exponent < 0:\n        temp = temp1\n        pow10 = temp2\n        pow10.pow10(u32(-digit_exponent), temp)\n\n        temp.mul(scaled_value, pow10)\n        temp.copy(scaled_value)\n\n        temp.mul(scaled_margin_low, pow10)\n        temp.copy(scaled_margin_low)\n\n        if scaled_margin_high is not scaled_margin_low:\n            scaled_margin_high.mul2(scaled_margin_low)\n\n    if scaled_value >= scale:\n        digit_exponent += 1\n    else:\n        scaled_value.mul10()\n        scaled_margin_low.mul10()\n        if scaled_margin_high is not scaled_margin_low:\n            scaled_margin_high.mul2(scaled_margin_low)\n\n    cutoff_max_exponent = digit_exponent - buffer_size\n    if cutoff_max >= 0:\n        if cutoff_mode == _CUTOFF_MODE_TOTAL_LENGTH:\n            desired_cutoff_exponent = digit_exponent - cutoff_max\n            if desired_cutoff_exponent > cutoff_max_exponent:\n                cutoff_max_exponent = desired_cutoff_exponent\n        else:\n            desired_cutoff_exponent = -cutoff_max\n            if desired_cutoff_exponent > cutoff_max_exponent:\n                cutoff_max_exponent = desired_cutoff_exponent\n\n    cutoff_min_exponent = digit_exponent\n    if cutoff_min >= 0:\n        if cutoff_mode == _CUTOFF_MODE_TOTAL_LENGTH:\n            desired_cutoff_exponent = digit_exponent - cutoff_min\n            if desired_cutoff_exponent < cutoff_min_exponent:\n                cutoff_min_exponent = desired_cutoff_exponent\n        else:\n            desired_cutoff_exponent = -cutoff_min\n            if desired_cutoff_exponent < cutoff_min_exponent:\n                cutoff_min_exponent = desired_cutoff_exponent\n\n    out_exponent[0] = i32(digit_exponent - 1)\n    low = False\n    high = False\n    output_digit = u32(0)\n\n    hi_block = scale[len(scale) - 1]\n    if hi_block < u32(8) or hi_block > u32(429496729):\n        hi_block_log2 = _log_base2_32(hi_block)\n        shift = (u32(32) + u32(27) - hi_block_log2) % u32(32)\n\n        scale.lshift(shift)\n        scaled_value.lshift(shift)\n        scaled_margin_low.lshift(shift)\n        if scaled_margin_high is not scaled_margin_low:\n            scaled_margin_high.mul2(scaled_margin_low)\n\n    if digit_mode == _DIGIT_MODE_UNIQUE:\n        while True:\n            scaled_value_high = temp1\n            digit_exponent -= 1\n            output_digit = scaled_value.div_with_rem_maxq9(scale)\n\n            scaled_value_high.add(scaled_value, scaled_margin_high)\n            comp = scaled_value.compare(scaled_margin_low)\n            low = (comp <= 0) if is_even else (comp < 0)\n            comp = scaled_value_high.compare(scale)\n            high = (comp >= 0) if is_even else (comp > 0)\n\n            if (((low or high) and (digit_exponent <= cutoff_min_exponent)) or\n                digit_exponent == cutoff_max_exponent):\n                break\n\n            cur_digit[0] = byte(48 + int(output_digit))\n            cur_digit += 1\n\n            scaled_value.mul10()\n            scaled_margin_low.mul10()\n            if scaled_margin_high is not scaled_margin_low:\n                scaled_margin_high.mul2(scaled_margin_low)\n    else:\n        while True:\n            digit_exponent -= 1\n            output_digit = scaled_value.div_with_rem_maxq9(scale)\n\n            if len(scaled_value) == 0 or digit_exponent == cutoff_max_exponent:\n                break\n\n            cur_digit[0] = byte(48 + int(output_digit))\n            cur_digit += 1\n\n            scaled_value.mul10()\n\n    round_down = low\n    if low == high:\n        scaled_value.mul2()\n        compare = scaled_value.compare(scale)\n        round_down = (compare < 0)\n\n        if compare == 0:\n            round_down = (output_digit & u32(1) == u32(0))\n\n    if round_down:\n        cur_digit[0] = byte(48 + int(output_digit))\n        cur_digit += 1\n    else:\n        if output_digit == u32(9):\n            while True:\n                if cur_digit == out_buffer:\n                    cur_digit[0] = byte(48 + 1)\n                    cur_digit += 1\n                    out_exponent[0] += i32(1)\n                    break\n\n                cur_digit -= 1\n                if cur_digit[0] != byte(48 + 9):\n                    cur_digit[0] = byte(int(cur_digit[0]) + 1)\n                    cur_digit += 1\n                    break\n        else:\n            cur_digit[0] = byte(48 + int(output_digit) + 1)\n            cur_digit += 1\n\n    output_len = cur_digit - out_buffer\n    return output_len\n\ndef format_positional(buffer: cobj,\n                      buffer_size: int,\n                      mantissa,\n                      exponent: i32,\n                      signbit: byte,\n                      mantissa_bit: u32,\n                      has_unequal_margins: bool,\n                      digit_mode: int,\n                      cutoff_mode: int,\n                      precision: i32,\n                      min_digits: i32,\n                      trim_mode: int,\n                      digits_left: int,\n                      digits_right: int):\n    num_whole_digits = 0\n    has_sign = 0\n    max_print_len = buffer_size - 1\n    pos = 0\n    num_fraction_digits = 0\n    print_exponent = i32(0)\n\n    if signbit == byte(43) and pos < max_print_len:  # '+'\n        buffer[pos] = byte(43)\n        pos += 1\n        has_sign = 1\n    elif signbit == byte(45) and pos < max_print_len:  # '-'\n        buffer[pos] = byte(45)\n        pos += 1\n        has_sign = 1\n\n    num_digits = dragon4(mantissa, exponent, mantissa_bit,\n                        has_unequal_margins, digit_mode,\n                        cutoff_mode, int(precision), int(min_digits),\n                        buffer + has_sign, max_print_len - has_sign,\n                        __ptr__(print_exponent))\n\n    if print_exponent >= i32(0):\n        num_whole_digits = int(print_exponent) + 1\n        if num_digits <= num_whole_digits:\n            count = num_whole_digits - num_digits\n            pos += num_digits\n\n            if pos + count > max_print_len:\n                count = max_print_len - pos\n\n            num_digits += count\n            while count > 0:\n                buffer[pos] = byte(48)\n                pos += 1\n                count -= 1\n        elif num_digits > num_whole_digits:\n            num_fraction_digits = num_digits - num_whole_digits\n            max_fraction_digits = max_print_len - num_whole_digits - 1 - pos\n            if num_fraction_digits > max_fraction_digits:\n                num_fraction_digits = max_fraction_digits\n\n            str.memmove(buffer + pos + num_whole_digits + 1,\n                        buffer + pos + num_whole_digits,\n                        num_fraction_digits)\n            pos += num_whole_digits\n            buffer[pos] = byte(46)  # '.'\n            num_digits = num_whole_digits + 1 + num_fraction_digits\n            pos += 1 + num_fraction_digits\n    else:\n        num_fraction_zeros = 0\n        if pos + 2 < max_print_len:\n            max_fraction_zeros = max_print_len - 2 - pos\n            num_fraction_zeros = -(int(print_exponent) + 1)\n            if num_fraction_zeros > max_fraction_zeros:\n                num_fraction_zeros = max_fraction_zeros\n\n            digits_start_idx = 2 + num_fraction_zeros\n            num_fraction_digits = num_digits\n            max_fraction_digits = max_print_len - digits_start_idx - pos\n            if num_fraction_digits > max_fraction_digits:\n                num_fraction_digits = max_fraction_digits\n\n            str.memmove(buffer + pos + digits_start_idx,\n                        buffer + pos,\n                        num_fraction_digits)\n\n            for i in range(2, digits_start_idx):\n                buffer[pos + i] = byte(48)\n\n            num_fraction_digits += num_fraction_zeros\n            num_digits = num_fraction_digits\n\n        if pos + 1 < max_print_len:\n            buffer[pos + 1] = byte(46)  # '.'\n\n        if pos < max_print_len:\n            buffer[pos] = byte(48)\n            num_digits += 1\n\n        num_whole_digits = 1\n        pos += 2 + num_fraction_digits\n\n    if (trim_mode != _TRIM_MODE_DPT_ZEROS and\n        num_fraction_digits == 0 and pos < max_print_len):\n        buffer[pos] = byte(46)  # '.'\n        pos += 1\n\n    add_digits = int(min_digits if digit_mode == _DIGIT_MODE_UNIQUE else precision)\n    desired_fractional_digits = 0 if add_digits < 0 else add_digits\n    if cutoff_mode == _CUTOFF_MODE_TOTAL_LENGTH:\n        desired_fractional_digits = add_digits - num_whole_digits\n\n    if trim_mode == _TRIM_MODE_LEAVE_ONE_ZERO:\n        if num_fraction_digits == 0 and pos < max_print_len:\n            buffer[pos] = byte(48)\n            pos += 1\n            num_fraction_digits += 1\n    elif (trim_mode == _TRIM_MODE_NONE and\n          desired_fractional_digits > num_fraction_digits and\n          pos < max_print_len):\n        count = desired_fractional_digits - num_fraction_digits\n        if pos + count > max_print_len:\n            count = max_print_len - pos\n        num_fraction_digits += count\n\n        while count > 0:\n            buffer[pos] = byte(48)\n            pos += 1\n            count -= 1\n\n    if trim_mode != _TRIM_MODE_NONE and num_fraction_digits > 0:\n        while buffer[pos - 1] == byte(48):\n            pos -= 1\n            num_fraction_digits -= 1\n\n        if buffer[pos - 1] == byte(46):  # '.'\n            if trim_mode == _TRIM_MODE_LEAVE_ONE_ZERO:\n                buffer[pos] = byte(48)\n                pos += 1\n                num_fraction_digits += 1\n            elif trim_mode == _TRIM_MODE_DPT_ZEROS:\n                pos -= 1\n\n    if digits_right >= num_fraction_digits:\n        count = digits_right - num_fraction_digits\n\n        if (trim_mode == _TRIM_MODE_DPT_ZEROS and\n            num_fraction_digits == 0 and\n            pos < max_print_len):\n            buffer[pos] = byte(32)  # ' '\n            pos += 1\n\n        if pos + count > max_print_len:\n            count = max_print_len - pos\n\n        while count > 0:\n            buffer[pos] = byte(32)  # ' '\n            pos += 1\n            count -= 1\n\n    if digits_left > num_whole_digits + has_sign:\n        shift = digits_left - (num_whole_digits + has_sign)\n        count = pos\n\n        if count + shift > max_print_len:\n            count = max_print_len - shift\n\n        if count > 0:\n            str.memmove(buffer + shift, buffer, count)\n\n        pos = shift + count\n        while shift > 0:\n            buffer[shift - 1] = byte(32)  # ' '\n            shift -= 1\n\n    # buffer[pos] = byte(0)\n    return pos\n\ndef format_scientific(buffer: cobj,\n                      buffer_size: int,\n                      mantissa,\n                      exponent: i32,\n                      signbit: byte,\n                      mantissa_bit: u32,\n                      has_unequal_margins: bool,\n                      digit_mode: int,\n                      precision: i32,\n                      min_digits: i32,\n                      trim_mode: int,\n                      digits_left: int,\n                      exp_digits: int):\n    cur_out = buffer\n    leftchars = 1 + int(signbit == byte(43) or signbit == byte(45))  # '+'/'-'\n    print_exponent = i32(0)\n\n    if digits_left > leftchars:\n        i = 0\n        while i < digits_left - leftchars and buffer_size > 1:\n            cur_out[0] = byte(32)  # ' '\n            cur_out += 1\n            buffer_size -= 1\n            i += 1\n\n    if signbit == byte(43) and buffer_size > 1:  # '+'\n        cur_out[0] = byte(43)\n        cur_out += 1\n        buffer_size -= 1\n    elif signbit == byte(45) and buffer_size > 1:  # '-'\n        cur_out[0] = byte(45)\n        cur_out += 1\n        buffer_size -= 1\n\n    num_digits = dragon4(mantissa, exponent, mantissa_bit, has_unequal_margins,\n                         digit_mode, _CUTOFF_MODE_TOTAL_LENGTH,\n                         -1 if precision < i32(0) else int(precision) + 1,\n                         -1 if min_digits < i32(0) else int(min_digits) + 1,\n                         cur_out, buffer_size, __ptr__(print_exponent))\n\n    if buffer_size > 1:\n        cur_out += 1\n        buffer_size -= 1\n\n    num_fraction_digits = num_digits - 1\n    if num_fraction_digits > 0 and buffer_size > 1:\n        max_fraction_digits = buffer_size - 2\n        if num_fraction_digits > max_fraction_digits:\n            num_fraction_digits = max_fraction_digits\n\n        str.memmove(cur_out + 1, cur_out, num_fraction_digits)\n        cur_out[0] = byte(46)  # '.'\n        cur_out += 1 + num_fraction_digits\n        buffer_size -= 1 + num_fraction_digits\n\n    if (trim_mode != _TRIM_MODE_DPT_ZEROS and\n        num_fraction_digits == 0 and\n        buffer_size > 1):\n        cur_out[0] = byte(46)  # '.'\n        cur_out += 1\n        buffer_size -= 1\n\n    add_digits = int(min_digits) if digit_mode == _DIGIT_MODE_UNIQUE else int(precision)\n    add_digits = 0 if add_digits < 0 else add_digits\n    if trim_mode == _TRIM_MODE_LEAVE_ONE_ZERO:\n        if num_fraction_digits == 0 and buffer_size > 1:\n            cur_out[0] = byte(48)\n            cur_out += 1\n            buffer_size -= 1\n            num_fraction_digits += 1\n    elif trim_mode == _TRIM_MODE_NONE:\n        if add_digits > num_fraction_digits:\n            num_zeros = add_digits - num_fraction_digits\n\n            if num_zeros > buffer_size - 1:\n                num_zeros = buffer_size - 1\n\n            end = cur_out + num_zeros\n            while cur_out < end:\n                cur_out[0] = byte(48)\n                num_fraction_digits += 1\n                cur_out += 1\n\n    if trim_mode != _TRIM_MODE_NONE and num_fraction_digits > 0:\n        cur_out -= 1\n        while cur_out[0] == byte(48):\n            cur_out -= 1\n            buffer_size += 1\n            num_fraction_digits -= 1\n\n        if trim_mode == _TRIM_MODE_LEAVE_ONE_ZERO and cur_out[0] == byte(46):\n            cur_out += 1\n            cur_out[0] = byte(48)\n            buffer_size -= 1\n            num_fraction_digits += 1\n\n        cur_out += 1\n\n    if buffer_size > 1:\n        exponent_buffer_t = (byte(0),) * 7\n        digits_t = (i32(0),) * 5\n        exponent_buffer = __ptr__(exponent_buffer_t).as_byte()\n        digits = Ptr[i32](__ptr__(digits_t).as_byte())\n\n        if exp_digits > 5:\n            exp_digits = 5\n        if exp_digits < 0:\n            exp_digits = 2\n\n        exponent_buffer[0] = byte(101)  # 'e'\n        if print_exponent >= i32(0):\n            exponent_buffer[1] = byte(43)  # '+'\n        else:\n            exponent_buffer[1] = byte(45)  # '-'\n            print_exponent = -print_exponent\n\n        for i in range(5):\n            digits[i] = print_exponent % i32(10)\n            print_exponent //= i32(10)\n\n        i = 5\n        while i > exp_digits and digits[i - 1] == i32(0):\n            i -= 1\n        exp_size = i\n\n        while i > 0:\n            exponent_buffer[2 + (exp_size - i)] = byte(48 + int(digits[i - 1]))\n            i -= 1\n\n        count = exp_size + 2\n        if count > buffer_size - 1:\n            count = buffer_size - 1\n\n        str.memcpy(cur_out, exponent_buffer, count)\n        cur_out += count\n        buffer_size -= count\n\n    # cur_out[0] = byte(0)\n    return cur_out - buffer\n\ndef print_inf_nan(buffer: cobj,\n                  buffer_size: int,\n                  mantissa: u64,\n                  mantissa_hex_width: int,\n                  signbit: byte):\n    max_print_len = buffer_size - 1\n    pos = 0\n\n    if mantissa == u64(0):\n        if signbit == byte(43):  # '+'\n            if pos < max_print_len - 1:\n                buffer[pos] = byte(43)\n                pos += 1\n        elif signbit == byte(45):  # '-'\n            if pos < max_print_len - 1:\n                buffer[pos] = byte(45)\n                pos += 1\n\n        print_len = 3 if 3 < max_print_len - pos else max_print_len - pos\n        str.memcpy(buffer + pos, \"inf\".ptr, print_len)\n        # buffer[pos + print_len] = byte(0)\n        return pos + print_len\n    else:\n        print_len = 3 if 3 < max_print_len - pos else max_print_len - pos\n        str.memcpy(buffer + pos, \"nan\".ptr, print_len)\n        # buffer[pos + print_len] = byte(0)\n        return pos + print_len\n\ndef format_float_bits(buffer: cobj,\n                      buffer_size: int,\n                      mantissa,\n                      exponent: i32,\n                      signbit: byte,\n                      mantissa_bit: u32,\n                      has_unequal_margins: bool,\n                      scientific: bool,\n                      digit_mode: int,\n                      cutoff_mode: int,\n                      precision: i32,\n                      min_digits: i32,\n                      sign: bool,\n                      trim_mode: int,\n                      digits_left: int,\n                      digits_right: int,\n                      exp_digits: int):\n    if scientific:\n        return format_scientific(buffer, buffer_size, mantissa, exponent,\n                                 signbit, mantissa_bit, has_unequal_margins,\n                                 digit_mode, precision, min_digits, trim_mode,\n                                 digits_left, exp_digits)\n    else:\n        return format_positional(buffer, buffer_size, mantissa, exponent,\n                                 signbit, mantissa_bit, has_unequal_margins,\n                                 digit_mode, cutoff_mode, precision, min_digits,\n                                 trim_mode, digits_left, digits_right)\n\ndef _trim_mode_code(trim_mode: str):\n    if trim_mode == 'k':\n        return _TRIM_MODE_NONE\n    elif trim_mode == '0':\n        return _TRIM_MODE_LEAVE_ONE_ZERO\n    elif trim_mode == '.':\n        return _TRIM_MODE_ZEROS\n    elif trim_mode == '-':\n        return _TRIM_MODE_DPT_ZEROS\n    else:\n        raise ValueError(f\"if supplied, trim must be 'k', '.', '0' or '-' found `{trim_mode}`\")\n\ndef _from_buffer(buffer: cobj, n: int):\n    p = cobj(n)\n    str.memcpy(p, buffer, n)\n    return str(p, n)\n\n# Buffer size that can hold any float128 string\n# TODO: Make this smaller for other float types\n_BUFFER_SIZE: Literal[int] = 16384\n\ndef format_ieee16(value: float16,\n                  scientific: bool = False,\n                  unique: bool = True,\n                  fractional: bool = True,\n                  precision: int = -1,\n                  min_digits: int = -1,\n                  sign: bool = False,\n                  trim_mode: str = 'k',\n                  digits_left: int = -1,\n                  digits_right: int = -1,\n                  exp_digits: int = -1):\n    bigint_data = __array__[u32](_BIGINT_DRAGON4_GROUPSIZE * _BIGINT_MAX_BLOCKS)\n    scratch = __array__[byte](_BUFFER_SIZE)\n    buffer = scratch.ptr\n    blocks = bigint_data.ptr\n    bigints = tuple(BigInt(0, blocks + (i * _BIGINT_MAX_BLOCKS))\n                    for i in static.range(_BIGINT_DRAGON4_GROUPSIZE))\n\n    signbit = byte(0)\n    digit_mode = _DIGIT_MODE_UNIQUE if unique else _DIGIT_MODE_EXACT\n    cutoff_mode = _CUTOFF_MODE_FRACTION_LENGTH if fractional else _CUTOFF_MODE_TOTAL_LENGTH\n\n    integer = util.zext(util.bitcast(value, u16), u32)\n    float_mantissa = integer & _bitmask_u32(10)\n    float_exponent = (integer >> u32(10)) & _bitmask_u32(5)\n    float_sign = integer >> u32(15)\n\n    if float_sign:\n        signbit = byte(45)  # '-'\n    elif sign:\n        signbit = byte(43)  # '+'\n\n    if float_exponent == _bitmask_u32(5):\n        n = print_inf_nan(buffer, _BUFFER_SIZE, util.zext(float_mantissa, u64), 3, signbit)\n        return _from_buffer(buffer, n)\n\n    mantissa = u32(0)\n    exponent = i32(0)\n    mantissa_bit = u32(0)\n    has_unequal_margins = False\n\n    if float_exponent:\n        mantissa = (u32(1) << u32(10)) | float_mantissa\n        exponent = util.bitcast(float_exponent, i32) - i32(15) - i32(10)\n        mantissa_bit = u32(10)\n        has_unequal_margins = (float_exponent != u32(1)) and (float_mantissa == u32(0))\n    else:\n        mantissa = float_mantissa\n        exponent = i32(1 - 15 - 10)\n        mantissa_bit = _log_base2_32(mantissa)\n        has_unequal_margins = False\n\n    bigints[0].set(mantissa)\n    n = format_float_bits(buffer,\n                          _BUFFER_SIZE,\n                          bigints,\n                          exponent,\n                          signbit,\n                          mantissa_bit,\n                          has_unequal_margins,\n                          scientific,\n                          digit_mode,\n                          cutoff_mode,\n                          i32(precision),\n                          i32(min_digits),\n                          sign,\n                          _trim_mode_code(trim_mode),\n                          digits_left,\n                          digits_right,\n                          exp_digits)\n    return _from_buffer(buffer, n)\n\ndef format_ieee32(value: float32,\n                  scientific: bool = False,\n                  unique: bool = True,\n                  fractional: bool = True,\n                  precision: int = -1,\n                  min_digits: int = -1,\n                  sign: bool = False,\n                  trim_mode: str = 'k',\n                  digits_left: int = -1,\n                  digits_right: int = -1,\n                  exp_digits: int = -1):\n    bigint_data = __array__[u32](_BIGINT_DRAGON4_GROUPSIZE * _BIGINT_MAX_BLOCKS)\n    scratch = __array__[byte](_BUFFER_SIZE)\n    buffer = scratch.ptr\n    blocks = bigint_data.ptr\n    bigints = tuple(BigInt(0, blocks + (i * _BIGINT_MAX_BLOCKS))\n                    for i in static.range(_BIGINT_DRAGON4_GROUPSIZE))\n\n    signbit = byte(0)\n    digit_mode = _DIGIT_MODE_UNIQUE if unique else _DIGIT_MODE_EXACT\n    cutoff_mode = _CUTOFF_MODE_FRACTION_LENGTH if fractional else _CUTOFF_MODE_TOTAL_LENGTH\n\n    integer = util.bitcast(value, u32)\n    float_mantissa = integer & _bitmask_u32(23)\n    float_exponent = (integer >> u32(23)) & _bitmask_u32(8)\n    float_sign = integer >> u32(31)\n\n    if float_sign:\n        signbit = byte(45)  # '-'\n    elif sign:\n        signbit = byte(43)  # '+'\n\n    if float_exponent == _bitmask_u32(8):\n        n = print_inf_nan(buffer, _BUFFER_SIZE, util.zext(float_mantissa, u64), 6, signbit)\n        return _from_buffer(buffer, n)\n\n    mantissa = u32(0)\n    exponent = i32(0)\n    mantissa_bit = u32(0)\n    has_unequal_margins = False\n\n    if float_exponent:\n        mantissa = (u32(1) << u32(23)) | float_mantissa\n        exponent = util.bitcast(float_exponent, i32) - i32(127) - i32(23)\n        mantissa_bit = u32(23)\n        has_unequal_margins = (float_exponent != u32(1)) and (float_mantissa == u32(0))\n    else:\n        mantissa = float_mantissa\n        exponent = i32(1 - 127 - 23)\n        mantissa_bit = _log_base2_32(mantissa)\n        has_unequal_margins = False\n\n    bigints[0].set(mantissa)\n    n = format_float_bits(buffer,\n                          _BUFFER_SIZE,\n                          bigints,\n                          exponent,\n                          signbit,\n                          mantissa_bit,\n                          has_unequal_margins,\n                          scientific,\n                          digit_mode,\n                          cutoff_mode,\n                          i32(precision),\n                          i32(min_digits),\n                          sign,\n                          _trim_mode_code(trim_mode),\n                          digits_left,\n                          digits_right,\n                          exp_digits)\n    return _from_buffer(buffer, n)\n\ndef format_ieee64(value: float,\n                  scientific: bool = False,\n                  unique: bool = True,\n                  fractional: bool = True,\n                  precision: int = -1,\n                  min_digits: int = -1,\n                  sign: bool = False,\n                  trim_mode: str = 'k',\n                  digits_left: int = -1,\n                  digits_right: int = -1,\n                  exp_digits: int = -1):\n    bigint_data = __array__[u32](_BIGINT_DRAGON4_GROUPSIZE * _BIGINT_MAX_BLOCKS)\n    scratch = __array__[byte](_BUFFER_SIZE)\n    buffer = scratch.ptr\n    blocks = bigint_data.ptr\n    bigints = tuple(BigInt(0, blocks + (i * _BIGINT_MAX_BLOCKS))\n                    for i in static.range(_BIGINT_DRAGON4_GROUPSIZE))\n\n    signbit = byte(0)\n    digit_mode = _DIGIT_MODE_UNIQUE if unique else _DIGIT_MODE_EXACT\n    cutoff_mode = _CUTOFF_MODE_FRACTION_LENGTH if fractional else _CUTOFF_MODE_TOTAL_LENGTH\n\n    integer = util.bitcast(value, u64)\n    float_mantissa = integer & _bitmask_u64(52)\n    float_exponent = util.itrunc(integer >> u64(52), u32) & _bitmask_u32(11)\n    float_sign = integer >> u64(63)\n\n    if float_sign:\n        signbit = byte(45)  # '-'\n    elif sign:\n        signbit = byte(43)  # '+'\n\n    if float_exponent == _bitmask_u32(11):\n        n = print_inf_nan(buffer, _BUFFER_SIZE, float_mantissa, 13, signbit)\n        return _from_buffer(buffer, n)\n\n    mantissa = u64(0)\n    exponent = i32(0)\n    mantissa_bit = u32(0)\n    has_unequal_margins = False\n\n    if float_exponent:\n        mantissa = (u64(1) << u64(52)) | float_mantissa\n        exponent = util.bitcast(float_exponent, i32) - i32(1023) - i32(52)\n        mantissa_bit = u32(52)\n        has_unequal_margins = (float_exponent != u32(1)) and (float_mantissa == u64(0))\n    else:\n        mantissa = float_mantissa\n        exponent = i32(1 - 1023 - 52)\n        mantissa_bit = util.itrunc(_log_base2_64(mantissa), u32)\n        has_unequal_margins = False\n\n    bigints[0].set(mantissa)\n    n = format_float_bits(buffer,\n                          _BUFFER_SIZE,\n                          bigints,\n                          exponent,\n                          signbit,\n                          mantissa_bit,\n                          has_unequal_margins,\n                          scientific,\n                          digit_mode,\n                          cutoff_mode,\n                          i32(precision),\n                          i32(min_digits),\n                          sign,\n                          _trim_mode_code(trim_mode),\n                          digits_left,\n                          digits_right,\n                          exp_digits)\n    return _from_buffer(buffer, n)\n\ndef format_ieee128(value: float128,\n                   scientific: bool = False,\n                   unique: bool = True,\n                   fractional: bool = True,\n                   precision: int = -1,\n                   min_digits: int = -1,\n                   sign: bool = False,\n                   trim_mode: str = 'k',\n                   digits_left: int = -1,\n                   digits_right: int = -1,\n                   exp_digits: int = -1):\n    bigint_data = __array__[u32](_BIGINT_DRAGON4_GROUPSIZE * _BIGINT_MAX_BLOCKS)\n    scratch = __array__[byte](_BUFFER_SIZE)\n    buffer = scratch.ptr\n    blocks = bigint_data.ptr\n    bigints = tuple(BigInt(0, blocks + (i * _BIGINT_MAX_BLOCKS))\n                    for i in static.range(_BIGINT_DRAGON4_GROUPSIZE))\n\n    signbit = byte(0)\n    digit_mode = _DIGIT_MODE_UNIQUE if unique else _DIGIT_MODE_EXACT\n    cutoff_mode = _CUTOFF_MODE_FRACTION_LENGTH if fractional else _CUTOFF_MODE_TOTAL_LENGTH\n\n    integer = util.bitcast(value, u128)\n    val_hi = util.itrunc(integer >> u128(64), u64)\n    val_lo = util.itrunc(integer & ((u128(1) << u128(64)) - u128(1)), u64)\n    mantissa_hi = val_hi & _bitmask_u64(48)\n    mantissa_lo = val_lo\n\n    float_exponent = util.itrunc(val_hi >> u64(48), u32) & _bitmask_u32(15)\n    float_sign = util.itrunc(val_hi >> u64(63), u32)\n\n    if float_sign:\n        signbit = byte(45)  # '-'\n    elif sign:\n        signbit = byte(43)  # '+'\n\n    if float_exponent == _bitmask_u32(15):\n        mantissa_zero = u64(1 if mantissa_hi or mantissa_lo else 0)\n        n = print_inf_nan(buffer, _BUFFER_SIZE, mantissa_zero, 16, signbit)\n        return _from_buffer(buffer, n)\n\n    exponent = i32(0)\n    mantissa_bit = u32(0)\n    has_unequal_margins = False\n\n    if float_exponent:\n        mantissa_hi = (u64(1) << u64(48)) | mantissa_hi\n        exponent = util.bitcast(float_exponent, i32) - i32(16383) - i32(112)\n        mantissa_bit = u32(112)\n        has_unequal_margins = (float_exponent != u32(1)) and (mantissa_hi == u64(0)) and (mantissa_lo == u64(0))\n    else:\n        exponent = i32(1 - 16383 - 112)\n        mantissa_bit = util.itrunc(_log_base2_128(mantissa_hi, mantissa_lo), u32)\n        has_unequal_margins = False\n\n    bigints[0].set(mantissa_hi, mantissa_lo)\n    n = format_float_bits(buffer,\n                          _BUFFER_SIZE,\n                          bigints,\n                          exponent,\n                          signbit,\n                          mantissa_bit,\n                          has_unequal_margins,\n                          scientific,\n                          digit_mode,\n                          cutoff_mode,\n                          i32(precision),\n                          i32(min_digits),\n                          sign,\n                          _trim_mode_code(trim_mode),\n                          digits_left,\n                          digits_right,\n                          exp_digits)\n    return _from_buffer(buffer, n)\n\ndef format_float(value,\n                 scientific: bool = False,\n                 unique: bool = True,\n                 fractional: bool = True,\n                 precision: int = -1,\n                 min_digits: int = -1,\n                 sign: bool = False,\n                 trim_mode: str = 'k',\n                 digits_left: int = -1,\n                 digits_right: int = -1,\n                 exp_digits: int = -1):\n    if isinstance(value, float16):\n        return format_ieee16(value,\n                             scientific=scientific,\n                             unique=unique,\n                             fractional=fractional,\n                             precision=precision,\n                             min_digits=min_digits,\n                             sign=sign,\n                             trim_mode=trim_mode,\n                             digits_left=digits_left,\n                             digits_right=digits_right,\n                             exp_digits=exp_digits)\n\n    if isinstance(value, float32):\n        return format_ieee32(value,\n                             scientific=scientific,\n                             unique=unique,\n                             fractional=fractional,\n                             precision=precision,\n                             min_digits=min_digits,\n                             sign=sign,\n                             trim_mode=trim_mode,\n                             digits_left=digits_left,\n                             digits_right=digits_right,\n                             exp_digits=exp_digits)\n\n    if isinstance(value, float):\n        return format_ieee64(value,\n                             scientific=scientific,\n                             unique=unique,\n                             fractional=fractional,\n                             precision=precision,\n                             min_digits=min_digits,\n                             sign=sign,\n                             trim_mode=trim_mode,\n                             digits_left=digits_left,\n                             digits_right=digits_right,\n                             exp_digits=exp_digits)\n\n    if isinstance(value, float128):\n        return format_ieee128(value,\n                              scientific=scientific,\n                              unique=unique,\n                              fractional=fractional,\n                              precision=precision,\n                              min_digits=min_digits,\n                              sign=sign,\n                              trim_mode=trim_mode,\n                              digits_left=digits_left,\n                              digits_right=digits_right,\n                              exp_digits=exp_digits)\n\n    compile_error(\"cannot format non-float value\")\n\ndef _none_or_positive_arg(x: Optional[int], name: str) -> int:\n    if x is None:\n        return -1\n    if x < 0:\n        raise ValueError(f\"{name} must be >= 0\")\n    return x\n\ndef format_float_positional(x,\n                            precision: Optional[int] = None,\n                            unique: bool = True,\n                            fractional: bool = True,\n                            trim: str = 'k',\n                            sign: bool = False,\n                            pad_left: Optional[int] = None,\n                            pad_right: Optional[int] = None,\n                            min_digits: Optional[int] = None):\n    precision = _none_or_positive_arg(precision, \"precision\")\n    pad_left = _none_or_positive_arg(pad_left, \"pad_left\")\n    pad_right = _none_or_positive_arg(pad_right, \"pad_right\")\n    min_digits = _none_or_positive_arg(min_digits, \"min_digits\")\n\n    if not fractional and precision == 0:\n        raise ValueError(\"precision must be greater than 0 if \"\n                         \"fractional=False\")\n\n    if min_digits > 0 and precision > 0 and min_digits > precision:\n        raise ValueError(\"min_digits must be less than or equal to precision\")\n\n    return format_float(x, scientific=False, precision=precision, unique=unique,\n                        fractional=fractional, trim_mode=trim, sign=sign, digits_left=pad_left,\n                        digits_right=pad_right, min_digits=min_digits)\n\ndef format_float_scientific(x,\n                            precision: Optional[int] = None,\n                            unique: bool = True,\n                            trim: str = 'k',\n                            sign: bool = False,\n                            pad_left: Optional[int] = None,\n                            exp_digits: Optional[int] = None,\n                            min_digits: Optional[int] = None):\n    precision = _none_or_positive_arg(precision, \"precision\")\n    pad_left = _none_or_positive_arg(pad_left, \"pad_left\")\n    exp_digits = _none_or_positive_arg(exp_digits, \"exp_digits\")\n    min_digits = _none_or_positive_arg(min_digits, \"min_digits\")\n\n    if min_digits > 0 and precision > 0 and min_digits > precision:\n        raise ValueError(\"min_digits must be less than or equal to precision\")\n\n    return format_float(x, scientific=True, precision=precision, unique=unique,\n                        trim_mode=trim, sign=sign, digits_left=pad_left,\n                        exp_digits=exp_digits, min_digits=min_digits)\n"
  },
  {
    "path": "stdlib/numpy/dtype.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nint8 = Int[8]\nint16 = Int[16]\nint32 = Int[32]\nint64 = int\n\nuint8 = UInt[8]\nuint16 = UInt[16]\nuint32 = UInt[32]\nuint64 = UInt[64]\n\nintp = int64\nuintp = uint64\n# float16 already present in Codon\n# float32 already present in Codon\nfloat64 = float\n# float128 already present in Codon\n# complex64 already present in Codon\ncomplex128 = complex\n\nbool_ = bool\n# byte already present in Codon\nubyte = uint8\nshort = int16\nushort = uint16\nintc = int32\nuintc = uint32\nint_ = int64\nuint = uint64\nlonglong = int64\nulonglong = uint64\n# half / float16\nsingle = float32\ndouble = float\nlongdouble = float\ncsingle = complex64\ncdouble = complex\nclongdouble = complex\n\nfloat_ = double\ncomplex_ = cdouble\nlongfloat = longdouble\nsinglecomplex = csingle\ncfloat = cdouble\nlongcomplex = clongdouble\nclongfloat = clongdouble\n\n@tuple\nclass finfo:\n    dtype: type\n\n    def __new__(x):\n        return finfo(type(x))\n\n    def __new__(dtype: type):\n        if (dtype is float64 or\n            dtype is float32 or\n            dtype is float16):\n            return finfo[dtype]()\n        elif dtype is complex128:\n            return finfo[float64]()\n        elif dtype is complex64:\n            return finfo[float32]()\n        else:\n            if dtype is float128 or dtype is bfloat16:\n                compile_error(\"data type '\" + dtype.__name__ + \"' not supported by finfo\")\n            else:\n                compile_error(\"data type '\" + dtype.__name__ + \"' not inexact\")\n\n    @property\n    def bits(self):\n        if dtype is float64:\n            return 64\n        elif dtype is float32:\n            return 32\n        elif dtype is float16:\n            return 16\n\n    @property\n    def iexp(self):\n        if dtype is float64:\n            return 11\n        elif dtype is float32:\n            return 8\n        elif dtype is float16:\n            return 5\n\n    @property\n    def machep(self):\n        if dtype is float64:\n            return -52\n        elif dtype is float32:\n            return -23\n        elif dtype is float16:\n            return -10\n\n    @property\n    def maxexp(self):\n        if dtype is float64:\n            return 1024\n        elif dtype is float32:\n            return 128\n        elif dtype is float16:\n            return 16\n\n    @property\n    def minexp(self):\n        if dtype is float64:\n            return -1022\n        elif dtype is float32:\n            return -126\n        elif dtype is float16:\n            return -14\n\n    @property\n    def negep(self):\n        if dtype is float64:\n            return -53\n        elif dtype is float32:\n            return -24\n        elif dtype is float16:\n            return -11\n\n    @property\n    def nexp(self):\n        if dtype is float64:\n            return 11\n        elif dtype is float32:\n            return 8\n        elif dtype is float16:\n            return 5\n\n    @property\n    def nmant(self):\n        if dtype is float64:\n            return 52\n        elif dtype is float32:\n            return 23\n        elif dtype is float16:\n            return 10\n\n    @property\n    def precision(self):\n        if dtype is float64:\n            return 15\n        elif dtype is float32:\n            return 6\n        elif dtype is float16:\n            return 3\n        else:\n            return 0\n\n    @property\n    def eps(self):\n        @pure\n        @llvm\n        def eps64() -> float:\n            declare double @llvm.exp2.f64(double)\n            %y = call double @llvm.exp2.f64(double -52.0)\n            ret double %y\n\n        @pure\n        @llvm\n        def eps32() -> float32:\n            declare float @llvm.exp2.f32(float)\n            %y = call float @llvm.exp2.f32(float -23.0)\n            ret float %y\n\n        @pure\n        @llvm\n        def eps16() -> float16:\n            declare half @llvm.exp2.f16(half)\n            %y = call half @llvm.exp2.f16(half -10.0)\n            ret half %y\n\n        if dtype is float64:\n            return eps64()\n        elif dtype is float32:\n            return eps32()\n        elif dtype is float16:\n            return eps16()\n\n    @property\n    def epsneg(self):\n        @pure\n        @llvm\n        def epsneg64() -> float:\n            declare double @llvm.exp2.f64(double)\n            %y = call double @llvm.exp2.f64(double -53.0)\n            ret double %y\n\n        @pure\n        @llvm\n        def epsneg32() -> float32:\n            declare float @llvm.exp2.f32(float)\n            %y = call float @llvm.exp2.f32(float -24.0)\n            ret float %y\n\n        @pure\n        @llvm\n        def epsneg16() -> float16:\n            declare half @llvm.exp2.f16(half)\n            %y = call half @llvm.exp2.f16(half -11.0)\n            ret half %y\n\n        if dtype is float64:\n            return epsneg64()\n        elif dtype is float32:\n            return epsneg32()\n        elif dtype is float16:\n            return epsneg16()\n\n    @property\n    def max(self):\n        @pure\n        @llvm\n        def max64() -> float:\n            ret double 0x7FEFFFFFFFFFFFFF\n\n        @pure\n        @llvm\n        def max32() -> float32:\n            ret float 0x47EFFFFFE0000000\n\n        @pure\n        @llvm\n        def max16() -> float16:\n            ret half 0xH7BFF\n\n        if dtype is float64:\n            return max64()\n        elif dtype is float32:\n            return max32()\n        elif dtype is float16:\n            return max16()\n\n    @property\n    def min(self):\n        @pure\n        @llvm\n        def min64() -> float:\n            ret double 0xFFEFFFFFFFFFFFFF\n\n        @pure\n        @llvm\n        def min32() -> float32:\n            ret float 0xC7EFFFFFE0000000\n\n        @pure\n        @llvm\n        def min16() -> float16:\n            ret half 0xHFBFF\n\n        if dtype is float64:\n            return min64()\n        elif dtype is float32:\n            return min32()\n        elif dtype is float16:\n            return min16()\n\n    @property\n    def resolution(self):\n        if dtype is float64:\n            return 1e-15\n        elif dtype is float32:\n            return float32(1e-06)\n        elif dtype is float16:\n            return float16(0.001)\n\n    @property\n    def smallest_normal(self):\n        @pure\n        @llvm\n        def smallest_normal64() -> float:\n            ret double 0x10000000000000\n\n        @pure\n        @llvm\n        def smallest_normal32() -> float32:\n            ret float 0x3810000000000000\n\n        @pure\n        @llvm\n        def smallest_normal16() -> float16:\n            ret half 0xH0400\n\n        if dtype is float64:\n            return smallest_normal64()\n        elif dtype is float32:\n            return smallest_normal32()\n        elif dtype is float16:\n            return smallest_normal16()\n\n    @property\n    def smallest_subnormal(self):\n        @pure\n        @llvm\n        def smallest_subnormal64() -> float:\n            ret double 4.940660e-324\n\n        @pure\n        @llvm\n        def smallest_subnormal32() -> float32:\n            ret float 0x36A0000000000000\n\n        @pure\n        @llvm\n        def smallest_subnormal16() -> float16:\n            ret half 0xH0001\n\n        if dtype is float64:\n            return smallest_subnormal64()\n        elif dtype is float32:\n            return smallest_subnormal32()\n        elif dtype is float16:\n            return smallest_subnormal16()\n\n    @property\n    def tiny(self):\n        return self.smallest_normal\n\n    def _type_name(self):\n        if dtype is float64:\n            return 'float64'\n        elif dtype is float32:\n            return 'float32'\n        elif dtype is float16:\n            return 'float16'\n        else:\n            return '?'\n\n    def __repr__(self):\n        return f'finfo(resolution={self.resolution}, min={self.min}, max={self.max}, dtype={self._type_name()})'\n\n    def __str__(self):\n        if dtype is float64:\n            return '''Machine parameters for float64\n---------------------------------------------------------------\nprecision =  15   resolution = 1.0000000000000001e-15\nmachep =    -52   eps =        2.2204460492503131e-16\nnegep =     -53   epsneg =     1.1102230246251565e-16\nminexp =  -1022   tiny =       2.2250738585072014e-308\nmaxexp =   1024   max =        1.7976931348623157e+308\nnexp =       11   min =        -max\nsmallest_normal = 2.2250738585072014e-308   smallest_subnormal = 4.9406564584124654e-324\n---------------------------------------------------------------\n'''\n        elif dtype is float32:\n            return '''Machine parameters for float32\n---------------------------------------------------------------\nprecision =   6   resolution = 1.0000000e-06\nmachep =    -23   eps =        1.1920929e-07\nnegep =     -24   epsneg =     5.9604645e-08\nminexp =   -126   tiny =       1.1754944e-38\nmaxexp =    128   max =        3.4028235e+38\nnexp =        8   min =        -max\nsmallest_normal = 1.1754944e-38   smallest_subnormal = 1.4012985e-45\n---------------------------------------------------------------\n'''\n        elif dtype is float16:\n            return '''Machine parameters for float16\n---------------------------------------------------------------\nprecision =   3   resolution = 1.00040e-03\nmachep =    -10   eps =        9.76562e-04\nnegep =     -11   epsneg =     4.88281e-04\nminexp =    -14   tiny =       6.10352e-05\nmaxexp =     16   max =        6.55040e+04\nnexp =        5   min =        -max\nsmallest_normal = 6.10352e-05   smallest_subnormal = 5.96046e-08\n---------------------------------------------------------------\n'''\n        else:\n            return '?'\n\n@tuple\nclass iinfo:\n    dtype: type\n\n    def __new__(x):\n        return iinfo(type(x))\n\n    def __new__(int_type: type):\n        if (int_type is int8 or\n            int_type is uint8 or\n            int_type is int16 or\n            int_type is uint16 or\n            int_type is int32 or\n            int_type is uint32 or\n            int_type is int64 or\n            int_type is uint64 or\n            int_type is int):\n            return iinfo[int_type]()\n        else:\n            compile_error(\"data type '\" + int_type.__name__ + \"' not supported by iinfo\")\n\n    @property\n    def bits(self):\n        if dtype is int8 or dtype is uint8:\n            return 8\n        elif dtype is int16 or dtype is uint16:\n            return 16\n        elif dtype is int32 or dtype is uint32:\n            return 32\n        elif dtype is int64 or dtype is uint64 or dtype is int:\n            return 64\n\n    @property\n    def min(self):\n        if dtype is int8:\n            return -128\n        elif dtype is uint8:\n            return 0\n        elif dtype is int16:\n            return -32768\n        elif dtype is uint16:\n            return 0\n        elif dtype is int32:\n            return -2147483648\n        elif dtype is uint32:\n            return 0\n        elif dtype is int64:\n            return -9223372036854775808\n        elif dtype is uint64:\n            return 0\n        elif dtype is int:\n            return -9223372036854775808\n\n    @property\n    def max(self):\n        if dtype is int8:\n            return 127\n        elif dtype is uint8:\n            return 255\n        elif dtype is int16:\n            return 32767\n        elif dtype is uint16:\n            return 65535\n        elif dtype is int32:\n            return 2147483647\n        elif dtype is uint32:\n            return 4294967295\n        elif dtype is int64:\n            return 9223372036854775807\n        elif dtype is uint64:\n            return 0xFFFFFFFFFFFFFFFF\n        elif dtype is int:\n            return 9223372036854775807\n\n    def _type_name(self):\n        if dtype is int8:\n            return 'int8'\n        elif dtype is uint8:\n            return 'uint8'\n        elif dtype is int16:\n            return 'int16'\n        elif dtype is uint16:\n            return 'uint16'\n        elif dtype is int32:\n            return 'int32'\n        elif dtype is uint32:\n            return 'uint32'\n        elif dtype is int64:\n            return 'int64'\n        elif dtype is uint64:\n            return 'uint64'\n        elif dtype is int:\n            return 'int64'\n        else:\n            return '?'\n\n    def __repr__(self):\n        return f'iinfo(min={dtype(self.min)}, max={dtype(self.max)}, dtype={self._type_name()})'\n\n    def __str__(self):\n        return f'Machine parameters for {self._type_name()}\\n---------------------------------------------------------------\\nmin = {dtype(self.min)}\\nmax = {dtype(self.max)}\\n---------------------------------------------------------------\\n'\n"
  },
  {
    "path": "stdlib/numpy/emath.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .ndarray import ndarray\nfrom .routines import asarray, isreal\nimport ndmath as nx\nimport util\n\ndef _complex_type(dtype: type):\n    if (dtype is float32 or\n        dtype is u8 or\n        dtype is i8 or\n        dtype is u16 or\n        dtype is i16 or\n        dtype is complex64):\n        return complex64()\n    else:\n        return complex()\n\ndef _tocomplex(arr, op, C: type):\n    return arr.map(lambda x: op(util.cast(x, C)))\n\ndef _isreal(x):\n    if isinstance(x, complex) or isinstance(x, complex64):\n        return not bool(x.imag)\n    return True\n\ndef _real_lt_zero(x):\n    T = type(x)\n    return _isreal(x) and x < T(0)\n\ndef _real_abs_gt_1(x):\n    T = type(x)\n    return _isreal(x) and abs(x) > T(1)\n\ndef _unary_emath_op(x, op, cond):\n    x = asarray(x)\n\n    if x.dtype is complex or x.dtype is complex64:\n        return op(x)\n\n    ndim: Literal[int] = x.ndim\n    C = type(_complex_type(x.dtype))\n    F = type(util.to_float(util.zero(x.dtype)))\n    O = Union[ndarray[F, ndim], ndarray[C, ndim]]\n\n    if x._any(cond):\n        return O(_tocomplex(x, op, C))\n    else:\n        return O(op(x))\n\ndef sqrt(x):\n    return _unary_emath_op(x, nx.sqrt, _real_lt_zero)\n\ndef log(x):\n    return _unary_emath_op(x, nx.log, _real_lt_zero)\n\ndef log10(x):\n    return _unary_emath_op(x, nx.log10, _real_lt_zero)\n\ndef log2(x):\n    return _unary_emath_op(x, nx.log2, _real_lt_zero)\n\ndef arccos(x):\n    return _unary_emath_op(x, nx.arccos, _real_abs_gt_1)\n\ndef arcsin(x):\n    return _unary_emath_op(x, nx.arcsin, _real_abs_gt_1)\n\ndef arctanh(x):\n    return _unary_emath_op(x, nx.arctanh, _real_abs_gt_1)\n\ndef logn(n, x):\n    return log(x) / log(n)\n\n\n# power\n\n\n\n\n\n\n\n"
  },
  {
    "path": "stdlib/numpy/fft/__init__.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\nfrom pocketfft import _swap_direction, fft as raw_fft, ifft as raw_ifft, rfft as raw_rfft, irfft as raw_irfft\nfrom ..routines import _check_out, asarray, empty, arange, roll\nfrom ..ndarray import ndarray\nfrom ..util import zero, cast, free, multirange, normalize_axis_index, tuple_range, tuple_set, tuple_insert, tuple_delete, sizeof\n\ndef _complex_dtype(dtype: type):\n    if (dtype is complex64 or\n        dtype is float32 or\n        dtype is float16):\n        return complex64()\n    else:\n        return complex()\n\ndef _fft(a, inv: bool, n: Optional[int] = None,\n         axis: int = -1, norm: Optional[str] = None,\n         out = None):\n    a = asarray(a)\n    T = type(_complex_dtype(a.dtype))\n    T0 = type(T().real)\n    axis = normalize_axis_index(axis, a.ndim)\n    istride = a.strides[axis]\n\n    m = a.shape[axis]\n    N = m\n    if n is not None:\n        N = n\n    M = N\n\n    out_shape = tuple_set(a.shape, axis, M)\n\n    if out is None:\n        ans = empty(out_shape, T)\n    else:\n        _check_out(out, out_shape)\n        if out.dtype is not T:\n            compile_error(\"'out' dtype must be complex\")\n        ans = out\n\n    ostride = ans.strides[axis]\n    need_buf = (a.dtype is not T or\n                ostride != sizeof(T) or\n                N != m)\n    buf = Ptr[T](N) if need_buf else Ptr[T]()\n\n    for idx in multirange(tuple_delete(a.shape, axis)):\n        idx1 = tuple_insert(idx, axis, 0)\n        p = a._ptr(idx1)\n        q = ans._ptr(idx1)\n        pb = p.as_byte()\n        data = buf if need_buf else Ptr[T](q.as_byte())\n\n        for i in range(min(m, N)):\n            data[i] = cast(Ptr[a.dtype](pb)[0], T)\n            pb += istride\n\n        for i in range(m, N):\n            data[i] = T(0.0, 0.0)\n\n        if inv:\n            raw_ifft(data, N, norm)\n        else:\n            raw_fft(data, N, norm)\n\n        if need_buf:\n            qb = q.as_byte()\n            for i in range(M):\n                Ptr[ans.dtype](qb)[0] = data[i]\n                qb += ostride\n\n    if need_buf:\n        free(buf)\n\n    return ans\n\ndef fft(a, n: Optional[int] = None,\n        axis: int = -1, norm: Optional[str] = None,\n        out = None):\n    return _fft(a, inv=False, n=n, axis=axis, norm=norm, out=out)\n\ndef ifft(a, n: Optional[int] = None,\n        axis: int = -1, norm: Optional[str] = None,\n        out = None):\n    return _fft(a, inv=True, n=n, axis=axis, norm=norm, out=out)\n\ndef rfft(a, n: Optional[int] = None,\n         axis: int = -1, norm: Optional[str] = None,\n         out = None):\n    a = asarray(a)\n    T = type(_complex_dtype(a.dtype))\n    T0 = type(T().real)\n    axis = normalize_axis_index(axis, a.ndim)\n    istride = a.strides[axis]\n\n    m = a.shape[axis]\n    N = m\n    if n is not None:\n        N = n\n    M = (N >> 1) + 1\n\n    out_shape = tuple_set(a.shape, axis, M)\n\n    need_ibuf = (a.dtype is not T0 or\n                 istride != sizeof(T0) or\n                 N != m)\n    need_ibuf = True\n\n    if out is None:\n        ans = empty(out_shape, T)\n    else:\n        _check_out(out, out_shape)\n        if out.dtype is not T:\n            compile_error(\"'out' dtype must be complex\")\n        ans = out\n\n    ostride = ans.strides[axis]\n    need_obuf = (ans.strides[axis] != sizeof(ans.dtype))\n    ibuf = Ptr[T0](N) if need_ibuf else Ptr[T0]()\n    obuf = Ptr[T](M) if need_obuf else Ptr[T]()\n\n    for idx in multirange(tuple_delete(a.shape, axis)):\n        idx1 = tuple_insert(idx, axis, 0)\n        p = a._ptr(idx1)\n        if need_ibuf:\n            pb = p.as_byte()\n            for i in range(min(m, N)):\n                ibuf[i] = cast(Ptr[a.dtype](pb)[0], T0)\n                pb += istride\n            for i in range(m, N):\n                ibuf[i] = T0(0.0)\n            in_data = ibuf\n        else:\n            in_data = Ptr[T0](p.as_byte())\n\n        q = ans._ptr(idx1)\n        if need_obuf:\n            out_data = obuf\n        else:\n            out_data = q\n\n        raw_rfft(in_data, N, True, norm, out_data)\n\n        if need_obuf:\n            qb = q.as_byte()\n            for i in range(M):\n                Ptr[ans.dtype](qb)[0] = out_data[i]\n                qb += ostride\n\n    if need_ibuf:\n        free(ibuf)\n    if need_obuf:\n        free(obuf)\n\n    return ans\n\ndef irfft(a, n: Optional[int] = None,\n          axis: int = -1, norm: Optional[str] = None,\n          out = None):\n    a = asarray(a)\n    T = type(_complex_dtype(a.dtype))\n    T0 = type(T().real)\n    axis = normalize_axis_index(axis, a.ndim)\n    istride = a.strides[axis]\n\n    m = a.shape[axis]\n    N = m\n    M = (m - 1) << 1\n    if n is not None:\n        N = (n >> 1) + 1\n        M = n\n\n    out_shape = tuple_set(a.shape, axis, M)\n\n    need_ibuf = (a.dtype is not T or\n                 istride != sizeof(T) or\n                 N != m)\n\n    if out is None:\n        ans = empty(out_shape, T0)\n    else:\n        _check_out(out, out_shape)\n        if out.dtype is not T0:\n            compile_error(\"'out' dtype must be float\")\n        ans = out\n\n    ostride = ans.strides[axis]\n    need_obuf = (ans.strides[axis] != sizeof(ans.dtype))\n    ibuf = Ptr[T](N) if need_ibuf else Ptr[T]()\n    obuf = Ptr[T0](M) if need_obuf else Ptr[T0]()\n\n    for idx in multirange(tuple_delete(a.shape, axis)):\n        idx1 = tuple_insert(idx, axis, 0)\n        p = a._ptr(idx1)\n        if need_ibuf:\n            pb = p.as_byte()\n            for i in range(min(m, N)):\n                ibuf[i] = cast(Ptr[a.dtype](pb)[0], T)\n                pb += istride\n            for i in range(m, N):\n                ibuf[i] = T(0.0, 0.0)\n            in_data = ibuf\n        else:\n            in_data = Ptr[T](p.as_byte())\n\n        q = ans._ptr(idx1)\n        if need_obuf:\n            out_data = obuf\n        else:\n            out_data = q\n\n        raw_irfft(in_data, M, norm, out_data)\n\n        if need_obuf:\n            qb = q.as_byte()\n            for i in range(M):\n                Ptr[ans.dtype](qb)[0] = out_data[i]\n                qb += ostride\n\n    if need_ibuf:\n        free(ibuf)\n    if need_obuf:\n        free(obuf)\n\n    return ans\n\ndef hfft(a, n: Optional[int] = None,\n         axis: int = -1, norm: Optional[str] = None,\n         out = None):\n    a = asarray(a)\n    n1 = 0\n    if n is None:\n        n1 = (a.shape[axis] - 1) * 2\n    else:\n        n1 = n\n    new_norm = _swap_direction(norm)\n    output = irfft(a.conj(), n1, axis, norm=new_norm, out=None)\n    return output\n\ndef ihfft(a, n: Optional[int] = None,\n          axis: int = -1, norm: Optional[str] = None,\n          out = None):\n    a = asarray(a)\n    n1 = 0\n    if n is None:\n        n1 = a.shape[axis]\n    else:\n        n1 = n\n    new_norm = _swap_direction(norm)\n    out = rfft(a, n1, axis, norm=new_norm, out=out)\n    out.map(lambda x: x.conjugate(), inplace=True)\n    return out\n\ndef _cook_nd_args(a, s = None, axes = None, invreal: Literal[bool] = False):\n    shapeless: Literal[bool] = (s is None)\n\n    if s is None:\n        if axes is None:\n            s1 = a.shape\n        else:\n            s1 = tuple(a.shape[i] for i in axes)\n    else:\n        s1 = s\n\n    if axes is None:\n        if not shapeless:\n            pass  # warning\n        r = tuple_range(static.len(s1))[::-1]\n        axes1 = tuple(-i - 1 for i in r)\n    else:\n        axes1 = axes\n\n    if static.len(s1) != static.len(axes1):\n        compile_error(\"Shape and axes have different lengths.\")\n\n    if invreal and shapeless:\n        s1 = tuple_set(s1, static.len(s1) - 1, (a.shape[axes1[-1]] - 1) * 2)\n\n    s2 = tuple(a.shape[axes1[i]] if s1[i] == -1 else s1[i] for i in static.range(static.len(s1)))\n    return s2, axes1\n\ndef _raw_fftnd(a, s = None, axes = None, function = fft, norm: Optional[str] = None, out = None):\n    a = asarray(a)\n    s, axes = _cook_nd_args(a, s, axes)\n    if static.len(axes) == 0:\n        return a\n    i = len(axes) - 1\n    a = function(a, n=s[i], axis=axes[i], norm=norm, out=out)\n    for i in range(len(axes) - 2, -1, -1):\n        a = function(a, n=s[i], axis=axes[i], norm=norm, out=out)\n    return a\n\ndef fftn(a, s=None, axes=None, norm: Optional[str] = None, out = None):\n    return _raw_fftnd(a, s, axes, fft, norm, out=out)\n\ndef ifftn(a, s=None, axes=None, norm: Optional[str] = None, out = None):\n    return _raw_fftnd(a, s, axes, ifft, norm, out=out)\n\ndef fft2(a, s=None, axes=(-2, -1), norm: Optional[str] = None, out = None):\n    return _raw_fftnd(a, s, axes, fft, norm, out=out)\n\ndef ifft2(a, s=None, axes=(-2, -1), norm: Optional[str] = None, out = None):\n    return _raw_fftnd(a, s, axes, ifft, norm, out=out)\n\ndef rfftn(a, s=None, axes=None, norm: Optional[str] = None, out = None):\n    a = asarray(a)\n    s, axes = _cook_nd_args(a, s, axes)\n    a = rfft(a, s[-1], axes[-1], norm, out=out)\n    for i in range(len(axes) - 1):\n        a = fft(a, s[i], axes[i], norm, out=out)\n    return a\n\ndef rfft2(a, s=None, axes=(-2, -1), norm: Optional[str] = None, out = None):\n    return rfftn(a, s, axes, norm, out=out)\n\ndef irfftn(a, s=None, axes=None, norm: Optional[str] = None, out = None):\n    a = asarray(a)\n    s, axes = _cook_nd_args(a, s, axes, invreal=True)\n    if static.len(axes) <= 1:\n        return irfft(a, s[-1], axes[-1], norm, out=out)\n    b = ifft(a, s[0], axes[0], norm)\n    for i in range(1, len(axes) - 1):\n        b = ifft(b, s[i], axes[i], norm)\n    return irfft(b, s[-1], axes[-1], norm, out=out)\n\ndef irfft2(a, s=None, axes=(-2, -1), norm: Optional[str] = None, out = None):\n    return irfftn(a, s, axes, norm, out=None)\n\n\n# Helpers\n\ndef fftshift(x, axes=None):\n    x = asarray(x)\n    if axes is None:\n        axes1 = tuple_range(x.ndim)\n        shift = tuple(dim // 2 for dim in x.shape)\n    elif isinstance(axes, int):\n        axes1 = axes\n        shift = x.shape[axes] // 2\n    else:\n        axes1 = axes\n        shift = tuple(x.shape[ax] // 2 for ax in axes)\n\n    return roll(x, shift, axes1)\n\ndef ifftshift(x, axes=None):\n    x = asarray(x)\n    if axes is None:\n        axes1 = tuple_range(x.ndim)\n        shift = tuple(-(dim // 2) for dim in x.shape)\n    elif isinstance(axes, int):\n        axes1 = axes\n        shift = -(x.shape[axes] // 2)\n    else:\n        axes1 = axes\n        shift = tuple(-(x.shape[ax] // 2) for ax in axes)\n\n    return roll(x, shift, axes1)\n\ndef fftfreq(n: int, d: float = 1.0):\n    val = 1.0 / (n * d)\n    results = empty(n, int)\n    N = (n-1)//2 + 1\n    p1 = arange(0, N, dtype=int)\n    results[:N] = p1\n    p2 = arange(-(n//2), 0, dtype=int)\n    results[N:] = p2\n    return results * val\n\ndef rfftfreq(n: int, d: float = 1.0):\n    val = 1.0/(n*d)\n    N = n//2 + 1\n    results = arange(0, N, dtype=int)\n    return results * val\n"
  },
  {
    "path": "stdlib/numpy/fft/pocketfft.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n# Adapted from PocketFFT - https://github.com/mreineck/pocketfft\n\nfrom ..util import free, sqrt, sin, cos, cast, count, sizeof, PI\n\ndef _special_mul(v1, v2, fwd: bool):\n    if type(v1) is complex and type(v2) is complex64:\n        return _special_mul(v1, complex(v2), fwd)\n\n    if type(v1) is complex64 and type(v2) is complex:\n        return _special_mul(complex(v1), v2, fwd)\n\n    T = type(v1)\n    if fwd:\n        return T(\n            v1.real * v2.real + v1.imag * v2.imag,\n            v1.imag * v2.real - v1.real * v2.imag\n        )\n    else:\n        return T(\n            v1.real * v2.real - v1.imag * v2.imag,\n            v1.real * v2.imag + v1.imag * v2.real\n        )\n\ndef ROT90(a):\n    T = type(a)\n    return T(-a.imag, a.real)\n\ndef ROTX90(a, fwd: bool):\n    T = type(a)\n    if fwd:\n        return T(a.imag, -a.real)\n    else:\n        return T(-a.imag, a.real)\n\ndef ROTX45(a, fwd: bool):\n    T = type(a)\n    T0 = type(a.real)\n    hsqt2 = cast(0.707106781186547524400844362104849, T0)\n    if fwd:\n        return T(hsqt2*(a.real+a.imag), hsqt2*(a.imag-a.real))\n    else:\n        return T(hsqt2*(a.real-a.imag), hsqt2*(a.imag+a.real))\n\ndef ROTX135(a, fwd: bool):\n    T = type(a)\n    T0 = type(a.real)\n    hsqt2 = cast(0.707106781186547524400844362104849, T0)\n    if fwd:\n        return T(hsqt2*(a.imag-a.real), hsqt2*(-a.real-a.imag))\n    else:\n        return T(hsqt2*(-a.real-a.imag), hsqt2*(a.real-a.imag))\n\n@tuple\nclass arr:\n    p: Ptr[T]\n    sz: int\n    T: type\n\n    def __new__(sz: int) -> arr[T]:\n        return arr(Ptr[T](sz), sz)\n\n    def dealloc(self):\n        free(self.p)\n\n    def __getitem__(self, idx: int):\n        return self.p[idx]\n\n    def __setitem__(self, idx: int, val: T):\n        self.p[idx] = val\n\n    def size(self):\n        return self.sz\n\n    def data(self):\n        return self.p\n\n@tuple\nclass sincos_2pibyn:\n    N: int\n    mask: int\n    shift: int\n    v1: arr[complex]\n    v2: arr[complex]\n    T: type\n\n    def calc(x: int, n: int, ang: float):\n        x <<= 3\n        if x < 4*n:\n            if x < 2*n:\n                if x < n:\n                    y = cast(x, float) * ang\n                    return complex(cos(y), sin(y))\n                else:\n                    y = cast(2*n - x, float) * ang\n                    return complex(sin(y), cos(y))\n            else:\n                x -= 2*n\n                if x < n:\n                    y = cast(x, float) * ang\n                    return complex(-sin(y), cos(y))\n                else:\n                    y = cast(2*n - x, float) * ang\n                    return complex(-cos(y), sin(y))\n        else:\n            x = 8*n - x\n            if x < 2*n:\n                if x < n:\n                    y = cast(x, float) * ang\n                    return complex(cos(y), -sin(y))\n                else:\n                    y = cast(2*n - x, float) * ang\n                    return complex(sin(y), -cos(y))\n            else:\n                x -= 2*n\n                if x < n:\n                    y = cast(x, float) * ang\n                    return complex(-sin(y), -cos(y))\n                else:\n                    y = cast(2*n - x, float) * ang\n                    return complex(-cos(y), -sin(y))\n\n    def __new__(n: int) -> sincos_2pibyn[T]:\n        ang = 0.25 * PI / n\n        nval = (n + 2) >> 1\n        shift = 1\n        while (1 << shift) * (1 << shift) < nval:\n            shift += 1\n        mask = (1 << shift) - 1\n\n        v1 = arr[complex](mask + 1)\n        v1[0] = complex(1, 0)\n        for i in range(1, v1.size()):\n            v1[i] = sincos_2pibyn.calc(i, n, ang)\n\n        v2 = arr[complex]((nval + mask) // (mask + 1))\n        v2[0] = complex(1, 0)\n        for i in range(1, v2.size()):\n            v2[i] = sincos_2pibyn.calc(i*(mask+1), n, ang)\n\n        return sincos_2pibyn[T](n, mask, shift, v1, v2)\n\n    def cmplx(self, re, im):\n        if T is float:\n            return complex(cast(re, float), cast(im, float))\n        elif T is float32:\n            return complex64(cast(re, float32), cast(im, float32))\n        else:\n            compile_error(\"[internal error] bad float type\")\n\n    def __getitem__(self, idx: int):\n        if 2 * idx <= self.N:\n            x1 = self.v1[idx & self.mask]\n            x2 = self.v2[idx >> self.shift]\n            return self.cmplx(x1.real * x2.real - x1.imag * x2.imag,\n                              x1.real * x2.imag + x1.imag * x2.real)\n        else:\n            idx = self.N - idx\n            x1 = self.v1[idx & self.mask]\n            x2 = self.v2[idx >> self.shift]\n            return self.cmplx(x1.real * x2.real - x1.imag * x2.imag,\n                              -(x1.real * x2.imag + x1.imag * x2.real))\n\n    def dealloc(self):\n        self.v1.dealloc()\n        self.v2.dealloc()\n\ndef _largest_prime_factor(n: int):\n    res = 1\n    while n & 1 == 0:\n        res = 2\n        n >>= 1\n\n    x = 3\n    while x * x <= n:\n        while n % x == 0:\n            res = x\n            n //= x\n        x += 2\n\n    if n > 1:\n        res = n\n    return res\n\ndef _cost_guess(n: int):\n    lfp = 1.1\n    ni = n\n    result = 0.0\n\n    while n & 1 == 0:\n        result += 2\n        n >>= 1\n\n    x = 3\n    while x * x <= n:\n        while n % x == 0:\n            result += float(x) if x <= 5 else lfp*float(x)\n            n //= x\n        x += 2\n\n    if n > 1:\n        result += float(n) if n <= 5 else lfp*float(n)\n    return result*float(ni)\n\ndef _good_size_cmplx(n: int):\n    if n <= 12:\n        return n\n    bestfac = 2*n\n    f11 = 1\n    while f11 < bestfac:\n        f117 = f11\n        while f117 < bestfac:\n            f1175 = f117\n            while f1175 < bestfac:\n                x = f1175\n                while x < n:\n                    x *= 2\n                while True:\n                    if x < n:\n                        x *= 3\n                    elif x > n:\n                        if x < bestfac:\n                            bestfac = x\n                        if x & 1:\n                            break\n                        x >>= 1\n                    else:\n                        return n\n                f1175 *= 5\n            f117 *= 7\n        f11 *= 11\n    return bestfac\n\ndef _good_size_real(n: int):\n    if n <= 6:\n        return n\n    bestfac = 2*n\n    f5 = 1\n    while f5 < bestfac:\n        x = f5\n        while x < n:\n            x *= 2\n        while True:\n            if x < n:\n                x *= 3\n            elif x > n:\n                if x < bestfac:\n                    bestfac = x\n                if x & 1:\n                    break\n                x >>= 1\n            else:\n                return n\n        f5 *= 5\n    return bestfac\n\ndef _prev_good_size_cmplx(n: int):\n    if n <= 12:\n        return n\n    bestfound = 1\n    f11 = 1\n    while f11 <= n:\n        f117 = f11\n        while f117 <= n:\n            f1175 = f117\n            while f1175 <= n:\n                x = f1175\n                while x*2 <= n:\n                    x *= 2\n                if x > bestfound:\n                    bestfound = x\n                while True:\n                    if x * 3 <= n:\n                        x *= 3\n                    elif x % 2 == 0:\n                        x //= 2\n                    else:\n                        break\n\n                    if x > bestfound:\n                        bestfound = x\n                f1175 *= 5\n            f117 *= 7\n        f11 *= 11\n    return bestfound\n\ndef _prev_good_size_real(n: int):\n    if n <= 6:\n        return n\n    bestfound = 1\n    f5 = 1\n    while f5 <= n:\n        x = f5\n        while x*2 <= n:\n            x *= 2\n        if x > bestfound:\n            bestfound = x\n        while True:\n            if x * 3 <= n:\n                x *= 3\n            elif x % 2 == 0:\n                x //= 2\n            else:\n                break\n\n            if x > bestfound:\n                bestfound = x\n        f5 *= 5\n    return bestfound\n\ndef PM(a, b):\n    return a + b, a - b\n\ndef MP(a, b):\n    return a - b, a + b\n\n@tuple\nclass fctdata:\n    fct: int\n    tw: Ptr[T]\n    tws: Ptr[T]\n    T: type\n\n    def with_fct(self, fct: int):\n        return fctdata[T](fct, self.tw, self.tws)\n\n    def with_tw(self, tw: Ptr[T]):\n        return fctdata[T](self.fct, tw, self.tws)\n\n    def with_tws(self, tws: Ptr[T]):\n        return fctdata[T](self.fct, self.tw, tws)\n\nclass cfftp:\n    length: int\n    mem: arr[T]\n    fact: List[fctdata[T]]\n    T: type   # complex datatype\n    T0: type  # real datatype\n\n    def add_factor(self, factor: int):\n        self.fact.append(fctdata(factor, Ptr[T](), Ptr[T]()))\n\n    def pass2(ido: int, l1: int, cc: Ptr[T], ch: Ptr[T], wa: Ptr[T], fwd: bool):\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+l1*c))\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+2*c)]\n        @inline\n        def WA(x: int, i: int):\n            return wa[i-1+x*(ido-1)]\n\n        if ido == 1:\n            for k in range(l1):\n                CH(0,k,0)[0] = CC(0,0,k)+CC(0,1,k)\n                CH(0,k,1)[0] = CC(0,0,k)-CC(0,1,k)\n        else:\n            for k in range(l1):\n                CH(0,k,0)[0] = CC(0,0,k)+CC(0,1,k)\n                CH(0,k,1)[0] = CC(0,0,k)-CC(0,1,k)\n                for i in range(1, ido):\n                    CH(i,k,0)[0] = CC(i,0,k)+CC(i,1,k)\n                    CH(i,k,1)[0] = _special_mul(CC(i,0,k)-CC(i,1,k), WA(0,i), fwd)\n\n    def pass3(ido: int, l1: int, cc: Ptr[T], ch: Ptr[T], wa: Ptr[T], fwd: bool):\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+l1*c))\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+3*c)]\n        @inline\n        def WA(x: int, i: int):\n            return wa[i-1+x*(ido-1)]\n\n        tw1r = cast(-0.5, T0)\n        tw1i = cast(0.8660254037844386467637231707529362, T0)\n        if fwd:\n            tw1i = -tw1i\n\n        if ido == 1:\n            for k in range(l1):\n                # POCKETFFT_PREP3(0)\n                t0 = CC(0,0,k)\n                t1, t2 = PM(CC(0,1,k), CC(0,2,k))\n                CH(0,k,0)[0] = t0 + t1\n                # POCKETFFT_PARTSTEP3a(1,2,tw1r,tw1i)\n                ca = t0 + t1*tw1r\n                cb = T(-t2.imag*tw1i, t2.real*tw1i)\n                CH(0,k,1)[0], CH(0,k,2)[0] = PM(ca, cb)\n        else:\n            for k in range(l1):\n                # POCKETFFT_PREP3(0)\n                t0 = CC(0,0,k)\n                t1, t2 = PM(CC(0,1,k), CC(0,2,k))\n                CH(0,k,0)[0] = t0 + t1\n                # POCKETFFT_PARTSTEP3a(1,2,tw1r,tw1i)\n                ca = t0 + t1*tw1r\n                cb = T(-t2.imag*tw1i, t2.real*tw1i)\n                CH(0,k,1)[0], CH(0,k,2)[0] = PM(ca, cb)\n\n                for i in range(1, ido):\n                    # POCKETFFT_PREP3(i)\n                    t0 = CC(i,0,k)\n                    t1, t2 = PM(CC(i,1,k), CC(i,2,k))\n                    CH(i,k,0)[0] = t0 + t1\n                    # POCKETFFT_PARTSTEP3b(1,2,tw1r,tw1i)\n                    ca = t0 + t1*tw1r\n                    cb = T(-t2.imag*tw1i, t2.real*tw1i)\n                    CH(i,k,1)[0] = _special_mul(ca+cb, WA(1-1,i), fwd)\n                    CH(i,k,2)[0] = _special_mul(ca-cb, WA(2-1,i), fwd)\n\n    def pass4(ido: int, l1: int, cc: Ptr[T], ch: Ptr[T], wa: Ptr[T], fwd: bool):\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+l1*c))\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+4*c)]\n        @inline\n        def WA(x: int, i: int):\n            return wa[i-1+x*(ido-1)]\n\n        if ido == 1:\n            for k in range(l1):\n                t2, t1 = PM(CC(0,0,k), CC(0,2,k))\n                t3, t4 = PM(CC(0,1,k), CC(0,3,k))\n                t4 = ROTX90(t4, fwd)\n                CH(0,k,0)[0], CH(0,k,2)[0] = PM(t2, t3)\n                CH(0,k,1)[0], CH(0,k,3)[0] = PM(t1, t4)\n        else:\n            for k in range(l1):\n                t2, t1 = PM(CC(0,0,k), CC(0,2,k))\n                t3, t4 = PM(CC(0,1,k), CC(0,3,k))\n                t4 = ROTX90(t4, fwd)\n                CH(0,k,0)[0], CH(0,k,2)[0] = PM(t2, t3)\n                CH(0,k,1)[0], CH(0,k,3)[0] = PM(t1, t4)\n\n                for i in range(1, ido):\n                    cc0 = CC(i,0,k)\n                    cc1 = CC(i,1,k)\n                    cc2 = CC(i,2,k)\n                    cc3 = CC(i,3,k)\n                    t2, t1 = PM(cc0, cc2)\n                    t3, t4 = PM(cc1, cc3)\n                    t4 = ROTX90(t4, fwd)\n                    CH(i,k,0)[0] = t2 + t3\n                    CH(i,k,1)[0] = _special_mul(t1+t4, WA(0,i), fwd)\n                    CH(i,k,2)[0] = _special_mul(t2-t3, WA(1,i), fwd)\n                    CH(i,k,3)[0] = _special_mul(t1-t4, WA(2,i), fwd)\n\n    def pass5(ido: int, l1: int, cc: Ptr[T], ch: Ptr[T], wa: Ptr[T], fwd: bool):\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+l1*c))\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+5*c)]\n        @inline\n        def WA(x: int, i: int):\n            return wa[i-1+x*(ido-1)]\n\n        tw1r = cast(0.3090169943749474241022934171828191, T0)\n        tw1i = cast(0.9510565162951535721164393333793821, T0)\n        if fwd:\n            tw1i = -tw1i\n        tw2r = cast(-0.8090169943749474241022934171828191, T0)\n        tw2i = cast(0.5877852522924731291687059546390728, T0)\n        if fwd:\n            tw2i = -tw2i\n\n        if ido == 1:\n            for k in range(l1):\n                # POCKETFFT_PREP5(0)\n                t0 = CC(0,0,k)\n                t1, t4 = PM(CC(0,1,k), CC(0,4,k))\n                t2, t3 = PM(CC(0,2,k), CC(0,3,k))\n                CH(0,k,0)[0] = T(t0.real+t1.real+t2.real, t0.imag+t1.imag+t2.imag)\n                # POCKETFFT_PARTSTEP5a(1,4,tw1r,tw2r,+tw1i,+tw2i)\n                ca = T(t0.real+tw1r*t1.real+tw2r*t2.real,\n                       t0.imag+tw1r*t1.imag+tw2r*t2.imag)\n                cb = T(-(tw1i*t4.imag+tw2i*t3.imag),\n                       tw1i*t4.real+tw2i*t3.real)\n                CH(0,k,1)[0], CH(0,k,4)[0] = PM(ca, cb)\n                # POCKETFFT_PARTSTEP5a(2,3,tw2r,tw1r,+tw2i,-tw1i)\n                ca = T(t0.real+tw2r*t1.real+tw1r*t2.real,\n                       t0.imag+tw2r*t1.imag+tw1r*t2.imag)\n                cb = T(-(tw2i*t4.imag-tw1i*t3.imag),\n                       tw2i*t4.real-tw1i*t3.real)\n                CH(0,k,2)[0], CH(0,k,3)[0] = PM(ca, cb)\n        else:\n            for k in range(l1):\n                # POCKETFFT_PREP5(0)\n                t0 = CC(0,0,k)\n                t1, t4 = PM(CC(0,1,k), CC(0,4,k))\n                t2, t3 = PM(CC(0,2,k), CC(0,3,k))\n                CH(0,k,0)[0] = T(t0.real+t1.real+t2.real, t0.imag+t1.imag+t2.imag)\n                # POCKETFFT_PARTSTEP5a(1,4,tw1r,tw2r,+tw1i,+tw2i)\n                ca = T(t0.real+tw1r*t1.real+tw2r*t2.real,\n                       t0.imag+tw1r*t1.imag+tw2r*t2.imag)\n                cb = T(-(tw1i*t4.imag+tw2i*t3.imag),\n                       tw1i*t4.real+tw2i*t3.real)\n                CH(0,k,1)[0], CH(0,k,4)[0] = PM(ca, cb)\n                # POCKETFFT_PARTSTEP5a(2,3,tw2r,tw1r,+tw2i,-tw1i)\n                ca = T(t0.real+tw2r*t1.real+tw1r*t2.real,\n                       t0.imag+tw2r*t1.imag+tw1r*t2.imag)\n                cb = T(-(tw2i*t4.imag-tw1i*t3.imag),\n                       tw2i*t4.real-tw1i*t3.real)\n                CH(0,k,2)[0], CH(0,k,3)[0] = PM(ca, cb)\n                for i in range(1, ido):\n                    # POCKETFFT_PREP5(i)\n                    t0 = CC(i,0,k)\n                    t1, t4 = PM(CC(i,1,k), CC(i,4,k))\n                    t2, t3 = PM(CC(i,2,k), CC(i,3,k))\n                    CH(i,k,0)[0] = T(t0.real+t1.real+t2.real, t0.imag+t1.imag+t2.imag)\n                    # POCKETFFT_PARTSTEP5b(1,4,tw1r,tw2r,+tw1i,+tw2i)\n                    ca = T(t0.real+tw1r*t1.real+tw2r*t2.real,\n                           t0.imag+tw1r*t1.imag+tw2r*t2.imag)\n                    cb = T(-(tw1i*t4.imag+tw2i*t3.imag),\n                           tw1i*t4.real+tw2i*t3.real)\n                    CH(i,k,1)[0] = _special_mul(ca+cb, WA(1-1,i), fwd)\n                    CH(i,k,4)[0] = _special_mul(ca-cb, WA(4-1,i), fwd)\n                    # POCKETFFT_PARTSTEP5b(2,3,tw2r,tw1r,+tw2i,-tw1i)\n                    ca = T(t0.real+tw2r*t1.real+tw1r*t2.real,\n                           t0.imag+tw2r*t1.imag+tw1r*t2.imag)\n                    cb = T(-(tw2i*t4.imag-tw1i*t3.imag),\n                           tw2i*t4.real-tw1i*t3.real)\n                    CH(i,k,2)[0] = _special_mul(ca+cb, WA(2-1,i), fwd)\n                    CH(i,k,3)[0] = _special_mul(ca-cb, WA(3-1,i), fwd)\n\n    def pass7(ido: int, l1: int, cc: Ptr[T], ch: Ptr[T], wa: Ptr[T], fwd: bool):\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+l1*c))\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+7*c)]\n        @inline\n        def WA(x: int, i: int):\n            return wa[i-1+x*(ido-1)]\n\n        tw1r = cast(0.6234898018587335305250048840042398, T0)\n        tw1i = cast(0.7818314824680298087084445266740578, T0)\n        if fwd:\n            tw1i = -tw1i\n        tw2r = cast(-0.2225209339563144042889025644967948, T0)\n        tw2i = cast(0.9749279121818236070181316829939312, T0)\n        if fwd:\n            tw2i = -tw2i\n        tw3r = cast(-0.9009688679024191262361023195074451, T0)\n        tw3i = cast(0.433883739117558120475768332848359, T0)\n        if fwd:\n            tw3i = -tw3i\n\n        if ido == 1:\n            for k in range(l1):\n                # POCKETFFT_PREP7(0)\n                t1 = CC(0,0,k)\n                t2, t7 = PM(CC(0,1,k), CC(0,6,k))\n                t3, t6 = PM(CC(0,2,k), CC(0,5,k))\n                t4, t5 = PM(CC(0,3,k), CC(0,4,k))\n                CH(0,k,0)[0] = T(t1.real+t2.real+t3.real+t4.real,\n                                 t1.imag+t2.imag+t3.imag+t4.imag)\n                # POCKETFFT_PARTSTEP7a(1,6,tw1r,tw2r,tw3r,+tw1i,+tw2i,+tw3i)\n                ca = T(t1.real+tw1r*t2.real+tw2r*t3.real+tw3r*t4.real,\n                       t1.imag+tw1r*t2.imag+tw2r*t3.imag+tw3r*t4.imag)\n                cb = T(-(tw1i*t7.imag+tw2i*t6.imag+tw3i*t5.imag),\n                       tw1i*t7.real+tw2i*t6.real+tw3i*t5.real)\n                CH(0,k,1)[0], CH(0,k,6)[0] = PM(ca, cb)\n                # POCKETFFT_PARTSTEP7a(2,5,tw2r,tw3r,tw1r,+tw2i,-tw3i,-tw1i)\n                ca = T(t1.real+tw2r*t2.real+tw3r*t3.real+tw1r*t4.real,\n                       t1.imag+tw2r*t2.imag+tw3r*t3.imag+tw1r*t4.imag)\n                cb = T(-(tw2i*t7.imag-tw3i*t6.imag-tw1i*t5.imag),\n                       tw2i*t7.real-tw3i*t6.real-tw1i*t5.real)\n                CH(0,k,2)[0], CH(0,k,5)[0] = PM(ca, cb)\n                # POCKETFFT_PARTSTEP7a(3,4,tw3r,tw1r,tw2r,+tw3i,-tw1i,+tw2i)\n                ca = T(t1.real+tw3r*t2.real+tw1r*t3.real+tw2r*t4.real,\n                       t1.imag+tw3r*t2.imag+tw1r*t3.imag+tw2r*t4.imag)\n                cb = T(-(tw3i*t7.imag-tw1i*t6.imag+tw2i*t5.imag),\n                       tw3i*t7.real-tw1i*t6.real+tw2i*t5.real)\n                CH(0,k,3)[0], CH(0,k,4)[0] = PM(ca, cb)\n        else:\n            for k in range(l1):\n                # POCKETFFT_PREP7(0)\n                t1 = CC(0,0,k)\n                t2, t7 = PM(CC(0,1,k), CC(0,6,k))\n                t3, t6 = PM(CC(0,2,k), CC(0,5,k))\n                t4, t5 = PM(CC(0,3,k), CC(0,4,k))\n                CH(0,k,0)[0] = T(t1.real+t2.real+t3.real+t4.real,\n                                 t1.imag+t2.imag+t3.imag+t4.imag)\n                # POCKETFFT_PARTSTEP7a(1,6,tw1r,tw2r,tw3r,+tw1i,+tw2i,+tw3i)\n                ca = T(t1.real+tw1r*t2.real+tw2r*t3.real+tw3r*t4.real,\n                       t1.imag+tw1r*t2.imag+tw2r*t3.imag+tw3r*t4.imag)\n                cb = T(-(tw1i*t7.imag+tw2i*t6.imag+tw3i*t5.imag),\n                       tw1i*t7.real+tw2i*t6.real+tw3i*t5.real)\n                CH(0,k,1)[0], CH(0,k,6)[0] = PM(ca, cb)\n                # POCKETFFT_PARTSTEP7a(2,5,tw2r,tw3r,tw1r,+tw2i,-tw3i,-tw1i)\n                ca = T(t1.real+tw2r*t2.real+tw3r*t3.real+tw1r*t4.real,\n                       t1.imag+tw2r*t2.imag+tw3r*t3.imag+tw1r*t4.imag)\n                cb = T(-(tw2i*t7.imag-tw3i*t6.imag-tw1i*t5.imag),\n                       tw2i*t7.real-tw3i*t6.real-tw1i*t5.real)\n                CH(0,k,2)[0], CH(0,k,5)[0] = PM(ca, cb)\n                # POCKETFFT_PARTSTEP7a(3,4,tw3r,tw1r,tw2r,+tw3i,-tw1i,+tw2i)\n                ca = T(t1.real+tw3r*t2.real+tw1r*t3.real+tw2r*t4.real,\n                       t1.imag+tw3r*t2.imag+tw1r*t3.imag+tw2r*t4.imag)\n                cb = T(-(tw3i*t7.imag-tw1i*t6.imag+tw2i*t5.imag),\n                       tw3i*t7.real-tw1i*t6.real+tw2i*t5.real)\n                CH(0,k,3)[0], CH(0,k,4)[0] = PM(ca, cb)\n                for i in range(1, ido):\n                    # POCKETFFT_PREP7(i)\n                    t1 = CC(i,0,k)\n                    t2, t7 = PM(CC(i,1,k), CC(i,6,k))\n                    t3, t6 = PM(CC(i,2,k), CC(i,5,k))\n                    t4, t5 = PM(CC(i,3,k), CC(i,4,k))\n                    CH(i,k,0)[0] = T(t1.real+t2.real+t3.real+t4.real,\n                                     t1.imag+t2.imag+t3.imag+t4.imag)\n                    # POCKETFFT_PARTSTEP7(1,6,tw1r,tw2r,tw3r,+tw1i,+tw2i,+tw3i)\n                    ca = T(t1.real+tw1r*t2.real+tw2r*t3.real+tw3r*t4.real,\n                           t1.imag+tw1r*t2.imag+tw2r*t3.imag+tw3r*t4.imag)\n                    cb = T(-(tw1i*t7.imag+tw2i*t6.imag+tw3i*t5.imag),\n                           tw1i*t7.real+tw2i*t6.real+tw3i*t5.real)\n                    da, db = PM(ca, cb)\n                    CH(i,k,1)[0] = _special_mul(da, WA(1-1,i), fwd)\n                    CH(i,k,6)[0] = _special_mul(db, WA(6-1,i), fwd)\n                    # POCKETFFT_PARTSTEP7(2,5,tw2r,tw3r,tw1r,+tw2i,-tw3i,-tw1i)\n                    ca = T(t1.real+tw2r*t2.real+tw3r*t3.real+tw1r*t4.real,\n                           t1.imag+tw2r*t2.imag+tw3r*t3.imag+tw1r*t4.imag)\n                    cb = T(-(tw2i*t7.imag-tw3i*t6.imag-tw1i*t5.imag),\n                           tw2i*t7.real-tw3i*t6.real-tw1i*t5.real)\n                    da, db = PM(ca, cb)\n                    CH(i,k,2)[0] = _special_mul(da, WA(2-1,i), fwd)\n                    CH(i,k,5)[0] = _special_mul(db, WA(5-1,i), fwd)\n                    # POCKETFFT_PARTSTEP7(3,4,tw3r,tw1r,tw2r,+tw3i,-tw1i,+tw2i)\n                    ca = T(t1.real+tw3r*t2.real+tw1r*t3.real+tw2r*t4.real,\n                           t1.imag+tw3r*t2.imag+tw1r*t3.imag+tw2r*t4.imag)\n                    cb = T(-(tw3i*t7.imag-tw1i*t6.imag+tw2i*t5.imag),\n                           tw3i*t7.real-tw1i*t6.real+tw2i*t5.real)\n                    da, db = PM(ca, cb)\n                    CH(i,k,3)[0] = _special_mul(da, WA(3-1,i), fwd)\n                    CH(i,k,4)[0] = _special_mul(db, WA(4-1,i), fwd)\n\n    def pass8(ido: int, l1: int, cc: Ptr[T], ch: Ptr[T], wa: Ptr[T], fwd: bool):\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+l1*c))\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+8*c)]\n        @inline\n        def WA(x: int, i: int):\n            return wa[i-1+x*(ido-1)]\n\n        if ido == 1:\n            for k in range(l1):\n                a1, a5 = PM(CC(0,1,k), CC(0,5,k))\n                a3, a7 = PM(CC(0,3,k), CC(0,7,k))\n                a1, a3 = PM(a1, a3)\n                a3 = ROTX90(a3, fwd)\n\n                a7 = ROTX90(a7, fwd)\n                a5, a7 = PM(a5, a7)\n                a5 = ROTX45(a5, fwd)\n                a7 = ROTX135(a7, fwd)\n\n                a0, a4 = PM(CC(0,0,k), CC(0,4,k))\n                a2, a6 = PM(CC(0,2,k), CC(0,6,k))\n                CH(0,k,0)[0], CH(0,k,4)[0] = PM(a0+a2, a1)\n                CH(0,k,2)[0], CH(0,k,6)[0] = PM(a0-a2, a3)\n                a6 = ROTX90(a6, fwd)\n                CH(0,k,1)[0], CH(0,k,5)[0] = PM(a4+a6, a5)\n                CH(0,k,3)[0], CH(0,k,7)[0] = PM(a4-a6, a7)\n        else:\n            for k in range(l1):\n                a1, a5 = PM(CC(0,1,k), CC(0,5,k))\n                a3, a7 = PM(CC(0,3,k), CC(0,7,k))\n                a1, a3 = PM(a1, a3)\n                a3 = ROTX90(a3, fwd)\n\n                a7 = ROTX90(a7, fwd)\n                a5, a7 = PM(a5, a7)\n                a5 = ROTX45(a5, fwd)\n                a7 = ROTX135(a7, fwd)\n\n                a0, a4 = PM(CC(0,0,k), CC(0,4,k))\n                a2, a6 = PM(CC(0,2,k), CC(0,6,k))\n                CH(0,k,0)[0], CH(0,k,4)[0] = PM(a0+a2, a1)\n                CH(0,k,2)[0], CH(0,k,6)[0] = PM(a0-a2, a3)\n                a6 = ROTX90(a6, fwd)\n                CH(0,k,1)[0], CH(0,k,5)[0] = PM(a4+a6, a5)\n                CH(0,k,3)[0], CH(0,k,7)[0] = PM(a4-a6, a7)\n\n                for i in range(1, ido):\n                    a1, a5 = PM(CC(i,1,k), CC(i,5,k))\n                    a3, a7 = PM(CC(i,3,k), CC(i,7,k))\n                    a7 = ROTX90(a7, fwd)\n                    a1, a3 = PM(a1, a3)\n                    a3 = ROTX90(a3, fwd)\n                    a5, a7 = PM(a5, a7)\n                    a5 = ROTX45(a5, fwd)\n                    a7 = ROTX135(a7, fwd)\n                    a0, a4 = PM(CC(i,0,k), CC(i,4,k))\n                    a2, a6 = PM(CC(i,2,k), CC(i,6,k))\n                    a0, a2 = PM(a0, a2)\n                    CH(i,k,0)[0] = a0+a1\n                    CH(i,k,4)[0] = _special_mul(a0-a1, WA(3,i), fwd)\n                    CH(i,k,2)[0] = _special_mul(a2+a3, WA(1,i), fwd)\n                    CH(i,k,6)[0] = _special_mul(a2-a3, WA(5,i), fwd)\n                    a6 = ROTX90(a6, fwd)\n                    a4, a6 = PM(a4, a6)\n                    CH(i,k,1)[0] = _special_mul(a4+a5, WA(0,i), fwd)\n                    CH(i,k,5)[0] = _special_mul(a4-a5, WA(4,i), fwd)\n                    CH(i,k,3)[0] = _special_mul(a6+a7, WA(2,i), fwd)\n                    CH(i,k,7)[0] = _special_mul(a6-a7, WA(6,i), fwd)\n\n    def pass11(ido: int, l1: int, cc: Ptr[T], ch: Ptr[T], wa: Ptr[T], fwd: bool):\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+l1*c))\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+11*c)]\n        @inline\n        def WA(x: int, i: int):\n            return wa[i-1+x*(ido-1)]\n\n        tw1r = cast(0.8412535328311811688618116489193677, T0)\n        tw1i = cast(0.5406408174555975821076359543186917, T0)\n        if fwd:\n            tw1i = -tw1i\n        tw2r = cast(0.4154150130018864255292741492296232, T0)\n        tw2i = cast(0.9096319953545183714117153830790285, T0)\n        if fwd:\n            tw2i = -tw2i\n        tw3r = cast(-0.1423148382732851404437926686163697, T0)\n        tw3i = cast(0.9898214418809327323760920377767188, T0)\n        if fwd:\n            tw3i = -tw3i\n        tw4r = cast(-0.6548607339452850640569250724662936, T0)\n        tw4i = cast(0.7557495743542582837740358439723444, T0)\n        if fwd:\n            tw4i = -tw4i\n        tw5r = cast(-0.9594929736144973898903680570663277, T0)\n        tw5i = cast(0.2817325568414296977114179153466169, T0)\n        if fwd:\n            tw5i = -tw5i\n\n        if ido == 1:\n            for k in range(l1):\n                # POCKETFFT_PREP11(0)\n                t1 = CC(0,0,k)\n                t2, t11 = PM(CC(0,1,k), CC(0,10,k))\n                t3, t10 = PM(CC(0,2,k), CC(0,9,k))\n                t4, t9 = PM(CC(0,3,k), CC(0,8,k))\n                t5, t8 = PM(CC(0,4,k), CC(0,7,k))\n                t6, t7 = PM(CC(0,5,k), CC(0,6,k))\n                CH(0,k,0)[0] = T(t1.real+t2.real+t3.real+t4.real+t5.real+t6.real,\n                                 t1.imag+t2.imag+t3.imag+t4.imag+t5.imag+t6.imag)\n                # POCKETFFT_PARTSTEP11a(1,10,tw1r,tw2r,tw3r,tw4r,tw5r,+tw1i,+tw2i,+tw3i,+tw4i,+tw5i)\n                ca = t1 + t2*tw1r + t3*tw2r + t4*tw3r + t5*tw4r + t6*tw5r\n                cb = T(-(tw1i*t11.imag + tw2i*t10.imag + tw3i*t9.imag + tw4i*t8.imag + tw5i*t7.imag),\n                       tw1i*t11.real + tw2i*t10.real + tw3i*t9.real + tw4i*t8.real + tw5i*t7.real)\n                CH(0,k,1)[0], CH(0,k,10)[0] = PM(ca, cb)\n                # POCKETFFT_PARTSTEP11a(2, 9,tw2r,tw4r,tw5r,tw3r,tw1r,+tw2i,+tw4i,-tw5i,-tw3i,-tw1i)\n                ca = t1 + t2*tw2r + t3*tw4r + t4*tw5r + t5*tw3r + t6*tw1r\n                cb = T(-(tw2i*t11.imag + tw4i*t10.imag - tw5i*t9.imag - tw3i*t8.imag - tw1i*t7.imag),\n                       tw2i*t11.real + tw4i*t10.real - tw5i*t9.real - tw3i*t8.real - tw1i*t7.real)\n                CH(0,k,2)[0], CH(0,k,9)[0] = PM(ca, cb)\n                # POCKETFFT_PARTSTEP11a(3, 8,tw3r,tw5r,tw2r,tw1r,tw4r,+tw3i,-tw5i,-tw2i,+tw1i,+tw4i)\n                ca = t1 + t2*tw3r + t3*tw5r + t4*tw2r + t5*tw1r + t6*tw4r\n                cb = T(-(tw3i*t11.imag - tw5i*t10.imag - tw2i*t9.imag + tw1i*t8.imag + tw4i*t7.imag),\n                       tw3i*t11.real - tw5i*t10.real - tw2i*t9.real + tw1i*t8.real + tw4i*t7.real)\n                CH(0,k,3)[0], CH(0,k,8)[0] = PM(ca, cb)\n                # POCKETFFT_PARTSTEP11a(4, 7,tw4r,tw3r,tw1r,tw5r,tw2r,+tw4i,-tw3i,+tw1i,+tw5i,-tw2i)\n                ca = t1 + t2*tw4r + t3*tw3r + t4*tw1r + t5*tw5r + t6*tw2r\n                cb = T(-(tw4i*t11.imag - tw3i*t10.imag + tw1i*t9.imag + tw5i*t8.imag - tw2i*t7.imag),\n                       tw4i*t11.real - tw3i*t10.real + tw1i*t9.real + tw5i*t8.real - tw2i*t7.real)\n                CH(0,k,4)[0], CH(0,k,7)[0] = PM(ca, cb)\n                # POCKETFFT_PARTSTEP11a(5, 6,tw5r,tw1r,tw4r,tw2r,tw3r,+tw5i,-tw1i,+tw4i,-tw2i,+tw3i)\n                ca = t1 + t2*tw5r + t3*tw1r + t4*tw4r + t5*tw2r + t6*tw3r\n                cb = T(-(tw5i*t11.imag - tw1i*t10.imag + tw4i*t9.imag - tw2i*t8.imag + tw3i*t7.imag),\n                       tw5i*t11.real - tw1i*t10.real + tw4i*t9.real - tw2i*t8.real + tw3i*t7.real)\n                CH(0,k,5)[0], CH(0,k,6)[0] = PM(ca, cb)\n        else:\n            for k in range(l1):\n                # POCKETFFT_PREP11(0)\n                t1 = CC(0,0,k)\n                t2, t11 = PM(CC(0,1,k), CC(0,10,k))\n                t3, t10 = PM(CC(0,2,k), CC(0,9,k))\n                t4, t9 = PM(CC(0,3,k), CC(0,8,k))\n                t5, t8 = PM(CC(0,4,k), CC(0,7,k))\n                t6, t7 = PM(CC(0,5,k), CC(0,6,k))\n                CH(0,k,0)[0] = T(t1.real+t2.real+t3.real+t4.real+t5.real+t6.real,\n                                 t1.imag+t2.imag+t3.imag+t4.imag+t5.imag+t6.imag)\n                # POCKETFFT_PARTSTEP11a(1,10,tw1r,tw2r,tw3r,tw4r,tw5r,+tw1i,+tw2i,+tw3i,+tw4i,+tw5i)\n                ca = t1 + t2*tw1r + t3*tw2r + t4*tw3r + t5*tw4r + t6*tw5r\n                cb = T(-(tw1i*t11.imag + tw2i*t10.imag + tw3i*t9.imag + tw4i*t8.imag + tw5i*t7.imag),\n                       tw1i*t11.real + tw2i*t10.real + tw3i*t9.real + tw4i*t8.real + tw5i*t7.real)\n                CH(0,k,1)[0], CH(0,k,10)[0] = PM(ca, cb)\n                # POCKETFFT_PARTSTEP11a(2, 9,tw2r,tw4r,tw5r,tw3r,tw1r,+tw2i,+tw4i,-tw5i,-tw3i,-tw1i)\n                ca = t1 + t2*tw2r + t3*tw4r + t4*tw5r + t5*tw3r + t6*tw1r\n                cb = T(-(tw2i*t11.imag + tw4i*t10.imag - tw5i*t9.imag - tw3i*t8.imag - tw1i*t7.imag),\n                       tw2i*t11.real + tw4i*t10.real - tw5i*t9.real - tw3i*t8.real - tw1i*t7.real)\n                CH(0,k,2)[0], CH(0,k,9)[0] = PM(ca, cb)\n                # POCKETFFT_PARTSTEP11a(3, 8,tw3r,tw5r,tw2r,tw1r,tw4r,+tw3i,-tw5i,-tw2i,+tw1i,+tw4i)\n                ca = t1 + t2*tw3r + t3*tw5r + t4*tw2r + t5*tw1r + t6*tw4r\n                cb = T(-(tw3i*t11.imag - tw5i*t10.imag - tw2i*t9.imag + tw1i*t8.imag + tw4i*t7.imag),\n                       tw3i*t11.real - tw5i*t10.real - tw2i*t9.real + tw1i*t8.real + tw4i*t7.real)\n                CH(0,k,3)[0], CH(0,k,8)[0] = PM(ca, cb)\n                # POCKETFFT_PARTSTEP11a(4, 7,tw4r,tw3r,tw1r,tw5r,tw2r,+tw4i,-tw3i,+tw1i,+tw5i,-tw2i)\n                ca = t1 + t2*tw4r + t3*tw3r + t4*tw1r + t5*tw5r + t6*tw2r\n                cb = T(-(tw4i*t11.imag - tw3i*t10.imag + tw1i*t9.imag + tw5i*t8.imag - tw2i*t7.imag),\n                       tw4i*t11.real - tw3i*t10.real + tw1i*t9.real + tw5i*t8.real - tw2i*t7.real)\n                CH(0,k,4)[0], CH(0,k,7)[0] = PM(ca, cb)\n                # POCKETFFT_PARTSTEP11a(5, 6,tw5r,tw1r,tw4r,tw2r,tw3r,+tw5i,-tw1i,+tw4i,-tw2i,+tw3i)\n                ca = t1 + t2*tw5r + t3*tw1r + t4*tw4r + t5*tw2r + t6*tw3r\n                cb = T(-(tw5i*t11.imag - tw1i*t10.imag + tw4i*t9.imag - tw2i*t8.imag + tw3i*t7.imag),\n                       tw5i*t11.real - tw1i*t10.real + tw4i*t9.real - tw2i*t8.real + tw3i*t7.real)\n                CH(0,k,5)[0], CH(0,k,6)[0] = PM(ca, cb)\n                for i in range(1, ido):\n                    # POCKETFFT_PREP11(i)\n                    t1 = CC(i,0,k)\n                    t2, t11 = PM(CC(i,1,k), CC(i,10,k))\n                    t3, t10 = PM(CC(i,2,k), CC(i,9,k))\n                    t4, t9 = PM(CC(i,3,k), CC(i,8,k))\n                    t5, t8 = PM(CC(i,4,k), CC(i,7,k))\n                    t6, t7 = PM(CC(i,5,k), CC(i,6,k))\n                    CH(i,k,0)[0] = T(t1.real+t2.real+t3.real+t4.real+t5.real+t6.real,\n                                     t1.imag+t2.imag+t3.imag+t4.imag+t5.imag+t6.imag)\n                    # POCKETFFT_PARTSTEP11(1,10,tw1r,tw2r,tw3r,tw4r,tw5r,+tw1i,+tw2i,+tw3i,+tw4i,+tw5i)\n                    ca = t1 + t2*tw1r + t3*tw2r + t4*tw3r + t5*tw4r + t6*tw5r\n                    cb = T(-(tw1i*t11.imag + tw2i*t10.imag + tw3i*t9.imag + tw4i*t8.imag + tw5i*t7.imag),\n                           tw1i*t11.real + tw2i*t10.real + tw3i*t9.real + tw4i*t8.real + tw5i*t7.real)\n                    da, db = PM(ca, cb)\n                    CH(i,k,1)[0] = _special_mul(da, WA(1-1,i), fwd)\n                    CH(i,k,10)[0] = _special_mul(db, WA(10-1,i), fwd)\n                    # POCKETFFT_PARTSTEP11(2, 9,tw2r,tw4r,tw5r,tw3r,tw1r,+tw2i,+tw4i,-tw5i,-tw3i,-tw1i)\n                    ca = t1 + t2*tw2r + t3*tw4r + t4*tw5r + t5*tw3r + t6*tw1r\n                    cb = T(-(tw2i*t11.imag + tw4i*t10.imag - tw5i*t9.imag - tw3i*t8.imag - tw1i*t7.imag),\n                           tw2i*t11.real + tw4i*t10.real - tw5i*t9.real - tw3i*t8.real - tw1i*t7.real)\n                    da, db = PM(ca, cb)\n                    CH(i,k,2)[0] = _special_mul(da, WA(2-1,i), fwd)\n                    CH(i,k,9)[0] = _special_mul(db, WA(9-1,i), fwd)\n                    # POCKETFFT_PARTSTEP11(3, 8,tw3r,tw5r,tw2r,tw1r,tw4r,+tw3i,-tw5i,-tw2i,+tw1i,+tw4i)\n                    ca = t1 + t2*tw3r + t3*tw5r + t4*tw2r + t5*tw1r + t6*tw4r\n                    cb = T(-(tw3i*t11.imag - tw5i*t10.imag - tw2i*t9.imag + tw1i*t8.imag + tw4i*t7.imag),\n                           tw3i*t11.real - tw5i*t10.real - tw2i*t9.real + tw1i*t8.real + tw4i*t7.real)\n                    da, db = PM(ca, cb)\n                    CH(i,k,3)[0] = _special_mul(da, WA(3-1,i), fwd)\n                    CH(i,k,8)[0] = _special_mul(db, WA(8-1,i), fwd)\n                    # POCKETFFT_PARTSTEP11(4, 7,tw4r,tw3r,tw1r,tw5r,tw2r,+tw4i,-tw3i,+tw1i,+tw5i,-tw2i)\n                    ca = t1 + t2*tw4r + t3*tw3r + t4*tw1r + t5*tw5r + t6*tw2r\n                    cb = T(-(tw4i*t11.imag - tw3i*t10.imag + tw1i*t9.imag + tw5i*t8.imag - tw2i*t7.imag),\n                           tw4i*t11.real - tw3i*t10.real + tw1i*t9.real + tw5i*t8.real - tw2i*t7.real)\n                    da, db = PM(ca, cb)\n                    CH(i,k,4)[0] = _special_mul(da, WA(4-1,i), fwd)\n                    CH(i,k,7)[0] = _special_mul(db, WA(7-1,i), fwd)\n                    # POCKETFFT_PARTSTEP11(5, 6,tw5r,tw1r,tw4r,tw2r,tw3r,+tw5i,-tw1i,+tw4i,-tw2i,+tw3i)\n                    ca = t1 + t2*tw5r + t3*tw1r + t4*tw4r + t5*tw2r + t6*tw3r\n                    cb = T(-(tw5i*t11.imag - tw1i*t10.imag + tw4i*t9.imag - tw2i*t8.imag + tw3i*t7.imag),\n                           tw5i*t11.real - tw1i*t10.real + tw4i*t9.real - tw2i*t8.real + tw3i*t7.real)\n                    da, db = PM(ca, cb)\n                    CH(i,k,5)[0] = _special_mul(da, WA(5-1,i), fwd)\n                    CH(i,k,6)[0] = _special_mul(db, WA(6-1,i), fwd)\n\n    def passg(ido: int, ip: int, l1: int, cc: Ptr[T], ch: Ptr[T], wa: Ptr[T], csarr: Ptr[T], fwd: bool):\n        cdim = ip\n        ipph = (ip + 1) // 2\n        idl1 = ido * l1\n\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+l1*c))\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+cdim*c)]\n        @inline\n        def CX(a: int, b: int, c: int):\n            return cc + (a+ido*(b+l1*c))\n        @inline\n        def CX2(a: int, b: int):\n            return cc + (a+idl1*b)\n        @inline\n        def CH2(a: int, b: int):\n            return ch[a+idl1*b]\n\n        wal = arr[T](ip)\n        wal[0] = T(1., 0.)\n        for i in range(1, ip):\n            wal[i] = T(csarr[i].real, -csarr[i].imag if fwd else csarr[i].imag)\n\n        for k in range(l1):\n            for i in range(ido):\n                CH(i,k,0)[0] = CC(i,0,k)\n        jc = ip - 1\n        for j in range(1, ipph):\n            for k in range(l1):\n                for i in range(ido):\n                    CH(i,k,j)[0], CH(i,k,jc)[0] = PM(CC(i,j,k), CC(i,jc,k))\n            jc -= 1\n\n        for k in range(l1):\n            for i in range(ido):\n                tmp = CH(i,k,0)[0]\n                for j in range(1, ipph):\n                    tmp += CH(i,k,j)[0]\n                CX(i,k,0)[0] = tmp\n\n        lc = ip - 1\n        for l in range(1, ipph):\n            for ik in range(idl1):\n                CX2(ik,l)[0] = T(CH2(ik,0).real+wal[l].real*CH2(ik,1).real+wal[2*l].real*CH2(ik,2).real,\n                                 CH2(ik,0).imag+wal[l].real*CH2(ik,1).imag+wal[2*l].real*CH2(ik,2).imag)\n                CX2(ik,lc)[0] = T(-wal[l].imag*CH2(ik,ip-1).imag-wal[2*l].imag*CH2(ik,ip-2).imag,\n                                  wal[l].imag*CH2(ik,ip-1).real+wal[2*l].imag*CH2(ik,ip-2).real)\n\n            iwal = 2*l\n            j = 3\n            jc = ip - 3\n            while j < ipph - 1:\n                iwal += l\n                if iwal > ip:\n                    iwal -= ip\n                xwal = wal[iwal]\n                iwal += l\n                if iwal > ip:\n                    iwal -= ip\n                xwal2 = wal[iwal]\n\n                for ik in range(idl1):\n                    CX2(ik,l)[0] += T(CH2(ik,j).real*xwal.real+CH2(ik,j+1).real*xwal2.real,\n                                      CH2(ik,j).imag*xwal.real+CH2(ik,j+1).imag*xwal2.real)\n                    CX2(ik,lc)[0] += T(-(CH2(ik,jc).imag*xwal.imag+CH2(ik,jc-1).imag*xwal2.imag),\n                                       CH2(ik,jc).real*xwal.imag+CH2(ik,jc-1).real*xwal2.imag)\n                j += 2\n                jc -= 2\n\n            while j < ipph:\n                iwal += l\n                if iwal > ip:\n                    iwal -= ip\n                xwal = wal[iwal]\n                for ik in range(idl1):\n                    CX2(ik,l)[0] += T(CH2(ik,j).real*xwal.real,\n                                      CH2(ik,j).imag*xwal.real)\n                    CX2(ik,lc)[0] += T(-(CH2(ik,jc).imag*xwal.imag),\n                                       CH2(ik,jc).real*xwal.imag)\n                j += 1\n                jc -= 1\n\n            lc -= 1\n\n        if ido == 1:\n            jc = ip - 1\n            for j in range(1, ipph):\n                for ik in range(idl1):\n                    t1 = CX2(ik,j)[0]\n                    t2 = CX2(ik,jc)[0]\n                    CX2(ik,j)[0], CX2(ik,jc)[0] = PM(t1, t2)\n                jc -= 1\n        else:\n            jc = ip - 1\n            for j in range(1, ipph):\n                for k in range(l1):\n                    t1 = CX(0,k,j)[0]\n                    t2 = CX(0,k,jc)[0]\n                    CX(0,k,j)[0], CX(0,k,jc)[0] = PM(t1, t2)\n\n                    for i in range(1, ido):\n                        x1, x2 = PM(CX(i,k,j)[0], CX(i,k,jc)[0])\n                        idij = (j-1)*(ido-1)+i-1\n                        CX(i,k,j)[0] = _special_mul(x1, wa[idij], fwd)\n                        idij = (jc-1)*(ido-1)+i-1\n                        CX(i,k,jc)[0] = _special_mul(x2, wa[idij], fwd)\n                jc -= 1\n        wal.dealloc()\n\n    def pass_all(self, c: Ptr[T], fct: T0, fwd: bool):\n        length = self.length\n        fact = self.fact\n\n        if length == 1:\n            c[0] *= fct\n            return\n\n        l1 = 1\n        ch = arr[T](length)\n        p1 = c\n        p2 = ch.data()\n\n        for k1 in range(len(fact)):\n            ip = fact[k1].fct\n            l2 = ip*l1\n            ido = length // l2\n\n            if ip == 4:\n                type(self).pass4(ido, l1, p1, p2, fact[k1].tw, fwd)\n            elif ip == 8:\n                type(self).pass8(ido, l1, p1, p2, fact[k1].tw, fwd)\n            elif ip == 2:\n                type(self).pass2(ido, l1, p1, p2, fact[k1].tw, fwd)\n            elif ip == 3:\n                type(self).pass3(ido, l1, p1, p2, fact[k1].tw, fwd)\n            elif ip == 5:\n                type(self).pass5(ido, l1, p1, p2, fact[k1].tw, fwd)\n            elif ip == 7:\n                type(self).pass7(ido, l1, p1, p2, fact[k1].tw, fwd)\n            elif ip == 11:\n                type(self).pass11(ido, l1, p1, p2, fact[k1].tw, fwd)\n            else:\n                type(self).passg(ido, ip, l1, p1, p2, fact[k1].tw, fact[k1].tws, fwd)\n                p1, p2 = p2, p1\n\n            p1, p2 = p2, p1\n            l1 = l2\n\n        if p1 != c:\n            if fct != T0(1):\n                for i in range(length):\n                    c[i] = ch[i] * fct\n            else:\n                str.memcpy(c.as_byte(), p1.as_byte(), length * sizeof(T))\n        else:\n            if fct != T0(1):\n                for i in range(length):\n                    c[i] *= fct\n\n        ch.dealloc()\n\n    def exec(self, c: Ptr[T], fct: T0, fwd: bool):\n        self.pass_all(c, fct, fwd)\n\n    def factorize(self):\n        n = self.length\n        fact = self.fact\n\n        while n & 7 == 0:\n            self.add_factor(8)\n            n >>= 3\n\n        while n & 3 == 0:\n            self.add_factor(4)\n            n >>= 2\n\n        if n & 1 == 0:\n            n >>= 1\n            self.add_factor(2)\n            fct_front = fact[0].fct\n            fct_back = fact[-1].fct\n            fact[0] = fact[0].with_fct(fct_back)\n            fact[-1] = fact[-1].with_fct(fct_front)\n\n        divisor = 3\n        while divisor * divisor <= n:\n            while n % divisor == 0:\n                self.add_factor(divisor)\n                n //= divisor\n            divisor += 2\n\n        if n > 1:\n            self.add_factor(n)\n\n    def twsize(self):\n        twsize = 0\n        l1 = 1\n        for k in range(len(self.fact)):\n            ip = self.fact[k].fct\n            ido = self.length // (l1*ip)\n            twsize += (ip-1)*(ido-1)\n            if ip > 11:\n                twsize += ip\n            l1 *= ip\n        return twsize\n\n    def comp_twiddle(self):\n        twiddle = sincos_2pibyn[T0](self.length)\n        l1 = 1\n        memofs = 0\n        fact = self.fact\n\n        for k in range(len(self.fact)):\n            ip = fact[k].fct\n            ido = self.length // (l1*ip)\n            fact[k] = fact[k].with_tw(self.mem.data() + memofs)\n            memofs += (ip-1)*(ido-1)\n\n            for j in range(1, ip):\n                for i in range(1, ido):\n                    fact[k].tw[(j-1)*(ido-1)+i-1] = twiddle[j*l1*i]\n            if ip > 11:\n                fact[k] = fact[k].with_tws(self.mem.data() + memofs)\n                memofs += ip\n                for j in range(ip):\n                    fact[k].tws[j] = twiddle[j*l1*ido]\n            l1 *= ip\n\n        twiddle.dealloc()\n\n    def __init__(self, length: int):\n        if length == 0:\n            raise ValueError(\"Invalid number of FFT data points (0) specified.\")\n        self.length = length\n        self.fact = []\n        if length != 1:\n            self.factorize()\n            self.mem = arr[T](self.twsize())\n            self.comp_twiddle()\n\n    def dealloc(self):\n        self.mem.dealloc()\n\ndef MULPM(c, d, e, f):\n    return c*e+d*f, c*f-d*e\n\ndef REARRANGE(rx, ix, ry, iy):\n    t1 = rx+ry\n    t2 = ry-rx\n    t3 = ix+iy\n    t4 = ix-iy\n    return t1, t3, t4, t2\n\nclass rfftp:\n    length: int\n    mem: arr[T0]\n    fact: List[fctdata[T0]]\n    T: type   # complex datatype\n    T0: type  # real datatype\n\n    def add_factor(self, factor: int):\n        self.fact.append(fctdata[T0](factor, Ptr[T0](), Ptr[T0]()))\n\n    def radf2(ido: int, l1: int, cc: Ptr[T0], ch: Ptr[T0], wa: Ptr[T0]):\n        @inline\n        def WA(x: int, i: int):\n            return wa[i+x*(ido-1)]\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+l1*c)]\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+2*c))\n\n        for k in range(l1):\n            CH(0,0,k)[0], CH(ido-1,1,k)[0] = PM(CC(0,k,0), CC(0,k,1))\n\n        if ido & 1 == 0:\n            for k in range(l1):\n                CH(0,1,k)[0] = -CC(ido-1,k,1)\n                CH(ido-1,0,k)[0] = CC(ido-1,k,0)\n\n        if ido <= 2:\n            return\n\n        for k in range(l1):\n            for i in range(2, ido, 2):\n                ic = ido-i\n                tr2, ti2 = MULPM(WA(0,i-2),WA(0,i-1),CC(i-1,k,1),CC(i,k,1))\n                CH(i-1,0,k)[0], CH(ic-1,1,k)[0] = PM(CC(i-1,k,0), tr2)\n                CH(i  ,0,k)[0], CH(ic  ,1,k)[0] = PM(ti2, CC(i,k,0))\n\n    def radf3(ido: int, l1: int, cc: Ptr[T0], ch: Ptr[T0], wa: Ptr[T0]):\n        @inline\n        def WA(x: int, i: int):\n            return wa[i+x*(ido-1)]\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+l1*c)]\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+3*c))\n\n        taur = cast(-0.5, T0)\n        taui = cast(0.8660254037844386467637231707529362, T0)\n\n        for k in range(l1):\n            cr2 = CC(0,k,1) + CC(0,k,2)\n            CH(0,0,k)[0] = CC(0,k,0) + cr2\n            CH(0,2,k)[0] = taui*(CC(0,k,2)-CC(0,k,1))\n            CH(ido-1,1,k)[0] = CC(0,k,0)+taur*cr2\n\n        if ido == 1:\n            return\n\n        for k in range(l1):\n            for i in range(2, ido, 2):\n                ic = ido-i\n                dr2, di2 = MULPM(WA(0,i-2),WA(0,i-1),CC(i-1,k,1),CC(i,k,1))\n                dr3, di3 = MULPM(WA(1,i-2),WA(1,i-1),CC(i-1,k,2),CC(i,k,2))\n                dr2, di2, dr3, di3 = REARRANGE(dr2, di2, dr3, di3)\n                CH(i-1,0,k)[0] = CC(i-1,k,0)+dr2\n                CH(i  ,0,k)[0] = CC(i  ,k,0)+di2\n                tr2 = CC(i-1,k,0)+taur*dr2\n                ti2 = CC(i  ,k,0)+taur*di2\n                tr3 = taui*dr3\n                ti3 = taui*di3\n                CH(i-1,2,k)[0], CH(ic-1,1,k)[0] = PM(tr2, tr3)\n                CH(i  ,2,k)[0], CH(ic  ,1,k)[0] = PM(ti3, ti2)\n\n    def radf4(ido: int, l1: int, cc: Ptr[T0], ch: Ptr[T0], wa: Ptr[T0]):\n        @inline\n        def WA(x: int, i: int):\n            return wa[i+x*(ido-1)]\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+l1*c)]\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+4*c))\n\n        hsqt2 = cast(0.707106781186547524400844362104849, T0)\n\n        for k in range(l1):\n            tr1, CH(0,2,k)[0] = PM(CC(0,k,3), CC(0,k,1))\n            tr2, CH(ido-1,1,k)[0] = PM(CC(0,k,0),CC(0,k,2))\n            CH(0,0,k)[0], CH(ido-1,3,k)[0] = PM(tr2, tr1)\n\n        if ido & 1 == 0:\n            for k in range(l1):\n                ti1 = -hsqt2*(CC(ido-1,k,1)+CC(ido-1,k,3))\n                tr1 = hsqt2*(CC(ido-1,k,1)-CC(ido-1,k,3))\n                CH(ido-1,0,k)[0], CH(ido-1,2,k)[0] = PM(CC(ido-1,k,0), tr1)\n                CH(    0,3,k)[0], CH(    0,1,k)[0] = PM(ti1, CC(ido-1,k,2))\n\n        if ido <= 2:\n            return\n\n        for k in range(l1):\n            for i in range(2, ido, 2):\n                ic = ido - i\n                cr2,ci2 = MULPM(WA(0,i-2),WA(0,i-1),CC(i-1,k,1),CC(i,k,1))\n                cr3,ci3 = MULPM(WA(1,i-2),WA(1,i-1),CC(i-1,k,2),CC(i,k,2))\n                cr4,ci4 = MULPM(WA(2,i-2),WA(2,i-1),CC(i-1,k,3),CC(i,k,3))\n                tr1,tr4 = PM(cr4,cr2)\n                ti1,ti4 = PM(ci2,ci4)\n                tr2,tr3 = PM(CC(i-1,k,0),cr3)\n                ti2,ti3 = PM(CC(i  ,k,0),ci3)\n                CH(i-1,0,k)[0], CH(ic-1,3,k)[0] = PM(tr2,tr1)\n                CH(i  ,0,k)[0], CH(ic  ,3,k)[0] = PM(ti1,ti2)\n                CH(i-1,2,k)[0], CH(ic-1,1,k)[0] = PM(tr3,ti4)\n                CH(i  ,2,k)[0], CH(ic  ,1,k)[0] = PM(tr4,ti3)\n\n    def radf5(ido: int, l1: int, cc: Ptr[T0], ch: Ptr[T0], wa: Ptr[T0]):\n        @inline\n        def WA(x: int, i: int):\n            return wa[i+x*(ido-1)]\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+l1*c)]\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+5*c))\n\n        tr11 = cast(0.3090169943749474241022934171828191, T0)\n        ti11 = cast(0.9510565162951535721164393333793821, T0)\n        tr12 = cast(-0.8090169943749474241022934171828191, T0)\n        ti12 = cast(0.5877852522924731291687059546390728, T0)\n\n        for k in range(l1):\n            cr2,ci5 = PM(CC(0,k,4),CC(0,k,1))\n            cr3,ci4 = PM(CC(0,k,3),CC(0,k,2))\n            CH(0,0,k)[0] = CC(0,k,0)+cr2+cr3\n            CH(ido-1,1,k)[0] = CC(0,k,0)+tr11*cr2+tr12*cr3\n            CH(0,2,k)[0] = ti11*ci5+ti12*ci4\n            CH(ido-1,3,k)[0] = CC(0,k,0)+tr12*cr2+tr11*cr3\n            CH(0,4,k)[0] = ti12*ci5-ti11*ci4\n\n        if ido == 1:\n            return\n\n        for k in range(l1):\n            ic = ido-2\n            for i in range(2, ido, 2):\n                dr2,di2 = MULPM(WA(0,i-2),WA(0,i-1),CC(i-1,k,1),CC(i,k,1))\n                dr3,di3 = MULPM(WA(1,i-2),WA(1,i-1),CC(i-1,k,2),CC(i,k,2))\n                dr4,di4 = MULPM(WA(2,i-2),WA(2,i-1),CC(i-1,k,3),CC(i,k,3))\n                dr5,di5 = MULPM(WA(3,i-2),WA(3,i-1),CC(i-1,k,4),CC(i,k,4))\n                dr2, di2, dr5, di5 = REARRANGE(dr2, di2, dr5, di5)\n                dr3, di3, dr4, di4 = REARRANGE(dr3, di3, dr4, di4)\n                CH(i-1,0,k)[0] = CC(i-1,k,0)+dr2+dr3\n                CH(i  ,0,k)[0] = CC(i  ,k,0)+di2+di3\n                tr2 = CC(i-1,k,0)+tr11*dr2+tr12*dr3\n                ti2 = CC(i  ,k,0)+tr11*di2+tr12*di3\n                tr3 = CC(i-1,k,0)+tr12*dr2+tr11*dr3\n                ti3 = CC(i  ,k,0)+tr12*di2+tr11*di3\n                tr5 = ti11*dr5 + ti12*dr4\n                ti5 = ti11*di5 + ti12*di4\n                tr4 = ti12*dr5 - ti11*dr4\n                ti4 = ti12*di5 - ti11*di4\n                CH(i-1,2,k)[0], CH(ic-1,1,k)[0] = PM(tr2,tr5)\n                CH(i  ,2,k)[0], CH(ic  ,1,k)[0] = PM(ti5,ti2)\n                CH(i-1,4,k)[0], CH(ic-1,3,k)[0] = PM(tr3,tr4)\n                CH(i  ,4,k)[0], CH(ic  ,3,k)[0] = PM(ti4,ti3)\n                ic -= 2\n\n    def radfg(ido: int, ip: int, l1: int, cc: Ptr[T0], ch: Ptr[T0], wa: Ptr[T0], csarr: Ptr[T0]):\n        cdim = ip\n        ipph = (ip + 1) // 2\n        idl1 = ido * l1\n\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc + a+ido*(b+cdim*c)\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+l1*c))\n        @inline\n        def C1(a: int, b: int, c: int):\n            return cc + (a+ido*(b+l1*c))\n        @inline\n        def C2(a: int, b: int):\n            return cc + (a+idl1*b)\n        @inline\n        def CH2(a: int, b: int):\n            return ch + a+idl1*b\n\n        if ido > 1:\n            jc = ip - 1\n            for j in range(1, ipph):\n                is_ = (j-1)*(ido-1)\n                is2 = (jc-1)*(ido-1)\n                for k in range(l1):\n                    idij = is_\n                    idij2 = is2\n                    i = 1\n                    while i <= ido - 2:\n                        t1 = C1(i,k,j )[0]\n                        t2 = C1(i+1,k,j )[0]\n                        t3 = C1(i,k,jc)[0]\n                        t4 = C1(i+1,k,jc)[0]\n                        x1 = wa[idij]*t1 + wa[idij+1]*t2\n                        x2 = wa[idij]*t2 - wa[idij+1]*t1\n                        x3 = wa[idij2]*t3 + wa[idij2+1]*t4\n                        x4 = wa[idij2]*t4 - wa[idij2+1]*t3\n                        C1(i,k,j)[0], C1(i+1,k,jc)[0] = PM(x3,x1)\n                        C1(i+1,k,j)[0], C1(i,k,jc)[0] = PM(x2,x4)\n                        idij += 2\n                        idij2 += 2\n                        i += 2\n                jc -= 1\n\n        jc = ip - 1\n        for j in range(1, ipph):\n            for k in range(l1):\n                C1(0,k,jc)[0], C1(0,k,j)[0] = MP(C1(0,k,jc)[0], C1(0,k,j)[0])\n            jc -= 1\n\n        lc = ip-1\n        for l in range(1, ipph):\n            for ik in range(idl1):\n                CH2(ik,l )[0] = C2(ik,0)[0]+csarr[2*l]*C2(ik,1)[0]+csarr[4*l]*C2(ik,2)[0]\n                CH2(ik,lc)[0] = csarr[2*l+1]*C2(ik,ip-1)[0]+csarr[4*l+1]*C2(ik,ip-2)[0]\n\n            iang = 2*l\n            j = 3\n            jc = ip - 3\n\n            while j < ipph - 3:\n                iang+=l\n                if iang>=ip:\n                    iang-=ip\n                ar1 = csarr[2*iang]\n                ai1 = csarr[2*iang+1]\n                iang+=l\n                if iang>=ip:\n                    iang-=ip\n                ar2=csarr[2*iang]\n                ai2=csarr[2*iang+1]\n                iang+=l\n                if iang>=ip:\n                    iang-=ip\n                ar3=csarr[2*iang]\n                ai3=csarr[2*iang+1]\n                iang+=l\n                if iang>=ip:\n                    iang-=ip\n                ar4 = csarr[2*iang]\n                ai4 = csarr[2*iang+1]\n\n                for ik in range(idl1):\n                    CH2(ik,l )[0] += ar1*C2(ik,j )[0]+ar2*C2(ik,j +1)[0] + ar3*C2(ik,j +2)[0]+ar4*C2(ik,j +3)[0]\n                    CH2(ik,lc)[0] += ai1*C2(ik,jc)[0]+ai2*C2(ik,jc-1)[0] + ai3*C2(ik,jc-2)[0]+ai4*C2(ik,jc-3)[0]\n\n                j += 4\n                jc -= 4\n\n            while j < ipph - 1:\n                iang += l\n                if iang >= ip:\n                    iang -= ip\n                ar1 = csarr[2*iang]\n                ai1 = csarr[2*iang+1]\n                iang += l\n                if iang >= ip:\n                    iang -= ip\n                ar2 = csarr[2*iang]\n                ai2 = csarr[2*iang+1]\n\n                for ik in range(idl1):\n                    CH2(ik,l )[0] += ar1*C2(ik,j )[0] + ar2*C2(ik,j +1)[0]\n                    CH2(ik,lc)[0] += ai1*C2(ik,jc)[0] + ai2*C2(ik,jc-1)[0]\n\n                j += 2\n                jc -= 2\n\n            while j < ipph:\n                iang += l\n                if iang >= ip:\n                    iang -= ip\n                ar=csarr[2*iang]\n                ai=csarr[2*iang+1]\n\n                for ik in range(idl1):\n                    CH2(ik,l )[0] += ar*C2(ik,j )[0]\n                    CH2(ik,lc)[0] += ai*C2(ik,jc)[0]\n\n                j += 1\n                jc -= 1\n\n            lc -= 1\n\n        for ik in range(idl1):\n            CH2(ik,0)[0] = C2(ik,0)[0]\n\n        for j in range(1, ipph):\n            for ik in range(idl1):\n                CH2(ik,0)[0] += C2(ik,j)[0]\n\n        for k in range(l1):\n            for i in range(ido):\n                CC(i,0,k)[0] = CH(i,k,0)[0]\n\n        jc = ip - 1\n        for j in range(1, ipph):\n            j2 = 2*j-1\n            for k in range(l1):\n                CC(ido-1,j2,k)[0] = CH(0,k,j)[0]\n                CC(0,j2+1,k)[0] = CH(0,k,jc)[0]\n            jc -= 1\n\n        if ido == 1:\n            return\n\n        jc = ip - 1\n        for j in range(1, ipph):\n            j2 = 2*j - 1\n            for k in range(l1):\n                i = 1\n                ic = ido - i - 2\n                while i <= ido - 2:\n                    CC(i   ,j2+1,k)[0] = CH(i  ,k,j )[0]+CH(i  ,k,jc)[0]\n                    CC(ic  ,j2  ,k)[0] = CH(i  ,k,j )[0]-CH(i  ,k,jc)[0]\n                    CC(i+1 ,j2+1,k)[0] = CH(i+1,k,j )[0]+CH(i+1,k,jc)[0]\n                    CC(ic+1,j2  ,k)[0] = CH(i+1,k,jc)[0]-CH(i+1,k,j )[0]\n                    i += 2\n                    ic -= 2\n            jc -= 1\n\n    def radb2(ido: int, l1: int, cc: Ptr[T0], ch: Ptr[T0], wa: Ptr[T0]):\n        @inline\n        def WA(x: int, i: int):\n            return wa[i+x*(ido-1)]\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+2*c)]\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+l1*c))\n\n        for k in range(l1):\n            CH(0,k,0)[0], CH(0,k,1)[0] = PM(CC(0,0,k),CC(ido-1,1,k))\n\n        if ido & 1 == 0:\n            for k in range(l1):\n                CH(ido-1,k,0)[0] =  T0(2)*CC(ido-1,0,k)\n                CH(ido-1,k,1)[0] = -T0(2)*CC(0    ,1,k)\n\n        if ido <= 2:\n            return\n\n        for k in range(l1):\n            for i in range(2, ido, 2):\n                ic = ido-i\n                CH(i-1,k,0)[0], tr2 = PM(CC(i-1,0,k),CC(ic-1,1,k))\n                ti2, CH(i  ,k,0)[0] = PM(CC(i  ,0,k), CC(ic  ,1,k))\n                CH(i,k,1)[0], CH(i-1,k,1)[0] = MULPM(WA(0,i-2),WA(0,i-1),ti2,tr2)\n\n    def radb3(ido: int, l1: int, cc: Ptr[T0], ch: Ptr[T0], wa: Ptr[T0]):\n        @inline\n        def WA(x: int, i: int):\n            return wa[i+x*(ido-1)]\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+3*c)]\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+l1*c))\n\n        taur = cast(-0.5, T0)\n        taui = cast(0.8660254037844386467637231707529362, T0)\n\n        for k in range(l1):\n            tr2=T0(2)*CC(ido-1,1,k)\n            cr2=CC(0,0,k)+taur*tr2\n            CH(0,k,0)[0]=CC(0,0,k)+tr2\n            ci3=T0(2)*taui*CC(0,2,k)\n            CH(0,k,2)[0], CH(0,k,1)[0] = PM(cr2,ci3)\n\n        if ido == 1:\n            return\n\n        for k in range(l1):\n            ic = ido - 2\n            for i in range(2, ido, 2):\n                tr2=CC(i-1,2,k)+CC(ic-1,1,k)\n                ti2=CC(i  ,2,k)-CC(ic  ,1,k)\n                cr2=CC(i-1,0,k)+taur*tr2\n                ci2=CC(i  ,0,k)+taur*ti2\n                CH(i-1,k,0)[0]=CC(i-1,0,k)+tr2\n                CH(i  ,k,0)[0]=CC(i  ,0,k)+ti2\n                cr3=taui*(CC(i-1,2,k)-CC(ic-1,1,k))\n                ci3=taui*(CC(i  ,2,k)+CC(ic  ,1,k))\n                dr3,dr2=PM(cr2,ci3)\n                di2,di3=PM(ci2,cr3)\n                CH(i,k,1)[0],CH(i-1,k,1)[0]=MULPM(WA(0,i-2),WA(0,i-1),di2,dr2)\n                CH(i,k,2)[0],CH(i-1,k,2)[0]=MULPM(WA(1,i-2),WA(1,i-1),di3,dr3)\n                ic -= 2\n\n    def radb4(ido: int, l1: int, cc: Ptr[T0], ch: Ptr[T0], wa: Ptr[T0]):\n        @inline\n        def WA(x: int, i: int):\n            return wa[i+x*(ido-1)]\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+4*c)]\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+l1*c))\n\n        sqrt2 = cast(1.414213562373095048801688724209698, T0)\n\n        for k in range(l1):\n            tr2,tr1 = PM(CC(0,0,k),CC(ido-1,3,k))\n            tr3=T0(2)*CC(ido-1,1,k)\n            tr4=T0(2)*CC(0,2,k)\n            CH(0,k,0)[0],CH(0,k,2)[0] = PM(tr2,tr3)\n            CH(0,k,3)[0],CH(0,k,1)[0] = PM(tr1,tr4)\n\n        if ido & 1 == 0:\n            for k in range(l1):\n                ti1,ti2 = PM(CC(0    ,3,k),CC(0    ,1,k))\n                tr2,tr1 = PM(CC(ido-1,0,k),CC(ido-1,2,k))\n                CH(ido-1,k,0)[0]=tr2+tr2\n                CH(ido-1,k,1)[0]=sqrt2*(tr1-ti1)\n                CH(ido-1,k,2)[0]=ti2+ti2\n                CH(ido-1,k,3)[0]=-sqrt2*(tr1+ti1)\n\n        if ido <= 2:\n            return\n\n        for k in range(l1):\n            for i in range(2, ido, 2):\n                ic=ido-i\n                tr2,tr1 = PM(CC(i-1,0,k),CC(ic-1,3,k))\n                ti1,ti2 = PM(CC(i  ,0,k),CC(ic  ,3,k))\n                tr4,ti3 = PM(CC(i  ,2,k),CC(ic  ,1,k))\n                tr3,ti4 = PM(CC(i-1,2,k),CC(ic-1,1,k))\n                CH(i-1,k,0)[0],cr3 = PM(tr2,tr3)\n                CH(i  ,k,0)[0],ci3 = PM(ti2,ti3)\n                cr4,cr2 = PM (tr1,tr4)\n                ci2,ci4 = PM (ti1,ti4)\n                CH(i,k,1)[0],CH(i-1,k,1)[0] = MULPM(WA(0,i-2),WA(0,i-1),ci2,cr2)\n                CH(i,k,2)[0],CH(i-1,k,2)[0] = MULPM(WA(1,i-2),WA(1,i-1),ci3,cr3)\n                CH(i,k,3)[0],CH(i-1,k,3)[0] = MULPM(WA(2,i-2),WA(2,i-1),ci4,cr4)\n\n    def radb5(ido: int, l1: int, cc: Ptr[T0], ch: Ptr[T0], wa: Ptr[T0]):\n        @inline\n        def WA(x: int, i: int):\n            return wa[i+x*(ido-1)]\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+5*c)]\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+l1*c))\n\n        tr11 = cast(0.3090169943749474241022934171828191, T0)\n        ti11 = cast(0.9510565162951535721164393333793821, T0)\n        tr12 = cast(-0.8090169943749474241022934171828191, T0)\n        ti12 = cast(0.5877852522924731291687059546390728, T0)\n\n        for k in range(l1):\n            ti5=CC(0,2,k)+CC(0,2,k)\n            ti4=CC(0,4,k)+CC(0,4,k)\n            tr2=CC(ido-1,1,k)+CC(ido-1,1,k)\n            tr3=CC(ido-1,3,k)+CC(ido-1,3,k)\n            CH(0,k,0)[0]=CC(0,0,k)+tr2+tr3\n            cr2=CC(0,0,k)+tr11*tr2+tr12*tr3\n            cr3=CC(0,0,k)+tr12*tr2+tr11*tr3\n            ci5,ci4=MULPM(ti5,ti4,ti11,ti12)\n            CH(0,k,4)[0],CH(0,k,1)[0]=PM(cr2,ci5)\n            CH(0,k,3)[0],CH(0,k,2)[0]=PM(cr3,ci4)\n\n        if ido == 1:\n            return\n\n        for k in range(l1):\n            ic = ido-2\n            for i in range(2, ido, 2):\n                tr2,tr5=PM(CC(i-1,2,k),CC(ic-1,1,k))\n                ti5,ti2=PM(CC(i  ,2,k),CC(ic  ,1,k))\n                tr3,tr4=PM(CC(i-1,4,k),CC(ic-1,3,k))\n                ti4,ti3=PM(CC(i  ,4,k),CC(ic  ,3,k))\n                CH(i-1,k,0)[0]=CC(i-1,0,k)+tr2+tr3\n                CH(i  ,k,0)[0]=CC(i  ,0,k)+ti2+ti3\n                cr2=CC(i-1,0,k)+tr11*tr2+tr12*tr3\n                ci2=CC(i  ,0,k)+tr11*ti2+tr12*ti3\n                cr3=CC(i-1,0,k)+tr12*tr2+tr11*tr3\n                ci3=CC(i  ,0,k)+tr12*ti2+tr11*ti3\n                cr5,cr4=MULPM(tr5,tr4,ti11,ti12)\n                ci5,ci4=MULPM(ti5,ti4,ti11,ti12)\n                dr4,dr3=PM(cr3,ci4)\n                di3,di4=PM(ci3,cr4)\n                dr5,dr2=PM(cr2,ci5)\n                di2,di5=PM(ci2,cr5)\n                CH(i,k,1)[0],CH(i-1,k,1)[0]=MULPM(WA(0,i-2),WA(0,i-1),di2,dr2)\n                CH(i,k,2)[0],CH(i-1,k,2)[0]=MULPM(WA(1,i-2),WA(1,i-1),di3,dr3)\n                CH(i,k,3)[0],CH(i-1,k,3)[0]=MULPM(WA(2,i-2),WA(2,i-1),di4,dr4)\n                CH(i,k,4)[0],CH(i-1,k,4)[0]=MULPM(WA(3,i-2),WA(3,i-1),di5,dr5)\n\n                ic -= 2\n\n    def radbg(ido: int, ip: int, l1: int, cc: Ptr[T0], ch: Ptr[T0], wa: Ptr[T0], csarr: Ptr[T0]):\n        cdim = ip\n        ipph = (ip + 1) // 2\n        idl1 = ido * l1\n\n        @inline\n        def CC(a: int, b: int, c: int):\n            return cc[a+ido*(b+cdim*c)]\n        @inline\n        def CH(a: int, b: int, c: int):\n            return ch + (a+ido*(b+l1*c))\n        @inline\n        def C1(a: int, b: int, c: int):\n            return cc + (a+ido*(b+l1*c))\n        @inline\n        def C2(a: int, b: int):\n            return cc + (a+idl1*b)\n        @inline\n        def CH2(a: int, b: int):\n            return ch + a+idl1*b\n\n        for k in range(l1):\n            for i in range(ido):\n                CH(i,k,0)[0] = CC(i,0,k)\n\n        jc=ip-1\n        for j in range(1, ipph):\n            j2 = 2*j - 1\n            for k in range(l1):\n                CH(0,k,j )[0] = T0(2)*CC(ido-1,j2,k)\n                CH(0,k,jc)[0] = T0(2)*CC(0,j2+1,k)\n            jc -= 1\n\n        if ido != -1:\n            jc = ip-1\n            for j in range(1, ipph):\n                j2 = 2*j - 1\n                for k in range(l1):\n                    i=1\n                    ic=ido-i-2\n                    while i <= ido - 2:\n                        CH(i  ,k,j )[0] = CC(i  ,j2+1,k)+CC(ic  ,j2,k)\n                        CH(i  ,k,jc)[0] = CC(i  ,j2+1,k)-CC(ic  ,j2,k)\n                        CH(i+1,k,j )[0] = CC(i+1,j2+1,k)-CC(ic+1,j2,k)\n                        CH(i+1,k,jc)[0] = CC(i+1,j2+1,k)+CC(ic+1,j2,k)\n                        i += 2\n                        ic -= 2\n                jc -= 1\n\n        lc = ip-1\n        for l in range(1, ipph):\n            for ik in range(idl1):\n                C2(ik,l )[0] = CH2(ik,0)[0]+csarr[2*l]*CH2(ik,1)[0]+csarr[4*l]*CH2(ik,2)[0]\n                C2(ik,lc)[0] = csarr[2*l+1]*CH2(ik,ip-1)[0]+csarr[4*l+1]*CH2(ik,ip-2)[0]\n            iang = 2*l\n            j=3\n            jc=ip-3\n            while j < ipph - 3:\n                iang+=l\n                if iang>ip:\n                    iang-=ip\n                ar1=csarr[2*iang]\n                ai1=csarr[2*iang+1]\n                iang+=l\n                if iang>ip:\n                    iang-=ip\n                ar2=csarr[2*iang]\n                ai2=csarr[2*iang+1]\n                iang+=l\n                if iang>ip:\n                    iang-=ip\n                ar3=csarr[2*iang]\n                ai3=csarr[2*iang+1]\n                iang+=l\n                if iang>ip:\n                    iang-=ip\n                ar4=csarr[2*iang]\n                ai4=csarr[2*iang+1]\n                for ik in range(idl1):\n                    C2(ik,l )[0] += ar1*CH2(ik,j )[0]+ar2*CH2(ik,j +1)[0] + ar3*CH2(ik,j +2)[0]+ar4*CH2(ik,j +3)[0]\n                    C2(ik,lc)[0] += ai1*CH2(ik,jc)[0]+ai2*CH2(ik,jc-1)[0] + ai3*CH2(ik,jc-2)[0]+ai4*CH2(ik,jc-3)[0]\n                j += 4\n                jc -= 4\n\n            while j < ipph - 1:\n                iang+=l\n                if iang>ip:\n                    iang-=ip\n                ar1=csarr[2*iang]\n                ai1=csarr[2*iang+1]\n                iang+=l\n                if iang>ip:\n                    iang-=ip\n                ar2=csarr[2*iang]\n                ai2=csarr[2*iang+1]\n                for ik in range(idl1):\n                    C2(ik,l )[0] += ar1*CH2(ik,j )[0]+ar2*CH2(ik,j +1)[0]\n                    C2(ik,lc)[0] += ai1*CH2(ik,jc)[0]+ai2*CH2(ik,jc-1)[0]\n                j += 2\n                jc -= 2\n\n            while j < ipph:\n                iang+=l\n                if iang>ip:\n                    iang-=ip\n                war=csarr[2*iang]\n                wai=csarr[2*iang+1]\n                for ik in range(idl1):\n                    C2(ik,l )[0] += war*CH2(ik,j )[0]\n                    C2(ik,lc)[0] += wai*CH2(ik,jc)[0]\n                j += 1\n                jc -= 1\n\n            lc -= 1\n\n        for j in range(1, ipph):\n            for ik in range(idl1):\n                CH2(ik,0)[0] += CH2(ik,j)[0]\n\n        jc = ip-1\n        for j in range(1, ipph):\n            for k in range(l1):\n                CH(0,k,jc)[0],CH(0,k,j)[0] = PM(C1(0,k,j)[0],C1(0,k,jc)[0])\n            jc -= 1\n\n        if ido == 1:\n            return\n\n        jc = ip - 1\n        for j in range(1, ipph):\n            for k in range(l1):\n                i = 1\n                while i <= ido - 2:\n                    CH(i  ,k,j )[0] = C1(i  ,k,j)[0]-C1(i+1,k,jc)[0]\n                    CH(i  ,k,jc)[0] = C1(i  ,k,j)[0]+C1(i+1,k,jc)[0]\n                    CH(i+1,k,j )[0] = C1(i+1,k,j)[0]+C1(i  ,k,jc)[0]\n                    CH(i+1,k,jc)[0] = C1(i+1,k,j)[0]-C1(i  ,k,jc)[0]\n                    i += 2\n            jc -= 1\n\n        for j in range(1, ip):\n            is_ = (j-1)*(ido-1)\n            for k in range(l1):\n                idij = is_\n                i = 1\n                while i <= ido - 2:\n                    t1=CH(i,k,j)[0]\n                    t2=CH(i+1,k,j)[0]\n                    CH(i  ,k,j)[0] = wa[idij]*t1-wa[idij+1]*t2\n                    CH(i+1,k,j)[0] = wa[idij]*t2+wa[idij+1]*t1\n                    idij+=2\n                    i += 2\n\n    def copy_and_norm(self, c: Ptr[T0], p1: Ptr[T0], fct: T0):\n        if p1 != c:\n            if fct != cast(1, T0):\n                for i in range(self.length):\n                    c[i] = fct * p1[i]\n            else:\n                str.memcpy(c.as_byte(), p1.as_byte(), self.length * sizeof(T0))\n        else:\n            if fct != cast(1, T0):\n                for i in range(self.length):\n                    c[i] *= fct\n\n    def exec(self, c: Ptr[T0], fct: T0, r2hc: bool):\n        length = self.length\n\n        if length == 1:\n            c[0] *= fct\n            return\n\n        fact = self.fact\n        nf = len(fact)\n        ch = arr[T0](length)\n        p1 = c\n        p2 = ch.data()\n\n        if r2hc:\n            l1 = length\n            for k1 in range(nf):\n                k = nf-k1-1\n                ip = fact[k].fct\n                ido = length // l1\n                l1 //= ip\n                if ip == 4:\n                    type(self).radf4(ido, l1, p1, p2, fact[k].tw)\n                elif ip == 2:\n                    type(self).radf2(ido, l1, p1, p2, fact[k].tw)\n                elif ip == 3:\n                    type(self).radf3(ido, l1, p1, p2, fact[k].tw)\n                elif ip == 5:\n                    type(self).radf5(ido, l1, p1, p2, fact[k].tw)\n                else:\n                    type(self).radfg(ido, ip, l1, p1, p2, fact[k].tw, fact[k].tws)\n                    p1, p2 = p2, p1\n                p1, p2 = p2, p1\n        else:\n            l1 = 1\n            for k in range(nf):\n                ip = fact[k].fct\n                ido = length // (ip * l1)\n                if ip == 4:\n                    type(self).radb4(ido, l1, p1, p2, fact[k].tw)\n                elif ip == 2:\n                    type(self).radb2(ido, l1, p1, p2, fact[k].tw)\n                elif ip == 3:\n                    type(self).radb3(ido, l1, p1, p2, fact[k].tw)\n                elif ip == 5:\n                    type(self).radb5(ido, l1, p1, p2, fact[k].tw)\n                else:\n                    type(self).radbg(ido, ip, l1, p1, p2, fact[k].tw, fact[k].tws)\n                p1, p2 = p2, p1\n                l1 *= ip\n\n        self.copy_and_norm(c, p1, fct)\n        ch.dealloc()\n\n    def factorize(self):\n        n = self.length\n        fact = self.fact\n\n        while n % 4 == 0:\n            self.add_factor(4)\n            n >>= 2\n\n        if n % 2 == 0:\n            n >>= 1\n            self.add_factor(2)\n            fct_front = fact[0].fct\n            fct_back = fact[-1].fct\n            fact[0] = fact[0].with_fct(fct_back)\n            fact[-1] = fact[-1].with_fct(fct_front)\n\n        divisor = 3\n        while divisor * divisor <= n:\n            while n % divisor == 0:\n                self.add_factor(divisor)\n                n //= divisor\n            divisor += 2\n\n        if n > 1:\n            self.add_factor(n)\n\n    def twsize(self):\n        length = self.length\n        fact = self.fact\n        twsz = 0\n        l1 = 1\n        for k in range(len(fact)):\n            ip = fact[k].fct\n            ido = length // (l1*ip)\n            twsz += (ip-1)*(ido-1)\n            if ip > 5:\n                twsz += 2*ip\n            l1 *= ip\n        return twsz\n\n    def comp_twiddle(self):\n        length = self.length\n        fact = self.fact\n        twid = sincos_2pibyn[T0](self.length)\n        l1 = 1\n        p = self.mem.data()\n\n        for k in range(len(fact)):\n            ip = fact[k].fct\n            ido = length // (l1*ip)\n\n            if k < len(fact) - 1:\n                fact[k] = fact[k].with_tw(p)\n                p += (ip-1)*(ido-1)\n\n                for j in range(1, ip):\n                    i = 1\n                    while i <= (ido-1)//2:\n                        fact[k].tw[(j-1)*(ido-1)+2*i-2] = twid[j*l1*i].real\n                        fact[k].tw[(j-1)*(ido-1)+2*i-1] = twid[j*l1*i].imag\n                        i += 1\n\n            if ip > 5:\n                fact[k] = fact[k].with_tws(p)\n                p += 2*ip\n                fact[k].tws[0] = cast(1, T0)\n                fact[k].tws[1] = cast(0, T0)\n                i = 2\n                ic = 2*ip-2\n                while i <= ic:\n                    fact[k].tws[i  ] = twid[i//2*(length//ip)].real\n                    fact[k].tws[i+1] = twid[i//2*(length//ip)].imag\n                    fact[k].tws[ic]   = twid[i//2*(length//ip)].real\n                    fact[k].tws[ic+1] = -twid[i//2*(length//ip)].imag\n                    i += 2\n                    ic -= 2\n\n            l1 *= ip\n\n        twid.dealloc()\n\n    def __init__(self, length: int):\n        if length == 0:\n            raise ValueError(\"Invalid number of FFT data points (0) specified.\")\n        self.length = length\n        self.fact = []\n        if length != 1:\n            self.factorize()\n            self.mem = arr[T0](self.twsize())\n            self.comp_twiddle()\n\n    def dealloc(self):\n        self.mem.dealloc()\n\nclass fftblue:\n    n: int\n    n2: int\n    plan: cfftp[T,T0]\n    mem: arr[T]\n    bk: Ptr[T]\n    bkf: Ptr[T]\n    T: type\n    T0: type\n\n    def fft(self, c: Ptr[T], fct: T0, fwd: bool):\n        n = self.n\n        n2 = self.n2\n        plan = self.plan\n        bk = self.bk\n        bkf = self.bkf\n\n        akf = arr[T](n2)\n\n        for m in range(n):\n            akf[m] = _special_mul(c[m], bk[m], fwd)\n\n        zero = akf[0] * cast(0, T0)\n        for m in range(n, n2):\n            akf[m] = zero\n\n        plan.exec(akf.data(), cast(1, T0), True)\n\n        akf[0] = _special_mul(akf[0], bkf[0], not fwd)\n        for m in range(1, (n2 + 1) // 2):\n            akf[m] = _special_mul(akf[m], bkf[m], not fwd)\n            akf[n2-m] = _special_mul(akf[n2-m], bkf[m], not fwd)\n\n        if n2 & 1 == 0:\n            akf[n2//2] = _special_mul(akf[n2//2], bkf[n2//2], not fwd)\n\n        plan.exec(akf.data(), cast(1, T0), False)\n\n        for m in range(n):\n            c[m] = _special_mul(akf[m], bk[m], fwd) * fct\n\n        akf.dealloc()\n\n    def __init__(self, length: int):\n        n = length\n        self.n = n\n        self.n2 = _good_size_cmplx(n*2-1)\n        n2 = self.n2\n        self.plan = cfftp[T,T0](n2)\n        self.mem = arr[T](n+n2//2+1)\n        self.bk = self.mem.data()\n        self.bkf = self.mem.data() + n\n\n        plan = self.plan\n        mem = self.mem\n        bk = self.bk\n        bkf = self.bkf\n\n        tmp = sincos_2pibyn[T0](2*n)\n        bk[0] = T(cast(1, T0), cast(0, T0))\n\n        coeff = 0\n        for m in range(1, n):\n            coeff += 2*m - 1\n            if coeff >= 2*n:\n                coeff -= 2*n\n            bk[m] = tmp[coeff]\n\n        tbkf = arr[T](n2)\n        xn2 = cast(1, T0) / cast(n2, T0)\n        tbkf[0] = bk[0]*xn2\n\n        for m in range(1, n):\n            x = bk[m]*xn2\n            tbkf[m] = x\n            tbkf[n2-m] = x\n\n        m = n\n        while m <= n2-n:\n            tbkf[m] = T(cast(0, T0), cast(0, T0))\n            m += 1\n\n        plan.exec(tbkf.data(), cast(1, T0), True)\n\n        for i in range(n2//2 + 1):\n            bkf[i] = tbkf[i]\n\n        tbkf.dealloc()\n        tmp.dealloc()\n\n    def exec(self, c: Ptr[T], fct: T0, fwd: bool):\n        return self.fft(c, fct, fwd)\n\n    def exec_r(self, c: Ptr[T0], fct: T0, fwd: bool):\n        n = self.n\n        tmp = arr[T](n)\n        if fwd:\n            zero = cast(0, T0) * c[0]\n            for m in range(n):\n                tmp[m] = T(c[m], zero)\n            self.fft(tmp.data(), fct, True)\n            c[0] = tmp[0].real\n            str.memcpy((c + 1).as_byte(), (tmp.data() + 1).as_byte(), (n-1) * sizeof(T0))\n        else:\n            tmp[0] = T(c[0], c[0] * cast(0, T0))\n            str.memcpy((tmp.data() + 1).as_byte(), (c + 1).as_byte(), (n-1) * sizeof(T0))\n            if n & 1 == 0:\n                tmp[n//2] = T(tmp[n//2].real, cast(0, T0) * c[0])\n            m = 1\n            while 2*m < n:\n                tmp[n-m] = T(tmp[m].real, -tmp[m].imag)\n                m += 1\n            self.fft(tmp.data(), fct, False)\n            for m in range(n):\n                c[m] = tmp[m].real\n        tmp.dealloc()\n\n    def dealloc(self):\n        self.plan.dealloc()\n        self.mem.dealloc()\n\nclass pocketfft_c:\n    packplan: Optional[cfftp[T,T0]]\n    blueplan: Optional[fftblue[T,T0]]\n    length: int\n    T: type\n    T0: type\n\n    def __init__(self, length: int):\n        self.packplan = None\n        self.blueplan = None\n        self.length = length\n\n        if length == 0:\n            raise ValueError(\"Invalid number of FFT data points (0) specified.\")\n\n        tmp = 0 if length < 50 else _largest_prime_factor(length)\n        if tmp * tmp <= length:\n            self.packplan = cfftp[T,T0](length)\n            return\n\n        comp1 = _cost_guess(length)\n        comp2 = 2*_cost_guess(_good_size_cmplx(2*length - 1))\n        comp2 *= 1.5\n\n        if comp2 < comp1:\n            self.blueplan = fftblue[T,T0](length)\n        else:\n            self.packplan = cfftp[T,T0](length)\n\n    def exec(self, c: Ptr[T], fct: T0, fwd: bool):\n        if self.packplan is not None:\n            self.packplan.exec(c, fct, fwd)\n            self.packplan.dealloc()\n        else:\n            self.blueplan.exec(c, fct, fwd)\n            self.blueplan.dealloc()\n\nclass pocketfft_r:\n    packplan: Optional[rfftp[T,T0]]\n    blueplan: Optional[fftblue[T,T0]]\n    length: int\n    T: type\n    T0: type\n\n    def __init__(self, length: int):\n        self.packplan = None\n        self.blueplan = None\n        self.length = length\n\n        if length == 0:\n            raise ValueError(\"Invalid number of FFT data points (0) specified.\")\n\n        tmp = 0 if length < 50 else _largest_prime_factor(length)\n        if tmp * tmp <= length:\n            self.packplan = rfftp[T,T0](length)\n            return\n\n        comp1 = 0.5*_cost_guess(length)\n        comp2 = 2*_cost_guess(_good_size_cmplx(2*length - 1))\n        comp2 *= 1.5\n\n        if comp2 < comp1:\n            self.blueplan = fftblue[T,T0](length)\n        else:\n            self.packplan = rfftp[T,T0](length)\n\n    def exec(self, c: Ptr[T0], fct: T0, fwd: bool):\n        if self.packplan is not None:\n            self.packplan.exec(c, fct, fwd)\n            self.packplan.dealloc()\n        else:\n            self.blueplan.exec_r(c, fct, fwd)\n            self.blueplan.dealloc()\n\ndef _bad_norm(norm: str):\n    raise ValueError(f'Invalid norm value {repr(norm)}; should be \"backward\", \"ortho\" or \"forward\".')\n\ndef _bad_length(n: int):\n    raise ValueError(f\"Invalid number of FFT data points ({n}) specified.\")\n\ndef _swap_direction(norm: Optional[str]) -> Optional[str]:\n    if norm is None:\n        return \"forward\"\n    if norm == \"backward\":\n        return \"forward\"\n    if norm == \"ortho\":\n        return \"ortho\"\n    if norm == \"forward\":\n        return \"backward\"\n    _bad_norm(norm)\n\ndef _fft(v: Ptr[T], n: int, is_forward: bool, norm: Optional[str], T: type):\n    if n < 1:\n        _bad_length(n)\n\n    if not is_forward:\n        norm = _swap_direction(norm)\n\n    real_dtype = type(T().real)\n    fct = cast(0, real_dtype)\n\n    if norm is None or norm == \"backward\":\n        fct = cast(1, real_dtype)\n    elif norm == \"ortho\":\n        fct = cast(1, real_dtype) / sqrt(cast(n, real_dtype))\n    elif norm == \"forward\":\n        fct = cast(1, real_dtype) / cast(n, real_dtype)\n    else:\n        _bad_norm(norm)\n\n    plan = pocketfft_c(n)\n    plan.exec(v, fct, is_forward)\n\ndef fft(v: Ptr[T], n: int, norm: Optional[str], T: type):\n    return _fft(v, n, is_forward=True, norm=norm)\n\ndef ifft(v: Ptr[T], n: int, norm: Optional[str], T: type):\n    return _fft(v, n, is_forward=False, norm=norm)\n\ndef rfft(v: Ptr[T0], n: int, is_forward: bool, norm: Optional[str], o: Ptr[T], T: type, T0: type):\n    if n < 1:\n        _bad_length(n)\n\n    real_dtype = type(T().real)\n    fct = cast(0, real_dtype)\n\n    if norm is None or norm == \"backward\":\n        fct = cast(1, real_dtype)\n    elif norm == \"ortho\":\n        fct = cast(1, real_dtype) / sqrt(cast(n, real_dtype))\n    elif norm == \"forward\":\n        fct = cast(1, real_dtype) / cast(n, real_dtype)\n    else:\n        _bad_norm(norm)\n\n    plan = pocketfft_r[T, T0](n)\n    plan.exec(v, fct, True)\n\n    i = 1\n    ii = 1\n    o[0] = T(v[0], cast(0, T0))\n\n    if is_forward:\n        while i < n - 1:\n            o[ii] = T(v[i], v[i+1])\n            i += 2\n            ii += 1\n    else:\n        while i < n - 1:\n            o[ii] = T(v[i], -v[i+1])\n            i += 2\n            ii += 1\n\n    if i < n:\n        o[ii] = T(v[i], cast(0, T0))\n\ndef irfft(v: Ptr[T], n: int, norm: Optional[str], o: Ptr[T0], T: type, T0: type):\n    if n < 1:\n        _bad_length(n)\n\n    real_dtype = type(T().real)\n    fct = cast(0, real_dtype)\n    norm = _swap_direction(norm)\n\n    if norm is None or norm == \"backward\":\n        fct = cast(1, real_dtype)\n    elif norm == \"ortho\":\n        fct = cast(1, real_dtype) / sqrt(cast(n, real_dtype))\n    elif norm == \"forward\":\n        fct = cast(1, real_dtype) / cast(n, real_dtype)\n    else:\n        _bad_norm(norm)\n\n    plan = pocketfft_r[T, T0](n)\n\n    i = 1\n    ii = 1\n    o[0] = v[0].real\n    is_forward = False  # hardcoded, but kept for reference\n\n    if is_forward:\n        while i < n - 1:\n            o[i] = v[ii].real\n            o[i+1] = -v[ii].imag\n            i += 2\n            ii += 1\n    else:\n        while i < n - 1:\n            o[i] = v[ii].real\n            o[i+1] = v[ii].imag\n            i += 2\n            ii += 1\n\n    if i < n:\n        o[i] = v[ii].real\n\n    plan.exec(o, fct, False)\n"
  },
  {
    "path": "stdlib/numpy/format.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport util\nimport internal.static as static\n\nfrom .ndarray import ndarray\nfrom .npdatetime import datetime64, timedelta64\nfrom .dragon4 import format_float_positional, format_float_scientific\n\nMAX_FLOAT_PREC = 4\n\nclass DefaultFormat:\n    def __call__(self, x):\n        return str(x)\n\nclass TimeDeltaFormat:\n    def __call__(self, x):\n        return \"NaT\" if x._nat else str(x.value)\n\nclass FloatingFormat:\n    _scientific: bool\n    _sign: bool\n    _unique: bool\n    _trim: str\n    _precision: int\n    _min_digits: int\n    _pad_left: int\n    _pad_right: int\n    _exp_size: int\n\n    def __init__(self,\n                 scientific: bool = False,\n                 sign: bool = False,\n                 unique: bool = True,\n                 trim: str = '.',\n                 precision: int = -1,\n                 min_digits: int = -1,\n                 pad_left: int = -1,\n                 pad_right: int = -1,\n                 exp_size: int = -1):\n        self._scientific = scientific\n        self._sign = sign\n        self._unique = unique\n        self._trim = trim\n        self._precision = precision\n        self._min_digits = min_digits\n        self._pad_left = pad_left\n        self._pad_right = pad_right\n        self._exp_size = exp_size\n\n    def __init__(self, a: ndarray, sign: bool = False):\n        dtype = a.dtype\n        if not (dtype is float16 or\n                dtype is float32 or\n                dtype is float):\n            compile_error(\"[internal error] cannot format non-float array\")\n\n        if a.size == 0:\n            self.__init__()\n            return\n\n        min_val: Optional[a.dtype] = None\n        max_val: Optional[a.dtype] = None\n        finite = 0\n        abs_non_zero = 0\n        exp_format = False\n\n        for idx in util.multirange(a.shape):\n            x = a._ptr(idx)[0]\n            if util.isfinite(x):\n                finite += 1\n                if x:\n                    x = abs(x)\n\n                    if max_val is None or x > max_val:\n                        max_val = x\n\n                    if min_val is None or x < min_val:\n                        min_val = x\n\n                    abs_non_zero += 1\n\n        if max_val is not None and min_val is not None:\n            exp_format = (abs_non_zero != 0 and (max_val >= dtype(1.e8) or\n                          min_val < dtype(0.0001) or max_val/min_val > dtype(1000.)))\n        precision = -1\n        exp_size = -1\n        pad_left = -1\n        pad_right = -1\n\n        if finite == 0:\n            self.__init__()\n            return\n        elif exp_format:\n            for x in a.flat:\n                if util.isfinite(x):\n                    s = format_float_scientific(x, trim='.', sign=sign)\n                    frac_str, _, exp_str = s.partition('e')\n                    int_part, frac_part = frac_str.split('.')\n                    precision = max(precision, len(frac_part))\n                    exp_size = max(exp_size, len(exp_str) - 1)\n                    pad_left = max(pad_left, len(int_part))\n            pad_right = exp_size + 2 + precision\n            min_digits = 0\n        else:\n            for x in a.flat:\n                if util.isfinite(x):\n                    s = format_float_positional(x, trim='.', sign=sign, fractional=True)\n                    int_part, frac_part = s.split('.')\n                    pad_left = max(pad_left, len(int_part))\n                    pad_right = max(pad_right, len(frac_part))\n\n        precision = min(precision, MAX_FLOAT_PREC)\n        pad_right = min(pad_right, exp_size + 2 + MAX_FLOAT_PREC)\n\n        return self.__init__(scientific=exp_format,\n                             sign=sign,\n                             unique=True,\n                             trim='k',\n                             precision=MAX_FLOAT_PREC,\n                             min_digits=max(precision, 0),\n                             pad_left=pad_left,\n                             pad_right=pad_right,\n                             exp_size=exp_size,)\n\n    @property\n    def scientific(self):\n        return self._scientific\n\n    @property\n    def sign(self):\n        return self._sign\n\n    @property\n    def unique(self):\n        return self._unique\n\n    @property\n    def trim(self):\n        return self._trim\n\n    @property\n    def precision(self):\n        return self._precision if self._precision >= 0 else None\n\n    @property\n    def min_digits(self):\n        return self._min_digits if self._min_digits >= 0 else None\n\n    @property\n    def pad_left(self):\n        return self._pad_left if self._pad_left >= 0 else None\n\n    @property\n    def pad_right(self):\n        return self._pad_right if self._pad_right >= 0 else None\n\n    @property\n    def exp_size(self):\n        return self._exp_size if self._exp_size >= 0 else None\n\n    def __call__(self, x):\n        if self.scientific:\n            s = format_float_scientific(x,\n                                        sign=self.sign,\n                                        trim=self.trim,\n                                        unique=self.unique,\n                                        precision=self.precision,\n                                        pad_left=self.pad_left,\n                                        exp_digits=self.exp_size,\n                                        min_digits=self.min_digits)\n        else:\n            s = format_float_positional(x,\n                                        sign=self.sign,\n                                        trim=self.trim,\n                                        unique=self.unique,\n                                        precision=self.precision,\n                                        fractional=True,\n                                        pad_left=self.pad_left,\n                                        pad_right=self.pad_right,\n                                        min_digits=self.min_digits)\n        return s\n\nclass ComplexFormat:\n    _re_fmt: FloatingFormat\n    _im_fmt: FloatingFormat\n\n    def __init__(self, a: ndarray):\n        dtype = a.dtype\n        if not (dtype is complex or dtype is complex64):\n            compile_error(\"[internal error] cannot format non-complex array\")\n        self._re_fmt = FloatingFormat(a.real, sign=False)\n        self._im_fmt = FloatingFormat(a.imag, sign=True)\n\n    def __call__(self, x):\n        r = self._re_fmt(x.real)\n        i = self._im_fmt(x.imag)\n        sp = len(i.rstrip())\n        return f\"{r}{i[:sp]}j{i[sp:]}\"\n\ndef get_formatter(a: ndarray):\n    if (a.dtype is float16 or\n        a.dtype is float32 or\n        a.dtype is float):\n        return FloatingFormat(a)\n    elif (a.dtype is complex or\n          a.dtype is complex64):\n        return ComplexFormat(a)\n    elif isinstance(a.dtype, timedelta64):\n        return TimeDeltaFormat()\n    else:\n        return DefaultFormat()\n\n@extend\nclass ndarray:\n\n    def __repr__(self):\n        return f'array({repr(self.tolist())})'\n\n    def _str_2d(fmt, shape, front: ndarray[X,ndim], back: Optional[ndarray[X,ndim]] = None, ndim: Literal[int], X: type):\n        BOX_VL = '│'\n        BOX_HL = '─'\n        BOX_TR = '╮'\n        BOX_BR = '╯'\n        BOX_TL = '╭'\n        BOX_BL = '╰'\n        MID_N = '┬'\n        MID_E = '┤'\n        MID_S = '┴'\n        MID_W = '├'\n        INNER_VL = '│'\n        INNER_HL = '─'\n        INNER_CR = '┼'\n        TRUNC = '...'\n\n        LIMIT = 10\n        TRUNC_LEN = 3\n\n        if static.len(front.shape) != 2:\n            compile_error(\"[internal error] wrong array shape passed to str2d\")\n\n        x, y = front.shape\n        v = _strbuf()\n\n        if util.count(shape) == 0:\n            v.append(str(shape[0]))\n            for a in shape[1:]:\n                v.append(' × ')\n                v.append(str(a))\n            v.append(' array of ')\n            v.append(X.__name__)\n            v.append('\\n')\n            v.append('<empty>')\n            return v.__str__()\n\n        trunc_x = x > LIMIT\n        trunc_y = y > LIMIT\n        if not trunc_x and not trunc_y:\n            items = [fmt(front[i,j]) for i in range(x) for j in range(y)]\n        else:\n            x_real = x\n            y_real = y\n\n            if trunc_x:\n                x_real = 2*TRUNC_LEN + 1\n            if trunc_y:\n                y_real = 2*TRUNC_LEN + 1\n\n            items = []\n            i = 0\n            while i < x:\n                if trunc_x and (TRUNC_LEN <= i < x - TRUNC_LEN ):\n                    i = x - TRUNC_LEN\n                    for _ in range(y_real):\n                        items.append(TRUNC)\n                    continue\n\n                j = 0\n                while j < y:\n                    if trunc_y and (TRUNC_LEN <= j < y - TRUNC_LEN):\n                        j = y - TRUNC_LEN\n                        items.append(TRUNC)\n                        continue\n                    items.append(fmt(front[i,j]))\n                    j += 1\n\n                i += 1\n\n            if trunc_x:\n                x = 2*TRUNC_LEN + 1\n            if trunc_y:\n                y = 2*TRUNC_LEN + 1\n\n        back_items = []\n        W = max(len(s) for s in items)\n\n        if back is not None:\n            back_x, back_y = back.shape\n            i = 0\n            while i < back_x:\n                if trunc_x and (TRUNC_LEN <= i < back_x - TRUNC_LEN):\n                    i = back_x - TRUNC_LEN\n                    back_items.append(TRUNC)\n                    continue\n                back_items.append(fmt(back[i, back_y - 1]))\n                i += 1\n\n            W = max(W, max(len(s) for s in back_items))\n\n        # tag\n        v.append(str(shape[0]))\n        for a in shape[1:]:\n            v.append(' × ')\n            v.append(str(a))\n        v.append(' array of ')\n        v.append(X.__name__)\n        v.append('\\n')\n\n        # top border\n        v.append(BOX_TL)\n        for i in range(y):\n            for k in range(W + 2):\n                v.append(BOX_HL)\n            if i != y - 1:\n                v.append(MID_N)\n        v.append(BOX_TR)\n        v.append('\\n')\n\n        k1, k2 = 0, 0\n        for i in range(x):\n            # row itself\n            v.append(BOX_VL)\n            for j in range(y):\n                e = items[k1].rjust(W)\n                k1 += 1\n                v.append(' ')\n                v.append(e)\n                v.append(' ')\n                if j != y - 1:\n                    v.append(INNER_VL)\n            v.append(BOX_VL)\n\n            # behind array\n            if back is not None:\n                for _ in range(W + 2):\n                    v.append(INNER_HL)\n                v.append(BOX_TR if i == 0 else MID_E)\n\n            v.append('\\n')\n            if i != x - 1:\n                # inner border\n                v.append(MID_W)\n                for j in range(y):\n                    for k in range(W + 2):\n                        v.append(INNER_HL)\n                    if j != y - 1:\n                        v.append(INNER_CR)\n                v.append(MID_E)\n\n                # behind array\n                if back is not None:\n                    e = back_items[k2].rjust(W)\n                    k2 += 1\n                    v.append(' ')\n                    v.append(e)\n                    v.append(' ')\n                    v.append(BOX_VL)\n\n                v.append('\\n')\n\n        # bottom border\n        v.append(BOX_BL)\n        for j in range(y):\n            for k in range(W+2):\n                v.append(BOX_HL)\n            if j != y - 1:\n                v.append(MID_S)\n        v.append(BOX_BR)\n\n        # behind array\n        if back is not None:\n            e = back_items[k2].rjust(W)\n            v.append(' ')\n            v.append(e)\n            v.append(' ')\n            v.append(BOX_VL)\n\n            v.append('\\n')\n            if W >= 1:\n                v.append(' ···')\n                for _ in range(W - 1):\n                    v.append(' ')\n            else:\n                v.append(' ··')\n                for _ in range(W):\n                    v.append(' ')\n            v.append(BOX_BL)\n            for i in range(y):\n                if i > 0:\n                    v.append(MID_S)\n                for _ in range(W + 2):\n                    v.append(INNER_HL)\n            v.append(BOX_BR)\n\n        return v.__str__()\n\n    def __str__(self):\n        fmt = get_formatter(self)\n        if static.len(self.shape) == 0:\n            return f'array({repr(self.data[0])})'\n        elif static.len(self.shape) == 1:\n            return ndarray._str_2d(fmt, self.shape, self.reshape((1, self.size)))\n        elif static.len(self.shape) == 2:\n            return ndarray._str_2d(fmt, self.shape, self)\n        else:\n            if self.size == 0:\n                return ndarray._str_2d(fmt, self.shape, self.reshape((0, 0)))\n\n            rem = self.shape[:-2]\n            top_idx = tuple(0 for _ in rem)\n            bot_idx = tuple(i - 1 for i in rem)\n            top = self[top_idx]\n            bot = self[bot_idx]\n\n            if util.count(rem) == 1:\n                return ndarray._str_2d(fmt, self.shape, top)\n            else:\n                return ndarray._str_2d(fmt, self.shape, top, bot)\n"
  },
  {
    "path": "stdlib/numpy/functional.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .ndarray import ndarray\nfrom .routines import asarray, empty, expand_dims\nfrom .ufunc import UnaryUFunc, UnaryUFunc2, BinaryUFunc\n\nimport util\nimport internal.static as static\n\ndef apply_along_axis(func1d, axis: int, arr, *args, **kwargs):\n    a = asarray(arr)\n    axis = util.normalize_axis_index(axis, a.ndim)\n    n = a.shape[axis]\n    s = a.strides[axis]\n\n    first_slice = ndarray((n,), (s,), a.data)\n    f0 = asarray(func1d(first_slice, *args, **kwargs))\n\n    a_iter_space = util.tuple_delete(a.shape, axis)\n    new_shape = util.tuple_insert_tuple(a_iter_space, axis, f0.shape)\n    ans = empty(new_shape, f0.dtype)\n    first = True\n\n    for idx0 in util.multirange(a_iter_space):\n        if first:\n            fx = f0\n            first = False\n        else:\n            idx = util.tuple_insert(idx0, axis, 0)\n            next_slice = ndarray((n,), (s,), a._ptr(idx))\n            fx = asarray(func1d(next_slice, *args, **kwargs))\n\n            if fx.shape != f0.shape:\n                raise ValueError(\"function is returning arrays of different shape\")\n\n        if fx.ndim > 0:\n            for idx1 in util.multirange(fx.shape):\n                idx2 = util.tuple_insert_tuple(idx0, axis, idx1)\n                p = fx._ptr(idx1)\n                q = ans._ptr(idx2)\n                q[0] = p[0]\n        else:\n            ans._ptr(idx0)[0] = fx.item()\n\n    return ans\n\ndef apply_over_axes(func, a, axes):\n    a = asarray(a)\n    dtype = func(a, 0).dtype\n\n    if static.len(dtype) >= 0:\n        val = a.astype(dtype)\n        if asarray(axes).ndim == 0:\n            ax = (axes,)\n        else:\n            ax = axes\n\n        for axis in ax:\n            if axis < 0:\n                axis += val.ndim\n            b = func(val, axis)\n\n            if isinstance(b, ndarray):\n                if b.ndim == val.ndim:\n                    val = b\n                elif b.ndim == val.ndim - 1:\n                    val = expand_dims(b, axis)\n                else:\n                    compile_error(\"function is not returning an array of the correct shape\")\n            else:\n                compile_error(\"function is not returning ndarray\")\n\n        return val\n\ndef frompyfunc(func, nin: Literal[int], nout: Literal[int], identity):\n    if nin == 1 and nout == 1:\n        return UnaryUFunc(func, func.__name__ + ' (vectorized)')\n    elif nin == 1 and nout == 2:\n        return UnaryUFunc2(func, func.__name__ + ' (vectorized)')\n    elif nin == 2 and nout == 1:\n        return BinaryUFunc(func, func.__name__ + ' (vectorized)', identity)\n    else:\n        compile_error(\"given combination of 'nin' and 'nout' is not supported\")\n\ndef vectorize(pyfunc):\n    import internal.static as S\n    nout: Literal[int] = 1\n    nin: Literal[int] = static.len(static.function.args(pyfunc))\n    return frompyfunc(pyfunc, nin=nin, nout=nout, identity=None)\n\ndef piecewise(x, condlist: List, funclist: List, *args, **kw):\n    x = asarray(x)\n    ans = empty(x.shape, x.dtype)\n\n    n_condlist = len(condlist)\n    n_funclist = len(funclist)\n\n    if n_condlist != n_funclist and n_condlist + 1 != n_funclist:\n        raise ValueError(f\"with {n_condlist} conditions, either {n_condlist} or {n_condlist + 1} functions are expected\")\n\n    for cond in condlist:\n        acond = asarray(cond)\n        if acond.ndim != x.ndim:\n            compile_error(\"condition dimension does not match input domain dimension\")\n        if acond.shape != x.shape:\n            raise ValueError(\"condition shape does not match input domain shape\")\n\n    for idx in util.multirange(x.shape):\n        e = x._ptr(idx)[0]\n        i = 0\n        for cond in condlist:\n            if asarray(cond)._ptr(idx)[0]:\n                break\n            i += 1\n\n        if i == n_condlist and n_condlist + 1 == n_funclist:\n            f = funclist[-1]\n        else:\n            f = funclist[i]\n\n        if hasattr(f, \"__call__\"):\n            e = util.cast(f(e, *args, **kw), x.dtype)\n        else:\n            e = util.cast(f, x.dtype)\n\n        ans._ptr(idx)[0] = e\n\n    return ans\n"
  },
  {
    "path": "stdlib/numpy/fusion.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport util\nimport zmath\nimport internal.static as static\n\nfrom .ndarray import ndarray\nfrom .routines import asarray, broadcast_to, empty_like\n\n# Utility\n\ndef _cast(x, dtype: type):\n    return util.cast(x, dtype)\n\ndef _coerce(dtype1: type, dtype2: type):\n    return util.coerce(dtype1, dtype2)\n\ndef _count(shape):\n    return util.count(shape)\n\ndef _contig_match(arrs):\n\n    def keep_arrays(arrs):\n        if static.len(arrs) == 0:\n            return ()\n        else:\n            if hasattr(arrs[0], \"_contig\"):\n                return (arrs[0],) + keep_arrays(arrs[1:])\n            else:\n                return keep_arrays(arrs[1:])\n\n    arrs = keep_arrays(arrs)\n    if static.len(arrs) == 0:\n        return True\n\n    all_cc = True\n    all_fc = True\n\n    for i in static.range(static.len(arrs)):\n        a = arrs[i]\n        cc, fc = a._contig\n\n        if a.ndim != arrs[0].ndim:\n            return False\n        else:\n            if a.shape != arrs[0].shape:\n                return False\n\n        all_cc = all_cc and cc\n        all_fc = all_fc and fc\n\n    return all_cc or all_fc\n\ndef _ptrset(p: Ptr[T], x: T, T: type):\n    p[0] = x\n\ndef _loop_alloc(arrays, func, extra, dtype: type):\n    return ndarray._loop(arrays, func, alloc=Tuple[dtype], extra=extra)[0]\n\ndef _loop_basic(arrays, func, extra):\n    ndarray._loop(arrays, func, extra=extra)\n\ndef _broadcast(sh1, sh2):\n\n    def bc_one(sh1, sh2, i: Literal[int]):\n        a = sh1[i]\n        b = sh2[i]\n        if a == 1 or b == 1 or a == b:\n            return max(a, b)\n        else:\n            raise ValueError(f\"operands could not be broadcast together with shapes {sh1} {sh2}\")\n\n    def bc_same(sh1, sh2):\n        return tuple(bc_one(sh1, sh2, i) for i in static.range(static.len(sh1)))\n\n    N1: Literal[int] = static.len(sh1)\n    N2: Literal[int] = static.len(sh2)\n\n    if N1 == 0:\n        return sh2\n    elif N2 == 0:\n        return sh1\n    elif N1 > N2:\n        return sh1[:-N2] + bc_same(sh1[-N2:], sh2)\n    elif N1 < N2:\n        return sh2[:-N1] + bc_same(sh1, sh2[-N1:])\n    else:\n        return bc_same(sh1, sh2)\n\ndef _matmul_shape(x1, x2):\n    x1d: Literal[int] = static.len(x1)\n    x2d: Literal[int] = static.len(x2)\n\n    if x1d == 0:\n        return x2\n\n    if x2d == 0:\n        return x1\n\n    if x1d == 1:\n        y1 =  (1,) + x1\n    else:\n        y1 = x1\n\n    if x2d == 1:\n        y2 = x2 + (1,)\n    else:\n        y2 = x2\n\n    y1d: Literal[int] = static.len(y1)\n    y2d: Literal[int] = static.len(y2)\n\n    base1s = y1[:-2]\n    base2s = y2[:-2]\n    mat1s = y1[-2:]\n    mat2s = y2[-2:]\n\n    m = mat1s[0]\n    k = mat1s[1]\n    n = mat2s[1]\n\n    if k != mat2s[0]:\n        raise ValueError(\"matmul: last dimension of first argument does not \"\n                         \"match second-to-last dimension of second argument\")\n\n    ans_base = _broadcast(base1s, base2s)\n    if x1d == 1 and x2d == 1:\n        return ans_base\n    elif x1d == 1:\n        return ans_base + (mat2s[1],)\n    elif x2d == 1:\n        return ans_base + (mat1s[0],)\n    else:\n        return ans_base + (mat1s[0], mat2s[1])\n\ndef _create(like, shape, dtype: type):\n    return empty_like(like, shape=shape, dtype=dtype)\n\ndef _shape(x):\n    if hasattr(x, \"shape\"):\n        return x.shape\n    else:\n        return ()\n\ndef _free(x):\n    util.free(x.data)\n\ndef _apply_vectorized_loop_unary(arr, out, func: Literal[str]):\n    if arr.ndim == 0 or out.ndim == 0 or arr.ndim > out.ndim:\n        compile_error(\"[internal error] bad array dims for vectorized loop\")\n\n    if out.ndim == 1:\n        util.call_vectorized_loop(arr.data, arr.strides[0], Ptr[arr.dtype](),\n                                  0, out.data, out.strides[0], out.size, func)\n        return\n\n    shape = arr.shape\n    arr = broadcast_to(arr, shape)\n\n    if arr._contig_match(out):\n        s = util.sizeof(out.dtype)\n        util.call_vectorized_loop(arr.data, s, Ptr[arr.dtype](), 0, out.data,\n                                  s, out.size, func)\n    else:\n        # Find smallest stride to use in vectorized loop\n        arr_strides = arr.strides\n        out_strides = out.strides\n        n = 0\n        si = 0\n        so = 0\n        loop_axis = -1\n\n        for i in static.range(arr.ndim):\n            if shape[i] > 1 and (loop_axis == -1 or abs(arr_strides[i]) < abs(si)):\n                n = shape[i]\n                si = arr_strides[i]\n                so = out_strides[i]\n                loop_axis = i\n\n        if loop_axis == -1:\n            n = shape[0]\n            si = arr_strides[0]\n            so = out_strides[0]\n            loop_axis = 0\n\n        for idx in util.multirange(util.tuple_delete(shape, loop_axis)):\n            idx1 = util.tuple_insert(idx, loop_axis, 0)\n            p = arr._ptr(idx1)\n            q = out._ptr(idx1)\n            util.call_vectorized_loop(p, si, Ptr[arr.dtype](), 0, q, so, n,\n                                      func)\n\ndef _apply_vectorized_loop_binary(arr1, arr2, out, func: Literal[str]):\n    if (arr1.ndim == 0 and arr2.ndim == 0) or out.ndim == 0 or arr1.ndim > out.ndim or arr2.ndim > out.ndim:\n        compile_error(\"[internal error] bad array dims for vectorized loop\")\n\n    if arr1.ndim == 0:\n        st1 = 0\n    else:\n        st1 = arr1.strides[0]\n\n    if arr2.ndim == 0:\n        st2 = 0\n    else:\n        st2 = arr2.strides[0]\n\n    if out.ndim == 1:\n        util.call_vectorized_loop(arr1.data, st1, arr2.data,\n                                  st2, out.data, out.strides[0],\n                                  out.size, func)\n        return\n\n    shape = out.shape\n    arr1 = broadcast_to(arr1, shape)\n    arr2 = broadcast_to(arr2, shape)\n\n    if arr1._contig_match(out) and arr2._contig_match(out):\n        s = util.sizeof(out.dtype)\n        util.call_vectorized_loop(arr1.data, s, arr2.data, s, out.data, s, out.size, func)\n    else:\n        # Find smallest stride to use in vectorized loop\n        arr1_strides = arr1.strides\n        arr2_strides = arr2.strides\n        out_strides = out.strides\n        n = 0\n        si1 = 0\n        si2 = 0\n        so = 0\n        loop_axis = -1\n\n        for i in static.range(arr1.ndim):\n            if shape[i] > 1 and (loop_axis == -1 or abs(arr1_strides[i]) < abs(si1)):\n                n = shape[i]\n                si1 = arr1_strides[i]\n                si2 = arr2_strides[i]\n                so = out_strides[i]\n                loop_axis = i\n\n        if loop_axis == -1:\n            n = shape[0]\n            si1 = arr1_strides[0]\n            si2 = arr2_strides[0]\n            so = out_strides[0]\n            loop_axis = 0\n\n        for idx in util.multirange(util.tuple_delete(shape, loop_axis)):\n            idx1 = util.tuple_insert(idx, loop_axis, 0)\n            p1 = arr1._ptr(idx1)\n            p2 = arr2._ptr(idx1)\n            q = out._ptr(idx1)\n            util.call_vectorized_loop(p1, si1, p2, si2, q, so, n, func)\n\n# Operations\n\n@inline\ndef _pos(x):\n    return +x\n\n@inline\ndef _neg(x):\n    return -x\n\n@inline\ndef _invert(x):\n    return ~x\n\n@inline\ndef _abs(x):\n    return abs(x)\n\n@inline\ndef _transpose(x):\n    return x.T\n\n@inline\ndef _add(x, y):\n    return x + y\n\n@inline\ndef _sub(x, y):\n    return x - y\n\n@inline\ndef _mul(x, y):\n    return x * y\n\n@inline\ndef _matmul(x, y):\n    return x @ y\n\n@inline\ndef _true_div(x, y):\n    return x / y\n\n@inline\ndef _floor_div(x, y):\n    X = type(x)\n    Y = type(y)\n    if isinstance(X, Int) and isinstance(Y, Int):\n        return util.pydiv(x, y)\n    else:\n        return x // y\n\n@inline\ndef _mod(x, y):\n    X = type(x)\n    Y = type(y)\n    if isinstance(X, Int) and isinstance(Y, Int):\n        return util.pymod(x, y)\n    elif ((X is float and Y is float) or\n          (X is float32 and Y is float32) or\n          (X is float16 and Y is float16)):\n        return util.pyfmod(x, y)\n    else:\n        return x % y\n\n@inline\ndef _fmod(x, y):\n    X = type(x)\n    Y = type(y)\n    if isinstance(X, Int) and isinstance(Y, Int):\n        return util.cmod_int(x, y)\n    elif ((X is float and Y is float) or\n        (X is float32 and Y is float32) or\n        (X is float16 and Y is float16)):\n        return util.cmod(x, y)\n    else:\n        return x % y\n\n@inline\ndef _pow(x, y):\n    return x ** y\n\n@inline\ndef _lshift(x, y):\n    return x << y\n\n@inline\ndef _rshift(x, y):\n    return x >> y\n\n@inline\ndef _and(x, y):\n    return x & y\n\n@inline\ndef _or(x, y):\n    return x | y\n\n@inline\ndef _xor(x, y):\n    return x ^ y\n\n@inline\ndef _eq(x, y):\n    return x == y\n\n@inline\ndef _ne(x, y):\n    return x != y\n\n@inline\ndef _lt(x, y):\n    return x < y\n\n@inline\ndef _le(x, y):\n    return x <= y\n\n@inline\ndef _gt(x, y):\n    return x > y\n\n@inline\ndef _ge(x, y):\n    return x >= y\n\ndef _apply(x, f, f_complex = None):\n    if f_complex is not None and (isinstance(x, complex) or isinstance(x, complex64)):\n        return f_complex(x)\n    elif isinstance(x, float) or isinstance(x, float32) or isinstance(x, float16):\n        return f(x)\n    else:\n        return f(util.to_float(x))\n\ndef _apply2(x, y, f, f_complex = None):\n    if type(x) is not type(y):\n        compile_error(\"type mismatch in util\")\n\n    if f_complex is not None and (isinstance(x, complex) or isinstance(x, complex64)):\n        return f_complex(x, y)\n    elif isinstance(x, float) or isinstance(x, float32) or isinstance(x, float16):\n        return f(x, y)\n    else:\n        return f(util.to_float(x), util.to_float(y))\n\ndef _fabs(x):\n    return _apply(x, util.fabs)\n\ndef _rint(x):\n    def rint_complex(x):\n        C = type(x)\n        return C(util.rint(x.real), util.rint(x.imag))\n\n    return _apply(x, util.rint, rint_complex)\n\ndef _exp(x):\n    return _apply(x, util.exp, zmath.exp)\n\ndef _exp2(x):\n    return _apply(x, util.exp2, zmath.exp2)\n\ndef _expm1(x):\n    return _apply(x, util.expm1, zmath.expm1)\n\ndef _log(x):\n    return _apply(x, util.log, zmath.log)\n\ndef _log2(x):\n    return _apply(x, util.log2, zmath.log2)\n\ndef _log10(x):\n    return _apply(x, util.log10, zmath.log10)\n\ndef _log1p(x):\n    return _apply(x, util.log1p, zmath.log1p)\n\ndef _sqrt(x):\n    return _apply(x, util.sqrt, zmath.sqrt)\n\ndef _cbrt(x):\n    return _apply(x, util.cbrt)\n\ndef _square(x):\n    return x * x\n\ndef _sin(x):\n    return _apply(x, util.sin, zmath.sin)\n\ndef _cos(x):\n    return _apply(x, util.cos, zmath.cos)\n\ndef _tan(x):\n    return _apply(x, util.tan, zmath.tan)\n\ndef _arcsin(x):\n    return _apply(x, util.asin, zmath.asin)\n\ndef _arccos(x):\n    return _apply(x, util.acos, zmath.acos)\n\ndef _arctan(x):\n    return _apply(x, util.atan, zmath.atan)\n\ndef _sinh(x):\n    return _apply(x, util.sinh, zmath.sinh)\n\ndef _cosh(x):\n    return _apply(x, util.cosh, zmath.cosh)\n\ndef _tanh(x):\n    return _apply(x, util.tanh, zmath.tanh)\n\ndef _arcsinh(x):\n    return _apply(x, util.asinh, zmath.asinh)\n\ndef _arccosh(x):\n    return _apply(x, util.acosh, zmath.acosh)\n\ndef _arctanh(x):\n    return _apply(x, util.atanh, zmath.atanh)\n\ndef _rad2deg(x):\n    r2d = 180.0 / util.PI\n    x = util.to_float(x)\n    F = type(x)\n    return x * F(r2d)\n\ndef _deg2rad(x):\n    d2r = util.PI / 180.0\n    x = util.to_float(x)\n    F = type(x)\n    return x * F(d2r)\n\ndef _arctan2(x, y):\n    return _apply2(x, y, util.atan2)\n\ndef _hypot(x, y):\n    return _apply2(x, y, util.hypot)\n\ndef _logaddexp(x, y):\n    return _apply2(x, y, util.logaddexp)\n\ndef _logaddexp2(x, y):\n    return _apply2(x, y, util.logaddexp2)\n\ndef _isnan(x):\n    if isinstance(x, float) or isinstance(x, float32) or isinstance(x, float16):\n        return util.isnan(x)\n    elif isinstance(x, complex) or isinstance(x, complex64):\n        return util.isnan(x.real) or util.isnan(x.imag)\n    else:\n        return False\n\ndef _isinf(x):\n    if isinstance(x, float) or isinstance(x, float32) or isinstance(x, float16):\n        return util.isinf(x)\n    elif isinstance(x, complex) or isinstance(x, complex64):\n        return util.isinf(x.real) or util.isinf(x.imag)\n    else:\n        return False\n\ndef _isfinite(x):\n    if isinstance(x, float) or isinstance(x, float32) or isinstance(x, float16):\n        return util.isfinite(x)\n    elif isinstance(x, complex) or isinstance(x, complex64):\n        return util.isfinite(x.real) and util.isfinite(x.imag)\n    else:\n        return True\n\ndef _signbit(x):\n    if isinstance(x, float) or isinstance(x, float32) or isinstance(x, float16):\n        return util.signbit(x)\n    else:\n        T = type(x)\n        return x < T()\n\ndef _copysign(x, y):\n    return _apply2(x, y, util.copysign)\n\ndef _nextafter(x, y):\n    return _apply2(x, y, util.nextafter)\n\ndef _floor(x):\n    return _apply(x, util.floor)\n\ndef _ceil(x):\n    return _apply(x, util.ceil)\n\ndef _trunc(x):\n    return _apply(x, util.trunc)\n\ndef _sign(x):\n    def sign1(x):\n        T = type(x)\n        if x < T(0):\n            return T(-1)\n        elif x > T(0):\n            return T(1)\n        else:\n            return x\n\n    if isinstance(x, complex):\n        if _isnan(x):\n            return complex(util.nan64(), 0.0)\n        return complex(sign1(x.real), 0) if x.real else complex(sign1(x.imag), 0)\n    elif isinstance(x, complex64):\n        if _isnan(x):\n            return complex64(util.nan64(), 0.0)\n        return complex64(sign1(x.real), 0) if x.real else complex64(sign1(x.imag), 0)\n    else:\n        return sign1(x)\n\ndef _heaviside(x, y):\n    def heaviside(x, y):\n        if isinstance(x, float16) and isinstance(y, float16):\n            if x < float16(0):\n                return float16(0)\n            elif x > float16(0):\n                return float16(1)\n            elif x == float16(0):\n                return y\n            else:\n                return x\n        elif isinstance(x, float32) and isinstance(y, float32):\n            if x < float32(0):\n                return float32(0)\n            elif x > float32(0):\n                return float32(1)\n            elif x == float32(0):\n                return y\n            else:\n                return x\n        elif isinstance(x, float) and isinstance(y, float):\n            if x < 0:\n                return 0.0\n            elif x > 0:\n                return 1.0\n            elif x == 0.0:\n                return y\n            else:\n                return x\n\n    return _apply2(x, y, heaviside)\n\ndef _conj(x):\n    if isinstance(x, complex) or isinstance(x, complex64):\n        return x.conjugate()\n    else:\n        return x\n\ndef _gcd(x, y):\n    while x:\n        z = x\n        x = y % x\n        y = z\n    return y\n\ndef _lcm(x, y):\n    gcd = _gcd(x, y)\n    return x // gcd * y if gcd else 0\n\ndef _reciprocal(x: T, T: type):\n    if (\n        isinstance(x, int) or\n        isinstance(x, Int) or\n        isinstance(x, UInt) or\n        isinstance(x, byte)\n    ):\n        return T(1) // x\n    else:\n        return T(1) / x\n\ndef _logical_and(x, y):\n    return bool(x) and bool(y)\n\ndef _logical_or(x, y):\n    return bool(x) or bool(y)\n\ndef _logical_xor(x, y):\n    return bool(x) ^ bool(y)\n\ndef _logical_not(x):\n    return not bool(x)\n\ndef _coerce_types_for_minmax(x, y):\n    if isinstance(x, complex):\n        if isinstance(y, complex64):\n            return x, complex(y)\n        elif not isinstance(y, complex):\n            return x, complex(util.cast(y, float))\n    elif isinstance(x, complex64):\n        if isinstance(y, complex):\n            return complex(x), y\n        elif not isinstance(y, complex64):\n            return complex(x), complex(util.cast(y, float))\n\n    if isinstance(y, complex):\n        if isinstance(x, complex64):\n            return complex(x), y\n        elif not isinstance(x, complex):\n            return complex(util.cast(x, float)), y\n    elif isinstance(y, complex64):\n        if isinstance(x, complex):\n           return x, complex(y)\n        elif not isinstance(x, complex64):\n            return complex(util.cast(x, float)), complex(y)\n\n    T = type(util.coerce(type(x), type(y)))\n    return util.cast(x, T), util.cast(y, T)\n\ndef _compare_le(x, y):\n    if isinstance(x, complex) or isinstance(x, complex64):\n        return (x.real, x.imag) <= (y.real, y.imag)\n    else:\n        return x <= y\n\ndef _compare_ge(x, y):\n    if isinstance(x, complex) or isinstance(x, complex64):\n        return (x.real, x.imag) >= (y.real, y.imag)\n    else:\n        return x >= y\n\ndef _maximum(x, y):\n    x, y = _coerce_types_for_minmax(x, y)\n\n    if _isnan(x):\n        return x\n\n    if _isnan(y):\n        return y\n\n    return x if _compare_ge(x, y) else y\n\ndef _minimum(x, y):\n    x, y = _coerce_types_for_minmax(x, y)\n\n    if _isnan(x):\n        return x\n\n    if _isnan(y):\n        return y\n\n    return x if _compare_le(x, y) else y\n\ndef _fmax(x, y):\n    x, y = _coerce_types_for_minmax(x, y)\n\n    if _isnan(y):\n        return x\n\n    if _isnan(x):\n        return y\n\n    return x if _compare_ge(x, y) else y\n\ndef _fmin(x, y):\n    x, y = _coerce_types_for_minmax(x, y)\n\n    if _isnan(y):\n        return x\n\n    if _isnan(x):\n        return y\n\n    return x if _compare_le(x, y) else y\n\ndef _divmod_float(x, y):\n    F = type(x)\n    mod = util.cmod(x, y)\n    if not y:\n        return util.cdiv(x, y), mod\n\n    div = util.cdiv(x - mod, y)\n    if mod:\n        if (y < F(0)) != (mod < F(0)):\n            mod += y\n            div -= F(1)\n    else:\n        mod = util.copysign(F(0), y)\n\n    floordiv = F()\n    if div:\n        floordiv = util.floor(div)\n        if div - floordiv > F(0.5):\n            floordiv += F(1)\n    else:\n        floordiv = util.copysign(F(0), util.cdiv(x, y))\n\n    return floordiv, mod\n\ndef _divmod(x, y):\n    if isinstance(x, float16) and isinstance(y, float16):\n        return _divmod_float(x, y)\n\n    if isinstance(x, float32) and isinstance(y, float32):\n        return _divmod_float(x, y)\n\n    if isinstance(x, float) or isinstance(y, float):\n        return _divmod_float(util.cast(x, float), util.cast(y, float))\n\n    return (x // y, x % y)\n\ndef _modf(x):\n    return _apply(x, util.modf)\n\ndef _frexp(x):\n    def frexp(x):\n        a, b = util.frexp(x)\n        return a, i32(b)\n\n    return _apply(x, frexp)\n\ndef _spacing16(h: float16):\n    h_u16 = util.bitcast(h, u16)\n    h_exp = h_u16 & u16(0x7c00)\n    h_sig = h_u16 & u16(0x03ff)\n\n    if h_exp == u16(0x7c00):\n        return util.nan16()\n    elif h_u16 == u16(0x7bff):\n        return util.inf16()\n    elif (h_u16 & u16(0x8000)) and not h_sig:\n        if h_exp > u16(0x2c00):\n            return util.bitcast(h_exp - u16(0x2c00), float16)\n        elif h_exp > u16(0x0400):\n            return util.bitcast(u16(1) << ((h_exp >> u16(10)) - u16(2)), float16)\n        else:\n            return util.bitcast(u16(0x0001), float16)\n    elif h_exp > u16(0x2800):\n        return util.bitcast(h_exp - u16(0x2800), float16)\n    elif h_exp > u16(0x0400):\n        return util.bitcast(u16(1) << ((h_exp >> u16(10)) - u16(1)), float16)\n    else:\n        return util.bitcast(u16(0x0001), float16)\n\ndef _spacing(x):\n    if isinstance(x, float16):\n        return _spacing16(x)\n    elif isinstance(x, float32):\n        if util.isinf32(x):\n            return util.nan32()\n        p = util.inf32() if x >= float32(0) else -util.inf32()\n        return util.nextafter32(x, util.inf32()) - x\n    elif isinstance(x, float):\n        x = util.cast(x, float)\n        if util.isinf64(x):\n            return util.nan64()\n        p = util.inf64() if x >= 0 else -util.inf64()\n        return util.nextafter64(x, p) - x\n    else:\n        return _spacing(util.to_float(x))\n\n\n# Indexing optimization\n\ndef _array1d_get_nocheck(arr: ndarray[T, 1], idx: int, T: type):\n    p = arr.data.as_byte() + (arr.strides[0] * idx)\n    return Ptr[T](p)[0]\n\ndef _array1d_set_nocheck(arr: ndarray[T, 1], idx: int, elem: S, T: type, S: type):\n    p = arr.data.as_byte() + (arr.strides[0] * idx)\n    Ptr[T](p)[0] = util.cast(elem, T)\n"
  },
  {
    "path": "stdlib/numpy/indexing.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport util\nimport internal.static as static\n\nfrom .ndarray import *\nfrom .routines import *\n\ndef _multiindex(indexes, shape, index: int = 0):\n    if static.len(indexes) != static.len(shape):\n        compile_error(\"[internal error] bad multi-index\")\n\n    if static.len(indexes) == 0:\n        return ()\n\n    idx = indexes[0]\n    n = shape[0]\n    rest = _multiindex(indexes[1:], shape[1:], index + 1)\n\n    if isinstance(idx, int):\n        idx = util.normalize_index(idx, index, n)\n        return ((idx, idx + 1, 1, 1), *rest)\n    else:\n        return (idx.adjust_indices(n), *rest)\n\ndef _keep_axes(indexes, index: int = 0):\n    if static.len(indexes) == 0:\n        return ()\n\n    idx = indexes[0]\n    rest = _keep_axes(indexes[1:], index + 1)\n\n    if isinstance(idx, int):\n        return rest\n    else:\n        return (index, *rest)\n\ndef _base_offset(mindices, strides):\n    offset = 0\n    for i in static.range(static.len(mindices)):\n        offset += mindices[i][0] * strides[i]\n    return offset\n\ndef _new_shape(mindex, keep):\n    return tuple(mindex[i][3] for i in keep)\n\ndef _new_strides(mindex, strides, keep):\n    return tuple(mindex[i][2] * strides[i] for i in keep)\n\ndef _extract_special(indexes):\n    if static.len(indexes) == 0:\n        return (), ()\n\n    idx = indexes[0]\n    rest_newaxis, rest_ellipsis = _extract_special(indexes[1:])\n\n    if isinstance(idx, type(newaxis)):\n        return (idx, *rest_newaxis), rest_ellipsis\n    elif isinstance(idx, type(Ellipsis)):\n        return rest_newaxis, (idx, *rest_ellipsis)\n    else:\n        return rest_newaxis, rest_ellipsis\n\ndef _expand_ellipsis(indexes, n: Literal[int], k: Literal[int] = 1):\n    if k == 0:\n        return indexes\n\n    if static.len(indexes) == 0:\n        return ()\n\n    idx = indexes[0]\n    rest = _expand_ellipsis(indexes[1:], n, k)\n\n    if isinstance(idx, type(Ellipsis)):\n        s = slice(None, None, None)\n        return ((s,) * n) + rest\n    else:\n        return (idx,) + rest\n\ndef _expand_remainder(indexes, n: Literal[int]):\n    if n > 0:\n        s = slice(None, None, None)\n        return indexes + ((s,) * n)\n    else:\n        return indexes\n\ndef _expand_newaxis(indexes, shape, strides):\n    if static.len(indexes) == 0:\n        return (), (), ()\n\n    idx = indexes[0]\n    if isinstance(idx, type(newaxis)):\n        rest_indexes, rest_shape, rest_strides = _expand_newaxis(indexes[1:], shape, strides)\n        return (slice(None, None, None), *rest_indexes), (1, *rest_shape), (0, *rest_strides)\n    else:\n        rest_indexes, rest_shape, rest_strides = _expand_newaxis(indexes[1:], shape[1:], strides[1:])\n        return (idx, *rest_indexes), (shape[0], *rest_shape), (strides[0], *rest_strides)\n\ndef _extract_sequences(indexes):\n    if static.len(indexes) == 0:\n        return ()\n\n    idx = indexes[0]\n    rest = _extract_sequences(indexes[1:])\n\n    if (isinstance(idx, ndarray) or\n        isinstance(idx, List) or\n        isinstance(idx, Tuple)):\n        return (idx,) + rest\n    else:\n        return rest\n\ndef _adv_idx_convert(idx):\n    if idx is None or isinstance(idx, slice) or isinstance(idx, int):\n        return idx\n    elif isinstance(idx, ndarray) or isinstance(idx, List) or isinstance(idx, Tuple):\n        arr = asarray(idx)\n        if arr.dtype is not int and arr.dtype is not bool:\n            compile_error(\"advanced indexing requires integer arrays\")\n        return arr\n    else:\n        compile_error(\"unsupported index type: \" + type(idx).__name__)\n\ndef _adv_idx_replace_bools(indexes):\n    if static.len(indexes) == 0:\n        return ()\n\n    idx = indexes[0]\n    rest = _adv_idx_replace_bools(indexes[1:])\n\n    if isinstance(idx, ndarray):\n        if idx.dtype is bool:\n            return idx.nonzero() + rest\n        else:\n            return (idx,) + rest\n    else:\n        return (idx,) + rest\n\ndef _adv_idx_length(idx, dim: int):\n    if isinstance(idx, slice):\n        return idx.adjust_indices(dim)[-1]\n    elif isinstance(idx, int):\n        return 0\n    elif idx is None:\n        return 1\n    else:\n        compile_error(\"[internal error]: bad input type\")\n\ndef _adv_idx_iter_non_arrays(indexes, shape):\n    if static.len(indexes) == 0:\n        yield ()\n    else:\n        idx = indexes[0]\n        dim = shape[0]\n        start, stop, step = idx.adjust_indices(dim)\n\n        k = 0\n        for i in range(start, stop, step):\n            if static.len(indexes) == 1:\n                yield ((k, i),)\n            else:\n                for rest in _adv_idx_iter_non_arrays(indexes[1:], shape[1:]):\n                    yield ((k, i),) + rest\n            k += 1\n\ndef _adv_idx_gather_arrays(indexes, k: Literal[int] = 0):\n    if static.len(indexes) == 0:\n        return (), ()\n\n    idx = indexes[0]\n    rest, rest_where = _adv_idx_gather_arrays(indexes[1:], k + 1)\n\n    if isinstance(idx, ndarray):\n        return (idx,) + rest, (k,) + rest_where\n    else:\n        return rest, rest_where\n\ndef _adv_idx_gather_non_arrays(indexes, k: Literal[int] = 0):\n    if static.len(indexes) == 0:\n        return (), ()\n\n    idx = indexes[0]\n    rest, rest_where = _adv_idx_gather_non_arrays(indexes[1:], k + 1)\n\n    if not isinstance(idx, ndarray):\n        return (idx,) + rest, (k,) + rest_where\n    else:\n        return rest, rest_where\n\ndef _adv_idx_replace_int(idx):\n    if idx is None or isinstance(idx, int):\n        return idx\n    else:\n        return slice(None, None, None)\n\ndef _adv_idx_prune_index(indexes):\n    if static.len(indexes) == 0:\n        return ()\n\n    idx = indexes[0]\n    rest = _adv_idx_prune_index(indexes[1:])\n\n    if idx is None:\n        return (slice(None, None, None),) + rest\n    elif isinstance(idx, int):\n        return rest\n    else:\n        return (idx,) + rest\n\ndef _adv_idx_gather_none_and_int(indexes):\n    if static.len(indexes) == 0:\n        return ()\n\n    idx = indexes[0]\n    rest = _adv_idx_gather_none_and_int(indexes[1:])\n\n    if idx is None or isinstance(idx, int):\n        return (idx,) + rest\n    else:\n        return rest\n\ndef _adv_idx_eliminate_new_and_used(arr, indexes):\n    if static.len(_adv_idx_gather_none_and_int(indexes)) == 0:\n        # nothing to do\n        return arr, indexes\n    else:\n        elim_idx = tuple(_adv_idx_replace_int(idx) for idx in indexes)\n        arr = arr[elim_idx]\n        indexes = _adv_idx_prune_index(indexes)\n        return arr, indexes\n\ndef _adv_idx_build_for_contig_array(indexes, shape_from_non_arrays, arr_shape, saw_array: Literal[bool] = False):\n    if static.len(indexes) == 0:\n        return ()\n\n    idx = indexes[0]\n    if isinstance(idx, ndarray):\n        if saw_array:\n            return _adv_idx_build_for_contig_array(indexes[1:], shape_from_non_arrays, arr_shape, saw_array)\n        else:\n            return arr_shape + _adv_idx_build_for_contig_array(indexes[1:], shape_from_non_arrays, arr_shape, True)\n    else:\n        return (shape_from_non_arrays[0],) + _adv_idx_build_for_contig_array(indexes[1:], shape_from_non_arrays[1:], arr_shape, saw_array)\n\ndef _bool_idx_get_bool_index(indexes):\n    if isinstance(indexes, ndarray):\n        if indexes.dtype is bool:\n            return (indexes,)\n        else:\n            return ()\n    elif isinstance(indexes, List):\n        if asarray(indexes).dtype is bool:\n            return (indexes,)\n        else:\n            return ()\n    else:\n        return ()\n\ndef _bool_idx_num_true(indexes, sz: int):\n    num_true = 0\n    if indexes._is_contig:\n        for i in range(sz):\n            if indexes.data[i]:\n                num_true += 1\n    else:\n        for idx in util.multirange(indexes.shape):\n            if indexes._ptr(idx)[0]:\n                num_true += 1\n\n    return num_true\n\n# adapted from routines\ndef _broadcast_shapes(*args):\n    def _largest(args):\n        if static.len(args) == 1:\n            return args[0]\n\n        a = args[0]\n        b = _largest(args[1:])\n        if static.len(b) > static.len(a):\n            return b\n        else:\n            return a\n\n    if static.len(args) == 0:\n        return ()\n\n    t = _largest(args)\n    N: Literal[int] = static.len(t)\n    ans = (0,) * N\n    p = Ptr[int](__ptr__(ans).as_byte())\n\n    for i in static.range(N):\n        p[i] = t[i]\n\n    for a in args:\n        for i in static.range(static.len(a)):\n            x = a[len(a) - 1 - i]\n            q = p + (len(t) - 1 - i)\n            y = q[0]\n\n            if y == 1:\n                q[0] = x\n            elif x != 1 and x != y:\n                msg = _strbuf(capacity=100)\n                msg.append(\"shape mismatch: indexing arrays could not be broadcast together with shapes\")\n                for sh in args:\n                    msg.append(\" \")\n                    msg.append(str(sh))\n                raise IndexError(msg.__str__())\n\n    return ans\n\ndef _getset_advanced(arr, indexes, item, dtype: type):\n    indexes = tuple(_adv_idx_convert(idx) for idx in indexes)\n    indexes = _adv_idx_replace_bools(indexes)\n    newaxis_tuple, ellipsis_tuple = _extract_special(indexes)\n\n    if static.len(ellipsis_tuple) > 1:\n        compile_error(\"an index can only have a single ellipsis ('...')\")\n\n    if static.len(indexes) - static.len(newaxis_tuple) - static.len(ellipsis_tuple) > static.len(arr.shape):\n        compile_error(\"too many indices for array\")\n\n    indexes = _expand_ellipsis(indexes,\n                               static.len(arr.shape)\n                                 - (static.len(indexes)\n                                     - static.len(newaxis_tuple)\n                                     - static.len(ellipsis_tuple)),\n                               static.len(ellipsis_tuple))\n    indexes = _expand_remainder(indexes,\n                                static.len(arr.shape)\n                                  - static.len(indexes)\n                                  + static.len(newaxis_tuple))\n\n    # eliminate newaxis and used axes (i.e. integer indices)\n    arr, indexes = _adv_idx_eliminate_new_and_used(arr, indexes)\n    shape = arr.shape\n\n    # which indices are array-like?\n    index_arrays, arrays_where = _adv_idx_gather_arrays(indexes)\n    arrays_bshape = _broadcast_shapes(*tuple(a.shape for a in index_arrays))\n\n    if static.len(index_arrays) == 0:\n        compile_error(\"[internal error] advanced indexing is not applicable to index\")\n\n    # which indices are not array-like?\n    index_non_arrays, non_arrays_where = _adv_idx_gather_non_arrays(indexes)\n\n    arrays_at_front = False\n    for i in static.range(1, static.len(arrays_where)):\n        if arrays_where[i] != arrays_where[i - 1] + 1:\n            arrays_at_front = True\n\n    shape_from_non_arrays = tuple(_adv_idx_length(index_non_arrays[i], shape[non_arrays_where[i]])\n                                  for i in static.range(static.len(index_non_arrays)))\n\n    if arrays_at_front:\n        ans_shape = arrays_bshape + shape_from_non_arrays\n    else:\n        ans_shape = _adv_idx_build_for_contig_array(indexes, shape_from_non_arrays, arrays_bshape)\n\n    if item is None:\n        ans = empty(ans_shape, dtype)\n        item_arr = None\n    else:\n        ans = None\n        item_arr = broadcast_to(asarray(item), ans_shape)\n\n    subshape = tuple(shape[i] for i in non_arrays_where)\n\n    for idx in util.multirange(arrays_bshape):\n        idx_from_arrays = tuple(a._ptr(idx, broadcast=True)[0] for a in index_arrays)\n\n        for idx_from_non_arrays in _adv_idx_iter_non_arrays(index_non_arrays, subshape):\n            dst_idx_from_non_arrays = tuple(x[0] for x in idx_from_non_arrays)\n            src_idx_from_non_arrays = tuple(x[1] for x in idx_from_non_arrays)\n\n            if arrays_at_front:\n                dst_idx = idx + dst_idx_from_non_arrays\n            else:\n                dst_idx = _adv_idx_build_for_contig_array(indexes, dst_idx_from_non_arrays, idx)\n\n            src_idx = (0,) * static.len(arr.shape)\n            psrc_idx = Ptr[int](__ptr__(src_idx).as_byte())\n\n            for i in static.range(static.len(index_arrays)):\n                psrc_idx[arrays_where[i]] = idx_from_arrays[i]\n\n            for i in static.range(static.len(index_non_arrays)):\n                psrc_idx[non_arrays_where[i]] = src_idx_from_non_arrays[i]\n\n            if item is None:\n                ans[dst_idx] = arr[src_idx]\n            else:\n                arr[src_idx] = util.cast(item_arr[dst_idx], arr.dtype)\n\n    if item is None:\n        return ans\n\ndef _getset_bool(arr, indexes, item, dtype: type = NoneType):\n    indexes = asarray(indexes)\n\n    if static.len(indexes.shape) > static.len(arr.shape):\n        compile_error(\"too many indices for array\")\n    elif static.len(arr.shape) == 0:\n        if item is None:\n            if indexes.item():\n                return atleast_1d(arr)\n            else:\n                return empty(0, arr.dtype)\n        else:\n            if indexes.item():\n                arr_item = asarray(item)\n                arr.data[0] = util.cast(arr_item.item(), arr.dtype)\n    elif static.len(indexes.shape) == static.len(arr.shape):\n        sz = 1\n        for i in range(len(indexes.shape)):\n            arr_dim = arr.shape[i]\n            idx_dim = indexes.shape[i]\n            if arr_dim != idx_dim:\n                raise IndexError(f\"boolean index did not match indexed array \"\n                                 f\"along dimension {i}; dimension is {arr_dim} but \"\n                                 f\"corresponding boolean dimension is {idx_dim}\")\n            sz *= arr_dim\n\n        num_true = 0\n\n        if item is None:\n            num_true = _bool_idx_num_true(indexes, sz)\n            arr_item = None\n            ans = empty(num_true, arr.dtype)\n        else:\n            arr_item = asarray(item)\n\n            if static.len(arr_item.shape) == 1:\n                num_true = _bool_idx_num_true(indexes, sz)\n                if arr_item.size != 1 and arr_item.size != num_true:\n                    raise ValueError(f\"NumPy boolean array indexing assignment \"\n                                     f\"cannot assign {arr_item.size} input values \"\n                                     f\"to the {num_true} output values where the \"\n                                     f\"mask is true\")\n            elif static.len(arr_item.shape) > 1:\n                compile_error(\"NumPy boolean array indexing assignment requires a 0 or 1-dimensional input\")\n\n            ans = None\n\n        cc1, _ = arr._contig\n        cc2, _ = indexes._contig\n        k = 0\n\n        if cc1 and cc2:\n            for i in range(sz):\n                if indexes.data[i]:\n                    if item is None:\n                        ans.data[k] = arr.data[i]\n                    else:\n                        if static.len(arr_item.shape) == 0:\n                            arr.data[i] = util.cast(arr_item.item(), arr.dtype)\n                        else:\n                            elem = arr_item.data[0] if arr_item.size == 1 else arr_item.data[k]\n                            arr.data[i] = util.cast(elem, arr.dtype)\n                    k += 1\n        else:\n            for idx in util.multirange(arr.shape):\n                if indexes._ptr(idx)[0]:\n                    if item is None:\n                        ans.data[k] = arr._ptr(idx)[0]\n                    else:\n                        if static.len(arr_item.shape) == 0:\n                            arr._ptr(idx)[0] = util.cast(arr_item.item(), arr.dtype)\n                        else:\n                            elem = arr_item.data[0] if arr_item.size == 1 else arr_item.data[k]\n                            arr._ptr(idx)[0] = util.cast(elem, arr.dtype)\n                    k += 1\n\n        if item is None:\n            return ans\n    else:\n        return _getset_advanced(arr, indexes.nonzero(), item, dtype)\n\ndef _assign_one(arr, elem):\n    elem = util.cast(elem, arr.dtype)\n\n    if static.len(arr.shape) == 0:\n        arr.data[0] = elem\n    else:\n        if arr._is_contig:\n            p = arr.data\n            for i in range(util.count(arr.shape)):\n                p[i] = elem\n        else:\n            for idx in util.multirange(arr.shape):\n                a = arr._ptr(idx)\n                a[0] = elem\n\ndef _getset(arr, indexes, item, dtype: type = NoneType):\n    if static.len(_bool_idx_get_bool_index(indexes)) > 0:\n        return _getset_bool(arr, indexes, item, dtype)\n\n    if not isinstance(indexes, Tuple):\n        return _getset(arr, (indexes,), item, dtype)\n\n    if static.len(_extract_sequences(indexes)) > 0:\n        return _getset_advanced(arr, indexes, item, dtype)\n\n    newaxis_tuple, ellipsis_tuple = _extract_special(indexes)\n\n    if static.len(ellipsis_tuple) > 1:\n        compile_error(\"an index can only have a single ellipsis ('...')\")\n\n    if static.len(indexes) - static.len(newaxis_tuple) - static.len(ellipsis_tuple) > static.len(arr.shape):\n        compile_error(\"too many indices for array\")\n\n    indexes = _expand_ellipsis(indexes,\n                               static.len(arr.shape)\n                                 - (static.len(indexes)\n                                     - static.len(newaxis_tuple)\n                                     - static.len(ellipsis_tuple)),\n                              static.len(ellipsis_tuple))\n    indexes = _expand_remainder(indexes,\n                                static.len(arr.shape)\n                                  - static.len(indexes)\n                                  + static.len(newaxis_tuple))\n    indexes, shape, strides = _expand_newaxis(indexes, arr.shape, arr.strides)\n    mindex = _multiindex(indexes, shape)\n    keep = _keep_axes(indexes)\n    p = Ptr[dtype](arr._data.as_byte() + _base_offset(mindex, strides))\n\n    if static.len(keep) == 0:\n        if item is None:\n            return p[0]\n        else:\n            p[0] = util.cast(item, dtype)\n\n    ans_shape = _new_shape(mindex, keep)\n    ans_strides = _new_strides(mindex, strides, keep)\n    sub = ndarray(ans_shape, ans_strides, p)\n\n    if item is None:\n        return sub\n    elif isinstance(item, ndarray) or isinstance(item, List) or isinstance(item, Tuple):\n        arr_item = asarray(item)\n\n        if static.len(arr_item.shape) == 0:\n            _assign_one(sub, arr_item.item())\n        else:\n            arr_bcast = broadcast_to(arr_item, sub.shape)\n            if sub._contig_match(arr_bcast):\n                q = arr_bcast._data\n                for i in range(arr_bcast.size):\n                    p[i] = util.cast(q[i], dtype)\n            else:\n                for idx in util.multirange(sub.shape):\n                    a = sub._ptr(idx)\n                    b = arr_bcast._ptr(idx)\n                    a[0] = util.cast(b[0], dtype)\n    else:\n        _assign_one(sub, item)\n\n@extend\nclass ndarray:\n    def __getitem__(self, indexes):\n        return _getset(self, indexes, None, dtype)\n\n    def __setitem__(self, indexes, item):\n        _getset(self, indexes, item=item, dtype=dtype)\n"
  },
  {
    "path": "stdlib/numpy/interp.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .routines import empty as empty_array, asarray, atleast_1d, empty, ascontiguousarray, _check_out, concatenate\nfrom .sorting import argsort\nimport util\n\n_LIKELY_IN_CACHE_SIZE: Literal[int] = 8\n\ndef _linear_search(key, arr, lenxp):\n    for i in range(lenxp):\n        if key < arr[i]:\n            return i - 1\n    return lenxp - 1\n\ndef _binary_search_with_guess(key, arr, lenxp, guess):\n    imin = 0\n    imax = lenxp\n    # Handle keys outside of the arr range first\n    if key > arr[lenxp - 1]:\n        return lenxp\n    elif key < arr[0]:\n        return -1\n\n    # If len <= 4 use linear search\n    if lenxp <= 4:\n        return _linear_search(key, arr, lenxp)\n\n    if guess > lenxp - 3:\n        guess = lenxp - 3\n    if guess < 1:\n        guess = 1\n\n    # Check most likely values: guess - 1, guess, guess + 1\n    if key < arr[guess]:\n        if key < arr[guess - 1]:\n            imax = guess - 1\n            # Last attempt to restrict search to items in cache\n            if guess > _LIKELY_IN_CACHE_SIZE and key >= arr[\n                    guess - _LIKELY_IN_CACHE_SIZE]:\n                imin = guess - _LIKELY_IN_CACHE_SIZE\n        else:\n            return guess - 1\n    else:\n        if key < arr[guess + 1]:\n            return guess\n        elif key < arr[guess + 2]:\n            return guess + 1\n        else:\n            imin = guess + 2\n            # Last attempt to restrict search to items in cache\n            if guess < lenxp - _LIKELY_IN_CACHE_SIZE - 1 and key < arr[\n                    guess + _LIKELY_IN_CACHE_SIZE]:\n                imax = guess + _LIKELY_IN_CACHE_SIZE\n\n    # Finally, find index by bisection\n    while imin < imax:\n        imid = imin + (imax - imin) // 2\n        if key >= arr[imid]:\n            imin = imid + 1\n        else:\n            imax = imid\n\n    return imin - 1\n\ndef _compiled_interp_complex(x: Ptr[X],\n                             lenx: int,\n                             X: type,\n                             xp: Ptr[T],\n                             lenxp: int,\n                             T: type,\n                             fp: Ptr[D],\n                             lenafp: int,\n                             D: type,\n                             left=None,\n                             right=None):\n    dx = xp\n    dz = x\n    dy = fp\n\n    af = empty_array(lenx, dtype=complex)\n\n    # Get left and right fill values\n    lval = dy[0] if left is None else complex(left)\n    rval = dy[lenafp - 1] if right is None else complex(right)\n\n    # binary_search_with_guess needs at least a 3 item long array\n    if lenxp == 1:\n        xp_val, fp_val = dx[0], dy[0]\n        for i in range(lenx):\n            x_val = dz[i]\n            af[i] = lval if x_val < xp_val else (\n                rval if x_val > xp_val else fp_val)\n    else:\n        j = 0\n\n        # only pre-calculate slopes if there are relatively few of them\n        slopes = empty_array(lenxp -\n                             1, dtype=complex) if lenxp <= lenx else None\n\n        if slopes is not None:\n            for i in range(lenxp - 1):\n                inv_dx = 1.0 / (dx[i + 1] - dx[i])\n                slopes[i] = ((dy[i + 1] - dy[i]) * inv_dx)\n\n        for i in range(lenx):\n            x_val = dz[i]\n\n            if util.isnan(x_val):\n                af[i] = x_val\n                continue\n\n            j = _binary_search_with_guess(x_val, dx, lenxp, j)\n\n            if j == -1:\n                af[i] = lval\n            elif j == lenxp:\n                af[i] = rval\n            elif j == lenxp - 1:\n                af[i] = dy[j]\n            elif dx[j] == x_val:\n                af[i] = dy[j]\n            else:\n                slope = slopes[j] if slopes is not None else (\n                    (dy[j + 1] - dy[j]) / (dx[j + 1] - dx[j]))\n\n                af_real = slope.real * (x_val - dx[j]) + dy[j].real\n                af_imag = slope.imag * (x_val - dx[j]) + dy[j].imag\n\n                if util.isnan(af_real):\n                    af_real = slope.real * (x_val - dx[j + 1]) + dy[j + 1].real\n                    if util.isnan(af_real) and dy[j].real == dy[j + 1].real:\n                        af_real = dy[j].real\n\n                if util.isnan(af_imag):\n                    af_imag = slope.imag * (x_val - dx[j]) + dy[j].imag\n                    if util.isnan(af_imag) and dy[j].imag == dy[j + 1].imag:\n                        af_imag = dy[j].imag\n\n                af[i] = complex(af_real, af_imag)\n\n    return af\n\ndef _compiled_interp(x: Ptr[X],\n                     lenx: int,\n                     X: type,\n                     xp: Ptr[T],\n                     lenxp: int,\n                     T: type,\n                     fp: Ptr[D],\n                     lenafp: int,\n                     D: type,\n                     left=None,\n                     right=None):\n    afp = fp\n    axp = xp\n    ax = x\n    if lenxp == 0:\n        raise ValueError(\"Array of sample points is empty\")\n\n    if lenafp != lenxp:\n        raise ValueError(\"fp and xp are not of the same length.\")\n\n    af = empty_array(lenx, dtype=float)\n    dy = afp\n    dx = axp\n    dz = ax\n    dres = af\n\n    # Get left and right fill values\n    lval = dy[0] if left is None else float(left)\n    rval = dy[lenafp - 1] if right is None else float(right)\n\n    # binary_search_with_guess needs at least a 3 item long array\n    if lenxp == 1:\n        xp_val = dx[0]\n        fp_val = dy[0]\n\n        for i in range(lenx):\n            x_val = dz[i]\n            if x_val < xp_val:\n                dres[i] = lval\n            elif x_val > xp_val:\n                dres[i] = rval\n            else:\n                dres[i] = fp_val\n    else:\n        slopes = None\n\n        if lenxp <= lenx:\n            slopes = [0.0] * (lenxp - 1)\n            for i in range(lenxp - 1):\n                slopes[i] = (dy[i + 1] - dy[i]) / (dx[i + 1] - dx[i])\n\n        j = 0\n\n        for i in range(lenx):\n            x_val = dz[i]\n\n            if util.isnan(x_val):\n                dres[i] = x_val\n                continue\n\n            j = _binary_search_with_guess(x_val, dx, lenxp, j)\n\n            if j == -1:\n                dres[i] = lval\n            elif j == lenxp:\n                dres[i] = rval\n            elif j == lenxp - 1 or dx[j] == x_val:\n                dres[i] = dy[j]\n            else:\n                slope = slopes[j] if slopes is not None else (\n                    dy[j + 1] - dy[j]) / (dx[j + 1] - dx[j])\n                dres[i] = slope * (x_val - dx[j]) + dy[j]\n\n                if util.isnan(dres[i]):\n                    dres[i] = slope * (x_val - dx[j + 1]) + dy[j + 1]\n\n                    if util.isnan(dres[i]) and dy[j] == dy[j + 1]:\n                        dres[i] = dy[j]\n\n    return af\n\ndef _interp_type(dtype: type):\n    if dtype is complex64:\n        return complex()\n    else:\n        return dtype()\n\ndef interp(x, xp, fp, left=None, right=None, period=None):\n    x = ascontiguousarray(atleast_1d(asarray(x)))\n    xp = ascontiguousarray(atleast_1d(asarray(xp)))\n    fp = asarray(fp)\n\n    fp_dtype = type(_interp_type(fp.dtype))\n    fp = ascontiguousarray(atleast_1d(asarray(fp, fp_dtype)))\n\n    if period is not None:\n        if period == 0:\n            raise ValueError(\"period must be a non-zero value\")\n        period = abs(period)\n        left = None\n        right = None\n\n        if xp.ndim != 1 or fp.ndim != 1:\n            compile_error(\"Data points must be 1-D sequences\")\n        if xp.shape[0] != fp.shape[0]:\n            raise ValueError(\"fp and xp are not of the same length\")\n\n        x = (x % period + period) % period\n        xp = (xp % period + period) % period\n        asort_xp = argsort(xp)\n        xp = xp[asort_xp]\n        fp = fp[asort_xp]\n        xp = concatenate((xp[-1:] - period, xp, xp[0:1] + period))\n        fp = concatenate((fp[-1:], fp, fp[0:1]))\n\n    if fp.dtype is complex:\n        af = _compiled_interp_complex(x.data,\n                                      x.size,\n                                      x.dtype,\n                                      xp.data,\n                                      xp.size,\n                                      xp.dtype,\n                                      fp.data,\n                                      fp.size,\n                                      fp.dtype,\n                                      left=left,\n                                      right=right)\n        return af\n    else:\n        af = _compiled_interp(x.data,\n                              x.size,\n                              x.dtype,\n                              xp.data,\n                              xp.size,\n                              xp.dtype,\n                              fp.data,\n                              fp.size,\n                              fp.dtype,\n                              left=left,\n                              right=right)\n        return af\n"
  },
  {
    "path": "stdlib/numpy/lib/__init__.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n"
  },
  {
    "path": "stdlib/numpy/lib/arraysetops.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom ..ndarray import ndarray\nfrom ..routines import empty, zeros, ones, asarray, broadcast_to, moveaxis, ascontiguousarray, rot90, atleast_1d\nfrom ..sorting import sort, lexsort\nfrom ..ndmath import isnan\nfrom ..util import multirange, free, count, normalize_axis_index, coerce, cast\n\ndef _unique1d(ar,\n              return_index: Literal[bool] = False,\n              return_inverse: Literal[bool] = False,\n              return_counts: Literal[bool] = False,\n              equal_nan: bool = True):\n    n = ar.size\n\n    if return_index or return_inverse:\n        sort_kind = 'mergesort' if return_index else 'quicksort'\n        ar_flat = ar.ravel()\n        perm = ar_flat.argsort(kind=sort_kind)\n    else:\n        ar_flat = ar.flatten()\n        ar_flat.sort()\n        perm = None\n\n    def perm_get(perm, i: int):\n        if perm is None:\n            return 0\n        else:\n            return perm._ptr((i, ))[0]\n\n    def ar_get(ar, i: int, perm):\n        if perm is None:\n            return ar._ptr((i, ))[0]\n        else:\n            return ar._ptr((perm_get(perm, i), ))[0]\n\n    def is_equal(a, b, equal_nan: bool):\n        if equal_nan:\n            return (a == b) or (isnan(a) and isnan(b))\n        else:\n            return a == b\n\n    num_unique = 0\n    i = 0\n    j = 0\n    while i < n:\n        curr = ar_get(ar_flat, i, perm)\n        j = i + 1\n        while j < n and is_equal(curr, ar_get(ar_flat, j, perm), equal_nan):\n            j += 1\n        i = j\n        num_unique += 1\n\n    ret_unique = empty((num_unique, ), ar.dtype)\n\n    if return_index:\n        ret_index = empty((num_unique, ), int)\n    else:\n        ret_index = None\n\n    if return_inverse:\n        ret_inverse = empty((n, ), int)\n    else:\n        ret_inverse = None\n\n    if return_counts:\n        ret_counts = empty((num_unique, ), int)\n    else:\n        ret_counts = None\n\n    i = 0\n    j = 0\n    k = 0\n    while i < n:\n        curr = ar_get(ar_flat, i, perm)\n\n        if return_inverse:\n            ret_inverse.data[perm_get(perm, i)] = k\n\n        j = i + 1\n        while j < n and is_equal(curr, ar_get(ar_flat, j, perm), equal_nan):\n            if return_inverse:\n                ret_inverse.data[perm_get(perm, j)] = k\n            j += 1\n\n        ret_unique.data[k] = curr\n\n        if return_index:\n            ret_index.data[k] = perm_get(perm, i)\n\n        if return_counts:\n            ret_counts.data[k] = j - i\n\n        i = j\n        k += 1\n\n    if not (return_index or return_inverse or return_counts):\n        return ret_unique\n\n    ans0 = (ret_unique, )\n\n    if return_index:\n        ans1 = ans0 + (ret_index, )\n    else:\n        ans1 = ans0\n\n    if return_inverse:\n        ans2 = ans1 + (ret_inverse, )\n    else:\n        ans2 = ans1\n\n    if return_counts:\n        ans3 = ans2 + (ret_counts, )\n    else:\n        ans3 = ans2\n\n    return ans3\n\ndef unique(ar,\n           return_index: Literal[bool] = False,\n           return_inverse: Literal[bool] = False,\n           return_counts: Literal[bool] = False,\n           axis=None,\n           equal_nan: bool = True):\n\n    ar = atleast_1d(asarray(ar))\n\n    if axis is None:\n        return _unique1d(ar,\n                         return_index=return_index,\n                         return_inverse=return_inverse,\n                         return_counts=return_counts,\n                         equal_nan=equal_nan)\n\n    if not isinstance(axis, int):\n        compile_error(\"'axis' must be an int or None\")\n\n    axis = normalize_axis_index(axis, ar.ndim)\n    ar = moveaxis(ar, axis, 0)\n    orig_shape = ar.shape\n    nrows = orig_shape[0]\n    ncols = count(orig_shape[1:])\n    ar = ar.reshape(nrows, ncols)\n    ar = ascontiguousarray(ar)\n    perm = lexsort(rot90(ar))\n\n    def perm_get(perm, i: int):\n        return perm._ptr((i, ))[0]\n\n    def ar_get(ar, i: int, ncols: int, perm):\n        i = perm_get(perm, i)\n        return ar.data + (i * ncols)\n\n    def is_equal(a, b, ncols: int, equal_nan: bool):\n        # NumPy ignores equal_nan in this case, so we do too\n        for i in range(ncols):\n            if not (a[i] == b[i]):\n                return False\n        return True\n\n    num_unique = 0\n    i = 0\n    j = 0\n    n = nrows\n    while i < n:\n        curr = ar_get(ar, i, ncols, perm)\n        j = i + 1\n        while j < n and is_equal(curr, ar_get(ar, j, ncols, perm), ncols,\n                                 equal_nan):\n            j += 1\n        i = j\n        num_unique += 1\n\n    ret_unique = empty((num_unique, ncols), ar.dtype)\n\n    if return_index:\n        ret_index = empty((num_unique, ), int)\n    else:\n        ret_index = None\n\n    if return_inverse:\n        ret_inverse = empty((n, ), int)\n    else:\n        ret_inverse = None\n\n    if return_counts:\n        ret_counts = empty((num_unique, ), int)\n    else:\n        ret_counts = None\n\n    i = 0\n    j = 0\n    k = 0\n    while i < n:\n        curr = ar_get(ar, i, ncols, perm)\n\n        if return_inverse:\n            ret_inverse.data[perm_get(perm, i)] = k\n\n        j = i + 1\n        while j < n and is_equal(curr, ar_get(ar, j, ncols, perm), ncols,\n                                 equal_nan):\n            if return_inverse:\n                ret_inverse.data[perm_get(perm, j)] = k\n            j += 1\n\n        p = ret_unique.data + (k * ncols)\n        for z in range(nrows):\n            p[z] = curr[z]\n\n        if return_index:\n            ret_index.data[k] = perm_get(perm, i)\n\n        if return_counts:\n            ret_counts.data[k] = j - i\n\n        i = j\n        k += 1\n\n    ret_unique = ret_unique.reshape(num_unique, *orig_shape[1:])\n    ret_unique = moveaxis(ret_unique, 0, axis)\n\n    if not (return_index or return_inverse or return_counts):\n        return ret_unique\n\n    ans0 = (ret_unique, )\n\n    if return_index:\n        ans1 = ans0 + (ret_index, )\n    else:\n        ans1 = ans0\n\n    if return_inverse:\n        ans2 = ans1 + (ret_inverse, )\n    else:\n        ans2 = ans1\n\n    if return_counts:\n        ans3 = ans2 + (ret_counts, )\n    else:\n        ans3 = ans2\n\n    return ans3\n\ndef _sorted(ar):\n    if isinstance(ar, ndarray):\n        x = ar.flatten()\n        x.sort()\n        return x\n    else:\n        x = asarray(ar).ravel()\n        x.sort()\n        return x\n\ndef _next_distinct(p, i: int, n: int, assume_unique: bool = False):\n    if assume_unique:\n        return i + 1\n\n    while True:\n        i += 1\n        if not (i < n and p[i - 1] == p[i]):\n            break\n    return i\n\ndef union1d(ar1, ar2):\n    v1 = _sorted(ar1)\n    v2 = _sorted(ar2)\n    dtype = type(coerce(v1.dtype, v2.dtype))\n    n1 = v1.size\n    n2 = v2.size\n    p1 = v1.data\n    p2 = v2.data\n    i1 = 0\n    i2 = 0\n    count = 0\n\n    while i1 < n1 and i2 < n2:\n        e1 = cast(p1[i1], dtype)\n        e2 = cast(p2[i2], dtype)\n        count += 1\n\n        if e1 < e2:\n            i1 = _next_distinct(p1, i1, n1)\n        elif e2 < e1:\n            i2 = _next_distinct(p2, i2, n2)\n        else:\n            i1 = _next_distinct(p1, i1, n1)\n            i2 = _next_distinct(p2, i2, n2)\n\n    while i1 < n1:\n        count += 1\n        i1 = _next_distinct(p1, i1, n1)\n\n    while i2 < n2:\n        count += 1\n        i2 = _next_distinct(p2, i2, n2)\n\n    ans = empty(count, dtype)\n    q = ans.data\n    j = 0\n\n    i1 = 0\n    i2 = 0\n\n    while i1 < n1 and i2 < n2:\n        e1 = cast(p1[i1], dtype)\n        e2 = cast(p2[i2], dtype)\n\n        if e1 < e2:\n            q[j] = e1\n            i1 = _next_distinct(p1, i1, n1)\n        elif e2 < e1:\n            q[j] = e2\n            i2 = _next_distinct(p2, i2, n2)\n        else:\n            q[j] = e1\n            i1 = _next_distinct(p1, i1, n1)\n            i2 = _next_distinct(p2, i2, n2)\n\n        j += 1\n\n    while i1 < n1:\n        e1 = cast(p1[i1], dtype)\n        q[j] = e1\n        j += 1\n        i1 = _next_distinct(p1, i1, n1)\n\n    while i2 < n2:\n        e2 = cast(p2[i2], dtype)\n        q[j] = e2\n        j += 1\n        i2 = _next_distinct(p2, i2, n2)\n\n    return ans\n\ndef setxor1d(ar1, ar2, assume_unique: bool = False):\n    v1 = _sorted(ar1)\n    v2 = _sorted(ar2)\n    dtype = type(coerce(v1.dtype, v2.dtype))\n    n1 = v1.size\n    n2 = v2.size\n    p1 = v1.data\n    p2 = v2.data\n    i1 = 0\n    i2 = 0\n    count = 0\n\n    while i1 < n1 and i2 < n2:\n        e1 = cast(p1[i1], dtype)\n        e2 = cast(p2[i2], dtype)\n\n        if e1 < e2:\n            count += 1\n            i1 = _next_distinct(p1, i1, n1, assume_unique)\n        elif e2 < e1:\n            count += 1\n            i2 = _next_distinct(p2, i2, n2, assume_unique)\n        else:\n            i1 = _next_distinct(p1, i1, n1, assume_unique)\n            i2 = _next_distinct(p2, i2, n2, assume_unique)\n\n    while i1 < n1:\n        count += 1\n        i1 = _next_distinct(p1, i1, n1, assume_unique)\n\n    while i2 < n2:\n        count += 1\n        i2 = _next_distinct(p2, i2, n2, assume_unique)\n\n    ans = empty(count, dtype)\n    q = ans.data\n    j = 0\n\n    i1 = 0\n    i2 = 0\n\n    while i1 < n1 and i2 < n2:\n        e1 = cast(p1[i1], dtype)\n        e2 = cast(p2[i2], dtype)\n\n        if e1 < e2:\n            q[j] = e1\n            j += 1\n            i1 = _next_distinct(p1, i1, n1, assume_unique)\n        elif e2 < e1:\n            q[j] = e2\n            j += 1\n            i2 = _next_distinct(p2, i2, n2, assume_unique)\n        else:\n            i1 = _next_distinct(p1, i1, n1, assume_unique)\n            i2 = _next_distinct(p2, i2, n2, assume_unique)\n\n    while i1 < n1:\n        e1 = cast(p1[i1], dtype)\n        q[j] = e1\n        j += 1\n        i1 = _next_distinct(p1, i1, n1, assume_unique)\n\n    while i2 < n2:\n        e2 = cast(p2[i2], dtype)\n        q[j] = e2\n        j += 1\n        i2 = _next_distinct(p2, i2, n2, assume_unique)\n\n    return ans\n\ndef setdiff1d(ar1, ar2, assume_unique: bool = False):\n    v1 = _sorted(ar1)\n    v2 = _sorted(ar2)\n    dtype = type(coerce(v1.dtype, v2.dtype))\n    n1 = v1.size\n    n2 = v2.size\n    p1 = v1.data\n    p2 = v2.data\n    i1 = 0\n    i2 = 0\n    count = 0\n\n    while i1 < n1 and i2 < n2:\n        e1 = cast(p1[i1], dtype)\n        e2 = cast(p2[i2], dtype)\n\n        if e1 < e2:\n            count += 1\n            i1 = _next_distinct(p1, i1, n1, assume_unique)\n        elif e2 < e1:\n            i2 = _next_distinct(p2, i2, n2, assume_unique)\n        else:\n            i1 = _next_distinct(p1, i1, n1, assume_unique)\n            i2 = _next_distinct(p2, i2, n2, assume_unique)\n\n    while i1 < n1:\n        count += 1\n        i1 = _next_distinct(p1, i1, n1, assume_unique)\n\n    ans = empty(count, dtype)\n    q = ans.data\n    j = 0\n\n    i1 = 0\n    i2 = 0\n\n    while i1 < n1 and i2 < n2:\n        e1 = cast(p1[i1], dtype)\n        e2 = cast(p2[i2], dtype)\n\n        if e1 < e2:\n            q[j] = e1\n            j += 1\n            i1 = _next_distinct(p1, i1, n1, assume_unique)\n        elif e2 < e1:\n            i2 = _next_distinct(p2, i2, n2, assume_unique)\n        else:\n            i1 = _next_distinct(p1, i1, n1, assume_unique)\n            i2 = _next_distinct(p2, i2, n2, assume_unique)\n\n    while i1 < n1:\n        e1 = cast(p1[i1], dtype)\n        q[j] = e1\n        j += 1\n        i1 = _next_distinct(p1, i1, n1, assume_unique)\n\n    return ans\n\ndef _intersect1d(ar1, ar2, assume_unique: bool = False):\n    v1 = _sorted(ar1)\n    v2 = _sorted(ar2)\n    dtype = type(coerce(v1.dtype, v2.dtype))\n    n1 = v1.size\n    n2 = v2.size\n    p1 = v1.data\n    p2 = v2.data\n    i1 = 0\n    i2 = 0\n    count = 0\n\n    while i1 < n1 and i2 < n2:\n        if not assume_unique and i1 > 0 and p1[i1] == p1[i1 - 1]:\n            i1 += 1\n            continue\n\n        e1 = cast(p1[i1], dtype)\n        e2 = cast(p2[i2], dtype)\n\n        if e1 < e2:\n            i1 += 1\n        elif e2 < e1:\n            i2 += 1\n        else:\n            count += 1\n            i1 += 1\n            i2 += 1\n\n    ans = empty(count, dtype)\n    q = ans.data\n    j = 0\n\n    i1 = 0\n    i2 = 0\n\n    while i1 < n1 and i2 < n2:\n        if i1 > 0 and p1[i1] == p1[i1 - 1]:\n            i1 += 1\n            continue\n\n        e1 = cast(p1[i1], dtype)\n        e2 = cast(p2[i2], dtype)\n\n        if e1 < e2:\n            i1 += 1\n        elif e2 < e1:\n            i2 += 1\n        else:\n            q[j] = e1\n            j += 1\n            i1 += 1\n            i2 += 1\n\n    return ans\n\ndef _intersect1d_indices(ar1, ar2, assume_unique: bool = False):\n    v1 = asarray(ar1).ravel()\n    v2 = asarray(ar2).ravel()\n    perm1 = v1.argsort().data  # argsort return array should always be contiguous\n    perm2 = v2.argsort().data\n    dtype = type(coerce(v1.dtype, v2.dtype))\n    n1 = v1.size\n    n2 = v2.size\n    p1 = v1.data\n    p2 = v2.data\n    i1 = 0\n    i2 = 0\n    count = 0\n\n    while i1 < n1 and i2 < n2:\n        if not assume_unique and i1 > 0 and p1[perm1[i1]] == p1[perm1[i1 - 1]]:\n            i1 += 1\n            continue\n\n        e1 = cast(p1[perm1[i1]], dtype)\n        e2 = cast(p2[perm2[i2]], dtype)\n\n        if e1 < e2:\n            i1 += 1\n        elif e2 < e1:\n            i2 += 1\n        else:\n            count += 1\n            i1 += 1\n            i2 += 1\n\n    ans = empty(count, dtype)\n    idx1 = empty(count, int)\n    idx2 = empty(count, int)\n    q = ans.data\n    j = 0\n\n    i1 = 0\n    i2 = 0\n\n    while i1 < n1 and i2 < n2:\n        if i1 > 0 and p1[perm1[i1]] == p1[perm1[i1 - 1]]:\n            i1 += 1\n            continue\n\n        e1 = cast(p1[perm1[i1]], dtype)\n        e2 = cast(p2[perm2[i2]], dtype)\n\n        if e1 < e2:\n            i1 += 1\n        elif e2 < e1:\n            i2 += 1\n        else:\n            q[j] = e1\n            idx1.data[j] = perm1[i1]\n            idx2.data[j] = perm2[i2]\n            j += 1\n            i1 += 1\n            i2 += 1\n\n    return ans, idx1, idx2\n\ndef intersect1d(ar1,\n                ar2,\n                assume_unique: bool = False,\n                return_indices: Literal[bool] = False):\n    if return_indices:\n        return _intersect1d_indices(ar1, ar2, assume_unique=assume_unique)\n    else:\n        return _intersect1d(ar1, ar2, assume_unique=assume_unique)\n\ndef isin(element,\n         test_elements,\n         assume_unique: bool = False,\n         invert: bool = False,\n         kind: Optional[str] = None):\n\n    def table_method_ok(dtype: type):\n        if dtype is int or dtype is byte or dtype is bool:\n            return True\n\n        if isinstance(dtype, Int) or isinstance(dtype, UInt):\n            return dtype.N <= 64\n\n        return False\n\n    def bitset_get(x, x_min, x_max, bitset: Ptr[u64]):\n        if x < x_min or x > x_max:\n            return False\n\n        pos = int(x) - int(x_min)\n        offset = pos >> 6\n        return bool(bitset[offset] & (u64(1) << (u64(pos) & u64(63))))\n\n    def bitset_set(x, x_min, x_max, bitset: Ptr[u64]):\n        pos = int(x) - int(x_min)\n        offset = pos >> 6\n        bitset[offset] |= (u64(1) << (u64(pos) & u64(63)))\n\n    def array_min_max(ar, dtype: type):\n        x_min = dtype()\n        x_max = dtype()\n        first = True\n\n        for idx in multirange(ar.shape):\n            x = cast(ar._ptr(idx)[0], dtype)\n            if first or x < x_min:\n                x_min = x\n            if first or x > x_max:\n                x_max = x\n            first = False\n\n        return x_min, x_max\n\n    def binsearch(arr, n: int, x: dtype, dtype: type):\n        low, high, mid = 0, n - 1, 0\n        while low <= high:\n            mid = (high + low) >> 1\n            if cast(arr[mid], dtype) < x:\n                low = mid + 1\n            elif cast(arr[mid], dtype) > x:\n                high = mid - 1\n            else:\n                return True\n        return False\n\n    ar1 = atleast_1d(asarray(element))\n    ar2 = atleast_1d(asarray(test_elements))\n    dtype = type(coerce(ar1.dtype, ar2.dtype))\n    use_table_method = False\n    kind_is_table = False\n\n    if kind is None:\n        use_table_method = table_method_ok(dtype)\n    elif kind == \"table\":\n        use_table_method = table_method_ok(dtype)\n        kind_is_table = True\n    elif kind == \"sort\":\n        use_table_method = False\n    else:\n        raise ValueError(\n            f\"Invalid kind: '{kind}'. Please use None, 'sort' or 'table'.\")\n\n    if ar1.size == 0:\n        return empty(ar1.shape, bool)\n\n    if ar2.size == 0:\n        if invert:\n            return ones(ar1.shape, bool)\n        else:\n            return zeros(ar1.shape, bool)\n\n    if use_table_method:\n        ar2_min, ar2_max = array_min_max(ar2, dtype)\n\n        # check for u64 to make sure we don't overflow\n        if dtype is u64:\n            ar2_range = u64(ar2_max) - u64(ar2_min) + u64(1)\n        else:\n            ar2_range = int(ar2_max) - int(ar2_min) + 1\n\n        R = type(ar2_range)\n        below_memory_constraint = ar2_range <= R(6 * (ar1.size + ar2.size))\n\n        if below_memory_constraint or kind_is_table:\n            bitset_size = int(ar2_range >> R(6))\n            if ar2_range & R(63):\n                bitset_size += 1\n\n            bitset = Ptr[u64](bitset_size)\n            for i in range(bitset_size):\n                bitset[i] = u64(0)\n\n            for idx in multirange(ar2.shape):\n                x2 = cast(ar2._ptr(idx)[0], dtype)\n                bitset_set(x2, ar2_min, ar2_max, bitset)\n\n            ans = empty(ar1.shape, bool)\n\n            for idx in multirange(ar1.shape):\n                x1 = cast(ar1._ptr(idx)[0], dtype)\n                p = ans._ptr(idx)\n                found = bitset_get(x1, ar2_min, ar2_max, bitset)\n                p[0] = (not found) if invert else found\n\n            free(bitset)\n            return ans\n        elif kind_is_table:\n            raise RuntimeError(\n                \"You have specified kind='table', \"\n                \"but the range of values in `ar2` or `ar1` exceed the \"\n                \"maximum integer of the datatype. \"\n                \"Please set `kind` to None or 'sort'.\")\n    elif kind_is_table:\n        raise ValueError(\"The 'table' method is only \"\n                         \"supported for boolean or integer arrays. \"\n                         \"Please select 'sort' or None for kind.\")\n\n    if ar2.size < 10 * ar1.size**0.145:\n        mask = empty(ar1.shape, bool)\n\n        for idx1 in multirange(ar1.shape):\n            x1 = cast(ar1._ptr(idx1)[0], dtype)\n            found = False\n\n            for idx2 in multirange(ar2.shape):\n                x2 = cast(ar2._ptr(idx2)[0], dtype)\n                if x1 == x2:\n                    found = True\n                    break\n\n            mask._ptr(idx1)[0] = (not found) if invert else found\n\n        return mask\n\n    s = sort(ar2)\n    ans = empty(ar1.shape, bool)\n\n    for idx1 in multirange(ar1.shape):\n        x1 = cast(ar1._ptr(idx1)[0], dtype)\n        found = binsearch(s.data, s.size, x1)\n        ans._ptr(idx1)[0] = (not found) if invert else found\n\n    return ans\n\ndef in1d(ar1,\n         ar2,\n         assume_unique: bool = False,\n         invert: bool = False,\n         kind: Optional[str] = None):\n    return isin(ar1,\n                ar2,\n                assume_unique=assume_unique,\n                invert=invert,\n                kind=kind).ravel()\n"
  },
  {
    "path": "stdlib/numpy/lib/stride_tricks.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\nfrom ..ndarray import ndarray\nfrom ..routines import asarray\nfrom ..util import strides as make_strides, tuple_range, normalize_axis_tuple\n\ndef as_strided(x, shape = None, strides = None, writeable: bool = True):\n    x = asarray(x)\n\n    if shape is None:\n        return as_strided(x, shape=x.shape, strides=strides, writeable=writeable)\n\n    if strides is None:\n        st = make_strides(shape, False, x.dtype)\n        return as_strided(x, shape=shape, strides=st, writeable=writeable)\n\n    if not isinstance(shape, Tuple):\n        compile_error(\"shape must be a tuple of integers\")\n\n    if not isinstance(strides, Tuple):\n        compile_error(\"strides must be a tuple of integers\")\n\n    if static.len(shape) != static.len(strides):\n        compile_error(\"shape and strides have different lengths\")\n\n    return ndarray(shape, strides, x.data)\n\ndef sliding_window_view(x, window_shape, axis = None, writeable: bool = False):\n    if isinstance(window_shape, int):\n        return sliding_window_view(x, window_shape=(window_shape,), axis=axis, writeable=writeable)\n\n    x = asarray(x)\n    ndim: Literal[int] = static.len(x.shape)\n\n    for w in window_shape:\n        if w < 0:\n            raise ValueError(\"`window_shape` cannot contain negative values\")\n\n    if axis is None:\n        ax = tuple_range(ndim)\n    else:\n        ax = normalize_axis_tuple(axis, ndim, allow_duplicates=True)\n\n    if static.len(window_shape) != static.len(ax):\n        compile_error(\"window_shape length does not match dimension of x\")\n\n    out_strides = x.strides + tuple(x.strides[a] for a in ax)\n    x_shape_trimmed = x.shape\n    px_shape_trimmed = Ptr[int](__ptr__(x_shape_trimmed).as_byte())\n\n    for i in range(len(ax)):\n        a = ax[i]\n        dim = window_shape[i]\n        if px_shape_trimmed[a] < dim:\n            raise ValueError(\n                \"window shape cannot be larger than input array shape\")\n        px_shape_trimmed[a] -= dim - 1\n    out_shape = tuple(x_shape_trimmed) + window_shape\n    return as_strided(x, strides=out_strides, shape=out_shape, writeable=writeable)\n"
  },
  {
    "path": "stdlib/numpy/linalg/__init__.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .linalg import cholesky, eig, eigh, eigvals, \\\n                    eigvalsh, inv, lstsq, qr, solve, \\\n                    svd, svdvals, inner, outer, \\\n                    tensordot, matmul, multi_dot, \\\n                    trace, tensorsolve, tensorinv, \\\n                    norm, pinv, matrix_rank, cond, \\\n                    matrix_power, det, slogdet, \\\n                    _matmul_ufunc, _trace_ufunc, \\\n                    matrix_transpose, LinAlgError, \\\n                    dot as _linalg_dot, \\\n                    vdot as _linalg_vdot, \\\n                    tensordot as _linalg_tensordot, \\\n                    inner as _linalg_inner, \\\n                    outer as _linalg_outer, \\\n                    kron as _linalg_kron\n"
  },
  {
    "path": "stdlib/numpy/linalg/blas.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfint = i32\nfintp = Ptr[fint]\n\n# Constants\nCBLAS_ROW_MAJOR: Literal[int] = 101\nCBLAS_COL_MAJOR: Literal[int] = 102\n\nCBLAS_NO_TRANS: Literal[int]   = 111\nCBLAS_TRANS: Literal[int]      = 112\nCBLAS_CONJ_TRANS: Literal[int] = 113\n\nCBLAS_UPPER: Literal[int] = 121\nCBLAS_LOWER: Literal[int] = 122\n\nCBLAS_NON_UNIT: Literal[int] = 131\nCBLAS_UNIT: Literal[int]     = 132\n\nCBLAS_LEFT: Literal[int]  = 141\nCBLAS_RIGHT: Literal[int] = 142\n\nLAPACK_ROW_MAJOR: Literal[int] = 101\nLAPACK_COL_MAJOR : Literal[int]= 102\n\n# Stubs\n# Note: D=float64, S=float32, Z=complex128, C=complex64\nfrom C import cblas_dcopy(fint, Ptr[float], fint, Ptr[float], fint)\nfrom C import cblas_scopy(fint, Ptr[float32], fint, Ptr[float32], fint)\nfrom C import cblas_zcopy(fint, Ptr[complex], fint, Ptr[complex], fint)\nfrom C import cblas_ccopy(fint, Ptr[complex64], fint, Ptr[complex64], fint)\n\nfrom C import cblas_dgemm(fint, fint, fint, fint, fint, fint, float,\n                          Ptr[float], fint, Ptr[float], fint,\n                          float, Ptr[float], fint)\nfrom C import cblas_sgemm(fint, fint, fint, fint, fint, fint, float32,\n                          Ptr[float32], fint, Ptr[float32], fint,\n                          float32, Ptr[float32], fint)\nfrom C import cblas_zgemm(fint, fint, fint, fint, fint, fint, cobj,\n                          Ptr[complex], fint, Ptr[complex], fint,\n                          cobj, Ptr[complex], fint)\nfrom C import cblas_cgemm(fint, fint, fint, fint, fint, fint, cobj,\n                          Ptr[complex64], fint, Ptr[complex64], fint,\n                          cobj, Ptr[complex64], fint)\n\nfrom C import cblas_dsyrk(fint, fint, fint, fint, fint, float, Ptr[float],\n                          fint, float, Ptr[float], fint)\nfrom C import cblas_ssyrk(fint, fint, fint, fint, fint, float32, Ptr[float32],\n                          fint, float32, Ptr[float32], fint)\nfrom C import cblas_zsyrk(fint, fint, fint, fint, fint, cobj, Ptr[complex],\n                          fint, cobj, Ptr[complex], fint)\nfrom C import cblas_csyrk(fint, fint, fint, fint, fint, cobj, Ptr[complex64],\n                          fint, cobj, Ptr[complex64], fint)\n\nfrom C import cblas_ddot(fint, cobj, fint, cobj, fint) -> float\nfrom C import cblas_sdot(fint, cobj, fint, cobj, fint) -> float32\nfrom C import cblas_zdotu_sub(fint, cobj, fint, cobj, fint, Ptr[complex])\nfrom C import cblas_cdotu_sub(fint, cobj, fint, cobj, fint, Ptr[complex64])\n\nfrom C import cblas_zdotc_sub(fint, cobj, fint, cobj, fint, cobj)\nfrom C import cblas_cdotc_sub(fint, cobj, fint, cobj, fint, cobj)\n\nfrom C import cblas_dgemv(fint, fint, fint, fint, float, cobj, fint, cobj, fint, float, cobj, fint)\nfrom C import cblas_sgemv(fint, fint, fint, fint, float32, cobj, fint, cobj, fint, float32, cobj, fint)\nfrom C import cblas_zgemv(fint, fint, fint, fint, cobj, cobj, fint, cobj, fint, cobj, cobj, fint)\nfrom C import cblas_cgemv(fint, fint, fint, fint, cobj, cobj, fint, cobj, fint, cobj, cobj, fint)\n\nfrom C import dpotrf_(cobj, fintp, cobj, fintp, fintp)\nfrom C import spotrf_(cobj, fintp, cobj, fintp, fintp)\nfrom C import zpotrf_(cobj, fintp, cobj, fintp, fintp)\nfrom C import cpotrf_(cobj, fintp, cobj, fintp, fintp)\n\nfrom C import dgetrf_(fintp, fintp, cobj, fintp, fintp, fintp)\nfrom C import sgetrf_(fintp, fintp, cobj, fintp, fintp, fintp)\nfrom C import zgetrf_(fintp, fintp, cobj, fintp, fintp, fintp)\nfrom C import cgetrf_(fintp, fintp, cobj, fintp, fintp, fintp)\n\nfrom C import dgeqrf_(fintp, fintp, cobj, fintp, cobj, cobj, fintp, fintp)\nfrom C import sgeqrf_(fintp, fintp, cobj, fintp, cobj, cobj, fintp, fintp)\nfrom C import zgeqrf_(fintp, fintp, cobj, fintp, cobj, cobj, fintp, fintp)\nfrom C import cgeqrf_(fintp, fintp, cobj, fintp, cobj, cobj, fintp, fintp)\n\nfrom C import dorgqr_(fintp, fintp, fintp, cobj, fintp, cobj, cobj, fintp, fintp)\nfrom C import sorgqr_(fintp, fintp, fintp, cobj, fintp, cobj, cobj, fintp, fintp)\nfrom C import zungqr_(fintp, fintp, fintp, cobj, fintp, cobj, cobj, fintp, fintp)\nfrom C import cungqr_(fintp, fintp, fintp, cobj, fintp, cobj, cobj, fintp, fintp)\n\nfrom C import dgeev_(cobj, cobj, fintp, cobj, fintp, cobj, cobj, cobj, fintp, cobj, fintp, cobj, fintp, fintp)\nfrom C import sgeev_(cobj, cobj, fintp, cobj, fintp, cobj, cobj, cobj, fintp, cobj, fintp, cobj, fintp, fintp)\nfrom C import zgeev_(cobj, cobj, fintp, cobj, fintp, cobj, cobj, fintp, cobj, fintp, cobj, fintp, cobj, fintp)\nfrom C import cgeev_(cobj, cobj, fintp, cobj, fintp, cobj, cobj, fintp, cobj, fintp, cobj, fintp, cobj, fintp)\n\nfrom C import dsyevd_(cobj, cobj, fintp, cobj, fintp, cobj, cobj, fintp, fintp, fintp, fintp)\nfrom C import ssyevd_(cobj, cobj, fintp, cobj, fintp, cobj, cobj, fintp, fintp, fintp, fintp)\nfrom C import zheevd_(cobj, cobj, fintp, cobj, fintp, cobj, cobj, fintp, cobj, fintp, fintp, fintp, fintp)\nfrom C import cheevd_(cobj, cobj, fintp, cobj, fintp, cobj, cobj, fintp, cobj, fintp, fintp, fintp, fintp)\n\nfrom C import dgesv_(fintp, fintp, cobj, fintp, fintp, cobj, fintp, fintp)\nfrom C import sgesv_(fintp, fintp, cobj, fintp, fintp, cobj, fintp, fintp)\nfrom C import zgesv_(fintp, fintp, cobj, fintp, fintp, cobj, fintp, fintp)\nfrom C import cgesv_(fintp, fintp, cobj, fintp, fintp, cobj, fintp, fintp)\n\nfrom C import dgelsd_(fintp, fintp, fintp, cobj, fintp, cobj, fintp, cobj, cobj, fintp, cobj, fintp, fintp, fintp)\nfrom C import sgelsd_(fintp, fintp, fintp, cobj, fintp, cobj, fintp, cobj, cobj, fintp, cobj, fintp, fintp, fintp)\nfrom C import zgelsd_(fintp, fintp, fintp, cobj, fintp, cobj, fintp, cobj, cobj, fintp, cobj, fintp, cobj, fintp, fintp)\nfrom C import cgelsd_(fintp, fintp, fintp, cobj, fintp, cobj, fintp, cobj, cobj, fintp, cobj, fintp, cobj, fintp, fintp)\n\nfrom C import dgesdd_(cobj, fintp, fintp, cobj, fintp, cobj, cobj, fintp, cobj, fintp, cobj, fintp, fintp, fintp)\nfrom C import sgesdd_(cobj, fintp, fintp, cobj, fintp, cobj, cobj, fintp, cobj, fintp, cobj, fintp, fintp, fintp)\nfrom C import zgesdd_(cobj, fintp, fintp, cobj, fintp, cobj, cobj, fintp, cobj, fintp, cobj, fintp, cobj, fintp, fintp)\nfrom C import cgesdd_(cobj, fintp, fintp, cobj, fintp, cobj, cobj, fintp, cobj, fintp, cobj, fintp, cobj, fintp, fintp)\n"
  },
  {
    "path": "stdlib/numpy/linalg/linalg.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\nfrom .blas import *\nfrom ..ndarray import ndarray\nfrom ..ndmath import divide, greater, isnan, multiply, sqrt\nfrom ..routines import atleast_2d, asarray, broadcast_shapes, broadcast_to, empty, \\\n                       empty_like, eye, expand_dims, moveaxis, reshape, swapaxes, \\\n                       zeros, array\nfrom ..util import cast, cdiv_int, coerce, eps, exp, free, inf, log, multirange, \\\n                   nan, normalize_axis_index, sizeof, sort, sqrt as util_sqrt, \\\n                   tuple_delete, tuple_insert, tuple_range, tuple_set, zero\n\n\n#############\n# Utilities #\n#############\n\nclass LinAlgError(Exception):\n    def __init__(self, message: str = ''):\n        super().__init__(message)\n\ndef _square_rows(a):\n    s = a.shape\n\n    if static.len(s) < 2:\n        compile_error(\"Array must be at least 2-dimensional\")\n\n    n = s[-1]\n    if n != s[-2]:\n        raise LinAlgError(\"Last 2 dimensions of the array must be square\")\n    return n\n\ndef _rows_cols(a):\n    s = a.shape\n\n    if static.len(s) < 2:\n        compile_error(\"Array must be at least 2-dimensional\")\n\n    m, n = s[-2:]\n    return m, n\n\ndef _asarray(a, dtype: type = NoneType):\n    if dtype is NoneType:\n        if isinstance(a, ndarray):\n            if (a.dtype is float or a.dtype is float32 or\n                a.dtype is complex or a.dtype is complex64):\n                return a\n            else:\n                return a.astype(float)\n        else:\n            dtype1 = type(asarray(a).data[0])\n            if (dtype1 is float or dtype1 is float32 or\n                dtype1 is complex or dtype1 is complex64):\n                return asarray(a, dtype=dtype1)\n            else:\n                return asarray(a, dtype=float)\n    elif (dtype is float or dtype is float32 or\n          dtype is complex or dtype is complex64):\n        return asarray(a, dtype=dtype)\n    else:\n        return asarray(a, dtype=float)\n\ndef _basetype(dtype: type):\n    if dtype is float or dtype is float32:\n        return dtype()\n    elif dtype is complex:\n        return float()\n    elif dtype is complex64:\n        return float32()\n    else:\n        compile_error(\"[internal error] bad dtype\")\n\ndef _complextype(dtype: type):\n    if dtype is float:\n        return complex()\n    elif dtype is float32:\n        return complex64()\n    elif dtype is complex or dtype is complex64:\n        return dtype()\n    else:\n        compile_error(\"[internal error] bad dtype\")\n\ndef _copy(n: int, sx: Ptr[T], incx: int, sy: Ptr[T], incy: int, T: type):\n    args = (fint(n), sx, fint(incx), sy, fint(incy))\n    if T is float:\n        cblas_dcopy(*args)\n    elif T is float32:\n        cblas_scopy(*args)\n    elif T is complex:\n        cblas_zcopy(*args)\n    elif T is complex64:\n        cblas_ccopy(*args)\n    else:\n        compile_error(\"[internal error] bad type for BLAS copy: \" + T.__name__)\n\ndef _nan(T: type):\n    if T is float or T is float32:\n        return nan(T)\n    elif T is complex:\n        return complex(nan(float), nan(float))\n    elif T is complex64:\n        return complex64(nan(float32), nan(float32))\n    else:\n        compile_error(\"[internal error] bad dtype\")\n\n@tuple\nclass LinearizeData:\n    rows: int\n    cols: int\n    row_strides: int\n    col_strides: int\n    out_lead_dim: int\n\n    def __new__(rows: int, cols: int, row_strides: int, col_strides: int):\n        return LinearizeData(rows, cols, row_strides, col_strides, cols)\n\n    def linearize(self, dst: Ptr[T], src: Ptr[T], T: type):\n        if dst:\n            rv = dst\n            cols = self.cols\n            col_strides = cdiv_int(self.col_strides, sizeof(T))\n            for i in range(self.rows):\n                if col_strides > 0:\n                    _copy(cols, src, col_strides, dst, 1)\n                elif col_strides < 0:\n                    _copy(cols, src + (cols - 1)*col_strides, col_strides, dst, 1);\n                else:\n                    for j in range(cols):\n                        dst[j] = src[0]\n                src += cdiv_int(self.row_strides, sizeof(T))\n                dst += self.out_lead_dim\n            return rv\n        else:\n            return src\n\n    def delinearize(self, dst: Ptr[T], src: Ptr[T], T: type):\n        if src:\n            rv = src\n            cols = self.cols\n            col_strides = cdiv_int(self.col_strides, sizeof(T))\n            for i in range(self.rows):\n                if col_strides > 0:\n                    _copy(cols, src, 1, dst, col_strides);\n                elif col_strides < 0:\n                    _copy(cols, src, 1, dst + (cols - 1)*col_strides, col_strides)\n                else:\n                    if cols > 0:\n                        dst[0] = src[cols - 1]\n                src += self.out_lead_dim\n                dst += cdiv_int(self.row_strides, sizeof(T))\n            return rv\n        else:\n            return src\n\n    def nan_matrix(self, dst: Ptr[T], T: type):\n        for i in range(self.rows):\n            cp = dst\n            cs = cdiv_int(self.col_strides, sizeof(T))\n            for j in range(self.cols):\n                cp[0] = _nan(T)\n                cp += cs\n            dst += cdiv_int(self.row_strides, sizeof(T))\n\n    def zero_matrix(self, dst: Ptr[T], T: type):\n        for i in range(self.rows):\n            cp = dst\n            cs = cdiv_int(self.col_strides, sizeof(T))\n            for j in range(self.cols):\n                cp[0] = T()\n                cp += cs\n            dst += cdiv_int(self.row_strides, sizeof(T))\n\n    def identity_matrix(dst: Ptr[T], n: int, T: type):\n        str.memset(dst.as_byte(), byte(0), n * n * sizeof(T))\n        for i in range(n):\n            dst[0] = T(1)\n            dst += n + 1\n\n\n###############\n# Determinant #\n###############\n\ndef _slogdet_from_factored_diagonal(src: Ptr[T],\n                                    m: int,\n                                    sign: T,\n                                    T: type):\n    if T is float or T is float32:\n        acc_sign = sign\n        acc_logdet = T(0.0)\n        for i in range(m):\n            abs_element = src[0]\n            if abs_element < T(0.0):\n                acc_sign = -acc_sign\n                abs_element = -abs_element\n            acc_logdet += log(abs_element)\n            src += m + 1\n        return acc_sign, acc_logdet\n    elif T is complex or T is complex64:\n        B = type(_basetype(T))\n        acc_sign = sign\n        acc_logdet = B()\n        for i in range(m):\n            abs_element = abs(src[0])\n            sign_element = src[0] / abs_element\n            acc_sign *= sign_element\n            acc_logdet += log(abs_element)\n            src += m + 1\n        return acc_sign, acc_logdet\n    else:\n        compile_error(\"[internal error] invalid type for _slogdet_from_factored_diagonal: \" + T.__name__)\n\ndef _slogdet_single_element(m: int,\n                            src: Ptr[T],\n                            pivots: Ptr[fint],\n                            T: type):\n    m0 = fint(m)\n    lda = fint(max(m, 1))\n    info = fint(0)\n    args = (__ptr__(m0), __ptr__(m0), src.as_byte(), __ptr__(lda), pivots, __ptr__(info))\n    if T is float:\n        dgetrf_(*args)\n    elif T is float32:\n        sgetrf_(*args)\n    elif T is complex:\n        zgetrf_(*args)\n    elif T is complex64:\n        cgetrf_(*args)\n    else:\n        compile_error(\"[internal error] bad input type\")\n\n    sign = T()\n    if info == fint(0):\n        change_sign = False\n        for i in range(m):\n            if pivots[i] != fint(i + 1):\n                change_sign = not change_sign\n        sign = T(-1.0 if change_sign else 1.0)\n        logdet = zero(_basetype(T))\n        sign, logdet = _slogdet_from_factored_diagonal(src, m, sign)\n        return sign, logdet\n    else:\n        return T(0.0), -inf(type(_basetype(T)))\n\ndef _det_from_slogdet(sign: T, logdet, T: type):\n    if T is float or T is float32:\n        return sign * exp(logdet)\n    elif T is complex or T is complex64:\n        return sign * T(exp(logdet))\n    else:\n        compile_error(\"[internal error] invalid type for _det_from_slogdet: \" + T.__name__)\n\n@tuple\nclass SlogdetResult[A, B]:\n    sign: A\n    logabsdet: B\n\n    def __getitem__(self, idx: Literal[int]):\n        if idx == 0 or idx == -2:\n            return self.sign\n        elif idx == 1 or idx == -1:\n            return self.logabsdet\n        else:\n            compile_error(\"tuple ('SlogdetResult') index out of range\")\n\ndef slogdet(a):\n    a = _asarray(a)\n    T = a.dtype\n    s = a.shape\n    m = _square_rows(a)\n    steps = a.strides\n\n    tmp = Ptr[T](m * m)\n    piv = Ptr[fint](m)\n    lin_data = LinearizeData(m, m, steps[-1], steps[-2])\n\n    if a.ndim == 2:\n        lin_data.linearize(tmp, a.data)\n        sign, logabsdet = _slogdet_single_element(m, tmp, piv)\n    else:\n        ans_shape = s[:-2]\n        sign = empty(ans_shape, dtype=a.dtype)\n        logabsdet = empty(ans_shape, dtype=type(_basetype(a.dtype)))\n\n        for idx in multirange(ans_shape):\n            lin_data.linearize(tmp, a._ptr(idx + (0, 0)))\n            sign0, logabsdet0 = _slogdet_single_element(m, tmp, piv)\n            sign._ptr(idx)[0] = sign0\n            logabsdet._ptr(idx)[0] = logabsdet0\n\n    free(tmp)\n    free(piv)\n    return SlogdetResult(sign, logabsdet)\n\ndef det(a):\n    a = _asarray(a)\n    T = a.dtype\n    s = a.shape\n    m = _square_rows(a)\n    steps = a.strides\n\n    tmp = Ptr[T](m * m)\n    piv = Ptr[fint](m)\n    lin_data = LinearizeData(m, m, steps[-1], steps[-2])\n\n    if a.ndim == 2:\n        lin_data.linearize(tmp, a.data)\n        sign, logabsdet = _slogdet_single_element(m, tmp, piv)\n        ans = _det_from_slogdet(sign, logabsdet)\n    else:\n        ans_shape = s[:-2]\n        ans = empty(ans_shape, dtype=a.dtype)\n\n        for idx in multirange(ans_shape):\n            lin_data.linearize(tmp, a._ptr(idx + (0, 0)))\n            sign0, logabsdet0 = _slogdet_single_element(m, tmp, piv)\n            ans._ptr(idx)[0] = _det_from_slogdet(sign0, logabsdet0)\n\n    free(tmp)\n    free(piv)\n    return ans\n\n\n########\n# Eigh #\n########\n\nclass EighParams:\n    A: Ptr[T]\n    W: Ptr[B]\n    WORK: Ptr[T]\n    RWORK: Ptr[B]\n    IWORK: Ptr[fint]\n    N: fint\n    LWORK: fint\n    LRWORK: fint\n    LIWORK: fint\n    JOBZ: byte\n    UPLO: byte\n    LDA: fint\n    T: type\n    B: type\n\n    def _init_real(self, JOBZ: byte, UPLO: byte, N: fint):\n        safe_N = int(N)\n        alloc_size = safe_N * (safe_N + 1) * sizeof(T)\n        lda = N if N else fint(1)\n        mem_buff = cobj(alloc_size)\n\n        a = mem_buff\n        w = mem_buff + safe_N * safe_N * sizeof(T)\n\n        self.A = Ptr[T](a)\n        self.W = Ptr[B](w)\n        self.RWORK = Ptr[B]()\n        self.N = N\n        self.LRWORK = fint(0)\n        self.JOBZ = JOBZ\n        self.UPLO = UPLO\n        self.LDA = lda\n\n        # work size query\n        query_work_size = T()\n        query_iwork_size = fint()\n\n        self.LWORK = fint(-1)\n        self.LIWORK = fint(-1)\n        self.WORK = __ptr__(query_work_size)\n        self.IWORK = __ptr__(query_iwork_size)\n\n        if self.call():\n            free(mem_buff)\n            raise LinAlgError(\"error in evd work size query\")\n\n        lwork = cast(query_work_size, int)\n        liwork = int(query_iwork_size)\n\n        mem_buff2 = cobj(lwork * sizeof(T) + liwork * sizeof(fint))\n        work = mem_buff2\n        iwork = mem_buff2 + lwork * sizeof(T)\n\n        self.LWORK = fint(lwork)\n        self.WORK = Ptr[T](work)\n        self.LIWORK = fint(liwork)\n        self.IWORK = Ptr[fint](iwork)\n\n    def _init_complex(self, JOBZ: byte, UPLO: byte, N: fint):\n        safe_N = int(N)\n        lda = N if N else fint(1)\n        mem_buff = cobj(safe_N * safe_N * sizeof(T) + safe_N * sizeof(B))\n\n        a = mem_buff\n        w = mem_buff + safe_N * safe_N * sizeof(T)\n\n        self.A = Ptr[T](a)\n        self.W = Ptr[B](w)\n        self.N = N\n        self.JOBZ = JOBZ\n        self.UPLO = UPLO\n        self.LDA = lda\n\n        # work size query\n        query_work_size = T()\n        query_rwork_size = B()\n        query_iwork_size = fint()\n\n        self.LWORK = fint(-1)\n        self.LRWORK = fint(-1)\n        self.LIWORK = fint(-1)\n        self.WORK = __ptr__(query_work_size)\n        self.RWORK = __ptr__(query_rwork_size)\n        self.IWORK = __ptr__(query_iwork_size)\n\n        if self.call():\n            free(mem_buff)\n            raise LinAlgError(\"error in evd work size query\")\n\n        lwork = cast(Ptr[B](__ptr__(query_work_size).as_byte())[0], int)\n        lrwork = cast(query_rwork_size, int)\n        liwork = int(query_iwork_size)\n\n        mem_buff2 = cobj(lwork * sizeof(T) + lrwork * sizeof(B) + liwork * sizeof(fint))\n        work = mem_buff2\n        rwork = work + lwork * sizeof(T)\n        iwork = rwork + lrwork * sizeof(B)\n\n        self.WORK = Ptr[T](work)\n        self.RWORK = Ptr[B](rwork)\n        self.IWORK = Ptr[fint](iwork)\n        self.LWORK = fint(lwork)\n        self.LRWORK = fint(lrwork)\n        self.LIWORK = fint(liwork)\n\n    def __init__(self, JOBZ: byte, UPLO: byte, N: fint):\n        if T is complex or T is complex64:\n            self._init_complex(JOBZ, UPLO, N)\n        else:\n            self._init_real(JOBZ, UPLO, N)\n\n    def release(self):\n        free(self.A)\n        free(self.WORK)\n\n    def call(self):\n        JOBZ = self.JOBZ\n        UPLO = self.UPLO\n        N = self.N\n        A = self.A\n        LDA = self.LDA\n        W = self.W\n        WORK = self.WORK\n        LWORK = self.LWORK\n        RWORK = self.RWORK\n        LRWORK = self.LRWORK\n        IWORK = self.IWORK\n        LIWORK = self.LIWORK\n        rv = fint()\n\n        args_real = (__ptr__(JOBZ),\n                     __ptr__(UPLO),\n                     __ptr__(N),\n                     A.as_byte(),\n                     __ptr__(LDA),\n                     W.as_byte(),\n                     WORK.as_byte(),\n                     __ptr__(LWORK),\n                     IWORK,\n                     __ptr__(LIWORK),\n                     __ptr__(rv))\n\n        args_cplx = (__ptr__(JOBZ),\n                     __ptr__(UPLO),\n                     __ptr__(N),\n                     A.as_byte(),\n                     __ptr__(LDA),\n                     W.as_byte(),\n                     WORK.as_byte(),\n                     __ptr__(LWORK),\n                     RWORK.as_byte(),\n                     __ptr__(LRWORK),\n                     IWORK,\n                     __ptr__(LIWORK),\n                     __ptr__(rv))\n\n        if T is float:\n            dsyevd_(*args_real)\n        elif T is float32:\n            ssyevd_(*args_real)\n        elif T is complex:\n            zheevd_(*args_cplx)\n        elif T is complex64:\n            cheevd_(*args_cplx)\n        else:\n            compile_error(\"[internal error] bad dtype for eigh\")\n\n        return rv\n\n@tuple\nclass EighResult[A, B]:\n    eigenvalues: A\n    eigenvectors: B\n\n    def __getitem__(self, idx: Literal[int]):\n        if idx == 0 or idx == -2:\n            return self.eigenvalues\n        elif idx == 1 or idx == -1:\n            return self.eigenvectors\n        else:\n            compile_error(\"tuple ('EighResult') index out of range\")\n\ndef _eigh(a, JOBZ: byte, UPLO: byte, compute_eigenvectors: Literal[bool]):\n    a = _asarray(a)\n    B = type(_basetype(a.dtype))\n\n    n = _square_rows(a)\n    params = EighParams[a.dtype, B](JOBZ, UPLO, fint(n))\n\n    eigenvalues = empty(a.shape[:-2] + (n,), dtype=B)\n    if compute_eigenvectors:\n        eigenvectors = empty(a.shape, dtype=a.dtype)\n    else:\n        eigenvectors = None\n\n    matrix_in_ld = LinearizeData(n, n, a.strides[-1], a.strides[-2])\n    eigenvalues_out_ld = LinearizeData(1, n, 0, eigenvalues.strides[-1])\n    if compute_eigenvectors:\n        eigenvectors_out_ld = LinearizeData(n, n, eigenvectors.strides[-1], eigenvectors.strides[-2])\n    else:\n        eigenvectors_out_ld = None\n    error_occured = False\n\n    for idx in multirange(a.shape[:-2]):\n        matrix_ptr = a._ptr(idx + (0, 0))\n        eigval_ptr = eigenvalues._ptr(idx + (0,))\n        if compute_eigenvectors:\n            eigvec_ptr = eigenvectors._ptr(idx + (0, 0))\n        else:\n            eigvec_ptr = None\n\n        matrix_in_ld.linearize(params.A, matrix_ptr)\n        not_ok = params.call()\n\n        if not not_ok:\n            eigenvalues_out_ld.delinearize(eigval_ptr, params.W)\n            if compute_eigenvectors:\n                eigenvectors_out_ld.delinearize(eigvec_ptr, params.A)\n        else:\n            eigenvalues_out_ld.nan_matrix(eigval_ptr)\n            if compute_eigenvectors:\n                eigenvectors_out_ld.nan_matrix(eigvec_ptr)\n            error_occured = True\n\n    params.release()\n\n    if error_occured:\n        raise LinAlgError(\"Eigenvalues did not converge\")\n\n    if compute_eigenvectors:\n        return EighResult(eigenvalues, eigenvectors)\n    else:\n        return eigenvalues\n\ndef eigh(a, UPLO: str = 'L'):\n    uplo_code = byte()\n    if UPLO == 'L' or UPLO == 'l':\n        uplo_code = byte(76)\n    elif UPLO == 'U' or UPLO == 'u':\n        uplo_code = byte(85)\n    else:\n        raise ValueError(\"UPLO argument must be 'L' or 'U'\")\n\n    jobz_code = byte(86)\n    return _eigh(a, JOBZ=jobz_code, UPLO=uplo_code, compute_eigenvectors=True)\n\ndef eigvalsh(a, UPLO: str = 'L'):\n    uplo_code = byte()\n    if UPLO == 'L' or UPLO == 'l':\n        uplo_code = byte(76)\n    elif UPLO == 'U' or UPLO == 'u':\n        uplo_code = byte(85)\n    else:\n        raise ValueError(\"UPLO argument must be 'L' or 'U'\")\n\n    jobz_code = byte(78)\n    return _eigh(a, JOBZ=jobz_code, UPLO=uplo_code, compute_eigenvectors=False)\n\n\n#########\n# Solve #\n#########\n\nclass GesvParams:\n    A: Ptr[T]\n    B: Ptr[T]\n    IPIV: Ptr[fint]\n    N: fint\n    NRHS: fint\n    LDA: fint\n    LDB: fint\n    T: type\n\n    def __init__(self, N: fint, NRHS: fint):\n        safe_N = int(N)\n        safe_NRHS = int(NRHS)\n        ld = N if N else fint(1)\n        mem_buff = cobj(safe_N * safe_N * sizeof(T) +\n                        safe_N * safe_NRHS * sizeof(T) +\n                        safe_N * sizeof(fint))\n\n        a = mem_buff\n        b = a + safe_N * safe_N * sizeof(T)\n        ipiv = b + safe_N * safe_NRHS * sizeof(T)\n\n        self.A = Ptr[T](a)\n        self.B = Ptr[T](b)\n        self.IPIV = Ptr[fint](ipiv)\n        self.N = N\n        self.NRHS = NRHS\n        self.LDA = ld\n        self.LDB = ld\n\n    def release(self):\n        free(self.A)\n\n    def call(self):\n        A = self.A\n        B = self.B\n        IPIV = self.IPIV\n        N = self.N\n        NRHS = self.NRHS\n        LDA = self.LDA\n        LDB = self.LDB\n        rv = fint()\n\n        args = (__ptr__(N),\n                __ptr__(NRHS),\n                A.as_byte(),\n                __ptr__(LDA),\n                IPIV,\n                B.as_byte(),\n                __ptr__(LDB),\n                __ptr__(rv))\n\n        if T is float:\n            dgesv_(*args)\n        elif T is float32:\n            sgesv_(*args)\n        elif T is complex:\n            zgesv_(*args)\n        elif T is complex64:\n            cgesv_(*args)\n        else:\n            compile_error(\"[internal error] bad dtype for gesv\")\n\n        return rv\n\ndef _solve(a, b):\n    if a.ndim == b.ndim:\n        if a.shape[:-2] != b.shape[:-2]:\n            pre_broadcast = broadcast_shapes(a.shape[:-2], b.shape[:-2])\n            a = broadcast_to(a, pre_broadcast + a.shape[-2:])\n            b = broadcast_to(b, pre_broadcast + b.shape[-2:])\n    else:\n        pre_broadcast = broadcast_shapes(a.shape[:-2], b.shape[:-2])\n        a1 = broadcast_to(a, pre_broadcast + a.shape[-2:])\n        b1 = broadcast_to(b, pre_broadcast + b.shape[-2:])\n        return _solve(a1, b1)\n\n    n = _square_rows(a)\n    m, k = _rows_cols(b)\n\n    if m != n:\n        raise ValueError(\"solve: 'a' and 'b' don't have the same number of rows\")\n\n    r = empty(b.shape, dtype=a.dtype)\n    nrhs = b.shape[-1]\n    params = GesvParams[a.dtype](fint(n), fint(nrhs))\n    a_in = LinearizeData(n, n, a.strides[-1], a.strides[-2])\n    b_in = LinearizeData(nrhs, n, b.strides[-1], b.strides[-2])\n    r_out = LinearizeData(nrhs, n, r.strides[-1], r.strides[-2])\n    error_occured = False\n\n    for idx in multirange(a.shape[:-2]):\n        a_ptr = a._ptr(idx + (0, 0))\n        b_ptr = b._ptr(idx + (0, 0))\n        r_ptr = r._ptr(idx + (0, 0))\n\n        a_in.linearize(params.A, a_ptr)\n        b_in.linearize(params.B, b_ptr)\n        not_ok = params.call()\n\n        if not not_ok:\n            r_out.delinearize(r_ptr, params.B)\n        else:\n            r_out.nan_matrix(r_ptr)\n            error_occured = True\n\n    params.release()\n\n    if error_occured:\n        raise LinAlgError(\"Singular matrix\")\n\n    return r\n\ndef _solve1(a, b):\n    n = _square_rows(a)\n    m = b.shape[0]\n\n    if m != n:\n        raise ValueError(\"solve: 'a' and 'b' don't have the same number of rows\")\n\n    r = empty((m,), dtype=a.dtype)\n    params = GesvParams[a.dtype](fint(n), fint(1))\n    a_in = LinearizeData(n, n, a.strides[-1], a.strides[-2])\n    b_in = LinearizeData(1, n, 1, b.strides[0])\n    r_out = LinearizeData(1, n, 1, r.strides[0])\n    error_occured = False\n\n    for idx in multirange(a.shape[:-2]):\n        a_ptr = a._ptr(idx + (0, 0))\n        b_ptr = b.data\n        r_ptr = r.data\n\n        a_in.linearize(params.A, a_ptr)\n        b_in.linearize(params.B, b_ptr)\n        not_ok = params.call()\n\n        if not not_ok:\n            r_out.delinearize(r_ptr, params.B)\n        else:\n            r_out.nan_matrix(r_ptr)\n            error_occured = True\n\n    params.release()\n\n    if error_occured:\n        raise LinAlgError(\"Singular matrix\")\n\n    return r\n\ndef solve(a, b):\n    dtype = type(\n              coerce(\n                type(asarray(a).data[0]),\n                type(asarray(b).data[0])))\n    a = _asarray(a, dtype=dtype)\n    b = _asarray(b, dtype=dtype)\n\n    if a.ndim < 2:\n        compile_error(\"'a' must be at least 2-dimensional\")\n\n    if b.ndim == 0:\n        compile_error(\"'b' must be at least 1-dimensional\")\n\n    if b.ndim == 1:\n        return _solve1(a, b)\n    else:\n        return _solve(a, b)\n\ndef _inv(a, ignore_errors: Literal[bool]):\n    a = _asarray(a)\n    n = _square_rows(a)\n\n    r = empty(a.shape, dtype=a.dtype)\n    params = GesvParams[a.dtype](fint(n), fint(n))\n    a_in = LinearizeData(n, n, a.strides[-1], a.strides[-2])\n    r_out = LinearizeData(n, n, r.strides[-1], r.strides[-2])\n    error_occured = False\n\n    for idx in multirange(a.shape[:-2]):\n        a_ptr = a._ptr(idx + (0, 0))\n        r_ptr = r._ptr(idx + (0, 0))\n\n        a_in.linearize(params.A, a_ptr)\n        LinearizeData.identity_matrix(params.B, n)\n        not_ok = params.call()\n\n        if not not_ok:\n            r_out.delinearize(r_ptr, params.B)\n        else:\n            r_out.nan_matrix(r_ptr)\n            error_occured = True\n\n    params.release()\n\n    if not ignore_errors:\n        if error_occured:\n            raise LinAlgError(\"Singular matrix\")\n\n    return r\n\ndef inv(a):\n    return _inv(a, ignore_errors=False)\n\n\n############\n# Cholesky #\n############\n\nclass PotrfParams:\n    A: Ptr[T]\n    N: fint\n    LDA: fint\n    UPLO: byte\n    T: type\n\n    def __init__(self, UPLO: byte, N: fint):\n        safe_N = int(N)\n        lda = N if N else fint(1)\n        mem_buff = cobj(safe_N * safe_N * sizeof(T))\n\n        a = mem_buff\n        self.A = Ptr[T](a)\n        self.N = N\n        self.LDA = lda\n        self.UPLO = UPLO\n\n    def zero_lower_triangle(self):\n        n = int(self.N)\n        matrix = self.A\n        for i in range(n - 1):\n            for j in range(i + 1, n):\n                matrix[j] = zero(T)\n            matrix += n\n\n    def zero_upper_triangle(self):\n        n = int(self.N)\n        matrix = self.A\n        matrix += n\n        for i in range(1, n):\n            for j in range(i):\n                matrix[j] = zero(T)\n            matrix += n\n\n    def call(self):\n        UPLO = self.UPLO\n        N = self.N\n        A = self.A\n        LDA = self.LDA\n        rv = fint()\n\n        args = (__ptr__(UPLO),\n                __ptr__(N),\n                A.as_byte(),\n                __ptr__(LDA),\n                __ptr__(rv))\n\n        if T is float:\n            dpotrf_(*args)\n        elif T is float32:\n            spotrf_(*args)\n        elif T is complex:\n            zpotrf_(*args)\n        elif T is complex64:\n            cpotrf_(*args)\n        else:\n            compile_error(\"[internal error] bad dtype for potrf\")\n\n        return rv\n\n    def release(self):\n        free(self.A)\n\ndef cholesky(a, upper: bool = False):\n    a = _asarray(a)\n    uplo = byte(85 if upper else 76)  # 'U' / 'L'\n    n = _square_rows(a)\n\n    params = PotrfParams[a.dtype](uplo, fint(n))\n    r = empty(a.shape, dtype=a.dtype)\n    a_in = LinearizeData(n, n, a.strides[-1], a.strides[-2])\n    r_out = LinearizeData(n, n, r.strides[-1], r.strides[-2])\n    error_occured = False\n\n    for idx in multirange(a.shape[:-2]):\n        a_ptr = a._ptr(idx + (0, 0))\n        r_ptr = r._ptr(idx + (0, 0))\n\n        a_in.linearize(params.A, a_ptr)\n        not_ok = params.call()\n\n        if not not_ok:\n            if upper:\n                params.zero_lower_triangle()\n            else:\n                params.zero_upper_triangle()\n            r_out.delinearize(r_ptr, params.A)\n        else:\n            error_occured = True\n            r_out.nan_matrix(r_ptr)\n\n    params.release()\n\n    if error_occured:\n        raise LinAlgError(\"Matrix is not positive definite\")\n\n    return r\n\n\n#######\n# Eig #\n#######\n\ndef _mk_complex_array_from_real(c: Ptr[C], re: Ptr[R], n: int, C: type, R: type):\n    for i in range(n):\n        c[i] = C(re[i], zero(R))\n\ndef _mk_complex_array(c: Ptr[C], re: Ptr[R], im: Ptr[R], n: int, C: type, R: type):\n    for i in range(n):\n        c[i] = C(re[i], im[i])\n\ndef _mk_complex_array_conjugate_pair(c: Ptr[C], r: Ptr[R], n: int, C: type, R: type):\n    for i in range(n):\n        re = r[i]\n        im = r[i + n]\n        c[i] = C(re, im)\n        c[i + n] = C(re, -im)\n\ndef _mk_geev_complex_eigenvectors(c: Ptr[C], r: Ptr[R], i: Ptr[R], n: int, C: type, R: type):\n    it = 0\n    while it < n:\n        if i[it] == zero(R):\n            _mk_complex_array_from_real(c, r, n)\n            c += n\n            r += n\n            it += 1\n        else:\n            _mk_complex_array_conjugate_pair(c, r, n)\n            c += 2 * n\n            r += 2 * n\n            it += 2\n\nclass GeevParams:\n    A: Ptr[T]\n    WR: Ptr[B]\n    WI: Ptr[T]\n    VLR: Ptr[T]\n    VRR: Ptr[T]\n    WORK: Ptr[T]\n    W: Ptr[T]\n    VL: Ptr[T]\n    VR: Ptr[T]\n    N: fint\n    LDA: fint\n    LDVL: fint\n    LDVR: fint\n    LWORK: fint\n    JOBVL: byte\n    JOBVR: byte\n    T: type\n    B: type\n\n    def _init_real(self, jobvl: byte, jobvr: byte, n: fint):\n        safe_n = int(n)\n        a_size = safe_n * safe_n * sizeof(T)\n        wr_size = safe_n * sizeof(T)\n        wi_size = safe_n * sizeof(T)\n        vlr_size = safe_n * safe_n * sizeof(T) if jobvl == byte(86) else 0\n        vrr_size = safe_n * safe_n * sizeof(T) if jobvr == byte(86) else 0\n        w_size = wr_size * 2\n        vl_size = vlr_size * 2\n        vr_size = vrr_size * 2\n        ld = n if n else fint(1)\n\n        mem_buff = cobj(a_size + wr_size + wi_size +\n                        vlr_size + vrr_size +\n                        w_size + vl_size + vr_size)\n        a = mem_buff\n        wr = a + a_size\n        wi = wr + wr_size\n        vlr = wi + wi_size\n        vrr = vlr + vlr_size\n        w = vrr + vrr_size\n        vl = w + w_size\n        vr = vl + vl_size\n\n        self.A = Ptr[T](a)\n        self.WR = Ptr[T](wr)\n        self.WI = Ptr[T](wi)\n        self.VLR = Ptr[T](vlr)\n        self.VRR = Ptr[T](vrr)\n        self.W = Ptr[T](w)\n        self.VL = Ptr[T](vl)\n        self.VR = Ptr[T](vr)\n        self.N = n\n        self.LDA = ld\n        self.LDVL = ld\n        self.LDVR = ld\n        self.JOBVL = jobvl\n        self.JOBVR = jobvr\n\n        # work size query\n        work_size_query = T()\n        self.LWORK = fint(-1)\n        self.WORK = __ptr__(work_size_query)\n\n        if self.call():\n            free(mem_buff)\n            raise LinAlgError(\"error in evd work size query\")\n\n        work_count = cast(work_size_query, int)\n\n        mem_buff2 = cobj(work_count * sizeof(T))\n        work = mem_buff2\n\n        self.LWORK = fint(work_count)\n        self.WORK = Ptr[T](work)\n\n    def _init_complex(self, jobvl: byte, jobvr: byte, n: fint):\n        safe_n = int(n)\n        a_size = safe_n * safe_n * sizeof(T)\n        w_size = safe_n * sizeof(T)\n        vl_size = safe_n * safe_n * sizeof(T) if jobvl == byte(86) else 0\n        vr_size = safe_n * safe_n * sizeof(T) if jobvr == byte(86) else 0\n        rwork_size = 2 * safe_n * sizeof(B)\n        total_size = a_size + w_size + vl_size + vr_size + rwork_size\n        ld = n if n else fint(1)\n\n        mem_buff = cobj(total_size)\n        a = mem_buff\n        w = a + a_size\n        vl = w + w_size\n        vr = vl + vl_size\n        rwork = vr + vr_size\n\n        self.A = Ptr[T](a)\n        self.WR = Ptr[B](rwork)\n        self.WI = Ptr[T]()\n        self.VLR = Ptr[T]()\n        self.VRR = Ptr[T]()\n        self.VL = Ptr[T](vl)\n        self.VR = Ptr[T](vr)\n        self.W = Ptr[T](w)\n        self.N = n\n        self.LDA = ld\n        self.LDVL = ld\n        self.LDVR = ld\n        self.JOBVL = jobvl\n        self.JOBVR = jobvr\n\n        # work size query\n        work_size_query = T()\n        self.LWORK = fint(-1)\n        self.WORK = __ptr__(work_size_query)\n\n        if self.call():\n            free(mem_buff)\n            raise LinAlgError(\"error in evd work size query\")\n\n        work_count = cast(work_size_query.real, int)\n        if work_count == 0:\n            work_count = 1\n\n        mem_buff2 = cobj(work_count * sizeof(T))\n        work = mem_buff2\n\n        self.LWORK = fint(work_count)\n        self.WORK = Ptr[T](work)\n\n    def __init__(self, jobvl: byte, jobvr: byte, n: fint):\n        if T is complex or T is complex64:\n            self._init_complex(jobvl, jobvr, n)\n        else:\n            self._init_real(jobvl, jobvr, n)\n\n    def call(self):\n        A = self.A\n        WR = self.WR\n        WI = self.WI\n        VLR = self.VLR\n        VRR = self.VRR\n        WORK = self.WORK\n        W = self.W\n        VL = self.VL\n        VR = self.VR\n        N = self.N\n        LDA = self.LDA\n        LDVL = self.LDVL\n        LDVR = self.LDVR\n        LWORK = self.LWORK\n        JOBVL = self.JOBVL\n        JOBVR = self.JOBVR\n        rv = fint()\n\n        args_real = (__ptr__(JOBVL),\n                     __ptr__(JOBVR),\n                     __ptr__(N),\n                     A.as_byte(),\n                     __ptr__(LDA),\n                     WR.as_byte(),\n                     WI.as_byte(),\n                     VLR.as_byte(),\n                     __ptr__(LDVL),\n                     VRR.as_byte(),\n                     __ptr__(LDVR),\n                     WORK.as_byte(),\n                     __ptr__(LWORK),\n                     __ptr__(rv))\n\n        args_cplx = (__ptr__(JOBVL),\n                     __ptr__(JOBVR),\n                     __ptr__(N),\n                     A.as_byte(),\n                     __ptr__(LDA),\n                     W.as_byte(),\n                     VL.as_byte(),\n                     __ptr__(LDVL),\n                     VR.as_byte(),\n                     __ptr__(LDVR),\n                     WORK.as_byte(),\n                     __ptr__(LWORK),\n                     WR.as_byte(),\n                     __ptr__(rv))\n\n        if T is float:\n            dgeev_(*args_real)\n        elif T is float32:\n            sgeev_(*args_real)\n        elif T is complex:\n            zgeev_(*args_cplx)\n        elif T is complex64:\n            cgeev_(*args_cplx)\n        else:\n            compile_error(\"[internal error] bad dtype for geev\")\n\n        return rv\n\n    def release(self):\n        free(self.WORK)\n        free(self.A)\n\n    def process_geev_results(self):\n        if T is complex or T is complex64:\n            return\n\n        C = type(_complextype(T))\n\n        _mk_complex_array(Ptr[C](self.W.as_byte()), self.WR, self.WI, int(self.N))\n\n        if self.JOBVL == byte(86):\n            _mk_geev_complex_eigenvectors(Ptr[C](self.VL.as_byte()), self.VLR, self.WI, int(self.N))\n\n        if self.JOBVR == byte(86):\n            _mk_geev_complex_eigenvectors(Ptr[C](self.VR.as_byte()), self.VRR, self.WI, int(self.N))\n\n@tuple\nclass EigResult[A, B]:\n    eigenvalues: A\n    eigenvectors: B\n\n    def __getitem__(self, idx: Literal[int]):\n        if idx == 0 or idx == -2:\n            return self.eigenvalues\n        elif idx == 1 or idx == -1:\n            return self.eigenvectors\n        else:\n            compile_error(\"tuple ('EigResult') index out of range\")\n\ndef _eig(a, JOBVL: byte, JOBVR: byte, compute_eigenvectors: Literal[bool]):\n    a = _asarray(a)\n    B = type(_basetype(a.dtype))\n    C = type(_complextype(a.dtype))\n\n    n = _square_rows(a)\n    params = GeevParams[a.dtype, B](JOBVL, JOBVR, fint(n))\n\n    eigenvalues = empty(a.shape[:-2] + (n,), dtype=C)\n    if compute_eigenvectors:\n        eigenvectors = empty(a.shape, dtype=C)\n    else:\n        eigenvectors = None\n\n    a_in = LinearizeData(n, n, a.strides[-1], a.strides[-2])\n    w_out = LinearizeData(1, n, 0, eigenvalues.strides[-1])\n    if compute_eigenvectors:\n        vr_out = LinearizeData(n, n, eigenvectors.strides[-1], eigenvectors.strides[-2])\n    else:\n        vr_out = None\n    error_occured = False\n\n    for idx in multirange(a.shape[:-2]):\n        matrix_ptr = a._ptr(idx + (0, 0))\n        eigval_ptr = eigenvalues._ptr(idx + (0,))\n        if compute_eigenvectors:\n            eigvec_ptr = eigenvectors._ptr(idx + (0, 0))\n        else:\n            eigvec_ptr = None\n\n        a_in.linearize(params.A, matrix_ptr)\n        not_ok = params.call()\n\n        if not not_ok:\n            params.process_geev_results()\n            w_out.delinearize(eigval_ptr, Ptr[C](params.W.as_byte()))\n            if compute_eigenvectors:\n                vr_out.delinearize(eigvec_ptr, Ptr[C](params.VR.as_byte()))\n        else:\n            w_out.nan_matrix(eigval_ptr)\n            if compute_eigenvectors:\n                vr_out.nan_matrix(eigvec_ptr)\n            error_occured = True\n\n    params.release()\n\n    if error_occured:\n        raise LinAlgError(\"Eigenvalues did not converge\")\n\n    if compute_eigenvectors:\n        return EigResult(eigenvalues, eigenvectors)\n    else:\n        return eigenvalues\n\ndef eig(a):\n    jobvl_code = byte(78)  # 'N'\n    jobvr_code = byte(86)  # 'V'\n    return _eig(a, jobvl_code, jobvr_code, compute_eigenvectors=True)\n\ndef eigvals(a):\n    jobvl_code = byte(78)  # 'N'\n    jobvr_code = byte(78)  # 'N'\n    return _eig(a, jobvl_code, jobvr_code, compute_eigenvectors=False)\n\n\n#######\n# SVD #\n#######\n\ndef _compute_urows_vtcolumns(jobz: byte, m: fint, n: fint):\n    if jobz == byte(78):  # 'N'\n        return (fint(0), fint(0))\n    elif jobz == byte(65):  # 'A'\n        return (m, n)\n    else:  # 'S'\n        min_m_n = m if m < n else n\n        return (min_m_n, min_m_n)\n\nclass GesddParams:\n    A: Ptr[T]\n    S: Ptr[B]\n    U: Ptr[T]\n    VT: Ptr[T]\n    WORK: Ptr[T]\n    RWORK: Ptr[B]\n    IWORK: Ptr[fint]\n    M: fint\n    N: fint\n    LDA: fint\n    LDU: fint\n    LDVT: fint\n    LWORK: fint\n    JOBZ: byte\n    T: type\n    B: type\n\n    def _init_real(self, jobz: byte, m: fint, n: fint):\n        safe_m = int(m)\n        safe_n = int(n)\n        a_size = safe_m * safe_n * sizeof(T)\n        min_m_n = m if m < n else n\n        safe_min_m_n = int(min_m_n)\n        s_size = safe_min_m_n * sizeof(T)\n        iwork_size = 8 * safe_min_m_n * sizeof(fint)\n        ld = m if m else fint(1)\n\n        u_row_count, vt_column_count = _compute_urows_vtcolumns(jobz, m, n)\n        safe_u_row_count = int(u_row_count)\n        safe_vt_column_count = int(vt_column_count)\n\n        u_size = safe_u_row_count * safe_m * sizeof(T)\n        vt_size = safe_n * safe_vt_column_count * sizeof(T)\n        mem_buff = cobj(a_size + s_size + u_size + vt_size + iwork_size)\n\n        a = mem_buff\n        s = a + a_size\n        u = s + s_size\n        vt = u + u_size\n        iwork = vt + vt_size\n        vt_column_count = vt_column_count if vt_column_count else fint(1)\n\n        self.M = m\n        self.N = n\n        self.A = Ptr[T](a)\n        self.S = Ptr[B](s)\n        self.U = Ptr[T](u)\n        self.VT = Ptr[T](vt)\n        self.RWORK = Ptr[B]()\n        self.IWORK = Ptr[fint](iwork)\n        self.LDA = ld\n        self.LDU = ld\n        self.LDVT = vt_column_count\n        self.JOBZ = jobz\n\n        # work size query\n        work_size_query = T()\n        self.LWORK = fint(-1)\n        self.WORK = __ptr__(work_size_query)\n\n        if self.call():\n            free(mem_buff)\n            raise LinAlgError(\"error in gesdd work size query\")\n\n        work_count = cast(work_size_query, int)\n        if work_count == 0:\n            work_count = 1\n\n        work_size = work_count * sizeof(T)\n        mem_buff2 = cobj(work_size)\n        work = mem_buff2\n\n        self.LWORK = fint(work_count)\n        self.WORK = Ptr[T](work)\n\n    def _init_complex(self, jobz: byte, m: fint, n: fint):\n        safe_m = int(m)\n        safe_n = int(n)\n        min_m_n = m if m < n else n\n        safe_min_m_n = int(min_m_n)\n        ld = m if m else fint(1)\n\n        u_row_count, vt_column_count = _compute_urows_vtcolumns(jobz, m, n)\n        safe_u_row_count = int(u_row_count)\n        safe_vt_column_count = int(vt_column_count)\n\n        a_size = safe_m * safe_n * sizeof(T)\n        s_size = safe_min_m_n * sizeof(B)\n        u_size = safe_u_row_count * safe_m * sizeof(T)\n        vt_size = safe_n * safe_vt_column_count * sizeof(T)\n        rwork_size = (7 * safe_min_m_n if jobz == byte(78) else\n                      5*safe_min_m_n * safe_min_m_n + 5*safe_min_m_n)\n        rwork_size *= sizeof(T)\n        iwork_size = 8 * safe_min_m_n * sizeof(fint)\n        mem_buff = cobj(a_size +\n                        s_size +\n                        u_size +\n                        vt_size +\n                        rwork_size +\n                        iwork_size)\n\n        a = mem_buff\n        s = a + a_size\n        u = s + s_size\n        vt = u + u_size\n        rwork = vt + vt_size\n        iwork = rwork + rwork_size\n\n        vt_column_count = vt_column_count if vt_column_count else fint(1)\n\n        self.A = Ptr[T](a)\n        self.S = Ptr[B](s)\n        self.U = Ptr[T](u)\n        self.VT = Ptr[T](vt)\n        self.RWORK = Ptr[B](rwork)\n        self.IWORK = Ptr[fint](iwork)\n        self.M = m\n        self.N = n\n        self.LDA = ld\n        self.LDU = ld\n        self.LDVT = vt_column_count\n        self.JOBZ = jobz\n\n        # work size query\n        work_size_query = T()\n        self.LWORK = fint(-1)\n        self.WORK = __ptr__(work_size_query)\n\n        if self.call():\n            free(mem_buff)\n            raise LinAlgError(\"error in gesdd work size query\")\n\n        work_count = cast(Ptr[B](__ptr__(work_size_query).as_byte())[0], int)\n        if work_count == 0:\n            work_count = 1\n\n        work_size = work_count * sizeof(T)\n        mem_buff2 = cobj(work_size)\n        work = mem_buff2\n\n        self.LWORK = fint(work_count)\n        self.WORK = Ptr[T](work)\n\n    def __init__(self, jobz: byte, m: fint, n: fint):\n        if T is complex or T is complex64:\n            self._init_complex(jobz, m, n)\n        else:\n            self._init_real(jobz, m, n)\n\n    def call(self):\n        A = self.A\n        S = self.S\n        U = self.U\n        VT = self.VT\n        WORK = self.WORK\n        RWORK = self.RWORK\n        IWORK = self.IWORK\n        M = self.M\n        N = self.N\n        LDA = self.LDA\n        LDU = self.LDU\n        LDVT = self.LDVT\n        LWORK = self.LWORK\n        JOBZ = self.JOBZ\n        rv = fint()\n\n        args_real = (__ptr__(JOBZ),\n                     __ptr__(M),\n                     __ptr__(N),\n                     A.as_byte(),\n                     __ptr__(LDA),\n                     S.as_byte(),\n                     U.as_byte(),\n                     __ptr__(LDU),\n                     VT.as_byte(),\n                     __ptr__(LDVT),\n                     WORK.as_byte(),\n                     __ptr__(LWORK),\n                     IWORK,\n                     __ptr__(rv))\n\n        args_cplx = (__ptr__(JOBZ),\n                     __ptr__(M),\n                     __ptr__(N),\n                     A.as_byte(),\n                     __ptr__(LDA),\n                     S.as_byte(),\n                     U.as_byte(),\n                     __ptr__(LDU),\n                     VT.as_byte(),\n                     __ptr__(LDVT),\n                     WORK.as_byte(),\n                     __ptr__(LWORK),\n                     RWORK.as_byte(),\n                     IWORK,\n                     __ptr__(rv))\n\n        if T is float:\n            dgesdd_(*args_real)\n        elif T is float32:\n            sgesdd_(*args_real)\n        elif T is complex:\n            zgesdd_(*args_cplx)\n        elif T is complex64:\n            cgesdd_(*args_cplx)\n        else:\n            compile_error(\"[internal error] bad dtype for gesdd\")\n\n        return rv\n\n    def release(self):\n        free(self.A)\n        free(self.WORK)\n\n@tuple\nclass SVDResult[A1, A2]:\n    U: A1\n    S: A2\n    Vh: A1\n\n    def __getitem__(self, idx: Literal[int]):\n        if idx == 0 or idx == -3:\n            return self.U\n        elif idx == 1 or idx == -2:\n            return self.S\n        elif idx == 2 or idx == -1:\n            return self.Vh\n        else:\n            compile_error(\"tuple ('SVDResult') index out of range\")\n\ndef _svd(a, JOBZ: byte, compute_uv: Literal[bool]):\n    B = type(_basetype(a.dtype))\n    m, n = _rows_cols(a)\n    min_m_n = min(m, n)\n\n    params = GesddParams[a.dtype, B](JOBZ, fint(m), fint(n))\n    S = empty(a.shape[:-2] + (min_m_n,), dtype=B)\n    a_in = LinearizeData(n, m, a.strides[-1], a.strides[-2])\n    s_out = LinearizeData(1, min_m_n, 0, S.strides[-1])\n\n    if compute_uv:\n        if JOBZ == byte(83):  # 'S'\n            u_columns = min_m_n\n            v_rows = min_m_n\n        else:\n            u_columns = m\n            v_rows = n\n\n        U = empty(a.shape[:-2] + (m, u_columns), dtype=a.dtype)\n        V = empty(a.shape[:-2] + (v_rows, n), dtype=a.dtype)\n        u_out = LinearizeData(u_columns, m, U.strides[-1], U.strides[-2])\n        v_out = LinearizeData(n, v_rows, V.strides[-1], V.strides[-2])\n    else:\n        U = None\n        V = None\n        u_out = None\n        v_out = None\n\n    error_occured = False\n\n    for idx in multirange(a.shape[:-2]):\n        a_ptr = a._ptr(idx + (0, 0))\n        s_ptr = S._ptr(idx + (0,))\n        if compute_uv:\n            u_ptr = U._ptr(idx + (0, 0))\n            v_ptr = V._ptr(idx + (0, 0))\n        else:\n            u_ptr = None\n            v_ptr = None\n\n        a_in.linearize(params.A, a_ptr)\n        not_ok = params.call()\n\n        if not not_ok:\n            if compute_uv:\n                if JOBZ == byte(65) and min_m_n == 0:\n                    LinearizeData.identity_matrix(params.U, m)\n                    LinearizeData.identity_matrix(params.VT, n)\n\n                u_out.delinearize(u_ptr, params.U)\n                s_out.delinearize(s_ptr, params.S)\n                v_out.delinearize(v_ptr, params.VT)\n            else:\n                s_out.delinearize(s_ptr, params.S)\n        else:\n            if compute_uv:\n                u_out.nan_matrix(u_ptr)\n                s_out.nan_matrix(s_ptr)\n                v_out.nan_matrix(v_ptr)\n            else:\n                s_out.nan_matrix(s_ptr)\n            error_occured = True\n\n    params.release()\n\n    if error_occured:\n        raise LinAlgError(\"SVD did not converge\")\n\n    if compute_uv:\n        return SVDResult(U, S, V)\n    else:\n        return S\n\ndef svd(a,\n        full_matrices: bool = True,\n        compute_uv: Literal[bool] = True,\n        hermitian: bool = False):\n\n    a = _asarray(a)\n    dtype = a.dtype\n    m, n = _rows_cols(a)\n    k = min(m, n)\n\n    if hermitian:\n        pre_shape = a.shape[:-2]\n        s_shape = pre_shape + (k,)\n        u_shape = pre_shape + ((m, m) if full_matrices else (m, k))\n        v_shape = pre_shape + ((n, n) if full_matrices else (k, n))\n\n        if compute_uv:\n            e_s, e_u = eigh(a)  # note that `k == m == n` in this case\n            sidx = Ptr[int](k)\n            if dtype is complex:\n                SH = empty(s_shape, float)\n            elif dtype is complex64:\n                SH = empty(s_shape, float32)\n            else:\n                SH = empty(s_shape, dtype)\n            UH = empty(u_shape, dtype)\n            VH = empty(v_shape, dtype)\n\n            if a.ndim == 2:\n                for r in range(k):\n                    sidx[r] = r\n                sort(sidx, k, key=lambda x: -abs(e_s.data[x]))\n\n                for j in range(k):\n                    mapping = sidx[j]\n                    s_elem = e_s.data[mapping]\n                    negative = s_elem < type(s_elem)()\n                    SH.data[j] = abs(s_elem)\n\n                    for i in range(k):\n                        u_elem = e_u._ptr((i, mapping))[0]\n                        dst = UH._ptr((i, j))\n                        dst[0] = u_elem\n\n                        dst = VH._ptr((j, i))  # transpose\n                        if negative:\n                            u_elem = -u_elem\n                        if hasattr(u_elem, \"conjugate\"):\n                            u_elem = u_elem.conjugate()\n                        dst[0] = u_elem\n            else:\n                for idx in multirange(pre_shape):\n                    sub_e_s = e_s._ptr(idx + (0,))\n                    sub_e_u = e_u._ptr(idx + (0, 0))\n                    sub_SH = SH._ptr(idx + (0,))\n                    sub_UH = UH._ptr(idx + (0, 0))\n                    sub_VH = VH._ptr(idx + (0, 0))\n\n                    for r in range(k):\n                        sidx[r] = r\n                    sort(sidx, k, key=lambda x: -abs(sub_e_s[x]))\n\n                    for j in range(k):\n                        mapping = sidx[j]\n                        s_elem = sub_e_s[mapping]\n                        negative = s_elem < type(s_elem)()\n                        sub_SH[j] = abs(s_elem)\n\n                        for i in range(k):\n                            u_elem = (sub_e_u + (i * k + mapping))[0]\n                            dst = sub_UH + (i * k + j)\n                            dst[0] = u_elem\n\n                            dst = sub_VH + (j * k + i)  # transpose\n                            if negative:\n                                u_elem = -u_elem\n                            if hasattr(u_elem, \"conjugate\"):\n                                u_elem = u_elem.conjugate()\n                            dst[0] = u_elem\n\n            return SVDResult(UH, SH, VH)\n        else:\n            evh = eigvalsh(a)\n\n            if a.ndim == 2:\n                evh = eigvalsh(a)\n                for i in range(k):\n                    evh.data[i] = abs(evh.data[i])\n                sort(evh.data, k, key=lambda x: -x)\n            else:\n                for idx in multirange(pre_shape):\n                    sub_evh = evh._ptr(idx + (0,))\n                    for i in range(k):\n                        sub_evh[i] = abs(sub_evh[i])\n                    sort(sub_evh, k, key=lambda x: -x)\n\n            return evh\n\n    if compute_uv:\n        jobz_code = byte(65 if full_matrices else 83)  # 'A' / 'S'\n        return _svd(a, jobz_code, compute_uv=True)\n    else:\n        jobz_code = byte(78)  # 'N'\n        return _svd(a, jobz_code, compute_uv=False)\n\ndef svdvals(x):\n    return svd(x, compute_uv=False, hermitian=False)\n\n\n######\n# QR #\n######\n\nclass GeqrfParams:\n    M: fint\n    N: fint\n    A: Ptr[T]\n    LDA: fint\n    TAU: Ptr[T]\n    WORK: Ptr[T]\n    LWORK: fint\n    T: type\n\n    def _init_real(self, m: fint, n: fint):\n        min_m_n = m if m < n else n\n        safe_min_m_n = int(min_m_n)\n        safe_m = int(m)\n        safe_n = int(n)\n\n        a_size = safe_m * safe_n * sizeof(T)\n        tau_size = safe_min_m_n * sizeof(T)\n        lda = m if m else fint(1)\n\n        mem_buff = cobj(a_size + tau_size)\n        a = mem_buff\n        tau = a + a_size\n        str.memset(tau.as_byte(), byte(0), tau_size)\n\n        self.M = m\n        self.N = n\n        self.A = Ptr[T](a)\n        self.TAU = Ptr[T](tau)\n        self.LDA = lda\n\n        # work size query\n        work_size_query = T()\n\n        self.WORK = __ptr__(work_size_query)\n        self.LWORK = fint(-1)\n\n        if self.call():\n            free(mem_buff)\n            raise LinAlgError(\"error in geqrf work size query\")\n\n        work_count = cast(work_size_query, fint)\n\n        lw = n if n else fint(1)\n        lw = lw if lw > work_count else work_count\n        self.LWORK = lw\n\n        work_size = int(lw) * sizeof(T)\n        mem_buff2 = cobj(work_size)\n\n        work = mem_buff2\n        self.WORK = Ptr[T](work)\n\n    def _init_complex(self, m: fint, n: fint):\n        min_m_n = m if m < n else n\n        safe_min_m_n = int(min_m_n)\n        safe_m = int(m)\n        safe_n = int(n)\n\n        a_size = safe_m * safe_n * sizeof(T)\n        tau_size = safe_min_m_n * sizeof(T)\n        lda = m if m else fint(1)\n\n        mem_buff = cobj(a_size + tau_size)\n        a = mem_buff\n        tau = a + a_size\n        str.memset(tau.as_byte(), byte(0), tau_size)\n\n        self.M = m\n        self.N = n\n        self.A = Ptr[T](a)\n        self.TAU = Ptr[T](tau)\n        self.LDA = lda\n\n        # work size query\n        work_size_query = T()\n\n        self.WORK = __ptr__(work_size_query)\n        self.LWORK = fint(-1)\n\n        if self.call():\n            free(mem_buff)\n            raise LinAlgError(\"error in geqrf work size query\")\n\n        work_count = cast(work_size_query.real, fint)\n\n        lw = n if n else fint(1)\n        lw = lw if lw > work_count else work_count\n        self.LWORK = lw\n\n        work_size = int(lw) * sizeof(T)\n        mem_buff2 = cobj(work_size)\n\n        work = mem_buff2\n        self.WORK = Ptr[T](work)\n\n    def __init__(self, m: fint, n: fint):\n        if T is complex or T is complex64:\n            self._init_complex(m, n)\n        else:\n            self._init_real(m, n)\n\n    def call(self):\n        M = self.M\n        N = self.N\n        A = self.A\n        LDA = self.LDA\n        TAU = self.TAU\n        WORK = self.WORK\n        LWORK = self.LWORK\n        rv = fint()\n\n        args = (__ptr__(M),\n                __ptr__(N),\n                A.as_byte(),\n                __ptr__(LDA),\n                TAU.as_byte(),\n                WORK.as_byte(),\n                __ptr__(LWORK),\n                __ptr__(rv))\n\n        if T is float:\n            dgeqrf_(*args)\n        elif T is float32:\n            sgeqrf_(*args)\n        elif T is complex:\n            zgeqrf_(*args)\n        elif T is complex64:\n            cgeqrf_(*args)\n        else:\n            compile_error(\"[internal error] bad dtype for geqrf\")\n\n        return rv\n\n    def release(self):\n        free(self.A)\n        free(self.WORK)\n\ndef _qr_r_raw(a):\n    m, n = _rows_cols(a)\n    params = GeqrfParams[a.dtype](fint(m), fint(n))\n    k = min(m, n)\n\n    tau = empty(a.shape[:-2] + (k,), dtype=a.dtype)\n    a_in = LinearizeData(n, m, a.strides[-1], a.strides[-2])\n    tau_out = LinearizeData(1, k, 1, tau.strides[-1])\n    error_occured = False\n\n    for idx in multirange(a.shape[:-2]):\n        a_ptr = a._ptr(idx + (0, 0))\n        tau_ptr = tau._ptr(idx + (0,))\n        a_in.linearize(params.A, a_ptr)\n        not_ok = params.call()\n\n        if not not_ok:\n            a_in.delinearize(a_ptr, params.A)\n            tau_out.delinearize(tau_ptr, params.TAU)\n        else:\n            tau_out.nan_matrix(tau_ptr)\n            error_occured = True\n\n    params.release()\n\n    if error_occured:\n        raise LinAlgError(\"Incorrect argument found while performing \"\n                          \"QR factorization\")\n\n    return tau\n\nclass GqrParams:\n    M: fint\n    MC: fint\n    MN: fint\n    A: Ptr[T]\n    Q: Ptr[T]\n    LDA: fint\n    TAU: Ptr[T]\n    WORK: Ptr[T]\n    LWORK: fint\n    T: type\n\n    def _init_real(self, m: fint, n: fint, mc: fint):\n        min_m_n = m if m < n else n\n        safe_mc = int(mc)\n        safe_min_m_n = int(min_m_n)\n        safe_m = int(m)\n        safe_n = int(n)\n        a_size = safe_m * safe_n * sizeof(T)\n        q_size = safe_m * safe_mc * sizeof(T)\n        tau_size = safe_min_m_n * sizeof(T)\n\n        lda = m if m else fint(1)\n        mem_buff = cobj(q_size + tau_size + a_size)\n\n        q = mem_buff\n        tau = q + q_size\n        a = tau + tau_size\n\n        self.M = m\n        self.MC = mc\n        self.MN = min_m_n\n        self.A = Ptr[T](a)\n        self.Q = Ptr[T](q)\n        self.TAU = Ptr[T](tau)\n        self.LDA = lda\n\n        # work size query\n        work_size_query = T()\n\n        self.WORK = __ptr__(work_size_query)\n        self.LWORK = fint(-1)\n\n        if self.call():\n            free(mem_buff)\n            raise LinAlgError(\"error in gqr work size query\")\n\n        work_count = cast(work_size_query, fint)\n\n        lw = n if n else fint(1)\n        lw = lw if lw > work_count else work_count\n        self.LWORK = lw\n\n        work_size = int(lw) * sizeof(T)\n        mem_buff2 = cobj(work_size)\n\n        work = mem_buff2\n        self.WORK = Ptr[T](work)\n\n    def _init_complex(self, m: fint, n: fint, mc: fint):\n        min_m_n = m if m < n else n\n        safe_mc = int(mc)\n        safe_min_m_n = int(min_m_n)\n        safe_m = int(m)\n        safe_n = int(n)\n        a_size = safe_m * safe_n * sizeof(T)\n        q_size = safe_m * safe_mc * sizeof(T)\n        tau_size = safe_min_m_n * sizeof(T)\n\n        lda = m if m else fint(1)\n        mem_buff = cobj(q_size + tau_size + a_size)\n\n        q = mem_buff\n        tau = q + q_size\n        a = tau + tau_size\n\n        self.M = m\n        self.MC = mc\n        self.MN = min_m_n\n        self.A = Ptr[T](a)\n        self.Q = Ptr[T](q)\n        self.TAU = Ptr[T](tau)\n        self.LDA = lda\n\n        # work size query\n        work_size_query = T()\n\n        self.WORK = __ptr__(work_size_query)\n        self.LWORK = fint(-1)\n\n        if self.call():\n            free(mem_buff)\n            raise LinAlgError(\"error in gqr work size query\")\n\n        work_count = cast(work_size_query.real, fint)\n\n        lw = n if n else fint(1)\n        lw = lw if lw > work_count else work_count\n        self.LWORK = lw\n\n        work_size = int(lw) * sizeof(T)\n        mem_buff2 = cobj(work_size)\n\n        work = mem_buff2\n        self.WORK = Ptr[T](work)\n\n    def __init__(self, m: fint, n: fint, mc: fint):\n        if T is complex or T is complex64:\n            self._init_complex(m, n, mc)\n        else:\n            self._init_real(m, n, mc)\n\n    def __init__(self, m: fint, n: fint):\n        self.__init__(m, n, m if m < n else n)\n\n    def call(self):\n        M = self.M\n        MC = self.MC\n        MN = self.MN\n        A = self.A\n        Q = self.Q\n        LDA = self.LDA\n        TAU = self.TAU\n        WORK = self.WORK\n        LWORK = self.LWORK\n        rv = fint()\n\n        args = (__ptr__(M),\n                __ptr__(MC),\n                __ptr__(MN),\n                Q.as_byte(),\n                __ptr__(LDA),\n                TAU.as_byte(),\n                WORK.as_byte(),\n                __ptr__(LWORK),\n                __ptr__(rv))\n\n        if T is float:\n            dorgqr_(*args)\n        elif T is float32:\n            sorgqr_(*args)\n        elif T is complex:\n            zungqr_(*args)\n        elif T is complex64:\n            cungqr_(*args)\n        else:\n            compile_error(\"[internal error] bad dtype for geqrf\")\n\n        return rv\n\n    def release(self):\n        free(self.Q)\n        free(self.WORK)\n\ndef _qr_reduced(a, tau):\n    m, n = _rows_cols(a)\n    params = GqrParams[a.dtype](fint(m), fint(n))\n    k = min(m, n)\n\n    q = empty(a.shape[:-2] + (m, k), dtype=a.dtype)\n    a_in = LinearizeData(n, m, a.strides[-1], a.strides[-2])\n    tau_in = LinearizeData(1, k, 1, tau.strides[-1])\n    q_out = LinearizeData(k, m, q.strides[-1], q.strides[-2])\n    error_occured = False\n\n    for idx in multirange(a.shape[:-2]):\n        a_ptr = a._ptr(idx + (0, 0))\n        tau_ptr = tau._ptr(idx + (0,))\n        q_ptr = q._ptr(idx + (0, 0))\n        a_in.linearize(params.A, a_ptr)\n        a_in.linearize(params.Q, a_ptr)\n        tau_in.linearize(params.TAU, tau_ptr)\n        not_ok = params.call()\n\n        if not not_ok:\n            q_out.delinearize(q_ptr, params.Q)\n        else:\n            q_out.nan_matrix(q_ptr)\n            error_occured = True\n\n    params.release()\n\n    if error_occured:\n        raise LinAlgError(\"Incorrect argument found while performing \"\n                          \"QR factorization\")\n\n    return q\n\ndef _qr_complete(a, tau):\n    m, n = _rows_cols(a)\n    params = GqrParams[a.dtype](fint(m), fint(n), fint(m))\n    k = min(m, n)\n\n    q = empty(a.shape[:-2] + (m, m), dtype=a.dtype)\n    a_in = LinearizeData(n, m, a.strides[-1], a.strides[-2])\n    tau_in = LinearizeData(1, k, 1, tau.strides[-1])\n    q_out = LinearizeData(m, m, q.strides[-1], q.strides[-2])\n    error_occured = False\n\n    for idx in multirange(a.shape[:-2]):\n        a_ptr = a._ptr(idx + (0, 0))\n        tau_ptr = tau._ptr(idx + (0,))\n        q_ptr = q._ptr(idx + (0, 0))\n        a_in.linearize(params.A, a_ptr)\n        a_in.linearize(params.Q, a_ptr)\n        tau_in.linearize(params.TAU, tau_ptr)\n        not_ok = params.call()\n\n        if not not_ok:\n            q_out.delinearize(q_ptr, params.Q)\n        else:\n            q_out.nan_matrix(q_ptr)\n            error_occured = True\n\n    params.release()\n\n    if error_occured:\n        raise LinAlgError(\"Incorrect argument found while performing \"\n                          \"QR factorization\")\n\n    return q\n\n@tuple\nclass QRResult[A]:\n    Q: A\n    R: A\n\n    def __getitem__(self, idx: Literal[int]):\n        if idx == 0 or idx == -2:\n            return self.Q\n        elif idx == 1 or idx == -1:\n            return self.R\n        else:\n            compile_error(\"tuple ('QRResult') index out of range\")\n\ndef _triu(a):\n    m = a.shape[-2]\n    n = a.shape[-1]\n    sm = a.strides[-2]\n    sn = a.strides[-1]\n    for idx in multirange(a.shape[:-2]):\n        a_ptr = a._ptr(idx + (0, 0))\n        for i in range(1, m):\n            for j in range(min(i, n)):\n                p = a_ptr.as_byte() + i*sm + j*sn\n                Ptr[a.dtype](p)[0] = zero(a.dtype)\n\ndef qr(a, mode: Literal[str] = 'reduced'):\n    if (mode != 'reduced' and mode != 'complete' and\n        mode != 'r' and mode != 'raw'):\n        compile_error(\"Unrecognized mode '\" + mode + \"'\")\n\n    a0 = a\n    a = _asarray(a)\n    # copy if _asarray() didn't do so already\n    if isinstance(a0, ndarray):\n        if a0.dtype is a.dtype:\n            a = a.copy()\n\n    tau = _qr_r_raw(a)\n    m, n = _rows_cols(a)\n    mn = min(m, n)\n\n    if mode == 'r':\n        r = a[..., :mn, :]\n        _triu(r)\n        return r\n\n    if mode == 'raw':\n        q = a.T\n        return q, tau\n\n    if mode == 'complete' and m > n:\n        mc = m\n        q = _qr_complete(a, tau)\n    else:\n        mc = mn\n        q = _qr_reduced(a, tau)\n\n    r = a[..., :mc, :]\n    _triu(a)\n    return QRResult(q, r)\n\n\n#################\n# Least Squares #\n#################\n\nclass GelsdParams:\n    M: fint\n    N: fint\n    NRHS: fint\n    A: Ptr[T]\n    LDA: fint\n    B_: Ptr[T]\n    LDB: fint\n    S: Ptr[B]\n    RCOND: Ptr[B]\n    RANK: fint\n    WORK: Ptr[T]\n    LWORK: fint\n    RWORK: Ptr[B]\n    IWORK: Ptr[fint]\n    T: type\n    B: type\n\n    def _init_real(self, m: fint, n: fint, nrhs: fint):\n        min_m_n = m if m < n else n\n        max_m_n = m if m > n else n\n        safe_min_m_n = int(min_m_n)\n        safe_max_m_n = int(max_m_n)\n        safe_m = int(m)\n        safe_n = int(n)\n        safe_nrhs = int(nrhs)\n\n        a_size = safe_m * safe_n * sizeof(T)\n        b_size = safe_max_m_n * safe_nrhs * sizeof(T)\n        s_size = safe_min_m_n * sizeof(T)\n\n        lda = m if m else fint(1)\n        ldb = max_m_n if max_m_n else fint(1)\n\n        msize = a_size + b_size + s_size\n        mem_buff = cobj(msize if msize else 1)\n\n        a = mem_buff\n        b = a + a_size\n        s = b + b_size\n\n        self.M = m\n        self.N = n\n        self.NRHS = nrhs\n        self.A = Ptr[T](a)\n        self.B_ = Ptr[T](b)\n        self.S = Ptr[B](s)\n        self.LDA = lda\n        self.LDB = ldb\n\n        # work size query\n        work_size_query = T()\n        iwork_size_query = fint()\n\n        self.WORK = __ptr__(work_size_query)\n        self.IWORK = __ptr__(iwork_size_query)\n        self.RWORK = Ptr[B]()\n        self.LWORK = fint(-1)\n\n        if self.call():\n            free(mem_buff)\n            raise LinAlgError(\"error in gelsd work size query\")\n\n        work_count = cast(work_size_query, fint)\n        work_size = int(work_count) * sizeof(T)\n        iwork_size = int(iwork_size_query) * sizeof(fint)\n\n        mem_buff2 = cobj(work_size + iwork_size)\n        work = mem_buff2\n        iwork = work + work_size\n\n        self.WORK = Ptr[T](work)\n        self.RWORK = Ptr[B]()\n        self.IWORK = Ptr[fint](iwork)\n        self.LWORK = work_count\n\n    def _init_complex(self, m: fint, n: fint, nrhs: fint):\n        min_m_n = m if m < n else n\n        max_m_n = m if m > n else n\n        safe_min_m_n = int(min_m_n)\n        safe_max_m_n = int(max_m_n)\n        safe_m = int(m)\n        safe_n = int(n)\n        safe_nrhs = int(nrhs)\n\n        a_size = safe_m * safe_n * sizeof(T)\n        b_size = safe_max_m_n * safe_nrhs * sizeof(T)\n        s_size = safe_min_m_n * sizeof(T)\n\n        lda = m if m else fint(1)\n        ldb = max_m_n if max_m_n else fint(1)\n\n        msize = a_size + b_size + s_size\n        mem_buff = cobj(msize if msize else 1)\n\n        a = mem_buff\n        b = a + a_size\n        s = b + b_size\n\n        self.M = m\n        self.N = n\n        self.NRHS = nrhs\n        self.A = Ptr[T](a)\n        self.B_ = Ptr[T](b)\n        self.S = Ptr[B](s)\n        self.LDA = lda\n        self.LDB = ldb\n\n        # work size query\n        work_size_query = T()\n        rwork_size_query = B()\n        iwork_size_query = fint()\n\n        self.WORK = __ptr__(work_size_query)\n        self.IWORK = __ptr__(iwork_size_query)\n        self.RWORK = __ptr__(rwork_size_query)\n        self.LWORK = fint(-1)\n\n        if self.call():\n            free(mem_buff)\n            raise LinAlgError(\"error in gelsd work size query\")\n\n        work_count = cast(work_size_query.real, fint)\n        work_size = int(work_count) * sizeof(T)\n        rwork_size = cast(rwork_size_query, int) * sizeof(B)\n        iwork_size = int(iwork_size_query) * sizeof(fint)\n\n        mem_buff2 = cobj(work_size + rwork_size + iwork_size)\n        work = mem_buff2\n        rwork = work + work_size\n        iwork = rwork + rwork_size\n\n        self.WORK = Ptr[T](work)\n        self.RWORK = Ptr[B](rwork)\n        self.IWORK = Ptr[fint](iwork)\n        self.LWORK = work_count\n\n    def __init__(self, m: fint, n: fint, nrhs: fint):\n        if T is complex or T is complex64:\n            self._init_complex(m, n, nrhs)\n        else:\n            self._init_real(m, n, nrhs)\n\n    def call(self):\n        M = self.M\n        N = self.N\n        NRHS = self.NRHS\n        A = self.A\n        LDA = self.LDA\n        B = self.B_\n        LDB = self.LDB\n        S = self.S\n        RCOND = self.RCOND\n        RANK = self.RANK\n        WORK = self.WORK\n        LWORK = self.LWORK\n        RWORK = self.RWORK\n        IWORK = self.IWORK\n        rv = fint()\n\n        args_real = (__ptr__(M),\n                     __ptr__(N),\n                     __ptr__(NRHS),\n                     A.as_byte(),\n                     __ptr__(LDA),\n                     B.as_byte(),\n                     __ptr__(LDB),\n                     S.as_byte(),\n                     RCOND.as_byte(),\n                     __ptr__(RANK),\n                     WORK.as_byte(),\n                     __ptr__(LWORK),\n                     IWORK,\n                     __ptr__(rv))\n\n        args_cplx = (__ptr__(M),\n                     __ptr__(N),\n                     __ptr__(NRHS),\n                     A.as_byte(),\n                     __ptr__(LDA),\n                     B.as_byte(),\n                     __ptr__(LDB),\n                     S.as_byte(),\n                     RCOND.as_byte(),\n                     __ptr__(RANK),\n                     WORK.as_byte(),\n                     __ptr__(LWORK),\n                     RWORK.as_byte(),\n                     IWORK,\n                     __ptr__(rv))\n\n        if T is float:\n            dgelsd_(*args_real)\n        elif T is float32:\n            sgelsd_(*args_real)\n        elif T is complex:\n            zgelsd_(*args_cplx)\n        elif T is complex64:\n            cgelsd_(*args_cplx)\n        else:\n            compile_error(\"[internal error] bad dtype for gelsd\")\n\n        self.RANK = RANK\n\n        return rv\n\n    def release(self):\n        free(self.A)\n        free(self.WORK)\n\ndef _abs2(p: Ptr[T], n: int, T: type):\n    B = type(_basetype(T))\n    res = B()\n    for i in range(n):\n        el = p[i]\n        if T is complex or T is complex64:\n            res += el.real*el.real + el.imag*el.imag\n        else:\n            res += el * el\n    return res\n\ndef lstsq(a, b, rcond = None):\n    dtype = type(\n              coerce(\n                type(asarray(a).data[0]),\n                type(asarray(b).data[0])))\n    a = _asarray(a, dtype=dtype)\n    b = _asarray(b, dtype=dtype)\n\n    if a.ndim != 2:\n        compile_error(\"lstsq argument 'a' must be 2-dimensional\")\n\n    if b.ndim == 1:\n        b2 = b.reshape(b.shape[0], 1)\n    elif b.ndim == 2:\n        b2 = b\n    else:\n        compile_error(\"lstsq argument 'b' must be 1- or 2-dimensional\")\n\n    B = type(_basetype(a.dtype))\n    m, n = a.shape\n    m2, nrhs = b2.shape\n\n    if m != m2:\n        raise LinAlgError('Incompatible dimensions')\n\n    if rcond is None:\n        rc = eps(B) * cast(max(m, n), B)\n    else:\n        rc = cast(rcond, B)\n\n    if nrhs == 0:\n        b2 = ndarray((m, 1), Ptr[b2.dtype](m))\n        str.memset(b2.data.as_byte(), byte(0), b2.nbytes)\n        safe_nrhs = 1\n    else:\n        safe_nrhs = nrhs\n\n    excess = m - n\n    params = GelsdParams[a.dtype, B](fint(m), fint(n), fint(safe_nrhs))\n\n    x = empty((n, safe_nrhs), dtype=a.dtype)\n    r = empty(safe_nrhs, dtype=B)\n    s = empty(min(m, n), dtype=B)\n\n    a_in = LinearizeData(n, m, a.strides[-1], a.strides[-2])\n    b_in = LinearizeData(safe_nrhs, m, b2.strides[-1], b2.strides[-2], max(m, n))\n    x_out = LinearizeData(safe_nrhs, n, x.strides[-1], x.strides[-2], max(m, n))\n    r_out = LinearizeData(1, safe_nrhs, 1, r.strides[-1])\n    s_out = LinearizeData(1, min(m, n), 1, s.strides[-1])\n\n    a_ptr = a.data\n    b_ptr = b2.data\n    x_ptr = x.data\n    r_ptr = r.data\n    s_ptr = s.data\n\n    a_in.linearize(params.A, a_ptr)\n    b_in.linearize(params.B_, b_ptr)\n    params.RCOND = __ptr__(rc)\n    not_ok = params.call()\n    error_occured = False\n\n    if not not_ok:\n        x_out.delinearize(x_ptr, params.B_)\n        rank = int(params.RANK)\n        s_out.delinearize(s_ptr, params.S)\n\n        if excess >= 0 and int(params.RANK) == n:\n            components = params.B_ + n\n            for i in range(safe_nrhs):\n                vector = components + i*m\n                r_ptr[i] = _abs2(vector, excess)\n        else:\n            r_out.nan_matrix(r_ptr)\n    else:\n        x_out.nan_matrix(x_ptr)\n        r_out.nan_matrix(r_ptr)\n        rank = -1\n        s_out.nan_matrix(s_ptr)\n        error_occured = True\n\n    params.release()\n\n    if error_occured:\n        raise LinAlgError(\"SVD did not converge in Linear Least Squares\")\n\n    if m == 0:\n        str.memset(x.data.as_byte(), byte(0), x.nbytes)\n\n    if nrhs == 0:\n        free(x.data)\n        free(r.data)\n        x = ndarray((x.shape[0], 0), Ptr[x.dtype]())\n        r = ndarray((0,), Ptr[r.dtype]())\n\n    if b.ndim == 1:\n        x1 = x.reshape(x.shape[0])\n    else:\n        x1 = x\n\n    if r.size and (rank != n or m <= n):\n        free(r.data)\n        r = ndarray((0,), Ptr[r.dtype]())\n\n    return x1, r, rank, s\n\n\n###################\n# Matrix Multiply #\n###################\n\ndef _dot_noblas(_ip1: Ptr[T1], is1: int, _ip2: Ptr[T2], is2: int, op: Ptr[T3], n: int,\n                T1: type, T2: type, T3: type):\n    ip1 = _ip1.as_byte()\n    ip2 = _ip2.as_byte()\n    ans = zero(T3)\n\n    for i in range(n):\n        e1 = Ptr[T1](ip1)[0]\n        e2 = Ptr[T2](ip2)[0]\n        ans += cast(e1, T3) * cast(e2, T3)\n        ip1 += is1\n        ip2 += is2\n\n    op[0] = ans\n\n_CBLAS_INT_MAX: Literal[int] = 0x7fffffff\n\ndef _blas_stride(stride: int, itemsize: int):\n    if stride > 0 and stride % itemsize == 0:\n        stride //= itemsize\n        if stride <= _CBLAS_INT_MAX:\n            return stride\n    return 0\n\ndef _dot(ip1: Ptr[T1], is1: int, ip2: Ptr[T2], is2: int, op: Ptr[T3], n: int,\n         T1: type, T2: type, T3: type):\n    ib1 = _blas_stride(is1, sizeof(T1))\n    ib2 = _blas_stride(is2, sizeof(T2))\n    args = (fint(n), ip1.as_byte(), fint(ib1), ip2.as_byte(), fint(ib2))\n\n    if ib1 == 0 or ib2 == 0:\n        _dot_noblas(ip1, is1, ip2, is2, op, n)\n    else:\n        if T1 is float and T2 is float and T3 is float:\n            op[0] = cblas_ddot(*args)\n        elif T1 is float32 and T2 is float32 and T3 is float32:\n            op[0] = cblas_sdot(*args)\n        elif T1 is complex and T2 is complex and T3 is complex:\n            cblas_zdotu_sub(*args, op)\n        elif T1 is complex64 and T2 is complex64 and T3 is complex64:\n            cblas_cdotu_sub(*args, op)\n        else:\n            _dot_noblas(ip1, is1, ip2, is2, op, n)\n\nBLAS_MAXSIZE: Literal[int] = 2147483646  # 2^31 - 1\n\ndef _is_blasable2d(byte_stride1: int,\n                   byte_stride2: int,\n                   d1: int,\n                   d2: int,\n                   itemsize: int):\n    unit_stride1 = byte_stride1 // itemsize\n    if byte_stride2 != itemsize:\n        return False\n    if (byte_stride1 % itemsize == 0 and\n        unit_stride1 >= d2 and\n        unit_stride1 <= BLAS_MAXSIZE):\n        return True\n    return False\n\ndef _gemv(ip1: Ptr[T], is1_m: int, is1_n: int,\n          ip2: Ptr[T], is2_n: int,\n          op: Ptr[T],  op_m: int,\n          m: int, n: int, T: type):\n    itemsize = sizeof(T)\n    if _is_blasable2d(is1_m, is1_n, m, n, itemsize):\n        order = fint(CBLAS_COL_MAJOR)\n        lda = is1_m // itemsize\n    else:\n        order = fint(CBLAS_ROW_MAJOR)\n        lda = is1_n // itemsize\n\n    one_dtype = cast(1, T)\n    zero_dtype = cast(0, T)\n\n    if T is float or T is float32:\n        one = one_dtype\n        zero = zero_dtype\n    else:\n        one = __ptr__(one_dtype).as_byte()\n        zero = __ptr__(zero_dtype).as_byte()\n\n    args = (order, fint(CBLAS_TRANS), fint(n), fint(m),\n            one, ip1.as_byte(), fint(lda), ip2.as_byte(),\n            fint(is2_n // itemsize), zero, op.as_byte(),\n            fint(op_m // itemsize))\n\n    if T is float:\n        cblas_dgemv(*args)\n    elif T is float32:\n        cblas_sgemv(*args)\n    elif T is complex:\n        cblas_zgemv(*args)\n    elif T is complex64:\n        cblas_cgemv(*args)\n    else:\n        compile_error(\"[internal error] bad input type\")\n\ndef _matmul_matrixmatrix(ip1: Ptr[T], is1_m: int, is1_n: int,\n                         ip2: Ptr[T], is2_n: int, is2_p: int,\n                         op: Ptr[T],  os_m: int, os_p: int,\n                         m: int, n: int, p: int, T: type):\n    order = fint(CBLAS_ROW_MAJOR)\n    itemsize = sizeof(T)\n    ldc = os_m // itemsize\n\n    if _is_blasable2d(is1_m, is1_n, m, n, itemsize):\n        trans1 = fint(CBLAS_NO_TRANS)\n        lda = is1_m // itemsize\n    else:\n        trans1 = fint(CBLAS_TRANS)\n        lda = is1_n // itemsize\n\n    if _is_blasable2d(is2_n, is2_p, n, p, itemsize):\n        trans2 = fint(CBLAS_NO_TRANS)\n        ldb = is2_n // itemsize\n    else:\n        trans2 = fint(CBLAS_TRANS)\n        ldb = is2_p // itemsize\n\n    one_dtype = cast(1, T)\n    zero_dtype = cast(0, T)\n\n    if T is float or T is float32:\n        one = one_dtype\n        zero = zero_dtype\n    else:\n        one = __ptr__(one_dtype).as_byte()\n        zero = __ptr__(zero_dtype).as_byte()\n\n    if (ip1 == ip2 and\n        m == p and\n        is1_m == is2_p and\n        is1_n == is2_n and\n        trans1 != trans2):\n\n        ld = lda if trans1 == fint(CBLAS_NO_TRANS) else ldb\n        args = (order, fint(CBLAS_UPPER), trans1, fint(p),\n                fint(n), one, ip1, fint(ld),\n                zero, op, fint(ldc))\n\n        if T is float:\n            cblas_dsyrk(*args)\n        elif T is float32:\n            cblas_ssyrk(*args)\n        elif T is complex:\n            cblas_zsyrk(*args)\n        elif T is complex64:\n            cblas_csyrk(*args)\n        else:\n            compile_error(\"[internal error] bad input type\")\n\n        # Copy the triangle\n        for i in range(p):\n            for j in range(i + 1, p):\n                op[j * ldc + i] = op[i * ldc + j]\n    else:\n        args = (order, trans1, trans2, fint(m), fint(p), fint(n),\n                one, ip1, fint(lda), ip2,\n                fint(ldb), zero, op, fint(ldc))\n\n        if T is float:\n            cblas_dgemm(*args)\n        elif T is float32:\n            cblas_sgemm(*args)\n        elif T is complex:\n            cblas_zgemm(*args)\n        elif T is complex64:\n            cblas_cgemm(*args)\n        else:\n            compile_error(\"[internal error] bad input type\")\n\ndef _matmul_inner_noblas(_ip1: Ptr[T1], is1_m: int, is1_n: int,\n                         _ip2: Ptr[T2], is2_n: int, is2_p: int,\n                         _op: Ptr[T3],  os_m: int, os_p: int,\n                         dm: int, dn: int, dp: int, T1: type,\n                         T2: type, T3: type):\n    ib1_n = is1_n * dn\n    ib2_n = is2_n * dn\n    ib2_p = is2_p * dp\n    ob_p = os_p * dp\n    ip1 = _ip1.as_byte()\n    ip2 = _ip2.as_byte()\n    op = _op.as_byte()\n\n    if T3 is bool:\n        for m in range(dm):\n            for p in range(dp):\n                ip1tmp = ip1\n                ip2tmp = ip2\n                Ptr[T3](op)[0] = False\n                for n in range(dn):\n                    val1 = Ptr[T1](ip1tmp)[0]\n                    val2 = Ptr[T2](ip2tmp)[0]\n                    if val1 and val2:\n                        Ptr[T3](op)[0] = True\n                        break\n                    ip2tmp += is2_n\n                    ip1tmp += is1_n\n                op += os_p\n                ip2 += is2_p\n            op -= ob_p\n            ip2 -= ib2_p\n            ip1 += is1_m\n            op += os_m\n    else:\n        for m in range(dm):\n            for p in range(dp):\n                for n in range(dn):\n                    val1 = cast(Ptr[T1](ip1)[0], T3)\n                    val2 = cast(Ptr[T2](ip2)[0], T3)\n                    Ptr[T3](op)[0] += val1 * val2\n                    ip2 += is2_n\n                    ip1 += is1_n\n                ip1 -= ib1_n\n                ip2 -= ib2_n\n                op += os_p\n                ip2 += is2_p\n            op -= ob_p\n            ip2 -= ib2_p\n            ip1 += is1_m\n            op += os_m\n\ndef _matmul(A, B, C):\n    # Caller ensures arrays are broadcasted and of correct shape.\n    # A -> (m, n)\n    # B -> (n, p)\n    # C -> (m, p)\n    dm = A.shape[-2]\n    dn = A.shape[-1]\n    dp = B.shape[-1]\n\n    is1_m = A.strides[-2]\n    is1_n = A.strides[-1]\n    is2_n = B.strides[-2]\n    is2_p = B.strides[-1]\n    os_m = C.strides[-2]\n    os_p = C.strides[-1]\n\n    sz = sizeof(A.dtype)\n    special_case = (dm == 1 or dn == 1 or dp == 1)\n    any_zero_dim = (dm == 0 or dn == 0 or dp == 0)\n    scalar_out = (dm == 1 and dp == 1)\n    scalar_vec = (dn == 1 and (dp == 1 or dm == 1))\n    too_big_for_blas = (dm > BLAS_MAXSIZE or dn > BLAS_MAXSIZE or dp > BLAS_MAXSIZE)\n    i1_c_blasable = _is_blasable2d(is1_m, is1_n, dm, dn, sz)\n    i2_c_blasable = _is_blasable2d(is2_n, is2_p, dn, dp, sz)\n    i1_f_blasable = _is_blasable2d(is1_n, is1_m, dn, dm, sz)\n    i2_f_blasable = _is_blasable2d(is2_p, is2_n, dp, dn, sz)\n    i1blasable = i1_c_blasable or i1_f_blasable\n    i2blasable = i2_c_blasable or i2_f_blasable\n    o_c_blasable = _is_blasable2d(os_m, os_p, dm, dp, sz)\n    o_f_blasable = _is_blasable2d(os_p, os_m, dp, dm, sz)\n    vector_matrix = (dm == 1 and i2blasable and\n                     _is_blasable2d(is1_n, sz, dn, 1, sz))\n    matrix_vector = (dp == 1 and i1blasable and\n                     _is_blasable2d(is2_n, sz, dn, 1, sz))\n\n    for idx in multirange(C.shape[:-2]):\n        ip1 = A._ptr(idx + (0, 0), broadcast=(A.ndim > 2))\n        ip2 = B._ptr(idx + (0, 0), broadcast=(B.ndim > 2))\n        op = C._ptr(idx + (0, 0))\n\n        if not ((A.dtype is B.dtype and B.dtype is C.dtype) and\n                (A.dtype is float or A.dtype is float32 or\n                 A.dtype is complex or A.dtype is complex64)):\n            _matmul_inner_noblas(ip1, is1_m, is1_n,\n                                 ip2, is2_n, is2_p,\n                                 op, os_m, os_p,\n                                 dm, dn, dp)\n        else:\n            if too_big_for_blas or any_zero_dim:\n                _matmul_inner_noblas(ip1, is1_m, is1_n,\n                                     ip2, is2_n, is2_p,\n                                     op, os_m, os_p,\n                                     dm, dn, dp)\n            elif special_case:\n                if scalar_out:\n                    _dot(ip1, is1_n, ip2, is2_n, op, dn)\n                elif scalar_vec:\n                    _matmul_inner_noblas(ip1, is1_m, is1_n,\n                                         ip2, is2_n, is2_p,\n                                         op, os_m, os_p,\n                                         dm, dn, dp)\n                elif vector_matrix:\n                    _gemv(ip2, is2_p, is2_n, ip1, is1_n, op, os_p, dp, dn)\n                elif matrix_vector:\n                    _gemv(ip1, is1_m, is1_n, ip2, is2_n, op, os_m, dm, dn)\n                else:\n                    _matmul_inner_noblas(ip1, is1_m, is1_n,\n                                         ip2, is2_n, is2_p,\n                                         op, os_m, os_p,\n                                         dm, dn, dp)\n            else:\n                if i1blasable and i2blasable and o_c_blasable:\n                    _matmul_matrixmatrix(ip1, is1_m, is1_n,\n                                         ip2, is2_n, is2_p,\n                                         op, os_m, os_p,\n                                         dm, dn, dp)\n                elif i1blasable and i2blasable and o_f_blasable:\n                    _matmul_matrixmatrix(ip2, is2_p, is2_n,\n                                         ip1, is1_n, is1_m,\n                                         op, os_p, os_m,\n                                         dp, dn, dm)\n                else:\n                    _matmul_inner_noblas(ip1, is1_m, is1_n,\n                                         ip2, is2_n, is2_p,\n                                         op, os_m, os_p,\n                                         dm, dn, dp)\n\ndef _check_out(out, ans_shape, dtype: type):\n    if out.dtype is not dtype:\n        compile_error(\"'out' array has incorrect type '\" + out.dtype.__name__ + \"'\")\n\n    if static.len(out.shape) != static.len(ans_shape):\n        compile_error(\"'out' array has incorrect number of dimensions\")\n\n    if out.shape != ans_shape:\n        raise ValueError(\"'out' array has incorrect shape\")\n\ndef _matmul_ufunc(x1, x2, out = None, dtype: type = NoneType):\n    if not isinstance(x1, ndarray) or not isinstance(x2, ndarray):\n        y1 = asarray(x1)\n        y2 = asarray(x2)\n        return _matmul_ufunc(y1, y2, out=out, dtype=dtype)\n\n    T1 = x1.dtype\n    T2 = x2.dtype\n    x1d: Literal[int] = x1.ndim\n    x2d: Literal[int] = x2.ndim\n\n    if dtype is NoneType:\n        return _matmul_ufunc(x1, x2, out=out, dtype=type(coerce(T1, T2)))\n\n    if x1d == 0:\n        compile_error(\"first argument is 0-dimensional; must be at least 1-d\")\n\n    if x2d == 0:\n        compile_error(\"second argument is 0-dimensional; must be at least 1-d\")\n\n    if x1d == 1 and x2d == 1:\n        if x1.shape != x2.shape:\n            raise ValueError(\"matmul: vectors have different lengths\")\n        op = zero(dtype)\n        _dot(x1.data, x1.strides[0], x2.data, x2.strides[0], __ptr__(op), x1.size)\n        return op\n\n    if x1d == 1:\n        y1 = x1.reshape((1, -1))\n    else:\n        y1 = x1\n\n    if x2d == 1:\n        y2 = x2.reshape((-1, 1))\n    else:\n        y2 = x2\n\n    y1s = y1.shape\n    y2s = y2.shape\n    y1d: Literal[int] = y1.ndim\n    y2d: Literal[int] = y2.ndim\n\n    base1s = y1s[:-2]\n    base2s = y2s[:-2]\n    mat1s = y1s[-2:]\n    mat2s = y2s[-2:]\n\n    m = mat1s[0]\n    k = mat1s[1]\n    n = mat2s[1]\n\n    if k != mat2s[0]:\n        raise ValueError(\"matmul: last dimension of first argument does not \"\n                         \"match second-to-last dimension of second argument\")\n\n    ans_base = broadcast_shapes(base1s, base2s)\n    if x1d == 1 and x2d == 1:\n        ans_shape = ans_base\n    elif x1d == 1:\n        ans_shape = ans_base + (mat2s[1],)\n    elif x2d == 1:\n        ans_shape = ans_base + (mat1s[0],)\n    else:\n        ans_shape = ans_base + (mat1s[0], mat2s[1])\n\n    if out is None:\n        ans = zeros(ans_shape, dtype=dtype)\n    elif isinstance(out, ndarray):\n        _check_out(out, ans_shape, dtype)\n        ans = out\n        ans.map(lambda _: cast(0, ans.dtype), inplace=True)\n    else:\n        compile_error(\"'out' must be an ndarray\")\n\n    if x1d == 1 and x2d == 1:\n        _matmul(y1, y2, ans.reshape(1, 1))\n    elif x1d == 1:\n        _matmul(y1.reshape((1,) * (x2d - 1) + y1.shape), y2, ans.reshape(ans.shape + (1,)))\n    elif x2d == 1:\n        _matmul(y1, y2.reshape((1,) * (x1d - 1) + y2.shape), ans.reshape(ans.shape + (1,)))\n    elif x1d > x2d:\n        _matmul(y1, y2.reshape((1,) * (x1d - x2d) + y2.shape), ans)\n    elif x1d < x2d:\n        _matmul(y1.reshape((1,) * (x2d - x1d) + y1.shape), y2, ans)\n    else:\n        _matmul(y1, y2, ans)\n\n    if out is not None:\n        return out\n    else:\n        if ans.ndim == 0:\n            return ans.data[0]\n        else:\n            return ans\n\ndef matmul(x1, x2):\n    return _matmul_ufunc(x1, x2)\n\ndef dot(a, b, out = None):\n    x1 = asarray(a)\n    x2 = asarray(b)\n    T1 = x1.dtype\n    T2 = x2.dtype\n    x1d: Literal[int] = static.len(x1.shape)\n    x2d: Literal[int] = static.len(x2.shape)\n\n    if x1d == 0 or x2d == 0:\n        return multiply(a, b, out=out)\n\n    if x1d <= 2 and x2d <= 2:\n        return _matmul_ufunc(a, b, out=out)\n\n    # most general case\n    x1s = x1.shape\n    x2s = x2.shape\n    m = x1s[-2]\n    k = x1s[-1]\n    n = x2s[-1]\n\n    if k != x2s[-2]:\n        raise ValueError(\"dot: last dimension of first argument does not \"\n                         \"match second-to-last dimension of second argument\")\n\n    dtype = type(coerce(T1, T2))\n    ans_shape = x1s[:-1] + x2s[:-2] + (x2s[-1],)\n    if out is None:\n        ans = zeros(ans_shape, dtype=dtype)\n    elif isinstance(out, ndarray):\n        _check_out(out, ans_shape, dtype)\n        ans = out\n    else:\n        compile_error(\"'out' must be an ndarray\")\n\n    for idx in multirange(ans_shape):\n        q = ans._ptr(idx)\n        for i1 in range(k):\n            x1_idx = idx[:x1d-1] + (i1,)\n            x2_idx = idx[x1d-1:-1] + (i1, idx[-1])\n            p1 = x1._ptr(x1_idx)\n            p2 = x2._ptr(x2_idx)\n            q[0] += cast(p1[0], dtype) * cast(p2[0], dtype)\n\n    return ans\n\n\n##################\n# Other Routines #\n##################\n\ndef _matrix_power(a, n: int):\n    a = asarray(a)\n    dtype = a.dtype\n    s = a.shape\n    m = _square_rows(a)\n\n    if n == 0:\n        if static.len(s) == 2:\n            return eye(m, dtype=dtype)\n        else:\n            b = empty_like(a)\n            for idx in multirange(s[:-2]):\n                p = b._ptr(idx + (0, 0))\n                for i in range(m):\n                    for j in range(m):\n                        val = cast(1, dtype) if i == j else zero(dtype)\n                        (p + (i * m + j))[0] = val\n            return b\n    elif n < 0:\n        if type(inv(a)) is type(a):\n            a = inv(a)\n            n = abs(n)\n        else:\n            raise ValueError(\n                \"cannot take integral matrix to non-constant negative \"\n                \"power; use 'matrix_power(inv(a), abs(n))' instead\"\n            )\n\n    if n == 1:\n        return a\n    elif n == 2:\n        return matmul(a, a)\n    elif n == 3:\n        return matmul(matmul(a, a), a)\n\n    result = a\n    z = a\n    first = True\n    have_result = False\n\n    while n > 0:\n        if first:\n            first = False\n        else:\n            z = matmul(z, z)\n            first = False\n\n        n, bit = divmod(n, 2)\n        if bit:\n            if have_result:\n                result = matmul(result, z)\n            else:\n                result = z\n                have_result = True\n\n    return result\n\ndef matrix_power(a, n: int):\n    return _matrix_power(a, n)\n\n@overload\ndef matrix_power(a, n: Literal[int]):\n    nonstatic = lambda x: x  # hack to get non-static argument\n    if n >= 0:\n        return _matrix_power(a, n)\n    else:\n        return _matrix_power(inv(a), -n)\n\ndef _multi_dot_three(A, B, C, out=None):\n    a0, a1b0 = A.shape\n    b1c0, c1 = C.shape\n    # cost1 = cost((AB)C) = a0*a1b0*b1c0 + a0*b1c0*c1\n    cost1 = a0 * b1c0 * (a1b0 + c1)\n    # cost2 = cost(A(BC)) = a1b0*b1c0*c1 + a0*a1b0*c1\n    cost2 = a1b0 * c1 * (a0 + b1c0)\n\n    if cost1 < cost2:\n        return dot(dot(A, B), C, out=out)\n    else:\n        return dot(A, dot(B, C), out=out)\n\ndef _multi_dot(arrays, order, i, j, out=None):\n    if i == j:\n        #assert out is None\n        return arrays[i]\n    else:\n        return dot(_multi_dot(arrays, order, i, order[i, j]),\n                   _multi_dot(arrays, order, order[i, j] + 1, j),\n                   out=out)\n\ndef _multi_dot_matrix_chain_order(arrays, return_costs: Literal[bool] = False):\n    n = len(arrays)\n    p = [a.shape[0] for a in arrays] + [arrays[-1].shape[1]]\n    m = zeros((n, n), dtype=float)\n    s = empty((n, n), dtype=int)\n\n    for l in range(1, n):\n        for i in range(n - l):\n            j = i + l\n            m[i, j] = inf(float)\n            for k in range(i, j):\n                q = m[i, k] + m[k+1, j] + p[i]*p[k+1]*p[j+1]\n                if q < m[i, j]:\n                    m[i, j] = q\n                    s[i, j] = k  # Note that Cormen uses 1-based index\n\n    if return_costs:\n        return (s, m)\n    else:\n        return s\n\ndef multi_dot(arrays, out = None):\n    n = len(arrays)\n\n    if isinstance(arrays, Tuple):\n        if static.len(arrays) < 2:\n            compile_error(\"Expecting at least two arrays.\")\n\n        if static.len(arrays) == 2:\n            return dot(arrays[0], arrays[1], out=out)\n    else:\n        if n < 2:\n            raise ValueError(\"Expecting at least two arrays.\")\n\n        if n == 2:\n            return dot(arrays[0], arrays[1], out=out)\n\n    ndim_first: Literal[int] = static.len(arrays[0].shape)\n    ndim_last:  Literal[int] = static.len(arrays[-1].shape)\n\n    def fix_first(arr):\n        arr = asarray(arr)\n        if static.len(arr.shape) == 1:\n            return atleast_2d(arr)\n        else:\n            return arr\n\n    def fix_last(arr):\n        arr = asarray(arr)\n        if static.len(arr.shape) == 1:\n            return atleast_2d(arr).T\n        else:\n            return arr\n\n    if isinstance(arrays, Tuple):\n        xarrays = (fix_first(arrays[0]),) + tuple(asarray(arr) for arr in arrays[1:-1]) + (fix_last(arrays[-1]),)\n    else:\n        xarrays = [asarray(arr) for arr in arrays]\n        xarrays[0] = fix_first(xarrays[0])\n        xarrays[-1] = fix_last(xarrays[-1])\n\n    for arr in xarrays:\n        if static.len(arr.shape) != 2:\n            compile_error(\"1-dimensional array given. Array must be two-dimensional\")\n\n    if n == 3:\n        result = _multi_dot_three(xarrays[0], xarrays[1], xarrays[2], out=out)\n    else:\n        order = _multi_dot_matrix_chain_order(xarrays)\n        result = _multi_dot(xarrays, order, 0, n - 1, out=out)\n\n    if ndim_first == 1 and ndim_last == 1:\n        return result[0, 0]  # scalar\n    elif ndim_first == 1 or ndim_last == 1:\n        return result.ravel()  # 1-D\n    else:\n        return result\n\ndef _vdot_ptr(x: Ptr[T1], y: Ptr[T2], n: int, incx: int, incy: int, T1: type, T2: type):\n    if T1 is float and T2 is float:\n        return cblas_ddot(fint(n), x.as_byte(), fint(incx), y.as_byte(), fint(incy))\n    elif T1 is float32 and T2 is float32:\n        return cblas_sdot(fint(n), x.as_byte(), fint(incx), y.as_byte(), fint(incy))\n    elif T1 is complex and T2 is complex:\n        z = complex()\n        cblas_zdotc_sub(fint(n), x.as_byte(), fint(incx), y.as_byte(), fint(incy), __ptr__(z).as_byte())\n        return z\n    elif T1 is complex64 and T2 is complex64:\n        z = complex64()\n        cblas_cdotc_sub(fint(n), x.as_byte(), fint(incx), y.as_byte(), fint(incy), __ptr__(z).as_byte())\n        return z\n    else:\n        TR = type(coerce(T1, T2))\n        z = TR()\n        for _ in range(n):\n            a = x[0]\n            b = y[0]\n\n            if hasattr(a, \"conjugate\"):\n                a = a.conjugate()\n\n            z += cast(a, TR) * cast(b, TR)\n            x += incx\n            y += incy\n\n        return z\n\ndef vdot(a, b):\n    def mismatch():\n        raise ValueError(\"vdot: inputs have different sizes\")\n\n    a = asarray(a)\n    b = asarray(b)\n\n    if static.len(a.shape) == 0 and static.len(b.shape) == 0:\n        x = a.data[0]\n        y = b.data[0]\n        TR = coerce(type(x), type(y))\n\n        if hasattr(x, \"conjugate\"):\n            x = x.conjugate()\n\n        return cast(x, TR) * cast(y, TR)\n\n    n = a.size\n    if n != b.size:\n        mismatch()\n\n    if static.len(a.shape) == 1 and static.len(b.shape) == 1:\n        inca = a.strides[0] // a.itemsize\n        incb = b.strides[0] // b.itemsize\n        return _vdot_ptr(a.data, b.data, n, inca, incb)\n\n    if a._contig_match(b):\n        return _vdot_ptr(a.data, b.data, n, 1, 1)\n\n    T1 = a.dtype\n    T2 = b.dtype\n    TR = type(coerce(T1, T2))\n    z = TR()\n\n    for e1, e2 in zip(a.flat, b.flat):\n        if hasattr(e1, \"conjugate\"):\n            e1 = e1.conjugate()\n        z += cast(e1, TR) * cast(e2, TR)\n\n    return z\n\ndef outer(a, b, out = None):\n    a = asarray(a)\n    b = asarray(b)\n    return multiply(a.ravel()[:, None], b.ravel()[None, :], out)\n\ndef inner(a, b):\n    a = asarray(a)\n    b = asarray(b)\n    shape_a = a.shape\n    shape_b = b.shape\n\n    if static.len(shape_a) == 0 or static.len(shape_b) == 0:\n        return multiply(a, b)\n\n    if static.len(shape_a) == 1 and static.len(shape_b) == 1:\n        return dot(a, b)\n\n    n = shape_a[-1]\n    if n != shape_b[-1]:\n        raise ValueError(\"inner: mismatch in last dimension of inputs\")\n\n    shape_cut_a = shape_a[:-1]\n    shape_cut_b = shape_b[:-1]\n    out_shape = shape_cut_a + shape_cut_b\n    dtype = type(coerce(a.dtype, b.dtype))\n    out = empty(out_shape, dtype=dtype)\n\n    for idx1 in multirange(shape_cut_a):\n        for idx2 in multirange(shape_cut_b):\n            q = out._ptr(idx1 + idx2)\n            q[0] = zero(dtype)\n            for r in range(n):\n                xa = a._ptr(idx1 + (r,))[0]\n                xb = b._ptr(idx2 + (r,))[0]\n                xc = cast(xa, dtype) * cast(xb, dtype)\n                q[0] += xc\n\n    return out\n\ndef _tensordot(a, b, axes, ndim: Literal[int] = -1):\n    def get_axes(axes):\n        if isinstance(axes, int):\n            axes_a = list(range(-axes, 0))\n            axes_b = list(range(0, axes))\n            na = len(axes_a)\n            nb = len(axes_b)\n            return axes_a, axes_b\n\n        axes_a, axes_b = axes\n\n        if isinstance(axes_a, int):\n            xa = [axes_a]\n        else:\n            xa = list(axes_a)\n\n        if isinstance(axes_b, int):\n            xb = [axes_b]\n        else:\n            xb = list(axes_b)\n\n        return xa, xb\n\n    axes_a, axes_b = get_axes(axes)\n    na = len(axes_a)\n    nb = len(axes_b)\n\n    as_ = List[int](a.shape)\n    nda = a.ndim\n    bs = List[int](b.shape)\n    ndb = b.ndim\n    equal = True\n    if na != nb:\n        equal = False\n    else:\n        for k in range(na):\n            if as_[axes_a[k]] != bs[axes_b[k]]:\n                equal = False\n                break\n            if axes_a[k] < 0:\n                axes_a[k] += nda\n            if axes_b[k] < 0:\n                axes_b[k] += ndb\n    if not equal:\n        raise ValueError(\"shape-mismatch for sum\")\n\n    notin = [k for k in range(nda) if k not in axes_a]\n    newaxes_a = notin + axes_a\n\n    N2 = 1\n    for axis in axes_a:\n        N2 *= as_[axis]\n\n    M2 = 1\n    for ax in notin:\n        M2 *= as_[ax]\n\n    newshape_a = (M2, N2)\n    if ndim >= 0:\n        olda = [as_[axis] for axis in notin]\n    else:\n        olda = None\n\n    notin = [k for k in range(ndb) if k not in axes_b]\n    newaxes_b = axes_b + notin\n\n    N2 = 1\n    for axis in axes_b:\n        N2 *= bs[axis]\n\n    M2 = 1\n    for ax in notin:\n        M2 *= bs[ax]\n\n    newshape_b = (N2, M2)\n    if ndim >= 0:\n        oldb = [bs[axis] for axis in notin]\n    else:\n        oldb = None\n\n    at = a.transpose(newaxes_a).reshape(newshape_a)\n    bt = b.transpose(newaxes_b).reshape(newshape_b)\n    res = dot(at, bt)\n    # NOTE: 'olda + oldb' length is not known at compile-time,\n    #       so cannot reshape unless 'ndim' is given.\n    if ndim >= 0:\n        newshape = (0,) * ndim\n        pnewshape = Ptr[int](__ptr__(newshape).as_byte())\n        i = 0\n        for j in olda:\n            pnewshape[i] = j\n            i += 1\n        for j in oldb:\n            pnewshape[i] = j\n            i += 1\n        return res.reshape(newshape)\n    else:\n        return res\n\ndef tensordot(a, b, axes):\n    a = asarray(a)\n    b = asarray(b)\n    return _tensordot(a, b, axes)\n\n@overload\ndef tensordot(a, b, axes: Literal[int] = 2):\n    if axes < 0:\n        return tensordot(a, b, axes=0)\n\n    a = asarray(a)\n    b = asarray(b)\n\n    if axes > a.ndim + 1 or axes > b.ndim + 1:\n        compile_error(\"'axes' too large for given arrays\")\n\n    return _tensordot(a, b, axes=axes, ndim=(a.ndim + b.ndim - 2*axes))\n\ndef kron(a, b):\n    b = asarray(b)\n    ndb: Literal[int] = static.len(b.shape)\n    a = array(a, copy=False, ndmin=ndb)\n    nda: Literal[int] = static.len(a.shape)\n    nd: Literal[int] = ndb if ndb >= nda else nda\n\n    if nda == 0 or ndb == 0:\n        return multiply(a, b)\n\n    as_ = a.shape\n    bs = b.shape\n    if not a.flags.contiguous:\n        a = reshape(a, as_)\n    if not b.flags.contiguous:\n        b = reshape(b, bs)\n\n    as_ = (1,)*(ndb-nda if ndb-nda >= 0 else 0) + as_\n    bs = (1,)*(nda-ndb if nda-ndb >= 0 else 0) + bs\n\n    a_arr = expand_dims(a, axis=tuple(i for i in static.range(ndb-nda)))\n    b_arr = expand_dims(b, axis=tuple(i for i in static.range(nda-ndb)))\n\n    a_arr = expand_dims(a_arr, axis=tuple(i for i in static.range(1, nd*2, 2)))\n    b_arr = expand_dims(b_arr, axis=tuple(i for i in static.range(0, nd*2, 2)))\n    result = multiply(a_arr, b_arr)\n\n    res_shape = tuple(as_[i] * bs[i] for i in static.range(static.len(as_)))\n    result = result.reshape(res_shape)\n\n    return result\n\ndef _trace_ufunc(a, offset: int = 0, axis1: int = 0, axis2: int = 1,\n                 dtype: type = NoneType, out = None):\n    a = asarray(a)\n\n    if dtype is NoneType:\n        return _trace_ufunc(a, offset=offset, axis1=axis1, axis2=axis2,\n                            dtype=a.dtype, out=out)\n\n    m, n = _rows_cols(a)\n    s = a.shape\n    ndim = a.ndim\n\n    axis1 = normalize_axis_index(axis1, a.ndim, \"axis1\")\n    axis2 = normalize_axis_index(axis2, a.ndim, \"axis2\")\n    if axis1 == axis2:\n        raise ValueError(\"axis1 and axis2 cannot be the same\")\n\n    if static.len(s) == 2:\n        i = 0\n        t = zero(dtype)\n        while ((offset >= 0 and (i < m and i + offset < n)) or\n               (offset < 0 and (i - offset < m and i < n))):\n            e = a._ptr((i, i + offset))[0] if offset >= 0 else a._ptr((i - offset, i))[0]\n            t += cast(e, dtype)\n            i += 1\n\n        if out is None:\n            return t\n        else:\n            if static.len(out.shape) != 0:\n                compile_error(\"expected 0-dimensional output parameter\")\n\n            if out.dtype is not dtype:\n                compile_error(\"output parameter has the wrong dtype\")\n\n            out.data[0] = t\n            return out\n    else:\n        if axis1 > axis2:\n            axis1, axis2 = axis2, axis2\n\n        ans_shape = tuple_delete(tuple_delete(s, axis2), axis1)\n        m, n = s[axis1], s[axis2]\n\n        if out is None:\n            ans = empty(ans_shape, dtype=dtype)\n        else:\n            if static.len(out.shape) != static.len(ans_shape):\n                compile_error(\"output parameter has the wrong number of dimensions\")\n\n            if out.dtype is not dtype:\n                compile_error(\"output parameter has the wrong dtype\")\n\n            if out.shape != ans_shape:\n                raise ValueError(\"output parameter has the wrong shape\")\n\n            ans = out\n\n        for idx0 in multirange(ans_shape):\n            i = 0\n            t = zero(dtype)\n\n            while ((offset >= 0 and (i < m and i + offset < n)) or\n                   (offset < 0 and (i - offset < m and i < n))):\n                idx = (tuple_insert(\n                         tuple_insert(idx0, axis1, i),\n                                           axis2, i + offset) if offset >= 0 else\n                       tuple_insert(\n                         tuple_insert(idx0, axis1, i - offset),\n                                           axis2, i))\n                e = a._ptr(idx)[0]\n                t += cast(e, dtype)\n                i += 1\n\n            q = ans._ptr(idx0)\n            q[0] = t\n\n        return ans\n\ndef trace(x, offset: int = 0, dtype: type = NoneType):\n    return _trace_ufunc(x, offset=offset, dtype=dtype)\n\ndef tensorsolve(a, b, axes = None):\n    a = asarray(a)\n    b = asarray(b)\n    an: Literal[int] = static.len(a.shape)\n    bn: Literal[int] = static.len(b.shape)\n\n    if axes is not None:\n        allaxes = list(range(0, an))\n        for k in axes:\n            allaxes.remove(k)\n            allaxes.insert(an, k)\n        a = a.transpose(allaxes)\n\n    oldshape = a.shape[-(an-bn):]\n    prod = 1\n    for k in oldshape:\n        prod *= k\n\n    if a.size != prod ** 2:\n        raise LinAlgError(\n            \"Input arrays must satisfy the requirement\"\n            \"prod(a.shape[b.ndim:]) == prod(a.shape[:b.ndim])\"\n        )\n\n    a = a.reshape(prod, prod)\n    b = b.ravel()\n    res = solve(a, b)\n    return res.reshape(oldshape)\n\ndef tensorinv(a, ind: int = 2):\n    a = asarray(a)\n    oldshape = a.shape\n    prod = 1\n    invshape = oldshape\n\n    if ind > 0:\n        # invshape = oldshape[ind:] + oldshape[:ind]\n        poldshape = Ptr[int](__ptr__(oldshape).as_byte())\n        pinvshape = Ptr[int](__ptr__(invshape).as_byte())\n\n        i = ind\n        j = 0\n        while i < len(oldshape):\n            pinvshape[j] = poldshape[i]\n            prod *= poldshape[i]\n            i += 1\n            j += 1\n\n        i = 0\n        while i < min(ind, len(oldshape)):\n            pinvshape[j] = poldshape[i]\n            i += 1\n            j += 1\n    else:\n        raise ValueError(\"Invalid ind argument.\")\n\n    a = a.reshape(prod, -1)\n    ia = inv(a)\n    return ia.reshape(*invshape)\n\ndef _square(elem: T, T: type):\n    if T is complex or T is complex64:\n        return (elem.real * elem.real) + (elem.imag * elem.imag)\n    else:\n        return elem * elem\n\ndef _norm_cast(elem: T, T: type):\n    if T is float or T is float32 or T is complex or T is complex64:\n        return elem\n    else:\n        return cast(elem, float)\n\ndef _vector_norm_f(x: ndarray, axis: int, R: type):\n    if static.len(x.shape) == 1:\n        dtype = x.dtype\n        sh = x.shape[0]\n        st = x.strides[0]\n        data = x.data\n        curr = R()\n\n        for i in range(sh):\n            elem = _norm_cast((Ptr[dtype](data.as_byte() + (st * i)))[0])\n            curr += _square(elem)\n\n        return util_sqrt(curr)\n    else:\n        s = x.shape\n        n = s[axis]\n        ans_shape = tuple_delete(x.shape, axis)\n        ans = empty(ans_shape, R)\n\n        for idx in multirange(ans_shape):\n            q = ans._ptr(idx)\n            curr = R()\n\n            for i in range(n):\n                idx1 = tuple_insert(idx, axis, i)\n                elem = _norm_cast(x._ptr(idx1)[0])\n                curr += _square(elem)\n\n            q[0] = util_sqrt(curr)\n\n        return ans\n\ndef _norm_error_zero_max():\n    raise ValueError(\"zero-size array to reduction operation \"\n                     \"maximum which has no identity\")\n\ndef _norm_error_zero_min():\n    raise ValueError(\"zero-size array to reduction operation \"\n                     \"minimum which has no identity\")\n\ndef _vector_norm_inf(x: ndarray, axis: int, R: type):\n    sh = x.shape[0]\n    if sh == 0:\n        _norm_error_zero_max()\n\n    if static.len(x.shape) == 1:\n        dtype = x.dtype\n        sh = x.shape[0]\n        st = x.strides[0]\n        data = x.data\n        curr = abs(_norm_cast(data[0]))\n\n        for i in range(1, sh):\n            elem = _norm_cast((Ptr[dtype](data.as_byte() + (st * i)))[0])\n            e = abs(elem)\n            if e > curr:\n                curr = e\n\n        return curr\n    else:\n        s = x.shape\n        n = s[axis]\n        ans_shape = tuple_delete(x.shape, axis)\n        ans = empty(ans_shape, R)\n\n        for idx in multirange(ans_shape):\n            q = ans._ptr(idx)\n            idx1 = tuple_insert(idx, axis, 0)\n            curr = abs(_norm_cast(x._ptr(idx1)[0]))\n\n            for i in range(1, n):\n                idx1 = tuple_insert(idx, axis, i)\n                elem = _norm_cast(x._ptr(idx1)[0])\n                e = abs(elem)\n                if e > curr:\n                    curr = e\n\n            q[0] = curr\n\n        return ans\n\ndef _vector_norm_ninf(x: ndarray, axis: int, R: type):\n    sh = x.shape[0]\n    if sh == 0:\n        _norm_error_zero_min()\n\n    if static.len(x.shape) == 1:\n        dtype = x.dtype\n        st = x.strides[0]\n        data = x.data\n        curr = abs(_norm_cast(data[0]))\n\n        for i in range(1, sh):\n            elem = _norm_cast((Ptr[dtype](data.as_byte() + (st * i)))[0])\n            e = abs(elem)\n            if e < curr:\n                curr = e\n\n        return curr\n    else:\n        s = x.shape\n        n = s[axis]\n        ans_shape = tuple_delete(x.shape, axis)\n        ans = empty(ans_shape, R)\n\n        for idx in multirange(ans_shape):\n            q = ans._ptr(idx)\n            idx1 = tuple_insert(idx, axis, 0)\n            curr = abs(_norm_cast(x._ptr(idx1)[0]))\n\n            for i in range(1, n):\n                idx1 = tuple_insert(idx, axis, i)\n                elem = _norm_cast(x._ptr(idx1)[0])\n                e = abs(elem)\n                if e < curr:\n                    curr = e\n\n            q[0] = curr\n\n        return ans\n\ndef _vector_norm_0(x: ndarray, axis: int, R: type):\n    if static.len(x.shape) == 1:\n        dtype = x.dtype\n        sh = x.shape[0]\n        st = x.strides[0]\n        data = x.data\n        curr = R()\n\n        for i in range(sh):\n            elem = _norm_cast((Ptr[dtype](data.as_byte() + (st * i)))[0])\n            if elem:\n                curr += R(1)\n\n        return curr\n    else:\n        s = x.shape\n        n = s[axis]\n        ans_shape = tuple_delete(x.shape, axis)\n        ans = empty(ans_shape, R)\n\n        for idx in multirange(ans_shape):\n            q = ans._ptr(idx)\n            idx1 = tuple_insert(idx, axis, 0)\n            curr = R()\n\n            for i in range(n):\n                idx1 = tuple_insert(idx, axis, i)\n                elem = _norm_cast(x._ptr(idx1)[0])\n                if elem:\n                    curr += R(1)\n\n            q[0] = curr\n\n        return ans\n\ndef _vector_norm_1(x: ndarray, axis: int, R: type):\n    if static.len(x.shape) == 1:\n        dtype = x.dtype\n        sh = x.shape[0]\n        st = x.strides[0]\n        data = x.data\n        curr = R()\n\n        for i in range(sh):\n            elem = _norm_cast((Ptr[dtype](data.as_byte() + (st * i)))[0])\n            curr += abs(elem)\n\n        return curr\n    else:\n        s = x.shape\n        n = s[axis]\n        ans_shape = tuple_delete(x.shape, axis)\n        ans = empty(ans_shape, R)\n\n        for idx in multirange(ans_shape):\n            q = ans._ptr(idx)\n            idx1 = tuple_insert(idx, axis, 0)\n            curr = R()\n\n            for i in range(n):\n                idx1 = tuple_insert(idx, axis, i)\n                elem = _norm_cast(x._ptr(idx1)[0])\n                curr += abs(elem)\n\n            q[0] = curr\n\n        return ans\n\ndef _vector_norm_g(x: ndarray, axis: int, g, R: type):\n    if static.len(x.shape) == 1:\n        dtype = x.dtype\n        sh = x.shape[0]\n        st = x.strides[0]\n        data = x.data\n        curr = R()\n\n        for i in range(sh):\n            elem = _norm_cast((Ptr[dtype](data.as_byte() + (st * i)))[0])\n            curr += abs(elem) ** R(g)\n\n        return curr ** (R(1) / R(g))\n    else:\n        s = x.shape\n        n = s[axis]\n        ans_shape = tuple_delete(x.shape, axis)\n        ans = empty(ans_shape, R)\n\n        for idx in multirange(ans_shape):\n            q = ans._ptr(idx)\n            idx1 = tuple_insert(idx, axis, 0)\n            curr = R()\n\n            for i in range(n):\n                idx1 = tuple_insert(idx, axis, i)\n                elem = _norm_cast(x._ptr(idx1)[0])\n                curr += abs(elem) ** R(g)\n\n            q[0] = curr ** (R(1) / R(g))\n\n        return ans\n\ndef _matrix_norm_f(x: ndarray, row_axis: int, col_axis: int, R: type):\n    s = x.shape\n    if static.len(s) == 2:\n        dtype = x.dtype\n        sh1, sh2 = s\n        data = x.data\n        curr = R()\n\n        for i in range(sh1):\n            for j in range(sh2):\n                elem = _norm_cast(x._ptr((i, j))[0])\n                curr += _square(elem)\n\n        return util_sqrt(curr)\n    else:\n        axis1, axis2 = row_axis, col_axis\n        if axis1 > axis2:\n            axis1, axis2 = axis2, axis1\n\n        ans_shape = tuple_delete(tuple_delete(s, axis2), axis1)\n        ans = empty(ans_shape, R)\n        sh1 = s[axis1]\n        sh2 = s[axis2]\n\n        for idx in multirange(ans_shape):\n            q = ans._ptr(idx)\n            curr = R()\n\n            for i in range(sh1):\n                for j in range(sh2):\n                    idx1 = tuple_insert(\n                            tuple_insert(idx, axis1, i), axis2, j)\n                    elem = _norm_cast(x._ptr(idx1)[0])\n                    curr += _square(elem)\n\n            q[0] = util_sqrt(curr)\n\n        return ans\n\ndef _matrix_norm_1(x: ndarray, row_axis: int, col_axis: int, R: type):\n    s = x.shape\n    if s[col_axis] == 0:\n        _norm_error_zero_max()\n\n    if static.len(s) == 2:\n        dtype = x.dtype\n        row_dim = s[row_axis]\n        col_dim = s[col_axis]\n        row_stride = x.strides[row_axis]\n        col_stride = x.strides[col_axis]\n        data = x.data\n        curr = R()\n        first = True\n\n        for col_idx in range(col_dim):\n            sub = R()\n            for row_idx in range(row_dim):\n                elem = _norm_cast((Ptr[dtype](data.as_byte() +\n                                    (row_stride * row_idx) +\n                                    (col_stride * col_idx)))[0])\n                sub += abs(elem)\n\n            if first or sub > curr:\n                curr = sub\n                first = False\n\n        return curr\n    else:\n        axis1, axis2 = row_axis, col_axis\n        if axis1 > axis2:\n            axis1, axis2 = axis2, axis1\n\n        ans_shape = tuple_delete(tuple_delete(s, axis2), axis1)\n        ans = empty(ans_shape, R)\n        row_dim = s[row_axis]\n        col_dim = s[col_axis]\n\n        for idx in multirange(ans_shape):\n            q = ans._ptr(idx)\n            curr = R()\n            first = True\n\n            for col_idx in range(col_dim):\n                sub = R()\n                for row_idx in range(row_dim):\n                    i, j = row_idx, col_idx\n                    if row_axis > col_axis:\n                        i, j = j, i\n                    idx1 = tuple_insert(\n                            tuple_insert(idx, axis1, i), axis2, j)\n                    elem = _norm_cast(x._ptr(idx1)[0])\n                    sub += abs(elem)\n\n                if first or sub > curr:\n                    curr = sub\n                    first = False\n\n            q[0] = curr\n\n        return ans\n\ndef _matrix_norm_inf(x: ndarray, row_axis: int, col_axis: int, R: type):\n    s = x.shape\n    if s[row_axis] == 0:\n        _norm_error_zero_max()\n\n    if static.len(s) == 2:\n        dtype = x.dtype\n        row_dim = s[row_axis]\n        col_dim = s[col_axis]\n        row_stride = x.strides[row_axis]\n        col_stride = x.strides[col_axis]\n        data = x.data\n        curr = R()\n        first = True\n\n        for row_idx in range(row_dim):\n            sub = R()\n            for col_idx in range(col_dim):\n                elem = _norm_cast((Ptr[dtype](data.as_byte() +\n                                    (row_stride * row_idx) +\n                                    (col_stride * col_idx)))[0])\n                sub += abs(elem)\n\n            if first or sub > curr:\n                curr = sub\n                first = False\n\n        return curr\n    else:\n        axis1, axis2 = row_axis, col_axis\n        if axis1 > axis2:\n            axis1, axis2 = axis2, axis1\n\n        ans_shape = tuple_delete(tuple_delete(s, axis2), axis1)\n        ans = empty(ans_shape, R)\n        row_dim = s[row_axis]\n        col_dim = s[col_axis]\n\n        for idx in multirange(ans_shape):\n            q = ans._ptr(idx)\n            curr = R()\n            first = True\n\n            for row_idx in range(row_dim):\n                sub = R()\n                for col_idx in range(col_dim):\n                    i, j = row_idx, col_idx\n                    if row_axis > col_axis:\n                        i, j = j, i\n                    idx1 = tuple_insert(\n                            tuple_insert(idx, axis1, i), axis2, j)\n                    elem = _norm_cast(x._ptr(idx1)[0])\n                    sub += abs(elem)\n\n                if first or sub > curr:\n                    curr = sub\n                    first = False\n\n            q[0] = curr\n\n        return ans\n\ndef _matrix_norm_n1(x: ndarray, row_axis: int, col_axis: int, R: type):\n    s = x.shape\n    if s[col_axis] == 0:\n        _norm_error_zero_min()\n\n    if static.len(s) == 2:\n        dtype = x.dtype\n        row_dim = s[row_axis]\n        col_dim = s[col_axis]\n        row_stride = x.strides[row_axis]\n        col_stride = x.strides[col_axis]\n        data = x.data\n        curr = R()\n        first = True\n\n        for col_idx in range(col_dim):\n            sub = R()\n            for row_idx in range(row_dim):\n                elem = _norm_cast((Ptr[dtype](data.as_byte() +\n                                    (row_stride * row_idx) +\n                                    (col_stride * col_idx)))[0])\n                sub += abs(elem)\n\n            if first or sub < curr:\n                curr = sub\n                first = False\n\n        return curr\n    else:\n        axis1, axis2 = row_axis, col_axis\n        if axis1 > axis2:\n            axis1, axis2 = axis2, axis1\n\n        ans_shape = tuple_delete(tuple_delete(s, axis2), axis1)\n        ans = empty(ans_shape, R)\n        row_dim = s[row_axis]\n        col_dim = s[col_axis]\n\n        for idx in multirange(ans_shape):\n            q = ans._ptr(idx)\n            curr = R()\n            first = True\n\n            for col_idx in range(col_dim):\n                sub = R()\n                for row_idx in range(row_dim):\n                    i, j = row_idx, col_idx\n                    if row_axis > col_axis:\n                        i, j = j, i\n                    idx1 = tuple_insert(\n                            tuple_insert(idx, axis1, i), axis2, j)\n                    elem = _norm_cast(x._ptr(idx1)[0])\n                    sub += abs(elem)\n\n                if first or sub < curr:\n                    curr = sub\n                    first = False\n\n            q[0] = curr\n\n        return ans\n\ndef _matrix_norm_ninf(x: ndarray, row_axis: int, col_axis: int, R: type):\n    s = x.shape\n    if s[row_axis] == 0:\n        _norm_error_zero_min()\n\n    if static.len(s) == 2:\n        dtype = x.dtype\n        row_dim = s[row_axis]\n        col_dim = s[col_axis]\n        row_stride = x.strides[row_axis]\n        col_stride = x.strides[col_axis]\n        data = x.data\n        curr = R()\n        first = True\n\n        for row_idx in range(row_dim):\n            sub = R()\n            for col_idx in range(col_dim):\n                elem = _norm_cast((Ptr[dtype](data.as_byte() +\n                                    (row_stride * row_idx) +\n                                    (col_stride * col_idx)))[0])\n                sub += abs(elem)\n\n            if first or sub < curr:\n                curr = sub\n                first = False\n\n        return curr\n    else:\n        axis1, axis2 = row_axis, col_axis\n        if axis1 > axis2:\n            axis1, axis2 = axis2, axis1\n\n        ans_shape = tuple_delete(tuple_delete(s, axis2), axis1)\n        ans = empty(ans_shape, R)\n        row_dim = s[row_axis]\n        col_dim = s[col_axis]\n\n        for idx in multirange(ans_shape):\n            q = ans._ptr(idx)\n            curr = R()\n            first = True\n\n            for row_idx in range(row_dim):\n                sub = R()\n                for col_idx in range(col_dim):\n                    i, j = row_idx, col_idx\n                    if row_axis > col_axis:\n                        i, j = j, i\n                    idx1 = tuple_insert(\n                            tuple_insert(idx, axis1, i), axis2, j)\n                    elem = _norm_cast(x._ptr(idx1)[0])\n                    sub += abs(elem)\n\n                if first or sub < curr:\n                    curr = sub\n                    first = False\n\n            q[0] = curr\n\n        return ans\n\ndef _multi_svd_norm(x: ndarray, row_axis: int, col_axis: int, op):\n    y = moveaxis(x, (row_axis, col_axis), (-2, -1))\n    return op(svd(y, compute_uv=False), axis=-1)\n\ndef _norm_wrap(x, kd_shape, keepdims: Literal[bool]):\n    if keepdims:\n        return asarray(x).reshape(kd_shape)\n    else:\n        return x\n\ndef norm(x, ord = None, axis = None, keepdims: Literal[bool] = False):\n    x = asarray(x)\n\n    if not (x.dtype is float or x.dtype is float32 or x.dtype is float16):\n        return norm(x.astype(float), ord=ord, axis=axis, keepdims=keepdims)\n\n    ndim: Literal[int] = static.len(x.shape)\n    dtype = x.dtype\n\n    if dtype is complex or dtype is float:\n        r = float()\n    elif dtype is complex64 or dtype is float32:\n        r = float32()\n    else:\n        r = float()\n\n    R = type(r)\n\n    # Common cases\n    if axis is None:\n        handle = False\n        if ord is None:\n            handle = True\n        elif isinstance(ord, str):\n            handle = ((ord == 'f' or ord == 'fro') and ndim == 2)\n        elif isinstance(ord, int):\n            handle = (ord == 2 and ndim == 1)\n\n        if handle:\n            y = x.ravel(order='K')\n            if dtype is complex or dtype is complex64:\n                x_real = y.real\n                x_imag = y.imag\n                sqnorm = x_real.dot(x_real) + x_imag.dot(x_imag)\n            else:\n                sqnorm = y.dot(y)\n\n            ret = sqrt(sqnorm)\n            if keepdims:\n                return asarray(ret).reshape((1,) * ndim)\n            else:\n                return ret\n\n    if axis is None:\n        ax = tuple_range(ndim)\n    elif isinstance(axis, int):\n        ax = (axis,)\n    elif isinstance(axis, Tuple):\n        ax = axis\n    else:\n        compile_error(\"'axis' must be None, an integer or a tuple of integers\")\n\n    if ndim == 0:\n        # this matches NumPy behavior\n        if ord is None:\n            return abs(x.data[0])\n        else:\n            compile_error(\"Improper number of dimensions to norm.\")\n\n    ax = tuple(normalize_axis_index(a, ndim) for a in ax)\n    kd_shape = x.shape\n    if keepdims:\n        for a in ax:\n            kd_shape = tuple_set(kd_shape, a, 1)\n\n    if static.len(ax) == 1:\n        if ord is None:\n            return _norm_wrap(_vector_norm_f(x, ax[0], R), kd_shape, keepdims)\n        elif isinstance(ord, int):\n            if ord == 0:\n                return _norm_wrap(_vector_norm_0(x, ax[0], R), kd_shape, keepdims)\n            elif ord == 1:\n                return _norm_wrap(_vector_norm_1(x, ax[0], R), kd_shape, keepdims)\n            elif ord == 2:\n                return _norm_wrap(_vector_norm_f(x, ax[0], R), kd_shape, keepdims)\n            else:\n                return _norm_wrap(_vector_norm_g(x, ax[0], float(ord), R), kd_shape, keepdims)\n        elif isinstance(ord, float):\n            if ord == 0.:\n                return _norm_wrap(_vector_norm_0(x, ax[0], R), kd_shape, keepdims)\n            elif ord == 1.:\n                return _norm_wrap(_vector_norm_1(x, ax[0], R), kd_shape, keepdims)\n            elif ord == 2.:\n                return _norm_wrap(_vector_norm_f(x, ax[0], R), kd_shape, keepdims)\n            elif ord == inf(float):\n                return _norm_wrap(_vector_norm_inf(x, ax[0], R), kd_shape, keepdims)\n            elif ord == -inf(float):\n                return _norm_wrap(_vector_norm_ninf(x, ax[0], R), kd_shape, keepdims)\n            else:\n                return _norm_wrap(_vector_norm_g(x, ax[0], ord, R), kd_shape, keepdims)\n        else:\n            compile_error(\"Invalid norm order for vectors\")\n    elif static.len(ax) == 2:\n        row_axis, col_axis = ax\n        if row_axis == col_axis:\n            raise ValueError(\"Duplicate axes given.\")\n\n        if ord is None:\n            return _norm_wrap(_matrix_norm_f(x, ax[0], ax[1], R), kd_shape, keepdims)\n        elif isinstance(ord, int):\n            if ord == 2:\n                return _norm_wrap(_multi_svd_norm(x, ax[0], ax[1], ndarray.max), kd_shape, keepdims)\n            elif ord == -2:\n                return _norm_wrap(_multi_svd_norm(x, ax[0], ax[1], ndarray.min), kd_shape, keepdims)\n            elif ord == 1:\n                return _norm_wrap(_matrix_norm_1(x, ax[0], ax[1], R), kd_shape, keepdims)\n            elif ord == -1:\n                return _norm_wrap(_matrix_norm_n1(x, ax[0], ax[1], R), kd_shape, keepdims)\n        elif isinstance(ord, float):\n            if ord == 2.:\n                return _norm_wrap(_multi_svd_norm(x, ax[0], ax[1], ndarray.max), kd_shape, keepdims)\n            elif ord == -2.:\n                return _norm_wrap(_multi_svd_norm(x, ax[0], ax[1], ndarray.min), kd_shape, keepdims)\n            elif ord == 1.:\n                return _norm_wrap(_matrix_norm_1(x, ax[0], ax[1], R), kd_shape, keepdims)\n            elif ord == -1.:\n                return _norm_wrap(_matrix_norm_n1(x, ax[0], ax[1], R), kd_shape, keepdims)\n            elif ord == inf(float):\n                return _norm_wrap(_matrix_norm_inf(x, ax[0], ax[1], R), kd_shape, keepdims)\n            elif ord == -inf(float):\n                return _norm_wrap(_matrix_norm_ninf(x, ax[0], ax[1], R), kd_shape, keepdims)\n        elif isinstance(ord, str):\n            if ord == 'fro' or ord == 'f':\n                return _norm_wrap(_matrix_norm_f(x, ax[0], ax[1], R), kd_shape, keepdims)\n            elif ord == 'nuc':\n                return _norm_wrap(_multi_svd_norm(x, ax[0], ax[1], ndarray.sum), kd_shape, keepdims)\n\n        raise ValueError(\"Invalid norm order for matrices.\")\n    else:\n        raise ValueError(\"Improper number of dimensions to norm.\")\n\ndef pinv(a, rcond = 1e-15, hermitian: bool = False):\n    a = asarray(a)\n    rcond = asarray(rcond)\n    m, n = _rows_cols(a)\n\n    if m == 0 or n == 0:\n        ans_shape = a.shape[:-2] + (n, m)\n        dtype = a.dtype\n\n        if (dtype is float or\n            dtype is float32 or\n            dtype is complex or\n            dtype is complex64):\n            return empty(ans_shape, dtype=dtype)\n        else:\n            return empty(ans_shape, dtype=float)\n\n    a = a.conjugate()\n    u, s, vt = svd(a, full_matrices=False, hermitian=hermitian)\n    cutoff = multiply(rcond[..., None], s.max(axis=-1, keepdims=True))\n    S = s.dtype\n\n    for idx in multirange(broadcast_shapes(s.shape, cutoff.shape)):\n        c = cutoff._ptr(idx, broadcast=True)[0]\n        p = s._ptr(idx, broadcast=True)\n        e = p[0]\n        if greater(e, c):\n            e = cast(divide(1, e), S)\n        else:\n            e = S()\n        p[0] = e\n\n    vt_t = swapaxes(vt, -1, -2)\n    u_t = swapaxes(u, -1, -2)\n    res = matmul(vt_t, multiply(s[..., None], u_t))\n    return res\n\ndef matrix_rank(A, tol = None, hermitian: bool = False):\n    A = asarray(A)\n    dtype = A.dtype\n\n    if static.len(A.shape) == 0:\n        return int(bool(A.data[0]))\n    elif static.len(A.shape) == 1:\n        for a in A:\n            if a:\n                return 1\n        return 0\n    elif static.len(A.shape) == 2:\n        m, n = _rows_cols(A)\n        if m == 0 or n == 0:\n            raise ValueError(\"cannot take rank of empty matrix\")\n        S = svd(A, compute_uv=False, hermitian=hermitian)\n        R = S.dtype\n\n        if static.len(asarray(tol).shape) != 0:\n            compile_error(\"invalid tolerance dimension\")\n\n        s_max = -inf(R)\n        if tol is None:\n            for s in S:\n                if s > s_max:\n                    s_max = s\n\n        r = 0\n        for s in S:\n            if tol is None:\n                if s > s_max * cast(max(m, n), R) * eps(R):\n                    r += 1\n            else:\n                if s > cast(tol, R):\n                    r += 1\n\n        return r\n    else:\n        m, n = _rows_cols(A)\n        if m == 0 or n == 0:\n            raise ValueError(\"cannot take rank of empty matrix\")\n        k = min(m, n)\n        pre_shape = A.shape[:-2]\n        ans = empty(pre_shape, int)\n        S = svd(A, compute_uv=False, hermitian=hermitian)\n        R = S.dtype\n\n        if tol is None:\n            t = 0\n        else:\n            t = asarray(tol)\n            if static.len(t.shape) > 0:\n                if static.len(t.shape) != static.len(A.shape) - 2:\n                    compile_error(\"invalid tolerance dimension\")\n                else:\n                    if t.shape != A.shape[:-2]:\n                        raise ValueError(\"tolerance does not broadcast against matrix_rank input\")\n\n        for idx in multirange(pre_shape):\n            q = ans._ptr(idx)\n            r = 0\n            s_max = -inf(R)\n\n            if tol is None:\n                for i in range(k):\n                    e = S._ptr(idx + (i,))[0]\n                    if e > s_max:\n                        s_max = e\n\n            for i in range(k):\n                e = S._ptr(idx + (i,))[0]\n                if tol is None:\n                    if e > s_max * cast(max(m, n), R) * eps(R):\n                        r += 1\n                else:\n                    if static.len(t.shape) == 0:\n                        if e > cast(t.data[0], R):\n                            r += 1\n                    else:\n                        if e > cast(t._ptr(idx)[0], R):\n                            r += 1\n            q[0] = r\n\n        return ans\n\ndef cond(x, p = None):\n    def cond_r_1(x, p: int):\n        s = svd(x, compute_uv=False)\n        if p == -2:\n            return s[..., -1] / s[..., 0]\n        else:\n            return s[..., 0] / s[..., -1]\n\n    def cond_r_2(x, p):\n        invx = _inv(x, ignore_errors=True)\n        return norm(x, p, axis=(-2, -1)) * norm(invx, p, axis=(-2, -1))\n\n    x = asarray(x)\n    m, n = _rows_cols(x)\n\n    if m == 0 or n == 0:\n        raise LinAlgError(\"cond is not defined on empty arrays\")\n\n    if p is None:\n        r = cond_r_1(x, 0)\n    elif isinstance(p, int):\n        if p == 2 or p == -2:\n            r = cond_r_1(x, p)\n        else:\n            r = cond_r_2(x, p)\n    else:\n        r = cond_r_2(x, p)\n\n    r = asarray(r)\n    if static.len(r.shape) == 0:\n        any_nan = False\n        for i in range(m):\n            for j in range(n):\n                if isnan(x._ptr((i, j))[0]):\n                    any_nan = True\n                    break\n\n        rval = r.data[0]\n        if isnan(rval) and not any_nan:\n            return inf(type(rval))\n        else:\n            return rval\n    else:\n        for idx in multirange(r.shape):\n            any_nan = False\n            for i in range(m):\n                for j in range(n):\n                    if isnan(x._ptr(idx + (i, j))[0]):\n                        any_nan = True\n                        break\n\n            p = r._ptr(idx)\n            if isnan(p[0]) and not any_nan:\n                p[0] = inf(type(p[0]))\n\n        return r\n\ndef matrix_transpose(x):\n    x = asarray(x)\n    if x.ndim < 2:\n        compile_error(\"Input array must be at least 2-dimensional\")\n    return swapaxes(x, -1, -2)\n\n@extend\nclass ndarray:\n    def __matmul__(self, other):\n        return matmul(self, other)\n\n    def dot(self, other):\n        return dot(self, other)\n\n    def trace(self, offset: int = 0, axis1: int = 0, axis2: int = 1, dtype: type = NoneType, out = None):\n        return _trace_ufunc(self, offset=offset, axis1=axis1, axis2=axis2, dtype=dtype, out=out)\n"
  },
  {
    "path": "stdlib/numpy/linalg_sym.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .linalg import _matmul_ufunc, _linalg_dot, _linalg_vdot, \\\n                    _linalg_tensordot, _linalg_inner, _linalg_outer, \\\n                    _linalg_kron, _trace_ufunc, \\\n                    matrix_transpose as _linalg_matrix_transpose\n\ndef matmul(x1, x2, out = None, dtype: type = NoneType):\n    return _matmul_ufunc(x1, x2, out=out, dtype=dtype)\n\ndef dot(a, b, out = None):\n    return _linalg_dot(a, b, out=out)\n\ndef vdot(a, b):\n    return _linalg_vdot(a, b)\n\ndef tensordot(a, b, axes):\n    return _linalg_tensordot(a, b, axes=axes)\n\n@overload\ndef tensordot(a, b, axes: Literal[int] = 2):\n    return _linalg_tensordot(a, b, axes=axes)\n\ndef inner(a, b):\n    return _linalg_inner(a, b)\n\ndef outer(a, b, out = None):\n    return _linalg_outer(a, b, out=out)\n\ndef kron(a, b):\n    return _linalg_kron(a, b)\n\ndef trace(a, offset: int = 0, axis1: int = 0, axis2: int = 1,\n          dtype: type = NoneType, out = None):\n    return _trace_ufunc(a, offset=offset, axis1=axis1, axis2=axis2,\n                        dtype=dtype, out=out)\n\ndef matrix_transpose(x):\n    return _linalg_matrix_transpose(x)\n"
  },
  {
    "path": "stdlib/numpy/misc.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .ndarray import ndarray\n\nimport util\nimport internal.static as static\n\nMAXDIMS             : Literal[int] = 32\nMAY_SHARE_BOUNDS    : Literal[int] = 0\nMAY_SHARE_EXACT     : Literal[int] = -1\n_MEM_OVERLAP_NO      : Literal[int] = 0   # no solution exists\n_MEM_OVERLAP_YES     : Literal[int] = 1   # solution found\n_MEM_OVERLAP_TOO_HARD: Literal[int] = -1  # max_work exceeded\n_MEM_OVERLAP_OVERFLOW: Literal[int] = -2  # algorithm failed due to integer overflow\n_MEM_OVERLAP_ERROR   : Literal[int] = -3  # invalid input\n\ni128 = Int[128]\n\ndef _euclid(a1: int, a2: int):\n    gamma1 = 1\n    gamma2 = 0\n    epsilon1 = 0\n    epsilon2 = 1\n\n    a_gcd = 0\n    gamma = 0\n    epsilon = 0\n\n    while True:\n        if a2 > 0:\n            r = a1 // a2\n            a1 -= r * a2\n            gamma1 -= r * gamma2\n            epsilon1 -= r * epsilon2\n        else:\n            a_gcd = a1\n            gamma = gamma1\n            epsilon = epsilon1\n            break\n\n        if a1 > 0:\n            r = a2 // a1\n            a2 -= r * a1\n            gamma2 -= r * gamma1\n            epsilon2 -= r * epsilon1\n        else:\n            a_gcd = a2\n            gamma = gamma2\n            epsilon = epsilon2\n            break\n\n    return a_gcd, gamma, epsilon\n\n@tuple\nclass DiophantineTerm:\n    a: int\n    ub: int\n\n    def __lt__(self, other: DiophantineTerm):\n        return self.a < other.a\n\n    def with_a(self, a: int):\n        return DiophantineTerm(a, self.ub)\n\n    def with_ub(self, ub: int):\n        return DiophantineTerm(self.a, ub)\n\ndef _sort(p: Ptr[DiophantineTerm], n: int):\n    arr = ndarray((n,), p)\n    arr.sort()\n\n@pure\n@llvm\ndef _safe_add_llvm(a: int, b: int) -> Tuple[int, UInt[1]]:\n    declare {i64, i1} @llvm.sadd.with.overflow.i64(i64, i64)\n    %c = call {i64, i1} @llvm.sadd.with.overflow.i64(i64 %a, i64 %b)\n    ret {i64, i1} %c\n\ndef _safe_add(a: int, b: int):\n    c, o = _safe_add_llvm(a, b)\n    return (c, bool(o))\n\n@pure\n@llvm\ndef _safe_sub_llvm(a: int, b: int) -> Tuple[int, UInt[1]]:\n    declare {i64, i1} @llvm.ssub.with.overflow.i64(i64, i64)\n    %c = call {i64, i1} @llvm.ssub.with.overflow.i64(i64 %a, i64 %b)\n    ret {i64, i1} %c\n\ndef _safe_sub(a: int, b: int):\n    c, o = _safe_sub_llvm(a, b)\n    return (c, bool(o))\n\n@pure\n@llvm\ndef _safe_mul_llvm(a: int, b: int) -> Tuple[int, UInt[1]]:\n    declare {i64, i1} @llvm.smul.with.overflow.i64(i64, i64)\n    %c = call {i64, i1} @llvm.smul.with.overflow.i64(i64 %a, i64 %b)\n    ret {i64, i1} %c\n\ndef _safe_mul(a: int, b: int):\n    c, o = _safe_mul_llvm(a, b)\n    return (c, bool(o))\n\n@pure\n@llvm\ndef _safe_add128_llvm(a: i128, b: i128) -> Tuple[i128, UInt[1]]:\n    declare {i128, i1} @llvm.sadd.with.overflow.i128(i128, i128)\n    %c = call {i128, i1} @llvm.sadd.with.overflow.i128(i128 %a, i128 %b)\n    ret {i128, i1} %c\n\ndef _safe_add128(a: i128, b: i128):\n    c, o = _safe_add128_llvm(a, b)\n    return (c, bool(o))\n\n@pure\n@llvm\ndef _safe_sub128_llvm(a: i128, b: i128) -> Tuple[i128, UInt[1]]:\n    declare {i128, i1} @llvm.ssub.with.overflow.i128(i128, i128)\n    %c = call {i128, i1} @llvm.ssub.with.overflow.i128(i128 %a, i128 %b)\n    ret {i128, i1} %c\n\ndef _safe_sub128(a: i128, b: i128):\n    c, o = _safe_sub128_llvm(a, b)\n    return (c, bool(o))\n\ndef _diophantine_precompute(n: int,\n                            E: Ptr[DiophantineTerm],\n                            Ep: Ptr[DiophantineTerm],\n                            Gamma: Ptr[int],\n                            Epsilon: Ptr[int]):\n    a_gcd, gamma, epsilon = _euclid(E[0].a, E[1].a)\n    Ep[0] = Ep[0].with_a(a_gcd)\n    Gamma[0] = gamma\n    Epsilon[0] = epsilon\n\n    if n > 2:\n        c1 = E[0].a // a_gcd\n        c2 = E[1].a // a_gcd\n        x1, o1 = _safe_mul(E[0].ub, c1)\n        x2, o2 = _safe_mul(E[1].ub, c2)\n        x3, o3 = _safe_add(x1, x2)\n        Ep[0] = Ep[0].with_ub(x3)\n        if o1 or o2 or o3:\n            return True\n\n    for j in range(2, n):\n        a_gcd, gamma, epsilon = _euclid(Ep[j - 2].a, E[j].a)\n        Ep[j - 1] = Ep[j - 1].with_a(a_gcd)\n        Gamma[j - 1] = gamma\n        Epsilon[j - 1] = epsilon\n\n        if j < n - 1:\n            c1 = Ep[j - 2].a // a_gcd\n            c2 = E[j].a // a_gcd\n            x1, o1 = _safe_mul(c1, Ep[j - 2].ub)\n            x2, o2 = _safe_mul(c2, E[j].ub)\n            x3, o3 = _safe_add(x1, x2)\n            Ep[j - 1] = Ep[j - 1].with_ub(x3)\n            if o1 or o2 or o3:\n                return True\n\n    return False\n\ndef _floordiv(a: i128, b: int):\n    b = i128(b)\n    result, remainder = a // b, a % b\n    if a < i128(0) and remainder != i128(0):\n        result -= i128(1)\n    return result\n\ndef _ceildiv(a: i128, b: int):\n    b = i128(b)\n    result, remainder = a // b, a % b\n    if a > i128(0) and remainder != i128(0):\n        result += i128(1)\n    return result\n\ndef _to_64(x: i128):\n    if x > i128(9223372036854775807) or x < i128(-9223372036854775808):\n        return 0, True\n    else:\n        return int(x), False\n\ndef _diophantine_dfs(n: int,\n                     v: int,\n                     E: Ptr[DiophantineTerm],\n                     Ep: Ptr[DiophantineTerm],\n                     Gamma: Ptr[int],\n                     Epsilon: Ptr[int],\n                     b: int,\n                     max_work: int,\n                     require_ub_nontrivial: bool,\n                     x: Ptr[int],\n                     count: Ptr[int]):\n\n    if max_work >= 0 and count[0] >= max_work:\n        return _MEM_OVERLAP_TOO_HARD\n\n    if v == 1:\n        a1 = E[0].a\n        u1 = E[0].ub\n    else:\n        a1 = Ep[v - 2].a\n        u1 = Ep[v - 2].ub\n\n    a2 = E[v].a\n    u2 = E[v].ub\n\n    a_gcd = Ep[v - 1].a\n    gamma = Gamma[v - 1]\n    epsilon = Epsilon[v - 1]\n\n    c = b // a_gcd\n    r = b % a_gcd\n    if r != 0:\n        count[0] += 1\n        return _MEM_OVERLAP_NO\n\n    c1 = a2 // a_gcd\n    c2 = a1 // a_gcd\n\n    x10 = i128(gamma) * i128(c)\n    x20 = i128(epsilon) * i128(c)\n\n    t_l1 = _ceildiv(-x10, c1)\n    v1, o1 = _safe_sub128(x20, i128(u2))\n    t_l2 = _ceildiv(v1, c2)\n\n    v2, o2 = _safe_sub128(i128(u1), x10)\n    t_u1 = _floordiv(v2, c1)\n    t_u2 = _floordiv(x20, c2)\n\n    if o1 or o2:\n        return _MEM_OVERLAP_OVERFLOW\n\n    if t_l2 > t_l1:\n        tl1 = t_l2\n\n    if t_u1 > t_u2:\n        t_u1 = t_u2\n\n    if t_l1 > t_u1:\n        count[0] += 1\n        return _MEM_OVERLAP_NO\n\n    t_l, o1 = _to_64(t_l1)\n    t_u, o2 = _to_64(t_u1)\n\n    x10, o3 = _safe_add128(x10, i128(c1) * i128(t_l))\n    x20, o4 = _safe_sub128(x20, i128(c2) * i128(t_l))\n\n    t_u, o5 = _safe_sub(t_u, t_l)\n    t_l = 0\n    x1, o6 = _to_64(x10)\n    x2, o7 = _to_64(x20)\n\n    if o1 or o2 or o3 or o4 or o5 or o6 or o7:\n        return _MEM_OVERLAP_OVERFLOW\n\n    if v == 1:\n        if t_u >= t_l:\n            x[0] = x1 + (c1 * t_l)\n            x[1] = x2 - (c2 * t_l)\n            if require_ub_nontrivial:\n                is_ub_nontrivial = True\n                for j in range(n):\n                    if x[j] != E[j].ub // 2:\n                        is_ub_nontrivial = False\n                        break\n\n                if is_ub_nontrivial:\n                    count[0] += 1\n                    return _MEM_OVERLAP_NO\n\n            return _MEM_OVERLAP_YES\n\n        count[0] += 1\n        return _MEM_OVERLAP_NO\n    else:\n        t = t_l\n        while t <= t_u:\n            x[v] = x2 - (c2 * t)\n            v1, o1 = _safe_mul(a2, x[v])\n            b2, o2 = _safe_sub(b, v1)\n\n            if o1 or o2:\n                return _MEM_OVERLAP_OVERFLOW\n\n            res = _diophantine_dfs(n, v - 1, E, Ep, Gamma, Epsilon,\n                                   b2, max_work, require_ub_nontrivial,\n                                   x, count)\n            if res != _MEM_OVERLAP_NO:\n                return res\n            t += 1\n\n        count[0] += 1\n        return _MEM_OVERLAP_NO\n\ndef _solve_diophantine(n: int,\n                       E: Ptr[DiophantineTerm],\n                       b: int,\n                       max_work: int,\n                       require_ub_nontrivial: bool,\n                       x: Ptr[int]):\n    for j in range(n):\n        if E[j].a <= 0:\n            return _MEM_OVERLAP_ERROR\n        elif E[j].ub < 0:\n            return _MEM_OVERLAP_NO\n\n    if require_ub_nontrivial:\n        ub_sum = 0\n        o = False\n        for j in range(n):\n            if E[j].ub % 2 != 0:\n                return _MEM_OVERLAP_ERROR\n\n            v1, o1 = _safe_mul(E[j].a, E[j].ub // 2)\n            v2, o2 = _safe_add(ub_sum, v1)\n            ub_sum = v2\n            o = o or o1 or o2\n\n        if o:\n            return _MEM_OVERLAP_ERROR\n        b = ub_sum\n\n    if b < 0:\n        return _MEM_OVERLAP_NO\n\n    if n == 0:\n        if require_ub_nontrivial:\n            return _MEM_OVERLAP_NO\n\n        if b == 0:\n            return _MEM_OVERLAP_YES\n\n        return _MEM_OVERLAP_NO\n    elif n == 1:\n        if require_ub_nontrivial:\n            return _MEM_OVERLAP_NO\n\n        if b % E[0].a == 0:\n            x[0] = b // E[0].a\n            if x[0] >= 0 and x[0] <= E[0].ub:\n                return _MEM_OVERLAP_YES\n\n        return _MEM_OVERLAP_NO\n    else:\n        count = 0\n        Ep = Ptr[DiophantineTerm](n)\n        Epsilon = Ptr[int](n)\n        Gamma = Ptr[int](n)\n\n        if _diophantine_precompute(n, E, Ep, Gamma, Epsilon):\n            res = _MEM_OVERLAP_OVERFLOW\n        else:\n            res = _diophantine_dfs(n, n - 1, E, Ep, Gamma, Epsilon, b, max_work,\n                                   require_ub_nontrivial, x, __ptr__(count))\n\n        util.free(Ep)\n        util.free(Gamma)\n        util.free(Epsilon)\n        return res\n\ndef _diophantine_simplify(n: Ptr[int], E: Ptr[DiophantineTerm], b: int):\n    for j in range(n[0]):\n        if E[j].ub < 0:\n            return 0\n\n    if b < 0:\n        return 0\n\n    _sort(E, n[0])\n\n    o = False\n    m = n[0]\n    i = 0\n    for j in range(1, m):\n        if E[i].a == E[j].a:\n            v1, o1 = _safe_add(E[i].ub, E[j].ub)\n            o = o or o1\n            E[i] = E[i].with_ub(v1)\n            n[0] -= 1\n        else:\n            i += 1\n            if i != j:\n                E[i] = E[j]\n\n    m = n[0]\n    i = 0\n    for j in range(m):\n        E[j] = E[j].with_ub(min(E[j].ub, b // E[j].a))\n        if E[j].ub == 0:\n            n[0] -= 1\n        else:\n            if i != j:\n                E[i] = E[j]\n            i += 1\n\n    if o:\n        return -1\n    else:\n        return 0\n\ndef _offset_bounds_from_strides(arr: ndarray):\n    lower = 0\n    upper = 0\n    nd = arr.ndim\n    shape = arr.shape\n    strides = arr.strides\n\n    for i in range(nd):\n        if shape[i] == 0:\n            return (0, 0)\n\n        max_axis_offset = strides[i] * (shape[i] - 1)\n        if max_axis_offset > 0:\n            upper += max_axis_offset\n        else:\n            lower += max_axis_offset\n\n    upper += arr.itemsize\n    return (lower, upper)\n\ndef _get_array_memory_extents(arr: ndarray):\n    low, upper = _offset_bounds_from_strides(arr)\n    out_start = arr.data.as_byte().__int__() + low\n    out_end = arr.data.as_byte().__int__() + upper\n\n    num_bytes = arr.itemsize\n    for j in range(arr.ndim):\n        num_bytes *= arr.shape[j]\n\n    return u64(out_start), u64(out_end), u64(num_bytes)\n\ndef _strides_to_terms(arr: ndarray,\n                      terms: Ptr[DiophantineTerm],\n                      nterms: Ptr[int],\n                      skip_empty: bool):\n    for i in range(arr.ndim):\n        if skip_empty:\n            if arr.shape[i] <= 1 or arr.strides[i] == 0:\n                continue\n\n        terms[nterms[0]] = terms[nterms[0]].with_a(arr.strides[i])\n\n        if terms[nterms[0]].a < 0:\n            terms[nterms[0]] = terms[nterms[0]].with_a(-terms[nterms[0]].a)\n\n        if terms[nterms[0]].a < 0:\n            return True\n\n        terms[nterms[0]] = terms[nterms[0]].with_ub(arr.shape[i] - 1)\n        nterms[0] += 1\n\n    return False\n\ndef _solve_may_share_memory(a: ndarray, b: ndarray, max_work: int):\n    if a.ndim > MAXDIMS or b.ndim > MAXDIMS:\n        compile_error(\"maximum array dimension exceeded\")\n\n    terms_tuple = (DiophantineTerm(0, 0),) * (2*MAXDIMS + 2)\n    x_tuple = (0,) * (2*MAXDIMS + 2)\n    terms = Ptr[DiophantineTerm](__ptr__(terms_tuple).as_byte())\n    x = Ptr[int](__ptr__(x_tuple).as_byte())\n\n    start1, end1, size1 = _get_array_memory_extents(a)\n    start2, end2, size2 = _get_array_memory_extents(b)\n\n    if not (start1 < end2 and start2 < end1 and start1 < end1 and start2 < end2):\n        return _MEM_OVERLAP_NO\n\n    if max_work == 0:\n        return _MEM_OVERLAP_TOO_HARD\n\n    uintp_rhs = min(end2 - u64(1) - start1, end1 - u64(1) - start2)\n    if uintp_rhs > u64(9223372036854775807):\n        return _MEM_OVERLAP_OVERFLOW\n    rhs = int(uintp_rhs)\n\n    nterms = 0\n    if _strides_to_terms(a, terms, __ptr__(nterms), True):\n        return _MEM_OVERLAP_OVERFLOW\n    if _strides_to_terms(b, terms, __ptr__(nterms), True):\n        return _MEM_OVERLAP_OVERFLOW\n    if a.itemsize > 1:\n        terms[nterms] = DiophantineTerm(a=1, ub=a.itemsize-1)\n        nterms += 1\n    if b.itemsize > 1:\n        terms[nterms] = DiophantineTerm(a=1, ub=b.itemsize-1)\n        nterms += 1\n\n    if _diophantine_simplify(__ptr__(nterms), terms, rhs):\n        return _MEM_OVERLAP_OVERFLOW\n\n    return _solve_diophantine(nterms, terms, rhs, max_work, False, x)\n\ndef _solve_may_have_internal_overlap(a: ndarray, max_work: int):\n    if a.ndim > MAXDIMS:\n        compile_error(\"maximum array dimension exceeded\")\n\n    terms_tuple = (DiophantineTerm(0, 0),) * (MAXDIMS + 1)\n    x_tuple = (0,) * (MAXDIMS + 1)\n    terms = Ptr[DiophantineTerm](__ptr__(terms_tuple).as_byte())\n    x = Ptr[int](__ptr__(x_tuple).as_byte())\n\n    cc, fc = a._contig\n    if cc or fc:\n        return _MEM_OVERLAP_NO\n\n\n    nterms = 0\n    if _strides_to_terms(a, terms, __ptr__(nterms), False):\n        return _MEM_OVERLAP_OVERFLOW\n    if a.itemsize > 1:\n        terms[nterms] = DiophantineTerm(a=1, ub=a.itemsize-1)\n        nterms += 1\n\n    i = 0\n    for j in range(nterms):\n        if terms[j].ub == 0:\n            continue\n        elif terms[j].ub < 0:\n            return _MEM_OVERLAP_NO\n        elif terms[j].a == 0:\n            return _MEM_OVERLAP_YES\n\n        if i != j:\n            terms[i] = terms[j]\n        i += 1\n    nterms = i\n\n    for j in range(nterms):\n        terms[j] = terms[j].with_ub(terms[j].ub * 2)\n\n    _sort(terms, nterms)\n\n    return _solve_diophantine(nterms, terms, -1, max_work, True, x)\n\ndef _array_shaes_memory_impl(a: ndarray, b: ndarray, max_work: int, raise_exception: bool):\n    if max_work < -2:\n        raise ValueError(\"Invalid value for max_work\")\n\n    result = _solve_may_share_memory(a, b, max_work)\n\n    if result == _MEM_OVERLAP_NO:\n        return False\n    elif result == _MEM_OVERLAP_YES:\n        return True\n    elif result == _MEM_OVERLAP_OVERFLOW:\n        if raise_exception:\n            raise OverflowError(\"Integer overflow in computing overlap\")\n        else:\n            return True\n    elif result == _MEM_OVERLAP_TOO_HARD:\n        if raise_exception:\n            raise util.TooHardError(\"Exceeded max_work\")\n        else:\n            return True\n    else:\n        raise RuntimeError(\"Error in computing overlap\")\n\ndef shares_memory(a: ndarray, b: ndarray, max_work: Optional[int] = None):\n    mw = 0\n    if max_work is None:\n        mw = MAY_SHARE_EXACT\n    else:\n        mw = max_work\n    return _array_shaes_memory_impl(a, b, mw, True)\n\ndef may_share_memory(a: ndarray, b: ndarray, max_work: Optional[int] = None):\n    mw = 0\n    if max_work is None:\n        mw = MAY_SHARE_BOUNDS\n    else:\n        mw = max_work\n    return _array_shaes_memory_impl(a, b, mw, False)\n\ndef setbufsize(size: int):\n    pass  # Codon-NumPy does not use ufunc buffers\n\ndef getbufsize():\n    return 0  # Codon-NumPy does not use ufunc buffers\n\ndef byte_bounds(a: ndarray):\n    a_data = a.data\n    astrides = a.strides\n    ashape = a.shape\n    a_low = a_data.as_byte().__int__()\n    a_high = a_data.as_byte().__int__()\n    bytes_a = a.itemsize\n    cc, fc = a._contig\n\n    if cc or fc:\n        a_high += a.size * bytes_a\n    else:\n        for i in static.range(static.len(ashape)):\n            shape = ashape[i]\n            stride = astrides[i]\n            if stride < 0:\n                a_low += (shape - 1) * stride\n            else:\n                a_high += (shape - 1) * stride\n        a_high += bytes_a\n    return (a_low, a_high)\n"
  },
  {
    "path": "stdlib/numpy/ndarray.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport util\nimport internal.static as static\n\nnewaxis = None\n\n_FLAG_C_CONTIGUOUS   : Literal[int] = 0x0001\n_FLAG_F_CONTIGUOUS   : Literal[int] = 0x0002\n_FLAG_OWNDATA        : Literal[int] = 0x0004\n_FLAG_FORCECAST      : Literal[int] = 0x0010\n_FLAG_ENSURECOPY     : Literal[int] = 0x0020\n_FLAG_ENSUREARRAY    : Literal[int] = 0x0040\n_FLAG_ELEMENTSTRIDES : Literal[int] = 0x0080\n_FLAG_ALIGNED        : Literal[int] = 0x0100\n_FLAG_NOTSWAPPED     : Literal[int] = 0x0200\n_FLAG_WRITEABLE      : Literal[int] = 0x0400\n_FLAG_WRITEBACKIFCOPY: Literal[int] = 0x2000\n_FLAG_ENSURENOCOPY   : Literal[int] = 0x4000\n\n_FLAG_BEHAVED      : Literal[int] = (_FLAG_ALIGNED | _FLAG_WRITEABLE)\n_FLAG_BEHAVED_NS   : Literal[int] = (_FLAG_ALIGNED | _FLAG_WRITEABLE | _FLAG_NOTSWAPPED)\n_FLAG_CARRAY       : Literal[int] = (_FLAG_C_CONTIGUOUS | _FLAG_BEHAVED)\n_FLAG_CARRAY_RO    : Literal[int] = (_FLAG_C_CONTIGUOUS | _FLAG_ALIGNED)\n_FLAG_FARRAY       : Literal[int] = (_FLAG_F_CONTIGUOUS | _FLAG_BEHAVED)\n_FLAG_FARRAY_RO    : Literal[int] = (_FLAG_F_CONTIGUOUS | _FLAG_ALIGNED)\n_FLAG_DEFAULT      : Literal[int] = (_FLAG_CARRAY)\n_FLAG_IN_ARRAY     : Literal[int] = (_FLAG_CARRAY_RO)\n_FLAG_OUT_ARRAY    : Literal[int] = (_FLAG_CARRAY)\n_FLAG_INOUT_ARRAY  : Literal[int] = (_FLAG_CARRAY)\n_FLAG_INOUT_ARRAY2 : Literal[int] = (_FLAG_CARRAY | _FLAG_WRITEBACKIFCOPY)\n_FLAG_IN_FARRAY    : Literal[int] = (_FLAG_FARRAY_RO)\n_FLAG_OUT_FARRAY   : Literal[int] = (_FLAG_FARRAY)\n_FLAG_INOUT_FARRAY : Literal[int] = (_FLAG_FARRAY)\n_FLAG_INOUT_FARRAY2: Literal[int] = (_FLAG_FARRAY | _FLAG_WRITEBACKIFCOPY)\n_FLAG_UPDATE_ALL   : Literal[int] = (_FLAG_C_CONTIGUOUS | _FLAG_F_CONTIGUOUS | _FLAG_ALIGNED)\n\n@tuple\nclass flagsobj:\n    _flags: u32\n\n    def __new__(f: int):\n        return flagsobj(u32(f))\n\n    def __new__(ccontig: bool, fcontig: bool):\n        f = _FLAG_ALIGNED | _FLAG_WRITEABLE\n        if ccontig:\n            f |= _FLAG_C_CONTIGUOUS\n        if fcontig:\n            f |= _FLAG_F_CONTIGUOUS\n        return flagsobj(f)\n\n    def _with(self, f: int):\n        return flagsobj(self._flags | u32(f))\n\n    def _without(self, f: int):\n        return flagsobj(self._flags & ~u32(f))\n\n    def _unown(self):\n        return self._without(_FLAG_OWNDATA)\n\n    @property\n    def num(self):\n        return int(self._flags)\n\n    @property\n    def c_contiguous(self):\n        return bool(self._flags & u32(_FLAG_C_CONTIGUOUS))\n\n    @property\n    def f_contiguous(self):\n        return bool(self._flags & u32(_FLAG_F_CONTIGUOUS))\n\n    @property\n    def contiguous(self):\n        return self.c_contiguous or self.f_contiguous\n\n    @property\n    def owndata(self):\n        return bool(self._flags & u32(_FLAG_OWNDATA))\n\n    @property\n    def writeable(self):\n        return bool(self._flags & u32(_FLAG_WRITEABLE))\n\n    @property\n    def aligned(self):\n        return bool(self._flags & u32(_FLAG_ALIGNED))\n\n    @property\n    def writebackifcopy(self):\n        return bool(self._flags & u32(_FLAG_WRITEBACKIFCOPY))\n\n    def __str__(self):\n        return (f'  C_CONTIGUOUS : {self.c_contiguous}\\n'\n                f'  F_CONTIGUOUS : {self.f_contiguous}\\n'\n                f'  OWNDATA : {self.owndata}\\n'\n                f'  WRITEABLE : {self.writeable}\\n'\n                f'  ALIGNED : {self.aligned}\\n'\n                f'  WRITEBACKIFCOPY : {self.writebackifcopy}\\n')\n\nclass flatiter[A]:\n    base: A\n    index: int\n\n    def __init__(self, base: A):\n        self.base = base\n        self.index = 0\n\n    def _index_to_coords(self, index: int):\n        return util.index_to_coords(index, self.base.shape)\n\n    @property\n    def coords(self):\n        shape = self.base.shape\n\n        if static.len(shape) == 0:\n            return ()\n\n        if self.index >= self.base.size:\n            return (shape[0],) + (0,) * (static.len(shape) - 1)\n        else:\n            return self._index_to_coords(self.index)\n\n    def __iter__(self):\n        arr = self.base\n        limits = arr.shape\n        N: Literal[int] = static.len(limits)\n        curr = self.coords\n        s = Ptr[int](__ptr__(limits).as_byte())\n        p = Ptr[int](__ptr__(curr).as_byte())\n        limit = arr.size\n\n        while self.index < limit:\n            curr0 = curr\n            p[N - 1] += 1\n\n            for i in range(N - 1, -1, -1):\n                if p[i] >= s[i]:\n                    p[i] = 0\n                    p[max(i - 1, 0)] += 1\n                else:\n                    break\n\n            self.index += 1\n            yield arr._ptr(curr0)[0]\n\n    def _fix_index(self, index: int):\n        n = self.base.size\n        i = index\n        if index < 0:\n            index += n\n        if index < 0 or index >= n:\n            raise IndexError(f\"index {i} into flatiter is out of bounds for array of size {n}\")\n        return index\n\n    def __getitem__(self, index: int):\n        index = self._fix_index(index)\n        coord = self._index_to_coords(index)\n        self.index = 0\n        return self.base._ptr(coord)[0]\n\n    def __getitem__(self, s: slice):\n        base = self.base\n        dtype = base.dtype\n\n        start, stop, step, length = s.adjust_indices(self.base.size)\n        p = Ptr[dtype](length)\n        off = 0\n\n        for i in range(start, stop, step):\n            coord = self._index_to_coords(i)\n            p[off] = base._ptr(coord)[0]\n            off += 1\n\n        self.index = 0\n        return A(p)\n\n    def __setitem__(self, index: int, value):\n        base = self.base\n        dtype = base.dtype\n        index = self._fix_index(index)\n        coord = self._index_to_coords(index)\n        base._ptr(coord)[0] = util.cast(value, dtype)\n        self.index = 0\n\n    def __setitem__(self, s: slice, value):\n        base = self.base\n        dtype = base.dtype\n\n        start, stop, step, _ = s.adjust_indices(self.base.size)\n        off = 0\n\n        for i in range(start, stop, step):\n            coord = self._index_to_coords(i)\n            if hasattr(value, \"__getitem__\"):\n                base._ptr(coord)[0] = util.cast(value[off % len(value)], dtype)\n            else:\n                base._ptr(coord)[0] = util.cast(value, dtype)\n            off += 1\n\n        self.index = 0\n\n    def copy(self):\n        return self.base.flatten()\n\n@tuple\nclass _UnaryFunctor:\n    op: F\n    F: type\n\n    def __new__(op: F, F: type) -> _UnaryFunctor[F]:\n        return superf(op)\n\n    def __call__(self, y, x):\n        y[0] = self.op(x[0])\n\n@tuple\nclass _InplaceUnaryFunctor:\n    op: F\n    F: type\n\n    def __new__(op: F, F: type) -> _InplaceUnaryFunctor[F]:\n        return superf(op)\n\n    def __call__(self, x):\n        x[0] = self.op(x[0])\n\n@tuple\nclass _BinaryFunctor:\n    op: F\n    F: type\n    R1: type\n    R2: type\n\n    def __new__(op: F, R1: type, R2: type, F: type) -> _BinaryFunctor[F, R1, R2]:\n        return superf(op)\n\n    def __call__(self, z, x, y):\n        z[0] = self.op(util.cast(x[0], R1), util.cast(y[0], R2))\n\n@tuple\nclass _InplaceBinaryFunctor:\n    op: F\n    F: type\n\n    def __new__(op: F, F: type) -> _InplaceBinaryFunctor[F]:\n        return superf(op)\n\n    def __call__(self, x, y):\n        x[0] = self.op(x[0], util.cast(y[0], type(x[0])))\n\n@tuple\nclass _RightBinaryFunctor:\n    op: F\n    F: type\n    R1: type\n    R2: type\n\n    def __new__(op: F, R1: type, R2: type, F: type) -> _RightBinaryFunctor[F, R1, R2]:\n        return superf(op)\n\n    def __call__(self, z, x, y):\n        z[0] = self.op(util.cast(y[0], R2), util.cast(x[0], R1))\n\n@tuple\nclass _ScalarFunctor:\n    op: F\n    y: Y\n    F: type\n    Y: type\n    R1: type\n    R2: type\n\n    def __new__(op: F, y: Y, R1: type, R2: type, F: type, Y: type) -> _ScalarFunctor[F, Y, R1, R2]:\n        return superf(op, y)\n\n    def __call__(self, z, x):\n        z[0] = self.op(util.cast(x[0], R1), util.cast(self.y, R2))\n\n@tuple\nclass _InplaceScalarFunctor:\n    op: F\n    y: Y\n    F: type\n    Y: type\n\n    def __new__(op: F, y: Y, F: type, Y: type) -> _InplaceScalarFunctor[F, Y]:\n        return superf(op, y)\n\n    def __call__(self, x):\n        x[0] = self.op(x[0], util.cast(self.y, type(x[0])))\n\n\n@tuple\nclass _RightScalarFunctor:\n    op: F\n    y: Y\n    F: type\n    Y: type\n    R1: type\n    R2: type\n\n    def __new__(op: F, y: Y, R1: type, R2: type, F: type, Y: type) -> _RightScalarFunctor[F, Y, R1, R2]:\n        return superf(op, y)\n\n    def __call__(self, z, x):\n        z[0] = self.op(util.cast(self.y, R2), util.cast(x[0], R1))\n\n@tuple(init=False)\nclass ndarray[dtype, ndim: Literal[int]]:\n    _shape: Tuple[ndim, int]\n    _strides: Tuple[ndim, int]\n    _data: Ptr[dtype]\n\n    def __new__(shape: Tuple[ndim, int],\n                strides: Tuple[ndim, int],\n                data: Ptr[dtype]) -> ndarray[dtype, ndim]:\n        return type._force_value_cast((shape, strides, data), ndarray[dtype, ndim])\n\n    def __new__(shape: Tuple[ndim, int], data: Ptr[dtype], fcontig: bool = False):\n        strides = util.strides(shape, fcontig, dtype)\n        return ndarray(shape, strides, data)\n\n    @property\n    def _contig(self):\n        shape = self.shape\n        strides = self.strides\n        itemsize = self.itemsize\n        p_shape = Ptr[int](__ptr__(shape).as_byte())\n        p_strides = Ptr[int](__ptr__(strides).as_byte())\n\n        is_c_contig = True\n        sd = itemsize\n        for i in range(len(shape) - 1, -1, -1):\n            dim = p_shape[i]\n            if dim == 0:\n                return (True, True)\n            if dim != 1:\n                if p_strides[i] != sd:\n                    is_c_contig = False\n                sd *= dim\n\n        sd = itemsize\n        for i in range(len(shape)):\n            dim = p_shape[i]\n            if dim != 1:\n                if p_strides[i] != sd:\n                    return (is_c_contig, False)\n                sd *= dim\n\n        return (is_c_contig, True)\n\n    @property\n    def _is_contig(self):\n        c, f = self._contig\n        return c or f\n\n    def _contig_match(self, other):\n        if static.len(self.shape) != static.len(other.shape):\n            return False\n\n        if self.shape != other.shape:\n            return False\n\n        c1, f1 = self._contig\n        c2, f2 = other._contig\n        return (c1 and c2) or (f1 and f2)\n\n    @property\n    def shape(self):\n        return self._shape\n\n    @property\n    def strides(self):\n        return self._strides\n\n    @property\n    def flags(self):\n        return flagsobj(*self._contig)\n\n    @property\n    def data(self):\n        return self._data\n\n    @property\n    def size(self):\n        return util.count(self.shape)\n\n    @property\n    def itemsize(self):\n        return util.sizeof(dtype)\n\n    @property\n    def nbytes(self):\n        return self.size * self.itemsize\n\n    def item(self, *args):\n        n = self.size\n        if static.len(args) == 0:\n            if n != 1:\n                raise ValueError(\"can only convert an array of size 1 to a Python scalar\")\n            return self._data[0]\n        if static.len(args) == 1:\n            idx = args[0]\n            if idx < -n or idx >= n:\n                raise IndexError(f\"index {idx} is out of bounds for size {n}\")\n            if idx < 0:\n                idx += n\n            coords = util.index_to_coords(idx, self.shape)\n            return self._ptr(coords)[0]\n        else:\n            if static.len(args) != static.len(self.shape):\n                compile_error(\"incorrect number of indices for array\")\n            return self[args]\n\n    def transpose(self, *axes):\n        if static.len(axes) == 0:\n            return ndarray(self.shape[::-1], self.strides[::-1], self._data)\n        elif static.len(axes) == 1:\n            if isinstance(axes[0], Tuple):\n                return self.transpose(*axes[0])\n            elif axes[0] is None:\n                return self.transpose()\n        elif static.len(axes) != static.len(self.shape):\n            compile_error(\"axes don't match array\")\n\n        axes = tuple(util.normalize_axis_index(ax, self.ndim) for ax in axes)\n        if util.has_duplicate(axes):\n            raise ValueError(\"repeated axis in transpose\")\n\n        new_shape = tuple(self.shape[j] for j in axes)\n        new_strides = tuple(self.strides[j] for j in axes)\n        return ndarray(new_shape, new_strides, self._data)\n\n    def transpose(self, axes: List[int]):\n        if len(axes) != len(self.shape):\n            raise ValueError(\"axes don't match array\")\n\n        if util.has_duplicate(axes):\n            raise ValueError(\"repeated axis in transpose\")\n\n        ndim: Literal[int] = static.len(self.shape)\n        new_shape = tuple(self.shape[axes[i]] for i in static.range(ndim))\n        new_strides = tuple(self.strides[axes[i]] for i in static.range(ndim))\n        return ndarray(new_shape, new_strides, self._data)\n\n    def swapaxes(self, axis1: int, axis2: int):\n        axis1 = util.normalize_axis_index(axis1, self.ndim, 'axis1')\n        axis2 = util.normalize_axis_index(axis2, self.ndim, 'axis2')\n        new_shape = self.shape\n        new_strides = self.strides\n        p1 = Ptr[int](__ptr__(new_shape).as_byte())\n        p2 = Ptr[int](__ptr__(new_strides).as_byte())\n        p1[axis1], p1[axis2] = p1[axis2], p1[axis1]\n        p2[axis1], p2[axis2] = p2[axis2], p2[axis1]\n        return ndarray(new_shape, new_strides, self._data)\n\n    @property\n    def T(self):\n        return self.transpose()\n\n    def _fix_unknown_dimension(self, newshape):\n        def raise_reshape_size_mismatch(newshape, arr):\n            raise ValueError(f\"cannot reshape array of size {arr.size} into shape {newshape}\")\n\n        s_original = self.size\n\n        if static.len(newshape) == 0:\n            if s_original != 1:\n                raise_reshape_size_mismatch(newshape, self)\n            return newshape\n        elif static.len(newshape) == 1:\n            if newshape[0] < 0:\n                return (s_original,)\n            elif newshape[0] != s_original:\n                raise_reshape_size_mismatch(newshape, self)\n            else:\n                return newshape\n        else:\n            dimensions = Ptr[int](__ptr__(newshape).as_byte())\n            n = len(newshape)\n            s_known = 1\n            i_unknown = -1\n\n            for i in range(n):\n                dim = dimensions[i]\n                if dim < 0:\n                    if i_unknown == -1:\n                        i_unknown = i\n                    else:\n                        raise ValueError(\"can only specify one unknown dimension\")\n                else:\n                    s_known *= dim\n\n            if i_unknown >= 0:\n                if s_known == 0 or s_original % s_known != 0:\n                    raise_reshape_size_mismatch(newshape, self)\n                dimensions[i_unknown] = s_original // s_known\n            else:\n                if s_original != s_known:\n                    raise_reshape_size_mismatch(newshape, self)\n\n            return newshape\n\n    def _attempt_reshape_nocopy(self, newdims, is_f_order: bool):\n        shape = self.shape\n        strides = self.strides\n        oldims = shape\n        oldstrides = shape\n        newstrides = (0,) * static.len(newdims)\n\n        p_olddims = Ptr[int](__ptr__(oldims).as_byte())\n        p_oldstrides = Ptr[int](__ptr__(oldstrides).as_byte())\n        p_newdims = Ptr[int](__ptr__(newdims).as_byte())\n        p_newstrides = Ptr[int](__ptr__(newstrides).as_byte())\n\n        oldnd = 0\n\n        for oi in static.range(self.ndim):\n            if shape[oi] != 1:\n                p_olddims[oldnd] = shape[oi]\n                p_oldstrides[oldnd] = strides[oi]\n                oldnd += 1\n\n        oi = 0\n        oj = 1\n        ni = 0\n        nj = 1\n        newnd = len(newdims)\n\n        while ni < newnd and oi < oldnd:\n            np = p_newdims[ni]\n            op = p_olddims[oi]\n\n            while np != op:\n                if np < op:\n                    np *= p_newdims[nj]\n                    nj += 1\n                else:\n                    op *= p_olddims[oj]\n                    oj += 1\n\n            ok = oi\n            while ok < oj - 1:\n                if is_f_order:\n                    if p_oldstrides[ok + 1] != p_olddims[ok] * p_oldstrides[ok]:\n                        return False, newdims\n                else:\n                    if p_oldstrides[ok] != p_olddims[ok + 1] * p_oldstrides[ok + 1]:\n                        return False, newdims\n                ok += 1\n\n            if is_f_order:\n                p_newstrides[ni] = p_oldstrides[oi]\n                nk = ni + 1\n                while nk < nj:\n                    p_newstrides[nk] = p_newstrides[nk - 1] * p_newdims[nk - 1]\n                    nk += 1\n            else:\n                p_newstrides[nj - 1] = p_oldstrides[oj - 1]\n                nk = nj - 1\n                while nk > ni:\n                    p_newstrides[nk - 1] = p_newstrides[nk] * p_newdims[nk]\n                    nk -= 1\n\n            ni = nj\n            nj += 1\n            oi = oj\n            oj += 1\n\n        last_stride = 0\n        if ni >= 1:\n            last_stride = p_newstrides[ni - 1]\n        else:\n            last_stride = self.itemsize\n\n        if is_f_order:\n            last_stride *= p_newdims[ni - 1]\n\n        nk = ni\n        while nk < newnd:\n            p_newstrides[nk] = last_stride\n            nk += 1\n\n        return True, newstrides\n\n    def reshape(self, *shape, order: str = 'C'):\n        ndarray._check_order(order)\n\n        if static.len(shape) == 0:\n            a = self.size\n            if a != 1:\n                raise ValueError(f'cannot reshape array of size {a} into shape ()')\n            return ndarray((), (), self.data)\n\n        if static.len(shape) == 1 and isinstance(shape[0], Tuple):\n            return self.reshape(*shape[0])\n\n        ccontig, fcontig = self._contig\n\n        if order == 'A':\n            order = 'F' if (fcontig and not ccontig) else 'C'\n        elif order == 'K':\n            raise ValueError(\"order 'K' is not permitted for reshaping\")\n\n        if static.len(shape) == static.len(self.shape):\n            if shape == self.shape:\n                return self\n\n        shape = self._fix_unknown_dimension(shape)\n\n        if (order == 'C' and not ccontig) or (order == 'F' and not fcontig):\n            success, newstrides = self._attempt_reshape_nocopy(shape, (order == 'F'))\n            if success:\n                return ndarray(shape, newstrides, self._data)\n            else:\n                self = self.copy(order=order)\n\n        return ndarray(shape, self._data, fcontig=(order == 'F'))\n\n    def _loop(arrays, func, broadcast: Literal[str] = 'all',\n              check: Literal[bool] = True, alloc: type = type(()),\n              optimize_order: Literal[bool] = True, extra = None):\n        def call(func, args, extra):\n            if extra is None:\n                return func(*args)\n            else:\n                return func(*args, extra)\n\n        def loop(shape, strides, ptrs, func, extra):\n            def incr_ptr(p: Ptr[T], s: int, T: type):\n                return Ptr[T](p.as_byte() + s)\n\n            if static.len(shape) == 0:\n                call(func, ptrs, extra)\n            elif static.len(shape) == 1:\n                n = shape[0]\n\n                # Common cases are:\n                #   - len(ptrs) == 1 ; i.e. in-place unary operation\n                #   - len(ptrs) == 2 ; i.e. unary or in-place binary operation\n                #   - len(ptrs) == 3 ; i.e. binary operation\n                # We handle these specially so as to ensure auto-vectorization.\n\n                if static.len(ptrs) == 2:\n                    s0 = strides[0][0]\n                    s1 = strides[1][0]\n                    e0 = util.sizeof(type(ptrs[0][0]))\n                    e1 = util.sizeof(type(ptrs[1][0]))\n\n                    if s0 == e0 and s1 == e1:\n                        for i in range(n):\n                            call(func, (ptrs[0] + i, ptrs[1] + i), extra)\n                    elif s0 == e0 and s1 == 0:\n                        for i in range(n):\n                            p0 = incr_ptr(ptrs[0], i * s0)\n                            p1 = ptrs[1]\n                            call(func, (p0, p1), extra)\n                    else:\n                        for i in range(n):\n                            p0 = incr_ptr(ptrs[0], i * s0)\n                            p1 = incr_ptr(ptrs[1], i * s1)\n                            call(func, (p0, p1), extra)\n                elif static.len(ptrs) == 3:\n                    s0 = strides[0][0]\n                    s1 = strides[1][0]\n                    s2 = strides[2][0]\n                    e0 = util.sizeof(type(ptrs[0][0]))\n                    e1 = util.sizeof(type(ptrs[1][0]))\n                    e2 = util.sizeof(type(ptrs[2][0]))\n\n                    if s0 == e0 and s1 == e1 and s2 == e2:\n                        for i in range(n):\n                            call(func, (ptrs[0] + i, ptrs[1] + i, ptrs[2] + i), extra)\n                    elif s0 == e0 and s1 == 0 and s2 == e2:\n                        for i in range(n):\n                            p0 = incr_ptr(ptrs[0], i * s0)\n                            p1 = ptrs[1]\n                            p2 = incr_ptr(ptrs[2], i * s2)\n                            call(func, (p0, p1, p2), extra)\n                    elif s0 == e0 and s1 == e1 and s2 == 0:\n                        for i in range(n):\n                            p0 = incr_ptr(ptrs[0], i * s0)\n                            p1 = incr_ptr(ptrs[1], i * s1)\n                            p2 = ptrs[2]\n                            call(func, (p0, p1, p2), extra)\n                    else:\n                        for i in range(n):\n                            p0 = incr_ptr(ptrs[0], i * s0)\n                            p1 = incr_ptr(ptrs[1], i * s1)\n                            p2 = incr_ptr(ptrs[2], i * s2)\n                            call(func, (p0, p1, p2), extra)\n                else:\n                    for i in range(shape[0]):\n                        ptrs_i = tuple(incr_ptr(ptrs[j], i * strides[j][0])\n                                       for j in static.range(static.len(ptrs)))\n                        call(func, ptrs_i, extra)\n            else:\n                shape1 = shape[1:]\n                strides1 = tuple(x[1:] for x in strides)\n                for _ in range(shape[0]):\n                    loop(shape1, strides1, ptrs, func, extra)\n                    ptrs = tuple(incr_ptr(ptrs[i], strides[i][0])\n                                 for i in static.range(static.len(ptrs)))\n\n        def reorder_loops(strides):\n            if static.len(strides) == 0:\n                return ()\n\n            if static.len(strides) == 1:\n                return (0,)\n\n            if static.len(strides) == 2:\n                s0 = strides[0]\n                s1 = strides[1]\n                if s0 and abs(s0) < abs(s1):\n                    return (1, 0)\n                return (0, 1)\n\n            perm = util.tuple_range(static.len(strides))\n            perm, _ = util.sort_by_stride(perm, strides)\n            return perm\n\n        def broadcast_shapes(args, check: Literal[bool]):\n            def largest(args):\n                if static.len(args) == 1:\n                    return args[0]\n\n                a = args[0]\n                b = largest(args[1:])\n                if static.len(b) > static.len(a):\n                    return b\n                else:\n                    return a\n\n            if static.len(args) == 0:\n                return ()\n\n            t = largest(args)\n            N: Literal[int] = static.len(t)\n            ans = (0,) * N\n            p = Ptr[int](__ptr__(ans).as_byte())\n\n            for i in static.range(N):\n                p[i] = t[i]\n\n            for a in args:\n                for i in static.range(static.len(a)):\n                    x = a[len(a) - 1 - i]\n                    q = p + (len(t) - 1 - i)\n                    y = q[0]\n\n                    if y == 1:\n                        q[0] = x\n                    elif check and x != 1 and x != y:\n                        raise ValueError('shape mismatch: objects cannot be broadcast to a single shape')\n\n            return ans\n\n        def broadcast_to(x, shape, check: Literal[bool]):\n            N: Literal[int] = x.ndim\n            substrides = (0,) * N\n            p = Ptr[int](__ptr__(substrides).as_byte())\n            shape1, shape2 = shape[:-N], shape[-N:]\n\n            for i in static.range(N):\n                a = x.shape[i]\n                b = shape2[i]\n                if a == b:\n                    p[i] = x.strides[i]\n                else:\n                    if check:\n                        if a != 1:\n                            raise ValueError(f'cannot broadcast array of shape {x.shape} to shape {shape}')\n                    p[i] = 0\n\n            z = (0,) * (static.len(shape) - x.ndim)\n            new_strides = (*z, *substrides)\n            return ndarray(shape, new_strides, x.data)\n\n        def broadcast_arrays(arrays, check: Literal[bool]):\n            shape = broadcast_shapes(tuple(arr.shape for arr in arrays), check=check)\n            return tuple(broadcast_to(arr, shape, check=False) for arr in arrays)\n\n        def min_dim(arrays):\n            if static.len(arrays) == 0:\n                compile_error(\"[internal error] arrays empty\")\n            elif static.len(arrays) == 1:\n                return arrays[0]\n            else:\n                arrays0 = arrays[0]\n                arrays1 = min_dim(arrays[1:])\n                if arrays1.ndim < arrays0.ndim:\n                    return arrays1\n                else:\n                    return arrays0\n\n        def max_dim(arrays):\n            if static.len(arrays) == 0:\n                compile_error(\"[internal error] arrays empty\")\n            elif static.len(arrays) == 1:\n                return arrays[0]\n            else:\n                arrays0 = arrays[0]\n                arrays1 = max_dim(arrays[1:])\n                if arrays1.ndim > arrays0.ndim:\n                    return arrays1\n                else:\n                    return arrays0\n\n        def all_contiguous(arrays):\n            min_arr = min_dim(arrays)\n            max_arr = max_dim(arrays)\n            if min_arr.ndim == max_arr.ndim:\n                sh = True\n                cc = True\n                fc = True\n\n                for i in static.range(static.len(arrays)):\n                    arr = arrays[i]\n                    if i > 0:\n                        sh = sh and (arr.shape == arrays[0].shape)\n                    cc1, fc1 = arr._contig\n                    cc = cc and cc1\n                    fc = fc and fc1\n\n                return sh and (cc or fc), cc\n            else:\n                return False, False\n\n        def alloc_array(count, perm_shape, dtype: type):\n            p = Ptr[dtype](count)\n            strides = ndarray(perm_shape, p).strides\n            return (p, strides)\n\n        def broadcast_args(arrays, broadcast: Literal[str], check: Literal[bool]):\n            if broadcast == 'none':\n                shape = arrays[0].shape\n                strides = tuple(arr.strides for arr in arrays)\n            elif broadcast == 'first':\n                shape = arrays[0].shape\n                arrays1 = arrays[:1] + tuple(broadcast_to(arr, shape, check=check) for arr in arrays[1:])\n                strides = tuple(arr.strides for arr in arrays1)\n            elif broadcast == 'all':\n                arrays1 = broadcast_arrays(arrays, check=check)\n                shape = arrays1[0].shape\n                strides = tuple(arr.strides for arr in arrays1)\n            else:\n                compile_error(\"'broadcast' argument must be 'none', 'first' or 'all'\")\n\n            return shape, strides\n\n        if static.len(arrays) == 0:\n            return\n\n        all_contig, ccontig = all_contiguous(arrays)\n        min_arr = min_dim(arrays)\n        max_arr = max_dim(arrays)\n\n        if min_arr.ndim == max_arr.ndim:\n            if all_contig:\n                shape = arrays[0].shape\n                strides = tuple(arr.strides for arr in arrays)\n            else:\n                shape, strides = broadcast_args(arrays, broadcast, check)\n        else:\n            shape, strides = broadcast_args(arrays, broadcast, check)\n\n        alloc_tuple = util.zero(alloc)\n\n        if static.len(alloc_tuple) > 0:\n            if optimize_order:\n                perm0 = reorder_loops(max_arr.strides)\n            else:\n                perm0 = None\n            count = util.count(shape)\n            perm_shape = util.tuple_perm(shape, perm0)\n            # `allocated` is a tuple of (ptr, strides) pairs\n            allocated = tuple(alloc_array(count, perm_shape, type(alloc_tuple[i]))\n                              for i in static.range(static.len(alloc_tuple)))\n        else:\n            perm0 = None\n            allocated = ()\n\n        if all_contig:\n            for i in range(arrays[0].size):\n                call(func,\n                     tuple(tup[0] + i for tup in allocated) + tuple(arr.data + i for arr in arrays),\n                     extra)\n            return tuple(ndarray(shape, tup[0], fcontig=(not ccontig)) for tup in allocated)\n\n        shape0 = shape\n\n        if optimize_order:\n            if perm0 is None:\n                perm = reorder_loops(max_arr.strides)\n            else:\n                perm = perm0\n            shape = util.tuple_perm(shape, perm)\n            strides = tuple(util.tuple_perm(s, perm) for s in strides)\n        else:\n            perm = None\n\n        loop(shape,\n             tuple(tup[1] for tup in allocated) + strides,\n             tuple(tup[0] for tup in allocated) + tuple(arr.data for arr in arrays),\n             func,\n             extra)\n\n        if perm is not None and static.len(allocated) > 0 and static.len(shape) >= 2:\n            # permute the strides\n            if static.len(shape) == 2:\n                rev = (perm[0] == 1)\n                return tuple(ndarray(shape0, tup[1][::-1] if rev else tup[1], tup[0]) for tup in allocated)\n            else:\n                iperm = util.tuple_perm_inv(perm)\n                return tuple(ndarray(shape0, util.tuple_perm(tup[1], iperm), tup[0]) for tup in allocated)\n        else:\n            return tuple(ndarray(shape0, tup[1], tup[0]) for tup in allocated)\n\n    def _contiguous(self, copy: Literal[bool] = False):\n        ccontig, _ = self._contig\n        if ccontig:\n            if copy:\n                n = self.size\n                p = Ptr[dtype](n)\n                str.memcpy(p.as_byte(), self._data.as_byte(), n * self.itemsize)\n                return p\n            else:\n                return self._data\n        else:\n            n = self.size\n            p = Ptr[dtype](n)\n            i = 0\n            for idx in util.multirange(self.shape):\n                q = self._ptr(idx)\n                p[i] = q[0]\n                i += 1\n            return p\n\n    def _fcontiguous(self, copy: Literal[bool] = False):\n        _, fcontig = self._contig\n        if fcontig:\n            if copy:\n                n = self.size\n                p = Ptr[dtype](n)\n                str.memcpy(p.as_byte(), self._data.as_byte(), n * self.itemsize)\n                return p\n            else:\n                return self._data\n        else:\n            n = self.size\n            p = Ptr[dtype](n)\n            i = 0\n            for idx in util.fmultirange(self.shape):\n                q = self._ptr(idx)\n                p[i] = q[0]\n                i += 1\n            return p\n\n    def tobytes(self, order: str = 'C'):\n        ndarray._check_order(order)\n        ccontig, fcontig = self._contig\n\n        if order == 'A':\n            order = 'F' if fcontig and not ccontig else 'C'\n\n        n = self.size\n        p = Ptr[dtype](n)\n\n        if (order == 'C' and ccontig) or (order == 'F' and fcontig):\n            str.memcpy(p.as_byte(), self._data.as_byte(), n * self.itemsize)\n        elif order == 'F':\n            i = 0\n            for idx in util.fmultirange(self.shape):\n                p[i] = self._ptr(idx)[0]\n                i += 1\n        else:\n            i = 0\n            for idx in util.multirange(self.shape):\n                p[i] = self._ptr(idx)[0]\n                i += 1\n\n        return str(p.as_byte(), n * self.itemsize)\n\n    def ravel(self, order: str = 'C'):\n        ndarray._check_order(order)\n        ccontig, fcontig = self._contig\n\n        if order == 'A':\n            order = 'F' if fcontig else 'C'\n\n        if order == 'C':\n            if ccontig:\n                return ndarray((self.size,), self._data)\n            else:\n                return ndarray((self.size,), self._contiguous())\n        elif order == 'F':\n            if fcontig:\n                return ndarray((self.size,), self._data)\n            else:\n                return ndarray((self.size,), self._fcontiguous())\n        else:\n            shape_sorted, strides_sorted = util.sort_by_stride(self.shape, self.strides)\n            other = ndarray(shape_sorted, strides_sorted, self._data)\n            return other.flatten()\n\n    def flatten(self, order: str = 'C'):\n        ndarray._check_order(order)\n        ccontig, fcontig = self._contig\n\n        if order == 'A':\n            order = 'F' if fcontig else 'C'\n\n        if order == 'C':\n            return ndarray((self.size,), self._contiguous(copy=True))\n        elif order == 'F':\n            return ndarray((self.size,), self._fcontiguous(copy=True))\n        else:\n            shape_sorted, strides_sorted = util.sort_by_stride(self.shape, self.strides)\n            other = ndarray(shape_sorted, strides_sorted, self._data)\n            return other.flatten()\n\n    @property\n    def flat(self):\n        return flatiter(self)\n\n    @flat.setter\n    def flat(self, value):\n        self.flat[:] = value\n\n    def tolist(self):\n        if static.len(self.shape) == 0:\n            return List[dtype]()\n        elif static.len(self.shape) == 1:\n            return [a for a in self]\n        else:\n            return [a.tolist() for a in self]\n\n    def _ptr_for_index(self, indexes, check: Literal[bool] = True, broadcast: Literal[bool] = False):\n        s = self.shape\n        strides = self.strides\n        pshape = Ptr[int](__ptr__(s).as_byte())\n        pindex = Ptr[int](__ptr__(indexes).as_byte())\n        pstride = Ptr[int](__ptr__(strides).as_byte())\n\n        offset = 0\n        for i in range(len(indexes)):\n            idx = pindex[i]\n\n            if static.len(indexes) > static.len(self.shape):\n                if not broadcast:\n                    compile_error(\"[internal error] index tuple too long\")\n                i -= static.len(indexes) - static.len(self.shape)\n                if i < 0:\n                    continue\n\n            n = pshape[i]\n            if broadcast:\n                if n == 1:\n                    continue\n            if check:\n                idx = util.normalize_index(idx, i, n)\n            offset += idx * pstride[i]\n\n        return Ptr[dtype](self._data.as_byte() + offset)\n\n    def _ptr(self, indexes, broadcast: Literal[bool] = False):\n        return self._ptr_for_index(indexes, check=False, broadcast=broadcast)\n\n    def __len__(self):\n        if static.len(self.shape) == 0:\n            compile_error(\"len() of unsized object\")\n        return self.shape[0]\n\n    def __iter__(self):\n        for i in range(self.shape[0]):\n            yield self[i]\n\n    def _check_order(order: str):\n        if order not in ('C', 'F', 'A', 'K'):\n            raise ValueError(f\"order must be one of 'C', 'F', 'A', or 'K' (got '{order}')\")\n\n    def astype(self, dtype: type, order: str = 'K', copy: bool = True):\n        ndarray._check_order(order)\n        cc, fc = self._contig\n\n        if dtype is self.dtype:\n            x = self\n            if copy or (order == 'C' and not cc) or (order == 'F' and not fc):\n                a = self._data\n                n = self.size\n                b = Ptr[dtype](n)\n\n                if ((order == 'C' and cc) or (order == 'F' and fc)):\n                    f = fc and not cc\n                    str.memcpy(b.as_byte(), a.as_byte(), n * self.itemsize)\n                    x = ndarray(self.shape, b, fcontig=f)\n                else:\n                    f = False\n                    if order == 'F':\n                        f = True\n                    elif order == 'A' or order == 'K':\n                        f = fc\n\n                    x = ndarray(self.shape, b, fcontig=f)\n                    for idx in util.multirange(self.shape):\n                        p = self._ptr(idx)\n                        q = x._ptr(idx)\n                        q[0] = p[0]\n            return x\n\n        a = self._data\n        n = self.size\n        b = Ptr[dtype](n)\n\n        f = False\n        if order == 'F':\n            f = True\n        elif order == 'A' or order == 'K':\n            f = fc\n\n        other = ndarray(self.shape, b, fcontig=f)\n        for idx in util.multirange(self.shape):\n            p = self._ptr(idx)\n            q = other._ptr(idx)\n            q[0] = util.cast(p[0], dtype)\n        return other\n\n    def copy(self, order: str = 'C'):\n        return self.astype(dtype=dtype, order=order, copy=True)\n\n    def __copy__(self):\n        return self.copy()\n\n    def _should_transpose(self, other = None) -> bool:\n        if other is None:\n            if self.ndim > 1:\n                s1 = self.strides[0]\n                s2 = self.strides[-1]\n                return s1 != 0 and abs(s1) < abs(s2)\n            else:\n                return False\n        else:\n            if self.ndim > 1 and other.ndim > 1:\n                sa1 = self.strides[0]\n                sa2 = self.strides[-1]\n                sb1 = other.strides[0]\n                sb2 = other.strides[-1]\n                return sa1 != 0 and sb1 != 0 and abs(sa1) < abs(sa2) and abs(sb1) < abs(sb2)\n            elif self.ndim > 1:\n                s1 = self.strides[0]\n                s2 = self.strides[-1]\n                return s1 != 0 and abs(s1) < abs(s2)\n            elif other.ndim > 1:\n                s1 = other.strides[0]\n                s2 = other.strides[-1]\n                return s1 != 0 and abs(s1) < abs(s2)\n            else:\n                return False\n\n    def _normalize(self, other: ndarray):\n        if self.ndim > other.ndim:\n            diff: Literal[int] = self.ndim - other.ndim\n            A = self\n            B = ndarray((1,) * diff + other.shape, (0,) * diff + other.strides, other.data)\n        elif self.ndim < other.ndim:\n            diff: Literal[int] = other.ndim - self.ndim\n            A = ndarray((1,) * diff + self.shape, (0,) * diff + self.strides, self.data)\n            B = other\n        else:\n            A = self\n            B = other\n\n        return A, B\n\n    def _op_elemwise(self, other: ndarray, op):\n        dtype1 = self.dtype\n        dtype2 = other.dtype\n        r1, r2 = util.op_types(dtype1, dtype2)\n        R1 = type(r1)\n        R2 = type(r2)\n        T = type(op(util.cast(self.data[0], R1),\n                    util.cast(other.data[0], R2)))\n        return ndarray._loop((self, other),\n                             _BinaryFunctor(op=op, R1=R1, R2=R2),\n                             alloc=Tuple[T])[0]\n\n    def _rop_elemwise(self, other: ndarray, op):\n        dtype1 = self.dtype\n        dtype2 = other.dtype\n        r1, r2 = util.op_types(dtype1, dtype2)\n        R1 = type(r1)\n        R2 = type(r2)\n        T = type(op(util.cast(self.data[0], R1),\n                    util.cast(other.data[0], R2)))\n        return ndarray._loop((self, other),\n                             _RightBinaryFunctor(op=op, R1=R1, R2=R2),\n                             alloc=Tuple[T])[0]\n\n    def _op_scalar(self, b, op):\n        dtype1 = self.dtype\n        dtype2 = type(b)\n        r1, r2 = util.op_types(dtype1, dtype2)\n        R1 = type(r1)\n        R2 = type(r2)\n        T = type(op(util.cast(self.data[0], R1),\n                    util.cast(b, R2)))\n        return ndarray._loop((self,),\n                             _ScalarFunctor(op=op, y=b, R1=R1, R2=R2),\n                             alloc=Tuple[T])[0]\n\n    def _iop_elemwise(self, other: ndarray, op):\n        ndarray._loop((self, other), _InplaceBinaryFunctor(op))\n        return self\n\n    def _iop_scalar(self, b, op):\n        ndarray._loop((self,), _InplaceScalarFunctor(op=op, y=b))\n        return self\n\n    def _rop_scalar(self, b, op):\n        dtype1 = self.dtype\n        dtype2 = type(b)\n        r1, r2 = util.op_types(dtype1, dtype2)\n        R1 = type(r1)\n        R2 = type(r2)\n        T = type(op(util.cast(self.data[0], R1),\n                    util.cast(b, R2)))\n        return ndarray._loop((self,),\n                             _RightScalarFunctor(op=op, y=b, R1=R1, R2=R2),\n                             alloc=Tuple[T])[0]\n\n    def _op_unary(self, op):\n        T = type(op(self.data[0]))\n        return ndarray._loop((self,), _UnaryFunctor(op), alloc=Tuple[T])[0]\n\n    def _iop_unary(self, op):\n        ndarray._loop((self,), _InplaceUnaryFunctor(op))\n        return self\n\n    def _any(self, cond):\n        n = self.size\n        a = self._data\n\n        if self._is_contig:\n            for i in range(n):\n                if cond(a[i]):\n                    return True\n        else:\n            A = self.T if self._should_transpose() else self\n            for idx in util.multirange(A.shape):\n                if cond(A._ptr(idx)[0]):\n                    return True\n\n        return False\n\n    def _all(self, cond):\n        n = self.size\n        a = self._data\n\n        if self._is_contig:\n            for i in range(n):\n                if not cond(a[i]):\n                    return False\n        else:\n            A = self.T if self._should_transpose() else self\n            for idx in util.multirange(A.shape):\n                if not cond(A._ptr(idx)[0]):\n                    return False\n\n        return False\n\n    def _minmax(self):\n        n = self.size\n        a = self._data\n\n        if n == 0:\n            return util.zero(dtype), util.zero(dtype)\n\n        M = a[0]\n        m = a[0]\n\n        if self._is_contig:\n            for i in range(1, n):\n                e = a[i]\n                if e > M:\n                    M = e\n                if e < m:\n                    m = e\n        else:\n            A = self.T if self._should_transpose() else self\n            for idx in util.multirange(A.shape):\n                e = A._ptr(idx)[0]\n                if e > M:\n                    M = e\n                if e < m:\n                    m = e\n\n        return m, M\n\n    def map(self, fn, inplace: Literal[bool] = False):\n        if inplace:\n            return self._iop_unary(fn)\n        else:\n            return self._op_unary(fn)\n\n    def fill(self, value):\n        value = util.cast(value, dtype)\n        self.map(lambda x: value, inplace=True)\n\n    def _size1_error():\n        raise ValueError(\"only size-1 arrays can be converted to scalars\")\n\n    def __int__(self):\n        if self.size != 1:\n            ndarray._size1_error()\n        return int(self._data[0])\n\n    def __float__(self):\n        if self.size != 1:\n            ndarray._size1_error()\n        return float(self._data[0])\n\n    def __complex__(self):\n        if self.size != 1:\n            ndarray._size1_error()\n        return complex(self._data[0])\n\n    def __bool__(self):\n        if self.size != 1:\n            raise ValueError(\"The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()\")\n        return bool(self._data[0])\n\n    def view(self, dtype: type):\n        my_size = self.itemsize\n        dt_size = util.sizeof(dtype)\n        new_shape = self.shape\n        new_strides = self.strides\n        new_data = Ptr[dtype](self.data.as_byte())\n\n        if my_size != dt_size:\n            if self.ndim == 0:\n                raise ValueError(\"Changing the dtype of a 0d array is only supported if the itemsize is unchanged\")\n            elif self.shape[-1] != 1 and self.size != 0 and self.strides[-1] != self.itemsize:\n                raise ValueError(\"To change to a dtype of a different size, the last axis must be contiguous\")\n            elif my_size > dt_size:\n                if dt_size == 0 or my_size % dt_size != 0:\n                    raise ValueError(\"When changing to a smaller dtype, its size must be a divisor of the size of original dtype\")\n                newdim = my_size // dt_size\n                new_shape = new_shape[:-1] + (new_shape[-1] * newdim,)\n                new_strides = new_strides[:-1] + (dt_size,)\n            elif my_size < dt_size:\n                newdim = self.shape[-1] * my_size\n                if newdim % dt_size != 0:\n                    raise ValueError(\"When changing to a larger dtype, its size must be a \"\n                                     \"divisor of the total size in bytes of the last axis \"\n                                     \"of the array.\")\n                new_shape = new_shape[:-1] + (newdim // dt_size,)\n                new_strides = new_strides[:-1] + (dt_size,)\n\n        return ndarray(new_shape, new_strides, new_data)\n\n    def byteswap(self, inplace: bool = False):\n        def bswap(x: T, T: type):\n            if T is int or T is byte or isinstance(T, Int) or isinstance(T, UInt):\n                return util.bswap(x)\n\n            if T is float:\n                return util.bitcast(util.bswap(util.bitcast(x, u64)), float)\n\n            if T is float32:\n                return util.bitcast(util.bswap(util.bitcast(x, u32)), float32)\n\n            if T is complex or T is complex64:\n                return T(bswap(x.real), bswap(x.imag))\n\n            if not util.atomic(T):\n                return x\n\n            y = x\n            p = __ptr__(y).as_byte()\n            n = util.sizeof(T)\n            q = p + (n - 1)\n\n            while p < q:\n                p[0], q[0] = q[0], p[0]\n                p += 1\n                q -= 1\n\n            return y\n\n        if inplace:\n            return self.map(bswap, inplace=True)\n        else:\n            return self.map(bswap, inplace=False)\n\n    def _ptr_flat(self, idx: int, check: Literal[bool]):\n        if check:\n            n = self.size\n            if idx < -n or idx >= n:\n                raise IndexError(f\"index {idx} is out of bounds for size {n}\")\n            if idx < 0:\n                idx += n\n        return self._ptr(util.index_to_coords(idx, self.shape))\n\n    def _get_flat(self, idx: int, check: Literal[bool]):\n        return self._ptr_flat(idx, check=check)[0]\n\n    def _set_flat(self, idx: int, val, check: Literal[bool]):\n        self._ptr_flat(idx, check=check)[0] = util.cast(val, dtype)\n"
  },
  {
    "path": "stdlib/numpy/ndgpu.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.gpu as gpu\nimport util\nfrom .ndarray import ndarray\nfrom .ufunc import UnaryUFunc, UnaryUFunc2, BinaryUFunc\n\n@extend\nclass ndarray:\n    def __to_gpu__(self, cache: gpu.AllocCache):\n        if self._is_contig:\n            data_gpu = gpu._ptr_to_gpu(self.data, self.size, cache)\n            return ndarray(self.shape, self.strides, data_gpu)\n        else:\n            n = self.size\n            p = Ptr[dtype](n)\n            i = 0\n            for idx in util.multirange(self.shape):\n                p[i] = self._ptr(idx)[0]\n                i += 1\n            data_gpu = gpu._ptr_to_gpu(p, self.size, cache)\n            util.free(p)\n            return ndarray(self.shape, data_gpu)\n\n    def __from_gpu__(self, other: ndarray[dtype, ndim]):\n        if self._is_contig:\n            gpu._ptr_from_gpu(self.data, other.data, self.size)\n        else:\n            for idx in util.multirange(self.shape):\n                gpu._ptr_from_gpu(self._ptr(idx), other._ptr(idx), 1)\n\n    def __from_gpu_new__(other: ndarray[dtype, ndim]):\n        data = Ptr[dtype](other.size)\n        if other._is_contig:\n            gpu._ptr_from_gpu(data, other.data, other.size)\n            return ndarray(other.shape, other.strides, data)\n        else:\n            i = 0\n            for idx in util.multirange(other.shape):\n                gpu._ptr_from_gpu(data + i, other._ptr(idx), 1)\n                i += 1\n            return ndarray(other.shape, data)\n\n@extend\nclass UnaryUFunc:\n    def __to_gpu__(self, cache: gpu.AllocCache):\n        return self\n\n    def __from_gpu__(self, other: UnaryUFunc):\n        pass\n\n    def __from_gpu_new__(other: UnaryUFunc):\n        return other\n\n@extend\nclass UnaryUFunc2:\n    def __to_gpu__(self, cache: gpu.AllocCache):\n        return self\n\n    def __from_gpu__(self, other: UnaryUFunc2):\n        pass\n\n    def __from_gpu_new__(other: UnaryUFunc2):\n        return other\n\n@extend\nclass BinaryUFunc:\n    def __to_gpu__(self, cache: gpu.AllocCache):\n        return self\n\n    def __from_gpu__(self, other: BinaryUFunc):\n        pass\n\n    def __from_gpu_new__(other: BinaryUFunc):\n        return other\n"
  },
  {
    "path": "stdlib/numpy/ndmath.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport operator\nimport util\nimport zmath\nimport internal.static as static\n\nfrom .ndarray import ndarray, flagsobj, newaxis\nfrom .routines import _check_out, asarray, empty_like, empty, concatenate, moveaxis, broadcast_shapes, zeros, broadcast_to, atleast_1d\nfrom .ufunc import UnaryUFunc, UnaryUFunc2, BinaryUFunc\nfrom .npdatetime import datetime64, timedelta64\nfrom .const import pi\n\ndef cumprod(a, axis=None, dtype: type = NoneType, out=None):\n    a = asarray(a)\n\n    if dtype is NoneType:\n        return cumprod(a, axis=axis, dtype=a.dtype, out=out)\n\n    if axis is None:\n        prod = util.cast(1, dtype)\n        if out is None:\n            result = empty((a.size, ), dtype=dtype)\n        else:\n            _check_out(out, (a.size, ))\n            result = out\n        i = 0\n        for idx in util.multirange(a.shape):\n            prod *= util.cast(a._ptr(idx)[0], dtype)\n            result._ptr((i, ))[0] = util.cast(prod, result.dtype)\n            i += 1\n        return result\n    else:\n        axis = util.normalize_axis_index(axis, a.ndim)\n\n    if out is None:\n        result = empty_like(a, dtype=dtype)\n    else:\n        _check_out(out, a.shape)\n        result = out\n\n    n = a.shape[axis]\n\n    for idx in util.multirange(util.tuple_delete(a.shape, axis)):\n        accum = util.cast(1, dtype)\n\n        for i in range(n):\n            full_idx = util.tuple_insert(idx, axis, i)\n            p = a._ptr(full_idx)\n            q = result._ptr(full_idx)\n            accum *= util.cast(p[0], dtype)\n            q[0] = util.cast(accum, result.dtype)\n\n    return result\n\ndef cumsum(a, axis=None, dtype: type = NoneType, out=None):\n    a = asarray(a)\n\n    if dtype is NoneType:\n        return cumsum(a, axis=axis, dtype=a.dtype, out=out)\n\n    if axis is None:\n        sum = util.cast(0, dtype)\n        if out is None:\n            result = empty((a.size, ), dtype=dtype)\n        else:\n            _check_out(out, (a.size, ))\n            result = out\n\n        i = 0\n        for idx in util.multirange(a.shape):\n            sum += util.cast(a._ptr(idx)[0], dtype)\n            result._ptr((i, ))[0] = util.cast(sum, result.dtype)\n            i += 1\n        return result\n\n    else:\n        axis = util.normalize_axis_index(axis, a.ndim)\n\n    if out is None:\n        result = empty_like(a, dtype=dtype)\n    else:\n        _check_out(out, a.shape)\n        result = out\n\n    n = a.shape[axis]\n\n    for idx in util.multirange(util.tuple_delete(a.shape, axis)):\n        accum = util.cast(0, dtype)\n\n        for i in range(n):\n            full_idx = util.tuple_insert(idx, axis, i)\n            p = a._ptr(full_idx)\n            q = result._ptr(full_idx)\n            accum += util.cast(p[0], dtype)\n            q[0] = util.cast(accum, result.dtype)\n\n    return result\n\ndef diff(a, axis: int = -1, n: int = 1, append=None, prepend=None):\n    a = asarray(a)\n\n    if append is None and prepend is None:\n        # If neither append nor prepend is provided, return a\n        pass\n    elif append is not None and prepend is None:\n        # If only append is provided, perform the append operation\n        a = append(a, append, axis=axis)\n    elif append is None and prepend is not None:\n        # If only prepend is provided, perform the prepend operation\n        a = append(prepend, a, axis=axis)\n    else:\n        # If both append and prepend are provided, concatenate them along the specified axis\n        a = concatenate((prepend, a, append), axis=axis)\n\n    if isinstance(a.dtype, datetime64):\n        g = a.view(timedelta64[a.dtype.base, a.dtype.num])\n        return diff(g, axis=axis, n=n, append=None, prepend=None)\n\n    axis = util.normalize_axis_index(axis, a.ndim)\n    axis_len = a.shape[axis]\n\n    new_shape = a.shape\n    value = new_shape[axis] - n\n\n    value = max(value,0)\n\n    new_shape = util.tuple_set(new_shape, axis, value)\n    dtype = a.dtype\n\n    result = empty(new_shape, dtype=dtype)\n\n    if result.size == 0:\n        return result\n\n    if n == 1:\n        # Iterate over all indices except the specified axis\n        for idx0 in util.multirange(util.tuple_delete(a.shape, axis)):\n\n            for i in range(axis_len - 1):\n                idx = util.tuple_insert(idx0, axis, i)\n                x0 = a._ptr(idx)[0]\n\n                idx1 = util.tuple_insert(idx0, axis, i + 1)\n                x1 = a._ptr(idx1)[0]\n\n                result._ptr(idx)[0] = (x1 - x0)\n\n        return result\n\n    buf = Ptr[a.dtype](axis_len)\n\n    # Iterate over all indices except the specified axis\n    for idx0 in util.multirange(util.tuple_delete(a.shape, axis)):\n        for i in range(axis_len):\n            idx = util.tuple_insert(idx0, axis, i)\n            e = a._ptr(idx)[0]\n            buf[i] = e\n\n        # Compute differences in-place on buf\n        for i in range(n):\n            for j in range(axis_len - i - 1):\n                buf[j] = buf[j + 1] - buf[j]\n\n        for i in range(axis_len - n):\n            idx = util.tuple_insert(idx0, axis, i)\n            result._ptr(idx)[0] = buf[i]\n\n    util.free(buf)\n    return result\n\ndef _gradient_helper_scaler(f, dx, dtype: type, axis: int, edge_order: int):\n    if isinstance(dx, datetime64):\n        compile_error(\"spacing datatype cannot be of type datetime\")\n\n    if not (isinstance(f.dtype, timedelta64) or isinstance(dx, timedelta64)):\n        vtype = type(util.coerce(dtype, type(dx)))\n        if isinstance(dx, vtype):\n            dxx = util.cast(dx, dtype)\n        else:\n            dxx = util.cast(dx, vtype)\n        p = util.cast(-1.5, dtype)\n        q = util.cast(2.0, dtype)\n        r = util.cast(-0.5, dtype)\n    else:\n        dxx = util.cast(dx, float)\n        p = util.cast(-1.5, float)\n        q = util.cast(2.0, float)\n        r = util.cast(-0.5, float)\n\n    if f.shape[axis] < edge_order + 1:\n        raise ValueError(\n            \"Shape of array too small to calculate a numerical gradient, \"\n            \"at least (edge_order + 1) elements are required.\")\n\n    axis_len = f.shape[axis]\n    result = empty(f.shape, dtype=dtype)\n    for idx0 in util.multirange(util.tuple_delete(f.shape, axis)):\n        if edge_order == 1:\n            #for the first element\n            idx = util.tuple_insert(idx0, axis, 0)\n            x0 = util.cast(f._ptr(idx)[0], dtype)\n            idx2 = util.tuple_insert(idx0, axis, 1)\n            x1 = util.cast(f._ptr(idx2)[0], dtype)\n            result._ptr(idx)[0] = util.cast((x1 - x0) / dxx, dtype)\n\n            #for the last element\n            idx = util.tuple_insert(idx0, axis, axis_len - 1)\n            x0 = util.cast(f._ptr(idx)[0], dtype)\n            idx2 = util.tuple_insert(idx0, axis, axis_len - 2)\n            x1 = util.cast(f._ptr(idx2)[0], dtype)\n            result._ptr(idx)[0] = util.cast((x0 - x1) / dxx, dtype)\n        else:\n            #for the first element\n            idx = util.tuple_insert(idx0, axis, 0)\n            x0 = util.cast(f._ptr(idx)[0], dtype)\n            idx2 = util.tuple_insert(idx0, axis, 1)\n            x1 = util.cast(f._ptr(idx2)[0], dtype)\n            idx3 = util.tuple_insert(idx0, axis, 2)\n            x2 = util.cast(f._ptr(idx3)[0], dtype)\n            d = p / dxx\n            b = q / dxx\n            c = r / dxx\n            result._ptr(idx)[0] = util.cast(d * x0 + b * x1 + c * x2, dtype)\n\n            #for the last element\n            idx = util.tuple_insert(idx0, axis, axis_len - 1)\n            x0 = util.cast(f._ptr(idx)[0], dtype)\n            idx2 = util.tuple_insert(idx0, axis, axis_len - 2)\n            x1 = util.cast(f._ptr(idx2)[0], dtype)\n            idx3 = util.tuple_insert(idx0, axis, axis_len - 3)\n            x2 = util.cast(f._ptr(idx3)[0], dtype)\n            result._ptr(idx)[0] = util.cast((-c) * x2 + (-b) * x1 + (-d) * x0,\n                                            dtype)\n\n        for i in range(0, axis_len - 2):\n            idx = util.tuple_insert(idx0, axis, i + 1)\n            idx1 = util.tuple_insert(idx0, axis, i)\n            x0 = util.cast(f._ptr(idx1)[0], dtype)\n            idx2 = util.tuple_insert(idx0, axis, i + 2)\n            x1 = util.cast(f._ptr(idx2)[0], dtype)\n            result._ptr(idx)[0] = util.cast(\n                (x1 - x0) / (util.cast(2, dtype) * dxx), dtype)\n\n    return result\n\ndef _gradient_helper_linear(f, dx, dtype: type, axis: int, edge_order: int):\n    if isinstance(dx, ndarray):\n        if dx.ndim == 0:\n            return _gradient_helper_scaler(f, dx.item(), dtype, axis,\n                                           edge_order)\n    dx = asarray(dx)\n\n    if isinstance(dx.dtype, datetime64):\n        compile_error(\"spacing datatype cannot be of type datetime\")\n\n    if not (isinstance(f.dtype, timedelta64)\n            or isinstance(dx.dtype, timedelta64)):\n        vtype = type(util.coerce(dtype, dx.dtype))\n        if isinstance(vtype, dx.dtype):\n            dxx = dx.astype(dtype)\n        else:\n            dxx = dx.astype(vtype)\n        const = util.cast(2, dtype)\n    else:\n        dxx = dx.astype(float)\n        const = util.cast(2, float)\n\n    if dxx.ndim != 1:\n        compile_error(\"distances must be either scalars or 1d\")\n    elif len(dxx) != f.shape[axis]:\n        raise ValueError(\"when 1d, distances must match \"\n                         \"the length of the corresponding dimension\")\n\n    if f.shape[axis] < edge_order + 1:\n        raise ValueError(\n            \"Shape of array too small to calculate a numerical gradient, \"\n            \"at least (edge_order + 1) elements are required.\")\n\n    val = len(dxx)\n\n    uniform_spacing = True\n    m = dxx._ptr((1, ))[0] - dxx._ptr((0, ))[0]\n    for i in range(val - 1):\n        if (m != (dxx._ptr((i + 1, ))[0] - dxx._ptr((i, ))[0])):\n            uniform_spacing = False\n            break\n\n    if uniform_spacing:\n        dval = m\n        return _gradient_helper_scaler(f, dval, dtype, axis, edge_order)\n\n    axis_len = f.shape[axis]\n    result = empty(f.shape, dtype=dtype)\n    for idx0 in util.multirange(util.tuple_delete(f.shape, axis)):\n        if edge_order == 1:\n            idx = util.tuple_insert(idx0, axis, 0)\n            x0 = util.cast(f._ptr(idx)[0], dtype)\n            idx2 = util.tuple_insert(idx0, axis, 1)\n            x1 = util.cast(f._ptr(idx2)[0], dtype)\n            result._ptr(idx)[0] = util.cast((x1 - x0) / (dxx._ptr(\n                (1, ))[0] - dxx._ptr((0, ))[0]), dtype)\n            idx = util.tuple_insert(idx0, axis, axis_len - 1)\n            x0 = util.cast(f._ptr(idx)[0], dtype)\n            idx2 = util.tuple_insert(idx0, axis, axis_len - 2)\n            x1 = util.cast(f._ptr(idx2)[0], dtype)\n            result._ptr(idx)[0] = util.cast((x0 - x1) / (dxx._ptr(\n                (dxx.size - 1, ))[0] - dxx._ptr((dxx.size - 2, ))[0]), dtype)\n        else:\n            idx = util.tuple_insert(idx0, axis, 0)\n            x0 = util.cast(f._ptr(idx)[0], dtype)\n            idx2 = util.tuple_insert(idx0, axis, 1)\n            x1 = util.cast(f._ptr(idx2)[0], dtype)\n            idx3 = util.tuple_insert(idx0, axis, 2)\n            x2 = util.cast(f._ptr(idx3)[0], dtype)\n            dx1 = dxx._ptr((1, ))[0] - dxx._ptr((0, ))[0]\n            dx2 = dxx._ptr((2, ))[0] - dxx._ptr((1, ))[0]\n            d = -(const * dx1 + dx2) / (dx1 * (dx1 + dx2))\n            b = (dx1 + dx2) / (dx1 * dx2)\n            c = -dx1 / (dx2 * (dx1 + dx2))\n            result._ptr(idx)[0] = util.cast(d * x0 + b * x1 + c * x2, dtype)\n\n            idx = util.tuple_insert(idx0, axis, axis_len - 1)\n            x0 = util.cast(f._ptr(idx)[0], dtype)\n            idx2 = util.tuple_insert(idx0, axis, axis_len - 2)\n            x1 = util.cast(f._ptr(idx2)[0], dtype)\n            idx3 = util.tuple_insert(idx0, axis, axis_len - 3)\n            x2 = util.cast(f._ptr(idx3)[0], dtype)\n            dx1 = dxx._ptr((dxx.size - 2, ))[0] - dxx._ptr((dxx.size - 3, ))[0]\n            dx2 = dxx._ptr((dxx.size - 1, ))[0] - dxx._ptr((dxx.size - 2, ))[0]\n            d = (dx2) / (dx1 * (dx1 + dx2))\n            b = -(dx2 + dx1) / (dx1 * dx2)\n            c = (const * dx2 + dx1) / (dx2 * (dx1 + dx2))\n            result._ptr(idx)[0] = util.cast(d * x2 + b * x1 + c * x0, dtype)\n\n        for i in range(0, axis_len - 2):\n            idx = util.tuple_insert(idx0, axis, i)\n            x0 = util.cast(f._ptr(idx)[0], dtype)\n            idx2 = util.tuple_insert(idx0, axis, i + 1)\n            x1 = util.cast(f._ptr(idx2)[0], dtype)\n            idx3 = util.tuple_insert(idx0, axis, i + 2)\n            x2 = util.cast(f._ptr(idx3)[0], dtype)\n            dx1 = dxx._ptr((i + 1, ))[0] - dxx._ptr((i, ))[0]\n            dx2 = dxx._ptr((i + 2, ))[0] - dxx._ptr((i + 1, ))[0]\n            d = -(dx2) / (dx1 * (dx1 + dx2))\n            b = (dx2 - dx1) / (dx1 * dx2)\n            c = dx1 / (dx2 * (dx1 + dx2))\n            result._ptr(idx2)[0] = util.cast(d * x0 + b * x1 + c * x2, dtype)\n\n    return result\n\ndef gradient(f, *varargs, axis=None, edge_order: int = 1):\n\n    def gradient_dtype(dtype: type):\n        if (dtype is int or dtype is byte or isinstance(dtype, Int)\n                or isinstance(dtype, UInt)):\n            return float()\n        else:\n            return dtype()\n\n    f = asarray(f)\n\n    if isinstance(f.dtype, datetime64):\n        g = f.view(timedelta64[f.dtype.base, f.dtype.num])\n        return gradient(g, *varargs, axis=axis, edge_order=edge_order)\n\n    rdtype = type(gradient_dtype(f.dtype))\n    result = empty(f.shape, dtype=rdtype)\n\n    if edge_order > 2:\n        raise ValueError(\"'edge_order' greater than 2 not supported\")\n\n    if axis is None:\n        outval = []\n        dval = util.cast(1, rdtype)\n        if static.len(varargs) == 0:\n            for i in static.range(f.ndim):\n                result = _gradient_helper_scaler(f,\n                                                 dval,\n                                                 rdtype,\n                                                 axis=i,\n                                                 edge_order=edge_order)\n                outval.append(result)\n        elif static.len(varargs) == 1:\n            if f.ndim != 1 and (isinstance(varargs[0], List)\n                                or isinstance(varargs[0], Tuple)):\n                compile_error(\"invalid number of arguments\")\n\n            if f.ndim != 1 and isinstance(varargs[0], ndarray):\n                if varargs[0].ndim != 0:\n                    compile_error(\"invalid number of arguments\")\n\n            for i in static.range(f.ndim):\n                if (isinstance(varargs[0], List)\n                        or isinstance(varargs[0], Tuple)\n                        or isinstance(varargs[0], ndarray)):\n                    result = _gradient_helper_linear(f,\n                                                     varargs[0],\n                                                     rdtype,\n                                                     axis=i,\n                                                     edge_order=edge_order)\n                else:\n                    result = _gradient_helper_scaler(f,\n                                                     varargs[0],\n                                                     rdtype,\n                                                     axis=i,\n                                                     edge_order=edge_order)\n                outval.append(result)\n        else:\n            if f.ndim == static.len(varargs):\n                for i in static.range(f.ndim):\n                    if (isinstance(varargs[i], List)\n                            or isinstance(varargs[i], Tuple)\n                            or isinstance(varargs[i], ndarray)):\n                        result = _gradient_helper_linear(f,\n                                                         varargs[i],\n                                                         rdtype,\n                                                         axis=i,\n                                                         edge_order=edge_order)\n                    else:\n                        result = _gradient_helper_scaler(f,\n                                                         varargs[i],\n                                                         rdtype,\n                                                         axis=i,\n                                                         edge_order=edge_order)\n                    outval.append(result)\n            else:\n                compile_error(\"invalid number of arguments\")\n        if f.ndim == 1:\n            return outval[0]\n        return outval\n    else:\n        if isinstance(axis, Tuple):\n            ax = util.normalize_axis_tuple(axis, f.ndim)\n            outval = []\n            dval = util.cast(1, rdtype)\n\n            if static.len(varargs) == 0:\n                for i in range(static.len(ax)):\n                    result = _gradient_helper_scaler(f,\n                                                     dval,\n                                                     rdtype,\n                                                     axis=ax[i],\n                                                     edge_order=edge_order)\n                    outval.append(result)\n            elif static.len(varargs) == 1:\n                if static.len(ax) != 1 and (isinstance(varargs[0], List)\n                                           or isinstance(varargs[0], Tuple)):\n                    compile_error(\"invalid number of arguments\")\n                if static.len(ax) != 1 and isinstance(varargs[0], ndarray):\n                    if varargs[0].ndim != 0:\n                        compile_error(\"invalid number of arguments\")\n                for i in range(static.len(ax)):\n                    if (isinstance(varargs[0], List)\n                            or isinstance(varargs[0], Tuple)\n                            or isinstance(varargs[0], ndarray)):\n                        result = _gradient_helper_linear(f,\n                                                         varargs[0],\n                                                         rdtype,\n                                                         axis=ax[i],\n                                                         edge_order=edge_order)\n                    else:\n                        result = _gradient_helper_scaler(f,\n                                                         varargs[0],\n                                                         rdtype,\n                                                         axis=ax[i],\n                                                         edge_order=edge_order)\n                    outval.append(result)\n            else:\n                if static.len(ax) == static.len(varargs):\n                    for i in range(static.len(ax)):\n                        if (isinstance(varargs[i], List)\n                                or isinstance(varargs[i], Tuple)\n                                or isinstance(varargs[i], ndarray)):\n                            result = _gradient_helper_linear(\n                                f,\n                                varargs[i],\n                                rdtype,\n                                axis=ax[i],\n                                edge_order=edge_order)\n                        else:\n                            result = _gradient_helper_scaler(\n                                f,\n                                varargs[i],\n                                rdtype,\n                                axis=ax[i],\n                                edge_order=edge_order)\n                        outval.append(result)\n                else:\n                    compile_error(\"invalid number of arguments\")\n\n            if static.len(ax) == 1:\n                return outval[0]\n            return outval\n        elif isinstance(axis, List[int]):\n            ax = util.normalize_axis_tuple(axis, f.ndim)\n            outval = []\n            dval = util.cast(1, rdtype)\n\n            if static.len(varargs) == 0:\n                for i in range(len(ax)):\n                    result = _gradient_helper_scaler(f,\n                                                     dval,\n                                                     rdtype,\n                                                     axis=ax[i],\n                                                     edge_order=edge_order)\n                    outval.append(result)\n            elif static.len(varargs) == 1:\n                if len(ax) != 1 and (isinstance(varargs[0], List)\n                                     or isinstance(varargs[0], Tuple)):\n                    raise ValueError(\"invalid number of arguments\")\n                if len(ax) != 1 and isinstance(varargs[0], ndarray):\n                    if varargs[0].ndim != 0:\n                        compile_error(\"invalid number of arguments\")\n\n                for i in range(len(ax)):\n                    if (isinstance(varargs[0], List)\n                            or isinstance(varargs[0], Tuple)\n                            or isinstance(varargs[0], ndarray)):\n                        result = _gradient_helper_linear(f,\n                                                         varargs[0],\n                                                         rdtype,\n                                                         axis=ax[i],\n                                                         edge_order=edge_order)\n                    else:\n                        result = _gradient_helper_scaler(f,\n                                                         varargs[0],\n                                                         rdtype,\n                                                         axis=ax[i],\n                                                         edge_order=edge_order)\n                    outval.append(result)\n            else:\n                if len(ax) == static.len(varargs):\n                    for i in range(len(ax)):\n                        if (isinstance(varargs[i], List)\n                                or isinstance(varargs[i], Tuple)\n                                or isinstance(varargs[i], ndarray)):\n                            result = _gradient_helper_linear(\n                                f,\n                                varargs[i],\n                                rdtype,\n                                axis=ax[i],\n                                edge_order=edge_order)\n                        else:\n                            result = _gradient_helper_scaler(\n                                f,\n                                varargs[i],\n                                rdtype,\n                                axis=ax[i],\n                                edge_order=edge_order)\n                        outval.append(result)\n                else:\n                    raise ValueError(\"invalid number of arguments\")\n            return outval\n        else:\n            axis = util.normalize_axis_index(axis, f.ndim)\n\n            if static.len(varargs) == 0:\n                return _gradient_helper_scaler(f,\n                                               dx=1,\n                                               dtype=rdtype,\n                                               axis=axis,\n                                               edge_order=edge_order)\n            else:\n                if static.len(varargs) == 1:\n                    if (isinstance(varargs[0], List)\n                            or isinstance(varargs[0], Tuple)\n                            or isinstance(varargs[0], ndarray)):\n                        return _gradient_helper_linear(f,\n                                                       varargs[0],\n                                                       rdtype,\n                                                       axis=axis,\n                                                       edge_order=edge_order)\n                    else:\n                        return _gradient_helper_scaler(f,\n                                                       varargs[0],\n                                                       rdtype,\n                                                       axis=axis,\n                                                       edge_order=edge_order)\n                else:\n                    compile_error(\"invalid number of arguments\")\n\ndef ediff1d(ary, to_begin=None, to_end=None):\n    a = asarray(ary).ravel()\n    n = a.size - 1\n\n    if to_begin is not None:\n        begin = asarray(to_begin).ravel()\n        n += begin.size\n    else:\n        begin = None\n\n    if to_end is not None:\n        end = asarray(to_end).ravel()\n        n += end.size\n    else:\n        end = None\n\n    ans = empty(n, a.dtype)\n    k = 0\n\n    if to_begin is not None:\n        for i in range(begin.size):\n            ans._ptr((i, ))[0] = util.cast(begin._ptr((i, ))[0], a.dtype)\n        k = begin.size\n\n    for i in range(a.size - 1):\n        ans._ptr((k + i, ))[0] = a._ptr((i + 1, ))[0] - a._ptr((i, ))[0]\n    k += a.size - 1\n\n    if to_end is not None:\n        for i in range(end.size):\n            ans._ptr((k + i, ))[0] = util.cast(end._ptr((i, ))[0], a.dtype)\n\n    return ans\n\ndef cross(a, b, axisa: int = -1, axisb: int = -1, axisc: int = -1, axis = None):\n    if axis is not None:\n        axisa, axisb, axisc = (axis, ) * 3\n    a = asarray(a)\n    b = asarray(b)\n\n    # Check axisa and axisb are within bounds\n    axisa = util.normalize_axis_index(axisa, a.ndim)\n    axisb = util.normalize_axis_index(axisb, b.ndim)\n\n    # Move working axis to the end of the shape\n    a = moveaxis(a, axisa, -1)\n    b = moveaxis(b, axisb, -1)\n    msg = (\"incompatible dimensions for cross product\\n\"\n           \"(dimension must be 2 or 3)\")\n    if a.shape[-1] not in (2, 3) or b.shape[-1] not in (2, 3):\n        raise ValueError(msg)\n\n    # Create the output array\n    x = a[..., 0]\n    x = asarray(x)\n    y = b[..., 0]\n    y = asarray(y)\n    shape = broadcast_shapes(x.shape, y.shape)\n\n    if a.shape[-1] == 3 or b.shape[-1] == 3:\n        new_shape = shape + (3, )\n        # Check axisc is within bounds\n        axisc = util.normalize_axis_index(axisc, len(new_shape))\n    else:\n        new_shape = shape + (1, )\n\n    dtype = type(util.coerce(a.dtype, b.dtype))\n\n    cp = zeros(new_shape, dtype)\n\n    # recast arrays as dtype\n    a = a.astype(dtype)\n    b = b.astype(dtype)\n\n    # create local aliases for readability\n    a0 = a[..., 0]\n    a1 = a[..., 1]\n    if a.shape[-1] == 3:\n        a2 = a[..., 2]\n    b0 = b[..., 0]\n    b1 = b[..., 1]\n    if b.shape[-1] == 3:\n        b2 = b[..., 2]\n    if cp.ndim != 0 and cp.shape[-1] == 3:\n\n        cp0 = cp[..., 0]\n        cp1 = cp[..., 1]\n        cp2 = cp[..., 2]\n\n    if a.shape[-1] == 2:\n        if b.shape[-1] == 2:\n            # a0 * b1 - a1 * b0\n            cp[0] = a[..., 0] * b[..., 1] - a[..., 1] * b[\n                ..., 0]  # cp2 = a0 * b1 - a1 * b0\n            return cp\n\n        else:\n            assert b.shape[-1] == 3\n\n            # cp0 = 0 - a1 * b2  (a2 = 0)\n            cp[..., 0] = -a[..., 1] * b[..., 2]\n\n            # cp1 = a0 * b2 - 0  (a2 = 0)\n            cp[..., 1] = a[..., 0] * b[..., 2]\n\n            # cp2 = a0 * b1 - a1 * b0\n            cp[..., 2] = a[..., 0] * b[..., 1] - a[..., 1] * b[..., 0]\n\n    else:\n        assert a.shape[-1] == 3\n        if b.shape[-1] == 3:\n\n            # cp0 = a1 * b2 - a2 * b1\n            cp[..., 0] = a[..., 1] * b[..., 2] - a[..., 2] * b[..., 1]\n\n            # cp1 = a2 * b0 - a0 * b2\n            cp[..., 1] = a[..., 2] * b[..., 0] - a[..., 0] * b[..., 2]\n\n            # cp2 = a0 * b1 - a1 * b0\n            cp[..., 2] = a[..., 0] * b[..., 1] - a[..., 1] * b[..., 0]\n\n        else:\n            assert b.shape[-1] == 2\n\n            # cp0 = a2 * b1 - 0  (b2 = 0)\n            cp[..., 0] = a[..., 2] * b[..., 1]\n\n            # cp1 = 0 - a2 * b0  (b2 = 0)\n            cp[..., 1] = -a[..., 2] * b[..., 0]\n\n            # cp2 = a0 * b1 - a1 * b0\n            cp[..., 2] = a[..., 0] * b[..., 1] - a[..., 1] * b[..., 0]\n\n    return moveaxis(cp, -1, axisc)\n\ndef trapz(y, x=None, dx=1.0, axis: int = -1):\n    def trapz_helper(a, axis, dx, dtype: type):\n        axis = util.normalize_axis_index(axis, a.ndim)\n        axis_len = a.shape[axis]\n        new_shape = util.tuple_delete(a.shape, axis)\n\n        # Create empty array\n        result = empty(new_shape, dtype=dtype)\n\n        # Iterate over all indices except the specified axis\n        for idx0 in util.multirange(util.tuple_delete(a.shape, axis)):\n            ans = dtype()\n\n            for i in range(axis_len - 1):\n                idx = util.tuple_insert(idx0, axis, i)\n                y0 = a._ptr(idx)[0]\n\n                idx = util.tuple_insert(idx0, axis, i + 1)\n                y1 = a._ptr(idx)[0]\n\n                ans += y1 + y0\n\n            result._ptr(idx0)[0] = ans * dx / dtype(2)\n        return result\n\n    def trapz_helper_with_x(a, axis, x, dtype: type):\n        axis = util.normalize_axis_index(axis, a.ndim)\n        axis_len = a.shape[axis]\n        new_shape = util.tuple_delete(a.shape, axis)\n\n        # Create empty array\n        result = empty(new_shape, dtype=dtype)\n\n        x = broadcast_to(x, a.shape)\n\n        # Iterate over all indices except the specified axis\n        for idx0 in util.multirange(util.tuple_delete(a.shape, axis)):\n            ans = dtype()\n\n            for i in range(axis_len - 1):\n                idx = util.tuple_insert(idx0, axis, i)\n                x0 = x._ptr(idx)[0]\n                y0 = a._ptr(idx)[0]\n\n                idx = util.tuple_insert(idx0, axis, i + 1)\n                x1 = x._ptr(idx)[0]\n                y1 = a._ptr(idx)[0]\n\n                ans += (y1 + y0) * (x1 - x0)\n\n            result._ptr(idx0)[0] = ans / dtype(2)\n        return result\n\n    def trapz_helper_with_x_1d(a, axis, x, dtype: type):\n        axis = util.normalize_axis_index(axis, a.ndim)\n        axis_len = a.shape[axis]\n\n        if len(x) != axis_len:\n            raise ValueError(\n                \"length of 'x' does not match dimension along axis\")\n\n        new_shape = util.tuple_delete(a.shape, axis)\n\n        # Create empty array\n        result = empty(new_shape, dtype=dtype)\n\n        # Iterate over all indices except the specified axis\n        for idx0 in util.multirange(util.tuple_delete(a.shape, axis)):\n            ans = dtype()\n\n            for i in range(axis_len - 1):\n                idx = util.tuple_insert(idx0, axis, i)\n                x0 = x._ptr((i, ))[0]\n                y0 = a._ptr(idx)[0]\n\n                idx = util.tuple_insert(idx0, axis, i + 1)\n                x1 = x._ptr((i + 1, ))[0]\n                y1 = a._ptr(idx)[0]\n\n                ans += (y1 + y0) * (x1 - x0)\n\n            result._ptr(idx0)[0] = ans / dtype(2)\n        return result\n\n    def trapz_dtype(dtype: type):\n        if (dtype is int or dtype is byte or isinstance(dtype, Int)\n                or isinstance(dtype, UInt)):\n            return float()\n        else:\n            return dtype()\n\n    def zero_dim_to_scalar(arr: ndarray):\n        if arr.ndim == 0:\n            return arr.item()\n        else:\n            return arr\n\n    y = asarray(y)\n    dtype = type(trapz_dtype(y.dtype))\n    axis = util.normalize_axis_index(axis, y.ndim)\n    if x is None:\n        return zero_dim_to_scalar(\n            trapz_helper(y, axis=axis, dx=dx, dtype=dtype))\n    else:\n        x = asarray(x)\n        if x.ndim == 1:\n            return zero_dim_to_scalar(\n                trapz_helper_with_x_1d(y, axis=axis, x=x, dtype=dtype))\n        else:\n            return zero_dim_to_scalar(\n                trapz_helper_with_x(y, axis=axis, x=x, dtype=dtype))\n\ndef convolve(v, a, mode: str = \"full\"):\n    # Convert inputs to NumPy arrays\n    v = atleast_1d(asarray(v))\n    a = atleast_1d(asarray(a))\n\n    if len(v) < len(a):\n        return convolve(a, v, mode=mode)\n\n    res_dtype = type(util.coerce(v.dtype, a.dtype))\n\n    if v.size == 0 or a.size == 0:\n        raise ValueError(\"Input arrays cannot be empty\")\n\n    if a.ndim > 1 or v.ndim > 1:\n        compile_error(\n            \"incompatible dimensions for convolution\\n(dimension must be 0 or 1)\"\n        )\n\n    a = a[::-1]\n\n    length = len(v)\n    n = len(a)\n    n_left = 0\n    n_right = 0\n\n    if mode == \"valid\":\n        reslen = length - n + 1\n    elif mode == \"same\":\n        if n % 2 == 1:\n            n_left = n // 2\n            n_right = n // 2\n            desc = n - n_left - 1\n        else:\n            n_left = n // 2\n            n_right = n - n_left - 1\n            desc = n - n_left\n        reslen = length\n\n    elif mode == \"full\":\n        n_right = n - 1\n        n_left = n - 1\n        desc = n - 1\n        reslen = length + n - 1\n    else:\n        raise ValueError(\n            f\"mode must be one of 'valid', 'same', or 'full' (got {repr(mode)})\"\n        )\n\n    # Allocate result array\n    ret = zeros(reslen, dtype=res_dtype)\n    for i in range(n_left):\n        k = desc - i\n        l = 0\n        for j in range(k, n, 1):\n            ret._ptr((i, ))[0] += util.cast(v._ptr(\n                (l, ))[0], res_dtype) * util.cast(a._ptr((j, ))[0], res_dtype)\n            l += 1\n\n    b = reslen - n_right\n    for i in range(n_left, b):\n        k = i - n_left\n        for j in range(n):\n            ret._ptr((i, ))[0] += util.cast(v._ptr(\n                (k, ))[0], res_dtype) * util.cast(a._ptr((j, ))[0], res_dtype)\n            k += 1\n\n    c = length - n + 1\n    for i in range(n_right):\n        k = length - (n - i) + 1\n        for j in range(n - i - 1):\n            ret._ptr((n_left + c +\n                      i, ))[0] += util.cast(v._ptr(\n                          (k, ))[0], res_dtype) * util.cast(\n                              a._ptr((j, ))[0], res_dtype)\n            k += 1\n\n    return ret\n\ndef _apply(x, f, f_complex = None):\n    if f_complex is not None and (isinstance(x, complex) or isinstance(x, complex64)):\n        return f_complex(x)\n    elif isinstance(x, float) or isinstance(x, float32) or isinstance(x, float16):\n        return f(x)\n    else:\n        return f(util.to_float(x))\n\ndef _apply2(x, y, f, f_complex = None):\n    if type(x) is not type(y):\n        compile_error(\"[internal error] type mismatch\")\n\n    if f_complex is not None and (isinstance(x, complex) or isinstance(x, complex64)):\n        return f_complex(x, y)\n    elif isinstance(x, float) or isinstance(x, float32) or isinstance(x, float16):\n        return f(x, y)\n    else:\n        return f(util.to_float(x), util.to_float(y))\n\ndef _fabs(x):\n    return _apply(x, util.fabs)\n\ndef _rint(x):\n    def rint_complex(x):\n        C = type(x)\n        return C(util.rint(x.real), util.rint(x.imag))\n\n    return _apply(x, util.rint, rint_complex)\n\ndef _exp(x):\n    return _apply(x, util.exp, zmath.exp)\n\ndef _exp2(x):\n    return _apply(x, util.exp2, zmath.exp2)\n\ndef _expm1(x):\n    return _apply(x, util.expm1, zmath.expm1)\n\ndef _log(x):\n    return _apply(x, util.log, zmath.log)\n\ndef _log2(x):\n    return _apply(x, util.log2, zmath.log2)\n\ndef _log10(x):\n    return _apply(x, util.log10, zmath.log10)\n\ndef _log1p(x):\n    return _apply(x, util.log1p, zmath.log1p)\n\ndef _sqrt(x):\n    return _apply(x, util.sqrt, zmath.sqrt)\n\ndef _cbrt(x):\n    return _apply(x, util.cbrt)\n\ndef _square(x):\n    return x * x\n\ndef _sin(x):\n    return _apply(x, util.sin, zmath.sin)\n\ndef _cos(x):\n    return _apply(x, util.cos, zmath.cos)\n\ndef _tan(x):\n    return _apply(x, util.tan, zmath.tan)\n\ndef _arcsin(x):\n    return _apply(x, util.asin, zmath.asin)\n\ndef _arccos(x):\n    return _apply(x, util.acos, zmath.acos)\n\ndef _arctan(x):\n    return _apply(x, util.atan, zmath.atan)\n\ndef _sinh(x):\n    return _apply(x, util.sinh, zmath.sinh)\n\ndef _cosh(x):\n    return _apply(x, util.cosh, zmath.cosh)\n\ndef _tanh(x):\n    return _apply(x, util.tanh, zmath.tanh)\n\ndef _arcsinh(x):\n    return _apply(x, util.asinh, zmath.asinh)\n\ndef _arccosh(x):\n    return _apply(x, util.acosh, zmath.acosh)\n\ndef _arctanh(x):\n    return _apply(x, util.atanh, zmath.atanh)\n\ndef _rad2deg(x):\n    r2d = 180.0 / util.PI\n    x = util.to_float(x)\n    F = type(x)\n    return x * F(r2d)\n\ndef _deg2rad(x):\n    d2r = util.PI / 180.0\n    x = util.to_float(x)\n    F = type(x)\n    return x * F(d2r)\n\ndef _arctan2(x, y):\n    return _apply2(x, y, util.atan2)\n\ndef _hypot(x, y):\n    return _apply2(x, y, util.hypot)\n\ndef _logaddexp(x, y):\n    return _apply2(x, y, util.logaddexp)\n\ndef _logaddexp2(x, y):\n    return _apply2(x, y, util.logaddexp2)\n\ndef _isnan(x):\n    if isinstance(x, float) or isinstance(x, float32) or isinstance(x, float16):\n        return util.isnan(x)\n    elif isinstance(x, complex) or isinstance(x, complex64):\n        return util.isnan(x.real) or util.isnan(x.imag)\n    elif isinstance(x, datetime64) or isinstance(x, timedelta64):\n        return x._nat\n    else:\n        return False\n\ndef _isinf(x):\n    if isinstance(x, float) or isinstance(x, float32) or isinstance(x, float16):\n        return util.isinf(x)\n    elif isinstance(x, complex) or isinstance(x, complex64):\n        return util.isinf(x.real) or util.isinf(x.imag)\n    else:\n        return False\n\ndef _isfinite(x):\n    if isinstance(x, float) or isinstance(x, float32) or isinstance(x, float16):\n        return util.isfinite(x)\n    elif isinstance(x, complex) or isinstance(x, complex64):\n        return util.isfinite(x.real) and util.isfinite(x.imag)\n    elif isinstance(x, datetime64) or isinstance(x, timedelta64):\n        return not x._nat\n    else:\n        return True\n\ndef _signbit(x):\n    if isinstance(x, float) or isinstance(x, float32) or isinstance(x, float16):\n        return util.signbit(x)\n    else:\n        T = type(x)\n        return x < T()\n\ndef _copysign(x, y):\n    return _apply2(x, y, util.copysign)\n\ndef _nextafter(x, y):\n    return _apply2(x, y, util.nextafter)\n\ndef _ldexp(x, y):\n    y = util.cast(y, int)\n    if isinstance(x, float) or isinstance(x, float32) or isinstance(x, float16):\n        return util.ldexp(x, y)\n    else:\n        return util.ldexp(util.to_float(x), y)\n\ndef _floor(x):\n    return _apply(x, util.floor)\n\ndef _ceil(x):\n    return _apply(x, util.ceil)\n\ndef _trunc(x):\n    return _apply(x, util.trunc)\n\ndef _sign(x):\n    def sign1(x):\n        T = type(x)\n        if x < T(0):\n            return T(-1)\n        elif x > T(0):\n            return T(1)\n        else:\n            return x\n\n    if isinstance(x, complex):\n        if _isnan(x):\n            return complex(util.nan64(), 0.0)\n        return complex(sign1(x.real), 0) if x.real else complex(sign1(x.imag), 0)\n    elif isinstance(x, complex64):\n        if _isnan(x):\n            return complex64(util.nan64(), 0.0)\n        return complex64(sign1(x.real), 0) if x.real else complex64(sign1(x.imag), 0)\n    elif isinstance(x, timedelta64):\n        return x._sign\n    else:\n        return sign1(x)\n\ndef _heaviside(x, y):\n    def heaviside(x, y):\n        if isinstance(x, float16) and isinstance(y, float16):\n            if x < float16(0):\n                return float16(0)\n            elif x > float16(0):\n                return float16(1)\n            elif x == float16(0):\n                return y\n            else:\n                return x\n        elif isinstance(x, float32) and isinstance(y, float32):\n            if x < float32(0):\n                return float32(0)\n            elif x > float32(0):\n                return float32(1)\n            elif x == float32(0):\n                return y\n            else:\n                return x\n        elif isinstance(x, float) and isinstance(y, float):\n            if x < 0:\n                return 0.0\n            elif x > 0:\n                return 1.0\n            elif x == 0.0:\n                return y\n            else:\n                return x\n\n    return _apply2(x, y, heaviside)\n\ndef _conj(x):\n    if isinstance(x, complex) or isinstance(x, complex64):\n        return x.conjugate()\n    else:\n        return x\n\ndef _gcd(x, y):\n    while x:\n        z = x\n        x = y % x\n        y = z\n    return y\n\ndef _lcm(x, y):\n    gcd = _gcd(x, y)\n    return x // gcd * y if gcd else 0\n\ndef _reciprocal(x: T, T: type):\n    if (\n        isinstance(x, int) or\n        isinstance(x, Int) or\n        isinstance(x, UInt) or\n        isinstance(x, byte)\n    ):\n        return T(1) // x\n    else:\n        return T(1) / x\n\ndef _logical_and(x, y):\n    return bool(x) and bool(y)\n\ndef _logical_or(x, y):\n    return bool(x) or bool(y)\n\ndef _logical_xor(x, y):\n    return bool(x) ^ bool(y)\n\ndef _logical_not(x):\n    return not bool(x)\n\ndef _coerce_types_for_minmax(x, y):\n    if isinstance(x, complex):\n        if isinstance(y, complex64):\n            return x, complex(y)\n        elif not isinstance(y, complex):\n            return x, complex(util.cast(y, float))\n    elif isinstance(x, complex64):\n        if isinstance(y, complex):\n            return complex(x), y\n        elif not isinstance(y, complex64):\n            return complex(x), complex(util.cast(y, float))\n\n    if isinstance(y, complex):\n        if isinstance(x, complex64):\n            return complex(x), y\n        elif not isinstance(x, complex):\n            return complex(util.cast(x, float)), y\n    elif isinstance(y, complex64):\n        if isinstance(x, complex):\n           return x, complex(y)\n        elif not isinstance(x, complex64):\n            return complex(util.cast(x, float)), complex(y)\n\n    T = type(util.coerce(type(x), type(y)))\n    return util.cast(x, T), util.cast(y, T)\n\ndef _compare_le(x, y):\n    if isinstance(x, complex) or isinstance(x, complex64):\n        return (x.real, x.imag) <= (y.real, y.imag)\n    else:\n        return x <= y\n\ndef _compare_ge(x, y):\n    if isinstance(x, complex) or isinstance(x, complex64):\n        return (x.real, x.imag) >= (y.real, y.imag)\n    else:\n        return x >= y\n\ndef _maximum(x, y):\n    x, y = _coerce_types_for_minmax(x, y)\n\n    if _isnan(x):\n        return x\n\n    if _isnan(y):\n        return y\n\n    return x if _compare_ge(x, y) else y\n\ndef _minimum(x, y):\n    x, y = _coerce_types_for_minmax(x, y)\n\n    if _isnan(x):\n        return x\n\n    if _isnan(y):\n        return y\n\n    return x if _compare_le(x, y) else y\n\ndef _fmax(x, y):\n    x, y = _coerce_types_for_minmax(x, y)\n\n    if _isnan(y):\n        return x\n\n    if _isnan(x):\n        return y\n\n    return x if _compare_ge(x, y) else y\n\ndef _fmin(x, y):\n    x, y = _coerce_types_for_minmax(x, y)\n\n    if _isnan(y):\n        return x\n\n    if _isnan(x):\n        return y\n\n    return x if _compare_le(x, y) else y\n\ndef _divmod_float(x, y):\n    F = type(x)\n    mod = util.cmod(x, y)\n    if not y:\n        return util.cdiv(x, y), mod\n\n    div = util.cdiv(x - mod, y)\n    if mod:\n        if (y < F(0)) != (mod < F(0)):\n            mod += y\n            div -= F(1)\n    else:\n        mod = util.copysign(F(0), y)\n\n    floordiv = F()\n    if div:\n        floordiv = util.floor(div)\n        if div - floordiv > F(0.5):\n            floordiv += F(1)\n    else:\n        floordiv = util.copysign(F(0), util.cdiv(x, y))\n\n    return floordiv, mod\n\ndef _divmod(x, y):\n    if isinstance(x, float16) and isinstance(y, float16):\n        return _divmod_float(x, y)\n\n    if isinstance(x, float32) and isinstance(y, float32):\n        return _divmod_float(x, y)\n\n    if isinstance(x, float) or isinstance(y, float):\n        return _divmod_float(util.cast(x, float), util.cast(y, float))\n\n    return (x // y, x % y)\n\ndef _modf(x):\n    return _apply(x, util.modf)\n\ndef _frexp(x):\n    def frexp(x):\n        a, b = util.frexp(x)\n        return a, i32(b)\n\n    return _apply(x, frexp)\n\ndef _spacing16(h: float16):\n    h_u16 = util.bitcast(h, u16)\n    h_exp = h_u16 & u16(0x7c00)\n    h_sig = h_u16 & u16(0x03ff)\n\n    if h_exp == u16(0x7c00):\n        return util.nan16()\n    elif h_u16 == u16(0x7bff):\n        return util.inf16()\n    elif (h_u16 & u16(0x8000)) and not h_sig:\n        if h_exp > u16(0x2c00):\n            return util.bitcast(h_exp - u16(0x2c00), float16)\n        elif h_exp > u16(0x0400):\n            return util.bitcast(u16(1) << ((h_exp >> u16(10)) - u16(2)), float16)\n        else:\n            return util.bitcast(u16(0x0001), float16)\n    elif h_exp > u16(0x2800):\n        return util.bitcast(h_exp - u16(0x2800), float16)\n    elif h_exp > u16(0x0400):\n        return util.bitcast(u16(1) << ((h_exp >> u16(10)) - u16(1)), float16)\n    else:\n        return util.bitcast(u16(0x0001), float16)\n\ndef _spacing(x):\n    if isinstance(x, float16):\n        return _spacing16(x)\n    elif isinstance(x, float32):\n        if util.isinf32(x):\n            return util.nan32()\n        p = util.inf32() if x >= float32(0) else -util.inf32()\n        return util.nextafter32(x, util.inf32()) - x\n    elif isinstance(x, float):\n        x = util.cast(x, float)\n        if util.isinf64(x):\n            return util.nan64()\n        p = util.inf64() if x >= 0 else -util.inf64()\n        return util.nextafter64(x, p) - x\n    else:\n        return _spacing(util.to_float(x))\n\ndef _isnat(x):\n    if isinstance(x, datetime64) or isinstance(x, timedelta64):\n        return x._nat\n    else:\n        compile_error(\"ufunc 'isnat' is only defined for np.datetime64 and np.timedelta64.\")\n\ndef _floor_divide(x, y):\n    X = type(x)\n    Y = type(y)\n    if isinstance(X, Int) and isinstance(Y, Int):\n        return util.pydiv(x, y)\n    else:\n        return x // y\n\ndef _remainder(x, y):\n    X = type(x)\n    Y = type(y)\n    if isinstance(X, Int) and isinstance(Y, Int):\n        return util.pymod(x, y)\n    elif ((X is float and Y is float) or\n          (X is float32 and Y is float32) or\n          (X is float16 and Y is float16)):\n        return util.pyfmod(x, y)\n    else:\n        return x % y\n\ndef _fmod(x, y):\n    X = type(x)\n    Y = type(y)\n    if isinstance(X, Int) and isinstance(Y, Int):\n        return util.cmod_int(x, y)\n    elif ((X is float and Y is float) or\n          (X is float32 and Y is float32) or\n          (X is float16 and Y is float16)):\n        return util.cmod(x, y)\n    else:\n        return x % y\n\nadd = BinaryUFunc(operator.add, 'add', 0)\nsubtract = BinaryUFunc(operator.sub, 'subtract')\nmultiply = BinaryUFunc(operator.mul, 'multiply', 1)\ndivide = BinaryUFunc(operator.truediv, 'divide')\ntrue_divide = divide\nlogaddexp = BinaryUFunc(_logaddexp, 'logaddexp')\nlogaddexp2 = BinaryUFunc(_logaddexp2, 'logaddexp2')\nfloor_divide = BinaryUFunc(_floor_divide, 'floor_divide')\nnegative = UnaryUFunc(operator.neg, 'negative')\npositive = UnaryUFunc(operator.pos, 'positive')\npower = BinaryUFunc(operator.pow, 'power')\nremainder = BinaryUFunc(_remainder, 'remainder')\nmod = remainder\nfmod = BinaryUFunc(_fmod, 'fmod')\n# divmod\nabsolute = UnaryUFunc(operator.abs, 'absolute')\nabs = absolute\nfabs = UnaryUFunc(_fabs, 'fabs')\nrint = UnaryUFunc(_rint, 'rint')\nsign = UnaryUFunc(_sign, 'sign')\nheaviside = BinaryUFunc(_heaviside, 'heaviside')\nconjugate = UnaryUFunc(_conj, 'conjugate')\nconj = conjugate\nexp = UnaryUFunc(_exp, 'exp')\nexp2 = UnaryUFunc(_exp2, 'exp2')\nlog = UnaryUFunc(_log, 'log')\nlog2 = UnaryUFunc(_log2, 'log2')\nlog10 = UnaryUFunc(_log10, 'log10')\nexpm1 = UnaryUFunc(_expm1, 'expm1')\nlog1p = UnaryUFunc(_log1p, 'log1p')\nsqrt = UnaryUFunc(_sqrt, 'sqrt')\nsquare = UnaryUFunc(_square, 'square')\ncbrt = UnaryUFunc(_cbrt, 'cbrt')\nreciprocal = UnaryUFunc(_reciprocal, 'reciprocal')\ngcd = BinaryUFunc(_gcd, 'gcd', 0)\nlcm = BinaryUFunc(_lcm, 'lcm')\n\nsin = UnaryUFunc(_sin, 'sin')\ncos = UnaryUFunc(_cos, 'cos')\ntan = UnaryUFunc(_tan, 'tan')\narcsin = UnaryUFunc(_arcsin, 'arcsin')\narccos = UnaryUFunc(_arccos, 'arccos')\narctan = UnaryUFunc(_arctan, 'arctan')\narctan2 = BinaryUFunc(_arctan2, 'arctan2')\nhypot = BinaryUFunc(_hypot, 'hypot')\nsinh = UnaryUFunc(_sinh, 'sinh')\ncosh = UnaryUFunc(_cosh, 'cosh')\ntanh = UnaryUFunc(_tanh, 'tanh')\narcsinh = UnaryUFunc(_arcsinh, 'arcsinh')\narccosh = UnaryUFunc(_arccosh, 'arccosh')\narctanh = UnaryUFunc(_arctanh, 'arctanh')\ndeg2rad = UnaryUFunc(_deg2rad, 'deg2rad')\nradians = UnaryUFunc(_deg2rad, 'radians')\nrad2deg = UnaryUFunc(_rad2deg, 'rad2deg')\ndegrees = UnaryUFunc(_rad2deg, 'degrees')\n\nbitwise_and = BinaryUFunc(operator.and_, 'bitwise_and')\nbitwise_or = BinaryUFunc(operator.or_, 'bitwise_or')\nbitwise_xor = BinaryUFunc(operator.xor, 'bitwise_xor')\ninvert = UnaryUFunc(operator.invert, 'invert')\nleft_shift = BinaryUFunc(operator.lshift, 'left_shift')\nright_shift = BinaryUFunc(operator.rshift, 'right_shift')\n\ngreater = BinaryUFunc(operator.gt, 'greater')\ngreater_equal = BinaryUFunc(operator.ge, 'greater_equal')\nless = BinaryUFunc(operator.lt, 'less')\nless_equal = BinaryUFunc(operator.le, 'less_equal')\nnot_equal = BinaryUFunc(operator.ne, 'not_equal')\nequal = BinaryUFunc(operator.eq, 'equal')\n\nlogical_and = BinaryUFunc(_logical_and, 'logical_and')\nlogical_or = BinaryUFunc(_logical_or, 'logical_or')\nlogical_xor = BinaryUFunc(_logical_xor, 'logical_xor')\nlogical_not = UnaryUFunc(_logical_not, 'logical_not')\n\nmaximum = BinaryUFunc(_maximum, 'maximum')\nminimum = BinaryUFunc(_minimum, 'minimum')\nfmax = BinaryUFunc(_fmax, 'fmax')\nfmin = BinaryUFunc(_fmin, 'fmin')\n\nisfinite = UnaryUFunc(_isfinite, 'isfinite')\nisinf = UnaryUFunc(_isinf, 'isinf')\nisnan = UnaryUFunc(_isnan, 'isnan')\nisnat = UnaryUFunc(_isnat, 'isnat')\nsignbit = UnaryUFunc(_signbit, 'signbit')\ncopysign = BinaryUFunc(_copysign, 'copysign')\nnextafter = BinaryUFunc(_nextafter, 'nextafter')\nspacing = UnaryUFunc(_spacing, 'spacing')\nmodf = UnaryUFunc2(_modf, 'modf')\nldexp = BinaryUFunc(_ldexp, 'ldexp')\nfrexp = UnaryUFunc2(_frexp, 'frexp')\nfloor = UnaryUFunc(_floor, 'floor')\nceil = UnaryUFunc(_ceil, 'ceil')\ntrunc = UnaryUFunc(_trunc, 'trunc')\n\ndef i0(x):\n    def i0_helper(x):\n        if isinstance(x, float) or isinstance(x, float32) or isinstance(x, float16):\n            return util.i0(x)\n        else:\n            return util.i0(util.cast(x, float))\n\n    x = asarray(x)\n    if x.dtype is complex or x.dtype is complex64:\n        compile_error(\"i0 is not supported for complex values\")\n    return x.map(i0_helper)\n\ndef sinc(x):\n    def sinc_helper(x):\n        if isinstance(x, float32):\n            if not x:\n                x = float32(1e-20)\n            x *= float32(pi)\n            return util.sin32(x) / x\n        elif isinstance(x, float16):\n            if not x:\n                x = float16(1e-7)\n            x *= float16(pi)\n            return util.sin16(x) / x\n        elif isinstance(x, complex):\n            if not x:\n                x = complex(1e-20)\n            x *= pi\n            return zmath.sin(x) / x\n        elif isinstance(x, complex64):\n            if not x:\n                x = complex64(1e-20)\n            x *= float32(pi)\n            return zmath.sin(x) / x\n        else:\n            x = util.cast(x, float)\n            if not x:\n                x = 1e-20\n            x *= pi\n            return util.sin64(x) / x\n\n    ans = asarray(x).map(sinc_helper)\n    if ans.ndim == 0:\n        return ans.item()\n    else:\n        return ans\n\ndef nancumprod(a, axis=None, dtype: type = NoneType, out=None):\n    a = asarray(a)\n\n    if dtype is NoneType:\n        return nancumprod(a, axis=axis, dtype=a.dtype, out=out)\n\n    if axis is None:\n        prod = util.cast(1, dtype)\n        if out is None:\n            result = empty((a.size, ), dtype=dtype)\n        else:\n            _check_out(out, (a.size, ))\n            result = out\n\n        i = 0\n        for idx in util.multirange(a.shape):\n            if not isnan(a._ptr(idx)[0]):\n                prod *= util.cast(a._ptr(idx)[0], dtype)\n            result._ptr((i, ))[0] = util.cast(prod, result.dtype)\n            i += 1\n        return result\n    else:\n        axis = util.normalize_axis_index(axis, a.ndim)\n\n    if out is None:\n        result = empty_like(a, dtype=dtype)\n    else:\n        _check_out(out, a.shape)\n        result = out\n\n    n = a.shape[axis]\n\n    for idx in util.multirange(util.tuple_delete(a.shape, axis)):\n        accum = util.cast(1, dtype)\n\n        for i in range(n):\n            full_idx = util.tuple_insert(idx, axis, i)\n            p = a._ptr(full_idx)\n            q = result._ptr(full_idx)\n            if not isnan(p[0]):\n                accum *= util.cast(p[0], dtype)\n            q[0] = util.cast(accum, result.dtype)\n\n    return result\n\ndef nancumsum(a, axis=None, dtype: type = NoneType, out=None):\n    a = asarray(a)\n\n    if dtype is NoneType:\n        return nancumsum(a, axis=axis, dtype=a.dtype, out=out)\n\n    if axis is None:\n        sum = util.cast(0, dtype)\n        if out is None:\n            result = empty((a.size, ), dtype=dtype)\n        else:\n            _check_out(out, (a.size, ))\n            result = out\n\n        i = 0\n        for idx in util.multirange(a.shape):\n            if not isnan(a._ptr(idx)[0]):\n                sum += util.cast(a._ptr(idx)[0], dtype)\n            result._ptr((i, ))[0] = util.cast(sum, result.dtype)\n            i += 1\n        return result\n    else:\n        axis = util.normalize_axis_index(axis, a.ndim)\n\n    if out is None:\n        result = empty_like(a, dtype=dtype)\n    else:\n        _check_out(out, a.shape)\n        result = out\n\n    n = a.shape[axis]\n\n    for idx in util.multirange(util.tuple_delete(a.shape, axis)):\n        accum = util.cast(0, dtype)\n\n        for i in range(n):\n            full_idx = util.tuple_insert(idx, axis, i)\n            p = a._ptr(full_idx)\n            q = result._ptr(full_idx)\n            if not isnan(p[0]):\n                accum += util.cast(p[0], dtype)\n            q[0] = util.cast(accum, result.dtype)\n\n    return result\n\n@extend\nclass ndarray:\n\n    def cumprod(\n        self,\n        axis=None,\n        dtype: type = NoneType,\n        out=None\n    ):\n        return cumprod(\n            self,\n            axis=axis,\n            dtype=dtype,\n            out=out\n        )\n\n    def cumsum(\n        self,\n        axis=None,\n        dtype: type = NoneType,\n        out=None\n    ):\n        return cumsum(\n            self,\n            axis=axis,\n            dtype=dtype,\n            out=out\n        )\n"
  },
  {
    "path": "stdlib/numpy/npdatetime.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport datetime\nimport math\nimport internal.static as static\n\n_DATETIME_NAT: Literal[int] = -9_223_372_036_854_775_808\n_DATETIME_MAX_ISO8601_STRLEN: Literal[int] = (21 + 3 * 5 + 1 + 3 * 6 + 6 + 1)\n\n_FR_ERROR: Literal[int] = -1  # error or undetermined\n_FR_Y: Literal[int] = 0  # Years\n_FR_M: Literal[int] = 1  # Months\n_FR_W: Literal[int] = 2  # Weeks\n_FR_D: Literal[int] = 4  # Days\n_FR_h: Literal[int] = 5  # hours\n_FR_m: Literal[int] = 6  # minutes\n_FR_s: Literal[int] = 7  # seconds\n_FR_ms: Literal[int] = 8  # milliseconds\n_FR_us: Literal[int] = 9  # microseconds\n_FR_ns: Literal[int] = 10  # nanoseconds\n_FR_ps: Literal[int] = 11  # picoseconds\n_FR_fs: Literal[int] = 12  # femtoseconds\n_FR_as: Literal[int] = 13  # attoseconds\n_FR_GENERIC: Literal[int] = 14  # unbound units, can convert to anything\n\n_DATETIME_NUMUNITS: Literal[int] = (_FR_GENERIC + 1)\n_DATETIME_DEFAULTUNIT: Literal[int] = _FR_GENERIC\n\n@tuple\nclass _time_t:\n    year: i16\n    yday: i16\n    sec: i8\n    min: i8\n    hour: i8\n    mday: i8\n    mon: i8\n    wday: i8\n    isdst: i8\n\n    def __new__() -> _time_t:\n        return _time_t(\n            i16(0), i16(0), i8(0), i8(0), i8(0), i8(0), i8(0), i8(0), i8(0)\n        )\n\n@pure\n@llvm\ndef _cdiv(a: T, b: T, T: type) -> T:\n    %c = sdiv {=T} %a, %b\n    ret {=T} %c\n\n@pure\n@llvm\ndef _cmod(a: T, b: T, T: type) -> T:\n    %c = srem {=T} %a, %b\n    ret {=T} %c\n\ndef _extract_unit_64(d: int, unit: int):\n    div = _cdiv(d, unit)\n    mod = _cmod(d, unit)\n    if mod < 0:\n        mod += unit\n        div -= 1\n    return div, mod\n\ndef _extract_unit_32(d: i32, unit: i32):\n    div = _cdiv(d, unit)\n    mod = _cmod(d, unit)\n    if mod < i32(0):\n        mod += unit\n        div -= i32(1)\n    return div, mod\n\n_datetime_strings = (\"Y\", \"M\", \"W\", \"<invalid>\", \"D\", \"h\", \"m\", \"s\", \"ms\",\n                     \"us\", \"ns\", \"ps\", \"fs\", \"as\", \"generic\")\n\n_days_per_month_table = ((31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31),\n                         (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31))\n\n_multiples_table = ((12, 52, 365, 0), (_FR_M, _FR_W, _FR_D, 0),\n                    (4, 30, 720, 0), (_FR_W, _FR_D, _FR_h, 0),\n                    (7, 168, 10080, 0), (_FR_D, _FR_h, _FR_m, 0), (0, 0, 0, 0),\n                    (0, 0, 0, 0), (24, 1440, 86400, 0), (_FR_h, _FR_m, _FR_s,\n                                                         0), (60, 3600, 0, 0),\n                    (_FR_m, _FR_s, 0, 0), (60, 60000, 0, 0),\n                    (_FR_s, _FR_ms, 0, 0), (1000, 1000000, 0, 0), (0, 0, 0, 0))\n\n_datetime_factors = (1, 1, 7, 1, 24, 60, 60, 1000, 1000, 1000, 1000, 1000,\n                     1000, 1, 0)\n\ndef _is_leapyear(year: int):\n    return year & 0x3 == 0 and (_cmod(year, 100) != 0 or _cmod(year, 400) == 0)\n\ndef _days_to_yearsdays(days: int):\n    days_per_400years: Literal[int] = 400 * 365 + 100 - 4 + 1\n    days = days - (365 * 30 + 7)\n    div, days = _extract_unit_64(days, days_per_400years)\n    year = 400 * div\n\n    if days >= 366:\n        year += 100 * ((days - 1) // (100 * 365 + 25 - 1))\n        days = (days - 1) % (100 * 365 + 25 - 1)\n        if days >= 365:\n            year += 4 * ((days + 1) // (4 * 365 + 1))\n            days = (days + 1) % (4 * 365 + 1)\n            if days >= 366:\n                year += (days - 1) // 365\n                days = (days - 1) % 365\n\n    return days, year + 2000\n\ndef _days_to_month_number(days: int):\n    days, year = _days_to_yearsdays(days)\n    month_lengths = _days_per_month_table[int(_is_leapyear(year))]\n\n    for i in range(12):\n        if days < month_lengths[i]:\n            return i + 1\n        else:\n            days -= month_lengths[i]\n\n    # should never be reached\n    return 1\n\ndef _get_datetime_units_factor(bigbase: int, littlebase: int):\n    factor = u64(1)\n    unit = bigbase\n\n    while unit < littlebase:\n        factor *= u64(_datetime_factors[unit])\n\n        if factor & u64(0xff00000000000000):\n            return u64(0)\n\n        unit += 1\n\n    return factor\n\ndef _uint64_euclidean_gcd(x: u64, y: u64):\n    if x > y:\n        tmp = x\n        x = y\n        y = tmp\n\n    while x != y and y:\n        tmp = x % y\n        x = y\n        y = tmp\n\n    return x\n\ndef _get_time():\n\n    @C\n    def time(a: cobj) -> int:\n        pass\n\n    return time(cobj())\n\ndef _get_localtime(ts: int = -1):\n    tm = _time_t()\n    if not _C.seq_localtime(ts, __ptr__(tm).as_byte()):\n        raise OSError(\"Failed to get local time\")\n    return tm\n\ndef _unit_error(base: Literal[str]):\n    compile_error(\"Invalid datetime unit in metadata string: \\\"[\" + base +\n                  \"]\\\"\")\n\ndef _validate_base(base: Literal[str]):\n    if (base != \"Y\" and base != \"M\" and base != \"W\" and base != \"D\"\n            and base != \"h\" and base != \"m\" and base != \"s\" and base != \"ms\"\n            and base != \"us\" and base != \"ns\" and base != \"ps\" and base != \"fs\"\n            and base != \"as\" and base != \"generic\"):\n        _unit_error(base)\n\ndef _validate_num(num: Literal[int]):\n    if num <= 0 or num > 0x7fffffff:\n        compile_error(\"Invalid datetime number (out of range 1..0x7fffffff)\")\n\ndef _base_code(base: Literal[str]):\n    if base == \"Y\":\n        return _FR_Y\n    elif base == \"M\":\n        return _FR_M\n    elif base == \"W\":\n        return _FR_W\n    elif base == \"D\":\n        return _FR_D\n    elif base == \"h\":\n        return _FR_h\n    elif base == \"m\":\n        return _FR_m\n    elif base == \"s\":\n        return _FR_s\n    elif base == \"ms\":\n        return _FR_ms\n    elif base == \"us\":\n        return _FR_us\n    elif base == \"ns\":\n        return _FR_ns\n    elif base == \"ps\":\n        return _FR_ps\n    elif base == \"fs\":\n        return _FR_fs\n    elif base == \"as\":\n        return _FR_as\n    elif base == \"generic\":\n        return _FR_GENERIC\n    else:\n        _unit_error(base)\n\ndef _base_code_nonstatic(base: str):\n    if base == \"Y\":\n        return _FR_Y\n    elif base == \"M\":\n        return _FR_M\n    elif base == \"W\":\n        return _FR_W\n    elif base == \"D\":\n        return _FR_D\n    elif base == \"h\":\n        return _FR_h\n    elif base == \"m\":\n        return _FR_m\n    elif base == \"s\":\n        return _FR_s\n    elif base == \"ms\":\n        return _FR_ms\n    elif base == \"us\":\n        return _FR_us\n    elif base == \"ns\":\n        return _FR_ns\n    elif base == \"ps\":\n        return _FR_ps\n    elif base == \"fs\":\n        return _FR_fs\n    elif base == \"as\":\n        return _FR_as\n    elif base == \"generic\":\n        return _FR_GENERIC\n    else:\n        raise ValueError(\n            f\"Invalid datetime unit in metadata string: \\\"[{base}]\\\"\")\n\ndef _base_str(base: Literal[str]):\n    if base == \"Y\":\n        return \"years\"\n    elif base == \"M\":\n        return \"months\"\n    elif base == \"W\":\n        return \"weeks\"\n    elif base == \"D\":\n        return \"days\"\n    elif base == \"h\":\n        return \"hours\"\n    elif base == \"m\":\n        return \"minutes\"\n    elif base == \"s\":\n        return \"seconds\"\n    elif base == \"ms\":\n        return \"milliseconds\"\n    elif base == \"us\":\n        return \"microseconds\"\n    elif base == \"ns\":\n        return \"nanoseconds\"\n    elif base == \"ps\":\n        return \"picoseconds\"\n    elif base == \"fs\":\n        return \"femtoseconds\"\n    elif base == \"as\":\n        return \"attoseconds\"\n    elif base == \"generic\":\n        return \"generic time units\"\n    else:\n        _unit_error(base)\n\nclass _Meta:\n    base: Literal[str]\n    num: Literal[int]\n\nclass _StaticIntWrapper:\n    n: Literal[int]\n\nclass _StaticStrWrapper:\n    s: Literal[str]\n\ndef _num_digits(s: Literal[str]):\n    if s == \"\":\n        return _StaticIntWrapper[0]()\n\n    if (s[0] == \"0\" or s[0] == \"1\" or\n        s[0] == \"2\" or s[0] == \"3\" or\n        s[0] == \"4\" or s[0] == \"5\" or\n        s[0] == \"6\" or s[0] == \"7\" or\n        s[0] == \"8\" or s[0] == \"9\"):\n        rest: Literal[int] = _num_digits(s[1:]).n\n        return _StaticIntWrapper[rest + 1]()\n\n    return _StaticIntWrapper[0]()\n\ndef _parse_static_str(s: Literal[str]):\n    if s == \"\":\n        return _StaticIntWrapper[0]()\n    else:\n        rest: Literal[int] = _parse_static_str(s[:-1]).n * 10\n\n        if s[-1] == \"0\":\n            return _StaticIntWrapper[rest + 0]()\n        elif s[-1] == \"1\":\n            return _StaticIntWrapper[rest + 1]()\n        elif s[-1] == \"2\":\n            return _StaticIntWrapper[rest + 2]()\n        elif s[-1] == \"3\":\n            return _StaticIntWrapper[rest + 3]()\n        elif s[-1] == \"4\":\n            return _StaticIntWrapper[rest + 4]()\n        elif s[-1] == \"5\":\n            return _StaticIntWrapper[rest + 5]()\n        elif s[-1] == \"6\":\n            return _StaticIntWrapper[rest + 6]()\n        elif s[-1] == \"7\":\n            return _StaticIntWrapper[rest + 7]()\n        elif s[-1] == \"8\":\n            return _StaticIntWrapper[rest + 8]()\n        elif s[-1] == \"9\":\n            return _StaticIntWrapper[rest + 9]()\n        else:\n            compile_error(\"invalid digit: \" + s[-1])\n\ndef _parse_datetime_helper(s: Literal[str], tlen: Literal[int]):\n    if s[-1] != \"]\":\n        compile_error(\"data type '\" + s + \"' not understood\")\n\n    num_digits: Literal[int] = _num_digits(s[tlen:]).n\n    num: Literal[int] = _parse_static_str(s[tlen:tlen + num_digits]).n if num_digits > 0 else 1\n    base: Literal[str] = s[tlen + num_digits:-1]\n    return _Meta[base, num]()\n\ndef _min_base(base1: Literal[str], base2: Literal[str]):\n    if base1 == base2:\n        return _StaticStrWrapper[base1]()\n    if base1 == \"generic\":\n        return _StaticStrWrapper[base2]()\n    if base2 == \"generic\":\n        return _StaticStrWrapper[base1]()\n    if base1 == \"Y\" and base2 == \"M\":\n        return _StaticStrWrapper[\"M\"]()\n    if base1 == \"Y\" and base2 == \"W\":\n        return _StaticStrWrapper[\"W\"]()\n    if base1 == \"Y\" and base2 == \"D\":\n        return _StaticStrWrapper[\"D\"]()\n    if base1 == \"Y\" and base2 == \"h\":\n        return _StaticStrWrapper[\"h\"]()\n    if base1 == \"Y\" and base2 == \"m\":\n        return _StaticStrWrapper[\"m\"]()\n    if base1 == \"Y\" and base2 == \"s\":\n        return _StaticStrWrapper[\"s\"]()\n    if base1 == \"Y\" and base2 == \"ms\":\n        return _StaticStrWrapper[\"ms\"]()\n    if base1 == \"Y\" and base2 == \"us\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"Y\" and base2 == \"ns\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"Y\" and base2 == \"ps\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"Y\" and base2 == \"fs\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"Y\" and base2 == \"as\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"M\" and base2 == \"Y\":\n        return _StaticStrWrapper[\"M\"]()\n    if base1 == \"M\" and base2 == \"W\":\n        return _StaticStrWrapper[\"W\"]()\n    if base1 == \"M\" and base2 == \"D\":\n        return _StaticStrWrapper[\"D\"]()\n    if base1 == \"M\" and base2 == \"h\":\n        return _StaticStrWrapper[\"h\"]()\n    if base1 == \"M\" and base2 == \"m\":\n        return _StaticStrWrapper[\"m\"]()\n    if base1 == \"M\" and base2 == \"s\":\n        return _StaticStrWrapper[\"s\"]()\n    if base1 == \"M\" and base2 == \"ms\":\n        return _StaticStrWrapper[\"ms\"]()\n    if base1 == \"M\" and base2 == \"us\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"M\" and base2 == \"ns\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"M\" and base2 == \"ps\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"M\" and base2 == \"fs\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"M\" and base2 == \"as\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"W\" and base2 == \"Y\":\n        return _StaticStrWrapper[\"W\"]()\n    if base1 == \"W\" and base2 == \"M\":\n        return _StaticStrWrapper[\"W\"]()\n    if base1 == \"W\" and base2 == \"D\":\n        return _StaticStrWrapper[\"D\"]()\n    if base1 == \"W\" and base2 == \"h\":\n        return _StaticStrWrapper[\"h\"]()\n    if base1 == \"W\" and base2 == \"m\":\n        return _StaticStrWrapper[\"m\"]()\n    if base1 == \"W\" and base2 == \"s\":\n        return _StaticStrWrapper[\"s\"]()\n    if base1 == \"W\" and base2 == \"ms\":\n        return _StaticStrWrapper[\"ms\"]()\n    if base1 == \"W\" and base2 == \"us\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"W\" and base2 == \"ns\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"W\" and base2 == \"ps\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"W\" and base2 == \"fs\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"W\" and base2 == \"as\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"D\" and base2 == \"Y\":\n        return _StaticStrWrapper[\"D\"]()\n    if base1 == \"D\" and base2 == \"M\":\n        return _StaticStrWrapper[\"D\"]()\n    if base1 == \"D\" and base2 == \"W\":\n        return _StaticStrWrapper[\"D\"]()\n    if base1 == \"D\" and base2 == \"h\":\n        return _StaticStrWrapper[\"h\"]()\n    if base1 == \"D\" and base2 == \"m\":\n        return _StaticStrWrapper[\"m\"]()\n    if base1 == \"D\" and base2 == \"s\":\n        return _StaticStrWrapper[\"s\"]()\n    if base1 == \"D\" and base2 == \"ms\":\n        return _StaticStrWrapper[\"ms\"]()\n    if base1 == \"D\" and base2 == \"us\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"D\" and base2 == \"ns\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"D\" and base2 == \"ps\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"D\" and base2 == \"fs\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"D\" and base2 == \"as\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"h\" and base2 == \"Y\":\n        return _StaticStrWrapper[\"h\"]()\n    if base1 == \"h\" and base2 == \"M\":\n        return _StaticStrWrapper[\"h\"]()\n    if base1 == \"h\" and base2 == \"W\":\n        return _StaticStrWrapper[\"h\"]()\n    if base1 == \"h\" and base2 == \"D\":\n        return _StaticStrWrapper[\"h\"]()\n    if base1 == \"h\" and base2 == \"m\":\n        return _StaticStrWrapper[\"m\"]()\n    if base1 == \"h\" and base2 == \"s\":\n        return _StaticStrWrapper[\"s\"]()\n    if base1 == \"h\" and base2 == \"ms\":\n        return _StaticStrWrapper[\"ms\"]()\n    if base1 == \"h\" and base2 == \"us\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"h\" and base2 == \"ns\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"h\" and base2 == \"ps\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"h\" and base2 == \"fs\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"h\" and base2 == \"as\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"m\" and base2 == \"Y\":\n        return _StaticStrWrapper[\"m\"]()\n    if base1 == \"m\" and base2 == \"M\":\n        return _StaticStrWrapper[\"m\"]()\n    if base1 == \"m\" and base2 == \"W\":\n        return _StaticStrWrapper[\"m\"]()\n    if base1 == \"m\" and base2 == \"D\":\n        return _StaticStrWrapper[\"m\"]()\n    if base1 == \"m\" and base2 == \"h\":\n        return _StaticStrWrapper[\"m\"]()\n    if base1 == \"m\" and base2 == \"s\":\n        return _StaticStrWrapper[\"s\"]()\n    if base1 == \"m\" and base2 == \"ms\":\n        return _StaticStrWrapper[\"ms\"]()\n    if base1 == \"m\" and base2 == \"us\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"m\" and base2 == \"ns\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"m\" and base2 == \"ps\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"m\" and base2 == \"fs\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"m\" and base2 == \"as\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"s\" and base2 == \"Y\":\n        return _StaticStrWrapper[\"s\"]()\n    if base1 == \"s\" and base2 == \"M\":\n        return _StaticStrWrapper[\"s\"]()\n    if base1 == \"s\" and base2 == \"W\":\n        return _StaticStrWrapper[\"s\"]()\n    if base1 == \"s\" and base2 == \"D\":\n        return _StaticStrWrapper[\"s\"]()\n    if base1 == \"s\" and base2 == \"h\":\n        return _StaticStrWrapper[\"s\"]()\n    if base1 == \"s\" and base2 == \"m\":\n        return _StaticStrWrapper[\"s\"]()\n    if base1 == \"s\" and base2 == \"ms\":\n        return _StaticStrWrapper[\"ms\"]()\n    if base1 == \"s\" and base2 == \"us\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"s\" and base2 == \"ns\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"s\" and base2 == \"ps\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"s\" and base2 == \"fs\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"s\" and base2 == \"as\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"ms\" and base2 == \"Y\":\n        return _StaticStrWrapper[\"ms\"]()\n    if base1 == \"ms\" and base2 == \"M\":\n        return _StaticStrWrapper[\"ms\"]()\n    if base1 == \"ms\" and base2 == \"W\":\n        return _StaticStrWrapper[\"ms\"]()\n    if base1 == \"ms\" and base2 == \"D\":\n        return _StaticStrWrapper[\"ms\"]()\n    if base1 == \"ms\" and base2 == \"h\":\n        return _StaticStrWrapper[\"ms\"]()\n    if base1 == \"ms\" and base2 == \"m\":\n        return _StaticStrWrapper[\"ms\"]()\n    if base1 == \"ms\" and base2 == \"s\":\n        return _StaticStrWrapper[\"ms\"]()\n    if base1 == \"ms\" and base2 == \"us\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"ms\" and base2 == \"ns\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"ms\" and base2 == \"ps\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"ms\" and base2 == \"fs\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"ms\" and base2 == \"as\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"us\" and base2 == \"Y\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"us\" and base2 == \"M\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"us\" and base2 == \"W\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"us\" and base2 == \"D\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"us\" and base2 == \"h\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"us\" and base2 == \"m\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"us\" and base2 == \"s\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"us\" and base2 == \"ms\":\n        return _StaticStrWrapper[\"us\"]()\n    if base1 == \"us\" and base2 == \"ns\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"us\" and base2 == \"ps\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"us\" and base2 == \"fs\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"us\" and base2 == \"as\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"ns\" and base2 == \"Y\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"ns\" and base2 == \"M\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"ns\" and base2 == \"W\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"ns\" and base2 == \"D\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"ns\" and base2 == \"h\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"ns\" and base2 == \"m\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"ns\" and base2 == \"s\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"ns\" and base2 == \"ms\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"ns\" and base2 == \"us\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base1 == \"ns\" and base2 == \"ps\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"ns\" and base2 == \"fs\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"ns\" and base2 == \"as\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"ps\" and base2 == \"Y\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"ps\" and base2 == \"M\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"ps\" and base2 == \"W\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"ps\" and base2 == \"D\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"ps\" and base2 == \"h\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"ps\" and base2 == \"m\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"ps\" and base2 == \"s\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"ps\" and base2 == \"ms\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"ps\" and base2 == \"us\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"ps\" and base2 == \"ns\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base1 == \"ps\" and base2 == \"fs\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"ps\" and base2 == \"as\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"fs\" and base2 == \"Y\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"fs\" and base2 == \"M\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"fs\" and base2 == \"W\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"fs\" and base2 == \"D\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"fs\" and base2 == \"h\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"fs\" and base2 == \"m\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"fs\" and base2 == \"s\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"fs\" and base2 == \"ms\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"fs\" and base2 == \"us\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"fs\" and base2 == \"ns\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"fs\" and base2 == \"ps\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base1 == \"fs\" and base2 == \"as\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"as\" and base2 == \"Y\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"as\" and base2 == \"M\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"as\" and base2 == \"W\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"as\" and base2 == \"D\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"as\" and base2 == \"h\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"as\" and base2 == \"m\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"as\" and base2 == \"s\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"as\" and base2 == \"ms\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"as\" and base2 == \"us\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"as\" and base2 == \"ns\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"as\" and base2 == \"ps\":\n        return _StaticStrWrapper[\"as\"]()\n    if base1 == \"as\" and base2 == \"fs\":\n        return _StaticStrWrapper[\"as\"]()\n    compile_error(\"unknown units: \" + base1 + \" and/or \" + base2)\n\ndef _base_factor(base: Literal[str]):\n    if base == \"Y\":\n        return _StaticIntWrapper[1]()\n    if base == \"M\":\n        return _StaticIntWrapper[1]()\n    if base == \"W\":\n        return _StaticIntWrapper[7]()\n    if base == \"D\":\n        return _StaticIntWrapper[24]()\n    if base == \"h\":\n        return _StaticIntWrapper[60]()\n    if base == \"m\":\n        return _StaticIntWrapper[60]()\n    if base == \"s\":\n        return _StaticIntWrapper[1000]()\n    if base == \"ms\":\n        return _StaticIntWrapper[1000]()\n    if base == \"us\":\n        return _StaticIntWrapper[1000]()\n    if base == \"ns\":\n        return _StaticIntWrapper[1000]()\n    if base == \"ps\":\n        return _StaticIntWrapper[1000]()\n    if base == \"fs\":\n        return _StaticIntWrapper[1000]()\n    if base == \"as\":\n        return _StaticIntWrapper[1]()\n    if base == \"generic\":\n        return _StaticIntWrapper[0]()\n    compile_error(\"[internal error] no factor for base: \" + base)\n\ndef _next_base(base: Literal[str]):\n    if base == \"Y\":\n        return _StaticStrWrapper[\"M\"]()\n    if base == \"M\":\n        return _StaticStrWrapper[\"W\"]()\n    if base == \"W\":\n        return _StaticStrWrapper[\"D\"]()\n    if base == \"D\":\n        return _StaticStrWrapper[\"h\"]()\n    if base == \"h\":\n        return _StaticStrWrapper[\"m\"]()\n    if base == \"m\":\n        return _StaticStrWrapper[\"s\"]()\n    if base == \"s\":\n        return _StaticStrWrapper[\"ms\"]()\n    if base == \"ms\":\n        return _StaticStrWrapper[\"us\"]()\n    if base == \"us\":\n        return _StaticStrWrapper[\"ns\"]()\n    if base == \"ns\":\n        return _StaticStrWrapper[\"ps\"]()\n    if base == \"ps\":\n        return _StaticStrWrapper[\"fs\"]()\n    if base == \"fs\":\n        return _StaticStrWrapper[\"as\"]()\n    if base == \"as\":\n        return _StaticStrWrapper[\"generic\"]()\n    if base == \"generic\":\n        return _StaticStrWrapper[\"<invalid>\"]()\n    compile_error(\"[internal error] no next base for: \" + base)\n\ndef _get_units_factor(bigbase: Literal[str],\n                      littlebase: Literal[str],\n                      factor: Literal[int] = 1):\n    minbase: Literal[str] = _min_base(bigbase, littlebase).s\n    if bigbase != littlebase and minbase == littlebase:\n        next_factor: Literal[int] = factor * _base_factor(bigbase).n\n        if next_factor & 0xff00000000000000 != 0:\n            compile_error(\"Integer overflow getting a common metadata divisor\")\n        return _get_units_factor(\n            _next_base(bigbase).s, littlebase, next_factor)\n    else:\n        return _StaticIntWrapper[factor]()\n\ndef _static_gcd(x: Literal[int], y: Literal[int]):\n    if y == 0:\n        return _StaticIntWrapper[x]()\n    else:\n        return _static_gcd(y, x % y)\n\ndef _meta_gcd(meta1: _Meta, meta2: _Meta, strict1: Literal[bool],\n              strict2: Literal[bool]):\n\n    def incompatible_units(base1: Literal[str], base2: Literal[str]):\n        compile_error(\n            \"Cannot get a common metadata divisor for Numpy datetime metadata [\"\n            + base1 + \"] and [\" + base2 +\n            \"] because they have incompatible nonlinear \" + \"base time units.\")\n\n    def overflow():\n        compile_error(\"Integer overflow getting a common metadata divisor\")\n\n    def make_meta(base: Literal[str], num1: Literal[int], num2: Literal[int],\n                  f1: Literal[int], f2: Literal[int]):\n        num: Literal[int] = _static_gcd(num1 * f1, num2 * f2).n\n        if num <= 0 or num > 0x7fffffff:\n            overflow()\n        return _Meta[base, num]()\n\n    if meta1.base == \"generic\":\n        return meta2\n\n    if meta2.base == \"generic\":\n        return meta1\n\n    if meta1.base == meta2.base:\n        return make_meta(meta1.base, meta1.num, meta2.num, 1, 1)\n\n    f_12: Literal[int] = _get_units_factor(meta1.base, meta2.base).n\n    f_21: Literal[int] = _get_units_factor(meta2.base, meta1.base).n\n    f1: Literal[int] = f_12 if (\n        _min_base(meta1.base, meta2.base).s == meta2.base\n        and meta1.base != meta2.base) else 1\n    f2: Literal[int] = f_21 if (\n        _min_base(meta1.base, meta2.base).s == meta1.base\n        and meta1.base != meta2.base) else 1\n\n    if meta1.base == \"Y\":\n        if meta2.base == \"M\":\n            return make_meta(\"M\", meta1.num * 12, meta2.num, f1, f2)\n        elif strict1:\n            incompatible_units(meta1.base, meta2.base)\n        else:\n            return make_meta(meta2.base, meta1.num, meta2.num, f1, f2)\n    elif meta2.base == \"Y\":\n        if meta1.base == \"M\":\n            return make_meta(\"M\", meta1.num, meta2.num * 12)\n        elif strict2:\n            incompatible_units(meta1.base, meta2.base)\n        else:\n            return make_meta(meta1.base, meta1.num, meta2.num, f1, f2)\n    elif meta1.base == \"M\":\n        if strict1:\n            incompatible_units(meta1.base, meta2.base)\n        else:\n            return make_meta(meta2.base, meta1.num, meta2.num, f1, f2)\n    elif meta2.base == \"M\":\n        if strict2:\n            incompatible_units(meta1.base, meta2.base)\n        else:\n            return make_meta(meta1.base, meta1.num, meta2.num, f1, f2)\n    else:\n        return make_meta(\n            _min_base(meta1.base, meta2.base).s, meta1.num, meta2.num, f1, f2)\n\ndef _can_cast_units(src_unit: int,\n                    dst_unit: int,\n                    casting: Literal[str] = 'same_kind'):\n    if casting == 'unsafe':\n        return True\n    elif casting == 'same_kind':\n        if src_unit == _FR_GENERIC or dst_unit == _FR_GENERIC:\n            return src_unit == _FR_GENERIC\n        else:\n            return True\n    elif casting == 'safe':\n        if src_unit == _FR_GENERIC or dst_unit == _FR_GENERIC:\n            return src_unit == _FR_GENERIC\n        else:\n            return src_unit <= dst_unit\n    else:\n        return src_unit == dst_unit\n\n@tuple\nclass DatetimeMetaData:\n    base: i32\n    num: i32\n\n@tuple\nclass timedelta64:\n    value: int\n    base: Literal[str]\n    num: Literal[int]\n\n@tuple\nclass datetime64:\n    value: int\n    base: Literal[str]\n    num: Literal[int]\n\nclass datetimestruct:\n    year: int\n    month: i32\n    day: i32\n    hour: i32\n    min: i32\n    sec: i32\n    us: i32\n    ps: i32\n    as_: i32\n\ndef _parse_datetime_type(s: Literal[str]):\n    if s[:11] == \"datetime64[\":\n        meta = _parse_datetime_helper(s, 11)\n        return datetime64[meta.base, meta.num]()\n    elif s[:12] == \"timedelta64[\":\n        meta = _parse_datetime_helper(s, 12)\n        return timedelta64[meta.base, meta.num]()\n    elif s[:3] == \"M8[\":\n        meta = _parse_datetime_helper(s, 3)\n        return datetime64[meta.base, meta.num]()\n    elif s[:3] == \"m8[\":\n        meta = _parse_datetime_helper(s, 3)\n        return timedelta64[meta.base, meta.num]()\n    else:\n        compile_error(\"unknown datetime type: \" + s)\n\ndef _promote(T1: type, T2: type):\n    meta1 = _Meta[T1.base, T1.num]()\n    meta2 = _Meta[T2.base, T2.num]()\n    strict1: Literal[bool] = isinstance(T1, timedelta64)\n    strict2: Literal[bool] = isinstance(T2, timedelta64)\n    meta_out = _meta_gcd(meta1, meta2, strict1, strict2)\n\n    if isinstance(T1, datetime64) or isinstance(T2, datetime64):\n        return datetime64[meta_out.base, meta_out.num]()\n    else:\n        return timedelta64[meta_out.base, meta_out.num]()\n\ndef _coerce(d1, d2):\n    T = type(_promote(type(d1), type(d2)))\n\n    if isinstance(d1, datetime64):\n        r1 = d1._cast(datetime64[T.base, T.num])\n    else:\n        r1 = d1._cast(timedelta64[T.base, T.num])\n\n    if isinstance(d2, datetime64):\n        r2 = d2._cast(datetime64[T.base, T.num])\n    else:\n        r2 = d2._cast(timedelta64[T.base, T.num])\n\n    return r1, r2\n\n@extend\nclass DatetimeMetaData:\n\n    def __new__(base: int, num: int = 1) -> DatetimeMetaData:\n        return DatetimeMetaData(i32(base), i32(num))\n\n    def __new__(base: Literal[str], num: int = 1) -> DatetimeMetaData:\n        return DatetimeMetaData(base=_base_code(base), num=num)\n\n    def __new__(dt: datetime64):\n        return DatetimeMetaData(base=_base_code(dt.base), num=dt.num)\n\n    def __new__(td: timedelta64):\n        return DatetimeMetaData(base=_base_code(td.base), num=td.num)\n\n    def divisor_to_multiple(self, den: int, metastr: str):\n        base = self.base\n\n        if base == i32(_FR_GENERIC):\n            raise ValueError(\"Can't use 'den' divisor with generic units\")\n\n        baseunit = (0, ) * 2\n        totry = (0, ) * 4\n        num = 3\n\n        if base == i32(_FR_W):\n            num = 4\n        elif base == i32(_FR_D):\n            num = 2\n        elif base >= i32(_FR_s):\n            ind = (_FR_s - _FR_Y) * 2\n            totry = _multiples_table[ind]\n            baseunit = _multiples_table[ind + 1]\n            baseunit = (int(base) + 1, int(base) + 2) + baseunit[2:]\n            if base == i32(_FR_as - 1):\n                num = 1\n            if base == i32(_FR_as):\n                num = 0\n        else:\n            ind = (int(self.base) - _FR_Y) * 2\n            totry = _multiples_table[ind]\n            baseunit = _multiples_table[ind + 1]\n\n        q = i32(0)\n        r = i32(0)\n\n        for i in range(num):\n            q = totry[i] // den\n            r = totry[i] % den\n            if r == i32(0):\n                break\n\n        if i == num:\n            if not metastr:\n                msg = f\"divisor ({den}) is not a multiple of a lower-unit in datetime metadata\"\n            else:\n                msg = f\"divisor ({den}) is not a multiple of a lower-unit in datetime metadata \\\"{metastr}\\\"\"\n            raise ValueError(msg)\n\n        return DatetimeMetaData(base=baseunit[i], num=int(self.num * q))\n\n    def conversion_factor(self, dst: DatetimeMetaData):\n        num = u64(1)\n        denom = u64(1)\n\n        if self.base == i32(_FR_GENERIC):\n            return (1, 1)\n        elif dst.base == i32(_FR_GENERIC):\n            raise ValueError(\"Cannot convert from specific units to generic \"\n                             \"units in NumPy datetimes or timedeltas\")\n\n        swapped = False\n        if self.base <= dst.base:\n            src_base = int(self.base)\n            dst_base = int(dst.base)\n        else:\n            src_base = int(dst.base)\n            dst_base = int(self.base)\n            swapped = True\n\n        if src_base != dst_base:\n            if src_base == _FR_Y:\n                if dst_base == _FR_M:\n                    num *= u64(12)\n                elif dst_base == _FR_W:\n                    num *= u64(97 + 400 * 365)\n                    denom *= u64(400 * 7)\n                else:\n                    num *= u64(97 + 400 * 365)\n                    denom *= u64(400)\n                    num *= _get_datetime_units_factor(_FR_D, dst_base)\n            elif src_base == _FR_M:\n                if dst_base == _FR_W:\n                    num *= u64(97 + 400 * 365)\n                    denom *= u64(400 * 12 * 7)\n                else:\n                    num *= u64(97 + 400 * 365)\n                    denom *= u64(400 * 12)\n                    num *= _get_datetime_units_factor(_FR_D, dst_base)\n            else:\n                num *= _get_datetime_units_factor(src_base, dst_base)\n\n        if not num:\n            raise OverflowError(\n                f\"Integer overflow while computing the conversion factor between NumPy datetime units {_datetime_strings[src_base]} and {_datetime_strings[dst_base]}\"\n            )\n\n        if swapped:\n            tmp = num\n            num = denom\n            denom = tmp\n\n        num *= u64(int(self.num))\n        denom *= u64(int(dst.num))\n        gcd = _uint64_euclidean_gcd(num, denom)\n        return int(num // gcd), int(denom // gcd)\n\n@extend\nclass timedelta64:\n\n    def __new__() -> timedelta64[base, num]:\n        return timedelta64[base, num](0)\n\n    def __new__(value: int,\n                base: Literal[str],\n                num: Literal[int]) -> timedelta64[base, num]:\n        _validate_base(base)\n        _validate_num(num)\n        return superf(value)\n\n    def __new__(td: timedelta64,\n                base: Literal[str],\n                num: Literal[int]) -> timedelta64[base, num]:\n        _validate_base(base)\n        _validate_num(num)\n        return td._cast(timedelta64[base, num])\n\n    def __new__(s: str,\n                base: Literal[str],\n                num: Literal[int]) -> timedelta64[base, num]:\n        _validate_base(base)\n        _validate_num(num)\n        if (len(s) == 3 and (s.ptr[0] == byte(78) or s.ptr[0] == byte(110))\n                and (s.ptr[1] == byte(65) or s.ptr[1] == byte(97))\n                and (s.ptr[2] == byte(116) or s.ptr[2] == byte(84))):\n            return timedelta64[base, num](_DATETIME_NAT)\n        else:\n            return timedelta64[base, num](int(s))\n\n    def __new__(value: int, unit: Literal[str]):\n        TD = _parse_datetime_type(\"timedelta64[\" + unit + \"]\")\n        return timedelta64(value, TD.base, TD.num)\n\n    def __new__(td: timedelta64, unit: Literal[str] = \"generic\"):\n        TD = _parse_datetime_type(\"timedelta64[\" + unit + \"]\")\n        return timedelta64(td, TD.base, TD.num)\n\n    def __new__(s: str, unit: Literal[str] = \"generic\"):\n        TD = _parse_datetime_type(\"timedelta64[\" + unit + \"]\")\n        return timedelta64(s, TD.base, TD.num)\n\n    def __new__(td: datetime.timedelta, unit: Literal[str] = \"us\"):\n        TD = _parse_datetime_type(\"timedelta64[\" + unit + \"]\")\n        return timedelta64(td._microseconds, \"us\", 1)._cast(type(TD))\n\n    def _cast(self, TD: type):\n        if not isinstance(TD, timedelta64):\n            compile_error(\n                \"[internal error] can only cast timedelta64 to timedelta64\")\n\n        meta1 = DatetimeMetaData(self.base, self.num)\n        meta2 = DatetimeMetaData(TD.base, TD.num)\n        num, denom = meta1.conversion_factor(meta2)\n\n        if num == 0:\n            raise TypeError(\n                f\"cannot cast {self.__class__.__name__} to {TD.__name__}\")\n\n        if self._nat:\n            return timedelta64(_DATETIME_NAT, TD.base, TD.num)\n        elif self.value < 0:\n            return timedelta64(_cdiv(self.value * num - (denom - 1), denom),\n                               TD.base, TD.num)\n        else:\n            return timedelta64(_cdiv(self.value * num, denom), TD.base, TD.num)\n\n    def _code():\n        return _base_code(base)\n\n    @property\n    def _nat(self):\n        return self.value == _DATETIME_NAT\n\n    def tolist(self):\n        v = self.num * self.value\n        if (base == \"M\" or base == \"ns\" or base == \"ps\" or base == \"fs\"\n                or base == \"as\" or base == \"generic\"):\n            return self.value\n        elif base == \"Y\":\n            return datetime.timedelta(days=(365 * v))\n        elif base == \"W\":\n            return datetime.timedelta(weeks=v)\n        elif base == \"D\":\n            return datetime.timedelta(days=v)\n        elif base == \"h\":\n            return datetime.timedelta(hours=v)\n        elif base == \"m\":\n            return datetime.timedelta(minutes=v)\n        elif base == \"s\":\n            return datetime.timedelta(seconds=v)\n        elif base == \"ms\":\n            return datetime.timedelta(milliseconds=v)\n        elif base == \"us\":\n            return datetime.timedelta(microseconds=v)\n        else:\n            _unit_error(base)\n\n    def __eq__(self, other: timedelta64):\n        if self._nat or other._nat:\n            return False\n        else:\n            self, other = _coerce(self, other)\n            return self.value == other.value\n\n    def __ne__(self, other: timedelta64):\n        if self._nat or other._nat:\n            return True\n        else:\n            self, other = _coerce(self, other)\n            return self.value != other.value\n\n    def __lt__(self, other: timedelta64):\n        if self._nat or other._nat:\n            return False\n        else:\n            self, other = _coerce(self, other)\n            return self.value < other.value\n\n    def __gt__(self, other: timedelta64):\n        if self._nat or other._nat:\n            return False\n        else:\n            self, other = _coerce(self, other)\n            return self.value > other.value\n\n    def __le__(self, other: timedelta64):\n        if self._nat or other._nat:\n            return False\n        else:\n            self, other = _coerce(self, other)\n            return self.value <= other.value\n\n    def __ge__(self, other: timedelta64):\n        if self._nat or other._nat:\n            return False\n        else:\n            self, other = _coerce(self, other)\n            return self.value >= other.value\n\n    def __bool__(self):\n        return bool(self.value)\n\n    def __pos__(self):\n        return self\n\n    def __neg__(self):\n        if self._nat:\n            return self\n        else:\n            return timedelta64(-self.value, base, num)\n\n    def __abs__(self):\n        if self._nat:\n            return self\n        else:\n            v = self.value\n            return timedelta64(v if v >= 0 else -v, base, num)\n\n    def _sign(self):\n        v = self.value\n        return timedelta64(1 if v > 0 else (-1 if v < 0 else 0), base, num)\n\n    def __add__(self, other):\n        if (isinstance(other, int) or\n            isinstance(other, bool) or\n            isinstance(other, byte) or\n            isinstance(other, Int) or\n            isinstance(other, UInt)):\n            return self + int(other)\n        else:\n            compile_error(\"cannot add timedelta64 and non-int type\")\n\n    def __radd__(self, other):\n        if (isinstance(other, int) or\n            isinstance(other, bool) or\n            isinstance(other, byte) or\n            isinstance(other, Int) or\n            isinstance(other, UInt)):\n            return int(other) + self\n        else:\n            compile_error(\"cannot add timedelta64 and non-int type\")\n\n    def __add__(self, other: timedelta64):\n        self, other = _coerce(self, other)\n        if self._nat or other._nat:\n            return timedelta64(_DATETIME_NAT, self.base, self.num)\n        else:\n            return timedelta64(self.value + other.value, self.base, self.num)\n\n    def __add__(self, other: datetime64):\n        self, other = _coerce(self, other)\n        if self._nat or other._nat:\n            return datetime64(_DATETIME_NAT, self.base, self.num)\n        else:\n            return datetime64(self.value + other.value, self.base, self.num)\n\n    def __add__(self, other: int):\n        return self + timedelta64(other, base, num)\n\n    def __radd__(self, other: int):\n        return self + timedelta64(other, base, num)\n\n    def __sub__(self, other):\n        if (isinstance(other, int) or\n            isinstance(other, bool) or\n            isinstance(other, byte) or\n            isinstance(other, Int) or\n            isinstance(other, UInt)):\n            return self - int(other)\n        else:\n            compile_error(\"cannot subtract timedelta64 and non-int type\")\n\n    def __rsub__(self, other):\n        if (isinstance(other, int) or\n            isinstance(other, bool) or\n            isinstance(other, byte) or\n            isinstance(other, Int) or\n            isinstance(other, UInt)):\n            return int(other) - self\n        else:\n            compile_error(\"cannot subtract timedelta64 and non-int type\")\n\n    def __sub__(self, other: timedelta64):\n        self, other = _coerce(self, other)\n        if self._nat or other._nat:\n            return timedelta64(_DATETIME_NAT, self.base, self.num)\n        else:\n            return timedelta64(self.value - other.value, self.base, self.num)\n\n    def __sub__(self, other: int):\n        return self - timedelta64(other, base, num)\n\n    def __rsub__(self, other: int):\n        return (-self) + other\n\n    def __mul__(self, other):\n        if (isinstance(other, int) or\n            isinstance(other, bool) or\n            isinstance(other, byte) or\n            isinstance(other, Int) or\n            isinstance(other, UInt)):\n            return self * int(other)\n        elif (isinstance(other, float) or\n              isinstance(other, float32) or\n              isinstance(other, float16)):\n            return self * float(other)\n        else:\n            compile_error(\"cannot multiply timedelta64 and non-int, non-float type\")\n\n    def __rmul__(self, other):\n        if (isinstance(other, int) or\n            isinstance(other, bool) or\n            isinstance(other, byte) or\n            isinstance(other, Int) or\n            isinstance(other, UInt)):\n            return int(other) * self\n        elif (isinstance(other, float) or\n              isinstance(other, float32) or\n              isinstance(other, float16)):\n            return float(other) * self\n        else:\n            compile_error(\"cannot multiply timedelta64 and non-int, non-float type\")\n\n    def __mul__(self, other: float):\n        if self._nat:\n            return timedelta64(_DATETIME_NAT, base, num)\n        else:\n            x = self.value * other\n            if math.isfinite(x):\n                return timedelta64(int(x), base)\n            else:\n                return timedelta64(_DATETIME_NAT, base, num)\n\n    def __rmul__(self, other: float):\n        if self._nat:\n            return timedelta64(_DATETIME_NAT, base, num)\n        else:\n            x = self.value * other\n            if math.isfinite(x):\n                return timedelta64(int(x), base)\n            else:\n                return timedelta64(_DATETIME_NAT, base, num)\n\n    def __mul__(self, other: int):\n        if self._nat:\n            return timedelta64(_DATETIME_NAT, base, num)\n        else:\n            return timedelta64(self.value * other, base, num)\n\n    def __rmul__(self, other: int):\n        if self._nat:\n            return timedelta64(_DATETIME_NAT, base, num)\n        else:\n            return timedelta64(self.value * other, base, num)\n\n    def __floordiv__(self, other):\n        if (isinstance(other, int) or\n            isinstance(other, bool) or\n            isinstance(other, byte) or\n            isinstance(other, Int) or\n            isinstance(other, UInt)):\n            return self // int(other)\n        elif (isinstance(other, float) or\n              isinstance(other, float32) or\n              isinstance(other, float16)):\n            return self // float(other)\n        else:\n            compile_error(\"cannot floordiv timedelta64 and non-int, non-float type\")\n\n    def __floordiv__(self, other: timedelta64):\n        if self._nat or other._nat:\n            return 0\n        else:\n            self, other = _coerce(self, other)\n            in1 = self.value\n            in2 = other.value\n\n            if in2 == 0:\n                return 0\n            else:\n                x = _cdiv(in1, in2)\n                if (in1 > 0) != (in2 > 0) and x * in2 != in1:\n                    x -= 1\n                return x\n\n    def __floordiv__(self, other: float):\n        if self._nat or other == 0.0 or math.isnan(other):\n            return timedelta64(_DATETIME_NAT, base, num)\n        else:\n            return timedelta64(int(float(self.value) / other), base, num)\n\n    def __floordiv__(self, other: int):\n        if self._nat or other == 0:\n            return timedelta64(_DATETIME_NAT, base, num)\n        else:\n            return timedelta64(_cdiv(self.value, other), base, num)\n\n    def __truediv__(self, other):\n        if (isinstance(other, int) or\n            isinstance(other, bool) or\n            isinstance(other, byte) or\n            isinstance(other, Int) or\n            isinstance(other, UInt)):\n            return self / int(other)\n        elif (isinstance(other, float) or\n              isinstance(other, float32) or\n              isinstance(other, float16)):\n            return self / float(other)\n        else:\n            compile_error(\"cannot truediv timedelta64 and non-int, non-float type\")\n\n    def __truediv__(self, other: timedelta64):\n        self, other = _coerce(self, other)\n        if self._nat or other._nat:\n            return math.nan\n        else:\n            return self.value / other.value\n\n    def __truediv__(self, other: float):\n        if self._nat or other == 0.0 or math.isnan(other):\n            return timedelta64(_DATETIME_NAT, base, num)\n        else:\n            return timedelta64(int(float(self.value) / other), base, num)\n\n    def __truediv__(self, other: int):\n        if self._nat or other == 0:\n            return timedelta64(_DATETIME_NAT, base, num)\n        else:\n            return timedelta64(int(float(self.value) / other), base, num)\n\n    def __mod__(self, other: timedelta64):\n        self, other = _coerce(self, other)\n        if self._nat or other._nat:\n            return timedelta64(_DATETIME_NAT, self.base, self.num)\n        else:\n            in1 = self.value\n            in2 = other.value\n\n            if in2 == 0:\n                return timedelta64(_DATETIME_NAT, self.base, self.num)\n            else:\n                rem = _cmod(in1, in2)\n                if (in1 > 0) == (in2 > 0) or rem == 0:\n                    return timedelta64(rem, self.base)\n                else:\n                    return timedelta64(rem + in2, self.base, self.num)\n\n    def __divmod__(self, other: timedelta64):\n        self, other = _coerce(self, other)\n        if self._nat or other._nat:\n            return (0, timedelta64(_DATETIME_NAT, self.base, self.num))\n        else:\n            in1 = self.value\n            in2 = other.value\n\n            if in2 == 0:\n                return (0, timedelta64(_DATETIME_NAT, self.base, self.num))\n            else:\n                quo = _cdiv(in1, in2)\n                rem = _cmod(in1, in2)\n                if (in1 > 0) == (in2 > 0) or rem == 0:\n                    return (quo, timedelta64(rem, self.base, self.num))\n                else:\n                    return (quo - 1, timedelta64(rem + in2, self.base,\n                                                 self.num))\n\n    def __repr__(self):\n        val = \"NaT\" if self._nat else str(self.value * self.num)\n        if base == \"generic\":\n            return f\"numpy.timedelta64({val}, '{base}')\"\n        else:\n            return f\"numpy.timedelta64({val})\"\n\n    def __str__(self):\n        if self._nat:\n            return \"NaT\"\n        else:\n            return f\"{self.value * self.num} {_base_str(base)}\"\n\n@extend\nclass datetime64:\n\n    def __new__() -> datetime64[base, num]:\n        return datetime64[base, num](0)\n\n    def __new__(value: int,\n                base: Literal[str],\n                num: Literal[int]) -> datetime64[base, num]:\n        _validate_base(base)\n        _validate_num(num)\n        return superf(value)\n\n    def __new__(value: datetime64,\n                base: Literal[str],\n                num: Literal[int]) -> datetime64[base, num]:\n        _validate_base(base)\n        _validate_num(num)\n        return value._cast(datetime64[base, num])\n\n    def __new__(s: str,\n                base: Literal[str],\n                num: Literal[int]) -> datetime64[base, num]:\n        _validate_base(base)\n        _validate_num(num)\n        meta = DatetimeMetaData(base=base, num=num)\n        dts = datetimestruct(s)\n        return datetime64[base, num](dts.to_datetime64(meta))\n\n    def __new__(value: int, unit: Literal[str]):\n        DT = _parse_datetime_type(\"datetime64[\" + unit + \"]\")\n        return datetime64(value, DT.base, DT.num)\n\n    def __new__(dt: datetime64, unit: Literal[str]):\n        DT = _parse_datetime_type(\"datetime64[\" + unit + \"]\")\n        return datetime64(dt, DT.base, DT.num)\n\n    def __new__(s: str, unit: Literal[str] = \"us\"):\n        DT = _parse_datetime_type(\"datetime64[\" + unit + \"]\")\n        return datetime64(s, DT.base, DT.num)\n\n    def __new__(dt: datetime.datetime, unit: Literal[str] = \"us\"):\n        DT = _parse_datetime_type(\"datetime64[\" + unit + \"]\")\n        meta = DatetimeMetaData(base=DT.base, num=DT.num)\n        dts = datetimestruct(dt)\n        return datetime64(dts.to_datetime64(meta), DT.base, DT.num)\n\n    def __new__(dt: datetime.date, unit: Literal[str] = \"D\"):\n        DT = _parse_datetime_type(\"datetime64[\" + unit + \"]\")\n        meta = DatetimeMetaData(base=DT.base, num=DT.num)\n        dts = datetimestruct(datetime.datetime(year=dt.year, month=dt.month, day=dt.day))\n        return datetime64(dts.to_datetime64(meta), DT.base, DT.num)\n\n    def _cast(self, DT: type):\n        if not isinstance(DT, datetime64):\n            compile_error(\n                \"[internal error] can only cast datetime64 to datetime64\")\n\n        meta1 = DatetimeMetaData(self.base, self.num)\n        meta2 = DatetimeMetaData(DT.base, DT.num)\n        dts = datetimestruct()\n        dts.from_datetime64(meta1, self.value)\n        return datetime64(dts.to_datetime64(meta2), DT.base, DT.num)\n\n    def _code():\n        return _base_code(base)\n\n    def tolist(self):\n        if (base == \"ns\" or base == \"ps\" or base == \"fs\" or base == \"as\"):\n            return self.value\n        else:\n            meta = DatetimeMetaData(base, num)\n            dts = datetimestruct()\n            dts.from_datetime64(meta, self.value)\n            return dts.datetime\n\n    @property\n    def _nat(self):\n        return self.value == _DATETIME_NAT\n\n    def __eq__(self, other: datetime64):\n        if self._nat or other._nat:\n            return False\n        else:\n            self, other = _coerce(self, other)\n            return self.value == other.value\n\n    def __ne__(self, other: datetime64):\n        if self._nat or other._nat:\n            return True\n        else:\n            self, other = _coerce(self, other)\n            return self.value != other.value\n\n    def __lt__(self, other: datetime64):\n        if self._nat or other._nat:\n            return False\n        else:\n            self, other = _coerce(self, other)\n            return self.value < other.value\n\n    def __gt__(self, other: datetime64):\n        if self._nat or other._nat:\n            return False\n        else:\n            self, other = _coerce(self, other)\n            return self.value > other.value\n\n    def __le__(self, other: datetime64):\n        if self._nat or other._nat:\n            return False\n        else:\n            self, other = _coerce(self, other)\n            return self.value <= other.value\n\n    def __ge__(self, other: datetime64):\n        if self._nat or other._nat:\n            return False\n        else:\n            self, other = _coerce(self, other)\n            return self.value >= other.value\n\n    def __bool__(self):\n        return bool(self.value)\n\n    def __add__(self, other):\n        if (isinstance(other, int) or\n            isinstance(other, bool) or\n            isinstance(other, byte) or\n            isinstance(other, Int) or\n            isinstance(other, UInt)):\n            return self + int(other)\n        else:\n            compile_error(\"cannot add datetime64 and non-int type\")\n\n    def __radd__(self, other):\n        if (isinstance(other, int) or\n            isinstance(other, bool) or\n            isinstance(other, byte) or\n            isinstance(other, Int) or\n            isinstance(other, UInt)):\n            return int(other) + self\n        else:\n            compile_error(\"cannot add datetime64 and non-int type\")\n\n    def __add__(self, other: timedelta64):\n        self, other = _coerce(self, other)\n        if self._nat or other._nat:\n            return datetime64(_DATETIME_NAT, self.base, self.num)\n        else:\n            return datetime64(self.value + other.value, self.base, self.num)\n\n    def __add__(self, other: int):\n        return self + timedelta64(other, base, num)\n\n    def __radd__(self, other: int):\n        return self + timedelta64(other, base, num)\n\n    def __sub__(self, other):\n        if (isinstance(other, int) or\n            isinstance(other, bool) or\n            isinstance(other, byte) or\n            isinstance(other, Int) or\n            isinstance(other, UInt)):\n            return self - int(other)\n        else:\n            compile_error(\"cannot subtract datetime64 and non-int type\")\n\n    def __sub__(self, other: timedelta64):\n        self, other = _coerce(self, other)\n        if self._nat or other._nat:\n            return datetime64(_DATETIME_NAT, self.base, self.num)\n        else:\n            return datetime64(self.value - other.value, self.base, self.num)\n\n    def __sub__(self, other: datetime64):\n        self, other = _coerce(self, other)\n        if self._nat or other._nat:\n            return timedelta64(_DATETIME_NAT, self.base, self.num)\n        else:\n            return timedelta64(self.value - other.value, self.base, self.num)\n\n    def __sub__(self, other: int):\n        return self - timedelta64(other, base, num)\n\n    def __repr__(self):\n        return f\"numpy.datetime64('{self.__str__()}', '{base}')\"\n\n    def __str__(self):\n        dts = datetimestruct()\n        meta = DatetimeMetaData(base, num)\n        dts.from_datetime64(meta, self.value)\n        return dts.to_str(_base_code(base))\n\n    def _as_string(self, unit=None, timezone: str = \"naive\"):\n        unit_code = _base_code(base)\n        if unit is not None:\n            if not isinstance(unit, str):\n                compile_error(\"unit must be a string or None\")\n\n            if unit == \"auto\":\n                unit_code = _FR_ERROR\n            else:\n                unit_code = _base_code_nonstatic(unit)\n\n        local = False\n        utc = False\n\n        if timezone == \"local\":\n            local = True\n        elif timezone == \"UTC\":\n            utc = True\n        elif timezone != \"naive\":\n            raise ValueError(\n                f\"Unsupported timezone input string \\\"{timezone}\\\"\")\n\n        meta = DatetimeMetaData(base, num)\n        dts = datetimestruct()\n        dts.from_datetime64(meta, self.value)\n        return dts.to_str(unit=unit_code, local=local, utc=utc, tzoffset=-1)\n\n@extend\nclass datetimestruct:\n\n    def __init__(self):\n        self.reset()\n\n    def __init__(self, s: str):\n        self.parse_iso8601(s)\n\n    def __init__(self, dt: datetime.datetime):\n        self.year = dt.year\n        self.month = i32(dt.month)\n        self.day = i32(dt.day)\n        self.hour = i32(dt.hour)\n        self.min = i32(dt.minute)\n        self.sec = i32(dt.second)\n        self.us = i32(dt.microsecond)\n        self.ps = i32(0)\n        self.as_ = i32(0)\n\n    def reset(self):\n        self.year = 0\n        self.month = i32(0)\n        self.day = i32(0)\n        self.hour = i32(0)\n        self.min = i32(0)\n        self.sec = i32(0)\n        self.us = i32(0)\n        self.ps = i32(0)\n        self.as_ = i32(0)\n\n    def copy_to(self, other: datetimestruct):\n        other.year = self.year\n        other.month = self.month\n        other.day = self.day\n        other.hour = self.hour\n        other.min = self.min\n        other.sec = self.sec\n        other.us = self.us\n        other.ps = self.ps\n        other.as_ = self.as_\n\n    @property\n    def datetime(self):\n        return datetime.datetime(year=self.year,\n                                 month=int(self.month),\n                                 day=int(self.day),\n                                 hour=int(self.hour),\n                                 minute=int(self.min),\n                                 second=int(self.sec),\n                                 microsecond=int(self.us))\n\n    @property\n    def days(self):\n        year = self.year - 1970\n        days = year * 365\n\n        if days >= 0:\n            year += 1\n            days += year // 4\n            year += 68\n            days -= year // 100\n            year += 300\n            days += year // 400\n        else:\n            year -= 2\n            days += year // 4\n            year -= 28\n            days -= year // 100\n            days += year // 400\n\n        month_lengths = _days_per_month_table[int(_is_leapyear(self.year))]\n        month = int(self.month) - 1\n\n        for i in range(month):\n            days += month_lengths[i]\n\n        days += int(self.day) - 1\n        return days\n\n    @property\n    def minutes(self):\n        days = self.days * 24 * 60\n        days += int(self.hour) * 60\n        days += int(self.min)\n        return days\n\n    def set_days(self, days: int):\n        days, year = _days_to_yearsdays(days)\n        self.year = year\n        month_lengths = _days_per_month_table[int(_is_leapyear(self.year))]\n\n        for i in range(12):\n            if days < month_lengths[i]:\n                self.month = i32(i + 1)\n                self.day = i32(days + 1)\n                return\n            else:\n                days -= month_lengths[i]\n\n    def add_minutes(self, minutes: i32):\n        self.min += minutes\n        h, m = _extract_unit_32(self.min, i32(60))\n        self.min = m\n        self.hour += h\n        d, h = _extract_unit_32(self.hour, i32(24))\n        self.hour = h\n        self.day += d\n\n        if self.day < i32(1):\n            self.month -= i32(1)\n            if self.month < i32(1):\n                self.year -= 1\n                self.month = i32(12)\n\n            leap = int(_is_leapyear(self.year))\n            self.day += i32(_days_per_month_table[leap][int(self.month) - 1])\n        elif self.day > i32(28):\n            leap = int(_is_leapyear(self.year))\n            dpm = i32(_days_per_month_table[leap][int(self.month) - 1])\n\n            if self.day > dpm:\n                self.day -= dpm\n                self.month += i32(1)\n                if self.month > i32(12):\n                    self.year += 1\n                    self.month = i32(1)\n\n    def to_datetime64(self, meta: DatetimeMetaData):\n        if self.year == _DATETIME_NAT:\n            return _DATETIME_NAT\n\n        base = meta.base\n        if base == i32(_FR_GENERIC):\n            raise ValueError(\"Cannot create a NumPy datetime other than NaT \"\n                             \"with generic units\")\n\n        ret = 0\n\n        if base == i32(_FR_Y):\n            ret = self.year - 1970\n        elif base == i32(_FR_M):\n            ret = 12 * (self.year - 1970) + (int(self.month) - 1)\n        else:\n            days = self.days\n\n            if base == i32(_FR_W):\n                if days >= 0:\n                    ret = days // 7\n                else:\n                    ret = (days - 6) // 7\n            elif base == i32(_FR_D):\n                ret = days\n            elif base == i32(_FR_h):\n                ret = days * 24 + int(self.hour)\n            elif base == i32(_FR_m):\n                ret = (days * 24 + int(self.hour)) * 60 + int(self.min)\n            elif base == i32(_FR_s):\n                ret = ((days * 24 + int(self.hour)) * 60 +\n                       int(self.min)) * 60 + int(self.sec)\n            elif base == i32(_FR_ms):\n                ret = ((\n                    (days * 24 + int(self.hour)) * 60 + int(self.min)) * 60 +\n                       int(self.sec)) * 1000 + int(self.us) // 1000\n            elif base == i32(_FR_us):\n                ret = ((\n                    (days * 24 + int(self.hour)) * 60 + int(self.min)) * 60 +\n                       int(self.sec)) * 1000000 + int(self.us)\n            elif base == i32(_FR_ns):\n                ret = (((\n                    (days * 24 + int(self.hour)) * 60 + int(self.min)) * 60 +\n                        int(self.sec)) * 1000000 + int(self.us)) * 1000 + int(\n                            self.ps) // 1000\n            elif base == i32(_FR_ps):\n                ret = (\n                    (((days * 24 + int(self.hour)) * 60 + int(self.min)) * 60 +\n                     int(self.sec)) * 1000000 + int(self.us)) * 1000000 + int(\n                         self.ps)\n            elif base == i32(_FR_fs):\n                ret = ((((\n                    (days * 24 + int(self.hour)) * 60 + int(self.min)) * 60 +\n                         int(self.sec)) * 1000000 + int(self.us)) * 1000000 +\n                       int(self.ps)) * 1000 + int(self.as_) // 1000\n            elif base == i32(_FR_as):\n                ret = ((((\n                    (days * 24 + int(self.hour)) * 60 + int(self.min)) * 60 +\n                         int(self.sec)) * 1000000 + int(self.us)) * 1000000 +\n                       int(self.ps)) * 1000000 + int(self.as_)\n            else:\n                raise ValueError(\n                    \"NumPy datetime metadata with corrupt unit value\")\n\n        num = int(meta.num)\n        if num > 1:\n            if ret >= 0:\n                ret //= num\n            else:\n                ret = (ret - num + 1) // num\n\n        return ret\n\n    def from_datetime64(self, meta: DatetimeMetaData, dt: int):\n        days = 0\n        self.reset()\n        self.year = 1970\n        self.month = i32(1)\n        self.day = i32(1)\n\n        if dt == _DATETIME_NAT:\n            self.year = _DATETIME_NAT\n            return\n\n        base = meta.base\n        if base == i32(_FR_GENERIC):\n            raise ValueError(\n                \"Cannot convert a NumPy datetime value other than NaT \"\n                \"with generic units\")\n\n        dt *= int(meta.num)\n\n        if base == i32(_FR_Y):\n            self.year = 1970 + dt\n        elif base == i32(_FR_M):\n            year, dt = _extract_unit_64(dt, 12)\n            self.year = 1970 + year\n            self.month = i32(dt + 1)\n        elif base == i32(_FR_W):\n            self.set_days(dt * 7)\n        elif base == i32(_FR_D):\n            self.set_days(dt)\n        elif base == i32(_FR_h):\n            days, dt = _extract_unit_64(dt, 24)\n            self.set_days(days)\n            self.hour = i32(dt)\n        elif base == i32(_FR_m):\n            days, dt = _extract_unit_64(dt, 60 * 24)\n            self.set_days(days)\n            hour, dt = _extract_unit_64(dt, 60)\n            self.hour = i32(hour)\n            self.min = i32(dt)\n        elif base == i32(_FR_s):\n            days, dt = _extract_unit_64(dt, 60 * 60 * 24)\n            self.set_days(days)\n            hour, dt = _extract_unit_64(dt, 60 * 60)\n            self.hour = i32(hour)\n            min, dt = _extract_unit_64(dt, 60)\n            self.min = i32(min)\n            self.sec = i32(dt)\n        elif base == i32(_FR_ms):\n            days, dt = _extract_unit_64(dt, 1000 * 60 * 60 * 24)\n            self.set_days(days)\n            hour, dt = _extract_unit_64(dt, 1000 * 60 * 60)\n            self.hour = i32(hour)\n            min, dt = _extract_unit_64(dt, 1000 * 60)\n            self.min = i32(min)\n            sec, dt = _extract_unit_64(dt, 1000)\n            self.sec = i32(sec)\n            self.us = i32(dt * 1000)\n        elif base == i32(_FR_us):\n            days, dt = _extract_unit_64(dt, 1000 * 1000 * 60 * 60 * 24)\n            self.set_days(days)\n            hour, dt = _extract_unit_64(dt, 1000 * 1000 * 60 * 60)\n            self.hour = i32(hour)\n            min, dt = _extract_unit_64(dt, 1000 * 1000 * 60)\n            self.min = i32(min)\n            sec, dt = _extract_unit_64(dt, 1000 * 1000)\n            self.sec = i32(sec)\n            self.us = i32(dt)\n        elif base == i32(_FR_ns):\n            days, dt = _extract_unit_64(dt, 1000 * 1000 * 1000 * 60 * 60 * 24)\n            self.set_days(days)\n            hour, dt = _extract_unit_64(dt, 1000 * 1000 * 1000 * 60 * 60)\n            self.hour = i32(hour)\n            min, dt = _extract_unit_64(dt, 1000 * 1000 * 1000 * 60)\n            self.min = i32(min)\n            sec, dt = _extract_unit_64(dt, 1000 * 1000 * 1000)\n            self.sec = i32(sec)\n            us, dt = _extract_unit_64(dt, 1000)\n            self.us = i32(us)\n            self.ps = i32(dt * 1000)\n        elif base == i32(_FR_ps):\n            days, dt = _extract_unit_64(\n                dt, 1000 * 1000 * 1000 * 1000 * 60 * 60 * 24)\n            self.set_days(days)\n            hour, dt = _extract_unit_64(dt,\n                                        1000 * 1000 * 1000 * 1000 * 60 * 60)\n            self.hour = i32(hour)\n            min, dt = _extract_unit_64(dt, 1000 * 1000 * 1000 * 1000 * 60)\n            self.min = i32(min)\n            sec, dt = _extract_unit_64(dt, 1000 * 1000 * 1000 * 1000)\n            self.sec = i32(sec)\n            us, dt = _extract_unit_64(dt, 1000 * 1000)\n            self.us = i32(us)\n            self.ps = i32(dt)\n        elif base == i32(_FR_fs):\n            hour, dt = _extract_unit_64(\n                dt, 1000 * 1000 * 1000 * 1000 * 1000 * 60 * 60)\n            self.hour = i32(hour)\n            if self.hour < i32(0):\n                self.year = 1969\n                self.month = i32(12)\n                self.day = i32(31)\n                self.hour += i32(24)\n\n            min, dt = _extract_unit_64(dt,\n                                       1000 * 1000 * 1000 * 1000 * 1000 * 60)\n            self.min = i32(min)\n            sec, dt = _extract_unit_64(dt, 1000 * 1000 * 1000 * 1000 * 1000)\n            self.sec = i32(sec)\n            us, dt = _extract_unit_64(dt, 1000 * 1000 * 1000)\n            self.us = i32(us)\n            ps, dt = _extract_unit_64(dt, 1000)\n            self.ps = i32(ps)\n            self.as_ = i32(dt * 1000)\n        elif base == i32(_FR_as):\n            sec, dt = _extract_unit_64(dt,\n                                       1000 * 1000 * 1000 * 1000 * 1000 * 1000)\n            self.sec = i32(sec)\n            if self.sec < i32(0):\n                self.year = 1969\n                self.month = i32(12)\n                self.day = i32(31)\n                self.hour = i32(23)\n                self.min = i32(59)\n                self.sec += i32(60)\n\n            us, dt = _extract_unit_64(dt, 1000 * 1000 * 1000 * 1000)\n            self.us = i32(us)\n            ps, dt = _extract_unit_64(dt, 1000 * 1000)\n            self.ps = i32(ps)\n            self.as_ = i32(dt)\n        else:\n            raise ValueError(\n                \"NumPy datetime metadata is corrupted with invalid base unit\")\n\n    def utc_to_local(self, out: datetimestruct):\n        rawtime = 0\n        localrawtime = 0\n        year_correction = 0\n        self.copy_to(out)\n\n        rawtime = self.days * 24 * 60 * 60\n        rawtime += int(self.hour) * 60 * 60\n        rawtime += int(self.min) * 60\n\n        tm = _get_localtime(rawtime)\n        out.min = i32(int(tm.min))\n        out.hour = i32(int(tm.hour))\n        out.day = i32(int(tm.mday))\n        out.month = i32(int(tm.mon) + 1)\n        out.year = int(tm.year) + 1900\n\n        rawtime = _cdiv(rawtime, 60)\n        localrawtime = out.days * 24 * 60\n        localrawtime += int(out.hour) * 60\n        localrawtime += int(out.min)\n\n        timezone_offset = localrawtime - rawtime\n        out.year += year_correction\n        return timezone_offset\n\n    def lossless_unit(self):\n        if _cmod(self.as_, i32(1000)):\n            return _FR_as\n        elif self.as_:\n            return _FR_fs\n        elif _cmod(self.ps, i32(1000)):\n            return _FR_ps\n        elif self.ps:\n            return _FR_ns\n        elif _cmod(self.us, i32(1000)):\n            return _FR_us\n        elif self.us:\n            return _FR_ms\n        elif self.sec:\n            return _FR_s\n        elif self.min:\n            return _FR_m\n        elif self.hour:\n            return _FR_h\n        elif self.day != i32(1):\n            return _FR_D\n        elif self.month != i32(1):\n            return _FR_M\n        else:\n            return _FR_Y\n\n    def parse_iso8601(self, s: str):\n\n        def tolower(b):\n            return byte(int(_C.tolower(i32(int(b)))))\n\n        def isspace(b):\n            return bool(_C.isspace(i32(int(b))))\n\n        def isdigit(b):\n            return bool(_C.isdigit(i32(int(b))))\n\n        def strcmp(str_: cobj, len_: int, vals):\n            if len_ != static.len(vals):\n                return False\n\n            for i in static.range(static.len(vals)):\n                if tolower(str_[i]) != byte(vals[i]):\n                    return False\n\n            return True\n\n        def parse_error(s: str, substr: cobj):\n            raise ValueError(\n                f\"Error parsing datetime string \\\"{s}\\\" at position {substr - s.ptr}\"\n            )\n\n        def parse_timezone(self, s: str, substr: cobj, sublen: int):\n            # Note: parsing timezones is deprecated in NumPy\n            if sublen == 0:\n                return\n\n            if substr[0] == byte(90):\n                if sublen == 1:\n                    return\n                else:\n                    substr += 1\n                    sublen -= 1\n            elif substr[0] == byte(45) or substr[0] == byte(43):\n                offset_neg = False\n                offset_hour = i32(0)\n                offset_minute = i32(0)\n\n                if substr[0] == byte(45):\n                    offset_neg = True\n\n                substr += 1\n                sublen -= 1\n\n                # Hours offset\n                if sublen >= 2 and isdigit(substr[0]) and isdigit(substr[1]):\n                    offset_hour = i32(10 * (int(substr[0]) - 48) +\n                                      (int(substr[1]) - 48))\n                    substr += 2\n                    sublen -= 2\n\n                    if offset_hour >= i32(24):\n                        raise ValueError(\n                            f\"Timezone hours offset out of range in datetime string \\\"{s}\\\"\"\n                        )\n                else:\n                    parse_error(s, substr)\n\n                if sublen > 0:\n                    if substr[0] == byte(58):\n                        substr += 1\n                        sublen -= 1\n\n                    # Minutes offset (optional)\n                    if sublen >= 2 and isdigit(substr[0]) and isdigit(\n                            substr[1]):\n                        offset_minute = i32(10 * (int(substr[0]) - 48) +\n                                            (int(substr[1]) - 48))\n                        substr += 2\n                        sublen -= 2\n\n                        if offset_minute >= i32(60):\n                            raise ValueError(\n                                f\"Timezone minutes offset out of range in datetime string \\\"{s}\\\"\"\n                            )\n                    else:\n                        parse_error(s, substr)\n\n                if offset_neg:\n                    offset_hour = -offset_hour\n                    offset_minute = -offset_minute\n\n                self.add_minutes(i32(-60) * offset_hour - offset_minute)\n\n            while sublen > 0 and isspace(substr[0]):\n                substr += 1\n                sublen -= 1\n\n            if sublen != 0:\n                parse_error(s, substr)\n\n        self.reset()\n        self.month = i32(1)\n        self.day = i32(1)\n\n        bestunit = 0\n        str_ = s.ptr\n        len_ = len(s)\n\n        if len_ <= 0 or strcmp(str_, len_, (110, 97, 116)):  # 'nat'\n            self.year = _DATETIME_NAT\n            bestunit = _FR_GENERIC\n            return bestunit\n\n        #if unit == _FR_GENERIC:\n        #    raise ValueError(\"Cannot create a NumPy datetime other than NaT with generic units\")\n\n        if strcmp(str_, len_, (116, 111, 100, 97, 121)):  # 'today'\n            tm = _get_localtime(-1)\n            self.year = int(tm.year) + 1900\n            self.month = i32(int(tm.mon) + 1)\n            self.day = i32(int(tm.mday))\n\n            bestunit = _FR_D\n            return bestunit\n\n        if strcmp(str_, len_, (110, 111, 119)):  # 'now'\n            rawtime = _get_time()\n            meta = DatetimeMetaData(base=_FR_s)\n            bestunit = _FR_s\n            self.from_datetime64(meta, rawtime)\n\n            bestunit = _FR_s\n            return bestunit\n\n        substr = str_\n        sublen = len_\n\n        while sublen > 0 and isspace(substr[0]):\n            substr += 1\n            sublen -= 1\n\n        if substr[0] == byte(45) or substr[0] == byte(43):\n            substr += 1\n            sublen -= 1\n\n        if sublen == 0:\n            parse_error(s, substr)\n\n        # Parse year\n        self.year = 0\n        while sublen > 0 and isdigit(substr[0]):\n            self.year = 10 * self.year + (int(substr[0]) - 48)\n            substr += 1\n            sublen -= 1\n\n        if str_[0] == byte(45):\n            self.year = -self.year\n\n        year_leap = _is_leapyear(self.year)\n\n        if sublen == 0:\n            bestunit = _FR_Y\n            return bestunit\n        elif substr[0] == byte(45):\n            substr += 1\n            sublen -= 1\n        else:\n            parse_error(s, substr)\n\n        if sublen == 0:\n            parse_error(s, substr)\n\n        # Parse month\n        if sublen >= 2 and isdigit(substr[0]) and isdigit(substr[1]):\n            self.month = i32(10 * (int(substr[0]) - 48) +\n                             (int(substr[1]) - 48))\n\n            if self.month < i32(1) or self.month > i32(12):\n                raise ValueError(\n                    f\"Month out of range in datetime string \\\"{s}\\\"\")\n\n            substr += 2\n            sublen -= 2\n        else:\n            parse_error(s, substr)\n\n        if sublen == 0:\n            bestunit = _FR_M\n            return bestunit\n        elif substr[0] == byte(45):\n            substr += 1\n            sublen -= 1\n        else:\n            parse_error(s, substr)\n\n        if sublen == 0:\n            parse_error(s, substr)\n\n        # Parse day\n        if sublen >= 2 and isdigit(substr[0]) and isdigit(substr[1]):\n            self.day = i32(10 * (int(substr[0]) - 48) + (int(substr[1]) - 48))\n\n            if self.day < i32(1) or self.day > i32(\n                    _days_per_month_table[int(year_leap)][int(self.month) -\n                                                          1]):\n                raise ValueError(\n                    f\"Day out of range in datetime string \\\"{s}\\\"\")\n\n            substr += 2\n            sublen -= 2\n        else:\n            parse_error(s, substr)\n\n        # Next char must be 'T', ' ' or end of string\n        if sublen == 0:\n            bestunit = _FR_D\n            return bestunit\n        elif substr[0] != byte(84) and substr[0] != byte(32):\n            parse_error(s, substr)\n        else:\n            substr += 1\n            sublen -= 1\n\n        # Parse hour\n        if sublen >= 2 and isdigit(substr[0]) and isdigit(substr[1]):\n            self.hour = i32(10 * (int(substr[0]) - 48) + (int(substr[1]) - 48))\n\n            if self.hour >= i32(24):\n                raise ValueError(\n                    f\"Hours out of range in datetime string \\\"{s}\\\"\")\n\n            substr += 2\n            sublen -= 2\n        else:\n            parse_error(s, substr)\n\n        if sublen > 0 and substr[0] == byte(58):\n            substr += 1\n            sublen -= 1\n        else:\n            bestunit = _FR_h\n            parse_timezone(self, s, substr, sublen)\n            return bestunit\n\n        if sublen == 0:\n            parse_error(s, substr)\n\n        # Parse minutes\n        if sublen >= 2 and isdigit(substr[0]) and isdigit(substr[1]):\n            self.min = i32(10 * (int(substr[0]) - 48) + (int(substr[1]) - 48))\n\n            if self.min >= i32(60):\n                raise ValueError(\n                    f\"Minutes out of range in datetime string \\\"{s}\\\"\")\n\n            substr += 2\n            sublen -= 2\n        else:\n            parse_error(s, substr)\n\n        if sublen > 0 and substr[0] == byte(58):\n            substr += 1\n            sublen -= 1\n        else:\n            bestunit = _FR_m\n            parse_timezone(self, s, substr, sublen)\n            return bestunit\n\n        if sublen == 0:\n            parse_error(s, substr)\n\n        # Parse seconds\n        if sublen >= 2 and isdigit(substr[0]) and isdigit(substr[1]):\n            self.sec = i32(10 * (int(substr[0]) - 48) + (int(substr[1]) - 48))\n\n            if self.sec >= i32(60):\n                raise ValueError(\n                    f\"Seconds out of range in datetime string \\\"{s}\\\"\")\n\n            substr += 2\n            sublen -= 2\n        else:\n            parse_error(s, substr)\n\n        if sublen > 0 and substr[0] == byte(46):\n            substr += 1\n            sublen -= 1\n        else:\n            bestunit = _FR_s\n            parse_timezone(self, s, substr, sublen)\n            return bestunit\n\n        # Parse microsec\n        numdigits = 0\n        for i in range(6):\n            self.us *= i32(10)\n            if sublen > 0 and isdigit(substr[0]):\n                self.us += i32(int(substr[0]) - 48)\n                substr += 1\n                sublen -= 1\n                numdigits += 1\n\n        if sublen == 0 or not isdigit(substr[0]):\n            if numdigits > 3:\n                bestunit = _FR_us\n            else:\n                bestunit = _FR_ms\n\n            parse_timezone(self, s, substr, sublen)\n            return bestunit\n\n        # Parse picosec\n        numdigits = 0\n        for i in range(6):\n            self.ps *= i32(10)\n            if sublen > 0 and isdigit(substr[0]):\n                self.ps += i32(int(substr[0]) - 48)\n                substr += 1\n                sublen -= 1\n                numdigits += 1\n\n        if sublen == 0 or not isdigit(substr[0]):\n            if numdigits > 3:\n                bestunit = _FR_ps\n            else:\n                bestunit = _FR_ns\n\n            parse_timezone(self, s, substr, sublen)\n            return bestunit\n\n        # Parse attosec\n        numdigits = 0\n        for i in range(6):\n            self.as_ *= i32(10)\n            if sublen > 0 and isdigit(substr[0]):\n                self.as_ += i32(int(substr[0]) - 48)\n                substr += 1\n                sublen -= 1\n                numdigits += 1\n\n        if numdigits > 3:\n            bestunit = _FR_as\n        else:\n            bestunit = _FR_fs\n\n        parse_timezone(self, s, substr, sublen)\n        return bestunit\n\n    def to_str(self,\n               unit: int,\n               local: bool = False,\n               utc: bool = False,\n               tzoffset: int = -1):\n\n        def date_length(unit: int):\n            if unit == _FR_Y:\n                return 4\n\n            if unit == _FR_M:\n                return 7\n\n            if unit == _FR_D or unit == _FR_W:\n                return 10\n\n            if unit == _FR_h:\n                return 13\n\n            if unit == _FR_m:\n                return 16\n\n            if unit == _FR_s:\n                return 19\n\n            if unit == _FR_ms:\n                return 23\n\n            if unit == _FR_us:\n                return 26\n\n            if unit == _FR_ns:\n                return 29\n\n            if unit == _FR_ps:\n                return 32\n\n            if unit == _FR_fs:\n                return 35\n\n            if unit == _FR_as:\n                return 38\n\n            return 0\n\n        def timezone_length(local: bool, utc: bool):\n            if local:\n                return 5\n            elif utc:\n                return 1\n            else:\n                return 0\n\n        def write_int(buf: _strbuf, n, width: int):\n            n = int(n)\n            if n < 0:\n                buf.append('-')\n                n = -n\n            elif n == 0:\n                for i in range(width):\n                    buf.append('0')\n                return\n\n            # compute and append leading 0s\n            m = n\n            digits = 0\n            while m != 0:\n                digits += 1\n                m //= 10\n\n            for i in range(width - digits):\n                buf.append('0')\n\n            # append digits\n            i1 = buf.n\n            while n != 0:\n                digit = n % 10\n                b = byte(48 + digit)\n                buf.append(str(__ptr__(b), 1))\n                n //= 10\n\n            # reverse added digits\n            i2 = buf.n - 1\n            while i1 < i2:\n                c1 = buf.data[i1]\n                c2 = buf.data[i2]\n                buf.data[i1] = c2\n                buf.data[i2] = c1\n                i1 += 1\n                i2 -= 1\n\n        def add_timezone(buf: _strbuf, local: bool, utc: bool,\n                         timezone_offset: int):\n            if local:\n                if timezone_offset < 0:\n                    buf.append('-')\n                    timezone_offset = -timezone_offset\n                else:\n                    buf.append('+')\n\n                c0 = byte(_cmod(_cdiv(timezone_offset, 10 * 60), 10) + 48)\n                c1 = byte(_cmod(_cdiv(timezone_offset, 60), 10) + 48)\n                c2 = byte(\n                    _cmod(_cdiv(_cmod(timezone_offset, 60), 10), 10) + 48)\n                c3 = byte(_cmod(_cmod(timezone_offset, 60), 10) + 48)\n\n                buf.append(str(__ptr__(c0), 1))\n                buf.append(str(__ptr__(c1), 1))\n                buf.append(str(__ptr__(c2), 1))\n                buf.append(str(__ptr__(c3), 1))\n            elif utc:\n                buf.append('Z')\n\n        if self.year == _DATETIME_NAT:\n            return \"NaT\"\n\n        if (self.year < 1970 or self.year >= 10000) and tzoffset == -1:\n            local = False\n\n        if unit == _FR_ERROR:\n            unit = self.lossless_unit()\n            if (unit < _FR_m and local) or unit == _FR_h:\n                unit = _FR_m\n            elif unit < _FR_D:\n                unit = _FR_D\n\n        if unit == _FR_W:\n            unit = _FR_D\n\n        timezone_offset = 0\n        if local:\n            tmp = datetimestruct()\n            if tzoffset == -1:\n                timezone_offset = self.utc_to_local(tmp)\n            else:\n                self.copy_to(tmp)\n                timezone_offset = tzoffset\n                tmp.add_minutes(i32(timezone_offset))\n            self = tmp\n\n        buf = _strbuf(capacity=(date_length(unit) +\n                                timezone_length(local=local, utc=utc)))\n\n        write_int(buf, self.year, 4)\n        if unit == _FR_Y:\n            return buf.__str__()\n\n        buf.append('-')\n        write_int(buf, self.month, 2)\n        if unit == _FR_M:\n            return buf.__str__()\n\n        buf.append('-')\n        write_int(buf, self.day, 2)\n        if unit == _FR_D:\n            return buf.__str__()\n\n        buf.append('T')\n        write_int(buf, self.hour, 2)\n        if unit == _FR_h:\n            add_timezone(buf,\n                         local=local,\n                         utc=utc,\n                         timezone_offset=timezone_offset)\n            return buf.__str__()\n\n        buf.append(':')\n        write_int(buf, self.min, 2)\n        if unit == _FR_m:\n            add_timezone(buf,\n                         local=local,\n                         utc=utc,\n                         timezone_offset=timezone_offset)\n            return buf.__str__()\n\n        buf.append(':')\n        write_int(buf, self.sec, 2)\n        if unit == _FR_s:\n            add_timezone(buf,\n                         local=local,\n                         utc=utc,\n                         timezone_offset=timezone_offset)\n            return buf.__str__()\n\n        buf.append('.')\n        write_int(buf, self.us // i32(1000), 3)\n        if unit == _FR_ms:\n            add_timezone(buf,\n                         local=local,\n                         utc=utc,\n                         timezone_offset=timezone_offset)\n            return buf.__str__()\n\n        write_int(buf, self.us % i32(1000), 3)\n        if unit == _FR_us:\n            add_timezone(buf,\n                         local=local,\n                         utc=utc,\n                         timezone_offset=timezone_offset)\n            return buf.__str__()\n\n        write_int(buf, self.ps // i32(1000), 3)\n        if unit == _FR_ns:\n            add_timezone(buf,\n                         local=local,\n                         utc=utc,\n                         timezone_offset=timezone_offset)\n            return buf.__str__()\n\n        write_int(buf, self.ps % i32(1000), 3)\n        if unit == _FR_ps:\n            add_timezone(buf,\n                         local=local,\n                         utc=utc,\n                         timezone_offset=timezone_offset)\n            return buf.__str__()\n\n        write_int(buf, self.as_ // i32(1000), 3)\n        if unit == _FR_fs:\n            add_timezone(buf,\n                         local=local,\n                         utc=utc,\n                         timezone_offset=timezone_offset)\n            return buf.__str__()\n\n        write_int(buf, self.as_ % i32(1000), 3)\n        add_timezone(buf,\n                     local=local,\n                     utc=utc,\n                     timezone_offset=timezone_offset)\n        return buf.__str__()\n\n    def __str__(self):\n        return self.to_str(_FR_as)\n\n# Busday functionality\n\n_BUSDAY_FORWARD: Literal[int] = 0\n_BUSDAY_FOLLOWING: Literal[int] = _BUSDAY_FORWARD\n_BUSDAY_BACKWARD: Literal[int] = 1\n_BUSDAY_PRECEDING: Literal[int] = _BUSDAY_BACKWARD\n_BUSDAY_MODIFIEDFOLLOWING: Literal[int] = 2\n_BUSDAY_MODIFIEDPRECEDING: Literal[int] = 3\n_BUSDAY_NAT: Literal[int] = 4\n_BUSDAY_RAISE: Literal[int] = 5\n\n@tuple\nclass _weekmask:\n    mask: u8\n\n    def __getitem__(self, idx: int):\n        return bool(self.mask & u8(1 << idx))\n\n    @property\n    def count(self):\n        return self.mask.popcnt()\n\ndef _get_day_of_week(date: datetime64['D', 1]):\n    day_of_week = _cmod(date.value - 4, 7)\n    if day_of_week < 0:\n        day_of_week += 7\n    return day_of_week\n\ndef _is_holiday(date: datetime64['D', 1], holidays_begin: Ptr[datetime64['D',\n                                                                         1]],\n                holidays_end: Ptr[datetime64['D', 1]]):\n    while holidays_begin < holidays_end:\n        trial = holidays_begin + (holidays_end - holidays_begin) // 2\n        if date.value < trial[0].value:\n            holidays_end = trial\n        elif date.value > trial[0].value:\n            holidays_begin = trial + 1\n        else:\n            return True\n    return False\n\ndef _find_earliest_holiday_on_or_after(date: datetime64['D', 1],\n                                       holidays_begin: Ptr[datetime64['D', 1]],\n                                       holidays_end: Ptr[datetime64['D', 1]]):\n    while holidays_begin < holidays_end:\n        trial = holidays_begin + (holidays_end - holidays_begin) // 2\n        if date.value < trial[0].value:\n            holidays_end = trial\n        elif date.value > trial[0].value:\n            holidays_begin = trial + 1\n        else:\n            return trial\n    return holidays_begin\n\ndef _find_earliest_holiday_after(date: datetime64['D', 1],\n                                 holidays_begin: Ptr[datetime64['D', 1]],\n                                 holidays_end: Ptr[datetime64['D', 1]]):\n    while holidays_begin < holidays_end:\n        trial = holidays_begin + (holidays_end - holidays_begin) // 2\n        if date.value < trial[0].value:\n            holidays_end = trial\n        elif date.value > trial[0].value:\n            holidays_begin = trial + 1\n        else:\n            return trial + 1\n    return holidays_begin\n\ndef _apply_busines_day_roll(date: datetime64['D', 1], roll: int,\n                            weekmask: _weekmask,\n                            holidays_begin: Ptr[datetime64['D', 1]],\n                            holidays_end: Ptr[datetime64['D', 1]]):\n    if date._nat:\n        if roll == _BUSDAY_RAISE:\n            raise ValueError(\"NaT input in busday_offset\")\n        else:\n            return date, 0\n\n    day_of_week = _get_day_of_week(date)\n\n    if (not weekmask[day_of_week]) or _is_holiday(date, holidays_begin,\n                                                  holidays_end):\n        start_date = date\n        start_day_of_week = day_of_week\n\n        if roll == _BUSDAY_FOLLOWING or roll == _BUSDAY_MODIFIEDFOLLOWING:\n            while True:\n                date += 1\n                day_of_week += 1\n                if day_of_week == 7:\n                    day_of_week = 0\n                if weekmask[day_of_week] and not _is_holiday(\n                        date, holidays_begin, holidays_end):\n                    break\n\n            if roll == _BUSDAY_MODIFIEDFOLLOWING:\n                if _days_to_month_number(\n                        start_date.value) != _days_to_month_number(date.value):\n                    date = start_date\n                    day_of_week = start_day_of_week\n\n                    while True:\n                        date -= 1\n                        day_of_week -= 1\n                        if day_of_week == -1:\n                            day_of_week = 6\n                        if weekmask[day_of_week] and not _is_holiday(\n                                date, holidays_begin, holidays_end):\n                            break\n        elif roll == _BUSDAY_PRECEDING or roll == _BUSDAY_MODIFIEDPRECEDING:\n            while True:\n                date -= 1\n                day_of_week -= 1\n                if day_of_week == -1:\n                    day_of_week = 6\n                if weekmask[day_of_week] and not _is_holiday(\n                        date, holidays_begin, holidays_end):\n                    break\n\n            if roll == _BUSDAY_MODIFIEDPRECEDING:\n                if _days_to_month_number(\n                        start_date.value) != _days_to_month_number(date.value):\n                    date = start_date\n                    day_of_week = start_day_of_week\n\n                    while True:\n                        date += 1\n                        day_of_week += 1\n                        if day_of_week == 7:\n                            day_of_week = 0\n                        if weekmask[day_of_week] and not _is_holiday(\n                                date, holidays_begin, holidays_end):\n                            break\n        elif roll == _BUSDAY_NAT:\n            date = datetime64(_DATETIME_NAT, 'D')\n        elif roll == _BUSDAY_RAISE:\n            raise ValueError(\"Non-business day date in busday_offset\")\n\n    return date, day_of_week\n\ndef _apply_busines_day_offset(date: datetime64['D', 1], offset: int, roll: int,\n                              weekmask: _weekmask, busdays_in_weekmask: int,\n                              holidays_begin: Ptr[datetime64['D', 1]],\n                              holidays_end: Ptr[datetime64['D', 1]]):\n    date, day_of_week = _apply_busines_day_roll(date, roll, weekmask,\n                                                holidays_begin, holidays_end)\n\n    if date._nat:\n        return date\n\n    if offset > 0:\n        holidays_begin = _find_earliest_holiday_on_or_after(\n            date, holidays_begin, holidays_end)\n        date += _cdiv(offset, busdays_in_weekmask) * 7\n        offset = _cmod(offset, busdays_in_weekmask)\n\n        holidays_temp = _find_earliest_holiday_after(date, holidays_begin,\n                                                     holidays_end)\n        offset += holidays_temp - holidays_begin\n        holidays_begin = holidays_temp\n\n        while offset > 0:\n            date += 1\n            day_of_week += 1\n            if day_of_week == 7:\n                day_of_week = 0\n            if weekmask[day_of_week] and not _is_holiday(\n                    date, holidays_begin, holidays_end):\n                offset -= 1\n    elif offset < 0:\n        holidays_end = _find_earliest_holiday_after(date, holidays_begin,\n                                                    holidays_end)\n        date += _cdiv(offset, busdays_in_weekmask) * 7\n        offset = _cmod(offset, busdays_in_weekmask)\n\n        holidays_temp = _find_earliest_holiday_on_or_after(\n            date, holidays_begin, holidays_end)\n        offset -= holidays_end - holidays_temp\n        holidays_end = holidays_temp\n\n        while offset < 0:\n            date -= 1\n            day_of_week -= 1\n            if day_of_week == -1:\n                day_of_week = 6\n            if weekmask[day_of_week] and not _is_holiday(\n                    date, holidays_begin, holidays_end):\n                offset += 1\n\n    return date\n\ndef _apply_busines_day_count(date_begin: datetime64['D', 1],\n                             date_end: datetime64['D', 1], weekmask: _weekmask,\n                             busdays_in_weekmask: int,\n                             holidays_begin: Ptr[datetime64['D', 1]],\n                             holidays_end: Ptr[datetime64['D', 1]]):\n    if date_begin._nat or date_end._nat:\n        raise ValueError(\n            \"Cannot compute a business day count with a NaT (not-a-time) date\")\n\n    swapped = False\n\n    if date_begin.value == date_end.value:\n        return 0\n    elif date_begin.value > date_end.value:\n        tmp = date_begin\n        date_begin = date_end\n        date_end = tmp\n        swapped = True\n        date_begin += 1\n        date_end += 1\n\n    holidays_begin = _find_earliest_holiday_on_or_after(\n        date_begin, holidays_begin, holidays_end)\n    holidays_end = _find_earliest_holiday_on_or_after(date_end, holidays_begin,\n                                                      holidays_end)\n    count = -(holidays_end - holidays_begin)\n\n    whole_weeks = _cdiv(date_end.value - date_begin.value, 7)\n    count += whole_weeks * busdays_in_weekmask\n    date_begin += whole_weeks * 7\n\n    if date_begin < date_end:\n        day_of_week = _get_day_of_week(date_begin)\n\n        while date_begin.value < date_end.value:\n            if weekmask[day_of_week]:\n                count += 1\n\n            date_begin += 1\n            day_of_week += 1\n            if day_of_week == 7:\n                day_of_week = 0\n\n    if swapped:\n        count = -count\n\n    return count\n\ndef _apply_is_business_day(date: datetime64['D', 1], weekmask: _weekmask,\n                           holidays_begin: Ptr[datetime64['D', 1]],\n                           holidays_end: Ptr[datetime64['D', 1]]):\n    day_of_week = _get_day_of_week(date)\n    return weekmask[day_of_week] and not _is_holiday(\n        date, holidays_begin, holidays_end) and not date._nat\n\nclass busdaycalendar:\n    _wm: _weekmask\n    _holidays: Ptr[datetime64['D', 1]]\n    _nholidays: int\n\n    def __init__(self, weekmask='1111100', holidays=None):\n\n        def invalid(weekmask):\n            raise ValueError(\n                f\"Invalid business day weekmask string \\\"{weekmask}\\\"\"\n            )\n\n        n = len(weekmask)\n        mask = u8(0)\n\n        if isinstance(weekmask, str):\n            if n == 7 and (weekmask[0] == '0' or weekmask[0] == '1'):\n                for i in range(7):\n                    w = weekmask[i]\n                    if w == '1':\n                        mask |= u8(1 << i)\n                    elif w != '0':\n                        invalid(weekmask)\n            else:\n                i = 0\n                while i < n:\n                    while i < n and weekmask[i].isspace():\n                        i += 1\n                    if i == n:\n                        break\n                    elif i + 2 >= n:\n                        invalid(weekmask)\n\n                    w = weekmask[i:i+3]\n                    j = 0\n                    if w == \"Mon\":\n                        j = 0\n                    elif w == \"Tue\":\n                        j = 1\n                    elif w == \"Wed\":\n                        j = 2\n                    elif w == \"Thu\":\n                        j = 3\n                    elif w == \"Fri\":\n                        j = 4\n                    elif w == \"Sat\":\n                        j = 5\n                    elif w == \"Sun\":\n                        j = 6\n                    else:\n                        invalid(weekmask)\n                    mask |= u8(1 << j)\n                    i += 3\n        else:\n            if n != 7:\n                raise ValueError(\n                    \"A business day weekmask array must have length 7\")\n\n            for i in range(7):\n                w = weekmask[i]\n                if isinstance(w, bool):\n                    if w:\n                        mask |= u8(1 << i)\n                elif isinstance(w, int):\n                    if w == 1:\n                        mask |= u8(1 << i)\n                    elif w != 0:\n                        raise ValueError(\n                            \"A business day weekmask array must have all 1's and 0's\"\n                        )\n                else:\n                    compile_error(\n                        \"weekmask array elements must be int or bool\")\n\n        if not mask:\n            raise ValueError(\n                \"Cannot construct a numpy.busdaycal with a weekmask of all zeros\"\n            )\n\n        self._wm = _weekmask(mask)\n\n        if holidays is not None:\n            lastdate = _DATETIME_NAT\n            count = len(holidays)\n            hlist = List[datetime64['D', 1]](capacity=count)\n            if isinstance(holidays, Tuple):\n                for i in static.range(static.len(holidays)):\n                    if isinstance(holidays[i], datetime64['D', 1]):\n                        hlist.append(holidays[i])\n                    else:\n                        hlist.append(datetime64(holidays[i], 'D'))\n            else:\n                for i in range(count):\n                    if isinstance(holidays[i], datetime64['D', 1]):\n                        hlist.append(holidays[i])\n                    else:\n                        hlist.append(datetime64(holidays[i], 'D'))\n            hlist.sort()\n\n            hptr = hlist.arr.ptr\n            trimcount = 0\n            for i in range(count):\n                date = hptr[i]\n\n                if not date._nat and date.value != lastdate:\n                    day_of_week = _cmod(date.value - 4, 7)\n                    if day_of_week < 0:\n                        day_of_week += 7\n\n                    if self._wm[day_of_week]:\n                        hptr[trimcount] = date\n                        trimcount += 1\n                        lastdate = date.value\n\n            self._holidays = hptr\n            self._nholidays = trimcount\n        else:\n            self._nholidays = 0\n            self._holidays = Ptr[datetime64['D', 1]]()\n"
  },
  {
    "path": "stdlib/numpy/npio.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .ndarray import ndarray\nfrom .routines import array, empty, asarray, atleast_2d\n\nimport util\nimport gzip\nimport re\nimport internal.static as static\n\n_ALLOCATIONGRANULARITY: Literal[int] = 16384\n\n_PROT_NONE: Literal[int] = 0\n_PROT_READ: Literal[int] = 1\n_PROT_WRITE: Literal[int] = 2\n_PROT_EXEC: Literal[int] = 4\n\n_MAP_SHARED: Literal[int] = 1\n_MAP_PRIVATE: Literal[int] = 2\n\n_MAGIC_PREFIX: Literal[str] = '\\x93NUMPY'\n_ARRAY_ALIGN: Literal[int] = 64\n\n##################\n# Memory Mapping #\n##################\n\ndef _io_error(base_msg: str):\n    from C import seq_check_errno() -> str\n    c_msg = seq_check_errno()\n    raise IOError(f\"{base_msg}: {c_msg}\" if c_msg else base_msg)\n\ndef _mmap(f, length: int, access: int, flags: int, offset: int = 0):\n    from C import mmap(cobj, int, i32, i32, i32, int) -> cobj\n    from C import fileno(cobj) -> i32\n\n    fd = fileno(f.fp)\n    mm = mmap(cobj(), length, i32(access), i32(flags), fd, offset)\n    if int(mm) == -1:\n        _io_error(\"mmap() call failed\")\n    return mm\n\ndef _munmap(p: cobj, length: int):\n    from C import munmap(cobj, int) -> i32\n    if munmap(p, length) == i32(-1):\n        _io_error(\"munmap() call failed\")\n\ndef _fix_mmap_mode(mode: str):\n    if mode == 'readonly':\n        mode = 'r'\n    elif mode == 'copyonwrite':\n        mode = 'c'\n    elif mode == 'readwrite':\n        mode = 'r+'\n    elif mode == 'write':\n        mode = 'w+'\n    elif mode not in ('r', 'c', 'r+', 'w+'):\n        raise ValueError(\n            f\"mode must be one of ['r', 'c', 'r+', 'w+', 'readonly', 'copyonwrite', 'readwrite', 'write'] (got {repr(mode)})\"\n        )\n    return mode\n\ndef _memmap(fid, dtype: type, mode: str, offset: int, shape, forder: bool):\n    fid.seek(0, 2)\n    flen = fid.tell()\n    dbytes = util.sizeof(dtype)\n\n    if shape is None:\n        nbytes = flen - offset\n        if nbytes % dbytes:\n            raise ValueError(\"Size of available data is not a \"\n                             \"multiple of the data-type size.\")\n        size = nbytes // dbytes\n        sh = (size, )\n    else:\n        if isinstance(shape, int):\n            sh = (shape, )\n        else:\n            sh = shape\n        size = util.count(shape)\n\n    nbytes = offset + size * dbytes\n\n    if mode in ('w+', 'r+') and flen < nbytes:\n        fid.seek(nbytes - 1, 0)\n        fid.write('\\0')\n        fid.flush()\n\n    if mode == 'c':\n        flags = _MAP_PRIVATE\n        acc = _PROT_READ | _PROT_WRITE\n    elif mode == 'r':\n        flags = _MAP_SHARED\n        acc = _PROT_READ\n    else:\n        flags = _MAP_SHARED\n        acc = _PROT_READ | _PROT_WRITE\n\n    start = offset - offset % _ALLOCATIONGRANULARITY\n    nbytes -= start\n    array_offset = offset - start\n    mm = _mmap(fid, length=nbytes, access=acc, flags=flags, offset=start)\n    return ndarray(sh, Ptr[dtype](mm + array_offset), fcontig=forder)\n\ndef memmap(filename,\n           dtype: type = u8,\n           mode: str = 'r+',\n           offset: int = 0,\n           shape=None,\n           order: str = 'C'):\n    mode = _fix_mmap_mode(mode)\n\n    if mode == 'w+' and shape is None:\n        raise ValueError(\"shape must be given if mode == 'w+'\")\n\n    if offset < 0:\n        raise ValueError(\n            f\"memmap() 'offset' cannot be negative (got {offset})\")\n\n    forder = False\n    if order == 'C' or order == 'c':\n        forder = False\n    elif order == 'F' or order == 'f':\n        forder = True\n    else:\n        raise ValueError(f\"memmap() 'order' must be 'C' or 'F' (got {order})\")\n\n    if hasattr(filename, 'read'):\n        return _memmap(filename,\n                       dtype=dtype,\n                       mode=mode,\n                       offset=offset,\n                       shape=shape,\n                       forder=forder)\n    elif isinstance(filename, str):\n        with open(filename, ('r' if mode == 'c' else mode) + 'b') as f:\n            return _memmap(f,\n                           dtype=dtype,\n                           mode=mode,\n                           offset=offset,\n                           shape=shape,\n                           forder=forder)\n    else:\n        compile_error(\"filename must be a string or file handle\")\n\n###############\n# General I/O #\n###############\n\ndef parse_header(header: str):\n    regex = (\n        r\"{\\s*\"\n        r\"(?:\"\n        r\"(?:'descr'|\\\"descr\\\")\\s*:\\s*'(?P<descr>.*?)'\\s*,?\\s*\"\n        r\"|\"\n        r\"(?:'fortran_order'|\\\"fortran_order\\\")\\s*:\\s*(?P<fortran_order>True|False)\\s*,?\\s*\"\n        r\"|\"\n        r\"(?:'shape'|\\\"shape\\\")\\s*:\\s*(?P<shape>\\(\\)|\\(\\d+(?:, \\d+)*(?:,)?\\))\\s*,?\\s*\"\n        r\"){3}\"\n        r\"\\s*}\")\n    m = re.match(regex, header)\n    if m:\n        # Use the named groups to access the matched values\n        dtype = m.group(1)\n        fortran_order = m.group(2)\n        shape = m.group(3)\n        return shape, fortran_order, dtype\n    else:\n        raise ValueError(f\"Cannot parse header: {repr(header)}\")\n\ndef _load(f, mmap_mode: Optional[str], ndim: Literal[int], dtype: type):\n    magic = f.read(len(_MAGIC_PREFIX))\n\n    if magic != _MAGIC_PREFIX:\n        raise ValueError(\"Invalid magic string.\")\n\n    # Extract the major and minor version numbers\n    major = f.read(1)\n    minor = f.read(1)\n    if (major != '\\x01' and major != '\\x02'\n            and major != '\\x03') or minor != '\\x00':\n        raise ValueError(\"Invalid version numbers.\")\n\n    # Extract the header length as a little-endian unsigned int\n    if major == '\\x01':\n        header_len_enc = f.read(2)\n        header_len = (int(header_len_enc.ptr[1]) << 8) | int(\n            header_len_enc.ptr[0])\n    else:\n        header_len_enc = f.read(4)\n        header_len = ((int(header_len_enc.ptr[3]) << 24) |\n                      (int(header_len_enc.ptr[2]) << 16) |\n                      (int(header_len_enc.ptr[1]) << 8)\n                      | int(header_len_enc.ptr[0]))\n\n    # Read the header data\n    header_data = f.read(header_len)\n\n    # Deserialize the header dictionary data\n    shape, fortran_order, dt = parse_header(header_data)\n\n    # Extract shape information from the header data\n    elements = [\n        elem.strip() for elem in shape[1:-1].split(',') if elem.strip()\n    ]\n    if len(elements) != ndim:\n        raise ValueError(\n            f\"Loaded array has dimension {len(elements)}, but expected dimension {ndim} (specified by 'ndim' argument)\"\n        )\n    shape = tuple(int(elements[i]) for i in static.range(ndim))\n    forder = (fortran_order == 'True')\n\n    # Read the binary data\n    if mmap_mode is None:\n        str_data = f.read()\n        binary_data = str_data.ptr\n        got_bytes = len(str_data)\n    else:\n        arr_data = _memmap(f,\n                           dtype=byte,\n                           mode=mmap_mode,\n                           offset=f.tell(),\n                           shape=None,\n                           forder=forder)\n        binary_data = arr_data.data\n        got_bytes = arr_data.size\n\n    exp_bytes = util.count(shape) * util.sizeof(dtype)\n    if got_bytes != exp_bytes:\n        raise ValueError(\n            f\"Unexpected number of bytes read from file for given array shape and dtype (expected {exp_bytes} but got {got_bytes})\"\n        )\n\n    # Create a ndarray from the binary data\n    data = Ptr[dtype](binary_data)\n    array = ndarray[dtype, ndim](shape, data, fcontig=forder)\n\n    if dt.startswith('>') and mmap_mode is None:\n        array.byteswap(inplace=True)\n\n    return array\n\ndef load(file,\n         mmap_mode: Optional[str] = None,\n         ndim: Literal[int] = 1,\n         dtype: type = float):\n    if mmap_mode is not None:\n        mmap_mode = _fix_mmap_mode(mmap_mode)\n        if mmap_mode == 'w+':\n            raise ValueError(\"cannot use mmap_mode='w+' in load()\")\n\n    if hasattr(file, 'read'):\n        return _load(file, mmap_mode=mmap_mode, ndim=ndim, dtype=dtype)\n    elif isinstance(file, str):\n        open_mode = 'rb'\n        if mmap_mode is not None:\n            open_mode = ('r' if mmap_mode == 'c' else mmap_mode) + 'b'\n        with open(file, open_mode) as f:\n            return _load(f, mmap_mode=mmap_mode, ndim=ndim, dtype=dtype)\n    else:\n        compile_error(\"fname must be a string or file handle\")\n\ndef _save(f, arr):\n    arr = asarray(arr)\n    cc, fc = arr._contig\n    fortran_order = (fc and not cc)\n    header_parts = (\"{'descr': '\",\n                    util.dtype_to_str(arr.dtype, include_byteorder=True),\n                    \"', 'fortran_order': \",\n                    \"True, 'shape': \" if fortran_order else \"False, 'shape': \",\n                    str(arr.shape), \", }\")\n    header_len = sum(len(h) for h in header_parts) + 1  # +1 for newline\n    long_header = False\n\n    if header_len > 0xffffffff:\n        raise ValueError(\"Header is too long for .npy format\")\n    elif header_len > 0xffff:\n        long_header = True\n    else:\n        long_header = False\n\n    f.write(_MAGIC_PREFIX)\n    f.write('\\x02\\x00' if long_header else '\\x01\\x00')\n\n    m = len(_MAGIC_PREFIX) + 2 + (4 if long_header else 2) + header_len\n    rem = m % _ARRAY_ALIGN\n    spaces = (_ARRAY_ALIGN - rem) if rem else 0\n    header_len += spaces\n\n    if long_header:\n        byte1 = byte(header_len & 0xFF)\n        byte2 = byte((header_len >> 8) & 0xFF)\n        byte3 = byte((header_len >> 16) & 0xFF)\n        byte4 = byte((header_len >> 24) & 0xFF)\n        f.write(str(__ptr__(byte1), 1))\n        f.write(str(__ptr__(byte2), 1))\n        f.write(str(__ptr__(byte3), 1))\n        f.write(str(__ptr__(byte4), 1))\n    else:\n        byte1 = byte(header_len & 0xFF)\n        byte2 = byte((header_len >> 8) & 0xFF)\n        f.write(str(__ptr__(byte1), 1))\n        f.write(str(__ptr__(byte2), 1))\n\n    for h in header_parts:\n        f.write(h)\n\n    for _ in range(spaces):\n        f.write('\\x20')\n\n    f.write('\\n')\n\n    if cc or fc:\n        f.write(str(arr.data.as_byte(), arr.nbytes))\n    else:\n        for idx in util.multirange(arr.shape):\n            e = arr._ptr(idx)[0]\n            s = str(__ptr__(e).as_byte(), util.sizeof(arr.dtype))\n            f.write(s)\n\ndef save(file, arr):\n    if hasattr(file, 'write'):\n        _save(file, arr)\n    elif isinstance(file, str):\n        if not file.endswith('.npy'):\n            file += '.npy'\n        with open(file, 'wb') as f:\n            _save(f, arr)\n    else:\n        compile_error(\"fname must be a string or file handle\")\n\ndef _savetxt(f, X, delimiter: str, newline: str, header: str, footer: str,\n             comments: str):\n    X = asarray(X)\n\n    if header:\n        header = header.replace('\\n', '\\n' + comments)\n        f.write(comments)\n        f.write(header)\n        f.write(newline)\n\n    if X.ndim == 1:\n        for x in X:\n            f.write(str(x))\n            f.write(newline)\n    elif X.ndim == 2:\n        m, n = X.shape\n        if m and n:\n            for i in range(m):\n                for j in range(n):\n                    x = X._ptr((i, j))[0]\n                    f.write(str(x))\n                    if j < n - 1:\n                        f.write(delimiter)\n                f.write(newline)\n    else:\n        compile_error(\"Expected 1D or 2D array\")\n\n    if footer:\n        footer = footer.replace('\\n', '\\n' + comments)\n        f.write(comments)\n        f.write(footer)\n        f.write(newline)\n\ndef savetxt(fname,\n            X,\n            delimiter: str = ' ',\n            newline: str = '\\n',\n            header: str = '',\n            footer: str = '',\n            comments: str = '# '):\n    if hasattr(fname, 'write'):\n        _savetxt(fname,\n                 X,\n                 delimiter=delimiter,\n                 newline=newline,\n                 header=header,\n                 footer=footer,\n                 comments=comments)\n    elif isinstance(fname, str):\n        if fname.endswith('.gz'):\n            with gzip.open(fname, 'w9') as f:\n                _savetxt(f,\n                         X,\n                         delimiter=delimiter,\n                         newline=newline,\n                         header=header,\n                         footer=footer,\n                         comments=comments)\n        else:\n            with open(fname, 'w') as f:\n                _savetxt(f,\n                         X,\n                         delimiter=delimiter,\n                         newline=newline,\n                         header=header,\n                         footer=footer,\n                         comments=comments)\n    else:\n        compile_error(\"fname must be a string or a file handle\")\n\ndef _fromfile(f, dtype: type, count: int, sep: str, offset: int):\n    if sep:\n        if offset:\n            raise TypeError(\n                \"'offset' argument only permitted for binary files\")\n\n        string_data = f.read()\n        if sep.isspace():\n            string_split = string_data.split(None, count)\n        else:\n            string_split = string_data.split(sep, count)\n\n        n = len(string_split)\n        p = Ptr[dtype](n)\n        for i in range(n):\n            p[i] = dtype(string_split[i])\n    else:\n        if offset:\n            f.seek(offset, 1)\n\n        if count < 0:\n            binary_data = f.read()\n        else:\n            binary_data = f.read(count * util.sizeof(dtype))\n\n        p = Ptr[dtype](binary_data.ptr)\n        n = len(binary_data) // util.sizeof(dtype)\n\n    return ndarray((n, ), p)\n\ndef fromfile(file,\n             dtype: type = float,\n             count: int = -1,\n             sep: str = '',\n             offset: int = 0):\n    if hasattr(file, 'read'):\n        return _fromfile(file,\n                         dtype=dtype,\n                         count=count,\n                         sep=sep,\n                         offset=offset)\n    elif isinstance(file, str):\n        with open(file, 'rb') as f:\n            return _fromfile(f,\n                             dtype=dtype,\n                             count=count,\n                             sep=sep,\n                             offset=offset)\n    else:\n        compile_error(\"fname must be a string or a file handle\")\n\ndef fromstring(string: str,\n               dtype: type = float,\n               count: int = -1,\n               sep: str = ''):\n    if sep:\n        split = string.split(sep, count)\n        k = len(split)\n        n = count if count >= 0 else k\n        result = empty((n, ), dtype=dtype)\n\n        p = result.data\n        for i in range(k if count < 0 else min(k, count)):\n            p[i] = dtype(split[i])\n\n        return result\n    else:\n        if count < 0:\n            if len(string) < util.sizeof(dtype):\n                raise ValueError(\"string is smaller than requested size\")\n\n            if len(string) % util.sizeof(dtype) != 0:\n                raise ValueError(\n                    \"string size must be a multiple of element size\")\n        else:\n            if len(string) < count * util.sizeof(dtype):\n                raise ValueError(\"string is smaller than requested size\")\n\n        n = count if count >= 0 else (len(string) // util.sizeof(dtype))\n        return ndarray((n, ), Ptr[dtype](string.ptr))\n\ndef _tofile(arr, f, sep: str):\n    if sep:\n        k = 0\n        n = arr.size\n\n        for idx in util.multirange(arr.shape):\n            e = arr._ptr(idx)[0]\n            f.write(str(e))\n            if k < n - 1:\n                f.write(sep)\n            k += 1\n    else:\n        cc, _ = arr._contig\n        if cc:\n            f.write(str(arr.data.as_byte(), arr.nbytes))\n        else:\n            for idx in util.multirange(arr.shape):\n                e = arr._ptr(idx)[0]\n                s = str(__ptr__(e).as_byte(), util.sizeof(arr.dtype))\n                f.write(s)\n\n@extend\nclass ndarray:\n    def tofile(self, file, sep: str = ''):\n        if hasattr(file, 'write'):\n            _tofile(self, file, sep=sep)\n        elif isinstance(file, str):\n            with open(file, 'w' if sep else 'wb') as f:\n                _tofile(self, f, sep=sep)\n        else:\n            compile_error(\"fname must be a string or file handle\")\n\n########################\n# loadtxt / genfromtxt #\n########################\n\n_NEWLINE: Literal[int] = 10\n_CARTRIDGE: Literal[int] = 13\n_DEFAULT_ROWS: Literal[int] = 512\n\n@tuple\nclass Converters:\n    funcs: F\n    mask: M\n    usecols: U\n    dtype: type\n    F: type\n    M: type\n    U: type\n\n    def __new__(funcs, mask, usecols,\n                dtype: type) -> Converters[dtype, F, M, U]:\n        return superf(funcs, mask, usecols)\n\n    def __call__(self, field: str, idx: int):\n        usecols = self.usecols\n        if usecols is not None:\n            idx = usecols[idx]\n\n        if self.mask[idx]:\n            return self.funcs[idx](field)\n        else:\n            return self.dtype(field)\n\ndef normalize_col(col: int, num_fields: int):\n    if col >= num_fields or col < -num_fields:\n        raise ValueError(\n            f\"given column {col} is out of bounds (file has {num_fields} columns)\"\n        )\n    elif col < 0:\n        return col + num_fields\n    else:\n        return col\n\ndef default_fill(dtype: type):\n    if dtype is bool:\n        return False\n    elif dtype is int or isinstance(dtype, Int) or isinstance(dtype, UInt):\n        return dtype(-1)\n    elif dtype is float or dtype is float32 or dtype is float16:\n        return util.nan(dtype)\n    elif dtype is complex:\n        return complex(util.nan64(), 0.0)\n    elif dtype is complex64:\n        return complex64(util.nan32(), float32(0.0))\n    elif dtype is str:\n        return '???'\n    else:\n        return util.zero(dtype)\n\ndef malformed(row: int, num_fields: int):\n    raise IOError(\n        f\"inconsistent number of fields in file (row = {row}, expected fields = {num_fields})\"\n    )\n\ndef min_dim(arr: ndarray, ndmin: Literal[int]):\n    if arr.ndim == 1 and ndmin == 2:\n        return arr.reshape(arr.size, 1)\n    else:\n        return arr\n\ndef make_conv(converters, num_fields: int, usecols, dtype: type):\n    if isinstance(converters, Dict):\n        funcs = Ptr[converters.V](num_fields)\n        mask = Ptr[bool](num_fields)\n\n        for i in range(num_fields):\n            mask[i] = False\n\n        for k, v in converters.items():\n            col = normalize_col(k, num_fields)\n            mask[col] = True\n            funcs[col] = v\n\n        return Converters[dtype, type(funcs),\n                          type(mask),\n                          type(usecols)](funcs, mask, usecols, dtype)\n    else:\n        return converters\n\ndef _isspace(c: byte):\n    return bool(_C.isspace(i32(int(c))))\n\nclass CSVReader:\n    _path: str\n    _delimiter: byte\n    _quotechar: byte\n    _comments: str\n    _mmap_ptr: cobj\n    _mmap_len: int\n\n    def __init__(self,\n                 path: str,\n                 delimiter: str = ',',\n                 quotechar: str = '\"',\n                 comments: str = ''):\n        dm = byte(0)\n        if len(delimiter) == 1:\n            dm = delimiter.ptr[0]\n        elif len(delimiter) != 0:\n            raise ValueError(\"'delimiter' must be a length-1 string\")\n\n        qc = byte(0)\n        if len(quotechar) == 1:\n            qc = quotechar.ptr[0]\n        elif len(quotechar) != 0:\n            raise ValueError(\"'quotechar' must be a length-1 string\")\n\n        self._path = path\n        self._delimiter = dm\n        self._quotechar = qc\n        self._comments = comments\n        self._mmap_ptr = cobj()\n        self._mmap_len = 0\n\n    def __enter__(self):\n        with open(self._path) as f:\n            f.seek(0, 2)\n            n = f.tell()\n            if n > 0:\n                self._mmap_ptr = _mmap(f,\n                                       length=n,\n                                       access=_PROT_READ,\n                                       flags=_MAP_SHARED)\n            self._mmap_len = n\n\n        if int(self._mmap_ptr) == -1:\n            raise IOError(\"CSVReader error: mmap() failed\")\n\n    def __exit__(self):\n        if self._mmap_len > 0:\n            _munmap(self._mmap_ptr, self._mmap_len)\n            self._mmap_ptr = cobj()\n            self._mmap_len = 0\n\n    def is_delimiter(self, c: byte):\n        delimiter = self._delimiter\n        if delimiter:\n            return c == delimiter\n        else:\n            return _isspace(c)\n\n    def is_comment(self, c: byte):\n        comments = self._comments\n        for i in range(len(comments)):\n            if comments.ptr[i] == c:\n                return True\n        return False\n\n    def is_whitespace_delimiter(self):\n        return not self._delimiter\n\n    def skip_past_line(self, i: int):\n        p = self._mmap_ptr\n        n = self._mmap_len\n\n        while i < n:\n            c = p[i]\n            i += 1\n            if c == byte(_NEWLINE) or c == byte(_CARTRIDGE):\n                if c == byte(_CARTRIDGE) and i < n and p[i] == byte(_NEWLINE):\n                    i += 1\n                break\n\n        return i\n\n    def skip_delimiter(self, i: int):\n        delimiter = self._delimiter\n        i += 1\n\n        # Single-char case\n        if delimiter:\n            return i\n\n        # Whitespace case\n        p = self._mmap_ptr\n        n = self._mmap_len\n\n        while i < n:\n            c = p[i]\n            if (c == byte(_NEWLINE) or c == byte(_CARTRIDGE)\n                    or not self.is_delimiter(c)):\n                break\n            i += 1\n\n        return i\n\n    def skip_comments(self, i: int):\n        if self.is_whitespace_delimiter():\n            p = self._mmap_ptr\n            n = self._mmap_len\n\n            while i < n:\n                if _isspace(p[i]):\n                    i += 1\n                elif self.is_comment(p[i]):\n                    i = self.skip_past_line(i + 1)\n                else:\n                    break\n\n            return i\n\n        if not self._comments:\n            return i\n\n        p = self._mmap_ptr\n        n = self._mmap_len\n\n        while i < n:\n            c = p[i]\n            if self.is_comment(c):\n                i = self.skip_past_line(i + 1)\n            else:\n                break\n\n        return i\n\n    def skip_lines(self, i: int, skip: int):\n        p = self._mmap_ptr\n        n = self._mmap_len\n        skipped = 0\n\n        while i < n and skipped < skip:\n            c = p[i]\n            if (c == byte(_NEWLINE) or c == byte(_CARTRIDGE)):\n                i += 1\n                if c == byte(_CARTRIDGE) and i < n and p[i] == byte(_NEWLINE):\n                    i += 1\n                skipped += 1\n            else:\n                i += 1\n\n        return i\n\n    def get_num_fields(self, i0: int):\n        p = self._mmap_ptr\n        n = self._mmap_len\n        quotechar = self._quotechar\n        comments = self._comments\n\n        if n == 0:\n            return 0\n\n        quote = False\n        num_fields = 1\n        i = self.skip_comments(i0)\n\n        if i >= n:\n            return 0\n\n        # Parse first row to get field count\n        while i < n:\n            c = p[i]\n            if quotechar and c == quotechar:\n                if quote:\n                    if i + 1 < n and p[i + 1] == quotechar:\n                        i += 2\n                    else:\n                        quote = False\n                        i += 1\n                else:\n                    quote = True\n                    i += 1\n            elif (c == byte(_NEWLINE) or c == byte(_CARTRIDGE)) and not quote:\n                i += 1\n                if c == byte(_CARTRIDGE) and i < n and p[i] == byte(_NEWLINE):\n                    i += 1\n                break\n            elif self.is_delimiter(c) and not quote:\n                i = self.skip_delimiter(i)\n\n                # trailing whitespace case\n                if (self.is_whitespace_delimiter() and\n                    (i >= n or p[i] == byte(_NEWLINE)\n                     or p[i] == byte(_CARTRIDGE) or self.is_comment(p[i]))):\n                    continue\n\n                num_fields += 1\n            elif self.is_comment(c) and not quote:\n                break\n            else:\n                i += 1\n\n        return num_fields\n\n    def parse(self, row_callback, num_fields: int, maxrows: int, i0: int):\n        p = self._mmap_ptr\n        n = self._mmap_len\n        quotechar = self._quotechar\n\n        if n == 0 or num_fields == 0 or maxrows == 0:\n            return\n\n        quote = False\n        field = 0\n        fields = Ptr[str](num_fields)\n        row = 0\n        i = self.skip_comments(i0)\n        last_field_start = i\n\n        while i < n:\n            c = p[i]\n            if quotechar and c == quotechar:\n                if quote:\n                    if i + 1 < n and p[i + 1] == quotechar:\n                        i += 2\n                    else:\n                        quote = False\n                        i += 1\n                else:\n                    quote = True\n                    i += 1\n            elif (c == byte(_NEWLINE) or c == byte(_CARTRIDGE)) and not quote:\n                if field != num_fields - 1:\n                    malformed(row, num_fields)\n\n                fields[field] = str(p + last_field_start, i - last_field_start)\n                row_callback(fields, num_fields, row)\n\n                i += 1\n                if c == byte(_CARTRIDGE) and i < n and p[i] == byte(_NEWLINE):\n                    i += 1\n\n                i = self.skip_comments(i)\n                last_field_start = i\n                field = 0\n                row += 1\n                if maxrows >= 0 and row >= maxrows:\n                    break\n            elif self.is_delimiter(c) and not quote:\n                fields[field] = str(p + last_field_start, i - last_field_start)\n\n                i = self.skip_delimiter(i)\n\n                # trailing whitespace case\n                if (self.is_whitespace_delimiter() and\n                    (i >= n or p[i] == byte(_NEWLINE)\n                     or p[i] == byte(_CARTRIDGE) or self.is_comment(p[i]))):\n                    continue\n\n                field += 1\n                if field >= num_fields:\n                    malformed(row, num_fields)\n\n                last_field_start = i\n            elif self.is_comment(c) and not quote:\n                if field != num_fields - 1:\n                    malformed(row, num_fields)\n\n                fields[field] = str(p + last_field_start, i - last_field_start)\n                row_callback(fields, num_fields, row)\n\n                i = self.skip_comments(i)\n                last_field_start = i\n                field = 0\n                row += 1\n                if maxrows >= 0 and row >= maxrows:\n                    break\n            else:\n                i += 1\n\n        if field > 0:\n            if field != num_fields - 1:\n                malformed(row, num_fields)\n\n            if maxrows < 0 or row <= maxrows:\n                fields[field] = str(p + last_field_start, i - last_field_start)\n                row_callback(fields, num_fields, row)\n\nclass ArrayUpdate:\n    arr: A\n    cols: C\n    conv: F\n    last_row: int\n    A: type\n    C: type\n    F: type\n\n    def __init__(self, arr: A, cols: C, conv: F):\n        self.arr = arr\n        self.cols = cols\n        self.conv = conv\n        self.last_row = 0\n\n    @property\n    def cap(self):\n        arr = self.arr\n        if isinstance(arr, Tuple):\n            return arr[0].size\n        else:\n            return arr.shape[0]\n\n    def resize_arrays(self, new_cap: int):\n        def resize_one(a, new_cap: int):\n            if a.ndim == 1:\n                data_new = util.realloc(a.data, new_cap, a.size)\n                return ndarray((new_cap, ), data_new)\n            else:\n                rows, cols = a.shape\n                data_new = util.realloc(a.data, new_cap * cols, a.size)\n                return ndarray((new_cap, cols), data_new)\n\n        arr = self.arr\n        if isinstance(arr, Tuple):\n            self.arr = tuple(resize_one(a, new_cap) for a in arr)\n        else:\n            self.arr = resize_one(arr, new_cap)\n\n    def trim_arrays(self):\n        if self.last_row < self.cap - 1:\n            self.resize_arrays(self.last_row + 1)\n\n    def convert(self, field: str, idx: int, dtype: type) -> dtype:\n        conv = self.conv\n        if conv is None:\n            r = dtype(field)\n        elif isinstance(conv, Converters):\n            r = conv(field, idx)\n        elif hasattr(conv, '__call__'):\n            r = conv(field)\n        else:\n            r = conv[idx](field)\n\n        # Make sure we copy strings out of the mmap buffer\n        if isinstance(r, str):\n            return r.__ptrcopy__()\n        else:\n            return r\n\n    def convert_static(self, field: str, idx: Literal[int],\n                       dtype: type) -> dtype:\n        conv = self.conv\n        if conv is None:\n            r = dtype(field)\n        elif isinstance(conv, Converters):\n            r = conv(field, idx)\n        elif hasattr(conv, '__call__'):\n            r = conv(field)\n        else:\n            r = conv[idx](field)\n\n        # Make sure we copy strings out of the mmap buffer\n        if isinstance(r, str):\n            return r.__ptrcopy__()\n        else:\n            return r\n\n    def __call__(self, fields: Ptr[str], num_fields: int, row: int):\n        def get_new_size(size: int, min_grow: int = 512):\n            new_size = size\n            growth = size >> 2\n            if growth <= min_grow:\n                new_size += min_grow\n            else:\n                new_size += growth + min_grow - 1\n                new_size &= ~min_grow\n            return new_size\n\n        arr = self.arr\n        cols = self.cols\n        cap = self.cap\n\n        if row >= cap:\n            new_cap = get_new_size(cap)\n            self.resize_arrays(new_cap)\n\n        # Lots of different cases to consider...\n        if cols is not None:\n            if isinstance(arr, Tuple):\n                for i in static.range(static.len(cols)):\n                    col = cols[i]\n                    arr[i].data[row] = self.convert_static(\n                        fields[col], i, arr[i].dtype)\n            else:\n                if isinstance(arr.dtype, Tuple):\n                    dummy = util.zero(arr.dtype)\n                    tup = tuple(\n                        self.convert(fields[cols[i]], i, type(dummy[i]))\n                        for i in static.range(static.len(arr.dtype)))\n                    arr._ptr((row, ))[0] = tup\n                else:\n                    if arr.ndim == 1:\n                        col = cols[0]\n                        arr.data[row] = self.convert_static(\n                            fields[col], 0, arr.dtype)\n                    else:\n                        for i in static.range(static.len(cols)):\n                            col = cols[i]\n                            arr._ptr((row, i))[0] = self.convert_static(\n                                fields[col], i, arr.dtype)\n        else:\n            if isinstance(arr, Tuple):\n                for i in static.range(static.len(arr)):\n                    arr[i]._ptr((row, ))[0] = self.convert_static(\n                        fields[i], i, arr[i].dtype)\n            elif isinstance(arr.dtype, Tuple):\n                dummy = util.zero(arr.dtype)\n                tup = tuple(\n                    self.convert(fields[i], i, type(dummy[i]))\n                    for i in static.range(static.len(arr.dtype)))\n                arr._ptr((row, ))[0] = tup\n            else:\n                for i in range(num_fields):\n                    arr._ptr(\n                        (row, i))[0] = self.convert(fields[i], i, arr.dtype)\n\n        self.last_row = row\n\ndef loadtxt(fname: str,\n            dtype: type = float,\n            comments: Optional[str] = '#',\n            delimiter: Optional[str] = None,\n            converters=None,\n            skiprows: int = 0,\n            usecols=None,\n            unpack: Literal[bool] = False,\n            ndmin: Literal[int] = 0,\n            max_rows: Optional[int] = None,\n            quotechar: Optional[str] = None):\n\n    if isinstance(usecols, int):\n        cols = (usecols, )\n    else:\n        cols = usecols\n        if cols is not None:\n            if static.len(cols) == 0:\n                compile_error(\"cannot pass empty tuple to 'usecols'\")\n\n    if ndmin != 0 and ndmin != 1 and ndmin != 2:\n        compile_error(\"'ndmin' must be 0, 1 or 2\")\n\n    maxrows: int = max_rows if max_rows is not None else -1\n    block_size = maxrows if maxrows >= 0 else _DEFAULT_ROWS\n\n    with CSVReader(fname,\n                   delimiter=(delimiter if delimiter is not None else ''),\n                   quotechar=(quotechar if quotechar is not None else ''),\n                   comments=comments if comments is not None else '') as csv:\n        i0 = csv.skip_lines(i=0, skip=skiprows) if skiprows > 0 else 0\n        num_fields = csv.get_num_fields(i0)\n\n        if cols is None:\n            if isinstance(dtype, Tuple):\n                if static.len(dtype) != num_fields:\n                    raise ValueError(\n                        \"number of fields is different than given tuple 'dtype' length\"\n                    )\n                if unpack:\n                    dummy = util.zero(dtype)\n                    arr = tuple(\n                        empty((block_size, ), type(dummy[i]))\n                        for i in static.range(static.len(dtype)))\n                else:\n                    arr = empty((block_size, ), dtype)\n            else:\n                arr = empty((block_size, num_fields), dtype)\n        else:\n            if isinstance(dtype, Tuple):\n                if static.len(dtype) != static.len(cols):\n                    compile_error(\n                        \"'usecols' has different length than given tuple 'dtype'\"\n                    )\n\n            cols = tuple(normalize_col(col, num_fields) for col in cols)\n            for i in range(1, len(cols)):\n                for j in range(i):\n                    if cols[i] == cols[j]:\n                        raise ValueError(\n                            f\"duplicate column {cols[i]} given in 'usecols'\")\n\n            if static.len(cols) == 1:\n                arr = empty((block_size, ), dtype)\n            else:\n                if unpack:\n                    if isinstance(dtype, Tuple):\n                        dummy = util.zero(dtype)\n                        ncols: Literal[int] = static.len(cols)\n                        arr = tuple(\n                            empty((block_size, ), type(dummy[i]))\n                            for i in static.range(ncols))\n                    else:\n                        arr = tuple(empty((block_size, ), dtype) for _ in cols)\n                else:\n                    if isinstance(dtype, Tuple):\n                        arr = empty((block_size, ), dtype)\n                    else:\n                        arr = empty((block_size, len(cols)), dtype)\n\n        converters = make_conv(converters, num_fields, cols, dtype)\n        callback = ArrayUpdate(arr, cols, converters)\n        csv.parse(callback, num_fields=num_fields, maxrows=maxrows, i0=i0)\n        callback.trim_arrays()\n        arr = callback.arr\n\n        if isinstance(arr, ndarray):\n            if unpack:\n                return min_dim(arr, ndmin).T\n            else:\n                return min_dim(arr, ndmin)\n        else:\n            return arr\n\nclass CSVReaderGen:\n    _path: str\n    _delimiter: byte\n    _comments: str\n    _names: List[str]\n    _mmap_ptr: cobj\n    _mmap_len: int\n    _length: int\n\n    def __init__(self, path: str, delimiter: str = ',', comments: str = ''):\n        dm = byte(0)\n        if len(delimiter) == 1:\n            dm = delimiter.ptr[0]\n        elif len(delimiter) != 0:\n            raise ValueError(\"'delimiter' must be a length-1 string\")\n\n        self._path = path\n        self._delimiter = dm\n        self._comments = comments\n        self._names = []\n        self._mmap_ptr = cobj()\n        self._mmap_len = 0\n        self._length = 0\n\n    def __enter__(self):\n        if not self._path:\n            return\n\n        with open(self._path) as f:\n            f.seek(0, 2)\n            n = f.tell()\n            if n > 0:\n                self._mmap_ptr = _mmap(f,\n                                       length=n,\n                                       access=_PROT_READ,\n                                       flags=_MAP_SHARED)\n            self._mmap_len = n\n\n        if int(self._mmap_ptr) == -1:\n            raise IOError(\"CSVReader error: mmap() failed\")\n\n    def __exit__(self):\n        if self._mmap_len > 0:\n            _munmap(self._mmap_ptr, self._mmap_len)\n            self._mmap_ptr = cobj()\n            self._mmap_len = 0\n\n    def fix_names(self, excludelist: List[str], deletechars: str,\n                  replace_space: str, upper: bool, lower: bool):\n        def fix_name(name: str, excludelist: List[str], deletechars: str,\n                     replace_space: str, upper: bool, lower: bool):\n            s = _strbuf(capacity=len(name))\n            for c in name:\n                if c in deletechars:\n                    continue\n                elif c.isspace():\n                    s.append(replace_space)\n                elif upper:\n                    s.append(c.upper())\n                elif lower:\n                    s.append(c.lower())\n                else:\n                    s.append(c)\n\n            if s.__str__() in excludelist:\n                s.append('_')\n\n            return s.__str__()\n\n        names = self._names\n        for i in range(len(names)):\n            names[i] = fix_name(names[i],\n                                excludelist=excludelist,\n                                deletechars=deletechars,\n                                replace_space=replace_space,\n                                upper=upper,\n                                lower=lower)\n\n    def is_delimiter(self, c: byte):\n        delimiter = self._delimiter\n        if delimiter:\n            return c == delimiter\n        else:\n            return _isspace(c)\n\n    def is_whitespace_delimiter(self):\n        return not self._delimiter\n\n    def is_comment(self, c: byte):\n        comments = self._comments\n        for i in range(len(comments)):\n            if comments.ptr[i] == c:\n                return True\n        return False\n\n    def skip_lines(self, i: int, skip: int):\n        p = self._mmap_ptr\n        n = self._length\n        skipped = 0\n\n        while i < n and skipped < skip:\n            c = p[i]\n            if (c == byte(_NEWLINE) or c == byte(_CARTRIDGE)):\n                i += 1\n                if c == byte(_CARTRIDGE) and i < n and p[i] == byte(_NEWLINE):\n                    i += 1\n                skipped += 1\n            else:\n                i += 1\n\n        return i\n\n    def find_length(self, skip_footer: int):\n        p = self._mmap_ptr\n        n = self._mmap_len\n        skipped = 0\n\n        if skip_footer <= 0:\n            self._length = n\n            return\n\n        i = n - 1\n\n        # Newline at the very end doesn't count\n        if i >= 0 and p[i] == byte(_NEWLINE):\n            i -= 1\n\n        while i >= 0:\n            c = p[i]\n            if c == byte(_NEWLINE):\n                skipped += 1\n                if skipped == skip_footer:\n                    self._length = i + 1\n                    return\n            i -= 1\n\n        self._length = 0\n\n    def skip_delimiter(self, i: int):\n        delimiter = self._delimiter\n        i += 1\n\n        # Single-char case\n        if delimiter:\n            return i\n\n        # Whitespace case\n        p = self._mmap_ptr\n        n = self._length\n\n        while i < n:\n            c = p[i]\n            if (c == byte(_NEWLINE) or c == byte(_CARTRIDGE)\n                    or not self.is_delimiter(c)):\n                break\n            i += 1\n\n        return i\n\n    def skip_past_line(self, i: int):\n        p = self._mmap_ptr\n        n = self._mmap_len\n\n        while i < n:\n            c = p[i]\n            i += 1\n            if c == byte(_NEWLINE) or c == byte(_CARTRIDGE):\n                if c == byte(_CARTRIDGE) and i < n and p[i] == byte(_NEWLINE):\n                    i += 1\n                break\n\n        return i\n\n    def skip_delimiter(self, i: int, line: str):\n        delimiter = self._delimiter\n        i += 1\n\n        # Single-char case\n        if delimiter:\n            return i\n\n        # Whitespace case\n        p = line.ptr\n        n = len(line)\n\n        while i < n:\n            c = p[i]\n            if not self.is_delimiter(c):\n                break\n            i += 1\n\n        return i\n\n    def skip_comments_no_whitespace(self, i: int):\n        if not self._comments:\n            return i\n\n        p = self._mmap_ptr\n        n = self._mmap_len\n\n        while i < n:\n            c = p[i]\n            if self.is_comment(c):\n                i = self.skip_past_line(i + 1)\n            else:\n                break\n\n        return i\n\n    def skip_comments(self, i: int):\n        if self.is_whitespace_delimiter():\n            p = self._mmap_ptr\n            n = self._mmap_len\n\n            while i < n:\n                if _isspace(p[i]):\n                    i += 1\n                elif self.is_comment(p[i]):\n                    i = self.skip_past_line(i + 1)\n                else:\n                    break\n\n            return i\n        else:\n            return self.skip_comments_no_whitespace(i)\n\n    def is_comment_or_blank_line(self, line: str):\n        if not line:\n            return True\n\n        if self.is_whitespace_delimiter():\n            p = line.ptr\n            n = len(line)\n            i = 0\n            while i < n:\n                c = p[i]\n                if self.is_comment(c):\n                    return True\n                elif not _isspace(c):\n                    return False\n                i += 1\n            return True\n        else:\n            return self.is_delimiter(line.ptr[0])\n\n    def get_num_fields(self, i: int, get_names: bool):\n        p = self._mmap_ptr\n        n = self._length\n        comments = self._comments\n        names = self._names\n\n        if i >= n:\n            return 0, i\n\n        i0 = i\n        num_fields = 1\n\n        if get_names:\n            if self.is_whitespace_delimiter():\n                while i < n and (_isspace(p[i]) or self.is_comment(p[i])):\n                    i += 1\n            else:\n                if self.is_comment(p[i]):\n                    i += 1\n        else:\n            i = self.skip_comments(i)\n            i0 = i\n\n        last_field_start = i\n\n        while i < n:\n            c = p[i]\n            if c == byte(_NEWLINE) or c == byte(_CARTRIDGE) or self.is_comment(\n                    c):\n                if get_names:\n                    name = str(p + last_field_start, i - last_field_start)\n                    names.append(name.strip())\n\n                i += 1\n                if c == byte(_CARTRIDGE) and i < n and p[i] == byte(_NEWLINE):\n                    i += 1\n                break\n\n            if self.is_delimiter(c):\n                if get_names:\n                    name = str(p + last_field_start, i - last_field_start)\n                    names.append(name.strip())\n\n                i = self.skip_delimiter(i)\n\n                # trailing whitespace case\n                if (self.is_whitespace_delimiter() and\n                    (i >= n or p[i] == byte(_NEWLINE)\n                     or p[i] == byte(_CARTRIDGE) or self.is_comment(p[i]))):\n                    continue\n\n                num_fields += 1\n                last_field_start = i\n            else:\n                i += 1\n\n        return num_fields, i if get_names else i0\n\n    def partition_line(self, line: str, delimiter, num_fields: int, row: int,\n                       invalid_raise: bool):\n        if isinstance(delimiter, int):\n            n = 0\n            i = 0\n\n            while i < len(line) and n < num_fields:\n                yield line[i:i + delimiter]\n                i += delimiter\n                n += 1\n\n            if n < num_fields:\n                if invalid_raise:\n                    malformed(row, num_fields)\n\n                while True:\n                    yield ''\n                    n += 1\n                    if n >= num_fields:\n                        break\n        else:\n            n = 0\n            i = 0\n\n            while i < len(line) and n < len(delimiter):\n                d = delimiter[n]\n                if not isinstance(d, int):\n                    compile_error(\n                        \"'delimiter' must be an int or a sequence of ints\")\n                yield line[i:i + d]\n                i += d\n                n += 1\n\n            if n < num_fields:\n                if invalid_raise:\n                    malformed(row, num_fields)\n\n                while True:\n                    yield ''\n                    n += 1\n                    if n >= num_fields:\n                        break\n\n    def get_num_fields_spaced(self, i: int, get_names: bool, delimiter):\n        p = self._mmap_ptr\n        n = self._length\n        comments = self._comments\n        names = self._names\n\n        if i >= n:\n            return 0, i\n\n        i0 = i\n        if self.is_comment(p[0]):\n            i += 1\n        line_start = i\n        line = ''\n\n        while i < n:\n            c = p[i]\n            if c == byte(_NEWLINE) or c == byte(_CARTRIDGE) or self.is_comment(\n                    c):\n                line = str(p + line_start, i - line_start)\n\n                i += 1\n                if c == byte(_CARTRIDGE) and i < n and p[i] == byte(_NEWLINE):\n                    i += 1\n                break\n            else:\n                i += 1\n\n        num_fields = 0\n        if isinstance(delimiter, int):\n            num_fields = len(line) // delimiter + (1 if len(line) %\n                                                   delimiter else 0)\n        else:\n            num_fields = len(delimiter)\n\n        if get_names:\n            for name in self.partition_line(line,\n                                            delimiter=delimiter,\n                                            num_fields=num_fields,\n                                            row=0,\n                                            invalid_raise=False):\n                names.append(name)\n\n        return num_fields, i if get_names else i0\n\n    def get_num_fields_single(self, line: str, get_names: bool):\n        p = line.ptr\n        n = len(line)\n        names = self._names\n\n        i = 0\n        if self.is_whitespace_delimiter():\n            if get_names:\n                while i < n and (_isspace(p[i]) or self.is_comment(p[i])):\n                    i += 1\n            else:\n                if n > 0 and _isspace(p[0]):\n                    i = self.skip_delimiter(i, line)\n        else:\n            if n > 0 and self.is_comment(p[0]):\n                i += 1\n\n        last_field_start = i\n        num_fields = 0\n\n        while i < n:\n            c = p[i]\n            if c == byte(_NEWLINE) or c == byte(_CARTRIDGE) or self.is_comment(\n                    c):\n                if get_names:\n                    name = str(p + last_field_start, i - last_field_start)\n                    names.append(name.strip())\n\n                num_fields += 1\n                break\n            elif self.is_delimiter(c):\n                if get_names:\n                    name = str(p + last_field_start, i - last_field_start)\n                    names.append(name.strip())\n\n                i = self.skip_delimiter(i, line)\n                num_fields += 1\n\n                # trailing whitespace case\n                if (self.is_whitespace_delimiter() and\n                    (i >= n or p[i] == byte(_NEWLINE)\n                     or p[i] == byte(_CARTRIDGE) or self.is_comment(p[i]))):\n                    break\n\n                last_field_start = i\n            else:\n                i += 1\n                # handle case when there's no trailing newline\n                if i >= n:\n                    num_fields += 1\n\n        return num_fields\n\n    def get_num_fields_single_spaced(self, line: str, get_names: bool,\n                                     delimiter):\n        p = line.ptr\n        n = len(line)\n        names = self._names\n\n        i = 0\n        if n > 0 and self.is_comment(p[0]):\n            i += 1\n        line_start = i\n\n        while i < n:\n            c = p[i]\n            if c == byte(_NEWLINE) or c == byte(_CARTRIDGE) or self.is_comment(\n                    c):\n                line = str(p + line_start, i - line_start)\n                break\n            else:\n                i += 1\n\n        if isinstance(delimiter, int):\n            num_fields = len(line) // delimiter + (1 if len(line) %\n                                                   delimiter else 0)\n        else:\n            num_fields = len(delimiter)\n\n        if get_names:\n            for name in self.partition_line(line,\n                                            delimiter=delimiter,\n                                            num_fields=num_fields,\n                                            row=0,\n                                            invalid_raise=False):\n                names.append(name)\n\n        return num_fields\n\n    def translate_cols(self, usecols, num_fields: int):\n        def translate_one(self, c, num_fields: int):\n            if isinstance(c, int):\n                return normalize_col(c, num_fields)\n            elif isinstance(c, str):\n                return self._names.index(c)\n            else:\n                compile_error(\"'usecols' elements must be either int or str\")\n\n        cols = tuple(translate_one(self, c, num_fields) for c in usecols)\n        for i in range(1, len(cols)):\n            for j in range(i):\n                if cols[i] == cols[j]:\n                    raise ValueError(\n                        f\"duplicate column {cols[i]} given in 'usecols'\")\n\n        return cols\n\n    def parse(self, i: int, row_callback, num_fields: int, maxrows: int,\n              invalid_raise: bool):\n        p = self._mmap_ptr\n        n = self._length\n\n        if n == 0 or num_fields == 0 or maxrows == 0:\n            return\n\n        field = 0\n        fields = Ptr[str](num_fields)\n        row = 0\n        i = self.skip_comments(i)\n        last_field_start = i\n\n        while i < n:\n            c = p[i]\n            if (c == byte(_NEWLINE) or c == byte(_CARTRIDGE)):\n                ok = True\n                if field != num_fields - 1:\n                    if invalid_raise:\n                        malformed(row, num_fields)\n                    else:\n                        ok = False\n\n                if ok:\n                    fields[field] = str(p + last_field_start,\n                                        i - last_field_start)\n                    row_callback(fields, num_fields, row)\n\n                i += 1\n                if c == byte(_CARTRIDGE) and i < n and p[i] == byte(_NEWLINE):\n                    i += 1\n\n                i = self.skip_comments(i)\n                last_field_start = i\n                field = 0\n                row += 1\n                if maxrows >= 0 and row >= maxrows:\n                    break\n            elif self.is_delimiter(c):\n                if invalid_raise or field < num_fields:\n                    fields[field] = str(p + last_field_start,\n                                        i - last_field_start)\n\n                i = self.skip_delimiter(i)\n\n                # trailing whitespace case\n                if (self.is_whitespace_delimiter() and\n                    (i >= n or p[i] == byte(_NEWLINE)\n                     or p[i] == byte(_CARTRIDGE) or self.is_comment(p[i]))):\n                    continue\n\n                field += 1\n\n                ok = True\n                if field >= num_fields:\n                    if invalid_raise:\n                        malformed(row, num_fields)\n                    else:\n                        ok = False\n\n                last_field_start = i\n            elif self.is_comment(c):\n                ok = True\n                if field != num_fields - 1:\n                    if invalid_raise:\n                        malformed(row, num_fields)\n                    else:\n                        ok = False\n\n                if ok:\n                    fields[field] = str(p + last_field_start,\n                                        i - last_field_start)\n                    row_callback(fields, num_fields, row)\n\n                i = self.skip_comments(i)\n                last_field_start = i\n                field = 0\n                row += 1\n                if maxrows >= 0 and row >= maxrows:\n                    break\n            else:\n                i += 1\n\n        if field > 0:\n            ok = True\n            if field != num_fields - 1:\n                if invalid_raise:\n                    malformed(row, num_fields)\n                else:\n                    ok = False\n\n            if ok and (maxrows < 0 or row <= maxrows):\n                fields[field] = str(p + last_field_start, i - last_field_start)\n                row_callback(fields, num_fields, row)\n\n    def parse_spaced(self, i: int, row_callback, num_fields: int, maxrows: int,\n                     invalid_raise: bool, delimiter):\n        p = self._mmap_ptr\n        n = self._length\n\n        if n == 0 or num_fields == 0 or maxrows == 0:\n            return\n\n        fields = Ptr[str](num_fields)\n        row = 0\n        last_line_start = i\n\n        while i < n:\n            c = p[i]\n            if (c == byte(_NEWLINE) or c == byte(_CARTRIDGE)):\n                line = str(p + last_line_start, i - last_line_start)\n                k = 0\n                for f in self.partition_line(line,\n                                             delimiter=delimiter,\n                                             num_fields=num_fields,\n                                             row=row,\n                                             invalid_raise=invalid_raise):\n                    fields[k] = f\n                    k += 1\n\n                row_callback(fields, num_fields, row)\n\n                i += 1\n                if c == byte(_CARTRIDGE) and i < n and p[i] == byte(_NEWLINE):\n                    i += 1\n\n                i = self.skip_comments_no_whitespace(i)\n                last_line_start = i\n                row += 1\n                if maxrows >= 0 and row >= maxrows:\n                    break\n            elif self.is_comment(c):\n                line = str(p + last_line_start, i - last_line_start)\n                k = 0\n                for f in self.partition_line(line,\n                                             delimiter=delimiter,\n                                             num_fields=num_fields,\n                                             row=row,\n                                             invalid_raise=invalid_raise):\n                    fields[k] = f\n                    k += 1\n\n                row_callback(fields, num_fields, row)\n                i = self.skip_comments_no_whitespace(i)\n                last_line_start = i\n                row += 1\n                if maxrows >= 0 and row >= maxrows:\n                    break\n            else:\n                i += 1\n\n        if last_line_start < i and (maxrows < 0 or row < maxrows):\n            line = str(p + last_line_start, i - last_line_start)\n            k = 0\n            for f in self.partition_line(line,\n                                         delimiter=delimiter,\n                                         num_fields=num_fields,\n                                         row=row,\n                                         invalid_raise=invalid_raise):\n                fields[k] = f\n                k += 1\n\n            row_callback(fields, num_fields, row)\n\n    def parse_single(self, line: str, row_callback, row: int, fields: Ptr[str],\n                     num_fields: int, invalid_raise: bool):\n        if not line:\n            return False\n\n        p = line.ptr\n        n = len(line)\n        i = 0\n        last_field_start = 0\n        field = 0\n\n        if n > 0 and self.is_comment(p[0]):\n            return False\n\n        # skip leading whitespace if required\n        if self.is_whitespace_delimiter() and i < n and self.is_delimiter(\n                p[i]):\n            i = self.skip_delimiter(i, line)\n\n        while i < n:\n            c = p[i]\n            if c == byte(_NEWLINE) or c == byte(_CARTRIDGE) or self.is_comment(\n                    c):\n                break\n            elif self.is_delimiter(c):\n                if field >= num_fields:\n                    if invalid_raise:\n                        malformed(row, num_fields)\n                    else:\n                        return False\n\n                fields[field] = str(p + last_field_start, i - last_field_start)\n                field += 1\n                i = self.skip_delimiter(i, line)\n                last_field_start = i\n            else:\n                i += 1\n\n        if i > last_field_start:\n            if field >= num_fields:\n                if invalid_raise:\n                    malformed(row, num_fields)\n                else:\n                    return False\n\n            fields[field] = str(p + last_field_start, i - last_field_start)\n            field += 1\n\n        if field != num_fields:\n            if invalid_raise:\n                malformed(row, num_fields)\n            else:\n                return False\n\n        row_callback(fields, num_fields, row)\n        return True\n\n    def parse_single_spaced(self, line: str, row_callback, row: int,\n                            fields: Ptr[str], num_fields: int,\n                            invalid_raise: bool, delimiter):\n        if not line:\n            return False\n\n        p = line.ptr\n        n = len(line)\n        i = 0\n\n        if n > 0 and self.is_comment(p[0]):\n            return False\n\n        while i < n:\n            c = p[i]\n            if c == byte(_NEWLINE) or c == byte(_CARTRIDGE) or self.is_comment(\n                    c):\n                line = line[:i]\n                break\n            else:\n                i += 1\n\n        k = 0\n        for f in self.partition_line(line,\n                                     delimiter=delimiter,\n                                     num_fields=num_fields,\n                                     row=row,\n                                     invalid_raise=invalid_raise):\n            fields[k] = f\n            k += 1\n\n        row_callback(fields, num_fields, row)\n        return True\n\nclass ArrayUpdateGen:\n    arr: A\n    cols: C\n    conv: F\n    filling_values: M\n    autostrip: bool\n    loose: bool\n    last_row: int\n    A: type\n    C: type\n    F: type\n    M: type\n\n    def __init__(self, arr: A, cols: C, conv: F, filling_values: M,\n                 autostrip: bool, loose: bool):\n        self.arr = arr\n        self.cols = cols\n        self.conv = conv\n        self.filling_values = filling_values\n        self.autostrip = autostrip\n        self.loose = loose\n        self.last_row = 0\n\n    @property\n    def cap(self):\n        arr = self.arr\n        if isinstance(arr, Tuple):\n            return arr[0].size\n        else:\n            return arr.shape[0]\n\n    def resize_arrays(self, new_cap: int):\n        def resize_one(a, new_cap: int):\n            if a.ndim == 1:\n                data_new = util.realloc(a.data, new_cap, a.size)\n                return ndarray((new_cap, ), data_new)\n            else:\n                rows, cols = a.shape\n                data_new = util.realloc(a.data, new_cap * cols, a.size)\n                return ndarray((new_cap, cols), data_new)\n\n        arr = self.arr\n        if isinstance(arr, Tuple):\n            self.arr = tuple(resize_one(a, new_cap) for a in arr)\n        else:\n            self.arr = resize_one(arr, new_cap)\n\n    def trim_arrays(self):\n        if self.last_row < self.cap - 1:\n            self.resize_arrays(self.last_row + 1)\n\n    def fill_value(self, idx: int, dtype: type) -> dtype:\n        filling_values = self.filling_values\n        if filling_values is None:\n            return default_fill(dtype)\n        elif (isinstance(filling_values, List)\n              or isinstance(filling_values, Tuple)\n              or isinstance(filling_values, ndarray)):\n            return filling_values[idx]\n        elif isinstance(filling_values, Dict):\n            default = default_fill(dtype)\n            if filling_values.K is int:\n                return filling_values.get(idx, default)\n            elif filling_values.K is str:\n                return filling_values.get(self._names[idx], default)\n            else:\n                compile_error(\"'filling_values' keys must be int or str\")\n        else:\n            return filling_values\n\n    def fill_value_static(self, idx: Literal[int], dtype: type) -> dtype:\n        filling_values = self.filling_values\n        if filling_values is None:\n            return default_fill(dtype)\n        elif (isinstance(filling_values, List)\n              or isinstance(filling_values, Tuple)\n              or isinstance(filling_values, ndarray)):\n            return filling_values[idx]\n        elif isinstance(filling_values, Dict):\n            default = default_fill(dtype)\n            if filling_values.K is int:\n                return filling_values.get(idx, default)\n            elif filling_values.K is str:\n                return filling_values.get(self._names[idx], default)\n            else:\n                compile_error(\"'filling_values' keys must be int or str\")\n        else:\n            return filling_values\n\n    def convert(self, field: str, idx: int, dtype: type) -> dtype:\n        field0 = field\n        conv = self.conv\n\n        if self.autostrip:\n            field = field.strip()\n\n        if not field and conv is None:\n            r = self.fill_value(idx, dtype)\n        else:\n            try:\n                if conv is None:\n                    r = dtype(field)\n                elif isinstance(conv, Converters):\n                    r = conv(field, idx)\n                elif hasattr(conv, '__call__'):\n                    r = conv(field)\n                else:\n                    r = conv[idx](field)\n            except:\n                if not self.loose:\n                    raise ValueError(f\"Cannot convert string '{field0}'\")\n                r = self.fill_value(idx, dtype)\n\n        # Make sure we copy strings out of the mmap buffer\n        if isinstance(r, str):\n            return r.__ptrcopy__()\n        else:\n            return r\n\n    def convert_static(self, field: str, idx: Literal[int],\n                       dtype: type) -> dtype:\n        field0 = field\n        conv = self.conv\n\n        if self.autostrip:\n            field = field.strip()\n\n        if not field and conv is None:\n            r = self.fill_value_static(idx, dtype)\n        else:\n            try:\n                if conv is None:\n                    r = dtype(field)\n                elif isinstance(conv, Converters):\n                    r = conv(field, idx)\n                elif hasattr(conv, '__call__'):\n                    r = conv(field)\n                else:\n                    r = conv[idx](field)\n            except:\n                if not self.loose:\n                    raise ValueError(f\"Cannot convert string '{field0}'\")\n                r = self.fill_value_static(idx, dtype)\n\n        # Make sure we copy strings out of the mmap buffer\n        if isinstance(r, str):\n            return r.__ptrcopy__()\n        else:\n            return r\n\n    def __call__(self, fields: Ptr[str], num_fields: int, row: int):\n        def get_new_size(size: int, min_grow: int = 512):\n            new_size = size\n            growth = size >> 2\n            if growth <= min_grow:\n                new_size += min_grow\n            else:\n                new_size += growth + min_grow - 1\n                new_size &= ~min_grow\n            return new_size\n\n        arr = self.arr\n        cols = self.cols\n        cap = self.cap\n\n        if row >= cap:\n            new_cap = get_new_size(cap)\n            self.resize_arrays(new_cap)\n\n        # Lots of different cases to consider...\n        if cols is not None:\n            if isinstance(arr, Tuple):\n                for i in static.range(static.len(cols)):\n                    col = cols[i]\n                    arr[i].data[row] = self.convert_static(\n                        fields[col], i, arr[i].dtype)\n            else:\n                if isinstance(arr.dtype, Tuple):\n                    dummy = util.zero(arr.dtype)\n                    tup = tuple(\n                        self.convert(fields[cols[i]], i, type(dummy[i]))\n                        for i in static.range(static.len(arr.dtype)))\n                    arr._ptr((row, ))[0] = tup\n                else:\n                    if arr.ndim == 1:\n                        col = cols[0]\n                        arr.data[row] = self.convert_static(\n                            fields[col], 0, arr.dtype)\n                    else:\n                        for i in static.range(static.len(cols)):\n                            col = cols[i]\n                            arr._ptr((row, i))[0] = self.convert_static(\n                                fields[col], i, arr.dtype)\n        else:\n            if isinstance(arr, Tuple):\n                for i in static.range(static.len(arr)):\n                    arr[i]._ptr((row, ))[0] = self.convert_static(\n                        fields[i], i, arr[i].dtype)\n            elif isinstance(arr.dtype, Tuple):\n                dummy = util.zero(arr.dtype)\n                tup = tuple(\n                    self.convert(fields[i], i, type(dummy[i]))\n                    for i in static.range(static.len(arr.dtype)))\n                arr._ptr((row, ))[0] = tup\n            else:\n                for i in range(num_fields):\n                    arr._ptr(\n                        (row, i))[0] = self.convert(fields[i].strip(), i,\n                                                    arr.dtype)\n\n        self.last_row = row\n\ndef genfromtxt(fname,\n               dtype: type = float,\n               comments: Optional[str] = '#',\n               delimiter=None,\n               skip_header: int = 0,\n               skip_footer: int = 0,\n               converters=None,\n               filling_values=None,\n               usecols=None,\n               names=None,\n               excludelist=None,\n               deletechars: str = \" !#$%&'()*+, -./:;<=>?@[\\\\]^{|}~\",\n               replace_space: str = '_',\n               autostrip: bool = False,\n               case_sensitive=True,\n               unpack: Literal[bool] = False,\n               loose: bool = True,\n               invalid_raise: bool = True,\n               max_rows: Optional[int] = None,\n               ndmin: Literal[int] = 0):\n    def make_callback(csv,\n                      cols,\n                      converters,\n                      filling_values,\n                      loose: bool,\n                      autostrip: bool,\n                      num_fields: int,\n                      block_size: int,\n                      dtype: type,\n                      unpack: Literal[bool] = False):\n        if cols is None:\n            if isinstance(dtype, Tuple):\n                if static.len(dtype) != num_fields:\n                    raise ValueError(\n                        \"number of fields is different than given tuple 'dtype' length\"\n                    )\n                if unpack:\n                    dummy = util.zero(dtype)\n                    arr = tuple(\n                        empty((block_size, ), type(dummy[i]))\n                        for i in static.range(static.len(dtype)))\n                else:\n                    arr = empty((block_size, ), dtype)\n            else:\n                arr = empty((block_size, num_fields), dtype)\n            xcols = cols\n        else:\n            if isinstance(dtype, Tuple):\n                if static.len(dtype) != static.len(cols):\n                    compile_error(\n                        \"'usecols' has different length than given tuple 'dtype'\"\n                    )\n\n            xcols = csv.translate_cols(cols, num_fields)\n\n            if static.len(xcols) == 1:\n                arr = empty((block_size, ), dtype)\n            else:\n                if unpack:\n                    if isinstance(dtype, Tuple):\n                        dummy = util.zero(dtype)\n                        ncols: Literal[int] = static.len(xcols)\n                        arr = tuple(\n                            empty((block_size, ), type(dummy[i]))\n                            for i in static.range(ncols))\n                    else:\n                        arr = tuple(\n                            empty((block_size, ), dtype) for _ in xcols)\n                else:\n                    if isinstance(dtype, Tuple):\n                        arr = empty((block_size, ), dtype)\n                    else:\n                        arr = empty((block_size, len(xcols)), dtype)\n\n        converters = make_conv(converters, num_fields, xcols, dtype)\n        callback = ArrayUpdateGen(arr,\n                                  cols=xcols,\n                                  conv=converters,\n                                  filling_values=filling_values,\n                                  autostrip=autostrip,\n                                  loose=loose)\n        return callback\n\n    def update_names(csv, excludelist, case_sensitive, replace_space: str,\n                     deletechars: str):\n        if csv._names:\n            ex = ['return', 'file', 'print']\n            if excludelist is not None:\n                ex.extend(excludelist)\n\n            upper = False\n            lower = False\n            BAD_CASE_SENSITIVE: Literal[str] = \"'case_sensitive' must be True, False, 'upper' or 'lower'\"\n\n            if isinstance(case_sensitive, bool):\n                if not case_sensitive:\n                    upper = True\n            elif isinstance(case_sensitive, str):\n                if case_sensitive == 'upper':\n                    upper = True\n                elif case_sensitive == 'lower':\n                    lower = True\n                else:\n                    raise ValueError(BAD_CASE_SENSITIVE)\n            else:\n                compile_error(BAD_CASE_SENSITIVE)\n\n            csv.fix_names(excludelist=ex,\n                          deletechars=deletechars,\n                          replace_space=replace_space,\n                          upper=upper,\n                          lower=lower)\n\n    if isinstance(fname, str):\n        if fname.endswith('.gz'):\n            from gzip import open as gz_open\n            with gz_open(fname, 'rb') as f:\n                return genfromtxt(f,\n                                  dtype=dtype,\n                                  comments=comments,\n                                  delimiter=delimiter,\n                                  skip_header=skip_header,\n                                  skip_footer=skip_footer,\n                                  converters=converters,\n                                  filling_values=filling_values,\n                                  usecols=usecols,\n                                  names=names,\n                                  excludelist=excludelist,\n                                  deletechars=deletechars,\n                                  replace_space=replace_space,\n                                  autostrip=autostrip,\n                                  case_sensitive=case_sensitive,\n                                  unpack=unpack,\n                                  loose=loose,\n                                  invalid_raise=invalid_raise,\n                                  max_rows=max_rows,\n                                  ndmin=ndmin)\n        elif fname.endswith('.bz2'):\n            from bz2 import open as bz_open\n            with bz_open(fname, 'r') as f:\n                return genfromtxt(f,\n                                  dtype=dtype,\n                                  comments=comments,\n                                  delimiter=delimiter,\n                                  skip_header=skip_header,\n                                  skip_footer=skip_footer,\n                                  converters=converters,\n                                  filling_values=filling_values,\n                                  usecols=usecols,\n                                  names=names,\n                                  excludelist=excludelist,\n                                  deletechars=deletechars,\n                                  replace_space=replace_space,\n                                  autostrip=autostrip,\n                                  case_sensitive=case_sensitive,\n                                  unpack=unpack,\n                                  loose=loose,\n                                  invalid_raise=invalid_raise,\n                                  max_rows=max_rows,\n                                  ndmin=ndmin)\n\n    if max_rows is not None:\n        if skip_footer:\n            raise ValueError(\n                \"The keywords 'skip_footer' and 'max_rows' can not be \"\n                \"specified at the same time.\")\n        if max_rows < 1:\n            raise ValueError(\"'max_rows' must be at least 1.\")\n\n    if ndmin != 0 and ndmin != 1 and ndmin != 2:\n        compile_error(\"'ndmin' must be 0, 1 or 2\")\n\n    if delimiter is None:\n        dm = ''\n        dx = 0\n        spaced = False\n    elif isinstance(delimiter, str):\n        dm = delimiter\n        dx = 0\n        spaced = False\n    else:\n        dm = ''\n        dx = delimiter\n        spaced = True\n\n    if isinstance(usecols, int) or isinstance(usecols, str):\n        cols = (usecols, )\n    else:\n        cols = usecols\n        if cols is not None:\n            if static.len(cols) == 0:\n                compile_error(\"cannot pass empty tuple to 'usecols'\")\n\n    if ndmin != 0 and ndmin != 1 and ndmin != 2:\n        compile_error(\"'ndmin' must be 0, 1 or 2\")\n\n    maxrows: int = max_rows if max_rows is not None else -1\n    block_size = maxrows if maxrows >= 0 else _DEFAULT_ROWS\n\n    if names is None:\n        get_names = False\n        given_names = None\n    elif isinstance(names, bool):\n        get_names = names\n        given_names = None\n    elif isinstance(names, str):\n        get_names = False\n        given_names = names.split(',')\n        for i in range(names):\n            names[i] = names[i].strip()\n    elif isinstance(names, List[str]):\n        get_names = False\n        given_names = names\n    else:\n        get_names = False\n        given_names = [a for a in names]\n\n    if not isinstance(fname, str):\n        # line-by-line mode\n        if skip_footer > 0:\n            raise ValueError(\n                \"'skip_footer' not supported in line-by-line mode\")\n\n        csv = CSVReaderGen('',\n                           delimiter=dm,\n                           comments=comments if comments is not None else '')\n        k = 0\n        row = 0\n        first = True\n        num_fields = 0\n        fields = Ptr[str]()\n        callback = None\n\n        for line in fname:\n            if k < skip_header or csv.is_comment_or_blank_line(line):\n                k += 1\n                continue\n\n            if maxrows >= 0 and row >= maxrows:\n                break\n\n            if first:\n                if spaced:\n                    num_fields = csv.get_num_fields_single_spaced(\n                        line, get_names=get_names, delimiter=dx)\n                else:\n                    num_fields = csv.get_num_fields_single(line,\n                                                           get_names=get_names)\n\n                update_names(csv,\n                             excludelist=excludelist,\n                             case_sensitive=case_sensitive,\n                             replace_space=replace_space,\n                             deletechars=deletechars)\n                callback = make_callback(csv=csv,\n                                         cols=cols,\n                                         converters=converters,\n                                         filling_values=filling_values,\n                                         loose=loose,\n                                         autostrip=autostrip,\n                                         num_fields=num_fields,\n                                         block_size=block_size,\n                                         dtype=dtype,\n                                         unpack=unpack)\n\n                fields = Ptr[str](num_fields)\n                first = False\n\n                if not get_names:\n                    if spaced:\n                        row += int(\n                            csv.parse_single_spaced(line, callback, row,\n                                                    fields, num_fields,\n                                                    invalid_raise, dx))\n                    else:\n                        row += int(\n                            csv.parse_single(line, callback, row, fields,\n                                             num_fields, invalid_raise))\n            else:\n                if spaced:\n                    row += int(\n                        csv.parse_single_spaced(line, callback, row, fields,\n                                                num_fields, invalid_raise, dx))\n                else:\n                    row += int(\n                        csv.parse_single(line, callback, row, fields,\n                                         num_fields, invalid_raise))\n\n            k += 1\n\n        if callback is None:\n            raise ValueError(\"empty input\")\n\n        callback.trim_arrays()\n        arr = callback.arr\n\n        if isinstance(arr, ndarray):\n            if unpack:\n                return min_dim(arr, ndmin).T\n            else:\n                return min_dim(arr, ndmin)\n        else:\n            return arr\n    else:\n        with CSVReaderGen(\n                fname,\n                delimiter=dm,\n                comments=comments if comments is not None else '') as csv:\n            if given_names is not None:\n                csv._names = given_names\n\n            csv.find_length(skip_footer)\n            i = 0\n            i = csv.skip_lines(i, skip_header)\n            i = csv.skip_comments(i)\n\n            if spaced:\n                num_fields, i = csv.get_num_fields_spaced(i,\n                                                          get_names=get_names,\n                                                          delimiter=dx)\n            else:\n                num_fields, i = csv.get_num_fields(i, get_names=get_names)\n\n            update_names(csv,\n                         excludelist=excludelist,\n                         case_sensitive=case_sensitive,\n                         replace_space=replace_space,\n                         deletechars=deletechars)\n            callback = make_callback(csv=csv,\n                                     cols=cols,\n                                     converters=converters,\n                                     filling_values=filling_values,\n                                     loose=loose,\n                                     autostrip=autostrip,\n                                     num_fields=num_fields,\n                                     block_size=block_size,\n                                     dtype=dtype,\n                                     unpack=unpack)\n\n            if spaced:\n                csv.parse_spaced(i,\n                                 callback,\n                                 num_fields=num_fields,\n                                 maxrows=maxrows,\n                                 invalid_raise=invalid_raise,\n                                 delimiter=dx)\n            else:\n                csv.parse(i,\n                          callback,\n                          num_fields=num_fields,\n                          maxrows=maxrows,\n                          invalid_raise=invalid_raise)\n\n            callback.trim_arrays()\n            arr = callback.arr\n\n            if isinstance(arr, ndarray):\n                if unpack:\n                    return min_dim(arr, ndmin).T\n                else:\n                    return min_dim(arr, ndmin)\n            else:\n                return arr\n\n##########\n# Pickle #\n##########\n\n@extend\nclass ndarray:\n    def __pickle__(self, jar: Jar):\n        from pickle import _write_raw\n        atomic = util.atomic(self.dtype)\n        cc, fc = self._contig\n        forder = (fc and not cc)\n\n        int(forder).__pickle__(jar)\n        for s in self.shape:\n            s.__pickle__(jar)\n\n        if atomic and (fc or cc):\n            _write_raw(jar, self.data.as_byte(), self.nbytes)\n        else:\n            for idx in util.multirange(self.shape):\n                e = self._ptr(idx)[0]\n                e.__pickle__(jar)\n\n    def __unpickle__(jar: Jar):\n        from pickle import _read_raw\n        atomic = util.atomic(dtype)\n        forder = bool(int.__unpickle__(jar))\n        shape = tuple(int.__unpickle__(jar) for _ in static.range(ndim))\n        n = util.count(shape)\n        p = Ptr[dtype](n)\n\n        if atomic:\n            _read_raw(jar, p.as_byte(), n * util.sizeof(dtype))\n        else:\n            for i in range(n):\n                p[i] = dtype.__unpickle__(jar)\n\n        return ndarray(shape, p, fcontig=forder)\n"
  },
  {
    "path": "stdlib/numpy/operators.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport operator\nimport util\n\nfrom .ndarray import ndarray\nfrom .routines import asarray\n\n@tuple\nclass _OpWrap:\n    op: F\n    F: type\n\n    def __call__(self, a, b):\n        A = type(a)\n        B = type(b)\n        c1, c2 = util.op_types(A, B)\n        C1 = type(c1)\n        C2 = type(c2)\n        return self.op(util.cast(a, C1), util.cast(b, C2))\n\ndef _fix_scalar(x, A: type):\n    X = type(x)\n\n    a_is_int: Literal[bool] = (A is int or A is byte or isinstance(A, Int)\n                               or isinstance(A, UInt))\n    x_is_int: Literal[bool] = X is int\n\n    a_is_float: Literal[bool] = (A is float or A is float32 or A is float16\n                                 or A is bfloat16 or A is float128)\n    x_is_float: Literal[bool] = X is float\n\n    a_is_complex: Literal[bool] = (A is complex or A is complex64)\n    x_is_complex: Literal[bool] = X is complex\n\n    should_cast: Literal[bool] = ((x_is_int and\n                                   (a_is_int or a_is_float or a_is_complex)) or\n                                  (x_is_float and (a_is_float or a_is_complex)) or\n                                  (x_is_complex and a_is_complex))\n\n    if (A is float16 or A is float32) and X is complex:\n        return util.cast(x, complex64)\n    elif should_cast:\n        return util.cast(x, A)\n    else:\n        return x\n\n@inline\ndef _binop_impl(arr: ndarray, other, op):\n    op = _OpWrap(op)\n\n    if isinstance(other, ndarray):\n        return arr._op_elemwise(other, op)\n\n    if  (isinstance(other, bool) or isinstance(other, int) or\n         isinstance(other, float) or isinstance(other, complex)):\n        return arr._op_scalar(_fix_scalar(other, arr.dtype), op)\n\n    return arr._op_elemwise(asarray(other), op)\n\n@inline\ndef _ibinop_impl(arr: ndarray, other, op):\n    op = _OpWrap(op)\n\n    if isinstance(other, ndarray):\n        return arr._iop_elemwise(other, op)\n\n    if  (isinstance(other, bool) or isinstance(other, int) or\n         isinstance(other, float) or isinstance(other, complex)):\n        return arr._iop_scalar(_fix_scalar(other, arr.dtype), op)\n\n    return arr._iop_elemwise(asarray(other), op)\n\n@inline\ndef _rbinop_impl(arr: ndarray, other, op):\n    op = _OpWrap(op)\n\n    if isinstance(other, ndarray):\n        return arr._rop_elemwise(other, op)\n\n    if  (isinstance(other, bool) or isinstance(other, int) or\n         isinstance(other, float) or isinstance(other, complex)):\n        return arr._rop_scalar(_fix_scalar(other, arr.dtype), op)\n\n    return arr._rop_elemwise(asarray(other), op)\n\ndef _floor_divide(x, y):\n    X = type(x)\n    Y = type(y)\n    if isinstance(X, Int) and isinstance(Y, Int):\n        return util.pydiv(x, y)\n    else:\n        return x // y\n\ndef _remainder(x, y):\n    X = type(x)\n    Y = type(y)\n    if isinstance(X, Int) and isinstance(Y, Int):\n        return util.pymod(x, y)\n    elif ((X is float and Y is float) or\n          (X is float32 and Y is float32) or\n          (X is float16 and Y is float16)):\n        return util.pyfmod(x, y)\n    else:\n        return x % y\n\n@extend\nclass ndarray:\n\n    @inline\n    def __add__(self, other):\n        return _binop_impl(self, other, operator.add)\n\n    @inline\n    def __radd__(self, other):\n        return _rbinop_impl(self, other, operator.add)\n\n    @inline\n    def __iadd__(self, other):\n        return _ibinop_impl(self, other, operator.add)\n\n    @inline\n    def __sub__(self, other):\n        return _binop_impl(self, other, operator.sub)\n\n    @inline\n    def __rsub__(self, other):\n        return _rbinop_impl(self, other, operator.sub)\n\n    @inline\n    def __isub__(self, other):\n        return _ibinop_impl(self, other, operator.sub)\n\n    @inline\n    def __mul__(self, other):\n        return _binop_impl(self, other, operator.mul)\n\n    @inline\n    def __rmul__(self, other):\n        return _rbinop_impl(self, other, operator.mul)\n\n    @inline\n    def __imul__(self, other):\n        return _ibinop_impl(self, other, operator.mul)\n\n    @inline\n    def __mod__(self, other):\n        return _binop_impl(self, other, _remainder)\n\n    @inline\n    def __rmod__(self, other):\n        return _rbinop_impl(self, other, _remainder)\n\n    @inline\n    def __imod__(self, other):\n        return _ibinop_impl(self, other, _remainder)\n\n    @inline\n    def __pow__(self, other):\n        return _binop_impl(self, other, operator.pow)\n\n    @inline\n    def __rpow__(self, other):\n        return _rbinop_impl(self, other, operator.pow)\n\n    @inline\n    def __ipow__(self, other):\n        return _ibinop_impl(self, other, operator.pow)\n\n    @inline\n    def __truediv__(self, other):\n        return _binop_impl(self, other, operator.truediv)\n\n    @inline\n    def __rtruediv__(self, other):\n        return _rbinop_impl(self, other, operator.truediv)\n\n    @inline\n    def __itruediv__(self, other):\n        return _ibinop_impl(self, other, operator.truediv)\n\n    @inline\n    def __floordiv__(self, other):\n        return _binop_impl(self, other, _floor_divide)\n\n    @inline\n    def __rfloordiv__(self, other):\n        return _rbinop_impl(self, other, _floor_divide)\n\n    @inline\n    def __ifloordiv__(self, other):\n        return _ibinop_impl(self, other, _floor_divide)\n\n    @inline\n    def __lshift__(self, other):\n        return _binop_impl(self, other, operator.lshift)\n\n    @inline\n    def __rlshift__(self, other):\n        return _rbinop_impl(self, other, operator.lshift)\n\n    @inline\n    def __ilshift__(self, other):\n        return _ibinop_impl(self, other, operator.lshift)\n\n    @inline\n    def __rshift__(self, other):\n        return _binop_impl(self, other, operator.rshift)\n\n    @inline\n    def __rrshift__(self, other):\n        return _rbinop_impl(self, other, operator.rshift)\n\n    @inline\n    def __irshift__(self, other):\n        return _ibinop_impl(self, other, operator.rshift)\n\n    @inline\n    def __and__(self, other):\n        return _binop_impl(self, other, operator.and_)\n\n    @inline\n    def __rand__(self, other):\n        return _rbinop_impl(self, other, operator.and_)\n\n    @inline\n    def __iand__(self, other):\n        return _ibinop_impl(self, other, operator.and_)\n\n    @inline\n    def __or__(self, other):\n        return _binop_impl(self, other, operator.or_)\n\n    @inline\n    def __ror__(self, other):\n        return _rbinop_impl(self, other, operator.or_)\n\n    @inline\n    def __ior__(self, other):\n        return _ibinop_impl(self, other, operator.or_)\n\n    @inline\n    def __xor__(self, other):\n        return _binop_impl(self, other, operator.xor)\n\n    @inline\n    def __rxor__(self, other):\n        return _rbinop_impl(self, other, operator.xor)\n\n    @inline\n    def __ixor__(self, other):\n        return _ibinop_impl(self, other, operator.xor)\n\n    @inline\n    def __pos__(self):\n        return self._op_unary(operator.pos)\n\n    @inline\n    def __neg__(self):\n        return self._op_unary(operator.neg)\n\n    @inline\n    def __invert__(self):\n        return self._op_unary(operator.invert)\n\n    @inline\n    def __abs__(self):\n        return self._op_unary(operator.abs)\n\n    @inline\n    def __eq__(self, other):\n        return _binop_impl(self, other, operator.eq)\n\n    @inline\n    def __ne__(self, other):\n        return _binop_impl(self, other, operator.ne)\n\n    @inline\n    def __lt__(self, other):\n        return _binop_impl(self, other, operator.lt)\n\n    @inline\n    def __le__(self, other):\n        return _binop_impl(self, other, operator.le)\n\n    @inline\n    def __gt__(self, other):\n        return _binop_impl(self, other, operator.gt)\n\n    @inline\n    def __ge__(self, other):\n        return _binop_impl(self, other, operator.ge)\n"
  },
  {
    "path": "stdlib/numpy/pybridge.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .ndarray import ndarray\nfrom .npdatetime import datetime64, timedelta64\n\nimport util\nimport internal.static as static\n\n# https://github.com/numpy/numpy/blob/main/numpy/_core/include/numpy/ndarraytypes.h\nNPY_BOOL: Literal[int] = 0\nNPY_BYTE: Literal[int] = 1\nNPY_UBYTE: Literal[int] = 2\nNPY_SHORT: Literal[int] = 3\nNPY_USHORT: Literal[int] = 4\nNPY_INT: Literal[int] = 5\nNPY_UINT: Literal[int] = 6\nNPY_LONG: Literal[int] = 7\nNPY_ULONG: Literal[int] = 8\nNPY_LONGLONG: Literal[int] = 9\nNPY_ULONGLONG: Literal[int] = 10\nNPY_FLOAT: Literal[int] = 11\nNPY_DOUBLE: Literal[int] = 12\nNPY_LONGDOUBLE: Literal[int] = 13\nNPY_CFLOAT: Literal[int] = 14\nNPY_CDOUBLE: Literal[int] = 15\nNPY_CLONGDOUBLE: Literal[int] = 16\nNPY_OBJECT: Literal[int] = 17\nNPY_STRING: Literal[int] = 18\nNPY_UNICODE: Literal[int] = 19\nNPY_VOID: Literal[int] = 20\nNPY_DATETIME: Literal[int] = 21\nNPY_TIMEDELTA: Literal[int] = 22\nNPY_HALF: Literal[int] = 23\nNPY_NTYPES: Literal[int] = 24\nNPY_NOTYPE: Literal[int] = 25\nNPY_CHAR: Literal[int] = 26\nNPY_STRING: Literal[int] = 18\nNPY_USERDEF: Literal[int] = 256\nNPY_NTYPES_ABI_COMPATIBLE: Literal[int] = 21\n\ndef _type_code(dtype: type):\n    if dtype is bool:\n        return NPY_BOOL\n\n    if dtype is i8:\n        return NPY_BYTE\n\n    if dtype is byte or dtype is u8:\n        return NPY_UBYTE\n\n    if dtype is i16:\n        return NPY_SHORT\n\n    if dtype is u16:\n        return NPY_USHORT\n\n    if dtype is i32:\n        return NPY_INT\n\n    if dtype is u32:\n        return NPY_UINT\n\n    if dtype is int or dtype is i64:\n        return NPY_LONG\n\n    if dtype is u64:\n        return NPY_ULONG\n\n    if dtype is float16:\n        return NPY_HALF\n\n    if dtype is float32:\n        return NPY_FLOAT\n\n    if dtype is float:\n        return NPY_DOUBLE\n\n    if dtype is complex64:\n        return NPY_CFLOAT\n\n    if dtype is complex:\n        return NPY_CDOUBLE\n\n    if isinstance(dtype, datetime64):\n        return NPY_DATETIME\n\n    if isinstance(dtype, timedelta64):\n        return NPY_TIMEDELTA\n\n    # TODO: add other types like string etc.\n    #       once we have them.\n\n    return NPY_OBJECT\n\n# https://github.com/numpy/numpy/blob/main/numpy/_core/include/numpy/ndarraytypes.h\n@tuple\nclass PyObject:\n    refcnt: int\n    typptr: cobj\n\n@tuple\nclass NpyAuxData:\n    free_func: cobj\n    clone_func: cobj\n    data1: cobj\n    data2: cobj\n\n@tuple\nclass PyArray_DatetimeMetaData:\n    base: i32\n    num: i32\n\n@tuple\nclass PyArray_DatetimeDTypeMetaData:\n    base: NpyAuxData\n    meta: PyArray_DatetimeMetaData\n\n@tuple\nclass PyArrayDescr:\n    head: PyObject\n    typeobj: cobj\n    kind: u8\n    type: u8\n    byteorder: u8\n    former_flags: u8\n    type_num: i32\n    flags: u64\n    elsize: int\n    alignment: int\n    metadata: cobj\n    hash: int\n    reserved0: cobj\n    reserved1: cobj\n    subarray: cobj\n    fields: cobj\n    names: cobj\n    c_metadata: cobj\n\n@tuple\nclass PyArrayObject:\n    head: PyObject\n    data: cobj\n    nd: i32\n    dimensions: Ptr[int]\n    strides: Ptr[int]\n    base: cobj\n    descr: Ptr[PyArrayDescr]\n    flags: i32\n    weakreflist: cobj\n\nNPY_ARRAY_C_CONTIGUOUS: Literal[int] = 1\nNPY_ARRAY_F_CONTIGUOUS: Literal[int] = 2\n\nPyArray_Type = cobj()\nPyArray_New = Function[[cobj, i32, cobj, i32, cobj, cobj, i32, i32, cobj], cobj](cobj())\nPyArray_NewFromDescr = Function[[cobj, cobj, i32, cobj, cobj, cobj, i32, cobj], cobj](cobj())\nPyArray_DescrNewFromType = Function[[i32], cobj](cobj())\n\ndef _pyobj_type(p: cobj):\n    return Ptr[PyObject](p)[0].typptr\n\ndef _setup_numpy_bridge():\n    import python\n    from internal.python import PyImport_ImportModule, PyObject_GetAttrString, PyCapsule_Type, PyCapsule_GetPointer\n    global PyArray_Type, PyArray_New, PyArray_NewFromDescr, PyArray_DescrNewFromType\n\n    module = PyImport_ImportModule(\"numpy._core._multiarray_umath\".ptr)\n\n    if not module:\n        raise RuntimeError(\"Failed to import 'numpy.core._multiarray_umath'\")\n\n    attr = PyObject_GetAttrString(module, \"_ARRAY_API\".ptr)\n\n    if not attr or _pyobj_type(attr) != PyCapsule_Type:\n        raise RuntimeError(\"NumPy API object not found or did not have type 'capsule'\")\n\n    api = Ptr[cobj](PyCapsule_GetPointer(attr, cobj()))\n    # https://github.com/numpy/numpy/blob/main/numpy/_core/code_generators/numpy_api.py\n    PyArray_Type = api[2]\n    PyArray_New = Function[[cobj, i32, cobj, i32, cobj, cobj, i32, i32, cobj], cobj](api[93])\n    PyArray_NewFromDescr = Function[[cobj, cobj, i32, cobj, cobj, cobj, i32, cobj], cobj](api[94])\n    PyArray_DescrNewFromType = Function[[i32], cobj](api[96])\n\n_setup_numpy_bridge()\n\n@extend\nclass ndarray:\n    def __to_py__(self):\n        dims = self.shape\n        code = _type_code(self.dtype)\n\n        if isinstance(self.dtype, datetime64) or isinstance(self.dtype, timedelta64):\n            descr = Ptr[PyArrayDescr](PyArray_DescrNewFromType(i32(code)))\n            p = Ptr[PyArray_DatetimeDTypeMetaData](descr[0].c_metadata)\n            meta = PyArray_DatetimeMetaData(i32(self.dtype._code()), i32(self.dtype.num))\n            p[0] = PyArray_DatetimeDTypeMetaData(p[0].base, meta)\n            arr = PyArray_NewFromDescr(PyArray_Type, descr.as_byte(), i32(self.ndim),\n                                       __ptr__(dims).as_byte(), cobj(), cobj(), i32(0),\n                                       cobj())\n        else:\n            arr = PyArray_New(PyArray_Type, i32(self.ndim), __ptr__(dims).as_byte(),\n                              i32(code), cobj(), cobj(), i32(0), i32(0), cobj())\n\n        arr_ptr = Ptr[PyArrayObject](arr)\n        data = arr_ptr[0].data\n\n        if code == NPY_OBJECT:\n            p = Ptr[cobj](data)\n            k = 0\n            for idx in util.multirange(dims):\n                e = self._ptr(idx)[0]\n                if hasattr(e, \"__to_py__\"):\n                    p[k] = e.__to_py__()\n                k += 1\n        else:\n            cc, _ = self._contig\n            if cc:\n                str.memcpy(data, self.data.as_byte(), self.nbytes)\n            else:\n                p = Ptr[self.dtype](data)\n                k = 0\n                for idx in util.multirange(dims):\n                    e = self._ptr(idx)[0]\n                    p[k] = e\n                    k += 1\n\n        return arr\n\n    def _from_py(a: cobj, copy: bool):\n        if _pyobj_type(a) != PyArray_Type:\n            raise PyError(\"NumPy conversion error: Python object did not have array type\")\n\n        arr = Ptr[PyArrayObject](a)[0]\n\n        if int(arr.nd) != ndim:\n            raise PyError(\"NumPy conversion error: Python array has incorrect dimension\")\n\n        code = _type_code(dtype)\n        if int(arr.descr[0].type_num) != code:\n            raise PyError(\"NumPy conversion error: Python array has incorrect dtype\")\n\n        arr_data = arr.data\n        arr_shape = arr.dimensions\n        arr_strides = arr.strides\n        arr_flags = int(arr.flags)\n        shape = tuple(arr_shape[i] for i in static.range(ndim))\n        strides = tuple(arr_strides[i] for i in static.range(ndim))\n        size = util.count(shape)\n\n        if code != NPY_OBJECT and not copy:\n            return ndarray(shape, strides, Ptr[dtype](arr_data))\n\n        data = Ptr[dtype](size)\n\n        if code == NPY_OBJECT:\n            k = 0\n            for idx in util.multirange(shape):\n                off = 0\n                for i in static.range(ndim):\n                    off += idx[i] * strides[i]\n                e = Ptr[cobj](arr_data + off)[0]\n                if hasattr(dtype, \"__from_py__\"):\n                    data[k] = dtype.__from_py__(e)\n                k += 1\n            return ndarray(shape, data)\n        else:\n            cc = (arr_flags & NPY_ARRAY_C_CONTIGUOUS != 0)\n            fc = (arr_flags & NPY_ARRAY_F_CONTIGUOUS != 0)\n\n            if cc or fc:\n                str.memcpy(data.as_byte(), arr_data, size * util.sizeof(dtype))\n                return ndarray(shape, strides, data)\n            else:\n                k = 0\n                for idx in util.multirange(shape):\n                    off = 0\n                    for i in static.range(ndim):\n                        off += idx[i] * strides[i]\n                    e = Ptr[dtype](arr_data + off)[0]\n                    data[k] = e\n                    k += 1\n                return ndarray(shape, data)\n\n    def __from_py__(a: cobj) -> ndarray[dtype, ndim]:\n        return ndarray[dtype, ndim]._from_py(a, copy=False)\n"
  },
  {
    "path": "stdlib/numpy/random/__init__.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\nfrom .. import *\n\nfrom .pcg64 import PCG64, PCG64DXSM\nfrom .philox import Philox\nfrom .mt19937 import MT19937\nfrom .sfc64 import SFC64\nfrom .bitgen import BitGenerator, Generator\nfrom .seed import SeedSequence\n\ndef default_rng(seed = None):\n    if isinstance(seed, BitGenerator):\n        return Generator(seed)\n\n    if isinstance(seed, Generator):\n        return seed\n\n    return Generator(PCG64(seed))\n\n_global_rng: Generator[MT19937] = Generator(MT19937(None))\n\ndef beta(a, b, size = None):\n    return _global_rng.beta(a, b, size)\n\ndef binomial(n, p, size = None):\n    return _global_rng.binomial(n, p, size)\n\ndef bytes(length: int):\n    return _global_rng.bytes(length)\n\ndef chisquare(df, size = None):\n    return _global_rng.chisquare(df, size)\n\ndef choice(a, size = None, replace: bool = True, p = None):\n    return _global_rng.choice(a, size, replace, p)\n\ndef dirichlet(alpha, size = None):\n    return _global_rng.dirichlet(alpha, size)\n\ndef exponential(scale = 1.0, size = None):\n    return _global_rng.exponential(scale, size)\n\ndef f(dfnum, dfden, size = None):\n    return _global_rng.f(dfnum, dfden, size)\n\ndef gamma(shape, scale = 1.0, size = None):\n    return _global_rng.gamma(shape, scale, size)\n\ndef geometric(p, size = None):\n    return _global_rng.geometric(p, size)\n\ndef get_state(legacy: bool):\n    return _global_rng.bit_generator.state.__get_state__()\n\ndef gumbel(loc = 0.0, scale = 1.0, size = None):\n    return _global_rng.gumbel(loc, scale, size)\n\ndef hypergeometric(ngood, nbad, nsample, size = None):\n    return _global_rng.hypergeometric(ngood, nbad, nsample, size)\n\ndef laplace(loc = 0.0, scale = 1.0, size = None):\n    return _global_rng.laplace(loc, scale, size)\n\ndef logistic(loc = 0.0, scale = 1.0, size = None):\n    return _global_rng.logistic(loc, scale, size)\n\ndef lognormal(mean = 0.0, sigma = 1.0, size = None):\n    return _global_rng.lognormal(mean, sigma, size)\n\ndef logseries(p, size = None):\n    return _global_rng.logseries(p, size)\n\ndef multinomial(n, pvals, size = None):\n    return _global_rng.multinomial(n, pvals, size)\n\ndef multivariate_normal(mean, cov, size = None, check_valid: Literal[str] = 'warn',\n                        tol: float = 1e-8):\n    return _global_rng.multivariate_normal(mean, cov, size, check_valid, tol)\n\ndef negative_binomial(n, p, size = None):\n    return _global_rng.negative_binomial(n, p, size)\n\ndef noncentral_chisquare(df, nonc, size = None):\n    return _global_rng.noncentral_chisquare(df, nonc, size)\n\ndef noncentral_f(dfnum, dfden, nonc, size = None):\n    return _global_rng.noncentral_f(dfnum, dfden, nonc, size)\n\ndef normal(loc = 0.0, scale = 1.0, size = None):\n    return _global_rng.normal(loc, scale, size)\n\ndef pareto(a, size = None):\n    return _global_rng.pareto(a, size)\n\ndef permutation(x):\n    return _global_rng.permutation(x)\n\ndef poisson(lam = 1.0, size = None):\n    return _global_rng.poisson(lam, size)\n\ndef power(a, size = None):\n    return _global_rng.power(a, size)\n\ndef rand(*d):\n    if static.len(d) == 0:\n        return _global_rng.random()\n    else:\n        return _global_rng.random(d)\n\ndef randint(low, high = None, size = None, dtype: type = int):\n    return _global_rng.integers(low, high, size, dtype)\n\ndef randn(*d):\n    if static.len(d) == 0:\n        return _global_rng.standard_normal()\n    else:\n        return _global_rng.standard_normal(d)\n\ndef random(size = None):\n    return _global_rng.random(size)\n\ndef random_integers(low, high = None, size = None):\n    return _global_rng.integers(low, high, size, endpoint=True)\n\ndef random_sample(size = None):\n    return _global_rng.random(size)\n\ndef ranf(size = None):\n    return _global_rng.random(size)\n\ndef rayleigh(scale = 1.0, size = None):\n    return _global_rng.rayleigh(scale, size)\n\ndef sample(size = None):\n    return _global_rng.random(size)\n\ndef seed(seed = None):\n    _global_rng.bit_generator.state.seed_legacy(seed)\n\ndef set_state(state):\n    _global_rng.bit_generator.state.__set_state__(state)\n\ndef shuffle(x):\n    return _global_rng.shuffle(x)\n\ndef standard_cauchy(size = None):\n    return _global_rng.standard_cauchy(size)\n\ndef standard_exponential(size = None):\n    return _global_rng.standard_exponential(size)\n\ndef standard_gamma(shape, size = None):\n    return _global_rng.standard_gamma(size)\n\ndef standard_normal(size = None):\n    return _global_rng.standard_normal(size)\n\ndef standard_t(df, size = None):\n    return _global_rng.standard_t(df, size)\n\ndef triangular(left, mode, right, size = None):\n    return _global_rng.triangular(left, mode, right, size)\n\ndef uniform(low = 0.0, high = 1.0, size = None):\n    return _global_rng.uniform(low, high, size)\n\ndef vonmises(mu, kappa, size = None):\n    return _global_rng.vonmises(mu, kappa, size)\n\ndef wald(mean, scale, size = None):\n    return _global_rng.wald(mean, scale, size)\n\ndef weibull(a, size = None):\n    return _global_rng.weibull(a, size)\n\ndef zipf(a, size = None):\n    return _global_rng.zipf(a, size)\n"
  },
  {
    "path": "stdlib/numpy/random/bitgen.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\nfrom ..routines import *\nfrom ..util import PI, uitofp, zext, itrunc, signbit, isnan, nan64, fabs, cmod, floor, ceil, log, log1p, exp, expm1, pow, sqrt, cos, acos\nfrom .logfactorial import logfactorial\nfrom .seed import SeedSequence\nfrom .ziggurat import *\n\nu128 = UInt[128]\n\nclass Binomial:\n    psave: float\n    nsave: int\n    r: float\n    q: float\n    fm: float\n    m: int\n    p1: float\n    xm: float\n    xl: float\n    xr: float\n    c: float\n    laml: float\n    lamr: float\n    p2: float\n    p3: float\n    p4: float\n\n    def __init__(self):\n        pass\n\nclass BitGenerator[G]:\n    state: G\n    binomial: Optional[Binomial]\n    uinteger: u32\n    has_uint32: bool\n\n    def __init__(self, state: G):\n        self.state = state\n        self.binomial = None\n        self.uinteger = u32(0)\n        self.has_uint32 = False\n\n    @property\n    def seed_seq(self):\n        return self.state.seed\n\n    def next64(self):\n        if hasattr(self.state, \"next64\"):\n            return self.state.next64()\n        else:\n            hi = zext(self.next32(), u64)\n            lo = zext(self.next32(), u64)\n            return (hi << u64(32)) | lo\n\n    def next32(self):\n        if hasattr(self.state, \"next32\"):\n            return self.state.next32()\n        else:\n            if self.has_uint32:\n                self.has_uint32 = False\n                return self.uinteger\n\n            n64 = self.next64()\n            self.has_uint32 = True\n            self.uinteger = itrunc(n64 >> u64(32), u32)\n            return itrunc(n64 & u64(0xffffffff), u32)\n\n    def next_double(self):\n        if hasattr(self.state, \"next_double\"):\n            return self.state.next_double()\n        else:\n            r = self.next64()\n            return uitofp(r >> u64(11), float) * (1.0 / 9007199254740992.0)\n\n    def next_float(self):\n        return uitofp(self.next32() >> u32(8), float32) * (float32(1.0) / float32(16777216.0))\n\n    def jumped(self, jumps: int):\n        if not hasattr(self.state, \"jump_inplace\"):\n            compile_error(\"generator '\" + G.__name__ + \"' is not jumpable\")\n\n        s = self.state.__get_state__()\n        g = G.__new__()\n        g.__set_state__(s)\n        g.jump_inplace(jumps)\n        return BitGenerator(g)\n\n    # distributions\n\n    def standard_uniform(self):\n        return self.next_double()\n\n    def standard_uniform_f(self):\n        return self.next_float()\n\n    def random_standard_uniform_fill(self, cnt: int, out: Ptr[float]):\n        for i in range(cnt):\n            out[i] = self.next_double()\n\n    def random_standard_uniform_fill_f(self, cnt: int, out: Ptr[float32]):\n        for i in range(cnt):\n            out[i] = self.next_float()\n\n    def standard_exponential_unlikely(self, idx: int, x: float):\n        if idx == 0:\n            return ziggurat_exp_r - log1p(-self.next_double())\n        elif (fe_double(idx - 1) - fe_double(idx)) * self.next_double() + fe_double(idx) < exp(-x):\n            return x\n        else:\n            return self.random_standard_exponential()\n\n    def random_standard_exponential(self):\n        ri = self.next64()\n        ri >>= u64(3)\n        idx = int(ri & u64(0xff))\n        ri >>= u64(8)\n        x = uitofp(ri, float) * we_double(idx)\n        if ri < ke_double(idx):\n            return x\n        return self.standard_exponential_unlikely(idx, x)\n\n    def random_standard_exponential_fill(self, cnt: int, out: Ptr[float]):\n        for i in range(cnt):\n            out[i] = self.random_standard_exponential()\n\n    def standard_exponential_unlikely_f(self, idx: int, x: float32):\n        if idx == 0:\n            return ziggurat_exp_r_f - log1p(-self.next_float())\n        elif (fe_float(idx - 1) - fe_float(idx)) * self.next_float() + fe_float(idx) < exp(-x):\n            return x\n        else:\n            return self.random_standard_exponential_f()\n\n    def random_standard_exponential_f(self):\n        ri = self.next32()\n        ri >>= u32(1)\n        idx = int(ri & u32(0xff))\n        ri >>= u32(8)\n        x = uitofp(ri, float32) * we_float(idx)\n        if ri < ke_float(idx):\n            return x\n        return self.standard_exponential_unlikely_f(idx, x)\n\n    def random_standard_exponential_fill_f(self, cnt: int, out: Ptr[float32]):\n        for i in range(cnt):\n            out[i] = self.random_standard_exponential_f()\n\n    def random_standard_exponential_inv_fill(self, cnt: int, out: Ptr[float]):\n        for i in range(cnt):\n            out[i] = -log1p(-self.next_double())\n\n    def random_standard_exponential_inv_fill_f(self, cnt: int, out: Ptr[float32]):\n        for i in range(cnt):\n            out[i] = -log1p(-self.next_float())\n\n    def random_standard_normal(self):\n        while True:\n            r = self.next64()\n            idx = int(r & u64(0xff))\n            r >>= u64(8)\n            sign = r & u64(0x1)\n            rabs = (r >> u64(1)) & u64(0x000fffffffffffff)\n            x = uitofp(rabs, float) * wi_double(idx)\n            if sign & u64(0x1):\n                x = -x\n            if rabs < ki_double(idx):\n                return x\n            if idx == 0:\n                while True:\n                    xx = -ziggurat_nor_inv_r * log1p(-self.next_double())\n                    yy = -log1p(-self.next_double())\n                    if yy + yy > xx * xx:\n                        return -(ziggurat_nor_r + xx) if ((rabs >> u64(8)) & u64(0x1)) else ziggurat_nor_r + xx\n            else:\n                if (((fi_double(idx - 1) - fi_double(idx)) * self.next_double() +\n                        fi_double(idx)) < exp(-0.5 * x * x)):\n                    return x\n\n    def random_standard_normal_fill(self, cnt: int, out: Ptr[float]):\n        for i in range(cnt):\n            out[i] = self.random_standard_normal()\n\n    def random_standard_normal_f(self):\n        while True:\n            r = self.next32()\n            idx = int(r & u32(0xff))\n            sign = (r >> u32(8)) & u32(0x1)\n            rabs = (r >> u32(9)) & u32(0x0007fffff)\n            x = uitofp(rabs, float32) * wi_float(idx)\n            if sign & u32(0x1):\n                x = -x\n            if rabs < ki_float(idx):\n                return x\n            if idx == 0:\n                while True:\n                    xx = -ziggurat_nor_inv_r_f * log1p(-self.next_float())\n                    yy = -log1p(-self.next_float())\n                    if yy + yy > xx * xx:\n                        return -(ziggurat_nor_r_f + xx) if ((rabs >> u32(8)) & u32(0x1)) else ziggurat_nor_r_f + xx\n            else:\n                if (((fi_float(idx - 1) - fi_float(idx)) * self.next_float() +\n                        fi_float(idx)) < exp(float32(-0.5) * x * x)):\n                    return x\n\n    def random_standard_normal_fill_f(self, cnt: int, out: Ptr[float32]):\n        for i in range(cnt):\n            out[i] = self.random_standard_normal_f()\n\n    def random_standard_gamma(self, shape: float):\n        if shape == 1.0:\n            return self.random_standard_exponential()\n        elif shape == 0.0:\n            return 0.0\n        elif shape < 1.0:\n            while True:\n                U = self.next_double()\n                V = self.random_standard_exponential()\n                if U < 1.0 - shape:\n                    X = pow(U, 1. / shape)\n                    if X <= V:\n                        return X\n                else:\n                    Y = -log((1. - U) / shape)\n                    X = pow(1.0 - shape + shape * Y, 1. / shape)\n                    if X <= V + Y:\n                        return X\n        else:\n            b = shape - 1. / 3.\n            c = 1. / sqrt(9 * b)\n            while True:\n                while True:\n                    X = self.random_standard_normal()\n                    V = 1.0 + c * X\n                    if V > 0.0:\n                        break\n\n                V = V * V * V\n                U = self.next_double()\n                if U < 1.0 - 0.0331 * (X * X) * (X * X):\n                    return b * V\n                if log(U) < 0.5 * X * X + b * (1. - V + log(V)):\n                    return b * V\n\n    def random_standard_gamma_f(self, shape: float32):\n        if shape == f32(1.0):\n            return self.random_standard_exponential_f()\n        elif shape == f32(0.0):\n            return f32(0.0)\n        elif shape < f32(1.0):\n            while True:\n                U = self.next_float()\n                V = self.random_standard_exponential_f()\n                if U < f32(1.0) - shape:\n                    X = pow(U, f32(1.) / shape)\n                    if X <= V:\n                        return X\n                else:\n                    Y = -log((f32(1.) - U) / shape)\n                    X = pow(f32(1.0) - shape + shape * Y, f32(1.) / shape)\n                    if X <= V + Y:\n                        return X\n        else:\n            b = shape - f32(1.) / f32(3.)\n            c = f32(1.) / sqrt(f32(9.) * b)\n            while True:\n                while True:\n                    X = self.random_standard_normal_f()\n                    V = f32(1.0) + c * X\n                    if V > f32(0.0):\n                        break\n\n                V = V * V * V\n                U = self.next_float()\n                if U < f32(1.0) - f32(0.0331) * (X * X) * (X * X):\n                    return b * V\n                if log(U) < f32(0.5) * X * X + b * (f32(1.) - V + log(V)):\n                    return b * V\n\n    def random_positive_int64(self):\n        return int(self.next64() >> u64(1))\n\n    def random_positive_int32(self):\n        return i32(self.next32() >> u32(1))\n\n    def random_positive_int(self):\n        return self.random_positive_int64()\n\n    def random_uint(self):\n        return self.next64()\n\n    def random_loggam(x: float):\n        @pure\n        @llvm\n        def a(idx: int) -> float:\n            @data = private unnamed_addr constant [10 x double] [double 0x3FB5555555555555, double 0xBF66C16C16C16C17, double 0x3F4A01A01A01A01A, double 0xBF43813813813813, double 0x3F4B951E2B18FF24, double 0xBF5F6AB0D9993C7F, double 0x3F7A41A41A41A41A, double 0xBF9E4286CB0F5397, double 0x3FC6FE96381E0685, double 0xBFF6476701181F35], align 16\n            %p = getelementptr inbounds [10 x double], ptr @data, i64 0, i64 %idx\n            %x = load double, ptr %p, align 8\n            ret double %x\n\n        n = 0\n        if x == 1.0 or x == 2.0:\n            return 0.0\n        elif x < 7.0:\n            n = int(7 - x)\n        else:\n            n = 0\n        x0 = x + n\n        x2 = (1.0 / x0) * (1.0 / x0)\n        lg2pi = 1.8378770664093453e+00\n        gl0 = a(9)\n        k = 8\n        while k >= 0:\n            gl0 *= x2\n            gl0 += a(k)\n            k -= 1\n        gl = gl0 / x0 + 0.5 * lg2pi + (x0 - 0.5) * log(x0) - x0\n        if x < 7.0:\n            k = 1\n            while k <= n:\n                gl -= log(x0 - 1.0)\n                x0 -= 1.0\n                k += 1\n        return gl\n\n    def random_normal(self, loc: float, scale: float):\n        return loc + scale * self.random_standard_normal()\n\n    def random_exponential(self, scale: float):\n        return scale * self.random_standard_exponential()\n\n    def random_uniform(self, off: float, rng: float):\n        return off + rng * self.next_double()\n\n    def random_gamma(self, shape: float, scale: float):\n        return scale * self.random_standard_gamma(shape)\n\n    def random_gamma_f(self, shape: float32, scale: float32):\n        return scale * self.random_standard_gamma_f(shape)\n\n    def random_beta(self, a: float, b: float):\n        if a <= 1.0 and b <= 1.0:\n            while True:\n                U = self.next_double()\n                V = self.next_double()\n                X = pow(U, 1.0 / a)\n                Y = pow(V, 1.0 / b)\n                XpY = X + Y\n\n                if XpY <= 1.0 and XpY > 0.0:\n                    if X + Y > 0:\n                        return X / XpY\n                    else:\n                        logX = log(U) / a\n                        logY = log(V) / b\n                        logM = logX if logX > logY else logY\n                        logX -= logM\n                        logY -= logM\n\n                        return exp(logX - log(exp(logX) + exp(logY)))\n        else:\n            Ga = self.random_standard_gamma(a)\n            Gb = self.random_standard_gamma(b)\n            return Ga / (Ga + Gb)\n\n    def random_chisquare(self, df: float):\n        return 2.0 * self.random_standard_gamma(df / 2.0)\n\n    def random_f(self, dfnum: float, dfden: float):\n        return (self.random_chisquare(dfnum) * dfden) / (self.random_chisquare(dfden) * dfnum)\n\n    def random_standard_cauchy(self):\n        return self.random_standard_normal() / self.random_standard_normal()\n\n    def random_pareto(self, a: float):\n        return expm1(self.random_standard_exponential() / a)\n\n    def random_weibull(self, a: float):\n        if a == 0.0:\n            return 0.0\n        return pow(self.random_standard_exponential(), 1. / a)\n\n    def random_power(self, a: float):\n        return pow(-expm1(-self.random_standard_exponential()), 1. / a)\n\n    def random_laplace(self, loc: float, scale: float):\n        U = self.next_double()\n        if U >= 0.5:\n            U = loc - scale * log(2.0 - U - U)\n        elif U > 0.0:\n            U = loc + scale * log(U + U)\n        else:\n            U = self.random_laplace(loc, scale)\n        return U\n\n    def random_gumbel(self, loc: float, scale: float):\n        U = 1.0 - self.next_double()\n        if U < 1.0:\n            return loc - scale * log(-log(U))\n        return self.random_gumbel(loc, scale)\n\n    def random_logistic(self, loc: float, scale: float):\n        U = self.next_double()\n        if U > 0.0:\n            return loc + scale * log(U / (1.0 - U))\n        return self.random_logistic(loc, scale)\n\n    def random_lognormal(self, mean: float, sigma: float):\n        return exp(self.random_normal(mean, sigma))\n\n    def random_rayleigh(self, mode: float):\n        return mode * sqrt(2.0 * self.random_standard_exponential())\n\n    def random_standard_t(self, df: float):\n        num = self.random_standard_normal()\n        denom = self.random_standard_gamma(df / 2)\n        return sqrt(df / 2) * num / sqrt(denom)\n\n    def random_poisson_mult(self, lam: float):\n        enlam = exp(-lam)\n        X = 0\n        prod = 1.0\n        while True:\n            U = self.next_double()\n            prod *= U\n            if prod > enlam:\n                X += 1\n            else:\n                return X\n\n    def random_poisson_ptrs(self, lam: float):\n        LS2PI = 0.91893853320467267\n        TWELFTH = 0.083333333333333333333333\n\n        slam = sqrt(lam)\n        loglam = log(lam)\n        b = 0.931 + 2.53 * slam\n        a = -0.059 + 0.02483 * b\n        invalpha = 1.1239 + 1.1328 / (b - 3.4)\n        vr = 0.9277 - 3.6224 / (b - 2)\n\n        while True:\n            U = self.next_double() - 0.5\n            V = self.next_double()\n            us = 0.5 - fabs(U)\n            k = int(floor((2 * a / us + b) * U + lam + 0.43))\n            if us >= 0.07 and V <= vr:\n                return k\n            if k < 0 or (us < 0.013 and V > us):\n                continue\n            if ((log(V) + log(invalpha) - log(a / (us * us) + b)) <=\n                (-lam + k * loglam - BitGenerator.random_loggam(k + 1))):\n                return k\n\n    def random_poisson(self, lam: float):\n        if lam >= 10:\n            return self.random_poisson_ptrs(lam)\n        elif lam == 0:\n            return 0\n        else:\n            return self.random_poisson_mult(lam)\n\n    def random_negative_binomial(self, n: float, p: float):\n        Y = self.random_gamma(n, (1 - p) / p)\n        return self.random_poisson(Y)\n\n    def random_binomial_btpe(self, n: int, p: float):\n        # god help us...\n        r = 0.0\n        q = 0.0\n        fm = 0.0\n        p1 = 0.0\n        xm = 0.0\n        xl = 0.0\n        xr = 0.0\n        c = 0.0\n        laml = 0.0\n        lamr = 0.0\n        p2 = 0.0\n        p3 = 0.0\n        p4 = 0.0\n        m = 0\n\n        binomial = self.binomial\n        if binomial is None or binomial.nsave != n or binomial.psave != p:\n            b = Binomial()\n            b.nsave = n\n            b.psave = p\n\n            r = min(p, 1.0 - p)\n            q = 1.0 - r\n            fm = n * r + r\n            m = int(floor(fm))\n            p1 = floor(2.195 * sqrt(n * r * q) - 4.6 * q) + 0.5\n            xm = m + 0.5\n            xl = xm - p1\n            xr = xm + p1\n            c = 0.134 + 20.5 / (15.3 + m)\n\n            b.r = r\n            b.q = q\n            b.fm = fm\n            b.m = m\n            b.p1 = p1\n            b.xm = xm\n            b.xl = xl\n            b.xr = xr\n            b.c = c\n\n            a = (fm - xl) / (fm - xl * r)\n            laml = a * (1.0 + a / 2.0)\n            b.laml = laml\n            a = (xr - fm) / (xr * q)\n            lamr = a * (1.0 + a / 2.0)\n            b.lamr = lamr\n\n            p2 = p1 * (1.0 + 2.0 * c)\n            p3 = p2 + c / laml\n            p4 = p3 + c / lamr\n\n            b.p2 = p2\n            b.p3 = p3\n            b.p4 = p4\n\n            self.binomial = b\n        else:\n            r = binomial.r\n            q = binomial.q\n            fm = binomial.fm\n            m = binomial.m\n            p1 = binomial.p1\n            xm = binomial.xm\n            xl = binomial.xl\n            xr = binomial.xr\n            c = binomial.c\n            laml = binomial.laml\n            lamr = binomial.lamr\n            p2 = binomial.p2\n            p3 = binomial.p3\n            p4 = binomial.p4\n\n        a = 0.0\n        u = 0.0\n        v = 0.0\n        s = 0.0\n        F = 0.0\n        rho = 0.0\n        t = 0.0\n        A = 0.0\n        nrq = 0.0\n        x1 = 0.0\n        x2 = 0.0\n        f1 = 0.0\n        f2 = 0.0\n        z = 0.0\n        z2 = 0.0\n        w = 0.0\n        w2 = 0.0\n        x = 0.0\n\n        y = 0\n        k = 0\n        i = 0\n\n        def step10(step10, step20, step30, step40,\n                   step50, step52, step60, args, self, n, p):\n            r, q, fm, p1, xm, xl, xr, c, laml, \\\n            lamr, p2, p3, p4, m, a, u, v, s, F, \\\n            rho, t, A, nrq, x1, x2, f1, f2, z, \\\n            z2, w, w2, x, y, k, i = args\n\n            nrq = n * r * q\n            u = self.next_double() * p4\n            v = self.next_double()\n            if u > p1:\n                args = (r, q, fm, p1, xm, xl, xr, c, laml,\n                        lamr, p2, p3, p4, m, a, u, v, s, F,\n                        rho, t, A, nrq, x1, x2, f1, f2, z,\n                        z2, w, w2, x, y, k, i)\n                return step20(step10, step20, step30, step40,\n                              step50, step52, step60, args, self, n, p)\n            y = int(floor(xm - p1 * v + u))\n\n            args = (r, q, fm, p1, xm, xl, xr, c, laml,\n                    lamr, p2, p3, p4, m, a, u, v, s, F,\n                    rho, t, A, nrq, x1, x2, f1, f2, z,\n                    z2, w, w2, x, y, k, i)\n            return step60(step10, step20, step30, step40,\n                          step50, step52, step60, args, self, n, p)\n\n        def step20(step10, step20, step30, step40,\n                   step50, step52, step60, args, self, n, p):\n            r, q, fm, p1, xm, xl, xr, c, laml, \\\n            lamr, p2, p3, p4, m, a, u, v, s, F, \\\n            rho, t, A, nrq, x1, x2, f1, f2, z, \\\n            z2, w, w2, x, y, k, i = args\n\n            if u > p2:\n                return step30(step10, step20, step30, step40,\n                              step50, step52, step60, args, self, n, p)\n            x = xl + (u - p1) / c\n            v = v * c + 1.0 - fabs(m - x + 0.5) / p1\n            if v > 1.0:\n                args = (r, q, fm, p1, xm, xl, xr, c, laml,\n                        lamr, p2, p3, p4, m, a, u, v, s, F,\n                        rho, t, A, nrq, x1, x2, f1, f2, z,\n                        z2, w, w2, x, y, k, i)\n                return step10(step10, step20, step30, step40,\n                              step50, step52, step60, args, self, n, p)\n            y = int(floor(x))\n            args = (r, q, fm, p1, xm, xl, xr, c, laml,\n                    lamr, p2, p3, p4, m, a, u, v, s, F,\n                    rho, t, A, nrq, x1, x2, f1, f2, z,\n                    z2, w, w2, x, y, k, i)\n            return step50(step10, step20, step30, step40,\n                          step50, step52, step60, args, self, n, p)\n\n        def step30(step10, step20, step30, step40,\n                   step50, step52, step60, args, self, n, p):\n            r, q, fm, p1, xm, xl, xr, c, laml, \\\n            lamr, p2, p3, p4, m, a, u, v, s, F, \\\n            rho, t, A, nrq, x1, x2, f1, f2, z, \\\n            z2, w, w2, x, y, k, i = args\n\n            if u > p3:\n                return step40(step10, step20, step30, step40,\n                              step50, step52, step60, args, self, n, p)\n            y = int(floor(xl + log(v) / laml))\n            if y < 0 or v == 0.0:\n                args = (r, q, fm, p1, xm, xl, xr, c, laml,\n                        lamr, p2, p3, p4, m, a, u, v, s, F,\n                        rho, t, A, nrq, x1, x2, f1, f2, z,\n                        z2, w, w2, x, y, k, i)\n                return step10(step10, step20, step30, step40,\n                              step50, step52, step60, args, self, n, p)\n            v = v * (u - p2) * laml\n            args = (r, q, fm, p1, xm, xl, xr, c, laml,\n                    lamr, p2, p3, p4, m, a, u, v, s, F,\n                    rho, t, A, nrq, x1, x2, f1, f2, z,\n                    z2, w, w2, x, y, k, i)\n            return step50(step10, step20, step30, step40,\n                          step50, step52, step60, args, self, n, p)\n\n        def step40(step10, step20, step30, step40,\n                   step50, step52, step60, args, self, n, p):\n            r, q, fm, p1, xm, xl, xr, c, laml, \\\n            lamr, p2, p3, p4, m, a, u, v, s, F, \\\n            rho, t, A, nrq, x1, x2, f1, f2, z, \\\n            z2, w, w2, x, y, k, i = args\n\n            y = int(floor(xr - log(v) / lamr))\n            if y > n or v == 0.0:\n                args = (r, q, fm, p1, xm, xl, xr, c, laml,\n                        lamr, p2, p3, p4, m, a, u, v, s, F,\n                        rho, t, A, nrq, x1, x2, f1, f2, z,\n                        z2, w, w2, x, y, k, i)\n                return step10(step10, step20, step30, step40,\n                              step50, step52, step60, args, self, n, p)\n            v = v * (u - p3) * lamr\n            args = (r, q, fm, p1, xm, xl, xr, c, laml,\n                    lamr, p2, p3, p4, m, a, u, v, s, F,\n                    rho, t, A, nrq, x1, x2, f1, f2, z,\n                    z2, w, w2, x, y, k, i)\n            return step50(step10, step20, step30, step40,\n                          step50, step52, step60, args, self, n, p)\n\n        def step50(step10, step20, step30, step40,\n                   step50, step52, step60, args, self, n, p):\n            r, q, fm, p1, xm, xl, xr, c, laml, \\\n            lamr, p2, p3, p4, m, a, u, v, s, F, \\\n            rho, t, A, nrq, x1, x2, f1, f2, z, \\\n            z2, w, w2, x, y, k, i = args\n\n            k = abs(y - m)\n            if k > 20 and k < (nrq / 2.0 - 1):\n                args = (r, q, fm, p1, xm, xl, xr, c, laml,\n                        lamr, p2, p3, p4, m, a, u, v, s, F,\n                        rho, t, A, nrq, x1, x2, f1, f2, z,\n                        z2, w, w2, x, y, k, i)\n                return step52(step10, step20, step30, step40,\n                              step50, step52, step60, args, self, n, p)\n\n            s = r / q\n            a = s * (n + 1)\n            F = 1.0\n            if m < y:\n                for i in range(m + 1, y + 1):\n                    F *= (a / i - s)\n            elif m > y:\n                for i in range(y + 1, m + 1):\n                    F /= (a / i - s)\n\n            args = (r, q, fm, p1, xm, xl, xr, c, laml,\n                    lamr, p2, p3, p4, m, a, u, v, s, F,\n                    rho, t, A, nrq, x1, x2, f1, f2, z,\n                    z2, w, w2, x, y, k, i)\n            if v > F:\n                return step10(step10, step20, step30, step40,\n                              step50, step52, step60, args, self, n, p)\n            return step60(step10, step20, step30, step40,\n                          step50, step52, step60, args, self, n, p)\n\n        def step52(step10, step20, step30, step40,\n                   step50, step52, step60, args, self, n, p):\n            r, q, fm, p1, xm, xl, xr, c, laml, \\\n            lamr, p2, p3, p4, m, a, u, v, s, F, \\\n            rho, t, A, nrq, x1, x2, f1, f2, z, \\\n            z2, w, w2, x, y, k, i = args\n\n            rho = \\\n                  (k / (nrq)) * ((k * (k / 3.0 + 0.625) + 0.16666666666666666) / nrq + 0.5)\n            t = -k * k / (2 * nrq)\n            A = log(v)\n            if A < t - rho:\n                args = (r, q, fm, p1, xm, xl, xr, c, laml,\n                        lamr, p2, p3, p4, m, a, u, v, s, F,\n                        rho, t, A, nrq, x1, x2, f1, f2, z,\n                        z2, w, w2, x, y, k, i)\n                return step60(step10, step20, step30, step40,\n                              step50, step52, step60, args, self, n, p)\n            if A > t + rho:\n                args = (r, q, fm, p1, xm, xl, xr, c, laml,\n                        lamr, p2, p3, p4, m, a, u, v, s, F,\n                        rho, t, A, nrq, x1, x2, f1, f2, z,\n                        z2, w, w2, x, y, k, i)\n                return step10(step10, step20, step30, step40,\n                              step50, step52, step60, args, self, n, p)\n\n            x1 = y + 1\n            f1 = m + 1\n            z = n + 1 - m\n            w = n - y + 1\n            x2 = x1 * x1\n            f2 = f1 * f1\n            z2 = z * z\n            w2 = w * w\n            args = (r, q, fm, p1, xm, xl, xr, c, laml,\n                    lamr, p2, p3, p4, m, a, u, v, s, F,\n                    rho, t, A, nrq, x1, x2, f1, f2, z,\n                    z2, w, w2, x, y, k, i)\n            if (A > (xm * log(f1 / x1) + (n - m + 0.5) * log(z / w) +\n                    (y - m) * log(w * r / (x1 * q)) +\n                    (13680. - (462. - (132. - (99. - 140. / f2) / f2) / f2) / f2) / f1 /\n                        166320. +\n                    (13680. - (462. - (132. - (99. - 140. / z2) / z2) / z2) / z2) / z /\n                        166320. +\n                    (13680. - (462. - (132. - (99. - 140. / x2) / x2) / x2) / x2) / x1 /\n                        166320. +\n                    (13680. - (462. - (132. - (99. - 140. / w2) / w2) / w2) / w2) / w /\n                        166320.)):\n                return step10(step10, step20, step30, step40,\n                              step50, step52, step60, args, self, n, p)\n            return step60(step10, step20, step30, step40,\n                          step50, step52, step60, args, self, n, p)\n\n        def step60(step10, step20, step30, step40,\n                   step50, step52, step60, args, self, n, p):\n            r, q, fm, p1, xm, xl, xr, c, laml, \\\n            lamr, p2, p3, p4, m, a, u, v, s, F, \\\n            rho, t, A, nrq, x1, x2, f1, f2, z, \\\n            z2, w, w2, x, y, k, i = args\n\n            if p > 0.5:\n                y = n - y\n\n            return y\n\n        args = (r, q, fm, p1, xm, xl, xr, c, laml,\n                lamr, p2, p3, p4, m, a, u, v, s, F,\n                rho, t, A, nrq, x1, x2, f1, f2, z,\n                z2, w, w2, x, y, k, i)\n        return step10(step10, step20, step30, step40,\n                      step50, step52, step60, args, self, n, p)\n\n    def random_binomial_inversion(self, n: int, p: float):\n        q = 0.0\n        qn = 0.0\n        np = 0.0\n        bound = 0\n\n        binomial = self.binomial\n        if binomial is None or binomial.nsave != n or binomial.psave != p:\n            b = Binomial()\n            b.nsave = n\n            b.psave = p\n            q = 1.0 - p\n            qn = exp(n * log(q))\n            np = n * p\n            bound = int(min(float(n), np + 10.0 * sqrt(np * q + 1)))\n            b.q = q\n            b.r = qn\n            b.c = np\n            b.m = bound\n            self.binomial = b\n        else:\n            q = binomial.q\n            qn = binomial.r\n            np = binomial.c\n            bound = binomial.m\n\n        X = 0\n        px = qn\n        U = self.next_double()\n        while U > px:\n            X += 1\n            if X > bound:\n                X = 0\n                px = qn\n                U = self.next_double()\n            else:\n                U -= px\n                px = ((n - X + 1) * p * px) / (X * q)\n        return X\n\n    def random_binomial(self, p: float, n: int):\n        if n == 0 or p == 0.0:\n            return 0\n\n        if p <= 0.5:\n            if p * n <= 30.0:\n                return self.random_binomial_inversion(n, p)\n            else:\n                return self.random_binomial_btpe(n, p)\n        else:\n            q = 1.0 - p\n            if q * n <= 30.0:\n                return n - self.random_binomial_inversion(n, q)\n            else:\n                return n - self.random_binomial_btpe(n, q)\n\n    def random_noncentral_chisquare(self, df: float, nonc: float):\n        if isnan(nonc):\n            return nan64()\n        if nonc == 0:\n            return self.random_chisquare(df)\n        if 1 < df:\n            Chi2 = self.random_chisquare(df - 1)\n            n = self.random_standard_normal() + sqrt(nonc)\n            return Chi2 + n * n\n        else:\n            i = self.random_poisson(nonc / 2.0)\n            return self.random_chisquare(df + 2 * i)\n\n    def random_noncentral_f(self, dfnum: float, dfden: float, nonc: float):\n        t = self.random_noncentral_chisquare(dfnum, nonc) * dfden\n        return t / (self.random_chisquare(dfden) * dfnum)\n\n    def random_wald(self, mean: float, scale: float):\n        mu_2l = mean / (2 * scale)\n        Y = self.random_standard_normal()\n        Y = mean * Y * Y\n        X = mean + mu_2l * (Y - sqrt(4 * scale * Y + Y * Y))\n        U = self.next_double()\n        if U <= mean / (mean + X):\n            return X\n        else:\n            return mean * mean / X\n\n    def random_vonmises(self, mu: float, kappa: float):\n        if isnan(kappa):\n            return nan64()\n\n        s = 0.0\n        if kappa < 1e-8:\n            return PI * (2 * self.next_double() - 1)\n        else:\n            if kappa < 1e-5:\n                s = 1. / kappa + kappa\n            else:\n                if kappa < 1e6:\n                    r = 1 + sqrt(1 + 4 * kappa * kappa)\n                    rho = (r - sqrt(2 * r)) / (2 * kappa)\n                    s = (1 + rho * rho) / (2 * rho)\n                else:\n                    result = mu + sqrt(1. / kappa) + self.random_standard_normal()\n                    if result < -PI:\n                        result += 2*PI\n                    if result > PI:\n                        result -= 2*PI\n                    return result\n\n            while True:\n                U = self.next_double()\n                Z = cos(PI * U)\n                W = (1 + s * Z) / (s + Z)\n                Y = kappa * (s - W)\n                V = self.next_double()\n\n                if (Y * (2 - Y) - V >= 0) or (log(Y / V) + 1 - Y >= 0):\n                    break\n\n            U = self.next_double()\n\n            result = acos(W)\n            if U < 0.5:\n                result = -result\n            result += mu\n            neg = (result < 0)\n            mod = fabs(result)\n            mod = cmod(mod + PI, 2 * PI) - PI\n            if neg:\n                mod *= -1\n\n            return mod\n\n    def random_logseries(self, p: float):\n        r = log1p(-p)\n\n        while True:\n            V = self.next_double()\n            if V >= p:\n                return 1\n            U = self.next_double()\n            q = -expm1(r * U)\n            if V <= q * q:\n                result = int(floor(1 + log(V) / log(q)))\n                if result < 1 or V == 0.0:\n                    continue\n                else:\n                    return result\n            if V >= q:\n                return 1\n            return 2\n\n    def random_geometric_search(self, p: float):\n        X = 1\n        s = p\n        prod = p\n        q = 1.0 - p\n        U = self.next_double()\n        while U > s:\n            prod *= q\n            s += prod\n            X += 1\n        return X\n\n    def random_geometric_inversion(self, p: float):\n        z = ceil(-self.random_standard_exponential() / log1p(-p))\n        if z >= 9.223372036854776e+18:\n            return 0x7FFFFFFFFFFFFFFF\n        return int(z)\n\n    def random_geometric(self, p: float):\n        if p >= 0.333333333333333333333333:\n            return self.random_geometric_search(p)\n        else:\n            return self.random_geometric_inversion(p)\n\n    def random_zipf(self, a: float):\n        am1 = a - 1.0\n        b = pow(2.0, am1)\n        while True:\n            U = 1.0 - self.next_double()\n            V = self.next_double()\n            X = floor(pow(U, -1.0 / am1))\n\n            if X > 0x7FFFFFFFFFFFFFFF or X < 1.0:\n                continue\n\n            T = pow(1.0 + 1.0 / X, am1)\n            if V * X * (T - 1.0) / (b - 1.0) <= T / b:\n                return int(X)\n\n    def random_triangular(self, left: float, mode: float, right: float):\n        base = right - left\n        leftbase = mode - left\n        ratio = leftbase / base\n        leftprod = leftbase * base\n        rightprod = (right - mode) * base\n\n        U = self.next_double()\n        if U <= ratio:\n            return left + sqrt(U * leftprod)\n        else:\n            return right - sqrt((1.0 - U) * rightprod)\n\n    def random_interval(self, max: u64):\n        if max == u64(0):\n            return u64(0)\n\n        mask = max\n        value = u64(0)\n\n        mask |= mask >> u64(1)\n        mask |= mask >> u64(2)\n        mask |= mask >> u64(4)\n        mask |= mask >> u64(8)\n        mask |= mask >> u64(16)\n        mask |= mask >> u64(32)\n\n        if max <= u64(0xffffffff):\n            while True:\n                value = zext(self.next32(), u64) & mask\n                if value <= max:\n                    break\n        else:\n            while True:\n                value = self.next64() & mask\n                if value <= max:\n                    break\n\n        return value\n\n    def gen_mask(max: u64):\n        mask = max\n        mask |= mask >> u64(1)\n        mask |= mask >> u64(2)\n        mask |= mask >> u64(4)\n        mask |= mask >> u64(8)\n        mask |= mask >> u64(16)\n        mask |= mask >> u64(32)\n        return mask\n\n    def buffered_uint16(self, bcnt: int, buf: u32):\n        if not bcnt:\n            buf = self.next32()\n            bcnt = 1\n        else:\n            buf >>= u32(16)\n            bcnt -= 1\n        return itrunc(buf, u16), bcnt, buf\n\n    def buffered_uint8(self, bcnt: int, buf: u32):\n        if not bcnt:\n            buf = self.next32()\n            bcnt = 3\n        else:\n            buf >>= u32(8)\n            bcnt -= 1\n        return itrunc(buf, u8), bcnt, buf\n\n    def bounded_masked_uint64(self, rng: u64, mask: u64):\n        val = u64(0)\n        while True:\n            val = self.next64() & mask\n            if val <= rng:\n                break\n        return val\n\n    def buffered_bounded_masked_uint32(self, rng: u32, mask: u32, bcnt: int, buf: u32):\n        val = u32(0)\n        while True:\n            val = self.next32() & mask\n            if val <= rng:\n                break\n        return val, bcnt, buf\n\n    def buffered_bounded_masked_uint16(self, rng: u16, mask: u16, bcnt: int, buf: u32):\n        val = u16(0)\n        while True:\n            val, bcnt, buf = self.buffered_uint16(bcnt, buf)\n            val &= mask\n            if val <= rng:\n                break\n        return val, bcnt, buf\n\n    def buffered_bounded_masked_uint8(self, rng: u8, mask: u8, bcnt: int, buf: u32):\n        val = u8(0)\n        while True:\n            val, bcnt, buf = self.buffered_uint8(bcnt, buf)\n            val &= mask\n            if val <= rng:\n                break\n        return val, bcnt, buf\n\n    def buffered_bounded_bool(self, off: bool, rng: bool, mask: bool, bcnt: int, buf: u32):\n        if not rng:\n            return off, bcnt, buf\n\n        if not bcnt:\n            buf = self.next32()\n            bcnt = 31\n        else:\n            buf >>= u32(1)\n            bcnt -= 1\n\n        return bool(buf & u32(1)), bcnt, buf\n\n    def bounded_lemire_uint64(self, rng: u64):\n        rng_excl = rng + u64(1)\n\n        m = zext(self.next64(), u128) * zext(rng_excl, u128)\n        leftover = itrunc(m & u128(0xFFFFFFFFFFFFFFFF), u64)\n\n        if leftover < rng_excl:\n            threshold = (u64(0xFFFFFFFFFFFFFFFF) - rng) % rng_excl\n\n            while leftover < threshold:\n                m = zext(self.next64(), u128) * zext(rng_excl, u128)\n                leftover = itrunc(m & u128(0xFFFFFFFFFFFFFFFF), u64)\n\n        return itrunc(m >> u128(64), u64)\n\n    def buffered_bounded_lemire_uint32(self, rng: u32, bcnt: int, buf: u32):\n        rng_excl = rng + u32(1)\n\n        m = zext(self.next32(), u64) * zext(rng_excl, u64)\n        leftover = itrunc(m & u64(0xFFFFFFFF), u32)\n\n        if leftover < rng_excl:\n            threshold = (u32(0xFFFFFFFF) - rng) % rng_excl\n\n            while leftover < threshold:\n                m = zext(self.next32(), u64) * zext(rng_excl, u64)\n                leftover = itrunc(m & u64(0xFFFFFFFF), u32)\n\n        return itrunc(m >> u64(32), u32), bcnt, buf\n\n    def buffered_bounded_lemire_uint16(self, rng: u16, bcnt: int, buf: u32):\n        rng_excl = rng + u16(1)\n\n        val, bcnt, buf = self.buffered_uint16(bcnt, buf)\n        m = zext(val, u32) * zext(rng_excl, u32)\n        leftover = itrunc(m & u32(0xFFFF), u16)\n\n        if leftover < rng_excl:\n            threshold = (u16(0xFFFF) - rng) % rng_excl\n\n            while leftover < threshold:\n                val, bcnt, buf = self.buffered_uint16(bcnt, buf)\n                m = zext(val, u32) * zext(rng_excl, u32)\n                leftover = itrunc(m & u32(0xFFFF), u16)\n\n        return itrunc(m >> u32(16), u16), bcnt, buf\n\n    def buffered_bounded_lemire_uint8(self, rng: u8, bcnt: int, buf: u32):\n        rng_excl = rng + u8(1)\n\n        val, bcnt, buf = self.buffered_uint8(bcnt, buf)\n        m = zext(val, u16) * zext(rng_excl, u16)\n        leftover = itrunc(m & u16(0xFF), u8)\n\n        if leftover < rng_excl:\n            threshold = (u8(0xFF) - rng) % rng_excl\n\n            while leftover < threshold:\n                val, bcnt, buf = self.buffered_uint8(bcnt, buf)\n                m = zext(val, u16) * zext(rng_excl, u16)\n                leftover = itrunc(m & u16(0xFF), u8)\n\n        return itrunc(m >> u16(8), u8), bcnt, buf\n\n    def random_bounded_uint64(self, off: u64, rng: u64, mask: u64, use_masked: bool):\n        if rng == u64(0):\n            return off\n        elif rng <= u64(0xFFFFFFFF):\n            if rng == u64(0xFFFFFFFF):\n                return off + zext(self.next32(), u64)\n\n            if use_masked:\n                return off + zext(self.buffered_bounded_masked_uint32(itrunc(rng, u32), itrunc(mask, u32), 0, u32(0))[0], u64)\n            else:\n                return off + zext(self.buffered_bounded_lemire_uint32(itrunc(rng, u32), 0, u32(0))[0], u64)\n        elif rng == u64(0xFFFFFFFFFFFFFFFF):\n            return off + self.next64()\n        else:\n            if use_masked:\n                return off + self.bounded_masked_uint64(rng, mask)\n            else:\n                return off + self.bounded_lemire_uint64(rng)\n\n    def random_buffered_bounded_uint32(self, off: u32, rng: u32, mask: u32, use_masked: bool, bcnt: int, buf: u32):\n        if rng == u32(0):\n            return off, bcnt, buf\n        elif rng == u32(0xFFFFFFFF):\n            return off + self.next32(), bcnt, buf\n        else:\n            if use_masked:\n                val, bcnt, buf = self.buffered_bounded_masked_uint32(rng, mask, bcnt, buf)\n                return off + val, bcnt, buf\n            else:\n                val, bcnt, buf = self.buffered_bounded_lemire_uint32(rng, bcnt, buf)\n                return off + val, bcnt, buf\n\n    def random_buffered_bounded_uint16(self, off: u16, rng: u16, mask: u16, use_masked: bool, bcnt: int, buf: u32):\n        if rng == u16(0):\n            return off, bcnt, buf\n        elif rng == u16(0xFFFF):\n            val, bcnt, buf = self.buffered_uint16(bcnt, buf)\n            return off + val, bcnt, buf\n        else:\n            if use_masked:\n                val, bcnt, buf = self.buffered_bounded_masked_uint16(rng, mask, bcnt, buf)\n                return off + val, bcnt, buf\n            else:\n                val, bcnt, buf = self.buffered_bounded_lemire_uint16(rng, bcnt, buf)\n                return off + val, bcnt, buf\n\n    def random_buffered_bounded_uint8(self, off: u8, rng: u8, mask: u8, use_masked: bool, bcnt: int, buf: u32):\n        if rng == u8(0):\n            return off, bcnt, buf\n        elif rng == u8(0xFF):\n            val, bcnt, buf = self.buffered_uint8(bcnt, buf)\n            return off + val, bcnt, buf\n        else:\n            if use_masked:\n                val, bcnt, buf = self.buffered_bounded_masked_uint8(rng, mask, bcnt, buf)\n                return off + val, bcnt, buf\n            else:\n                val, bcnt, buf = self.buffered_bounded_lemire_uint8(rng, bcnt, buf)\n                return off + val, bcnt, buf\n\n    def random_buffered_bounded_bool(self, off: bool, rng: bool, mask: bool, use_masked_bool, bcnt: int, buf: u32):\n        return self.buffered_bounded_bool(off, rng, mask, bcnt, buf)\n\n    def random_bounded_uint64_fill(self, off: u64, rng: u64, cnt: int, use_masked: bool, out: Ptr[u64]):\n        if rng == u64(0):\n            for i in range(cnt):\n                out[i] = off\n        elif rng <= u64(0xFFFFFFFF):\n            if rng == u64(0xFFFFFFFF):\n                for i in range(cnt):\n                    out[i] = off + zext(self.next32(), u64)\n            else:\n                rng32 = util.itrunc(rng, u32)\n                buf = u32(0)\n                bcnt = 0\n\n                if use_masked:\n                    mask = itrunc(BitGenerator.gen_mask(rng), u32)\n\n                    for i in range(cnt):\n                        val, bcnt, buf = self.buffered_bounded_masked_uint32(rng32, mask, bcnt, buf)\n                        out[i] = off + zext(val, u64)\n                else:\n                    for i in range(cnt):\n                        val, bcnt, buf = self.buffered_bounded_lemire_uint32(rng32, bcnt, buf)\n                        out[i] = off + zext(val, u64)\n        elif rng == u64(0xFFFFFFFFFFFFFFFF):\n            for i in range(cnt):\n                out[i] = off + self.next64()\n        else:\n            if use_masked:\n                mask = BitGenerator.gen_mask(rng)\n\n                for i in range(cnt):\n                    out[i] = off + self.bounded_masked_uint64(rng, mask)\n            else:\n                for i in range(cnt):\n                    out[i] = off + self.bounded_lemire_uint64(rng)\n\n    def random_bounded_uint32_fill(self, off: u32, rng: u32, cnt: int, use_masked: bool, out: Ptr[u32]):\n        buf = u32(0)\n        bcnt = 0\n\n        if rng == u32(0):\n            for i in range(cnt):\n                out[i] = off\n        elif rng == u32(0xFFFFFFFF):\n            for i in range(cnt):\n                out[i] = off + self.next32()\n        else:\n            if use_masked:\n                mask = itrunc(BitGenerator.gen_mask(zext(rng, u64)), u32)\n\n                for i in range(cnt):\n                    val, bcnt, buf = self.buffered_bounded_masked_uint32(rng, mask, bcnt, buf)\n                    out[i] = off + val\n            else:\n                for i in range(cnt):\n                    val, bcnt, buf = self.buffered_bounded_lemire_uint32(rng, bcnt, buf)\n                    out[i] = off + val\n\n    def random_bounded_uint16_fill(self, off: u16, rng: u16, cnt: int, use_masked: bool, out: Ptr[u16]):\n        buf = u32(0)\n        bcnt = 0\n\n        if rng == u16(0):\n            for i in range(cnt):\n                out[i] = off\n        elif rng == u16(0xFFFF):\n            for i in range(cnt):\n                val, bcnt, buf = self.buffered_uint16(bcnt, buf)\n                out[i] = off + val\n        else:\n            if use_masked:\n                mask = itrunc(BitGenerator.gen_mask(zext(rng, u64)), u16)\n\n                for i in range(cnt):\n                    val, bcnt, buf = self.buffered_bounded_masked_uint16(rng, mask, bcnt, buf)\n                    out[i] = off + val\n            else:\n                for i in range(cnt):\n                    val, bcnt, buf = self.buffered_bounded_lemire_uint16(rng, bcnt, buf)\n                    out[i] = off + val\n\n    def random_bounded_uint8_fill(self, off: u8, rng: u8, cnt: int, use_masked: bool, out: Ptr[u8]):\n        buf = u32(0)\n        bcnt = 0\n\n        if rng == u8(0):\n            for i in range(cnt):\n                out[i] = off\n        elif rng == u8(0xFF):\n            for i in range(cnt):\n                val, bcnt, buf = self.buffered_uint8(bcnt, buf)\n                out[i] = off + val\n        else:\n            if use_masked:\n                mask = itrunc(BitGenerator.gen_mask(zext(rng, u64)), u8)\n\n                for i in range(cnt):\n                    val, bcnt, buf = self.buffered_bounded_masked_uint8(rng, mask, bcnt, buf)\n                    out[i] = off + val\n            else:\n                for i in range(cnt):\n                    val, bcnt, buf = self.buffered_bounded_lemire_uint8(rng, bcnt, buf)\n                    out[i] = off + val\n\n    def random_bounded_bool_fill(self, off: bool, rng: bool, cnt: int, use_masked: bool, out: Ptr[bool]):\n        buf = u32(0)\n        bcnt = 0\n        mask = False\n\n        for i in range(cnt):\n            val, bcnt, buf = self.buffered_bounded_bool(off, rng, mask, bcnt, buf)\n            out[i] = val\n\n    def random_multinomial(self, n: int, mnix: Ptr[int], pix: Ptr[float], d: int):\n        remaining_p = 1.0\n        dn = n\n\n        for j in range(d - 1):\n            mnix[j] = self.random_binomial(pix[j] / remaining_p, dn)\n            dn = dn - mnix[j]\n            if dn <= 0:\n                break\n            remaining_p -= pix[j]\n\n        if dn > 0:\n            mnix[d - 1] = dn\n\n    def hypergeometric_sample(self, good: int, bad: int, sample: int):\n        computed_sample = 0\n        total = good + bad\n\n        if sample > total // 2:\n            computed_sample = total - sample\n        else:\n            computed_sample = sample\n\n        remaining_total = total\n        remaining_good = good\n\n        while (computed_sample > 0 and\n               remaining_good > 0 and\n               remaining_total > remaining_good):\n            remaining_total -= 1\n            if int(self.random_interval(u64(remaining_total))) < remaining_good:\n                remaining_good -= 1\n            computed_sample -= 1\n\n        if remaining_total == remaining_good:\n            remaining_good -= computed_sample\n\n        result = 0\n        if sample > total // 2:\n            result = remaining_good\n        else:\n            result = good - remaining_good\n\n        return result\n\n    def hypergeometric_hrua(self, good: int, bad: int, sample: int):\n        D1 = 1.7155277699214135\n        D2 = 0.8989161620588988\n\n        popsize = good + bad\n        computed_sample = min(sample, popsize - sample)\n        mingoodbad = min(good, bad)\n        maxgoodbad = max(good, bad)\n\n        p = mingoodbad / popsize\n        q = maxgoodbad / popsize\n\n        mu = computed_sample * p\n\n        a = mu + 0.5\n\n        var = float(popsize - computed_sample) * computed_sample * p * q / (popsize - 1)\n\n        c = sqrt(var + 0.5)\n\n        h = D1*c + D2\n\n        m = int(floor(float(computed_sample + 1) * (mingoodbad + 1) / (popsize + 2)))\n\n        g = (logfactorial(m) +\n             logfactorial(mingoodbad - m) +\n             logfactorial(computed_sample - m) +\n             logfactorial(maxgoodbad - computed_sample + m))\n\n        b = min(min(computed_sample, mingoodbad) + 1., floor(a + 16*c))\n        K = 0\n\n        while True:\n            U = self.next_double()\n            V = self.next_double()\n            X = a + h*(V - 0.5) / U\n\n            if X < 0.0 or X >= b:\n                continue\n\n            K = int(floor(X))\n\n            gp = (logfactorial(K) +\n                  logfactorial(mingoodbad - K) +\n                  logfactorial(computed_sample - K) +\n                  logfactorial(maxgoodbad - computed_sample + K))\n\n            T = g - gp\n\n            if (U*(4.0 - U) - 3.0) <= T:\n                break\n\n            if U*(U - T) >= 1:\n                continue\n\n            if 2.0*log(U) <= T:\n                break\n\n        if good > bad:\n            K = computed_sample - K\n\n        if computed_sample < sample:\n            K = good - K\n\n        return K\n\n    def random_hypergeometric(self, good: int, bad: int, sample: int):\n        if sample >= 10 and sample <= good + bad - 10:\n            return self.hypergeometric_hrua(good, bad, sample)\n        else:\n            return self.hypergeometric_sample(good, bad, sample)\n\n    def random_multivariate_hypergeometric_count(self, total: int, num_colors: int, colors: Ptr[int],\n                                                 nsample: int, num_variates: int, variates: Ptr[int]):\n        from internal.gc import free\n\n        if total == 0 or nsample == 0 or num_variates == 0:\n            return\n\n        choices = Ptr[int](total)\n\n        k = 0\n        for i in range(num_colors):\n            for j in range(colors[i]):\n                choices[k] = i\n                k += 1\n\n        more_than_half = nsample > (total // 2)\n        if more_than_half:\n            nsample = total - nsample\n\n        for i in range(0, num_variates * num_colors, num_colors):\n            for j in range(nsample):\n                k = j + int(self.random_interval(u64(total - j - 1)))\n                tmp = choices[k]\n                choices[k] = choices[j]\n                choices[j] = tmp\n\n            for j in range(nsample):\n                idx = i + choices[j]\n                variates[idx] += 1\n\n            if more_than_half:\n                for k in range(num_colors):\n                    variates[i + k] = colors[k] - variates[i + k]\n\n        free(choices.as_byte())\n\n    def random_multivariate_hypergeometric_marginals(self, total: int, num_colors: int, colors: Ptr[int],\n                                                     nsample: int, num_variates: int, variates: Ptr[int]):\n        if total == 0 or nsample == 0 or num_variates == 0:\n            return\n\n        more_than_half = nsample > total // 2\n        if more_than_half:\n            nsample = total - nsample\n\n        for i in range(0, num_variates * num_colors, num_colors):\n            num_to_sample = nsample\n            remaining = total\n            j = 0\n\n            while num_to_sample > 0 and j + 1 < num_colors:\n                remaining -= colors[j]\n                r = self.random_hypergeometric(colors[j], remaining, num_to_sample)\n                variates[i + j] = r\n                num_to_sample -= r\n                j += 1\n\n            if num_to_sample > 0:\n                variates[i + num_colors - 1] = num_to_sample\n\n            if more_than_half:\n                for k in range(num_colors):\n                    variates[i + k] = colors[k] - variates[i + k]\n\n    def next_raw(self):\n        return self.next64()\n\n    def random_raw(self, lock, size, output):\n        if isinstance(size, int):\n            return self.random_raw(lock, (size,), output)\n\n        if output is None:\n            if size is None:\n                with lock:\n                    self.next_raw()\n                return None\n\n            n = 0\n            for a in size:\n                n += a\n            with lock:\n                for _ in range(n):\n                    self.next_raw()\n            return None\n\n        if size is None:\n            with lock:\n                return self.next_raw()\n\n        randoms = empty(size, u64)\n        randoms_data = randoms.data\n        n = randoms.size\n\n        with lock:\n            for i in range(n):\n                randoms_data[i] = self.next_raw()\n        return randoms\n\n    def random_raw(self, size = None):\n        if isinstance(size, int):\n            return self.random_raw((size,))\n        elif size is None:\n            return int(self.next64())\n        else:\n            arr = empty(size, u64)\n            p = arr.data\n            for i in range(arr.size):\n                p[i] = self.next64()\n            return arr\n\n    def shuffle_raw(self, n: int, first: int, itemsize: int,\n                    stride: int, data: cobj, buf: cobj):\n        for i in range(n - 1, first - 1, -1):\n            j = int(self.random_interval(u64(i)))\n            str.memcpy(buf, data + j * stride, itemsize)\n            str.memcpy(data + j * stride, data + i * stride, itemsize)\n            str.memcpy(data + i * stride, buf, itemsize)\n\n    def shuffle_int(self, n: int, first: int, data: Ptr[int]):\n        for i in range(n - 1, first - 1, -1):\n            j = int(self.random_bounded_uint64(u64(0), u64(i), u64(0), False))\n            temp = data[j]\n            data[j] = data[i]\n            data[i] = temp\n\n    def shuffle_raw_wrap(self, n: int, first: int, itemsize: int,\n                         stride: int, data: cobj, buf: cobj):\n        int_size = util.sizeof(int)\n        if itemsize == int_size:\n            self.shuffle_raw(n, first, int_size, stride, data, buf)\n        else:\n            self.shuffle_raw(n, first, itemsize, stride, data, buf)\n\ndef validate_output_shape(iter_shape, output: ndarray):\n    dims = output.shape\n    ndim: Literal[int] = static.len(dims)\n\n    if ndim != static.len(iter_shape):\n        compile_error(\"Output size is not compatible with broadcast dimensions of inputs\")\n\n    if iter_shape != dims:\n        raise ValueError(f\"Output size {dims} is not compatible with broadcast \"\n                         f\"dimensions of inputs {iter_shape}.\")\n\ndef check_output(out, dtype: type, size, require_c_array: bool):\n    if isinstance(size, int):\n        return check_output(out, dtype, (size,), require_c_array)\n\n    if out is None:\n        return\n\n    T = out.dtype\n    if T is not dtype:\n        compile_error(\"Supplied output array has the wrong type. Expected \"\n                        + dtype.__name__ + \" but got \" + T.__name__)\n\n    cc, fc = out._contig\n    if not (cc or (fc and not require_c_array)):\n        req = \"C-\" if require_c_array else \"\"\n        raise ValueError(\n            f'Supplied output array must be {req}contiguous, writable, '\n            f'aligned, and in machine byte-order.'\n        )\n\n    if size is not None:\n        if static.len(size) != static.len(out.shape):\n            compile_error(\"size must match out.shape when used together\")\n\n        if size != out.shape:\n            raise ValueError(\"size must match out.shape when used together\")\n\ndef double_fill(func, size, lock, out):\n    out_val = 0.0\n\n    if size is None and out is None:\n        with lock:\n            func(1, __ptr__(out_val))\n            return out_val\n    else:\n        if out is not None:\n            check_output(out, float, size, False)\n            out_array = out\n        else:\n            out_array = empty(size, float)\n\n        n = out_array.size\n        out_array_data = out_array.data\n        with lock:\n            func(n, out_array_data)\n        return out_array\n\ndef float_fill(func, size, lock, out):\n    out_val = float32(0.0)\n\n    if size is None and out is None:\n        with lock:\n            func(1, __ptr__(out_val))\n            return out_val\n    else:\n        if out is not None:\n            check_output(out, float32, size, False)\n            out_array = out\n        else:\n            out_array = empty(size, float32)\n\n        n = out_array.size\n        out_array_data = out_array.data\n        with lock:\n            func(n, out_array_data)\n        return out_array\n\ndef float_fill_from_double(func, size, lock, out):\n    if size is None and out is None:\n        with lock:\n            return func()\n    else:\n        if out is not None:\n            check_output(out, float32, size, False)\n            out_array = out\n        else:\n            out_array = empty(size, float32)\n\n        n = out_array.size\n        out_array_data = out_array.data\n        with lock:\n            for i in range(n):\n                out_array_data[i] = float32(func())\n        return out_array\n\ndef _check_all(fn, val, name: str, msg):\n    if isinstance(val, ndarray):\n        cc, fc = val._contig\n\n        if cc or fc:\n            val_data = val.data\n            for i in range(val.size):\n                e = util.cast(val_data[i], float)\n                if not fn(e):\n                    raise ValueError(msg(name))\n        else:\n            for idx in util.multirange(val.shape):\n                e = util.cast(val._ptr(idx)[0], float)\n                if not fn(e):\n                    raise ValueError(msg(name))\n    else:\n        if not fn(val):\n            raise ValueError(msg(name))\n\nCONS_NONE            : Literal[int] = 0\nCONS_NON_NEGATIVE    : Literal[int] = 1\nCONS_POSITIVE        : Literal[int] = 2\nCONS_POSITIVE_NOT_NAN: Literal[int] = 3\nCONS_BOUNDED_0_1     : Literal[int] = 4\nCONS_BOUNDED_GT_0_1  : Literal[int] = 5\nCONS_BOUNDED_LT_0_1  : Literal[int] = 6\nCONS_GT_1            : Literal[int] = 7\nCONS_GTE_1           : Literal[int] = 8\nCONS_POISSON         : Literal[int] = 9\n\nMAX_INT = 0x7FFFFFFFFFFFFFFF\nMAXSIZE = MAX_INT\nPOISSON_LAM_MAX = MAX_INT - sqrt(float(MAX_INT))*10\n\ndef _signbit(x: T, T: type):\n    if isinstance(x, float) or isinstance(x, float32):\n        return signbit(x)\n    else:\n        zero = T()\n        return x < zero\n\ndef check_array_constraint(val, name: str, const: int):\n    if const == CONS_NONE:\n        pass\n    elif const == CONS_NON_NEGATIVE:\n        fn = lambda x: not ((not isnan(x)) and _signbit(x))\n        msg = lambda name: f\"{name} < 0\"\n        _check_all(fn, val, name, msg)\n    elif const == CONS_POSITIVE or const == CONS_POSITIVE_NOT_NAN:\n        if const == CONS_POSITIVE_NOT_NAN:\n            fn = lambda x: not isnan(x)\n            msg = lambda name: f\"{name} must not be NaN\"\n            _check_all(fn, val, name, msg)\n\n        fn = lambda x: x > type(x)(0)\n        msg = lambda name: f\"{name} <= 0\"\n        _check_all(fn, val, name, msg)\n    elif const == CONS_BOUNDED_0_1:\n        fn = lambda x: x >= type(x)(0) and x <= type(x)(1)\n        msg = lambda name: f\"{name} < 0, {name} > 1 or {name} contains NaNs\"\n        _check_all(fn, val, name, msg)\n    elif const == CONS_BOUNDED_GT_0_1:\n        fn = lambda x: x > type(x)(0) and x <= type(x)(1)\n        msg = lambda name: f\"{name} <= 0, {name} > 1 or {name} contains NaNs\"\n        _check_all(fn, val, name, msg)\n    elif const == CONS_BOUNDED_LT_0_1:\n        fn = lambda x: x >= type(x)(0) and x < type(x)(1)\n        msg = lambda name: f\"{name} < 0, {name} >= 1 or {name} contains NaNs\"\n        _check_all(fn, val, name, msg)\n    elif const == CONS_BOUNDED_LT_0_1:\n        fn = lambda x: x >= type(x)(0) and x < type(x)(1)\n        msg = lambda name: f\"{name} < 0, {name} >= 1 or {name} contains NaNs\"\n        _check_all(fn, val, name, msg)\n    elif const == CONS_GT_1:\n        fn = lambda x: x > type(x)(1)\n        msg = lambda name: f\"{name} <= 1 or {name} contains NaNs\"\n        _check_all(fn, val, name, msg)\n    elif const == CONS_GTE_1:\n        fn = lambda x: x >= type(x)(1)\n        msg = lambda name: f\"{name} < 1 or {name} contains NaNs\"\n        _check_all(fn, val, name, msg)\n    elif const == CONS_POISSON:\n        fn = lambda x: util.cast(x, float) >= 0.0\n        msg = lambda name: f\"{name} < 0 or {name} is NaN\"\n        _check_all(fn, val, name, msg)\n\n        fn = lambda x: util.cast(x, float) <= POISSON_LAM_MAX\n        msg = lambda name: f\"{name} value too large\"\n        _check_all(fn, val, name, msg)\n\ndef convert_array_like(a):\n    if (isinstance(a, ndarray) or\n        isinstance(a, List) or\n        isinstance(a, Tuple)):\n        return asarray(a)\n    else:\n        return a\n\ndef gather_arrays(t):\n    if static.len(t) == 0:\n        return ()\n\n    a, rest = t[0], gather_arrays(t[1:])\n\n    if isinstance(a, ndarray):\n        return (a, *rest)\n    else:\n        return rest\n\ndef cont(fn, size, lock, arrays, names, constraints, out = None, dtype: type = float):\n    if not (static.len(arrays) == static.len(names) and static.len(names) == static.len(constraints)):\n        compile_error(\"[internal error] tuple size mismatch\")\n\n    if isinstance(size, int):\n        return cont(fn, (size,), lock, arrays, names, constraints, out)\n\n    arrays = tuple(convert_array_like(arr) for arr in arrays)\n\n    for i in static.range(static.len(names)):\n        check_array_constraint(arrays[i], names[i], constraints[i])\n\n    # constant parameters case\n    if static.len(gather_arrays(arrays)) == 0:\n        args = arrays\n\n        if size is not None and out is None:\n            randoms = empty(size, dtype)\n            randoms_data = randoms.data\n            n = randoms.size\n\n            for i in range(n):\n                randoms_data[i] = fn(*args)\n\n            return randoms\n        else:\n            if out is None:\n                return fn(*args)\n            else:\n                randoms = out\n                validate_output_shape((), randoms)\n                randoms_data = randoms.data\n                n = randoms.size\n                cc, fc = randoms._contig\n\n                if cc or fc:\n                    for i in range(n):\n                        randoms_data[i] = fn(*args)\n                else:\n                    for idx in util.multirange(randoms.shape):\n                        p = randoms._ptr(idx)\n                        p[0] = fn(*args)\n\n                return randoms\n\n    arrays = tuple(asarray(arr) for arr in arrays)\n\n    if size is not None and out is None:\n        shapes = tuple(arr.shape for arr in arrays)\n        broadcast_shapes(*shapes, size)  # error check\n        randoms = empty(size, dtype)\n    else:\n        shapes = tuple(arr.shape for arr in arrays)\n        if size is None:\n            bshape = broadcast_shapes(*shapes)\n        else:\n            bshape = broadcast_shapes(*shapes, size)\n\n        if out is None:\n            randoms = empty(bshape, dtype)\n        else:\n            randoms = out\n            validate_output_shape(bshape, randoms)\n\n    randoms_data = randoms.data\n    n = randoms.size\n\n    for idx in util.multirange(randoms.shape):\n        args = tuple(arr._ptr(idx, broadcast=True)[0] for arr in arrays)\n        p = randoms._ptr(idx)\n        p[0] = fn(*args)\n\n    return randoms\n\ndef kahan_sum(darr: Ptr[float], n: int):\n    if n <= 0:\n        return 0.0\n    sum = darr[0]\n    c = 0.0\n    for i in range(1, n):\n        y = darr[i] - c\n        t = sum + y\n        c = (t-sum) - y\n        sum = t\n    return sum\n\n\nfrom threading import Lock\n\n@tuple\nclass Generator[G]:\n    lock: Lock\n    bit_generator: BitGenerator[G]\n\n    def __new__(g: G):\n        return Generator[G](Lock(), BitGenerator[G](g))\n\n    def __str__(self):\n        return f\"{self.__class__.__name__}({self.bit_generator.__class__.__name__})\"\n\n    # TODO: pickle support\n\n    def spawn(self, n_children: int):\n        return [Generator(G(seed)) for seed in self.bit_generator.seed_seq.spawn(n_children)]\n\n    def random(self, size = None, dtype: type = float, out = None):\n        if dtype is float:\n            return double_fill(self.bit_generator.random_standard_uniform_fill, size, self.lock, out)\n        elif dtype is float32:\n            return float_fill(self.bit_generator.random_standard_uniform_fill_f, size, self.lock, out)\n        else:\n            compile_error(\"Unsupported dtype \" + dtype.__name__ + \" for random\")\n\n    def beta(self, a, b, size = None):\n        return cont(self.bit_generator.random_beta,\n                    size, self.lock, (a, b), ('a', 'b'),\n                    (CONS_POSITIVE, CONS_POSITIVE))\n\n    def exponential(self, scale = 1.0, size = None):\n        return cont(self.bit_generator.random_exponential,\n                    size, self.lock, (scale,), ('scale',),\n                    (CONS_NON_NEGATIVE,))\n\n    def standard_exponential(self, size = None, dtype: type = float,\n                             method: str = 'zig', out = None):\n        def bad_method():\n            raise ValueError(\"'method' argument must be either 'zig' or 'inv'\")\n\n        if dtype is float:\n            if method == 'zig':\n                return double_fill(self.bit_generator.random_standard_exponential_fill, size, self.lock, out)\n            elif method == 'inv':\n                return double_fill(self.bit_generator.random_standard_exponential_inv_fill, size, self.lock, out)\n            else:\n                bad_method()\n        elif dtype is float32:\n            if method == 'zig':\n                return float_fill(self.bit_generator.random_standard_exponential_fill_f, size, self.lock, out)\n            elif method == 'inv':\n                return float_fill(self.bit_generator.random_standard_exponential_inv_fill_f, size, self.lock, out)\n            else:\n                bad_method()\n        else:\n            compile_error(\"Unsupported dtype \" + dtype.__name__ + \" for standard_exponential\")\n\n    def integers(self, low, high = None, size = None, dtype: type = int, endpoint: bool = False):\n        if high is None:\n            return self.integers(0, low, size=size, dtype=dtype, endpoint=endpoint)\n\n        if isinstance(size, int):\n            return self.integers(low, high, size=(size,), dtype=dtype, endpoint=endpoint)\n        elif size is not None:\n            for s in size:\n                if s == 0:\n                    return empty(size, dtype)\n\n        if dtype is u64:\n            lb = 0\n            ub = 0x7FFFFFFFFFFFFFFF\n            fn = self.bit_generator.random_bounded_uint64_fill\n            out_val = u64()\n        elif dtype is u32:\n            lb = 0\n            ub = 0xFFFFFFFF\n            fn = self.bit_generator.random_bounded_uint32_fill\n            out_val = u32()\n        elif dtype is u16:\n            lb = 0\n            ub = 0xFFFF\n            fn = self.bit_generator.random_bounded_uint16_fill\n            out_val = u16()\n        elif dtype is u8:\n            lb = 0\n            ub = 0xFF\n            fn = self.bit_generator.random_bounded_uint8_fill\n            out_val = u8()\n        elif dtype is bool:\n            lb = 0\n            ub = 1\n            fn = self.bit_generator.random_bounded_bool_fill\n            out_val = False\n        elif dtype is i64 or dtype is int:\n            lb = -9223372036854775808\n            ub = 0x7FFFFFFFFFFFFFFF\n            fn = self.bit_generator.random_bounded_uint64_fill\n            out_val = u64()\n        elif dtype is i32:\n            lb = -0x80000000\n            ub = 0x7FFFFFFF\n            fn = self.bit_generator.random_bounded_uint32_fill\n            out_val = u32()\n        elif dtype is i16:\n            lb = -0x8000\n            ub = 0x7FFF\n            fn = self.bit_generator.random_bounded_uint16_fill\n            out_val = u16()\n        elif dtype is i8:\n            lb = -0x80\n            ub = 0x7F\n            fn = self.bit_generator.random_bounded_uint8_fill\n            out_val = u8()\n        else:\n            compile_error(\"Unsupported dtype \" + dtype.__name__ + \" for integers\")\n\n        use_masked = False\n        low = convert_array_like(low)\n        high = convert_array_like(high)\n\n        if static.len(gather_arrays((low, high))) == 0:\n            ilow = int(low)\n            ihigh = int(high)\n\n            if not endpoint:\n                ihigh -= 1\n\n            if ilow < lb:\n                raise ValueError(\"low is out of bounds for \" + dtype.__name__)\n\n            if ihigh > ub:\n                raise ValueError(\"high is out of bounds for \" + dtype.__name__)\n\n            if ilow > ihigh:\n                raise ValueError(\"low > high\" if endpoint else \"low >= high\")\n\n            rng = type(out_val)(ihigh - ilow)\n            off = type(out_val)(ilow)\n\n            if size is None:\n                with self.lock:\n                    fn(off, rng, 1, use_masked, __ptr__(out_val))\n                return util.cast(out_val, dtype)\n            else:\n                out_arr = empty(size, dtype)\n                cnt = out_arr.size\n                out_data = Ptr[type(out_val)](out_arr.data.as_byte())\n                with self.lock:\n                    fn(off, rng, cnt, use_masked, out_data)\n                return out_arr\n        else:\n            low = asarray(low)\n            high = asarray(high)\n\n            if size is None:\n                bshape = broadcast_shapes(low.shape, high.shape)\n            else:\n                bshape = broadcast_shapes(low.shape, high.shape, size)\n\n            randoms = empty(bshape, dtype)\n            randoms_data = randoms.data\n            n = randoms.size\n\n            for idx in util.multirange(randoms.shape):\n                e_low = int(low._ptr(idx, broadcast=True)[0])\n                e_high = int(high._ptr(idx, broadcast=True)[0])\n\n                if not endpoint:\n                    e_high -= 1\n\n                if e_low < lb:\n                    raise ValueError(\"low is out of bounds for \" + dtype.__name__)\n\n                if e_high > ub:\n                    raise ValueError(\"high is out of bounds for \" + dtype.__name__)\n\n                if e_low > e_high:\n                    raise ValueError(\"low > high\" if endpoint else \"low >= high\")\n\n                rng = type(out_val)(e_high - e_low)\n                off = type(out_val)(e_low)\n\n                with self.lock:\n                    fn(off, rng, 1, use_masked, __ptr__(out_val))\n                p = randoms._ptr(idx)\n                p[0] = util.cast(out_val, dtype)\n\n            return randoms\n\n    def bytes(self, length: int):\n        if length < 0:\n            raise ValueError(\"length must be non-negative\")\n        n_uint32 = ((length - 1) // 4 + 1)\n        arr = self.integers(0, 4294967296, size=n_uint32, dtype=u32)\n        return str(arr.data.as_byte(), length)\n\n    def choice(self, a, size = None, replace: bool = True, p = None, axis: int = 0, shuffle: bool = True):\n        def prod(s):\n            if s is None:\n                return 0\n            else:\n                return util.count(s)\n\n        def bisect_right(a, x, n: int):\n            lo = 0\n            hi = n\n            while lo < hi:\n                mid = (lo + hi) // 2\n                if x < a[mid]:\n                    hi = mid\n                else:\n                    lo = mid + 1\n            return lo\n\n        if isinstance(size, int):\n            return self.choice(a, size=(size,), replace=replace, p=p, axis=axis, shuffle=shuffle)\n\n        pop_size = 0\n        if isinstance(a, int):\n            pop_size = a\n            if pop_size <= 0 and prod(size) != 0:\n                raise ValueError(\"a must be a positive integer unless no \"\n                                 \"samples are taken\")\n            a1 = asarray(a)\n        else:\n            a1 = asarray(a)\n            pop_size = a1.shape[axis]\n            if pop_size == 0 and prod(size) != 0:\n                raise ValueError(\"a cannot be empty unless no samples are \"\n                                 \"taken\")\n\n        a = a1\n        pix = Ptr[float]()\n\n        if p is not None:\n            p1 = asarray(p, order='C')\n            d = len(p1)\n\n            if static.len(p1.shape) != 1:\n                compile_error(\"p must be 1-dimensional\")\n\n            if p1.dtype is not float:\n                compile_error(\"p must contain floats\")\n\n            atol = util.sqrt(util.eps64())\n            pix = p1.data\n\n            if p1.size != pop_size:\n                raise ValueError(\"a and p must have same size\")\n            p_sum = kahan_sum(pix, d)\n            if util.isnan(p_sum):\n                raise ValueError(\"probabilities contain NaN\")\n            for i in range(pop_size):\n                if pix[i] < 0:\n                    raise ValueError(\"probabilities are not non-negative\")\n            if abs(p_sum - 1.) > atol:\n                raise ValueError(\"probabilities do not sum to 1\")\n\n        is_scalar: Literal[bool] = size is None\n        if not is_scalar:\n            shape = size\n            size1 = prod(shape)\n        else:\n            shape = ()\n            size1 = 1\n\n        if replace:\n            if p is not None:\n                cdf = Ptr[float](pop_size)\n                s = 0.0\n                for i in range(pop_size):\n                    s += pix[i]\n                    cdf[i] = s\n                for i in range(pop_size):\n                    cdf[i] /= cdf[pop_size - 1]\n                uniform_samples = atleast_1d(self.random(shape))\n                nu = len(uniform_samples)\n                idx1 = empty(shape, dtype=int)\n\n                for idx in util.multirange(shape):\n                    px = uniform_samples._ptr(idx)\n                    qx = idx1._ptr(idx)\n                    qx[0] = bisect_right(cdf, px[0], pop_size)\n            else:\n                idx1 = self.integers(0, pop_size, size=shape, dtype=int)\n\n            idx2 = idx1\n        else:\n            if size1 > pop_size:\n                raise ValueError(\"Cannot take a larger sample than \"\n                                 \"population when replace is False\")\n            elif size1 < 0:\n                raise ValueError(\"negative dimensions are not allowed\")\n\n            if p is not None:\n                num_non_zero = 0\n                for i in range(pop_size):\n                    if pix[i] > 0:\n                        num_non_zero += 1\n                if num_non_zero < size1:\n                    raise ValueError(\"Fewer non-zero entries in p than size\")\n\n                n_uniq = 0\n                p1 = p1.copy()\n                found = zeros(shape, dtype=int)\n                flat_found = found.ravel()\n\n                while n_uniq < size1:\n                    m = size1 - n_uniq\n                    x = self.random((m,))\n                    if n_uniq > 0:\n                        for i in flat_found[0:n_uniq]:\n                            pix[i] = 0\n\n                    cdf = Ptr[float](pop_size)\n                    s = 0.0\n                    for i in range(pop_size):\n                        s += pix[i]\n                        cdf[i] = s\n                    for i in range(pop_size):\n                        cdf[i] /= cdf[pop_size - 1]\n\n                    new = Ptr[int](m)\n                    for i in range(m):\n                        new[i] = bisect_right(cdf, x[i], pop_size)\n\n                    idx_val = [(new[i], i) for i in range(m)]\n                    idx_val.sort()\n                    unique_indices = []\n\n                    i = 0\n                    j = 1\n                    while i < m:\n                        e, k = idx_val[i]\n                        unique_indices.append(k)\n                        while j < m and idx_val[j][0] == e:\n                            j += 1\n                        i = j\n                    unique_indices.sort()\n\n                    for i in range(n_uniq, n_uniq + len(unique_indices)):\n                        flat_found[i] = new[unique_indices[i - n_uniq]]\n\n                    n_uniq += len(unique_indices)\n                idx2 = found\n            else:\n                size_i = size1\n                pop_size_i = pop_size\n                if shuffle:\n                    cutoff = 50\n                else:\n                    cutoff = 20\n\n                if pop_size_i > 10000 and (size_i > (pop_size_i // cutoff)):\n                    idx1a = arange(0, pop_size_i, 1, int)\n                    idx_data = idx1a.data\n                    with self.lock:\n                        self.bit_generator.shuffle_int(pop_size_i, max(pop_size_i - size_i, 1), idx_data)\n                    idx1a = idx1a[(pop_size - size1):].copy()\n                else:\n                    idx1a = empty(size1, dtype=int)\n                    idx_data = idx1a.data\n                    set_size = u64(int(1.2 * size_i))\n                    mask = BitGenerator.gen_mask(set_size)\n                    set_size = u64(1) + mask\n                    hash_set = full(int(set_size), u64(-1), u64)\n                    with self.lock:\n                        for j in range(pop_size_i - size_i, pop_size_i):\n                            val = self.bit_generator.random_bounded_uint64(u64(0), u64(j), u64(0), False)\n                            loc = int(val & mask)\n                            while hash_set[loc] != u64(-1) and hash_set[loc] != val:\n                                loc = (loc + 1) & int(mask)\n                            if hash_set[loc] == u64(-1): # then val not in hash_set\n                                hash_set[loc] = val\n                                idx_data[j - pop_size_i + size_i] = int(val)\n                            else: # we need to insert j instead\n                                loc = j & int(mask)\n                                while hash_set[loc] != u64(-1):\n                                    loc = (loc + 1) & int(mask)\n                                hash_set[loc] = u64(j)\n                                idx_data[j - pop_size_i + size_i] = int(j)\n                        if shuffle:\n                            self.bit_generator.shuffle_int(size_i, 1, idx_data)\n\n                idx2 = idx1a.reshape(shape)\n\n        if is_scalar and isinstance(idx2, ndarray):\n            idx3 = idx2.item(0)\n        else:\n            idx3 = idx2\n\n        if static.len(a.shape) == 0:\n            return idx2\n\n        if not is_scalar and static.len(asarray(idx3).shape) == 0:\n            res = empty((), dtype=a.dtype)\n            res[()] = a[asarray(idx3).item()]\n            return res\n\n        axis = util.normalize_axis_index(axis, a.ndim)\n        idx3 = asarray(idx3)\n\n        if static.len(idx3.shape) == 0 and static.len(a.shape) == 1:\n            return a._ptr((idx3.item(),))[0]\n\n        idx3 = atleast_1d(idx3)\n        new_shape = util.tuple_set(a.shape, axis, len(idx3))\n        rest_shape = util.tuple_delete(a.shape, axis)\n        res = empty(new_shape, dtype=a.dtype)\n\n        ai1 = 0\n        for ai2 in idx3:\n            for idx_rest in util.multirange(rest_shape):\n                i2 = util.tuple_insert(idx_rest, axis, ai1)\n                i1 = util.tuple_insert(idx_rest, axis, ai2)\n                p = a._ptr(i1)\n                q = res._ptr(i2)\n                q[0] = p[0]\n\n            ai1 += 1\n\n        return res\n\n    def uniform(self, low = 0.0, high = 1.0, size = None):\n        low = convert_array_like(low)\n        high = convert_array_like(high)\n\n        if static.len(gather_arrays((low, high))) == 0:\n            rng = float(high - low)\n            if not util.isfinite(rng):\n                raise OverflowError(\"high - low range exceeds valid bounds\")\n\n        def random_uniform_low_high(low: float, high: float):\n            rng = high - low\n            if not util.isfinite(rng):\n                raise OverflowError(\"high - low range exceeds valid bounds\")\n            if util.signbit(rng):\n                raise ValueError(\"low > high\")\n            return self.bit_generator.random_uniform(low, rng)\n\n        return cont(random_uniform_low_high,\n                    size, self.lock, (low, high), ('low', 'high'),\n                    (CONS_NONE, CONS_NONE))\n\n    def standard_normal(self, size = None, dtype: type = float, out = None):\n        if dtype is float:\n            return double_fill(self.bit_generator.random_standard_normal_fill, size, self.lock, out)\n        elif dtype is float32:\n            return float_fill(self.bit_generator.random_standard_normal_fill_f, size, self.lock, out)\n        else:\n            compile_error(\"Unsupported dtype \" + dtype.__name__ + \" for standard_normal\")\n\n    def normal(self, loc = 0.0, scale = 1.0, size = None):\n        return cont(self.bit_generator.random_normal,\n                    size, self.lock, (loc, scale), ('loc', 'scale'),\n                    (CONS_NONE, CONS_NON_NEGATIVE))\n\n    def standard_gamma(self, shape, size = None, dtype: type = float, out = None):\n        if dtype is float:\n            return cont(self.bit_generator.random_standard_gamma,\n                        size, self.lock, (shape,), ('shape',),\n                        (CONS_NON_NEGATIVE,))\n        elif dtype is float32:\n            @tuple\n            class U[G]:\n                bit_generator: G\n\n                def standard_gamma_f_cast(self, shape: float):\n                    return self.bit_generator.random_standard_gamma_f(util.cast(shape, float32))\n\n            return cont(U(self.bit_generator).standard_gamma_f_cast,\n                        size, self.lock, (shape,), ('shape',),\n                        (CONS_NON_NEGATIVE,), dtype=float32)\n        else:\n            compile_error(\"Unsupported dtype \" + dtype.__name__ + \" for standard_gamma\")\n\n    def gamma(self, shape, scale = 1.0, size = None):\n        return cont(self.bit_generator.random_gamma,\n                    size, self.lock, (shape, scale), ('shape', 'scale'),\n                    (CONS_NON_NEGATIVE, CONS_NON_NEGATIVE))\n\n    def f(self, dfnum, dfden, size = None):\n        return cont(self.bit_generator.random_f,\n                    size, self.lock, (dfnum, dfden), ('dfnum', 'dfden'),\n                    (CONS_POSITIVE, CONS_POSITIVE))\n\n    def noncentral_f(self, dfnum, dfden, nonc, size = None):\n        return cont(self.bit_generator.random_noncentral_f,\n                    size, self.lock, (dfnum, dfden, nonc), ('dfnum', 'dfden', 'nonc'),\n                    (CONS_POSITIVE, CONS_POSITIVE, CONS_NON_NEGATIVE))\n\n    def chisquare(self, df, size = None):\n        return cont(self.bit_generator.random_chisquare,\n                    size, self.lock, (df,), ('df',),\n                    (CONS_POSITIVE,))\n\n    def noncentral_chisquare(self, df, nonc, size = None):\n        return cont(self.bit_generator.random_noncentral_chisquare,\n                    size, self.lock, (df, nonc), ('df', 'nonc'),\n                    (CONS_POSITIVE, CONS_NON_NEGATIVE))\n\n    def standard_cauchy(self, size = None):\n        return cont(self.bit_generator.random_standard_cauchy,\n                    size, self.lock, (), (), ())\n\n    def standard_t(self, df, size = None):\n        return cont(self.bit_generator.random_standard_t,\n                    size, self.lock, (df,), ('df',),\n                    (CONS_POSITIVE,))\n\n    def vonmises(self, mu, kappa, size = None):\n        return cont(self.bit_generator.random_vonmises,\n                    size, self.lock, (mu, kappa), ('mu', 'kappa'),\n                    (CONS_NONE, CONS_NON_NEGATIVE))\n\n    def pareto(self, a, size = None):\n        return cont(self.bit_generator.random_pareto,\n                    size, self.lock, (a,), ('a',),\n                    (CONS_POSITIVE,))\n\n    def weibull(self, a, size = None):\n        return cont(self.bit_generator.random_weibull,\n                    size, self.lock, (a,), ('a',),\n                    (CONS_NON_NEGATIVE,))\n\n    def power(self, a, size = None):\n        return cont(self.bit_generator.random_power,\n                    size, self.lock, (a,), ('a',),\n                    (CONS_POSITIVE,))\n\n    def laplace(self, loc = 0.0, scale = 1.0, size = None):\n        return cont(self.bit_generator.random_laplace,\n                    size, self.lock, (loc, scale), ('loc', 'scale'),\n                    (CONS_NONE, CONS_NON_NEGATIVE))\n\n    def gumbel(self, loc = 0.0, scale = 1.0, size = None):\n        return cont(self.bit_generator.random_gumbel,\n                    size, self.lock, (loc, scale), ('loc', 'scale'),\n                    (CONS_NONE, CONS_NON_NEGATIVE))\n\n    def logistic(self, loc = 0.0, scale = 1.0, size = None):\n        return cont(self.bit_generator.random_logistic,\n                    size, self.lock, (loc, scale), ('loc', 'scale'),\n                    (CONS_NONE, CONS_NON_NEGATIVE))\n\n    def lognormal(self, mean = 0.0, sigma = 1.0, size = None):\n        return cont(self.bit_generator.random_lognormal,\n                    size, self.lock, (mean, sigma), ('mean', 'sigma'),\n                    (CONS_NONE, CONS_NON_NEGATIVE))\n\n    def rayleigh(self, scale = 1.0, size = None):\n        return cont(self.bit_generator.random_rayleigh,\n                    size, self.lock, (scale,), ('scale',),\n                    (CONS_NON_NEGATIVE,))\n\n    def wald(self, mean, scale, size = None):\n        return cont(self.bit_generator.random_wald,\n                    size, self.lock, (mean, scale), ('mean', 'scale'),\n                    (CONS_POSITIVE, CONS_POSITIVE))\n\n    def triangular(self, left, mode, right, size = None):\n        if static.len(gather_arrays((asarray(left), asarray(mode), asarray(right)))) == 0:\n            if left > mode:\n                raise ValueError(\"left > mode\")\n            if mode > right:\n                raise ValueError(\"mode > right\")\n            if left == right:\n                raise ValueError(\"left == right\")\n        else:\n            oleft = asarray(left)\n            omode = asarray(mode)\n            oright = asarray(right)\n\n            if (oleft > omode).any():\n                raise ValueError(\"left > mode\")\n            if (omode > oright).any():\n                raise ValueError(\"mode > right\")\n            if (oleft == oright).any():\n                raise ValueError(\"left == right\")\n\n        return cont(self.bit_generator.random_triangular,\n                    size, self.lock, (left, mode, right), ('left', 'mode', 'right'),\n                    (CONS_NONE, CONS_NONE, CONS_NONE))\n\n    def binomial(self, n, p, size = None):\n        if isinstance(size, int):\n            return self.binomial(n, p, size=(size,))\n\n        n = convert_array_like(n)\n        p = convert_array_like(p)\n        if static.len(gather_arrays((n, p))) > 0:\n            an = asarray(n).astype(int, copy=False)\n            ap = asarray(p)\n            check_array_constraint(ap, 'p', CONS_BOUNDED_0_1)\n            check_array_constraint(an, 'n', CONS_NON_NEGATIVE)\n            bshape = broadcast_shapes(ap.shape, an.shape)\n            if size is not None:\n                randoms = empty(size, int)\n                broadcast_shapes(randoms.shape, bshape)  # error check\n            else:\n                randoms = empty(bshape, int)\n\n            with self.lock:\n                for idx in util.multirange(randoms.shape):\n                    e_n = an._ptr(idx, broadcast=True)[0]\n                    e_p = ap._ptr(idx, broadcast=True)[0]\n                    randoms._ptr(idx)[0] = self.bit_generator.random_binomial(e_p, e_n)\n\n            return randoms\n\n        p = float(p)\n        n = int(n)\n        check_array_constraint(p, 'p', CONS_BOUNDED_0_1)\n        check_array_constraint(n, 'n', CONS_NON_NEGATIVE)\n\n        if size is None:\n            with self.lock:\n                return self.bit_generator.random_binomial(p, n)\n        else:\n            randoms = empty(size, int)\n            cnt = randoms.size\n            randoms_data = randoms.data\n\n            with self.lock:\n                for i in range(cnt):\n                    randoms_data[i] = self.bit_generator.random_binomial(p, n)\n\n            return randoms\n\n    def negative_binomial(self, n, p, size = None):\n        if isinstance(size, int):\n            return self.negative_binomial(n, p, size=(size,))\n\n        n = convert_array_like(n)\n        p = convert_array_like(p)\n        if static.len(gather_arrays((n, p))) > 0:\n            an = asarray(n)\n            ap = asarray(p)\n            check_array_constraint(an, 'n', CONS_POSITIVE_NOT_NAN)\n            check_array_constraint(ap, 'p', CONS_BOUNDED_GT_0_1)\n\n            for idx in util.multirange(broadcast_shapes(an.shape, ap.shape)):\n                e_n = an._ptr(idx, broadcast=True)[0]\n                e_p = ap._ptr(idx, broadcast=True)[0]\n                if (1 - e_p) / e_p * (e_n + 10 * util.sqrt(util.cast(e_n, float))) > POISSON_LAM_MAX:\n                    raise ValueError(\"n too large or p too small, see Generator.negative_binomial Notes\")\n        else:\n            check_array_constraint(n, 'n', CONS_POSITIVE_NOT_NAN)\n            check_array_constraint(p, 'p', CONS_BOUNDED_GT_0_1)\n\n            dmax_lam = (1 - p) / p * (n + 10 * util.sqrt(util.cast(n, float)))\n            if dmax_lam > POISSON_LAM_MAX:\n                raise ValueError(\"n too large or p too small, see Generator.negative_binomial Notes\")\n\n        return cont(self.bit_generator.random_negative_binomial,\n                    size, self.lock, (n, p), ('n', 'p'),\n                    (CONS_NONE, CONS_NONE), dtype=int)\n\n    def poisson(self, lam = 1.0, size = None):\n        return cont(self.bit_generator.random_poisson,\n                    size, self.lock, (lam,), ('lam',),\n                    (CONS_POISSON,), dtype=int)\n\n    def zipf(self, a, size = None):\n        return cont(self.bit_generator.random_zipf,\n                    size, self.lock, (a,), ('a',),\n                    (CONS_GT_1,), dtype=int)\n\n    def geometric(self, p, size = None):\n        return cont(self.bit_generator.random_geometric,\n                    size, self.lock, (p,), ('p',),\n                    (CONS_BOUNDED_GT_0_1,), dtype=int)\n\n    def hypergeometric(self, ngood, nbad, nsample, size = None):\n        HYPERGEOM_MAX = 1_000_000_000\n        ngood = convert_array_like(ngood)\n        nbad = convert_array_like(nbad)\n        nsample = convert_array_like(nsample)\n\n        if static.len(gather_arrays((ngood, nbad, nsample))) == 0:\n            if ngood >= HYPERGEOM_MAX or nbad >= HYPERGEOM_MAX:\n                raise ValueError(\"both ngood and nbad must be less than 1000000000\")\n            if ngood + nbad < nsample:\n                raise ValueError(\"ngood + nbad < nsample\")\n        else:\n            angood = asarray(ngood)\n            anbad = asarray(nbad)\n            ansample = asarray(nsample)\n\n            for idx in util.multirange(broadcast_shapes(angood.shape, anbad.shape, ansample.shape)):\n                e_ngood = angood._ptr(idx, broadcast=True)[0]\n                e_nbad = anbad._ptr(idx, broadcast=True)[0]\n                e_nsample = ansample._ptr(idx, broadcast=True)[0]\n\n                if e_ngood >= HYPERGEOM_MAX or e_nbad >= HYPERGEOM_MAX:\n                    raise ValueError(\"both ngood and nbad must be less than 1000000000\")\n                if e_ngood + e_nbad < e_nsample:\n                    raise ValueError(\"ngood + nbad < nsample\")\n\n        return cont(self.bit_generator.random_hypergeometric,\n                    size, self.lock, (ngood, nbad, nsample), ('ngood', 'nbad', 'nsample'),\n                    (CONS_NON_NEGATIVE, CONS_NON_NEGATIVE, CONS_NON_NEGATIVE), dtype=int)\n\n    def logseries(self, p, size = None):\n        return cont(self.bit_generator.random_logseries,\n                    size, self.lock, (p,), ('p',),\n                    (CONS_BOUNDED_LT_0_1,), dtype=int)\n\n    def multivariate_normal(self, mean, cov, size = None, check_valid: Literal[str] = 'warn',\n                            tol: float = 1e-8, method: Literal[str] = 'svd'):\n        if method != 'eigh' and method != 'svd' and method != 'cholesky':\n            compile_error(\n                \"method must be one of {'eigh', 'svd', 'cholesky'}\")\n\n        mean = asarray(mean)\n        cov = asarray(cov)\n\n        dtype_mean = mean.dtype\n        dtype_cov = cov.dtype\n        if (dtype_mean is complex or dtype_mean is complex64 or\n            dtype_cov is complex or dtype_cov is complex64):\n            compile_error(\"mean and cov must not be complex\")\n\n        if size is None:\n            shape = ()\n        elif isinstance(size, int):\n            shape = (size,)\n        else:\n            shape = size\n\n        if static.len(mean.shape) != 1:\n            compile_error(\"mean must be 1 dimensional\")\n        if static.len(cov.shape) != 2:\n            compile_error(\"cov must be 2 dimensional\")\n        if cov.shape[0] != cov.shape[1]:\n            raise ValueError(\"cov must be square\")\n        if mean.shape[0] != cov.shape[0]:\n            raise ValueError(\"mean and cov must have same length\")\n\n        final_shape = shape + (mean.shape[0],)\n        x = self.standard_normal(final_shape).reshape(-1, mean.shape[0])\n\n        cov = cov.astype(float)\n        if method == 'svd':\n            from ..linalg import svd\n            (u, s, vh) = svd(cov)\n        elif method == 'eigh':\n            from ..linalg import eigh\n            (s, u)  = eigh(cov)\n        else:\n            from ..linalg import cholesky\n            l = cholesky(cov)\n\n        if check_valid != 'ignore' and method != 'cholesky':\n            if check_valid != 'warn' and check_valid != 'raise':\n                compile_error(\n                    \"check_valid must equal 'warn', 'raise', or 'ignore'\")\n            if method == 'svd':\n                from ..linalg import _linalg_dot\n                psd = allclose(_linalg_dot(vh.T * s, vh), cov, rtol=tol, atol=tol)\n            else:\n                psd = True\n                for a in s:\n                    if a < -tol:\n                        psd = False\n            if not psd:\n                if check_valid == 'warn':\n                    # TODO: warn \"covariance is not symmetric positive-semidefinite\"\n                    pass\n                else:\n                    raise ValueError(\"covariance is not symmetric positive-semidefinite.\")\n\n        if method == 'cholesky':\n            _factor = l\n        elif method == 'eigh':\n            for i in range(len(s)):\n                s[i] = util.sqrt(abs(s[i]))\n            _factor = u * s\n        else:\n            for i in range(len(s)):\n                s[i] = util.sqrt(s[i])\n            _factor = u * s\n\n        x = mean + x @ _factor.T\n        x = x.reshape(final_shape)\n        return x\n\n    def multinomial(self, n, pvals, size = None):\n        if isinstance(size, int):\n            return self.multinomial(n, pvals, size=(size,))\n\n        on = asarray(n).astype(int, copy=False)\n        parr = asarray(pvals)\n        ndim: Literal[int] = static.len(parr.shape)\n\n        if ndim >= 1:\n            d = parr.shape[ndim - 1]\n        else:\n            d = 0\n\n        if d == 0:\n            raise ValueError(\n                \"pvals must have at least 1 dimension and the last dimension \"\n                \"of pvals must be greater than 0.\"\n            )\n\n        check_array_constraint(parr, 'pvals', CONS_BOUNDED_0_1)\n        pix = parr.data\n        sz = parr.size\n        offset = 0\n\n        while offset < sz:\n            if kahan_sum(pix + offset, d - 1) > 1.0 + 1e-12:\n                slice_repr = \"[:-1]\" if ndim == 1 else \"[...,:-1]\"\n                raise ValueError(f\"sum(pvals{slice_repr}) > 1.0\")\n            offset += d\n\n        check_array_constraint(on, 'n', CONS_NON_NEGATIVE)\n\n        if static.len(on.shape) != 0 or ndim > 1:\n            offsets = arange(0, parr.size, d, dtype=int).reshape(parr.shape[:ndim - 1])\n            if size is None:\n                it = broadcast_shapes(on.shape, offsets.shape)\n            else:\n                it = broadcast_shapes(on.shape, offsets.shape, size)\n\n                if not util.tuple_equal(it, size):\n                    raise ValueError(\n                        f\"Output size {size} is not compatible with \"\n                        f\"broadcast dimensions of inputs.\"\n                    )\n\n            shape = it + (d,)\n            multin = zeros(shape, dtype=int)\n            mnix = multin.data\n            offset = 0\n            sz = util.count(it)\n            with self.lock:\n                for idx in util.multirange(shape):\n                    ni = on._ptr(idx, broadcast=True)[0]\n                    pi = offsets._ptr(idx, broadcast=True)[0]\n                    self.bit_generator.random_multinomial(ni, mnix + offset, pix + pi, d)\n                    offset += d\n            return multin\n\n        if size is None:\n            shape = (d,)\n        else:\n            shape = size + (d,)\n\n        multin = zeros(shape, dtype=int)\n        mnix = multin.data\n        sz = multin.size\n        ni = on.data[0]\n        check_array_constraint(ni, 'n', CONS_NON_NEGATIVE)\n        offset = 0\n        with self.lock:\n            for i in range(sz // d):\n                self.bit_generator.random_multinomial(ni, mnix + offset, pix, d)\n                offset += d\n\n        return multin\n\n    def multivariate_hypergeometric(self, colors, nsample: int, size = None, method: str = 'marginals'):\n        def safe_sum_nonneg_int64(num_colors: int, colors: Ptr[int]):\n            sum = 0\n            for i in range(num_colors):\n                if colors[i] > MAX_INT - sum:\n                    return -1\n                sum += colors[i]\n            return sum\n\n        if isinstance(size, int):\n            return self.multivariate_hypergeometric(colors, nsample, size=(size,), method=method)\n\n        if method != 'count' and method != 'marginals':\n            raise ValueError('method must be \"count\" or \"marginals\"')\n\n        if nsample < 0:\n            raise ValueError(\"nsample must be nonnegative.\")\n        nsamp = nsample\n\n        invalid_colors = False\n        colors = asarray(colors)\n        if static.len(colors.shape) != 1:\n            compile_error(\"colors must be one-dimensional\")\n\n        if colors.size == 0 or any(c < 0 for c in colors):\n            raise ValueError(\"colors must be a one-dimensional \"\n                             \"sequence of nonnegative integers\")\n\n        colors = ascontiguousarray(colors, dtype=int)\n        num_colors = colors.size\n        colors_ptr = colors.data\n\n        total = safe_sum_nonneg_int64(num_colors, colors_ptr)\n        if total == -1:\n            raise ValueError(\"sum(colors) must not exceed the maximum value \"\n                             \"of a 64 bit signed integer\")\n\n        if method == 'marginals':\n            if total >= 1000000000:\n                raise ValueError('When method is \"marginals\", sum(colors) must '\n                                 'be less than 1000000000.')\n\n        max_index = MAXSIZE // util.sizeof(int)\n        if method == 'count' and total > max_index:\n            raise ValueError(f\"When method is 'count', sum(colors) must not \"\n                             f\"exceed {max_index}\")\n        if nsamp > total:\n            raise ValueError(\"nsample > sum(colors)\")\n\n        if size is None:\n            shape = (num_colors,)\n        else:\n            shape = size + (num_colors,)\n\n        variates = zeros(shape, dtype=int)\n\n        if num_colors == 0:\n            return variates\n\n        num_variates = variates.size // num_colors\n        variates_ptr = variates.data\n\n        if method == 'count':\n            with self.lock:\n                self.bit_generator.random_multivariate_hypergeometric_count(\n                    total, num_colors, colors_ptr, nsamp, num_variates, variates_ptr)\n        else:\n            with self.lock:\n                self.bit_generator.random_multivariate_hypergeometric_marginals(\n                    total, num_colors, colors_ptr, nsamp, num_variates, variates_ptr)\n\n        return variates\n\n    def dirichlet(self, alpha, size = None):\n        if isinstance(size, int):\n            return self.dirichlet(alpha, size=(size,))\n\n        alpha = ascontiguousarray(asarray(alpha), dtype=float)\n        if static.len(alpha.shape) != 1:\n            compile_error(\"alpha must be a one-dimensional sequence of floats\")\n        k = len(alpha)\n\n        m = alpha[0] if k > 0 else 0.0\n        for a in alpha:\n            if a < 0.0:\n                raise ValueError(\"alpha < 0\")\n            if a > m:\n                m = a\n\n        alpha_data = alpha.data\n\n        if size is None:\n            shape = (k,)\n        else:\n            shape = size + (k,)\n\n        diric = zeros(shape, dtype=float)\n        val_data = diric.data\n\n        i = 0\n        totsize = diric.size\n\n        if k > 0 and m < 0.1:\n            alpha_csum_arr = empty_like(alpha)\n            alpha_csum_data = alpha_csum_arr.data\n            csum = 0.0\n            for j in range(k - 1, -1, -1):\n                csum += alpha_data[j]\n                alpha_csum_data[j] = csum\n\n            with self.lock:\n                while i < totsize:\n                    acc = 1.\n                    for j in range(k - 1):\n                        v = self.bit_generator.random_beta(alpha_data[j], alpha_csum_data[j + 1])\n                        val_data[i + j] = acc * v\n                        acc *= (1. - v)\n                    val_data[i + k - 1] = acc\n                    i = i + k\n        else:\n            with self.lock:\n                while i < totsize:\n                    acc = 0.\n                    for j in range(k):\n                        val_data[i + j] = self.bit_generator.random_standard_gamma(alpha_data[j])\n                        acc = acc + val_data[i + j]\n                    invacc = 1. / acc\n                    for j in range(k):\n                        val_data[i + j] = val_data[i + j] * invacc\n                    i = i + k\n\n        return diric\n\n    def permuted(self, x, axis = None, out = None):\n        x = asarray(x)\n\n        if out is None:\n            return self.permuted(x, axis=axis, out=x.copy(order='K'))\n        else:\n            if not isinstance(out, ndarray):\n                compile_error(\"out must be a numpy array\")\n            if not util.tuple_equal(out.shape, x.shape):\n                raise ValueError(\"out must have the same shape as x\")\n            copyto(out, x)\n\n        if axis is None:\n            if static.len(x.shape) > 1:\n                cc, fc = out._contig\n                if not (cc or fc):\n                    to_shuffle = array(out, order='C')\n                    self.shuffle(to_shuffle.ravel(order='K'))\n                    copyto(out, to_shuffle)\n                else:\n                    self.shuffle(out.ravel(order='A'))\n            else:\n                self.shuffle(out)\n            return out\n\n        ax = util.normalize_axis_index(axis, out.ndim)\n        itemsize = out.itemsize\n        axlen = out.shape[ax]\n        axstride = out.strides[ax]\n\n        shape = out.shape\n        buf = cobj(itemsize)\n\n        with self.lock:\n            for idx0 in util.multirange(util.tuple_delete(shape, ax)):\n                idx = util.tuple_insert(idx0, ax, 0)\n                data = out._ptr(idx).as_byte()\n                self.bit_generator.shuffle_raw_wrap(axlen, 0, itemsize, axstride, data, buf)\n\n        return out\n\n    def shuffle(self, x, axis: int = 0):\n        n = len(x)\n\n        if isinstance(x, ndarray):\n            ndim: Literal[int] = static.len(x.shape)\n            axis = util.normalize_axis_index(axis, ndim)\n            if x.size <= 1:\n                return\n\n            if ndim == 1:\n                x_ptr = x.data.as_byte()\n                stride = x.strides[0]\n                itemsize = x.itemsize\n                buf_ptr = cobj(itemsize)\n                with self.lock:\n                    self.bit_generator.shuffle_raw_wrap(n, 1, itemsize, stride, x_ptr, buf_ptr)\n            else:\n                x = swapaxes(x, 0, axis)\n                buf = empty_like(x[0, ...])\n\n                with self.lock:\n                    for i in range(len(x) - 1, 0, -1):\n                        j = int(self.bit_generator.random_interval(u64(i)))\n                        if i == j:\n                            continue\n                        buf[...] = x[j, ...]\n                        x[j, ...] = x[i, ...]\n                        x[i, ...] = buf\n        else:\n            if axis != 0:\n                raise ValueError(\"Axis argument is only supported on ndarray objects\")\n\n            with self.lock:\n                for i in range(len(x) - 1, 0, -1):\n                    j = int(self.bit_generator.random_interval(u64(i)))\n                    x[i], x[j] = x[j], x[i]\n\n    def permutation(self, x, axis: int = 0):\n        if isinstance(x, int):\n            arr = arange(x)\n            self.shuffle(arr)\n            return arr\n\n        x = asarray(x)\n        arr = x.copy()\n        ndim: Literal[int] = static.len(arr.shape)\n        axis = util.normalize_axis_index(axis, ndim)\n\n        if ndim == 1:\n            self.shuffle(arr)\n            return arr\n\n        shape = x.shape\n        axlen = shape[axis]\n        perm = arange(arr.shape[axis], dtype=int)\n        self.shuffle(perm)\n\n        with self.lock:\n            for ai in range(axlen):\n                src_on_axis = perm[ai]\n                for idx0 in util.multirange(util.tuple_delete(shape, axis)):\n                    src_idx = util.tuple_insert(idx0, axis, src_on_axis)\n                    dst_idx = util.tuple_insert(idx0, axis, ai)\n                    p = x._ptr(src_idx)\n                    q = arr._ptr(dst_idx)\n                    q[0] = p[0]\n\n        return arr\n"
  },
  {
    "path": "stdlib/numpy/random/logfactorial.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom ..util import log\n\n@pure\n@llvm\ndef logfact(idx: int) -> float:\n    @data = private unnamed_addr constant [126 x double] [double 0.000000e+00, double 0.000000e+00, double 0x3FE62E42FEFA39EF, double 0x3FFCAB0BFA2A2002, double 0x40096CA77C922CF9, double 0x401326643C4479C9, double 0x401A51273ACF01CA, double 0x40210CE1F32DCC30, double 0x4025358E82FCB70D, double 0x40299A8921A7F7CF, double 0x402E357590954D15, double 0x403180973F3A8D74, double 0x4033FCBA16D50143, double 0x40368D5A9C3B32CE, double 0x403930F3DF162A42, double 0x403BE636A63FD346, double 0x403EABFF061F1A84, double 0x4040C0A63F2F353A, double 0x4042329DF2D5EE52, double 0x4043AB8153363985, double 0x40452AF57AED77BE, double 0x4046B0A8643472A9, double 0x40483C4FABA84F06, double 0x4049CDA78B856A45, double 0x404B6472034E8D14, double 0x404D007622CD65E7, double 0x404EA17F717C6794, double 0x405023AEB67E4FEF, double 0x4050F8F18D330240, double 0x4051D07353917231, double 0x4052AA208B59D0E5, double 0x405385E6FD9E5A40, double 0x405463B59B942084, double 0x4055437C633ACE4A, double 0x4056252C474896BA, double 0x405708B719E11658, double 0x4057EE0F79B26758, double 0x4058D528C1243D96, double 0x4059BDF6F75257A3, double 0x405AA86EC2969812, double 0x405B94855C702BA2, double 0x405C8230869CA105, double 0x405D7166813E12EE, double 0x405E621E01EEBA4F, double 0x405F544E2BA69CF1, double 0x406023F743ADDD9F, double 0x40609E7B7EA41EA9, double 0x406119AFE762626B, double 0x40619590C853A559, double 0x4062121A930C6EC3, double 0x40628F49DDEB1F31, double 0x40630D1B61E86335, double 0x40638B8BF8931DDB, double 0x40640A989A33A6CD, double 0x40648A3E5C12AF19, double 0x40650A7A6EE08711, double 0x40658B4A1D39DA73, double 0x40660CAACA474746, double 0x40668E99F0757979, double 0x406711152043B2C4, double 0x40679419FF26DC59, double 0x406817A6467F6FB9, double 0x40689BB7C2A0AEA1, double 0x4069204C51E7C761, double 0x4069A561E3E1A4BD, double 0x406A2AF6787E4609, double 0x406AB1081F509726, double 0x406B3794F6D9D7AF, double 0x406BBE9B2BDFB621, double 0x406C4618F8CC56F7, double 0x406CCE0CA5179100, double 0x406D567484B8B7B6, double 0x406DDF4EF7A05A70, double 0x406E689A69396BEF, double 0x406EF2554FF15148, double 0x406F7C7E2CC66183, double 0x40700389C56E3462, double 0x40704909FF8B652B, double 0x40708EBF13DBF263, double 0x4070D4A85602B129, double 0x40711AC51DF8932A, double 0x40716114C7E34736, double 0x4071A796B3EDE1AC, double 0x4071EE4A46236D3E, double 0x4072352EE64B46D5, double 0x40727C43FFC72962, double 0x4072C3890172D057, double 0x40730AFD5D851956, double 0x407352A089728F1B, double 0x40739A71FDD14947, double 0x4073E271363E0DF7, double 0x40742A9DB142A36A, double 0x407472F6F03D410C, double 0x4074BB7C77491066, double 0x4075042DCD27AF64, double 0x40754D0A7B2BA658, double 0x407596120D23C4EC, double 0x4075DF4411475A1C, double 0x407628A018233BED, double 0x40767225B4879462, double 0x4076BBD47B7669B6, double 0x407705AC0412D89F, double 0x40774FABE790F7BE, double 0x407799D3C1265C0E, double 0x4077E4232DFB367D, double 0x40782E99CD1C0368, double 0x407879373F6BC4FE, double 0x4078C3FB2796C21C, double 0x40790EE52A05C35F, double 0x407959F4ECD1C8B3, double 0x4079A52A17B831CC, double 0x4079F084540F545E, double 0x407A3C034CBB7B2C, double 0x407A87A6AE24493A, double 0x407AD36E262A7CC0, double 0x407B1F59641E0DB5, double 0x407B6B6818B4A3EB, double 0x407BB799F600610A, double 0x407C03EEAF66FACD, double 0x407C5065F9992226, double 0x407C9CFF8A8A340D, double 0x407CE9BB196830EA, double 0x407D36985E93F7B8, double 0x407D83971399C213, double 0x407DD0B6F329DEA4, double 0x407E1DF7B911A74C], align 16\n    %p = getelementptr inbounds [126 x double], ptr @data, i64 0, i64 %idx\n    %x = load double, ptr %p, align 8\n    ret double %x\n\ndef logfactorial(k: int):\n\thalfln2pi = 0.9189385332046728\n\n\tif k < 126:\n\t\treturn logfact(k)\n\n\treturn (k + 0.5)*log(float(k)) - k + (halfln2pi + (1.0/k)*(1/12.0 - 1/(360.0*k*k)))\n"
  },
  {
    "path": "stdlib/numpy/random/mt19937.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .seed import SeedSequence\n\nN: Literal[int] = 624\nM: Literal[int] = 397\n\nMATRIX_A = u32(0x9908b0df)    # constant vector a\nUPPER_MASK = u32(0x80000000)  # most significant w-r bits\nLOWER_MASK = u32(0x7fffffff)  # least significant r bits\n\n# parameters for computing jump\nW_SIZE: Literal[int] = 32  # size of unsigned long\nMEXP  : Literal[int] = 19937\nP_SIZE: Literal[int] = ((MEXP // W_SIZE) + 1)\nLSB   : Literal[int] = 0x00000001\nQQ    : Literal[int] = 7\nLL    : Literal[int] = 128  # LL = 2^(QQ)\n\ndef get_coef(pf: Ptr[u64], deg: int):\n    return bool(pf[deg >> 5] & u64(LSB << (deg & 0x1f)))\n\n@tuple\nclass MT19937:\n    data: Ptr[u32]\n    seed: SeedSequence\n\n    def __new__(seed, legacy: Literal[bool] = False):\n        if not isinstance(seed, SeedSequence):\n            return MT19937(SeedSequence(seed))\n        else:\n            g = MT19937(Ptr[u32](N + 1), seed)\n\n            if legacy:\n                if not isinstance(seed, int):\n                    compile_error(\"'seed' must be an int for legacy seeding\")\n                if seed < 0 or seed > 0xffffffff:\n                    raise ValueError(\"Seed must be between 0 and 2**32 - 1\")\n                g.seed_legacy(seed)\n            else:\n                g.data[0] = u32(N - 1)\n                val = seed.generate_state(N, u32)\n                key = g.state\n\n                key[0] = u32(0x80000000)\n                for i in range(1, N):\n                    key[i] = val[i]\n\n            return g\n\n    def seed_legacy(self, seed: int):\n        seed = u32(seed)\n        seed &= u32(0xffffffff)\n\n        for pos in range(N):\n            self.state[pos] = u32(seed)\n            seed = (u32(1812433253) * (seed ^ (seed >> u32(30))) + u32(pos + 1)) & u32(0xffffffff)\n\n        self.data[0] = u32(N)\n\n    @property\n    def pos(self):\n        return int(self.data[0])\n\n    @property\n    def state(self):\n        return self.data + 1\n\n    def __get_state__(self):\n        from internal.gc import sizeof\n        p = Ptr[u32](N + 1)\n        str.memcpy(p.as_byte(), self.data.as_byte(), (N + 1) * sizeof(u32))\n        return (p,)\n\n    def __set_state__(self, state):\n        from internal.gc import sizeof\n        p = state[0]\n        str.memcpy(self.data.as_byte(), p.as_byte(), (N + 1) * sizeof(u32))\n\n    def copy_state(self, state: MT19937):\n        str.memcpy(self.data.as_byte(), state.data.as_byte(), (N + 1) * sizeof(u32))\n\n    def genrand_int32(self):\n        mag01t = (u32(0), MATRIX_A)\n        mag01 = Ptr[u32](__ptr__(mag01t).as_byte())\n        mt = self.state\n\n        if self.pos >= N:\n            kk = 0\n\n            while kk < int(N - M):\n                y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK)\n                mt[kk] = mt[kk + M] ^ (y >> u32(1)) ^ mag01[int(y & u32(1))]\n                kk += 1\n\n            while kk < int(N - 1):\n                y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK)\n                mt[kk] = mt[kk+(M-N)] ^ (y >> u32(1)) ^ mag01[int(y & u32(1))]\n                kk += 1\n\n            y = (mt[N-1] & UPPER_MASK) | (mt[0] & LOWER_MASK)\n            mt[N-1] = mt[M-1] ^ (y >> u32(1)) ^ mag01[int(y & u32(1))]\n            self.data[0] = u32(0)\n\n        i = self.pos\n        y = mt[i]\n        self.data[0] = u32(i + 1)\n        y ^= (y >> u32(11))\n        y ^= (y << u32(7)) & u32(0x9d2c5680)\n        y ^= (y << u32(15)) & u32(0xefc60000)\n        y ^= (y >> u32(18))\n        return y\n\n    def genrand_res53(self):\n        a = self.genrand_int32() >> u32(5)\n        b = self.genrand_int32() >> u32(6)\n        return (int(a) * 67108864.0 + int(b)) * (1.0 / 9007199254740992.0)\n\n    def random(self):\n        return self.genrand_res53()\n\n    def init_u32(self, s: u32):\n        mt = self.state\n        mt[0] = s\n        for mti in range(1, N):\n            mt[mti] = (u32(1812433253) * (mt[mti-1] ^ (mt[mti-1] >> u32(30))) + u32(mti))\n        self.data[0] = u32(N)\n\n    def init_array(self, init_key: Ptr[u32], key_length: int):\n        mt = self.state\n        self.init_u32(u32(19650218))\n        i = 1\n        j = 0\n\n        k = N if N > key_length else key_length\n        while k:\n            mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> u32(30))) * u32(1664525))) + init_key[j] + u32(j)\n            i += 1\n            j += 1\n            if i >= N:\n                mt[0] = mt[N - 1]\n                i = 1\n            if j >= key_length:\n                j = 0\n            k -= 1\n\n        k = N - 1\n        while k:\n            mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> u32(30))) * u32(1566083941))) - u32(i)\n            i += 1\n            if i >= N:\n                mt[0] = mt[N - 1]\n                i = 1\n            k -= 1\n\n        mt[0] = u32(0x80000000)\n\n    def seed_cpython(self, s: int):\n        init_key = (u32(s & ((1 << 32) - 1)), u32(s >> 32))\n        self.init_array(Ptr[u32](__ptr__(init_key).as_byte()), 2 if init_key[1] else 1)\n\n    # jump\n\n    def gen_next(self):\n        mag01t = (u32(0), MATRIX_A)\n        mag01 = Ptr[u32](__ptr__(mag01t).as_byte())\n        mt = self.state\n\n        kk = self.pos\n\n        if kk < N - M:\n            y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK)\n            mt[kk] = mt[kk + M] ^ (y >> u32(1)) ^ mag01[int(y & u32(1))]\n            self.data[0] += u32(1)\n            kk += 1\n        elif kk < N - 1:\n            y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK)\n            mt[kk] = mt[kk+(M-N)] ^ (y >> u32(1)) ^ mag01[int(y & u32(1))]\n            self.data[0] += u32(1)\n            kk += 1\n        elif kk == N - 1:\n            y = (mt[N-1] & UPPER_MASK) | (mt[0] & LOWER_MASK)\n            mt[N-1] = mt[M-1] ^ (y >> u32(1)) ^ mag01[int(y & u32(1))]\n            self.data[0] = u32(0)\n\n    def add_state(self, state2: MT19937):\n        pt1 = self.pos\n        pt2 = state2.pos\n        mt1 = self.state\n        mt2 = state2.state\n        i = 0\n\n        if pt2 - pt1 >= 0:\n            while i < N - pt2:\n              mt1[i + pt1] ^= mt2[i + pt2]\n              i += 1\n            while i < N - pt1:\n              mt1[i + pt1] ^= mt2[i + (pt2 - N)]\n              i += 1\n            while i < N:\n              mt1[i + (pt1 - N)] ^= mt2[i + (pt2 - N)]\n              i += 1\n        else:\n            while i < N - pt1:\n              mt1[i + pt1] ^= mt2[i + pt2]\n              i += 1\n            while i < N - pt2:\n              mt1[i + (pt1 - N)] ^= mt2[i + pt2]\n              i += 1\n            while i < N:\n              mt1[i + (pt1 - N)] ^= mt2[i + (pt2 - N)]\n              i += 1\n\n    def horner1(self, pf: Ptr[u64]):\n        from internal.gc import sizeof\n        i = MEXP - 1\n        temp_data = __array__[u32](N + 1)\n        str.memset(temp_data.ptr.as_byte(), byte(0), (N + 1) * sizeof(u32))\n        temp = MT19937(temp_data.ptr)\n\n        while not get_coef(pf, i):\n            i -= 1\n\n        if i > 0:\n            temp.copy_state(self)\n            temp.gen_next()\n            i -= 1\n            while i > 0:\n                if get_coef(pf, i):\n                    temp.add_state(self)\n                temp.gen_next()\n                i -= 1\n            if get_coef(pf, 0):\n                temp.add_state(self)\n        elif i == 0:\n            temp.copy_state(self)\n\n        self.copy_state(temp)\n\n    def jump_state(self):\n        if self.pos >= N:\n            self.data[0] = u32(0)\n\n        self.horner1(MT19937.poly_coef())\n\n    def jump_inplace(self, jumps: int):\n        for _ in range(jumps):\n            self.jump_state()\n\n    def next32(self):\n        return self.genrand_int32()\n\n    def next_double(self):\n        return self.genrand_res53()\n\n    # TODO: somehow add wrapping to this...\n    @pure\n    @llvm\n    def poly_coef() -> Ptr[u64]:\n        @pf = private unnamed_addr constant [624 x i64] [i64 1927166307, i64 3044056772, i64 2284297142, i64 2820929765, i64 651705945, i64 69149273, i64 3892165397, i64 2337412983, i64 1219880790, i64 3207074517, i64 3836784057, i64 189286826, i64 1049791363, i64 3916249550, i64 2942382547, i64 166392552, i64 861176918, i64 3246476411, i64 2302311555, i64 4273801148, i64 29196903, i64 1363664063, i64 3802562022, i64 2600400244, i64 3090369801, i64 4040416970, i64 1432485208, i64 3632558139, i64 4015816763, i64 3013316418, i64 551532385, i64 3592224467, i64 3479125595, i64 1195467127, i64 2391032553, i64 2393493419, i64 1482493632, i64 1625159565, i64 748389672, i64 4042774030, i64 2998615036, i64 3393119101, i64 2177492569, i64 2265897321, i64 2507383006, i64 3461498961, i64 2003319700, i64 1942857197, i64 1455226044, i64 4097545580, i64 529653268, i64 3204756480, i64 2486748289, i64 495294513, i64 3396001954, i64 2643963605, i64 2655404568, i64 3881604377, i64 624710790, i64 3443737948, i64 1941294296, i64 2139259604, i64 3368734020, i64 422436761, i64 3602810182, i64 1384691081, i64 3035786407, i64 2551797119, i64 537227499, i64 65486120, i64 642436100, i64 2023822537, i64 2515598203, i64 1122953367, i64 2882306242, i64 1743213032, i64 321965189, i64 336496623, i64 2436602518, i64 3556266590, i64 1055117829, i64 463541647, i64 743234441, i64 527083645, i64 2606668346, i64 2274046499, i64 2761475053, i64 2760669048, i64 2538258534, i64 487125077, i64 3365962306, i64 3604906217, i64 2714700608, i64 680709708, i64 2217161159, i64 1614899374, i64 3710119533, i64 3201300658, i64 3752620679, i64 2755041105, i64 3129723037, i64 1247297753, i64 2812642690, i64 4114340845, i64 3485092247, i64 2752814364, i64 3586551747, i64 4073138437, i64 3462966585, i64 2924318358, i64 4061374901, i64 3314086806, i64 2640385723, i64 744590670, i64 3007586513, i64 3959120371, i64 997207767, i64 3420235506, i64 2092400998, i64 3190305685, i64 60965738, i64 549507222, i64 3784354415, i64 3209279509, i64 1238863299, i64 2605037827, i64 178570440, i64 1743491299, i64 4079686640, i64 2136795825, i64 3435430548, i64 1679732443, i64 1835708342, i64 2159367000, i64 1924487218, i64 4059723674, i64 996192116, i64 2308091645, i64 1336281586, i64 674600050, i64 1642572529, i64 1383973289, i64 2202960007, i64 3165481279, i64 3385474038, i64 2501318550, i64 2671842890, i64 3084085109, i64 3475033915, i64 1551329147, i64 4101397249, i64 1205851807, i64 3641536021, i64 3607635071, i64 1609126163, i64 2910426664, i64 3324508658, i64 4244311266, i64 254034382, i64 1258304384, i64 1914048768, i64 1358592011, i64 527610138, i64 3072108727, i64 4289413885, i64 1417001678, i64 2445445945, i64 896462712, i64 339855811, i64 3699378285, i64 2529457297, i64 3049459401, i64 2723472429, i64 2838633181, i64 2520397330, i64 3272339035, i64 1667003847, i64 3742634787, i64 942706520, i64 2301027215, i64 1907791250, i64 2306299096, i64 1021173342, i64 1539334516, i64 2907834628, i64 3199959207, i64 1556251860, i64 3642580275, i64 2355865416, i64 285806145, i64 867932457, i64 1177354172, i64 3291107470, i64 4022765061, i64 1613380116, i64 588147929, i64 650574324, i64 1236855601, i64 1371354511, i64 2085218212, i64 1203081931, i64 420526905, i64 1022192219, i64 2903287064, i64 2470845899, i64 3649873273, i64 2502333582, i64 3972385637, i64 4246356763, i64 199084157, i64 1567178788, i64 2107121836, i64 4293612856, i64 1902910177, i64 332397359, i64 83422598, i64 3614961721, i64 456321943, i64 2277615967, i64 2302518510, i64 3258315116, i64 2521897172, i64 3900282042, i64 4186973154, i64 3146532165, i64 2299685029, i64 3889120948, i64 1293301857, i64 187455105, i64 3395849230, i64 913321567, i64 3093513909, i64 1440944571, i64 1923481911, i64 338680924, i64 1204882963, i64 2739724491, i64 2886241328, i64 2408907774, i64 1299817192, i64 2474012871, i64 45400213, i64 553186784, i64 134558656, i64 2180943666, i64 2870807589, i64 76511085, i64 3053566760, i64 2516601415, i64 4172865902, i64 1751297915, i64 1251975234, i64 2964780642, i64 1412975316, i64 2739978478, i64 2171013719, i64 637935041, i64 975972384, i64 3044407449, i64 3111425639, i64 1938684970, i64 2860857400, i64 13419586, i64 2772079268, i64 3484375614, i64 3184054178, i64 159924837, i64 1386213021, i64 2765617231, i64 2523689118, i64 1283505218, i64 3510789588, i64 4125878259, i64 2990287597, i64 2152014833, i64 3084155970, i64 2815101609, i64 1932985704, i64 114887365, i64 1712687646, i64 2550515629, i64 3299051916, i64 2022747614, i64 2143630992, i64 2244188960, i64 3309469192, i64 3234358520, i64 800720365, i64 3278176634, i64 554357439, i64 2415629802, i64 1620877315, i64 2389462898, i64 2229691332, i64 1007748450, i64 1966873768, i64 2264971043, i64 1214524156, i64 346854700, i64 3471905342, i64 3984889660, i64 4034246840, i64 216712649, i64 4027196762, i64 3754772604, i64 2121785562, i64 2347070732, i64 7457687, i64 1443375102, i64 683948143, i64 2940226032, i64 3211475670, i64 2836507357, i64 774899409, i64 1588968308, i64 780438009, i64 3278878781, i64 2217181540, i64 2184194887, i64 1642129086, i64 69346830, i64 297114710, i64 3841068188, i64 2631265450, i64 4167492314, i64 2613519651, i64 1388582503, i64 2171556668, i64 1201873758, i64 2698772382, i64 207791958, i64 3936134563, i64 3725025702, i64 3306317801, i64 1055730422, i64 4069230694, i64 1767821343, i64 4252407395, i64 2422583118, i64 3158834399, i64 3754582617, i64 1112422556, i64 376187931, i64 3137549150, i64 712221089, i64 3300799453, i64 3868250200, i64 1165257666, i64 2494837767, i64 131304831, i64 1619349427, i64 1958236644, i64 3678218946, i64 3651007751, i64 2261987899, i64 1567368524, i64 2193599522, i64 3034394674, i64 2994602555, i64 3072727647, i64 889094521, i64 1089692095, i64 1822324824, i64 3876999182, i64 1703361286, i64 902229515, i64 4213728487, i64 3838170364, i64 672727494, i64 2240733828, i64 3858539469, i64 1149254245, i64 4166055926, i64 4193525313, i64 1709921593, i64 2278290377, i64 3190784116, i64 2919588882, i64 1012709717, i64 3640562031, i64 2931984863, i64 3515665246, i64 250577343, i64 1147230194, i64 1183856202, i64 3734511989, i64 3243867808, i64 3499383067, i64 2985115159, i64 2036821626, i64 3298159553, i64 2726542838, i64 1686910320, i64 1778823772, i64 965412224, i64 233509772, i64 3843098861, i64 1312622954, i64 500855830, i64 2950562091, i64 1915683607, i64 3405781138, i64 596073719, i64 2195150546, i64 3381728478, i64 546426436, i64 3527890868, i64 2324975353, i64 2241074266, i64 3992514859, i64 2576108287, i64 4077653225, i64 2632319392, i64 3127212632, i64 917000669, i64 2498161805, i64 3980835128, i64 2259526768, i64 1083920509, i64 1187452089, i64 97018536, i64 3056075838, i64 2059706760, i64 2373335692, i64 182196406, i64 2136713111, i64 1762080153, i64 1572125803, i64 1145919955, i64 1023966754, i64 3921694345, i64 1632005969, i64 1418372326, i64 354407429, i64 2438288265, i64 1620072033, i64 1586320921, i64 1044153697, i64 969324572, i64 613487980, i64 4230993062, i64 397726764, i64 2194259193, i64 735511759, i64 2066049260, i64 88093248, i64 1562536153, i64 2114157419, i64 3630951546, i64 589238503, i64 3120654384, i64 2521793793, i64 2746692127, i64 2557723425, i64 889897693, i64 2778878177, i64 643269509, i64 3342389831, i64 19218890, i64 3442706236, i64 3314581273, i64 3503147052, i64 1546343434, i64 1448529060, i64 529038801, i64 2748942264, i64 2213019208, i64 111314040, i64 2488697563, i64 1180642808, i64 2605272289, i64 4207476668, i64 1502558669, i64 2972370981, i64 4204339995, i64 1046225278, i64 992840610, i64 3847290298, i64 2387673094, i64 2221565747, i64 1045901716, i64 3997739302, i64 1556952765, i64 1103336648, i64 279418400, i64 2711316466, i64 2336215718, i64 2317900806, i64 974624729, i64 909575434, i64 1675610631, i64 1922393214, i64 2054896570, i64 3197007361, i64 3932554569, i64 1008619802, i64 3349254938, i64 113511461, i64 932630384, i64 2098759268, i64 3436837432, i64 3119972401, i64 1612590197, i64 2281609013, i64 4174211248, i64 4016332246, i64 2097525539, i64 1398632760, i64 1543697535, i64 2419227174, i64 1676465074, i64 2882923045, i64 23216933, i64 808195649, i64 3690720147, i64 484419260, i64 2254772642, i64 2975434733, i64 288528113, i64 204598404, i64 589968818, i64 3021152400, i64 2463155141, i64 1397846755, i64 157285579, i64 4230258857, i64 2469135246, i64 625357422, i64 3435224647, i64 465239124, i64 1022535736, i64 2823317040, i64 274194469, i64 2214966446, i64 3661001613, i64 518802547, i64 2293436304, i64 1335881988, i64 2247010176, i64 1856732584, i64 1088028094, i64 1877563709, i64 1015352636, i64 1700817932, i64 2960695857, i64 1882229300, i64 1666906557, i64 1838841022, i64 3983797810, i64 1667630361, i64 385998221, i64 241341791, i64 403550441, i64 2629200403, i64 3552759102, i64 2029750442, i64 2247999048, i64 2726665298, i64 2507798776, i64 2419064129, i64 1266444923, i64 526255242, i64 2384866697, i64 1886200981, i64 3954956408, i64 2171436866, i64 2295200753, i64 1047315850, i64 1967809707, i64 2860382973, i64 3918334466, i64 3057439479, i64 952682588, i64 1925559679, i64 3112119050, i64 3833190964, i64 1430139895, i64 2089165610, i64 3009202424, i64 3989186157, i64 3395807230, i64 347600520, i64 120428923, i64 3017004655, i64 1384933954, i64 303039929, i64 234010146, i64 2278760249, i64 315514836, i64 3987659575, i64 1239335668, i64 2387869477, i64 3885908826, i64 1983922602, i64 698609264, i64 3009002846, i64 1520611399, i64 809159940, i64 3089771783, i64 374838722, i64 2789914419, i64 2500831937, i64 3751970335, i64 4279852547, i64 2362894437, i64 1588814060, i64 1671213155, i64 434218829, i64 2126587176, i64 2002526422, i64 2756464095, i64 141700479, i64 2965974322, i64 2211530172, i64 992085992, i64 1943691492, i64 2705131817, i64 2519208889, i64 1938768395, i64 3949294294, i64 354046666, i64 2158272751, i64 602858583, i64 0], align 16\n        ret ptr @pf\n"
  },
  {
    "path": "stdlib/numpy/random/pcg64.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .seed import SeedSequence\nfrom ..util import itrunc, zext\n\nu128 = UInt[128]\n\ndef pcg_int(hi, lo):\n    return (u128(hi) << u128(64)) | u128(lo)\n\nPCG_DEFAULT_MULTIPLIER_HIGH = u128(2549297995355413924)\nPCG_DEFAULT_MULTIPLIER_LOW  = u128(4865540595714422341)\nPCG_DEFAULT_MULTIPLIER_128  = pcg_int(PCG_DEFAULT_MULTIPLIER_HIGH,\n                                      PCG_DEFAULT_MULTIPLIER_LOW)\nPCG_DEFAULT_INCREMENT_128   = pcg_int(6364136223846793005,\n                                      1442695040888963407)\nPCG_STATE_SETSEQ_128_INITIALIZER = (\n    pcg_int(0x979c9a98d8462005, 0x7d3e9cb6cfe0549b),\n    pcg_int(1, 0xda3e39cb94b95bdb)\n)\nPCG_CHEAP_MULTIPLIER_128 = (u128(0xda942042) << u128(32)) | u128(0xe4dd58b5)\n\nPCG64_INITIALIZER = PCG_STATE_SETSEQ_128_INITIALIZER\n\ndef rotr64(value: u64, rot: int):\n    return (value >> u64(rot)) | (value << u64((-rot) & 63))\n\nclass PCG64:\n    state: u128\n    inc: u128\n    seed: SeedSequence\n\n    def __init__(self, initstate: u128, initseq: u128):\n        self.srandom_r(initstate, initseq)\n\n    def __init__(self, seed):\n        if not isinstance(seed, SeedSequence):\n            self.__init__(SeedSequence(seed))\n        else:\n            val = seed.generate_state(4, u64)\n            v0 = zext(val[0], u128)\n            v1 = zext(val[1], u128)\n            v2 = zext(val[2], u128)\n            v3 = zext(val[3], u128)\n\n            p = val.data\n            initstate = pcg_int(v0, v1)\n            initseq = pcg_int(v2, v3)\n            self.__init__(initstate, initseq)\n            self.seed = seed\n\n    def __get_state__(self):\n        return (self.state, self.inc)\n\n    def __set_state__(self, state):\n        s, i = state\n        self.state = s\n        self.inc = i\n\n    def setseq_128_step_r(self):\n        self.state = (self.state * PCG_DEFAULT_MULTIPLIER_128) + self.inc\n\n    def output_xsl_rr_128_64(self):\n        state = self.state\n        state_high = itrunc(state >> u128(64), u64)\n        state_low = itrunc(state, u64)\n        return rotr64(state_high ^ state_low, int(state >> u128(122)))\n\n    def setseq_128_xsl_rr_64_random_r(self):\n        self.setseq_128_step_r()\n        return self.output_xsl_rr_128_64()\n\n    def setseq_128_srandom_r(self, initstate: u128, initseq: u128):\n        self.state = u128(0)\n        self.inc = (initseq << u128(1)) | u128(1)\n        self.setseq_128_step_r()\n        self.state += initstate\n        self.setseq_128_step_r()\n\n    def advance_lcg_128(self, delta: u128, cur_mult: u128, cur_plus: u128):\n        acc_mult = u128(1)\n        acc_plus = u128(0)\n\n        while delta > u128(0):\n            if delta & u128(1):\n               acc_mult *= cur_mult\n               acc_plus = acc_plus * cur_mult + cur_plus\n            cur_plus = (cur_mult + u128(1)) * cur_plus\n            cur_mult *= cur_mult\n            delta //= u128(2)\n\n        self.state = acc_mult * self.state + acc_plus\n\n    def setseq_128_advance_r(self, delta: u128):\n        self.advance_lcg_128(delta, PCG_DEFAULT_MULTIPLIER_128, self.inc)\n\n    def random_r(self):\n        return self.setseq_128_xsl_rr_64_random_r()\n\n    def srandom_r(self, initstate: u128, initseq: u128):\n        return self.setseq_128_srandom_r(initstate, initseq)\n\n    def advance_r(self, delta: u128):\n        self.setseq_128_advance_r(delta)\n\n    def next64(self):\n        return self.random_r()\n\n    def pcg_advance(self, step: Tuple[u64, u64]):\n        delta = (u128(step[0]) << u128(64)) | u128(step[1])\n        self.advance_r(delta)\n\n    def advance(self, step: int):\n        self.pcg_advance((u64(0), u64(step)))\n\n    def jump_inplace(self, jumps: int):\n        step = ((u128(0x9e3779b9) << u128(96)) |\n                (u128(0x7f4a7c15) << u128(64)) |\n                (u128(0xf39cc060) << u128(32)) |\n                 u128(0x5cedc835))\n        self.advance_r(step * u128(jumps))\n\n    def set_seed(self, seed: Tuple[u64, u64], inc: Tuple[u64, u64]):\n        s = (u128(seed[0]) << u128(64)) | u128(seed[1])\n        i = (u128(inc[0]) << u128(64)) | u128(inc[1])\n        self.srandom_r(s, i)\n\nclass PCG64DXSM:\n    state: u128\n    inc: u128\n    seed: SeedSequence\n\n    def __init__(self, initstate: u128, initseq: u128):\n        # For some reason NumPy uses PCG64 seeding here...\n        # self.cm_srandom_r(initstate, initseq)\n        self.srandom_r(initstate, initseq)\n\n    def __init__(self, seed):\n        if not isinstance(seed, SeedSequence):\n            self.__init__(SeedSequence(seed))\n        else:\n            val = seed.generate_state(4, u64)\n            v0 = zext(val[0], u128)\n            v1 = zext(val[1], u128)\n            v2 = zext(val[2], u128)\n            v3 = zext(val[3], u128)\n\n            p = val.data\n            initstate = pcg_int(v0, v1)\n            initseq = pcg_int(v2, v3)\n            self.__init__(initstate, initseq)\n            self.seed = seed\n\n    def __get_state__(self):\n        return (self.state, self.inc)\n\n    def __set_state__(self, state):\n        s, i = state\n        self.state = s\n        self.inc = i\n\n    def cm_step_r(self):\n        self.state = (self.state * PCG_CHEAP_MULTIPLIER_128) + self.inc\n\n    def setseq_128_srandom_r(self, initstate: u128, initseq: u128):\n        self.state = u128(0)\n        self.inc = (initseq << u128(1)) | u128(1)\n        self.setseq_128_step_r()\n        self.state += initstate\n        self.setseq_128_step_r()\n\n    def srandom_r(self, initstate: u128, initseq: u128):\n        return self.setseq_128_srandom_r(initstate, initseq)\n\n    def setseq_128_step_r(self):\n        self.state = (self.state * PCG_DEFAULT_MULTIPLIER_128) + self.inc\n\n    def output_cm_128_64(self):\n        state = self.state\n        hi = itrunc(state >> u128(64), u64)\n        lo = itrunc(state, u64)\n        lo |= u64(1)\n        hi ^= hi >> u64(32)\n        hi *= u64(0xda942042e4dd58b5)\n        hi ^= hi >> u64(48)\n        hi *= lo\n        return hi\n\n    def cm_random_r(self):\n        ret = self.output_cm_128_64()\n        self.cm_step_r()\n        return ret\n\n    def cm_srandom_r(self, initstate: u128, initseq: u128):\n        self.state = u128(0)\n        self.inc = (initseq << u128(1)) | u128(1)\n        self.cm_step_r()\n        self.state += initstate\n        self.cm_step_r()\n\n    def advance_lcg_128(self, delta: u128, cur_mult: u128, cur_plus: u128):\n        acc_mult = u128(1)\n        acc_plus = u128(0)\n\n        while delta > u128(0):\n            if delta & u128(1):\n               acc_mult *= cur_mult\n               acc_plus = acc_plus * cur_mult + cur_plus\n            cur_plus = (cur_mult + u128(1)) * cur_plus\n            cur_mult *= cur_mult\n            delta //= u128(2)\n\n        self.state = acc_mult * self.state + acc_plus\n\n    def cm_advance_r(self, delta: u128):\n        self.advance_lcg_128(delta, pcg_int(0, PCG_CHEAP_MULTIPLIER_128), self.inc)\n\n    def next64(self):\n        return self.cm_random_r()\n\n    def pcg_advance(self, step: Tuple[u64, u64]):\n        delta = (u128(step[0]) << u128(64)) | u128(step[1])\n        self.cm_advance_r(delta)\n\n    def advance(self, step: int):\n        self.pcg_advance((u64(0), u64(step)))\n\n    def jump_inplace(self, jumps: int):\n        step = ((u128(0x9e3779b9) << u128(96)) |\n                (u128(0x7f4a7c15) << u128(64)) |\n                (u128(0xf39cc060) << u128(32)) |\n                 u128(0x5cedc835))\n        self.cm_advance_r(step * u128(jumps))\n\n    def set_seed(self, seed: Tuple[u64, u64], inc: Tuple[u64, u64]):\n        s = (u128(seed[0]) << u128(64)) | u128(seed[1])\n        i = (u128(inc[0]) << u128(64)) | u128(inc[1])\n        self.cm_srandom_r(s, i)\n"
  },
  {
    "path": "stdlib/numpy/random/philox.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .seed import SeedSequence\nfrom ..util import zext, itrunc\n\nu128 = UInt[128]\nPHILOX_BUFFER_SIZE: Literal[int] = 4\nphilox4x64_rounds: Literal[int] = 10\n\ndef mulhilo64(a: u64, b: u64):\n    product = zext(a, u128) * zext(b, u128)\n    return itrunc(product >> u128(64), u64), itrunc(product, u64)\n\ndef _philox4x64bumpkey(key: Tuple[u64, u64]):\n    v0 = key[0]\n    v1 = key[1]\n    v0 += (u64(0x9E3779B9) << u64(32)) | u64(0x7F4A7C15)\n    v1 += (u64(0xBB67AE85) << u64(32)) | u64(0x84CAA73B)\n    return (v0, v1)\n\ndef _philox4x64round(ctr: Tuple[u64, u64, u64, u64],\n                     key: Tuple[u64, u64]):\n    c0 = (u64(0xD2E7470E) << u64(32)) | u64(0xE14C6C93)\n    c1 = (u64(0xCA5A8263) << u64(32)) | u64(0x95121157)\n    hi0, lo0 = mulhilo64(c0, ctr[0])\n    hi1, lo1 = mulhilo64(c1, ctr[2])\n    return (hi1 ^ ctr[1] ^ key[0], lo1, hi0 ^ ctr[3] ^ key[1], lo0)\n\ndef philox4x64_R(R: int,\n                 ctr: Tuple[u64, u64, u64, u64],\n                 key: Tuple[u64, u64]):\n    if R > 0:\n        ctr = _philox4x64round(ctr, key)\n\n    if R > 1:\n        key = _philox4x64bumpkey(key)\n        ctr = _philox4x64round(ctr, key)\n\n    if R > 2:\n        key = _philox4x64bumpkey(key)\n        ctr = _philox4x64round(ctr, key)\n\n    if R > 3:\n        key = _philox4x64bumpkey(key)\n        ctr = _philox4x64round(ctr, key)\n\n    if R > 4:\n        key = _philox4x64bumpkey(key)\n        ctr = _philox4x64round(ctr, key)\n\n    if R > 5:\n        key = _philox4x64bumpkey(key)\n        ctr = _philox4x64round(ctr, key)\n\n    if R > 6:\n        key = _philox4x64bumpkey(key)\n        ctr = _philox4x64round(ctr, key)\n\n    if R > 7:\n        key = _philox4x64bumpkey(key)\n        ctr = _philox4x64round(ctr, key)\n\n    if R > 8:\n        key = _philox4x64bumpkey(key)\n        ctr = _philox4x64round(ctr, key)\n\n    if R > 9:\n        key = _philox4x64bumpkey(key)\n        ctr = _philox4x64round(ctr, key)\n\n    if R > 10:\n        key = _philox4x64bumpkey(key)\n        ctr = _philox4x64round(ctr, key)\n\n    if R > 11:\n        key = _philox4x64bumpkey(key)\n        ctr = _philox4x64round(ctr, key)\n\n    if R > 12:\n        key = _philox4x64bumpkey(key)\n        ctr = _philox4x64round(ctr, key)\n\n    if R > 13:\n        key = _philox4x64bumpkey(key)\n        ctr = _philox4x64round(ctr, key)\n\n    if R > 14:\n        key = _philox4x64bumpkey(key)\n        ctr = _philox4x64round(ctr, key)\n\n    if R > 15:\n        key = _philox4x64bumpkey(key)\n        ctr = _philox4x64round(ctr, key)\n\n    return ctr\n\nclass Philox:\n    ctr: Tuple[u64, u64, u64, u64]\n    key: Tuple[u64, u64]\n    buffer_pos: int\n    buffer: Tuple[u64, u64, u64, u64]\n    seed: SeedSequence\n\n    def __init__(self,\n                 ctr: Tuple[u64, u64, u64, u64],\n                 key: Tuple[u64, u64]):\n        self.ctr = ctr\n        self.key = key\n        self.buffer_pos = PHILOX_BUFFER_SIZE\n        self.buffer = (u64(0), u64(0), u64(0), u64(0))\n\n    def __init__(self, seed):\n        if not isinstance(seed, SeedSequence):\n            self.__init__(SeedSequence(seed))\n        else:\n            ctr = (u64(0), u64(0), u64(0), u64(0))\n            key = seed.generate_state(2, u64)\n            key = (key[0], key[1])\n            self.__init__(ctr, key)\n            self.seed = seed\n\n    def __get_state__(self):\n        return (self.ctr, self.key, self.buffer_pos, self.buffer)\n\n    def __set_state__(self, state):\n        ctr, key, buffer_pos, buffer = state\n        self.ctr = ctr\n        self.key = key\n        self.buffer_pos = buffer_pos\n        self.buffer = buffer\n\n    def next64(self):\n        out = u64(0)\n\n        if self.buffer_pos < PHILOX_BUFFER_SIZE:\n            buf = self.buffer\n            out = Ptr[u64](__ptr__(buf).as_byte())[self.buffer_pos]\n            self.buffer_pos += 1\n            return out\n\n        v0, v1, v2, v3 = self.ctr\n        v0 += u64(1)\n        if not v0:\n            v1 += u64(1)\n            if not v1:\n                v2 += u64(1)\n                if not v2:\n                    v3 += u64(1)\n\n        self.ctr = (v0, v1, v2, v3)\n        ct = philox4x64_R(philox4x64_rounds, self.ctr, self.key)\n        self.buffer = ct\n        self.buffer_pos = 1\n        return self.buffer[0]\n\n    def jump(self):\n        v0, v1, v2, v3 = self.ctr\n        v2 += u64(1)\n        if not v2:\n            v3 += 1\n        self.ctr = (v0, v1, v2, v3)\n\n    def advance(self, step: Tuple[u64, u64, u64, u64]):\n        v0, v1, v2, v3 = self.ctr\n        carry = False\n\n        v0 += u64(1)\n        carry = not v0\n        v_orig = v0\n        v0 += step[0]\n        if v0 < v_orig and not carry:\n            carry = True\n\n        if carry:\n            v1 += u64(1)\n            carry = not v1\n        v_orig = v1\n        v1 += step[1]\n        if v1 < v_orig and not carry:\n            carry = True\n\n        if carry:\n            v2 += u64(1)\n            carry = not v2\n        v_orig = v2\n        v2 += step[2]\n        if v2 < v_orig and not carry:\n            carry = True\n\n        if carry:\n            v3 += u64(1)\n            carry = not v3\n        v3 += step[3]\n\n        self.ctr = (v0, v1, v2, v3)\n\n    def jump_inplace(self, jumps: int):\n        self.advance((u64(0), u64(jumps), u64(0), u64(0)))\n"
  },
  {
    "path": "stdlib/numpy/random/seed.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom ..routines import *\n\nDEFAULT_POOL_SIZE = 4\nINIT_A            = u32(0x43b0d7e5)\nMULT_A            = u32(0x931e8875)\nINIT_B            = u32(0x8b51f9dd)\nMULT_B            = u32(0x58f38ded)\nMIX_MULT_L        = u32(0xca01f9dd)\nMIX_MULT_R        = u32(0x4973f715)\nXSHIFT            = u32(4 * 8 // 2)\nMASK32            = u32(0xFFFFFFFF)\n\ndef _int_to_uint32_array(n: int):\n    if n < 0:\n        raise ValueError(\"expected non-negative integer\")\n    elif n < int(MASK32):\n        return array((u32(n),))\n    else:\n        return array((u32(n & 0xFFFFFFFF), u32(n >> 32)))\n\ndef _coerce_to_uint32_array(x):\n    if isinstance(x, ndarray):\n        if type(x.data[0]) is u32:\n            return x.copy()\n\n    if isinstance(x, str):\n        if x.startswith('0x'):\n            return _int_to_uint32_array(int(x, base=16))\n        elif x.isdigit():\n            return _int_to_uint32_array(int(x))\n        else:\n            raise ValueError(\"unrecognized seed string\")\n\n    if isinstance(x, int):\n        return _int_to_uint32_array(x)\n\n    if isinstance(x, float):\n        compile_error(\"seed must be integer\")\n\n    if len(x) == 0:\n        return empty(0, dtype=u32)\n    subseqs = [_coerce_to_uint32_array(v) for v in x]\n    return concatenate(subseqs)\n\ndef hashmix(value: u32, hash_const: u32):\n    value ^= hash_const\n    hash_const *= MULT_A\n    value *= hash_const\n    value ^= value >> XSHIFT\n    return value, hash_const\n\ndef mix(x: u32, y: u32):\n    result = (MIX_MULT_L * x - MIX_MULT_R * y)\n    result ^= result >> XSHIFT\n    return result\n\n# TODO: use NumPy's default seeding scheme,\n#       which falls back to Python's os.urandom()\ndef random_seed_time_pid():\n    now = _C.seq_time() * 1000\n    key = empty((5,), dtype=u32)\n    key[0] = u32(now & 0xFFFFFFFF)\n    key[1] = u32(now >> 32)\n    key[2] = u32(_C.seq_pid())\n    now = _C.seq_time_monotonic()\n    key[3] = u32(now & 0xFFFFFFFF)\n    key[4] = u32(now >> 32)\n    return key\n\nclass SeedSequence:\n    entropy: ndarray[u32, 1]\n    _spawn_key: Optional[List[int]]\n    pool_size: int\n    n_children_spawned: int\n    pool: ndarray[u32, 1]\n\n    @property\n    def spawn_key(self) -> List[int]:\n        if self._spawn_key is not None:\n            return self._spawn_key.copy()\n        else:\n            return []\n\n    def __init__(self,\n                 entropy = None,\n                 spawn_key: Optional[List[int]] = None,\n                 pool_size: int = DEFAULT_POOL_SIZE,\n                 n_children_spawned: int = 0):\n        if pool_size < DEFAULT_POOL_SIZE:\n            raise ValueError(f\"The size of the entropy pool should be at least {DEFAULT_POOL_SIZE}\")\n\n        if entropy is None:\n            run_entropy = random_seed_time_pid()\n        else:\n            run_entropy = _coerce_to_uint32_array(entropy)\n\n        spawn_entropy = _coerce_to_uint32_array(spawn_key) if spawn_key is not None else empty(0, dtype=u32)\n\n        # assemble entropy\n        if len(spawn_entropy) > 0 and len(run_entropy) < pool_size:\n            diff = pool_size - len(run_entropy)\n            run_entropy = concatenate((run_entropy, zeros(diff, dtype=u32)))\n        entropy_array = concatenate((run_entropy, spawn_entropy))\n\n        self.entropy = entropy_array  # standard NumPy stores original entropy, but much easier to just store array\n        self._spawn_key = spawn_key\n        self.pool_size = pool_size\n        self.n_children_spawned = n_children_spawned\n        self.pool = zeros(pool_size, dtype=u32)\n\n        # mix entropy\n        mixer = self.pool\n        hash_const = INIT_A\n        for i in range(len(mixer)):\n            if i < len(entropy_array):\n                mixer[i], hash_const = hashmix(entropy_array[i], hash_const)\n            else:\n                mixer[i], hash_const = hashmix(u32(0), hash_const)\n\n        for i_src in range(len(mixer)):\n            for i_dst in range(len(mixer)):\n                if i_src != i_dst:\n                    value, hash_const = hashmix(mixer[i_src], hash_const)\n                    mixer[i_dst] = mix(mixer[i_dst], value)\n\n        for i_src in range(len(mixer), len(entropy_array)):\n            for i_dst in range(len(mixer)):\n                value, hash_const = hashmix(entropy_array[i_src], hash_const)\n                mixer[i_dst] = mix(mixer[i_dst], value)\n\n    def generate_state(self, n_words: int, dtype: type = u32):\n        hash_const = INIT_B\n        if dtype is u64:\n            n_words *= 2\n        elif dtype is not u32:\n            compile_error(\"only support uint32 or uint64\")\n\n        state = zeros(n_words, dtype=u32)\n        pool = self.pool\n        npool = len(self.pool)\n\n        for i_dst in range(n_words):\n            data_val = pool[i_dst % npool]\n            data_val ^= hash_const\n            hash_const *= MULT_B\n            data_val *= hash_const\n            data_val ^= data_val >> XSHIFT\n            state[i_dst] = data_val\n\n        if dtype is u64:\n            return ndarray((n_words // 2,), Ptr[u64](state.data.as_byte()))\n        else:\n            return state\n\n    def spawn(self, n_children: int):\n        seqs = []\n        sk: List[int] = self._spawn_key if self._spawn_key is not None else []\n        for i in range(self.n_children_spawned,\n                       self.n_children_spawned + n_children):\n            seqs.append(SeedSequence(\n                self.entropy,\n                spawn_key=(sk + [i]),\n                pool_size=self.pool_size\n            ))\n        return seqs\n\n"
  },
  {
    "path": "stdlib/numpy/random/sfc64.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .seed import SeedSequence\n\ndef rotl(value: u64, rot: int):\n    return (value << u64(rot)) | (value >> u64((-rot) & 63))\n\nclass SFC64:\n    s0: u64\n    s1: u64\n    s2: u64\n    s3: u64\n    seed: SeedSequence\n\n    def __init__(self, s0: u64, s1: u64, s2: u64):\n        self.s0 = s0\n        self.s1 = s1\n        self.s2 = s2\n        self.s3 = u64(1)\n\n        for _ in range(12):\n            self.next64()\n\n    def __init__(self, seed):\n        if not isinstance(seed, SeedSequence):\n            self.__init__(SeedSequence(seed))\n        else:\n            val = seed.generate_state(3, u64)\n            self.__init__(val[0], val[1], val[2])\n            self.seed = seed\n\n    def __get_state__(self):\n        return (self.s0, self.s1, self.s2, self.s3)\n\n    def __set_state__(self, state):\n        s0, s1, s2, s3 = state\n        self.s0 = s0\n        self.s1 = s1\n        self.s2 = s2\n        self.s3 = s3\n\n    def next64(self):\n        s0 = self.s0\n        s1 = self.s1\n        s2 = self.s2\n        s3 = self.s3\n\n        tmp = s0 + s1 + s3\n\n        self.s0 = s1 ^ (s1 >> u64(11))\n        self.s1 = s2 + (s2 << u64(3))\n        self.s2 = rotl(s2, 24) + tmp\n        self.s3 += u64(1)\n\n        return tmp\n"
  },
  {
    "path": "stdlib/numpy/random/splitmix64.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nclass Splitmix64:\n    state: u64\n\n    def __init__(self, state: u64):\n        self.state = state\n\n    def __init__(self, state: int = 0):\n        self.state = u64(state)\n\n    def __get_state__(self):\n        return (self.state,)\n\n    def __set_state__(self, state):\n        self.state = state[0]\n\n    def next64(self):\n        c0 = (u64(0x9e3779b9) << u64(32)) | u64(0x7f4a7c15)\n        c1 = (u64(0xbf58476d) << u64(32)) | u64(0x1ce4e5b9)\n        c2 = (u64(0x94d049bb) << u64(32)) | u64(0x133111eb)\n        self.state += c0\n        z = self.state\n        z = (z ^ (z >> u64(30))) * c1\n        z = (z ^ (z >> u64(27))) * c2\n        return z ^ (z >> u64(31))\n"
  },
  {
    "path": "stdlib/numpy/random/ziggurat.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n@pure\n@llvm\ndef ki_double(idx: int) -> u64:\n    @data = private unnamed_addr constant [256 x i64] [i64 4208095142473578, i64 0, i64 3387314423973544, i64 3838760076542274, i64 4030768804392682, i64 4136731738896254, i64 4203757248105145, i64 4249917568205994, i64 4283617341590296, i64 4309289223136604, i64 4329489775174550, i64 4345795907393188, i64 4359232558744730, i64 4370494503737299, i64 4380069246215646, i64 4388308869042394, i64 4395473957549321, i64 4401761481783924, i64 4407323076021240, i64 4412277362218204, i64 4416718463613199, i64 4420722014516422, i64 4424349484777079, i64 4427651345409294, i64 4430669422005229, i64 4433438668975191, i64 4435988524278344, i64 4438343955930065, i64 4440526279077425, i64 4442553800234660, i64 4444442329865861, i64 4446205593658138, i64 4447855565093316, i64 4449402736340121, i64 4450856340408624, i64 4452224534496486, i64 4453514552210512, i64 4454732830656798, i64 4455885117109368, i64 4456976558985043, i64 4458011780094444, i64 4458994945550386, i64 4459929817254120, i64 4460819801517196, i64 4461667990089170, i64 4462477195632268, i64 4463249982500384, i64 4463988693531856, i64 4464695473445501, i64 4465372289331869, i64 4466020948651920, i64 4466643115089764, i64 4467240322552142, i64 4467813987562542, i64 4468365420260672, i64 4468895834186994, i64 4469406355006040, i64 4469898028300364, i64 4470371826548633, i64 4470828655385770, i64 4471269359229841, i64 4471694726349190, i64 4472105493433674, i64 4472502349725738, i64 4472885940759935, i64 4473256871753524, i64 4473615710685532, i64 4473962991097124, i64 4474299214642296, i64 4474624853414418, i64 4474940352071305, i64 4475246129778808, i64 4475542581990776, i64 4475830082081194, i64 4476108982842610, i64 4476379617863426, i64 4476642302795321, i64 4476897336520866, i64 4477145002230339, i64 4477385568415884, i64 4477619289790266, i64 4477846408136804, i64 4478067153096380, i64 4478281742896886, i64 4478490385029917, i64 4478693276879082, i64 4478890606303906, i64 4479082552182886, i64 4479269284918997, i64 4479450966910588, i64 4479627752990372, i64 4479799790834988, i64 4479967221347354, i64 4480130179013872, i64 4480288792238368, i64 4480443183654460, i64 4480593470417939, i64 4480739764480586, i64 4480882172846772, i64 4481020797814010, i64 4481155737198612, i64 4481287084547452, i64 4481414929336784, i64 4481539357158974, i64 4481660449897960, i64 4481778285894165, i64 4481892940099539, i64 4482004484223382, i64 4482112986869492, i64 4482218513665204, i64 4482321127382802, i64 4482420888053758, i64 4482517853076245, i64 4482612077316275, i64 4482703613202871, i64 4482792510817576, i64 4482878817978627, i64 4482962580320076, i64 4483043841366126, i64 4483122642600925, i64 4483199023534056, i64 4483273021761922, i64 4483344673025224, i64 4483414011262724, i64 4483481068661428, i64 4483545875703378, i64 4483608461209170, i64 4483668852378323, i64 4483727074826624, i64 4483783152620564, i64 4483837108308932, i64 4483888962951686, i64 4483938736146144, i64 4483986446050596, i64 4484032109405372, i64 4484075741551420, i64 4484117356446452, i64 4484156966678662, i64 4484194583478081, i64 4484230216725550, i64 4484263874959345, i64 4484295565379450, i64 4484325293849474, i64 4484353064896186, i64 4484378881706674, i64 4484402746123075, i64 4484424658634833, i64 4484444618368474, i64 4484462623074794, i64 4484478669113436, i64 4484492751434740, i64 4484504863558830, i64 4484514997551788, i64 4484523143998833, i64 4484529291974394, i64 4484533429008906, i64 4484535541052219, i64 4484535612433424, i64 4484533625816926, i64 4484529562154580, i64 4484523400633636, i64 4484515118620291, i64 4484504691598554, i64 4484492093104164, i64 4484477294653230, i64 4484460265665252, i64 4484440973380154, i64 4484419382768918, i64 4484395456437370, i64 4484369154522621, i64 4484340434581640, i64 4484309251471359, i64 4484275557219678, i64 4484239300886654, i64 4484200428415112, i64 4484158882469814, i64 4484114602264271, i64 4484067523374160, i64 4484017577536216, i64 4483964692431365, i64 4483908791450714, i64 4483849793442887, i64 4483787612441036, i64 4483722157367660, i64 4483653331715198, i64 4483581033200083, i64 4483505153387764, i64 4483425577285833, i64 4483342182902157, i64 4483254840764470, i64 4483163413397547, i64 4483067754753536, i64 4482967709590562, i64 4482863112794072, i64 4482753788634692, i64 4482639549955636, i64 4482520197281720, i64 4482395517841076, i64 4482265284489409, i64 4482129254525304, i64 4481987168383486, i64 4481838748191074, i64 4481683696169781, i64 4481521692864464, i64 4481352395175570, i64 4481175434169564, i64 4480990412637506, i64 4480796902367134, i64 4480594441088331, i64 4480382529045225, i64 4480160625140311, i64 4479928142586662, i64 4479684443993061, i64 4479428835793398, i64 4479160561915451, i64 4478878796564388, i64 4478582635972392, i64 4478271088936406, i64 4477943065929958, i64 4477597366530538, i64 4477232664848704, i64 4476847492576192, i64 4476440219183781, i64 4476009028690434, i64 4475551892286424, i64 4475066535915646, i64 4474550401693506, i64 4474000601739904, i64 4473413862618200, i64 4472786458058295, i64 4472114126959004, i64 4471391972746494, i64 4470614338917719, i64 4469774653883156, i64 4468865235838896, i64 4467877045039530, i64 4466799366045354, i64 4465619395558397, i64 4464321701199635, i64 4462887501169282, i64 4461293691124341, i64 4459511507635972, i64 4457504658253067, i64 4455226650325010, i64 4452616884242348, i64 4449594783440798, i64 4446050695647666, i64 4441831266659618, i64 4436714892174061, i64 4430368316897338, i64 4422264825074740, i64 4411517007702132, i64 4396496531309976, i64 4373832704204284, i64 4335125104963628, i64 4251099761679434], align 16\n    %p = getelementptr inbounds [256 x i64], ptr @data, i64 0, i64 %idx\n    %x = load i64, ptr %p, align 8\n    ret i64 %x\n\n@pure\n@llvm\ndef wi_double(idx: int) -> float:\n    @data = private unnamed_addr constant [256 x double] [double 0x3CCF493B7815D979, double 0x3C8B8D0BE3FDF6C6, double 0x3C9250AF3C2C5BB4, double 0x3C957CB938443B61, double 0x3C9801FCE82FA70C, double 0x3C9A230C2E4CD0BC, double 0x3C9C004D2F3861F7, double 0x3C9DAC2F5A747274, double 0x3C9F32482D4CD5C3, double 0x3CA04D32278EBBAD, double 0x3CA0F5053B025D43, double 0x3CA192A697413677, double 0x3CA227A28F7A1AF5, double 0x3CA2B52E3863D880, double 0x3CA33C3FC05791F5, double 0x3CA3BD9EC1A2B12F, double 0x3CA439EF8DFF9B55, double 0x3CA4B1BB363DFEA7, double 0x3CA52575621AD374, double 0x3CA59580A707CE96, double 0x3CA60231CFD97EEA, double 0x3CA66BD261A37C3D, double 0x3CA6D2A292000570, double 0x3CA736DAD346F8A6, double 0x3CA798AD10B32A77, double 0x3CA7F845AD46F543, double 0x3CA855CC53430A77, double 0x3CA8B1649E7B769A, double 0x3CA90B2EA94ECF98, double 0x3CA96347822C1EEA, double 0x3CA9B9C98E38C546, double 0x3CAA0ECCDCA4A72C, double 0x3CAA62676D77CD59, double 0x3CAAB4AD6E101630, double 0x3CAB05B16D136C9C, double 0x3CAB558487427A29, double 0x3CABA4368E529F3A, double 0x3CABF1D62ABF8232, double 0x3CAC3E70F9594EF3, double 0x3CAC8A13A5323B61, double 0x3CACD4C9FE72268B, double 0x3CAD1E9F0E80B748, double 0x3CAD679D29E41F10, double 0x3CADAFCE0023B8C3, double 0x3CADF73AA9F17653, double 0x3CAE3DEBB5D2EDFE, double 0x3CAE83E9337A6F00, double 0x3CAEC93ABDF982CE, double 0x3CAF0DE784F06226, double 0x3CAF51F654D8F688, double 0x3CAF956D9E87D7AE, double 0x3CAFD8537DFA2EAC, double 0x3CB00D56E04234EC, double 0x3CB02E40F5398F9A, double 0x3CB04EEA9E16A5FC, double 0x3CB06F565B72A010, double 0x3CB08F869071F40B, double 0x3CB0AF7D84BC6113, double 0x3CB0CF3D664BCC7F, double 0x3CB0EEC84B16086B, double 0x3CB10E20329515EE, double 0x3CB12D4707310FBE, double 0x3CB14C3E9F8E9141, double 0x3CB16B08BFC4201E, double 0x3CB189A71A78DA34, double 0x3CB1A81B51EE6D88, double 0x3CB1C666F8F82ACB, double 0x3CB1E48B93E0D42E, double 0x3CB2028A9940A09F, double 0x3CB2206572C4C6E9, double 0x3CB23E1D7DE9C31F, double 0x3CB25BB40CA96BFB, double 0x3CB2792A661DD37F, double 0x3CB29681C719D71B, double 0x3CB2B3BB62B82EDA, double 0x3CB2D0D862E1B853, double 0x3CB2EDD9E8CBA98E, double 0x3CB30AC10D6E48D7, double 0x3CB3278EE1F4B930, double 0x3CB3444470265EA1, double 0x3CB360E2BACA52D5, double 0x3CB37D6ABE05586A, double 0x3CB399DD6FB2B264, double 0x3CB3B63BBFB83D03, double 0x3CB3D28698561DE0, double 0x3CB3EEBEDE725A83, double 0x3CB40AE571E09E74, double 0x3CB426FB2DA6745D, double 0x3CB44300E83C30A4, double 0x3CB45EF773CAC75D, double 0x3CB47ADF9E66C336, double 0x3CB496BA32488F2F, double 0x3CB4B287F602415D, double 0x3CB4CE49ACB311DC, double 0x3CB4EA001638A605, double 0x3CB505ABEF5E5562, double 0x3CB5214DF20A8B5A, double 0x3CB53CE6D56A664F, double 0x3CB558774E1BB2C8, double 0x3CB574000E555F78, double 0x3CB58F81C60E8514, double 0x3CB5AAFD23241B59, double 0x3CB5C672D17D733D, double 0x3CB5E1E37B2F8CD3, double 0x3CB5FD4FC89F5E38, double 0x3CB618B860A31FC3, double 0x3CB6341DE8A2B0A2, double 0x3CB64F8104B7260B, double 0x3CB66AE257C99672, double 0x3CB6864283B13137, double 0x3CB6A1A22950B2B1, double 0x3CB6BD01E8B343BB, double 0x3CB6D8626128D352, double 0x3CB6F3C43161F854, double 0x3CB70F27F78B68EB, double 0x3CB72A8E516914C6, double 0x3CB745F7DC70EEDC, double 0x3CB7616535E5731F, double 0x3CB77CD6FAEFF449, double 0x3CB7984DC8BABD93, double 0x3CB7B3CA3C8B1409, double 0x3CB7CF4CF3DB22FB, double 0x3CB7EAD68C73DEE7, double 0x3CB80667A486EA1F, double 0x3CB82200DAC88676, double 0x3CB83DA2CE899F15, double 0x3CB8594E1FD1F5BD, double 0x3CB875036F7A7EC5, double 0x3CB890C35F47F72D, double 0x3CB8AC8E9205C043, double 0x3CB8C865ABA10C9C, double 0x3CB8E44951446A27, double 0x3CB9003A2973B58F, double 0x3CB91C38DC288347, double 0x3CB9384612EF0AFC, double 0x3CB954627903A28A, double 0x3CB9708EBB70D5EE, double 0x3CB98CCB892E2A31, double 0x3CB9A919933F99BF, double 0x3CB9C5798CD5D92C, double 0x3CB9E1EC2B6F7411, double 0x3CB9FE7226FAD24A, double 0x3CBA1B0C39F93692, double 0x3CBA37BB21A2C85B, double 0x3CBA547F9E0BBB88, double 0x3CBA715A724AA9A4, double 0x3CBA8E4C64A0313D, double 0x3CBAAB563E9FF108, double 0x3CBAC878CD5AF5CE, double 0x3CBAE5B4E18BB336, double 0x3CBB030B4FC3A11A, double 0x3CBB207CF09A985B, double 0x3CBB3E0AA0E00C00, double 0x3CBB5BB541CE3D03, double 0x3CBB797DB93F8927, double 0x3CBB9764F1E5F73C, double 0x3CBBB56BDB85256E, double 0x3CBBD3936B2EC0A2, double 0x3CBBF1DC9B81AE83, double 0x3CBC10486CEC16A0, double 0x3CBC2ED7E5F07A2D, double 0x3CBC4D8C136E0D1C, double 0x3CBC6C6608EC8705, double 0x3CBC8B66E0EBA617, double 0x3CBCAA8FBD36A2AB, double 0x3CBCC9E1C73BD690, double 0x3CBCE95E3068E037, double 0x3CBD0906328B8F6E, double 0x3CBD28DB1037EF20, double 0x3CBD48DE1533C647, double 0x3CBD691096E7F123, double 0x3CBD8973F4D7FBA5, double 0x3CBDAA0999206E70, double 0x3CBDCAD2F8FC490E, double 0x3CBDEBD195522E37, double 0x3CBE0D06FB49D21C, double 0x3CBE2E74C4EA46F6, double 0x3CBE501C99C1D188, double 0x3CBE72002F97FE25, double 0x3CBE94214B2ABF0A, double 0x3CBEB681C0F76F08, double 0x3CBED9237610A73A, double 0x3CBEFC086101ECA9, double 0x3CBF1F328AC25321, double 0x3CBF42A40FB74D6D, double 0x3CBF665F20C90168, double 0x3CBF8A6604899782, double 0x3CBFAEBB187122BF, double 0x3CBFD360D22FE785, double 0x3CBFF859C118F60B, double 0x3CC00ED447D3A075, double 0x3CC021A8028FC947, double 0x3CC034A983A902AB, double 0x3CC047DA4E3EF5C7, double 0x3CC05B3BF6ADB37E, double 0x3CC06ED023A72668, double 0x3CC082988F632E17, double 0x3CC0969708E8A254, double 0x3CC0AACD7571C0C4, double 0x3CC0BF3DD1EED448, double 0x3CC0D3EA34AA3D30, double 0x3CC0E8D4CF116593, double 0x3CC0FDFFEFA69FB6, double 0x3CC1136E04207041, double 0x3CC129219BBB5D35, double 0x3CC13F1D69C4096D, double 0x3CC1556448602E3B, double 0x3CC16BF93B9DEEF3, double 0x3CC182DF74D21261, double 0x3CC19A1A564EEBAC, double 0x3CC1B1AD777F2F8E, double 0x3CC1C99CA971A694, double 0x3CC1E1EBFBE4AE39, double 0x3CC1FA9FC2E2D901, double 0x3CC213BC9D04CC81, double 0x3CC22D477A6FD3EE, double 0x3CC24745A4AC9C24, double 0x3CC261BCC77658E0, double 0x3CC27CB2FAA8592E, double 0x3CC2982ECD770E78, double 0x3CC2B437532A0A52, double 0x3CC2D0D43196DB97, double 0x3CC2EE0DB1A978F5, double 0x3CC30BECD256AEEE, double 0x3CC32A7B5E68A4A3, double 0x3CC349C405AE12A3, double 0x3CC369D27A33A840, double 0x3CC38AB39256410A, double 0x3CC3AC7570AE88FA, double 0x3CC3CF27B31704A6, double 0x3CC3F2DBAA60F475, double 0x3CC417A49CB9E5DA, double 0x3CC43D9815545E94, double 0x3CC464CE44A73A15, double 0x3CC48D62759C43BC, double 0x3CC4B7739D6B5A27, double 0x3CC4E3250DCD8902, double 0x3CC5109F53E9AC41, double 0x3CC54011523A7E42, double 0x3CC571B1A94AE41B, double 0x3CC5A5C08B718DD9, double 0x3CC5DC8A243AD0FE, double 0x3CC61669CF861E4C, double 0x3CC653CE7B006AEA, double 0x3CC69540BE9FE5C3, double 0x3CC6DB6B8D09E232, double 0x3CC72728F05F7A34, double 0x3CC7799556090673, double 0x3CC7D42DF4D6CE8C, double 0x3CC839030529F234, double 0x3CC8AB0FBFAA7C14, double 0x3CC92EE0946F4496, double 0x3CC9CBEE014057AB, double 0x3CCA8FDC7894775A, double 0x3CCB981F3878FDB1, double 0x3CCD3BB48209AD33], align 16\n    %p = getelementptr inbounds [256 x double], ptr @data, i64 0, i64 %idx\n    %x = load double, ptr %p, align 8\n    ret double %x\n\n@pure\n@llvm\ndef fi_double(idx: int) -> float:\n    @data = private unnamed_addr constant [256 x double] [double 1.000000e+00, double 0x3FEF446AC979F087, double 0x3FEEB7545B6CA915, double 0x3FEE3F11E027F077, double 0x3FEDD36FA704DE95, double 0x3FED70920657BCF2, double 0x3FED144978A119DC, double 0x3FECBD33A8A72DEB, double 0x3FEC6A5ECEA9787F, double 0x3FEC1B1CD9EEBAEA, double 0x3FEBCEEB4EE1DC82, double 0x3FEB85653A8FF552, double 0x3FEB3E3A8234DD10, double 0x3FEAF92A3F6CE8A2, double 0x3FEAB5FEF17A2504, double 0x3FEA748BD550C9E1, double 0x3FEA34AAFDF5AF0F, double 0x3FE9F63BEE651FD8, double 0x3FE9B9228D240681, double 0x3FE97D4657617AC1, double 0x3FE94291C21B7A47, double 0x3FE908F1BD31714F, double 0x3FE8D0554FE60AA8, double 0x3FE898AD48BADF02, double 0x3FE861EBFC37BCAC, double 0x3FE82C050F56CF6E, double 0x3FE7F6ED4B20E2CB, double 0x3FE7C29A779C6858, double 0x3FE78F033CA0B0D5, double 0x3FE75C1F0770D856, double 0x3FE729E5F43F6D12, double 0x3FE6F850BAEA7AEE, double 0x3FE6C7589E635A89, double 0x3FE696F75E513B2A, double 0x3FE667272A92E323, double 0x3FE637E298550C18, double 0x3FE6092498802665, double 0x3FE5DAE86F4AFF6A, double 0x3FE5AD29ACC85C89, double 0x3FE57FE4264C8D8F, double 0x3FE55313F08D9E46, double 0x3FE526B55A656CD5, double 0x3FE4FAC4E820B667, double 0x3FE4CF3F4F494EC0, double 0x3FE4A42172DC5278, double 0x3FE479685FDF5012, double 0x3FE44F114A493679, double 0x3FE425198A355FE3, double 0x3FE3FB7E99585B82, double 0x3FE3D23E10AF31A3, double 0x3FE3A955A662CD0E, double 0x3FE380C32BDA00D5, double 0x3FE358848BF550E9, double 0x3FE33097C9703A35, double 0x3FE308FAFD6438EF, double 0x3FE2E1AC55EA3BEE, double 0x3FE2BAAA14D7954A, double 0x3FE293F28E93CD15, double 0x3FE26D84290504ED, double 0x3FE2475D5A90DB84, double 0x3FE2217CA92FF7F2, double 0x3FE1FBE0A9929620, double 0x3FE1D687FE549969, double 0x3FE1B171573FD111, double 0x3FE18C9B709B3C50, double 0x3FE16805128639DA, double 0x3FE143AD105EA99C, double 0x3FE11F9248311F38, double 0x3FE0FBB3A2325913, double 0x3FE0D810104142A0, double 0x3FE0B4A68D70D9AE, double 0x3FE091761D995D81, double 0x3FE06E7DCCF03C36, double 0x3FE04BBCAFA63F2E, double 0x3FE02931E18B822A, double 0x3FE006DC85B8CAC4, double 0x3FDFC9778C7BBDA1, double 0x3FDF859DA7A900CA, double 0x3FDF4229CB2F7AF3, double 0x3FDEFF1A717E8F95, double 0x3FDEBC6E20BD1F54, double 0x3FDE7A236A4EC3C5, double 0x3FDE3838EA5F9B85, double 0x3FDDF6AD47763A09, double 0x3FDDB57F320B56B1, double 0x3FDD74AD6426DE33, double 0x3FDD3436A1021080, double 0x3FDCF419B4AE5B6D, double 0x3FDCB45573C0A848, double 0x3FDC74E8BB00D7C7, double 0x3FDC35D26F1D2CB8, double 0x3FDBF7117C616A17, double 0x3FDBB8A4D6716D91, double 0x3FDB7A8B7807131B, double 0x3FDB3CC462B331CA, double 0x3FDAFF4E9EA18552, double 0x3FDAC2293A5F5A9E, double 0x3FDA85534AA4D880, double 0x3FDA48CBEA20C04D, double 0x3FDA0C923946843E, double 0x3FD9D0A55E1E93DF, double 0x3FD995048418C0C6, double 0x3FD959AEDBE09F93, double 0x3FD91EA39B33CB17, double 0x3FD8E3E1FCB9F115, double 0x3FD8A9693FDE9188, double 0x3FD86F38A8AC5AB6, double 0x3FD8354F7FAA0DD9, double 0x3FD7FBAD11B8D911, double 0x3FD7C250AFF414B0, double 0x3FD78939AF9252EB, double 0x3FD7506769C7B1ED, double 0x3FD717D93BA9614C, double 0x3FD6DF8E86124CAA, double 0x3FD6A786AD88DE21, double 0x3FD66FC11A25CBE2, double 0x3FD6383D377BE515, double 0x3FD600FA7480D2C8, double 0x3FD5C9F84376C244, double 0x3FD5933619D6EEBE, double 0x3FD55CB3703D0100, double 0x3FD5266FC2533BED, double 0x3FD4F06A8EBF6D92, double 0x3FD4BAA357109CA2, double 0x3FD485199FAD6AD4, double 0x3FD44FCCEFC324FE, double 0x3FD41ABCD1357A19, double 0x3FD3E5E8D08ED2DB, double 0x3FD3B1507CF143AE, double 0x3FD37CF368081379, double 0x3FD348D125F9D19E, double 0x3FD314E94D5AF62F, double 0x3FD2E13B77210766, double 0x3FD2ADC73E963FDD, double 0x3FD27A8C414DB11E, double 0x3FD2478A1F17DE89, double 0x3FD214C079F7CC9E, double 0x3FD1E22EF6188116, double 0x3FD1AFD539C2F050, double 0x3FD17DB2ED5454E8, double 0x3FD14BC7BB34EE67, double 0x3FD11A134FCF2423, double 0x3FD0E895598709C4, double 0x3FD0B74D88B242DA, double 0x3FD0863B8F904336, double 0x3FD0555F2242E9D9, double 0x3FD024B7F6C7747E, double 0x3FCFE88B89DF93C5, double 0x3FCF88108CB83235, double 0x3FCF27FE6CE998D2, double 0x3FCEC854A4C99C44, double 0x3FCE6912B2283CDD, double 0x3FCE0A3816457184, double 0x3FCDABC455C7900A, double 0x3FCD4DB6F8B2514F, double 0x3FCCF00F8A5E6FCC, double 0x3FCC92CD9971DF53, double 0x3FCC35F0B7D89D47, double 0x3FCBD9787ABE18A1, double 0x3FCB7D647A8731AA, double 0x3FCB21B452CCD13A, double 0x3FCAC667A2571807, double 0x3FCA6B7E0B19267E, double 0x3FCA10F7322D7E3D, double 0x3FC9B6D2BFD2FE5A, double 0x3FC95D105F6A7C27, double 0x3FC903AFBF74FA69, double 0x3FC8AAB09192815B, double 0x3FC852128A819A38, double 0x3FC7F9D5621F7175, double 0x3FC7A1F8D368A323, double 0x3FC74A7C9C7AB5A6, double 0x3FC6F3607E964716, double 0x3FC69CA43E21F25C, double 0x3FC64647A2ADF19C, double 0x3FC5F04A76F883F9, double 0x3FC59AAC88F31D6C, double 0x3FC5456DA9C86835, double 0x3FC4F08DADE31FC1, double 0x3FC49C0C6CF5CE2D, double 0x3FC447E9C20375D5, double 0x3FC3F4258B6931AE, double 0x3FC3A0BFAAE8D7EE, double 0x3FC34DB805B4AB88, double 0x3FC2FB0E847C2A65, double 0x3FC2A8C3137A071A, double 0x3FC256D5A2835EB7, double 0x3FC2054625183C34, double 0x3FC1B41492757D42, double 0x3FC16340E5A82D63, double 0x3FC112CB1DA26EB9, double 0x3FC0C2B33D5209BA, double 0x3FC072F94BB8BF85, double 0x3FC0239D54067D2A, double 0x3FBFA93ECB6B222C, double 0x3FBF0BFF29520E1C, double 0x3FBE6F7BF29AA54B, double 0x3FBDD3B56176E88F, double 0x3FBD38ABB9BD91E5, double 0x3FBC9E5F493B740A, double 0x3FBC04D0680B1015, double 0x3FBB6BFF78F2E233, double 0x3FBAD3ECE9CAF633, double 0x3FBA3C9933EA6286, double 0x3FB9A604DC9D5B19, double 0x3FB9103075A4A0AB, double 0x3FB87B1C9DBF2852, double 0x3FB7E6CA013EEFD6, double 0x3FB753395AAA1176, double 0x3FB6C06B73694A4C, double 0x3FB62E6124854D18, double 0x3FB59D1B577466A4, double 0x3FB50C9B06FA2BAE, double 0x3FB47CE1401B2213, double 0x3FB3EDEF23269A86, double 0x3FB35FC5E4D93E70, double 0x3FB2D266CF9B3111, double 0x3FB245D344DD0D91, double 0x3FB1BA0CBE97897D, double 0x3FB12F14D0F2179D, double 0x3FB0A4ED2C159625, double 0x3FB01B979E30E497, double 0x3FAF262C2B6C6E35, double 0x3FAE16D547B25181, double 0x3FAD092EFEADF162, double 0x3FABFD3E0F282A2C, double 0x3FAAF30790385F70, double 0x3FA9EA90F9295563, double 0x3FA8E3E02A68B5AB, double 0x3FA7DEFB77AF271E, double 0x3FA6DBE9B398D064, double 0x3FA5DAB23CF2ADD4, double 0x3FA4DB5D0E11275D, double 0x3FA3DDF2CE98EECB, double 0x3FA2E27CE83DF497, double 0x3FA1E9059F1F6ABC, double 0x3FA0F1982E968011, double 0x3F9FF881D718A5C4, double 0x3F9E121ADB828C75, double 0x3F9C301983CD091A, double 0x3F9A529F4E22EBF8, double 0x3F9879D1B600C10A, double 0x3F96A5DAF40BBF82, double 0x3F94D6EAF2FBB064, double 0x3F930D388DAB5E13, double 0x3F91490334603012, double 0x3F8F152A4F72DD49, double 0x3F8BA48D274F8FAC, double 0x3F8841040D8DA478, double 0x3F84EB96421ACFE0, double 0x3F81A59229952F92, double 0x3F7CE160F8EC6837, double 0x3F769EA8D90CB85D, double 0x3F708A1F03B0B1FD, double 0x3F655F9F43C1B067, double 0x3F54A605B6B9F70F], align 16\n    %p = getelementptr inbounds [256 x double], ptr @data, i64 0, i64 %idx\n    %x = load double, ptr %p, align 8\n    ret double %x\n\n@pure\n@llvm\ndef ki_float(idx: int) -> u32:\n    @data = private unnamed_addr constant [256 x i32] [i32 7838188, i32 0, i32 6309365, i32 7150248, i32 7507892, i32 7705263, i32 7830108, i32 7916088, i32 7978859, i32 8026677, i32 8064303, i32 8094676, i32 8119703, i32 8140680, i32 8158515, i32 8173862, i32 8187208, i32 8198920, i32 8209279, i32 8218507, i32 8226779, i32 8234236, i32 8240993, i32 8247143, i32 8252765, i32 8257923, i32 8262673, i32 8267060, i32 8271125, i32 8274901, i32 8278419, i32 8281703, i32 8284777, i32 8287658, i32 8290366, i32 8292914, i32 8295317, i32 8297586, i32 8299733, i32 8301766, i32 8303694, i32 8305525, i32 8307267, i32 8308924, i32 8310504, i32 8312012, i32 8313451, i32 8314827, i32 8316143, i32 8317404, i32 8318612, i32 8319771, i32 8320884, i32 8321952, i32 8322979, i32 8323967, i32 8324918, i32 8325834, i32 8326716, i32 8327567, i32 8328388, i32 8329180, i32 8329946, i32 8330685, i32 8331399, i32 8332090, i32 8332759, i32 8333405, i32 8334032, i32 8334638, i32 8335226, i32 8335795, i32 8336348, i32 8336883, i32 8337403, i32 8337907, i32 8338396, i32 8338871, i32 8339332, i32 8339781, i32 8340216, i32 8340639, i32 8341050, i32 8341450, i32 8341838, i32 8342216, i32 8342584, i32 8342941, i32 8343289, i32 8343628, i32 8343957, i32 8344277, i32 8344589, i32 8344893, i32 8345188, i32 8345476, i32 8345756, i32 8346028, i32 8346293, i32 8346552, i32 8346803, i32 8347048, i32 8347286, i32 8347518, i32 8347743, i32 8347963, i32 8348176, i32 8348384, i32 8348586, i32 8348783, i32 8348974, i32 8349160, i32 8349340, i32 8349516, i32 8349686, i32 8349852, i32 8350012, i32 8350169, i32 8350320, i32 8350467, i32 8350609, i32 8350747, i32 8350880, i32 8351009, i32 8351134, i32 8351255, i32 8351372, i32 8351484, i32 8351592, i32 8351697, i32 8351797, i32 8351894, i32 8351987, i32 8352076, i32 8352161, i32 8352242, i32 8352319, i32 8352393, i32 8352463, i32 8352530, i32 8352592, i32 8352651, i32 8352707, i32 8352758, i32 8352807, i32 8352851, i32 8352892, i32 8352929, i32 8352963, i32 8352992, i32 8353019, i32 8353041, i32 8353060, i32 8353075, i32 8353087, i32 8353094, i32 8353098, i32 8353099, i32 8353095, i32 8353087, i32 8353076, i32 8353060, i32 8353041, i32 8353017, i32 8352990, i32 8352958, i32 8352922, i32 8352882, i32 8352837, i32 8352788, i32 8352735, i32 8352677, i32 8352614, i32 8352547, i32 8352474, i32 8352397, i32 8352314, i32 8352227, i32 8352134, i32 8352035, i32 8351931, i32 8351821, i32 8351705, i32 8351583, i32 8351455, i32 8351320, i32 8351179, i32 8351031, i32 8350876, i32 8350713, i32 8350543, i32 8350364, i32 8350178, i32 8349983, i32 8349780, i32 8349567, i32 8349345, i32 8349112, i32 8348870, i32 8348616, i32 8348352, i32 8348075, i32 8347786, i32 8347485, i32 8347169, i32 8346840, i32 8346495, i32 8346135, i32 8345758, i32 8345363, i32 8344949, i32 8344516, i32 8344062, i32 8343586, i32 8343087, i32 8342562, i32 8342010, i32 8341430, i32 8340819, i32 8340175, i32 8339496, i32 8338778, i32 8338020, i32 8337217, i32 8336365, i32 8335461, i32 8334500, i32 8333476, i32 8332383, i32 8331214, i32 8329962, i32 8328617, i32 8327168, i32 8325604, i32 8323910, i32 8322070, i32 8320062, i32 8317864, i32 8315447, i32 8312776, i32 8309807, i32 8306487, i32 8302749, i32 8298506, i32 8293645, i32 8288016, i32 8281415, i32 8273555, i32 8264025, i32 8252204, i32 8237110, i32 8217091, i32 8189113, i32 8146898, i32 8074800, i32 7918290], align 16\n    %p = getelementptr inbounds [256 x i32], ptr @data, i64 0, i64 %idx\n    %x = load i32, ptr %p, align 8\n    ret i32 %x\n\n@pure\n@llvm\ndef wi_float(idx: int) -> float32:\n    @data = private unnamed_addr constant [256 x float] [float 0x3E9F493B80000000, float 0x3E5B8D0BE0000000, float 0x3E6250AF40000000, float 0x3E657CB940000000, float 0x3E6801FCE0000000, float 0x3E6A230C20000000, float 0x3E6C004D20000000, float 0x3E6DAC2F60000000, float 0x3E6F324820000000, float 0x3E704D3220000000, float 0x3E70F50540000000, float 0x3E7192A6A0000000, float 0x3E7227A280000000, float 0x3E72B52E40000000, float 0x3E733C3FC0000000, float 0x3E73BD9EC0000000, float 0x3E7439EF80000000, float 0x3E74B1BB40000000, float 0x3E75257560000000, float 0x3E759580A0000000, float 0x3E760231C0000000, float 0x3E766BD260000000, float 0x3E76D2A2A0000000, float 0x3E7736DAE0000000, float 0x3E7798AD20000000, float 0x3E77F845A0000000, float 0x3E7855CC60000000, float 0x3E78B164A0000000, float 0x3E790B2EA0000000, float 0x3E79634780000000, float 0x3E79B9C980000000, float 0x3E7A0ECCE0000000, float 0x3E7A626760000000, float 0x3E7AB4AD60000000, float 0x3E7B05B160000000, float 0x3E7B558480000000, float 0x3E7BA43680000000, float 0x3E7BF1D620000000, float 0x3E7C3E7100000000, float 0x3E7C8A13A0000000, float 0x3E7CD4CA00000000, float 0x3E7D1E9F00000000, float 0x3E7D679D20000000, float 0x3E7DAFCE00000000, float 0x3E7DF73AA0000000, float 0x3E7E3DEBC0000000, float 0x3E7E83E940000000, float 0x3E7EC93AC0000000, float 0x3E7F0DE780000000, float 0x3E7F51F660000000, float 0x3E7F956DA0000000, float 0x3E7FD85380000000, float 0x3E800D56E0000000, float 0x3E802E4100000000, float 0x3E804EEAA0000000, float 0x3E806F5660000000, float 0x3E808F86A0000000, float 0x3E80AF7D80000000, float 0x3E80CF3D60000000, float 0x3E80EEC840000000, float 0x3E810E2040000000, float 0x3E812D4700000000, float 0x3E814C3EA0000000, float 0x3E816B08C0000000, float 0x3E8189A720000000, float 0x3E81A81B60000000, float 0x3E81C66700000000, float 0x3E81E48BA0000000, float 0x3E82028AA0000000, float 0x3E82206580000000, float 0x3E823E1D80000000, float 0x3E825BB400000000, float 0x3E82792A60000000, float 0x3E829681C0000000, float 0x3E82B3BB60000000, float 0x3E82D0D860000000, float 0x3E82EDD9E0000000, float 0x3E830AC100000000, float 0x3E83278EE0000000, float 0x3E83444480000000, float 0x3E8360E2C0000000, float 0x3E837D6AC0000000, float 0x3E8399DD60000000, float 0x3E83B63BC0000000, float 0x3E83D286A0000000, float 0x3E83EEBEE0000000, float 0x3E840AE580000000, float 0x3E8426FB20000000, float 0x3E844300E0000000, float 0x3E845EF780000000, float 0x3E847ADFA0000000, float 0x3E8496BA40000000, float 0x3E84B28800000000, float 0x3E84CE49A0000000, float 0x3E84EA0020000000, float 0x3E8505ABE0000000, float 0x3E85214E00000000, float 0x3E853CE6E0000000, float 0x3E85587740000000, float 0x3E85740000000000, float 0x3E858F81C0000000, float 0x3E85AAFD20000000, float 0x3E85C672E0000000, float 0x3E85E1E380000000, float 0x3E85FD4FC0000000, float 0x3E8618B860000000, float 0x3E86341DE0000000, float 0x3E864F8100000000, float 0x3E866AE260000000, float 0x3E86864280000000, float 0x3E86A1A220000000, float 0x3E86BD01E0000000, float 0x3E86D86260000000, float 0x3E86F3C440000000, float 0x3E870F2800000000, float 0x3E872A8E60000000, float 0x3E8745F7E0000000, float 0x3E87616540000000, float 0x3E877CD700000000, float 0x3E87984DC0000000, float 0x3E87B3CA40000000, float 0x3E87CF4D00000000, float 0x3E87EAD680000000, float 0x3E880667A0000000, float 0x3E882200E0000000, float 0x3E883DA2C0000000, float 0x3E88594E20000000, float 0x3E88750360000000, float 0x3E8890C360000000, float 0x3E88AC8EA0000000, float 0x3E88C865A0000000, float 0x3E88E44960000000, float 0x3E89003A20000000, float 0x3E891C38E0000000, float 0x3E89384620000000, float 0x3E89546280000000, float 0x3E89708EC0000000, float 0x3E898CCB80000000, float 0x3E89A919A0000000, float 0x3E89C57980000000, float 0x3E89E1EC20000000, float 0x3E89FE7220000000, float 0x3E8A1B0C40000000, float 0x3E8A37BB20000000, float 0x3E8A547FA0000000, float 0x3E8A715A80000000, float 0x3E8A8E4C60000000, float 0x3E8AAB5640000000, float 0x3E8AC878C0000000, float 0x3E8AE5B4E0000000, float 0x3E8B030B40000000, float 0x3E8B207D00000000, float 0x3E8B3E0AA0000000, float 0x3E8B5BB540000000, float 0x3E8B797DC0000000, float 0x3E8B976500000000, float 0x3E8BB56BE0000000, float 0x3E8BD39360000000, float 0x3E8BF1DCA0000000, float 0x3E8C104860000000, float 0x3E8C2ED7E0000000, float 0x3E8C4D8C20000000, float 0x3E8C6C6600000000, float 0x3E8C8B66E0000000, float 0x3E8CAA8FC0000000, float 0x3E8CC9E1C0000000, float 0x3E8CE95E40000000, float 0x3E8D090640000000, float 0x3E8D28DB20000000, float 0x3E8D48DE20000000, float 0x3E8D6910A0000000, float 0x3E8D897400000000, float 0x3E8DAA09A0000000, float 0x3E8DCAD300000000, float 0x3E8DEBD1A0000000, float 0x3E8E0D0700000000, float 0x3E8E2E74C0000000, float 0x3E8E501CA0000000, float 0x3E8E720020000000, float 0x3E8E942140000000, float 0x3E8EB681C0000000, float 0x3E8ED92380000000, float 0x3E8EFC0860000000, float 0x3E8F1F3280000000, float 0x3E8F42A400000000, float 0x3E8F665F20000000, float 0x3E8F8A6600000000, float 0x3E8FAEBB20000000, float 0x3E8FD360E0000000, float 0x3E8FF859C0000000, float 0x3E900ED440000000, float 0x3E9021A800000000, float 0x3E9034A980000000, float 0x3E9047DA40000000, float 0x3E905B3C00000000, float 0x3E906ED020000000, float 0x3E90829880000000, float 0x3E90969700000000, float 0x3E90AACD80000000, float 0x3E90BF3DE0000000, float 0x3E90D3EA40000000, float 0x3E90E8D4C0000000, float 0x3E90FDFFE0000000, float 0x3E91136E00000000, float 0x3E912921A0000000, float 0x3E913F1D60000000, float 0x3E91556440000000, float 0x3E916BF940000000, float 0x3E9182DF80000000, float 0x3E919A1A60000000, float 0x3E91B1AD80000000, float 0x3E91C99CA0000000, float 0x3E91E1EC00000000, float 0x3E91FA9FC0000000, float 0x3E9213BCA0000000, float 0x3E922D4780000000, float 0x3E924745A0000000, float 0x3E9261BCC0000000, float 0x3E927CB300000000, float 0x3E92982EC0000000, float 0x3E92B43760000000, float 0x3E92D0D440000000, float 0x3E92EE0DC0000000, float 0x3E930BECE0000000, float 0x3E932A7B60000000, float 0x3E9349C400000000, float 0x3E9369D280000000, float 0x3E938AB3A0000000, float 0x3E93AC7580000000, float 0x3E93CF27C0000000, float 0x3E93F2DBA0000000, float 0x3E9417A4A0000000, float 0x3E943D9820000000, float 0x3E9464CE40000000, float 0x3E948D6280000000, float 0x3E94B773A0000000, float 0x3E94E32500000000, float 0x3E95109F60000000, float 0x3E95401160000000, float 0x3E9571B1A0000000, float 0x3E95A5C080000000, float 0x3E95DC8A20000000, float 0x3E961669C0000000, float 0x3E9653CE80000000, float 0x3E969540C0000000, float 0x3E96DB6B80000000, float 0x3E97272900000000, float 0x3E97799560000000, float 0x3E97D42E00000000, float 0x3E98390300000000, float 0x3E98AB0FC0000000, float 0x3E992EE0A0000000, float 0x3E99CBEE00000000, float 0x3E9A8FDC80000000, float 0x3E9B981F40000000, float 0x3E9D3BB480000000], align 16\n    %p = getelementptr inbounds [256 x float], ptr @data, i64 0, i64 %idx\n    %x = load float, ptr %p, align 8\n    ret float %x\n\n@pure\n@llvm\ndef fi_float(idx: int) -> float32:\n    @data = private unnamed_addr constant [256 x float] [float 1.000000e+00, float 0x3FEF446AC0000000, float 0x3FEEB75460000000, float 0x3FEE3F11E0000000, float 0x3FEDD36FA0000000, float 0x3FED709200000000, float 0x3FED144980000000, float 0x3FECBD33A0000000, float 0x3FEC6A5EC0000000, float 0x3FEC1B1CE0000000, float 0x3FEBCEEB40000000, float 0x3FEB856540000000, float 0x3FEB3E3A80000000, float 0x3FEAF92A40000000, float 0x3FEAB5FF00000000, float 0x3FEA748BE0000000, float 0x3FEA34AB00000000, float 0x3FE9F63BE0000000, float 0x3FE9B92280000000, float 0x3FE97D4660000000, float 0x3FE94291C0000000, float 0x3FE908F1C0000000, float 0x3FE8D05540000000, float 0x3FE898AD40000000, float 0x3FE861EC00000000, float 0x3FE82C0500000000, float 0x3FE7F6ED40000000, float 0x3FE7C29A80000000, float 0x3FE78F0340000000, float 0x3FE75C1F00000000, float 0x3FE729E600000000, float 0x3FE6F850C0000000, float 0x3FE6C758A0000000, float 0x3FE696F760000000, float 0x3FE6672720000000, float 0x3FE637E2A0000000, float 0x3FE60924A0000000, float 0x3FE5DAE860000000, float 0x3FE5AD29A0000000, float 0x3FE57FE420000000, float 0x3FE5531400000000, float 0x3FE526B560000000, float 0x3FE4FAC4E0000000, float 0x3FE4CF3F40000000, float 0x3FE4A42180000000, float 0x3FE4796860000000, float 0x3FE44F1140000000, float 0x3FE4251980000000, float 0x3FE3FB7EA0000000, float 0x3FE3D23E20000000, float 0x3FE3A955A0000000, float 0x3FE380C320000000, float 0x3FE3588480000000, float 0x3FE33097C0000000, float 0x3FE308FB00000000, float 0x3FE2E1AC60000000, float 0x3FE2BAAA20000000, float 0x3FE293F280000000, float 0x3FE26D8420000000, float 0x3FE2475D60000000, float 0x3FE2217CA0000000, float 0x3FE1FBE0A0000000, float 0x3FE1D68800000000, float 0x3FE1B17160000000, float 0x3FE18C9B80000000, float 0x3FE1680520000000, float 0x3FE143AD20000000, float 0x3FE11F9240000000, float 0x3FE0FBB3A0000000, float 0x3FE0D81020000000, float 0x3FE0B4A680000000, float 0x3FE0917620000000, float 0x3FE06E7DC0000000, float 0x3FE04BBCA0000000, float 0x3FE02931E0000000, float 0x3FE006DC80000000, float 0x3FDFC97780000000, float 0x3FDF859DA0000000, float 0x3FDF4229C0000000, float 0x3FDEFF1A80000000, float 0x3FDEBC6E20000000, float 0x3FDE7A2360000000, float 0x3FDE3838E0000000, float 0x3FDDF6AD40000000, float 0x3FDDB57F40000000, float 0x3FDD74AD60000000, float 0x3FDD3436A0000000, float 0x3FDCF419C0000000, float 0x3FDCB45580000000, float 0x3FDC74E8C0000000, float 0x3FDC35D260000000, float 0x3FDBF71180000000, float 0x3FDBB8A4E0000000, float 0x3FDB7A8B80000000, float 0x3FDB3CC460000000, float 0x3FDAFF4EA0000000, float 0x3FDAC22940000000, float 0x3FDA855340000000, float 0x3FDA48CBE0000000, float 0x3FDA0C9240000000, float 0x3FD9D0A560000000, float 0x3FD9950480000000, float 0x3FD959AEE0000000, float 0x3FD91EA3A0000000, float 0x3FD8E3E200000000, float 0x3FD8A96940000000, float 0x3FD86F38A0000000, float 0x3FD8354F80000000, float 0x3FD7FBAD20000000, float 0x3FD7C250A0000000, float 0x3FD78939A0000000, float 0x3FD7506760000000, float 0x3FD717D940000000, float 0x3FD6DF8E80000000, float 0x3FD6A786A0000000, float 0x3FD66FC120000000, float 0x3FD6383D40000000, float 0x3FD600FA80000000, float 0x3FD5C9F840000000, float 0x3FD5933620000000, float 0x3FD55CB380000000, float 0x3FD5266FC0000000, float 0x3FD4F06A80000000, float 0x3FD4BAA360000000, float 0x3FD48519A0000000, float 0x3FD44FCCE0000000, float 0x3FD41ABCE0000000, float 0x3FD3E5E8E0000000, float 0x3FD3B15080000000, float 0x3FD37CF360000000, float 0x3FD348D120000000, float 0x3FD314E940000000, float 0x3FD2E13B80000000, float 0x3FD2ADC740000000, float 0x3FD27A8C40000000, float 0x3FD2478A20000000, float 0x3FD214C080000000, float 0x3FD1E22F00000000, float 0x3FD1AFD540000000, float 0x3FD17DB2E0000000, float 0x3FD14BC7C0000000, float 0x3FD11A1340000000, float 0x3FD0E89560000000, float 0x3FD0B74D80000000, float 0x3FD0863B80000000, float 0x3FD0555F20000000, float 0x3FD024B800000000, float 0x3FCFE88B80000000, float 0x3FCF881080000000, float 0x3FCF27FE60000000, float 0x3FCEC854A0000000, float 0x3FCE6912C0000000, float 0x3FCE0A3820000000, float 0x3FCDABC460000000, float 0x3FCD4DB700000000, float 0x3FCCF00F80000000, float 0x3FCC92CDA0000000, float 0x3FCC35F0C0000000, float 0x3FCBD97880000000, float 0x3FCB7D6480000000, float 0x3FCB21B460000000, float 0x3FCAC667A0000000, float 0x3FCA6B7E00000000, float 0x3FCA10F740000000, float 0x3FC9B6D2C0000000, float 0x3FC95D1060000000, float 0x3FC903AFC0000000, float 0x3FC8AAB0A0000000, float 0x3FC8521280000000, float 0x3FC7F9D560000000, float 0x3FC7A1F8E0000000, float 0x3FC74A7CA0000000, float 0x3FC6F36080000000, float 0x3FC69CA440000000, float 0x3FC64647A0000000, float 0x3FC5F04A80000000, float 0x3FC59AAC80000000, float 0x3FC5456DA0000000, float 0x3FC4F08DA0000000, float 0x3FC49C0C60000000, float 0x3FC447E9C0000000, float 0x3FC3F42580000000, float 0x3FC3A0BFA0000000, float 0x3FC34DB800000000, float 0x3FC2FB0E80000000, float 0x3FC2A8C320000000, float 0x3FC256D5A0000000, float 0x3FC2054620000000, float 0x3FC1B414A0000000, float 0x3FC16340E0000000, float 0x3FC112CB20000000, float 0x3FC0C2B340000000, float 0x3FC072F940000000, float 0x3FC0239D60000000, float 0x3FBFA93EC0000000, float 0x3FBF0BFF20000000, float 0x3FBE6F7C00000000, float 0x3FBDD3B560000000, float 0x3FBD38ABC0000000, float 0x3FBC9E5F40000000, float 0x3FBC04D060000000, float 0x3FBB6BFF80000000, float 0x3FBAD3ECE0000000, float 0x3FBA3C9940000000, float 0x3FB9A604E0000000, float 0x3FB9103080000000, float 0x3FB87B1CA0000000, float 0x3FB7E6CA00000000, float 0x3FB7533960000000, float 0x3FB6C06B80000000, float 0x3FB62E6120000000, float 0x3FB59D1B60000000, float 0x3FB50C9B00000000, float 0x3FB47CE140000000, float 0x3FB3EDEF20000000, float 0x3FB35FC5E0000000, float 0x3FB2D266C0000000, float 0x3FB245D340000000, float 0x3FB1BA0CC0000000, float 0x3FB12F14E0000000, float 0x3FB0A4ED20000000, float 0x3FB01B97A0000000, float 0x3FAF262C20000000, float 0x3FAE16D540000000, float 0x3FAD092F00000000, float 0x3FABFD3E00000000, float 0x3FAAF307A0000000, float 0x3FA9EA9100000000, float 0x3FA8E3E020000000, float 0x3FA7DEFB80000000, float 0x3FA6DBE9C0000000, float 0x3FA5DAB240000000, float 0x3FA4DB5D00000000, float 0x3FA3DDF2C0000000, float 0x3FA2E27CE0000000, float 0x3FA1E905A0000000, float 0x3FA0F19820000000, float 0x3F9FF881E0000000, float 0x3F9E121AE0000000, float 0x3F9C301980000000, float 0x3F9A529F40000000, float 0x3F9879D1C0000000, float 0x3F96A5DB00000000, float 0x3F94D6EB00000000, float 0x3F930D3880000000, float 0x3F91490340000000, float 0x3F8F152A40000000, float 0x3F8BA48D20000000, float 0x3F88410400000000, float 0x3F84EB9640000000, float 0x3F81A59220000000, float 0x3F7CE16100000000, float 0x3F769EA8E0000000, float 0x3F708A1F00000000, float 0x3F655F9F40000000, float 0x3F54A605C0000000], align 16\n    %p = getelementptr inbounds [256 x float], ptr @data, i64 0, i64 %idx\n    %x = load float, ptr %p, align 8\n    ret float %x\n\n@pure\n@llvm\ndef ke_double(idx: int) -> u64:\n    @data = private unnamed_addr constant [256 x i64] [i64 7971545857431494, i64 0, i64 5485857970336126, i64 6877400373607440, i64 7489560515621038, i64 7829793950745724, i64 8045251395085594, i64 8193552821270898, i64 8301707212298418, i64 8384003209374832, i64 8448689755168200, i64 8500854585063478, i64 8543802742323106, i64 8579772857648236, i64 8610334328270398, i64 8636619566280862, i64 8659465946817878, i64 8679505875409358, i64 8697225801520776, i64 8713005977443536, i64 8727147906454692, i64 8739893704890038, i64 8751440024696698, i64 8761948238062960, i64 8771552003860596, i64 8780362968290610, i64 8788475114930438, i64 8795968123070796, i64 8802909988292858, i64 8809359087581710, i64 8815365821575970, i64 8820973931588800, i64 8826221564107158, i64 8831142137483404, i64 8835765052397426, i64 8840116277974648, i64 8844218838221542, i64 8848093218006260, i64 8851757703688506, i64 8855228670347734, i64 8858520825126080, i64 8861647414312948, i64 8864620400320394, i64 8867450613535032, i64 8870147883110754, i64 8872721150032146, i64 8875178565190242, i64 8877527574738170, i64 8879774994610604, i64 8881927075778634, i64 8883989561556502, i64 8885967738067168, i64 8887866478800856, i64 8889690284057818, i64 8891443315947666, i64 8893129429518482, i64 8894752200505984, i64 8896314950123264, i64 8897820767252858, i64 8899272528353282, i64 8900672915349966, i64 8902024431744704, i64 8903329417147192, i64 8904590060406012, i64 8905808411494018, i64 8906986392283810, i64 8908125806332284, i64 8909228347778946, i64 8910295609450180, i64 8911329090250868, i64 8912330201915374, i64 8913300275181656, i64 8914240565445170, i64 8915152257942916, i64 8916036472512488, i64 8916894267966144, i64 8917726646115692, i64 8918534555480190, i64 8919318894705170, i64 8920080515719156, i64 8920820226650618, i64 8921538794526266, i64 8922236947769418, i64 8922915378515480, i64 8923574744759820, i64 8924215672351958, i64 8924838756848636, i64 8925444565237162, i64 8926033637539416, i64 8926606488305930, i64 8927163608008600, i64 8927705464339880, i64 8928232503425544, i64 8928745150957558, i64 8929243813252980, i64 8929728878244356, i64 8930200716406566, i64 8930659681624710, i64 8931106112007190, i64 8931540330647876, i64 8931962646340834, i64 8932373354250910, i64 8932772736543124, i64 8933161062973652, i64 8933538591444894, i64 8933905568527004, i64 8934262229948010, i64 8934608801054528, i64 8934945497244894, i64 8935272524376414, i64 8935590079148328, i64 8935898349461876, i64 8936197514758882, i64 8936487746340036, i64 8936769207664048, i64 8937042054628744, i64 8937306435835058, i64 8937562492834854, i64 8937810360363402, i64 8938050166557284, i64 8938282033158466, i64 8938506075705162, i64 8938722403710132, i64 8938931120826946, i64 8939132325004766, i64 8939326108632062, i64 8939512558669762, i64 8939691756774158, i64 8939863779409990, i64 8940028697953972, i64 8940186578789100, i64 8940337483389966, i64 8940481468399302, i64 8940618585695992, i64 8940748882454662, i64 8940872401197050, i64 8940989179835208, i64 8941099251706688, i64 8941202645601704, i64 8941299385782368, i64 8941389491993960, i64 8941472979468230, i64 8941549858918698, i64 8941620136527848, i64 8941683813926148, i64 8941740888162738, i64 8941791351667640, i64 8941835192205302, i64 8941872392819226, i64 8941902931767472, i64 8941926782448690, i64 8941943913318396, i64 8941954287795084, i64 8941957864155806, i64 8941954595420724, i64 8941944429226144, i64 8941927307685492, i64 8941903167237602, i64 8941871938481652, i64 8941833545998016, i64 8941787908154234, i64 8941734936895206, i64 8941674537516674, i64 8941606608420918, i64 8941531040853536, i64 8941447718620056, i64 8941356517781006, i64 8941257306323958, i64 8941149943810914, i64 8941034280999228, i64 8940910159434164, i64 8940777411010892, i64 8940635857503634, i64 8940485310059376, i64 8940325568653336, i64 8940156421503112, i64 8939977644438114, i64 8939789000220574, i64 8939590237814000, i64 8939381091594596, i64 8939161280500638, i64 8938930507114326, i64 8938688456670012, i64 8938434795982096, i64 8938169172285110, i64 8937891211977748, i64 8937600519261604, i64 8937296674664432, i64 8936979233436516, i64 8936647723807416, i64 8936301645088910, i64 8935940465608202, i64 8935563620453574, i64 8935170509012442, i64 8934760492279316, i64 8934332889908232, i64 8933886976981030, i64 8933421980459034, i64 8932937075281380, i64 8932431380068266, i64 8931903952381602, i64 8931353783488908, i64 8930779792568492, i64 8930180820284952, i64 8929555621653500, i64 8928902858099222, i64 8928221088602964, i64 8927508759808348, i64 8926764194944404, i64 8925985581394242, i64 8925170956711904, i64 8924318192855506, i64 8923424978364230, i64 8922488798157886, i64 8921506910578770, i64 8920476321224196, i64 8919393753031106, i64 8918255611967910, i64 8917057947558148, i64 8915796407299442, i64 8914466183841290, i64 8913061953535784, i64 8911577804662434, i64 8910007153233212, i64 8908342643782164, i64 8906576031902208, i64 8904698044465300, i64 8902698212389652, i64 8900564669414922, i64 8898283908495804, i64 8895840484961220, i64 8893216652275640, i64 8890391911743352, i64 8887342451323378, i64 8884040440144924, i64 8880453133239800, i64 8876541723776518, i64 8872259855113102, i64 8867551668208538, i64 8862349204777254, i64 8856568902200012, i64 8850106784293916, i64 8842831740745002, i64 8834575940248166, i64 8825120832349124, i64 8814176156651890, i64 8801347484544986, i64 8786084197194146, i64 8767592496903178, i64 8744682338845716, i64 8715480686119910, i64 8676850260251934, i64 8623083654098352, i64 8542525795804796, i64 8406823688997808, i64 8122426762520768], align 16\n    %p = getelementptr inbounds [256 x i64], ptr @data, i64 0, i64 %idx\n    %x = load i64, ptr %p, align 8\n    ret i64 %x\n\n@pure\n@llvm\ndef we_double(idx: int) -> float:\n    @data = private unnamed_addr constant [256 x double] [double 0x3CD164EC94BF5DC1, double 0x3C60589D8B5D4119, double 0x3C6AD6B2495B4D2B, double 0x3C719335A95B8DBA, double 0x3C7522E6E54A2A73, double 0x3C785090FBC27A80, double 0x3C7B38D1EF79B7CC, double 0x3C7DECD8B76DBD98, double 0x3C803BF049C65C3C, double 0x3C8170DB24D6F670, double 0x3C82980290DA2633, double 0x3C83B388FE3D6ECA, double 0x3C84C515C60BFE21, double 0x3C85CDF89D024AC3, double 0x3C86CF40F0A72BBD, double 0x3C87C9CDDA17D019, double 0x3C88BE5954D3606F, double 0x3C89AD80552237D2, double 0x3C8A97C8BE5D5203, double 0x3C8B7DA5DDDDA3C4, double 0x3C8C5F7BD78C3F89, double 0x3C8D3DA24DF17C36, double 0x3C8E186678F1735A, double 0x3C8EF00CCF5F4FAA, double 0x3C8FC4D25D683209, double 0x3C904B76ED6A7558, double 0x3C90B348479B80FC, double 0x3C9119F38749F5AF, double 0x3C917F8CEB4BDFA0, double 0x3C91E426E93E49E7, double 0x3C9247D26538FF2E, double 0x3C92AA9EE123680B, double 0x3C930C9AA526DA4B, double 0x3C936DD2E26D8202, double 0x3C93CE53D12162A0, double 0x3C942E28CA706748, double 0x3C948D5C5F35E712, double 0x3C94EBF86BCD0B93, double 0x3C954A0629786F4D, double 0x3C95A78E3DB8BEFD, double 0x3C960498C7DD2ECF, double 0x3C96612D6D0C68E0, double 0x3C96BD5362FAA944, double 0x3C971911797990BB, double 0x3C97746E23077973, double 0x3C97CF6F7C7E8172, double 0x3C982A1B53FED599, double 0x3C9884772F2BE1EC, double 0x3C98DE8850D0C52A, double 0x3C993853BDFDA244, double 0x3C9991DE42AD1338, double 0x3C99EB2C75FF03BF, double 0x3C9A4442BE14884A, double 0x3C9A9D255396D261, double 0x3C9AF5D844F224C9, double 0x3C9B4E5F794C979B, double 0x3C9BA6BEB33F8F89, double 0x3C9BFEF99359FE99, double 0x3C9C57139A70D29F, double 0x3C9CAF102BC25ADB, double 0x3C9D06F28EF0E6FB, double 0x3C9D5EBDF1D86B8D, double 0x3C9DB6756A429057, double 0x3C9E0E1BF77C31FE, double 0x3C9E65B483CF1044, double 0x3C9EBD41E5E21B62, double 0x3C9F14C6E202949F, double 0x3C9F6C462B57FEB5, double 0x3C9FC3C26504A9A1, double 0x3CA00D9F119A3CD9, double 0x3CA0395DF60DB162, double 0x3CA0651F1C7276F8, double 0x3CA090E3BB4B0072, double 0x3CA0BCAD03710137, double 0x3CA0E87C207A2F66, double 0x3CA114523917AC15, double 0x3CA140306F707DBE, double 0x3CA16C17E1777FFB, double 0x3CA19809A93D2396, double 0x3CA1C406DD3D5283, double 0x3CA1F01090A9C4E2, double 0x3CA21C27D3B10E05, double 0x3CA2484DB3C2A329, double 0x3CA274833BD0189F, double 0x3CA2A0C9748BCDAA, double 0x3CA2CD2164A53B5D, double 0x3CA2F98C11031721, double 0x3CA3260A7CFB7611, double 0x3CA3529DAA8A1BA1, double 0x3CA37F469A851AF0, double 0x3CA3AC064CCFEFFC, double 0x3CA3D8DDC08D336D, double 0x3CA405CDF44F09C4, double 0x3CA432D7E6466CD0, double 0x3CA45FFC94716CA7, double 0x3CA48D3CFCC883C4, double 0x3CA4BA9A1D6B18A4, double 0x3CA4E814F4CB45EA, double 0x3CA515AE81D900FB, double 0x3CA54367C42CB5F8, double 0x3CA57141BC316F27, double 0x3CA59F3D6B4E9CF9, double 0x3CA5CD5BD4119335, double 0x3CA5FB9DFA56CF26, double 0x3CA62A04E3731A2E, double 0x3CA65891965C9B8C, double 0x3CA687451BD3EBEE, double 0x3CA6B6207E8D3CDF, double 0x3CA6E524CB59A608, double 0x3CA714531150A9FB, double 0x3CA743AC61FA041C, double 0x3CA77331D177D130, double 0x3CA7A2E476B1240A, double 0x3CA7D2C56B7D17F7, double 0x3CA802D5CCCE7277, double 0x3CA83316BADFE62A, double 0x3CA86389596108E7, double 0x3CA8942ECFA40F54, double 0x3CA8C50848CC6094, double 0x3CA8F616F3FE1513, double 0x3CA9275C048E73E1, double 0x3CA958D8B235828A, double 0x3CA98A8E3940BBF4, double 0x3CA9BC7DDAC7035D, double 0x3CA9EEA8DCDDE951, double 0x3CAA21108AD0592D, double 0x3CAA53B63556C690, double 0x3CAA869B32D0F30F, double 0x3CAAB9C0DF81657A, double 0x3CAAED289DCAACFF, double 0x3CAB20D3D66E8BB5, double 0x3CAB54C3F8CF2542, double 0x3CAB88FA7B324FB6, double 0x3CABBD78DB072610, double 0x3CABF2409D2DFD85, double 0x3CAC27534E42E02D, double 0x3CAC5CB282EAB1A4, double 0x3CAC925FD82323FB, double 0x3CACC85CF395A56C, double 0x3CACFEAB83ED7180, double 0x3CAD354D4130F2AD, double 0x3CAD6C43ED1EA3FE, double 0x3CADA391538DA50A, double 0x3CADDB374AD2357F, double 0x3CAE1337B426509B, double 0x3CAE4B947C16A452, double 0x3CAE844F9AF4237F, double 0x3CAEBD6B154A7678, double 0x3CAEF6E8FC5B9168, double 0x3CAF30CB6EA0BC7F, double 0x3CAF6B1498515ED0, double 0x3CAFA5C6B3EFE1E5, double 0x3CAFE0E40ADD09D8, double 0x3CB00E377AF911D4, double 0x3CB02C34EF11391B, double 0x3CB04A6B9E9224A3, double 0x3CB068DCCF1126DB, double 0x3CB08789CF3AAD0F, double 0x3CB0A673F733C819, double 0x3CB0C59CA900946F, double 0x3CB0E50550EFCFB7, double 0x3CB104AF660BEFCE, double 0x3CB1249C6A92154A, double 0x3CB144CDEC6F3A2B, double 0x3CB1654585C404C1, double 0x3CB18604DD6FAE9E, double 0x3CB1A70DA7A27820, double 0x3CB1C861A6782A5A, double 0x3CB1EA02AA9B3370, double 0x3CB20BF293F0F4A2, double 0x3CB22E33524FE550, double 0x3CB250C6E6403BBA, double 0x3CB273AF61C7DAA6, double 0x3CB296EEE942532B, double 0x3CB2BA87B445DB51, double 0x3CB2DE7C0E962D70, double 0x3CB302CE59265965, double 0x3CB327810B2AA7D0, double 0x3CB34C96B33BC965, double 0x3CB37211F88CA856, double 0x3CB397F59C345143, double 0x3CB3BE447A8D8B83, double 0x3CB3E5018CADDED0, double 0x3CB40C2FE9F5EEAD, double 0x3CB433D2C9BD42F8, double 0x3CB45BED851BC92C, double 0x3CB4848398D39432, double 0x3CB4AD98A75DA14C, double 0x3CB4D7307B1CB127, double 0x3CB5014F08B99508, double 0x3CB52BF871ACAAB2, double 0x3CB5573106F8A75A, double 0x3CB582FD4C1B4461, double 0x3CB5AF61FA38E107, double 0x3CB5DC640388BD9E, double 0x3CB60A0897081879, double 0x3CB63855247B2E94, double 0x3CB6674F60C3F432, double 0x3CB696FD4A9748EE, double 0x3CB6C7652F9A7B1E, double 0x3CB6F88DB1F42507, double 0x3CB72A7DCE5CD218, double 0x3CB75D3CE2BD71C3, double 0x3CB790D2B56B71F9, double 0x3CB7C5477D1476D3, double 0x3CB7FAA3E96E1412, double 0x3CB830F12CC0BEC3, double 0x3CB8683906687342, double 0x3CB8A085CE695BAB, double 0x3CB8D9E2823B3695, double 0x3CB9145AD2F37544, double 0x3CB94FFB34FC2A0E, double 0x3CB98CD0F18D1AD8, double 0x3CB9CAEA3A24D9EA, double 0x3CBA0A563E49F178, double 0x3CBA4B2543E84C3B, double 0x3CBA8D68C2AD86EA, double 0x3CBAD13382D845C4, double 0x3CBB1699C003B60A, double 0x3CBB5DB15091EA0F, double 0x3CBBA691D276DA5E, double 0x3CBBF154DE4BEF77, double 0x3CBC3E1641C2E0A7, double 0x3CBC8CF442C8C8F4, double 0x3CBCDE0FECF2A97F, double 0x3CBD318D6B2738C5, double 0x3CBD87946FEC3BEC, double 0x3CBDE050AF4EF19F, double 0x3CBE3BF26E190960, double 0x3CBE9AAF2AF383C1, double 0x3CBEFCC26750EA4A, double 0x3CBF626E9791F7A7, double 0x3CBFCBFE43F6C6E5, double 0x3CC01CE2B362EC2E, double 0x3CC056118BF58EEF, double 0x3CC091C1CDCBA54E, double 0x3CC0D031785D48A0, double 0x3CC111A8034392A6, double 0x3CC156786775442A, double 0x3CC19F03BCB3C2D6, double 0x3CC1EBBCA0C9FA7C, double 0x3CC23D2BB659919F, double 0x3CC293F5AE49AAA5, double 0x3CC2F0E38A4411F0, double 0x3CC354EE27CCF75E, double 0x3CC3C14EC7C8B861, double 0x3CC4379766E41362, double 0x3CC4B9D7CD4751D1, double 0x3CC54AD83CCF73F6, double 0x3CC5EE7AE17313D2, double 0x3CC6AA676D4BBF72, double 0x3CC78750D6EAC62F, double 0x3CC8939FE6F2ED19, double 0x3CC9E9DC0D487B85, double 0x3CCBC39E51DA71FC, double 0x3CCEC9D9297EBB83], align 16\n    %p = getelementptr inbounds [256 x double], ptr @data, i64 0, i64 %idx\n    %x = load double, ptr %p, align 8\n    ret double %x\n\n@pure\n@llvm\ndef fe_double(idx: int) -> float:\n    @data = private unnamed_addr constant [256 x double] [double 1.000000e+00, double 0x3FEE0545E5881137, double 0x3FECD0A65081FFF1, double 0x3FEBE5007BEB7B27, double 0x3FEB210F0EE67F2A, double 0x3FEA76BAA562FAE7, double 0x3FE9DE9715556D9B, double 0x3FE95431C455AA39, double 0x3FE8D4A376D3D22F, double 0x3FE85DE87806C5B8, double 0x3FE7EE8A2D243126, double 0x3FE7856E9B09D47E, double 0x3FE721BB5BA94B63, double 0x3FE6C2C3498418C6, double 0x3FE667FA6D4F5C06, double 0x3FE610EDC1A7AF66, double 0x3FE5BD3D694CAC75, double 0x3FE56C9882DA8773, double 0x3FE51EBA1578899A, double 0x3FE4D366C151F8AF, double 0x3FE48A6AFB8EE069, double 0x3FE44399AFA8E125, double 0x3FE3FECB2BB18B80, double 0x3FE3BBDC44E1D114, double 0x3FE37AADA708DDD9, double 0x3FE33B23450E6318, double 0x3FE2FD23E345DA5E, double 0x3FE2C098B61F4F24, double 0x3FE2856D111132BD, double 0x3FE24B8E228C50A3, double 0x3FE212EABA813EC8, double 0x3FE1DB7319877B89, double 0x3FE1A518C71E3B25, double 0x3FE16FCE6DCE6FEE, double 0x3FE13B87BC33169C, double 0x3FE108394A1CC38D, double 0x3FE0D5D8812B1E2B, double 0x3FE0A45B8854D02A, double 0x3FE073B931EE3B7D, double 0x3FE043E8EBD26548, double 0x3FE014E2B160F324, double 0x3FDFCD3DFE214576, double 0x3FDF722D8EBFC5FA, double 0x3FDF1886D1EB424D, double 0x3FDEC03D4B969D90, double 0x3FDE6945367DD351, double 0x3FDE139375E137FC, double 0x3FDDBF1D88A7210C, double 0x3FDD6BD97DB9ED7A, double 0x3FDD19BDE97E1A0B, double 0x3FDCC8C1DC40E092, double 0x3FDC78DCD983FB60, double 0x3FDC2A06D00EA583, double 0x3FDBDC3812AEEEB5, double 0x3FDB8F6951990B88, double 0x3FDB43939454806F, double 0x3FDAF8B03428EF5F, double 0x3FDAAEB8D6FDF6E5, double 0x3FDA65A76AA30140, double 0x3FDA1D76207521F4, double 0x3FD9D61F695A3792, double 0x3FD98F9DF2097BA8, double 0x3FD949EC9F9A8110, double 0x3FD905068C545D04, double 0x3FD8C0E704B75D39, double 0x3FD87D8984BC3F8C, double 0x3FD83AE9B5446138, double 0x3FD7F90369B6CE59, double 0x3FD7B7D29DC6801E, double 0x3FD77753735E72E3, double 0x3FD7378230B08DEA, double 0x3FD6F85B3E649E9D, double 0x3FD6B9DB25E4E99C, double 0x3FD67BFE8FC60D9F, double 0x3FD63EC2424827E4, double 0x3FD602231FEF5876, double 0x3FD5C61E2631EE6C, double 0x3FD58AB06C3AA9EF, double 0x3FD54FD721BDA3E7, double 0x3FD5158F8DDE89F5, double 0x3FD4DBD70E26F91D, double 0x3FD4A2AB158BDAD3, double 0x3FD46A092B80BEEF, double 0x3FD431EEEB1841E2, double 0x3FD3FA5A0230A14E, double 0x3FD3C34830ABB285, double 0x3FD38CB747B17DEF, double 0x3FD356A528FCD0DD, double 0x3FD3210FC6312435, double 0x3FD2EBF520394270, double 0x3FD2B75346AE2262, double 0x3FD2832857457629, double 0x3FD24F727D4776FD, double 0x3FD21C2FF10B7EFF, double 0x3FD1E95EF77B09DB, double 0x3FD1B6FDE19ABC5A, double 0x3FD1850B0C191982, double 0x3FD15384DEE291EF, double 0x3FD12269CCBA9FBA, double 0x3FD0F1B852D9A66C, double 0x3FD0C16EF88F5333, double 0x3FD0918C4EE93E13, double 0x3FD0620EF05D90D2, double 0x3FD032F580797C2C, double 0x3FD0043EAB93476A, double 0x3FCFABD24CFF9354, double 0x3FCF4FE75C963E7E, double 0x3FCEF4BA0FE8E09B, double 0x3FCE9A48005940F2, double 0x3FCE408ED62F83A7, double 0x3FCDE78C48224F39, double 0x3FCD8F3E1AE3EEB8, double 0x3FCD37A220B431FD, double 0x3FCCE0B638F6D09F, double 0x3FCC8A784FCE1802, double 0x3FCC34E65DB9AFEE, double 0x3FCBDFFE67394435, double 0x3FCB8BBE7C72E4A5, double 0x3FCB3824B8DCEF3E, double 0x3FCAE52F42EB5B0B, double 0x3FCA92DC4BC03C49, double 0x3FCA412A0EDF5CBC, double 0x3FC9F016D1E4C512, double 0x3FC99FA0E43E1623, double 0x3FC94FC69EE692A1, double 0x3FC900866425BB79, double 0x3FC8B1DE9F5062D5, double 0x3FC863CDC48C1AF9, double 0x3FC816525094E7E6, double 0x3FC7C96AC8851BAE, double 0x3FC77D15B99F46FE, double 0x3FC73151B91A2839, double 0x3FC6E61D63EE84EA, double 0x3FC69B775EA6DA28, double 0x3FC6515E5530D1AC, double 0x3FC607D0FAB06A31, double 0x3FC5BECE0954C2B6, double 0x3FC57654422E78F5, double 0x3FC52E626D078C49, double 0x3FC4E6F7583CB6FA, double 0x3FC4A011D8983096, double 0x3FC459B0C92DCCC6, double 0x3FC413D30B386A9A, double 0x3FC3CE7785F8A905, double 0x3FC3899D2694D5C9, double 0x3FC34542DFFA0CAF, double 0x3FC30167AABE7D6E, double 0x3FC2BE0A8504CF34, double 0x3FC27B2A72609940, double 0x3FC238C67BBBE878, double 0x3FC1F6DDAF3DCA65, double 0x3FC1B56F2031D666, double 0x3FC17479E6F0AE78, double 0x3FC133FD20C9712F, double 0x3FC0F3F7EFEC1720, double 0x3FC0B4697B54B62F, double 0x3FC07550EEB7A5BE, double 0x3FC036AD7A6E7F04, double 0x3FBFF0FCA6CBEA8D, double 0x3FBF758566190414, double 0x3FBEFAF3AE83C33C, double 0x3FBE8146048EB9CC, double 0x3FBE087AF561BAFB, double 0x3FBD909116AD9398, double 0x3FBD198706914DD7, double 0x3FBCA35B6B80FD57, double 0x3FBC2E0CF42E10AF, double 0x3FBBB99A5771268F, double 0x3FBB460254356548, double 0x3FBAD343B1655465, double 0x3FBA615D3DD938B7, double 0x3FB9F04DD046F428, double 0x3FB9801447336B70, double 0x3FB910AF88E574B9, double 0x3FB8A21E835A533B, double 0x3FB834602C3BC4BA, double 0x3FB7C77380D7A6F3, double 0x3FB75B5786193C1E, double 0x3FB6F00B488416B6, double 0x3FB6858DDC30B620, double 0x3FB61BDE5CCADEF7, double 0x3FB5B2FBED91BB3E, double 0x3FB54AE5B959D036, double 0x3FB4E39AF290D929, double 0x3FB47D1AD343985C, double 0x3FB417649D25B10E, double 0x3FB3B277999B9F9E, double 0x3FB34E5319C6E718, double 0x3FB2EAF676948DD1, double 0x3FB2886110CE0570, double 0x3FB22692512C9D8C, double 0x3FB1C589A86FA340, double 0x3FB165468F755392, double 0x3FB105C88756CA50, double 0x3FB0A70F19871B3B, double 0x3FB04919D7F5C817, double 0x3FAFD7D0BA699676, double 0x3FAF1EF49944E834, double 0x3FAE679EA52EB2E5, double 0x3FADB1CE49315810, double 0x3FACFD83031E794A, double 0x3FAC4ABC640721E9, double 0x3FAB997A10BED985, double 0x3FAAE9BBC26A8084, double 0x3FAA3B81471BF138, double 0x3FA98ECA827B7C4C, double 0x3FA8E3976E80776D, double 0x3FA839E81C3A396B, double 0x3FA791BCB4AB089E, double 0x3FA6EB1579B6AF52, double 0x3FA645F2C726A041, double 0x3FA5A25513C5D2CA, double 0x3FA5003CF296C5EB, double 0x3FA45FAB14266B19, double 0x3FA3C0A047FF18FF, double 0x3FA3231D7E3F14AE, double 0x3FA28723C956C00C, double 0x3FA1ECB45FF312D4, double 0x3FA153D09F19B3A1, double 0x3FA0BC7A0C7CD651, double 0x3FA026B2590DFAEE, double 0x3F9F24F6C7AF9890, double 0x3F9DFFAE7A517468, double 0x3F9CDD9054331B0C, double 0x3F9BBEA150FA5870, double 0x3F9AA2E6E6924E9B, double 0x3F998A670F132A48, double 0x3F98752853EC9967, double 0x3F976331DA87FC96, double 0x3F96548B72A24077, double 0x3F95493DA6AB0251, double 0x3F944151CE87F0BE, double 0x3F933CD225315D84, double 0x3F923BC9E1B93A32, double 0x3F913E4554725F5F, double 0x3F904452091E02F0, double 0x3F8E9BFDDE89C7CE, double 0x3F8CB6B9146E2757, double 0x3F8AD8FA5542C92D, double 0x3F8902EA688FA7BD, double 0x3F8734B6E6AA74F5, double 0x3F856E930BE416CB, double 0x3F83B0B8C1516F62, double 0x3F81FB69EDB37671, double 0x3F804EF2295FD7F9, double 0x3F7D5751FA745DC5, double 0x3F7A23E9D4974836, double 0x3F77049F37EC3620, double 0x3F73FA97CEE322FD, double 0x3F71073D69574043, double 0x3F6C58B381CD4B11, double 0x3F66D888F3A1FEFF, double 0x3F61946BA8E1A324, double 0x3F592BB5540C3E25, double 0x3F4FB20AF78DFCB9, double 0x3F3DC31C329F0B4B], align 16\n    %p = getelementptr inbounds [256 x double], ptr @data, i64 0, i64 %idx\n    %x = load double, ptr %p, align 8\n    ret double %x\n\n@pure\n@llvm\ndef ke_float(idx: int) -> u32:\n    @data = private unnamed_addr constant [256 x i32] [i32 7424081, i32 0, i32 5109103, i32 6405078, i32 6975197, i32 7292064, i32 7492724, i32 7630841, i32 7731567, i32 7808211, i32 7868455, i32 7917038, i32 7957036, i32 7990536, i32 8018999, i32 8043479, i32 8064756, i32 8083420, i32 8099923, i32 8114619, i32 8127790, i32 8139660, i32 8150414, i32 8160200, i32 8169144, i32 8177350, i32 8184905, i32 8191884, i32 8198349, i32 8204355, i32 8209949, i32 8215172, i32 8220059, i32 8224642, i32 8228947, i32 8233000, i32 8236821, i32 8240429, i32 8243842, i32 8247074, i32 8250140, i32 8253052, i32 8255821, i32 8258457, i32 8260969, i32 8263366, i32 8265654, i32 8267842, i32 8269935, i32 8271939, i32 8273860, i32 8275702, i32 8277471, i32 8279169, i32 8280802, i32 8282372, i32 8283884, i32 8285339, i32 8286741, i32 8288093, i32 8289398, i32 8290656, i32 8291872, i32 8293046, i32 8294180, i32 8295277, i32 8296339, i32 8297365, i32 8298359, i32 8299322, i32 8300254, i32 8301158, i32 8302033, i32 8302883, i32 8303706, i32 8304505, i32 8305280, i32 8306033, i32 8306763, i32 8307472, i32 8308161, i32 8308830, i32 8309481, i32 8310113, i32 8310727, i32 8311324, i32 8311904, i32 8312468, i32 8313017, i32 8313550, i32 8314069, i32 8314574, i32 8315064, i32 8315542, i32 8316006, i32 8316458, i32 8316898, i32 8317325, i32 8317741, i32 8318145, i32 8318538, i32 8318921, i32 8319293, i32 8319655, i32 8320006, i32 8320348, i32 8320680, i32 8321003, i32 8321316, i32 8321621, i32 8321917, i32 8322204, i32 8322482, i32 8322753, i32 8323015, i32 8323269, i32 8323515, i32 8323754, i32 8323985, i32 8324208, i32 8324424, i32 8324632, i32 8324834, i32 8325028, i32 8325216, i32 8325396, i32 8325570, i32 8325737, i32 8325897, i32 8326051, i32 8326198, i32 8326338, i32 8326472, i32 8326600, i32 8326721, i32 8326836, i32 8326945, i32 8327048, i32 8327144, i32 8327234, i32 8327318, i32 8327396, i32 8327467, i32 8327533, i32 8327592, i32 8327645, i32 8327692, i32 8327733, i32 8327768, i32 8327796, i32 8327818, i32 8327834, i32 8327844, i32 8327847, i32 8327844, i32 8327835, i32 8327819, i32 8327796, i32 8327767, i32 8327731, i32 8327689, i32 8327640, i32 8327583, i32 8327520, i32 8327450, i32 8327372, i32 8327287, i32 8327195, i32 8327095, i32 8326987, i32 8326871, i32 8326748, i32 8326616, i32 8326476, i32 8326327, i32 8326169, i32 8326003, i32 8325827, i32 8325642, i32 8325447, i32 8325243, i32 8325028, i32 8324802, i32 8324566, i32 8324319, i32 8324060, i32 8323789, i32 8323506, i32 8323211, i32 8322902, i32 8322579, i32 8322243, i32 8321892, i32 8321526, i32 8321144, i32 8320746, i32 8320331, i32 8319898, i32 8319446, i32 8318975, i32 8318484, i32 8317971, i32 8317437, i32 8316879, i32 8316297, i32 8315689, i32 8315054, i32 8314390, i32 8313697, i32 8312972, i32 8312213, i32 8311419, i32 8310587, i32 8309715, i32 8308801, i32 8307841, i32 8306833, i32 8305773, i32 8304657, i32 8303482, i32 8302244, i32 8300936, i32 8299554, i32 8298091, i32 8296541, i32 8294895, i32 8293146, i32 8291284, i32 8289297, i32 8287173, i32 8284897, i32 8282453, i32 8279823, i32 8276983, i32 8273907, i32 8270566, i32 8266924, i32 8262936, i32 8258551, i32 8253706, i32 8248323, i32 8242304, i32 8235529, i32 8227840, i32 8219034, i32 8208841, i32 8196894, i32 8182679, i32 8165457, i32 8144120, i32 8116924, i32 8080947, i32 8030872, i32 7955847, i32 7829465, i32 7564599], align 16\n    %p = getelementptr inbounds [256 x i32], ptr @data, i64 0, i64 %idx\n    %x = load i32, ptr %p, align 8\n    ret i32 %x\n\n@pure\n@llvm\ndef we_float(idx: int) -> float32:\n    @data = private unnamed_addr constant [256 x float] [float 0x3EB164ECA0000000, float 0x3E40589D80000000, float 0x3E4AD6B240000000, float 0x3E519335A0000000, float 0x3E5522E6E0000000, float 0x3E58509100000000, float 0x3E5B38D1E0000000, float 0x3E5DECD8C0000000, float 0x3E603BF040000000, float 0x3E6170DB20000000, float 0x3E629802A0000000, float 0x3E63B38900000000, float 0x3E64C515C0000000, float 0x3E65CDF8A0000000, float 0x3E66CF4100000000, float 0x3E67C9CDE0000000, float 0x3E68BE5960000000, float 0x3E69AD8060000000, float 0x3E6A97C8C0000000, float 0x3E6B7DA5E0000000, float 0x3E6C5F7BE0000000, float 0x3E6D3DA240000000, float 0x3E6E186680000000, float 0x3E6EF00CC0000000, float 0x3E6FC4D260000000, float 0x3E704B76E0000000, float 0x3E70B34840000000, float 0x3E7119F380000000, float 0x3E717F8CE0000000, float 0x3E71E426E0000000, float 0x3E7247D260000000, float 0x3E72AA9EE0000000, float 0x3E730C9AA0000000, float 0x3E736DD2E0000000, float 0x3E73CE53E0000000, float 0x3E742E28C0000000, float 0x3E748D5C60000000, float 0x3E74EBF860000000, float 0x3E754A0620000000, float 0x3E75A78E40000000, float 0x3E760498C0000000, float 0x3E76612D60000000, float 0x3E76BD5360000000, float 0x3E77191180000000, float 0x3E77746E20000000, float 0x3E77CF6F80000000, float 0x3E782A1B60000000, float 0x3E78847720000000, float 0x3E78DE8860000000, float 0x3E793853C0000000, float 0x3E7991DE40000000, float 0x3E79EB2C80000000, float 0x3E7A4442C0000000, float 0x3E7A9D2560000000, float 0x3E7AF5D840000000, float 0x3E7B4E5F80000000, float 0x3E7BA6BEC0000000, float 0x3E7BFEF9A0000000, float 0x3E7C5713A0000000, float 0x3E7CAF1020000000, float 0x3E7D06F280000000, float 0x3E7D5EBE00000000, float 0x3E7DB67560000000, float 0x3E7E0E1C00000000, float 0x3E7E65B480000000, float 0x3E7EBD41E0000000, float 0x3E7F14C6E0000000, float 0x3E7F6C4620000000, float 0x3E7FC3C260000000, float 0x3E800D9F20000000, float 0x3E80395E00000000, float 0x3E80651F20000000, float 0x3E8090E3C0000000, float 0x3E80BCAD00000000, float 0x3E80E87C20000000, float 0x3E81145240000000, float 0x3E81403060000000, float 0x3E816C17E0000000, float 0x3E819809A0000000, float 0x3E81C406E0000000, float 0x3E81F010A0000000, float 0x3E821C27E0000000, float 0x3E82484DC0000000, float 0x3E82748340000000, float 0x3E82A0C980000000, float 0x3E82CD2160000000, float 0x3E82F98C20000000, float 0x3E83260A80000000, float 0x3E83529DA0000000, float 0x3E837F46A0000000, float 0x3E83AC0640000000, float 0x3E83D8DDC0000000, float 0x3E8405CE00000000, float 0x3E8432D7E0000000, float 0x3E845FFCA0000000, float 0x3E848D3D00000000, float 0x3E84BA9A20000000, float 0x3E84E81500000000, float 0x3E8515AE80000000, float 0x3E854367C0000000, float 0x3E857141C0000000, float 0x3E859F3D60000000, float 0x3E85CD5BE0000000, float 0x3E85FB9E00000000, float 0x3E862A04E0000000, float 0x3E865891A0000000, float 0x3E86874520000000, float 0x3E86B62080000000, float 0x3E86E524C0000000, float 0x3E87145320000000, float 0x3E8743AC60000000, float 0x3E877331E0000000, float 0x3E87A2E480000000, float 0x3E87D2C560000000, float 0x3E8802D5C0000000, float 0x3E883316C0000000, float 0x3E88638960000000, float 0x3E88942EC0000000, float 0x3E88C50840000000, float 0x3E88F61700000000, float 0x3E89275C00000000, float 0x3E8958D8C0000000, float 0x3E898A8E40000000, float 0x3E89BC7DE0000000, float 0x3E89EEA8E0000000, float 0x3E8A211080000000, float 0x3E8A53B640000000, float 0x3E8A869B40000000, float 0x3E8AB9C0E0000000, float 0x3E8AED28A0000000, float 0x3E8B20D3E0000000, float 0x3E8B54C400000000, float 0x3E8B88FA80000000, float 0x3E8BBD78E0000000, float 0x3E8BF240A0000000, float 0x3E8C275340000000, float 0x3E8C5CB280000000, float 0x3E8C925FE0000000, float 0x3E8CC85D00000000, float 0x3E8CFEAB80000000, float 0x3E8D354D40000000, float 0x3E8D6C43E0000000, float 0x3E8DA39160000000, float 0x3E8DDB3740000000, float 0x3E8E1337C0000000, float 0x3E8E4B9480000000, float 0x3E8E844FA0000000, float 0x3E8EBD6B20000000, float 0x3E8EF6E900000000, float 0x3E8F30CB60000000, float 0x3E8F6B14A0000000, float 0x3E8FA5C6C0000000, float 0x3E8FE0E400000000, float 0x3E900E3780000000, float 0x3E902C34E0000000, float 0x3E904A6BA0000000, float 0x3E9068DCC0000000, float 0x3E908789C0000000, float 0x3E90A67400000000, float 0x3E90C59CA0000000, float 0x3E90E50560000000, float 0x3E9104AF60000000, float 0x3E91249C60000000, float 0x3E9144CDE0000000, float 0x3E91654580000000, float 0x3E918604E0000000, float 0x3E91A70DA0000000, float 0x3E91C861A0000000, float 0x3E91EA02A0000000, float 0x3E920BF2A0000000, float 0x3E922E3360000000, float 0x3E9250C6E0000000, float 0x3E9273AF60000000, float 0x3E9296EEE0000000, float 0x3E92BA87C0000000, float 0x3E92DE7C00000000, float 0x3E9302CE60000000, float 0x3E93278100000000, float 0x3E934C96C0000000, float 0x3E93721200000000, float 0x3E9397F5A0000000, float 0x3E93BE4480000000, float 0x3E93E50180000000, float 0x3E940C2FE0000000, float 0x3E9433D2C0000000, float 0x3E945BED80000000, float 0x3E948483A0000000, float 0x3E94AD98A0000000, float 0x3E94D73080000000, float 0x3E95014F00000000, float 0x3E952BF880000000, float 0x3E95573100000000, float 0x3E9582FD40000000, float 0x3E95AF6200000000, float 0x3E95DC6400000000, float 0x3E960A08A0000000, float 0x3E96385520000000, float 0x3E96674F60000000, float 0x3E9696FD40000000, float 0x3E96C76520000000, float 0x3E96F88DC0000000, float 0x3E972A7DC0000000, float 0x3E975D3CE0000000, float 0x3E9790D2C0000000, float 0x3E97C54780000000, float 0x3E97FAA3E0000000, float 0x3E9830F120000000, float 0x3E98683900000000, float 0x3E98A085C0000000, float 0x3E98D9E280000000, float 0x3E99145AE0000000, float 0x3E994FFB40000000, float 0x3E998CD100000000, float 0x3E99CAEA40000000, float 0x3E9A0A5640000000, float 0x3E9A4B2540000000, float 0x3E9A8D68C0000000, float 0x3E9AD13380000000, float 0x3E9B1699C0000000, float 0x3E9B5DB160000000, float 0x3E9BA691E0000000, float 0x3E9BF154E0000000, float 0x3E9C3E1640000000, float 0x3E9C8CF440000000, float 0x3E9CDE0FE0000000, float 0x3E9D318D60000000, float 0x3E9D879480000000, float 0x3E9DE050A0000000, float 0x3E9E3BF260000000, float 0x3E9E9AAF20000000, float 0x3E9EFCC260000000, float 0x3E9F626EA0000000, float 0x3E9FCBFE40000000, float 0x3EA01CE2C0000000, float 0x3EA0561180000000, float 0x3EA091C1C0000000, float 0x3EA0D03180000000, float 0x3EA111A800000000, float 0x3EA1567860000000, float 0x3EA19F03C0000000, float 0x3EA1EBBCA0000000, float 0x3EA23D2BC0000000, float 0x3EA293F5A0000000, float 0x3EA2F0E380000000, float 0x3EA354EE20000000, float 0x3EA3C14EC0000000, float 0x3EA4379760000000, float 0x3EA4B9D7C0000000, float 0x3EA54AD840000000, float 0x3EA5EE7AE0000000, float 0x3EA6AA6760000000, float 0x3EA78750E0000000, float 0x3EA8939FE0000000, float 0x3EA9E9DC00000000, float 0x3EABC39E60000000, float 0x3EAEC9D920000000], align 16\n    %p = getelementptr inbounds [256 x float], ptr @data, i64 0, i64 %idx\n    %x = load float, ptr %p, align 8\n    ret float %x\n\n@pure\n@llvm\ndef fe_float(idx: int) -> float32:\n    @data = private unnamed_addr constant [256 x float] [float 1.000000e+00, float 0x3FEE0545E0000000, float 0x3FECD0A660000000, float 0x3FEBE50080000000, float 0x3FEB210F00000000, float 0x3FEA76BAA0000000, float 0x3FE9DE9720000000, float 0x3FE95431C0000000, float 0x3FE8D4A380000000, float 0x3FE85DE880000000, float 0x3FE7EE8A20000000, float 0x3FE7856EA0000000, float 0x3FE721BB60000000, float 0x3FE6C2C340000000, float 0x3FE667FA60000000, float 0x3FE610EDC0000000, float 0x3FE5BD3D60000000, float 0x3FE56C9880000000, float 0x3FE51EBA20000000, float 0x3FE4D366C0000000, float 0x3FE48A6B00000000, float 0x3FE44399A0000000, float 0x3FE3FECB20000000, float 0x3FE3BBDC40000000, float 0x3FE37AADA0000000, float 0x3FE33B2340000000, float 0x3FE2FD23E0000000, float 0x3FE2C098C0000000, float 0x3FE2856D20000000, float 0x3FE24B8E20000000, float 0x3FE212EAC0000000, float 0x3FE1DB7320000000, float 0x3FE1A518C0000000, float 0x3FE16FCE60000000, float 0x3FE13B87C0000000, float 0x3FE1083940000000, float 0x3FE0D5D880000000, float 0x3FE0A45B80000000, float 0x3FE073B940000000, float 0x3FE043E8E0000000, float 0x3FE014E2C0000000, float 0x3FDFCD3E00000000, float 0x3FDF722D80000000, float 0x3FDF1886E0000000, float 0x3FDEC03D40000000, float 0x3FDE694540000000, float 0x3FDE139380000000, float 0x3FDDBF1D80000000, float 0x3FDD6BD980000000, float 0x3FDD19BDE0000000, float 0x3FDCC8C1E0000000, float 0x3FDC78DCE0000000, float 0x3FDC2A06E0000000, float 0x3FDBDC3820000000, float 0x3FDB8F6960000000, float 0x3FDB4393A0000000, float 0x3FDAF8B040000000, float 0x3FDAAEB8E0000000, float 0x3FDA65A760000000, float 0x3FDA1D7620000000, float 0x3FD9D61F60000000, float 0x3FD98F9E00000000, float 0x3FD949ECA0000000, float 0x3FD9050680000000, float 0x3FD8C0E700000000, float 0x3FD87D8980000000, float 0x3FD83AE9C0000000, float 0x3FD7F90360000000, float 0x3FD7B7D2A0000000, float 0x3FD7775380000000, float 0x3FD7378240000000, float 0x3FD6F85B40000000, float 0x3FD6B9DB20000000, float 0x3FD67BFE80000000, float 0x3FD63EC240000000, float 0x3FD6022320000000, float 0x3FD5C61E20000000, float 0x3FD58AB060000000, float 0x3FD54FD720000000, float 0x3FD5158F80000000, float 0x3FD4DBD700000000, float 0x3FD4A2AB20000000, float 0x3FD46A0920000000, float 0x3FD431EEE0000000, float 0x3FD3FA5A00000000, float 0x3FD3C34840000000, float 0x3FD38CB740000000, float 0x3FD356A520000000, float 0x3FD3210FC0000000, float 0x3FD2EBF520000000, float 0x3FD2B75340000000, float 0x3FD2832860000000, float 0x3FD24F7280000000, float 0x3FD21C3000000000, float 0x3FD1E95F00000000, float 0x3FD1B6FDE0000000, float 0x3FD1850B00000000, float 0x3FD15384E0000000, float 0x3FD12269C0000000, float 0x3FD0F1B860000000, float 0x3FD0C16F00000000, float 0x3FD0918C40000000, float 0x3FD0620F00000000, float 0x3FD032F580000000, float 0x3FD0043EA0000000, float 0x3FCFABD240000000, float 0x3FCF4FE760000000, float 0x3FCEF4BA00000000, float 0x3FCE9A4800000000, float 0x3FCE408EE0000000, float 0x3FCDE78C40000000, float 0x3FCD8F3E20000000, float 0x3FCD37A220000000, float 0x3FCCE0B640000000, float 0x3FCC8A7840000000, float 0x3FCC34E660000000, float 0x3FCBDFFE60000000, float 0x3FCB8BBE80000000, float 0x3FCB3824C0000000, float 0x3FCAE52F40000000, float 0x3FCA92DC40000000, float 0x3FCA412A00000000, float 0x3FC9F016E0000000, float 0x3FC99FA0E0000000, float 0x3FC94FC6A0000000, float 0x3FC9008660000000, float 0x3FC8B1DEA0000000, float 0x3FC863CDC0000000, float 0x3FC8165240000000, float 0x3FC7C96AC0000000, float 0x3FC77D15C0000000, float 0x3FC73151C0000000, float 0x3FC6E61D60000000, float 0x3FC69B7760000000, float 0x3FC6515E60000000, float 0x3FC607D100000000, float 0x3FC5BECE00000000, float 0x3FC5765440000000, float 0x3FC52E6260000000, float 0x3FC4E6F760000000, float 0x3FC4A011E0000000, float 0x3FC459B0C0000000, float 0x3FC413D300000000, float 0x3FC3CE7780000000, float 0x3FC3899D20000000, float 0x3FC34542E0000000, float 0x3FC30167A0000000, float 0x3FC2BE0A80000000, float 0x3FC27B2A80000000, float 0x3FC238C680000000, float 0x3FC1F6DDA0000000, float 0x3FC1B56F20000000, float 0x3FC17479E0000000, float 0x3FC133FD20000000, float 0x3FC0F3F800000000, float 0x3FC0B46980000000, float 0x3FC07550E0000000, float 0x3FC036AD80000000, float 0x3FBFF0FCA0000000, float 0x3FBF758560000000, float 0x3FBEFAF3A0000000, float 0x3FBE814600000000, float 0x3FBE087B00000000, float 0x3FBD909120000000, float 0x3FBD198700000000, float 0x3FBCA35B60000000, float 0x3FBC2E0D00000000, float 0x3FBBB99A60000000, float 0x3FBB460260000000, float 0x3FBAD343C0000000, float 0x3FBA615D40000000, float 0x3FB9F04DC0000000, float 0x3FB9801440000000, float 0x3FB910AF80000000, float 0x3FB8A21E80000000, float 0x3FB8346020000000, float 0x3FB7C77380000000, float 0x3FB75B5780000000, float 0x3FB6F00B40000000, float 0x3FB6858DE0000000, float 0x3FB61BDE60000000, float 0x3FB5B2FBE0000000, float 0x3FB54AE5C0000000, float 0x3FB4E39B00000000, float 0x3FB47D1AE0000000, float 0x3FB41764A0000000, float 0x3FB3B277A0000000, float 0x3FB34E5320000000, float 0x3FB2EAF680000000, float 0x3FB2886120000000, float 0x3FB2269260000000, float 0x3FB1C589A0000000, float 0x3FB1654680000000, float 0x3FB105C880000000, float 0x3FB0A70F20000000, float 0x3FB04919E0000000, float 0x3FAFD7D0C0000000, float 0x3FAF1EF4A0000000, float 0x3FAE679EA0000000, float 0x3FADB1CE40000000, float 0x3FACFD8300000000, float 0x3FAC4ABC60000000, float 0x3FAB997A20000000, float 0x3FAAE9BBC0000000, float 0x3FAA3B8140000000, float 0x3FA98ECA80000000, float 0x3FA8E39760000000, float 0x3FA839E820000000, float 0x3FA791BCC0000000, float 0x3FA6EB1580000000, float 0x3FA645F2C0000000, float 0x3FA5A25520000000, float 0x3FA5003D00000000, float 0x3FA45FAB20000000, float 0x3FA3C0A040000000, float 0x3FA3231D80000000, float 0x3FA28723C0000000, float 0x3FA1ECB460000000, float 0x3FA153D0A0000000, float 0x3FA0BC7A00000000, float 0x3FA026B260000000, float 0x3F9F24F6C0000000, float 0x3F9DFFAE80000000, float 0x3F9CDD9060000000, float 0x3F9BBEA160000000, float 0x3F9AA2E6E0000000, float 0x3F998A6700000000, float 0x3F98752860000000, float 0x3F976331E0000000, float 0x3F96548B80000000, float 0x3F95493DA0000000, float 0x3F944151C0000000, float 0x3F933CD220000000, float 0x3F923BC9E0000000, float 0x3F913E4560000000, float 0x3F90445200000000, float 0x3F8E9BFDE0000000, float 0x3F8CB6B920000000, float 0x3F8AD8FA60000000, float 0x3F8902EA60000000, float 0x3F8734B6E0000000, float 0x3F856E9300000000, float 0x3F83B0B8C0000000, float 0x3F81FB69E0000000, float 0x3F804EF220000000, float 0x3F7D575200000000, float 0x3F7A23E9E0000000, float 0x3F77049F40000000, float 0x3F73FA97C0000000, float 0x3F71073D60000000, float 0x3F6C58B380000000, float 0x3F66D88900000000, float 0x3F61946BA0000000, float 0x3F592BB560000000, float 0x3F4FB20B00000000, float 0x3F3DC31C40000000], align 16\n    %p = getelementptr inbounds [256 x float], ptr @data, i64 0, i64 %idx\n    %x = load float, ptr %p, align 8\n    ret float %x\n\nziggurat_nor_r = 3.6541528853610087963519472518\nziggurat_nor_inv_r = 0.27366123732975827203338247596 # 1.0 / ziggurat_nor_r;\nziggurat_exp_r = 7.6971174701310497140446280481\n\nziggurat_nor_r_f = float32(3.6541528853610087963519472518)\nziggurat_nor_inv_r_f = float32(0.27366123732975827203338247596)\nziggurat_exp_r_f = float32(7.6971174701310497140446280481)\n"
  },
  {
    "path": "stdlib/numpy/reductions.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .ndarray import ndarray\nfrom .routines import asarray, broadcast_to, full, copyto, round, moveaxis, concatenate, take, atleast_1d\nfrom .ndmath import isnan as math_isnan, floor, ceil, subtract, add, true_divide\nfrom .lib.arraysetops import unique\n\nimport util\nimport internal.static as static\n\nnewaxis = None\n\ndef _check_out(out, shape):\n    if not isinstance(out, ndarray):\n        compile_error(\"output must be an array\")\n\n    if out.ndim != static.len(shape):\n        compile_error(\"output parameter has the wrong number of dimensions\")\n\n    if out.shape != shape:\n        raise ValueError(\"output parameter has incorrect shape\")\n\ndef _float(x):\n    T = type(x)\n    if (T is float or T is float32 or T is float16 or T is complex\n            or T is complex64):\n        return x\n    else:\n        return util.cast(x, float)\n\ndef _nan(T: type):\n    if T is float or T is float32 or T is float16:\n        return util.nan(T)\n    elif T is complex:\n        return complex(util.nan64(), util.nan64())\n    elif T is complex64:\n        return complex64(util.nan32(), util.nan32())\n    else:\n        compile_error(\"[internal error] no nan for type \" + T.__name__)\n\ndef _isnan(x):\n    T = type(x)\n    if T is float or T is float32 or T is float16:\n        return util.isnan(x)\n    else:\n        return math_isnan(x)\n\ndef _supports_nan(T: type):\n    return (T is float or T is float32 or T is float16 or T is float128\n            or T is bfloat16 or T is complex or T is complex64)\n\ndef _nan_to_back(v: Ptr[T], n: int, T: type):\n    if _supports_nan(T):\n        fill_index = 0\n        while fill_index < n and not _isnan(v[fill_index]):\n            fill_index += 1\n\n        for i in range(fill_index + 1, n):\n            e = v[i]\n            if not _isnan(e):\n                v[fill_index] = e\n                fill_index += 1\n        return fill_index\n    else:\n        return n\n\ndef _make_reducer(R, ans_type: type, dtype: type, conv_to_float: Literal[bool],\n                  bool_to_int: Literal[bool], **kwargs):\n    if dtype is NoneType:\n        if conv_to_float:\n            ftype = type(_float(ans_type()))\n            return R(ftype, **kwargs)\n        elif bool_to_int and ans_type is bool:\n            return R(int, **kwargs)\n        else:\n            return R(ans_type, **kwargs)\n    else:\n        if conv_to_float:\n            ftype = type(_float(dtype()))\n            return R(ftype, **kwargs)\n        else:\n            return R(dtype, **kwargs)\n\ndef _cast_elem(e0, dtype: type, conv_to_float: Literal[bool]):\n    if dtype is not NoneType:\n        e1 = util.cast(e0, dtype)\n    else:\n        e1 = e0\n\n    if conv_to_float:\n        e2 = _float(e1)\n    else:\n        e2 = e1\n\n    return e2\n\ndef _increment_ptr(p: Ptr[T], stride: int, T: type):\n    return Ptr[T](p.as_byte() + stride)\n\ndef _where_to_array(where, arr):\n    if where is None or isinstance(where, util._NoValue):\n        return None\n    else:\n        return broadcast_to(asarray(where), arr.shape)\n\ndef _pairwise_sum_complex(a: Ptr[C], n: int, stride: int, C: type):\n    PW_BLOCKSIZE: Literal[int] = 128\n    T = type(C().real)\n    sz = T.__elemsize__\n    p = a.as_byte()\n\n    if n < 8:\n        rr = T(-0.0)\n        ri = T(-0.0)\n        for i in range(0, n, 2):\n            rr += Ptr[T](p + i * stride + 0)[0]\n            ri += Ptr[T](p + i * stride + sz)[0]\n        return C(rr, ri)\n    elif n <= PW_BLOCKSIZE:\n        r0 = Ptr[T](p + 0 * stride)[0]\n        r1 = Ptr[T](p + 0 * stride + sz)[0]\n        r2 = Ptr[T](p + 2 * stride)[0]\n        r3 = Ptr[T](p + 2 * stride + sz)[0]\n        r4 = Ptr[T](p + 4 * stride)[0]\n        r5 = Ptr[T](p + 4 * stride + sz)[0]\n        r6 = Ptr[T](p + 6 * stride)[0]\n        r7 = Ptr[T](p + 6 * stride + sz)[0]\n\n        i = 8\n        while i < n - (n & 7):\n            (p + (i + 512//sz)*stride).__prefetch_r3__()\n            r0 += Ptr[T](p + (i + 0) * stride)[0]\n            r1 += Ptr[T](p + (i + 0) * stride + sz)[0]\n            r2 += Ptr[T](p + (i + 2) * stride)[0]\n            r3 += Ptr[T](p + (i + 2) * stride + sz)[0]\n            r4 += Ptr[T](p + (i + 4) * stride)[0]\n            r5 += Ptr[T](p + (i + 4) * stride + sz)[0]\n            r6 += Ptr[T](p + (i + 6) * stride)[0]\n            r7 += Ptr[T](p + (i + 6) * stride + sz)[0]\n            i += 8\n\n        rr = (r0 + r2) + (r4 + r6)\n        ri = (r1 + r3) + (r5 + r7)\n\n        while i < n:\n            rr += Ptr[T](p + i * stride + 0)[0]\n            ri += Ptr[T](p + i * stride + sz)[0]\n            i += 2\n\n        return C(rr, ri)\n    else:\n        n2 = n >> 1\n        n2 -= n2 & 7\n        return (_pairwise_sum_complex(a, n2, stride) +\n                _pairwise_sum_complex(Ptr[C](p + n2 * stride), n - n2, stride))\n\ndef _pairwise_sum(a: Ptr[T], n: int, stride: int, T: type, dtype: type = NoneType):\n    if T is complex or T is complex64:\n        return _pairwise_sum_complex(a, n << 1, stride >> 1)\n\n    if dtype is NoneType:\n        return _pairwise_sum(a, n, stride, dtype=T)\n\n    PW_BLOCKSIZE: Literal[int] = 128\n    p = a.as_byte()\n\n    if n < 8:\n        res = util.cast(T(-0.0), dtype)\n        for i in range(n):\n            res += util.cast(Ptr[T](p + i * stride)[0], dtype)\n        return res\n    elif n <= PW_BLOCKSIZE:\n        r0 = util.cast(Ptr[T](p + 0 * stride)[0], dtype)\n        r1 = util.cast(Ptr[T](p + 1 * stride)[0], dtype)\n        r2 = util.cast(Ptr[T](p + 2 * stride)[0], dtype)\n        r3 = util.cast(Ptr[T](p + 3 * stride)[0], dtype)\n        r4 = util.cast(Ptr[T](p + 4 * stride)[0], dtype)\n        r5 = util.cast(Ptr[T](p + 5 * stride)[0], dtype)\n        r6 = util.cast(Ptr[T](p + 6 * stride)[0], dtype)\n        r7 = util.cast(Ptr[T](p + 7 * stride)[0], dtype)\n\n        i = 8\n        while i < n - (n & 7):\n            (p + (i + 512//T.__elemsize__)*stride).__prefetch_r3__()\n            r0 += util.cast(Ptr[T](p + (i + 0) * stride)[0], dtype)\n            r1 += util.cast(Ptr[T](p + (i + 1) * stride)[0], dtype)\n            r2 += util.cast(Ptr[T](p + (i + 2) * stride)[0], dtype)\n            r3 += util.cast(Ptr[T](p + (i + 3) * stride)[0], dtype)\n            r4 += util.cast(Ptr[T](p + (i + 4) * stride)[0], dtype)\n            r5 += util.cast(Ptr[T](p + (i + 5) * stride)[0], dtype)\n            r6 += util.cast(Ptr[T](p + (i + 6) * stride)[0], dtype)\n            r7 += util.cast(Ptr[T](p + (i + 7) * stride)[0], dtype)\n            i += 8\n\n        res = ((r0 + r1) + (r2 + r3)) + ((r4 + r5) + (r6 + r7))\n        while i < n:\n            res += util.cast(Ptr[T](p + i * stride)[0], dtype)\n            i += 1\n\n        return res\n    else:\n        n2 = n >> 1\n        n2 -= n2 & 7\n        return (_pairwise_sum(a, n2, stride, dtype=dtype) +\n                _pairwise_sum(Ptr[T](p + n2 * stride), n - n2, stride, dtype=dtype))\n\ndef _empty_like(arr, shape, dtype: type):\n    fcontig = arr._should_transpose()\n    p = Ptr[dtype](util.count(shape))\n    return ndarray(shape, p, fcontig=fcontig)\n\ndef _reduce_all(arr,\n                R,\n                empty,\n                dtype: type = NoneType,\n                out=None,\n                keepdims: Literal[bool] = False,\n                where=util._NoValue(),\n                conv_to_float: Literal[bool] = False,\n                bool_to_int: Literal[bool] = False,\n                **kwargs):\n    if out is not None:\n        if keepdims:\n            _check_out(out, (1, ) * arr.ndim)\n        else:\n            _check_out(out, ())\n\n    n = arr.size\n    p = arr.data\n    shape = arr.shape\n    strides = arr.strides\n\n    if empty is not None:\n        if n == 0:\n            empty(**kwargs)\n\n    where = _where_to_array(where, arr)\n    redux = _make_reducer(R, arr.dtype, dtype, conv_to_float, bool_to_int, **kwargs)\n    i = 0\n\n    if where is None:\n        if arr.ndim == 1:\n            stride = strides[0]\n            if hasattr(redux, \"loop\"):\n                redux.loop(p, n, stride, partial=False)\n                i = n\n            else:\n                while i < n:\n                    e = _cast_elem(p[0], dtype, conv_to_float)\n                    redux.accept(e, i)\n                    if redux.done():\n                        break\n                    p = _increment_ptr(p, stride)\n                    i += 1\n        else:\n            if arr._is_contig:\n                if hasattr(redux, \"loop\"):\n                    redux.loop(p, n, arr.itemsize, partial=False)\n                    i = n\n                else:\n                    while i < n:\n                        e = _cast_elem(p[i], dtype, conv_to_float)\n                        redux.accept(e, i)\n                        if redux.done():\n                            break\n                        i += 1\n            else:\n                if hasattr(redux, \"loop\") and arr.ndim > 0:\n                    loop_axis = -1\n                    min_abs_stride = 0x7FFFFFFFFFFFFFFF\n\n                    for j in static.range(arr.ndim):\n                        stride = strides[j]\n                        if stride:\n                            abs_stride = abs(stride)\n                            if abs_stride < min_abs_stride:\n                                loop_axis = j\n                                min_abs_stride = abs_stride\n\n                    if loop_axis == -1:\n                        loop_axis = arr.ndim - 1\n\n                    outer_loop_shape = util.tuple_delete(shape, loop_axis)\n                    loop_size = util.tuple_get(shape, loop_axis)\n                    loop_stride = util.tuple_get(strides, loop_axis)\n\n                    for idx in util.multirange(outer_loop_shape):\n                        idx1 = util.tuple_insert(idx, loop_axis, 0)\n                        q = arr._ptr(idx1)\n                        redux.loop(q, loop_size, loop_stride, partial=True)\n\n                    i = loop_size * util.count(outer_loop_shape)\n                else:\n                    A = arr.T if arr._should_transpose() else arr\n                    for idx in util.multirange(A.shape):\n                        e = _cast_elem(A._ptr(idx)[0], dtype, conv_to_float)\n                        redux.accept(e, i)\n                        if redux.done():\n                            break\n                        i += 1\n    else:\n        if arr._contig_match(where):\n            w = where.data\n            for k in range(n):\n                if not w[k]:\n                    continue\n                e = _cast_elem(p[k], dtype, conv_to_float)\n                redux.accept(e, i)\n                if redux.done():\n                    break\n                i += 1\n        else:\n            transpose = arr._should_transpose(where)\n            A = arr\n            W = where\n            if transpose:\n                A = A.T\n                W = W.T\n\n            for idx in util.multirange(A.shape):\n                if not W._ptr(idx)[0]:\n                    continue\n                e = _cast_elem(A._ptr(idx)[0], dtype, conv_to_float)\n                redux.accept(e, i)\n                if redux.done():\n                    break\n                i += 1\n\n    ans = redux.result(i)\n\n    if out is not None:\n        out.data[0] = util.cast(ans, out.dtype)\n        return out\n    else:\n        if keepdims:\n            return asarray(ans).reshape((1, ) * arr.ndim)\n        else:\n            return ans\n\n@tuple\nclass _GradualFunctor:\n    redux: R\n    k: int\n    kwargs: KW\n    dtype: type\n    conv_to_float: Literal[bool]\n    R: type\n    KW: type\n\n    def __new__(redux: R,\n                k: int,\n                dtype: type,\n                conv_to_float: Literal[bool],\n                kwargs: KW,\n                R: type,\n                KW: type) -> _GradualFunctor[dtype, conv_to_float, R, KW]:\n        return superf(redux, k, kwargs)\n\n    def __call__(self, q, p):\n        e = _cast_elem(p[0], self.dtype, self.conv_to_float)\n        q[0] = util.cast(\n                self.redux.gradual_accept(q[0], e, self.k, **self.kwargs), type(q[0]))\n\ndef _reduce_gradual(arr,\n                    R,\n                    empty,\n                    axis=None,\n                    dtype: type = NoneType,\n                    out=None,\n                    keepdims: Literal[bool] = False,\n                    conv_to_float: Literal[bool] = False,\n                    bool_to_int: Literal[bool] = False,\n                    **kwargs):\n    data = arr.data\n    shape = arr.shape\n    strides = arr.strides\n    ax = axis\n\n    redux = _make_reducer(R, arr.dtype, dtype, conv_to_float,\n                          bool_to_int, **kwargs)\n    if hasattr(redux, \"gradual_init\"):\n        init_value = redux.gradual_init(**kwargs)\n    else:\n        init_value = None\n\n    if static.len(ax) == 1:\n        ax0 = ax[0]\n        length = util.tuple_get(shape, ax0)\n        stride = util.tuple_get(arr.strides, ax0)\n        iter_shape = util.tuple_delete(shape, ax0)\n        sub_strides = util.tuple_delete(arr.strides, ax0)\n\n        if keepdims:\n            ans_shape = util.tuple_set(shape, ax0, 1)\n        else:\n            ans_shape = iter_shape\n\n        if out is None:\n            out_type = type(redux.result(0))\n            ans = _empty_like(arr, iter_shape, out_type)\n        else:\n            _check_out(out, ans_shape)\n            if keepdims:\n                sub_ans_strides = util.tuple_delete(out.strides, ax0)\n                ans = ndarray(iter_shape, sub_ans_strides, out.data)\n            else:\n                ans = out\n\n        if init_value is not None:\n            for i in range(ans.size):\n                ans.data[i] = util.cast(init_value, ans.dtype)\n\n        for k in range(length):\n            sub_arr_ptr = _increment_ptr(arr.data, k * stride)\n            sub_arr = ndarray(iter_shape, sub_strides, sub_arr_ptr)\n            fn = _GradualFunctor(redux, k, dtype, conv_to_float, kwargs)\n            ndarray._loop((ans, sub_arr), fn, broadcast='none')\n\n        if hasattr(redux, \"gradual_result\"):\n            ans.map(lambda e: redux.gradual_result(e, length), inplace=True)\n\n        if out is not None:\n            return out\n        elif keepdims:\n            return ans.reshape(ans_shape)\n        elif ans.ndim == 0:\n            return ans.item()\n        else:\n            return ans\n\n    new_shape = (0, ) * (static.len(shape) - static.len(ax))\n    idx_bound = (0, ) * static.len(ax)\n    out_strides = (0, ) * (static.len(shape) - static.len(ax))\n    sub_strides = (0, ) * static.len(ax)\n    mask = (False, ) * static.len(shape)\n    ptr_new_shape = Ptr[int](__ptr__(new_shape).as_byte())\n    ptr_idx_bound = Ptr[int](__ptr__(idx_bound).as_byte())\n    ptr_out_strides = Ptr[int](__ptr__(out_strides).as_byte())\n    ptr_sub_strides = Ptr[int](__ptr__(sub_strides).as_byte())\n    ptr_mask = Ptr[bool](__ptr__(mask).as_byte())\n\n    shape_size = 1\n    bound_size = 1\n    a = 0\n    b = 0\n\n    for i in static.range(static.len(shape)):\n        s = shape[i]\n        stride = strides[i]\n\n        if i in ax:\n            bound_size *= s\n            ptr_idx_bound[a] = s\n            ptr_sub_strides[a] = stride\n            ptr_mask[i] = False\n            a += 1\n        else:\n            shape_size *= s\n            ptr_new_shape[b] = s\n            ptr_out_strides[b] = stride\n            ptr_mask[i] = True\n            b += 1\n\n    if keepdims:\n        ones = (1, ) * static.len(idx_bound)\n        ans_shape = util.reconstruct_index(new_shape, ones, mask)\n    else:\n        ans_shape = new_shape\n\n    redux = _make_reducer(R, arr.dtype, dtype, conv_to_float, bool_to_int, **kwargs)\n    k = 0\n\n    if out is None:\n        out_type = type(redux.result(0))\n        ans = _empty_like(arr, new_shape, out_type)\n    else:\n        _check_out(out, ans_shape)\n        if keepdims:\n            sub_ans_strides = (0, ) * static.len(new_shape)\n            ptr_sub_ans_strides = Ptr[int](__ptr__(sub_ans_strides).as_byte())\n            a = 0\n            for i in static.range(out.ndim):\n                if i not in ax:\n                    ptr_sub_ans_strides[a] = out.strides[i]\n                    a += 1\n            ans = ndarray(new_shape, sub_ans_strides, out.data)\n        else:\n            ans = out\n\n    if init_value is not None:\n        for i in range(ans.size):\n            ans.data[i] = util.cast(init_value, ans.dtype)\n\n    if arr._should_transpose():\n        idx_bound = idx_bound[::-1]\n        sub_strides = sub_strides[::-1]\n\n    for t2 in util.multirange(idx_bound):\n        offset = 0\n        for i in static.range(static.len(sub_strides)):\n            offset += sub_strides[i] * t2[i]\n\n        sub_arr_ptr = _increment_ptr(arr.data, offset)\n        sub_arr = ndarray(new_shape, out_strides, sub_arr_ptr)\n        fn = _GradualFunctor(redux, k, dtype, conv_to_float, kwargs)\n        ndarray._loop((ans, sub_arr), fn, broadcast='none')\n        k += 1\n\n    if hasattr(redux, \"gradual_result\"):\n        ans.map(lambda e: redux.gradual_result(e, bound_size), inplace=True)\n\n    if out is not None:\n        return out\n    elif keepdims:\n        return ans.reshape(ans_shape)\n    elif ans.ndim == 0:\n        return ans.item()\n    else:\n        return ans\n\ndef _reduce(arr,\n            R,\n            empty,\n            axis=None,\n            dtype: type = NoneType,\n            out=None,\n            keepdims: Literal[bool] = False,\n            where=util._NoValue(),\n            conv_to_float: Literal[bool] = False,\n            bool_to_int: Literal[bool] = False,\n            **kwargs):\n    data = arr.data\n    shape = arr.shape\n    strides = arr.strides\n\n    # Strangely, NumPy supports this, so we do too...\n    if arr.ndim == 0 and isinstance(axis, int):\n        if axis != 0 and axis != -1:\n            util.normalize_axis_index(axis=axis, ndim=0)  # raises error\n        return _reduce(arr,\n                       R=R,\n                       empty=empty,\n                       axis=None,\n                       dtype=dtype,\n                       out=out,\n                       keepdims=keepdims,\n                       where=where,\n                       conv_to_float=conv_to_float,\n                       bool_to_int=bool_to_int,\n                       **kwargs)\n\n    if axis is None:\n        ax = util.tuple_range(arr.ndim)\n    elif isinstance(axis, int):\n        ax = (util.normalize_axis_index(axis, arr.ndim), )\n    else:\n        ax = util.normalize_axis_tuple(axis, arr.ndim)\n\n    if static.len(ax) == static.len(shape):\n        return _reduce_all(arr,\n                           R=R,\n                           empty=empty,\n                           dtype=dtype,\n                           out=out,\n                           keepdims=keepdims,\n                           where=where,\n                           conv_to_float=conv_to_float,\n                           bool_to_int=bool_to_int,\n                           **kwargs)\n\n    if empty is not None:\n        if arr.size == 0:\n            empty(**kwargs)\n\n    new_shape = (0, ) * (static.len(shape) - static.len(ax))\n    idx_bound = (0, ) * static.len(ax)\n    out_strides = (0, ) * (static.len(shape) - static.len(ax))\n    sub_strides = (0, ) * static.len(ax)\n    mask = (False, ) * static.len(shape)\n    ptr_new_shape = Ptr[int](__ptr__(new_shape).as_byte())\n    ptr_idx_bound = Ptr[int](__ptr__(idx_bound).as_byte())\n    ptr_out_strides = Ptr[int](__ptr__(out_strides).as_byte())\n    ptr_sub_strides = Ptr[int](__ptr__(sub_strides).as_byte())\n    ptr_mask = Ptr[bool](__ptr__(mask).as_byte())\n\n    shape_size = 1\n    bound_size = 1\n    min_stride_bound = -1\n    min_stride_shape = -1\n    a = 0\n    b = 0\n\n    for i in static.range(static.len(shape)):\n        s = shape[i]\n        stride = strides[i]\n\n        if i in ax:\n            bound_size *= s\n            ptr_idx_bound[a] = s\n            ptr_sub_strides[a] = stride\n            ptr_mask[i] = False\n            if (stride and\n                (min_stride_bound == -1 or abs(stride) < min_stride_bound)):\n                min_stride_bound = stride\n            a += 1\n        else:\n            shape_size *= s\n            ptr_new_shape[b] = s\n            ptr_out_strides[b] = stride\n            ptr_mask[i] = True\n            if (stride and\n                (min_stride_shape == -1 or abs(stride) < min_stride_shape)):\n                min_stride_shape = stride\n            b += 1\n\n    if hasattr(type(\n            _make_reducer(R, arr.dtype, dtype, conv_to_float,\n                          bool_to_int, **kwargs)), \"gradual_init\"):\n        if (out is None and (where is None or isinstance(where, util._NoValue)) and arr.ndim > 1 and\n            min_stride_shape > 0 and min_stride_shape < min_stride_bound):\n            return _reduce_gradual(arr,\n                                   R=R,\n                                   empty=empty,\n                                   axis=ax,\n                                   dtype=dtype,\n                                   out=out,\n                                   keepdims=keepdims,\n                                   conv_to_float=conv_to_float,\n                                   bool_to_int=bool_to_int,\n                                   **kwargs)\n    if keepdims:\n        ones = (1, ) * static.len(idx_bound)\n        ans_shape = util.reconstruct_index(new_shape, ones, mask)\n    else:\n        ans_shape = new_shape\n\n    if out is None:\n        out_type = type(\n            _make_reducer(R, arr.dtype, dtype, conv_to_float,\n                          bool_to_int, **kwargs).result(0))\n        ans = _empty_like(arr, new_shape, out_type)\n    else:\n        _check_out(out, ans_shape)\n        if keepdims:\n            if static.len(ax) == 1:\n                sub_ans_strides = util.tuple_delete(out.strides, ax[0])\n            else:\n                sub_ans_strides = (0, ) * static.len(new_shape)\n                ptr_sub_ans_strides = Ptr[int](__ptr__(sub_ans_strides).as_byte())\n                a = 0\n                for i in static.range(out.ndim):\n                    if i not in ax:\n                        ptr_sub_ans_strides[a] = out.strides[i]\n                        a += 1\n            ans = ndarray(new_shape, sub_ans_strides, out.data)\n        else:\n            ans = out\n\n    where = _where_to_array(where, arr)\n    where_out_shape = (0, ) * (static.len(shape) - static.len(ax))\n    where_sub_shape = (0, ) * static.len(ax)\n    where_out_strides = (0, ) * (static.len(shape) - static.len(ax))\n    where_sub_strides = (0, ) * static.len(ax)\n\n    if where is not None and static.len(shape) > 0:\n        ptr_where_out_shape = Ptr[int](__ptr__(where_out_shape).as_byte())\n        ptr_where_sub_shape = Ptr[int](__ptr__(where_sub_shape).as_byte())\n        ptr_where_out_strides = Ptr[int](__ptr__(where_out_strides).as_byte())\n        ptr_where_sub_strides = Ptr[int](__ptr__(where_sub_strides).as_byte())\n        a = 0\n        b = 0\n        for i in static.range(static.len(shape)):\n            if i in ax:\n                ptr_where_sub_shape[a] = where.shape[i]\n                ptr_where_sub_strides[a] = where.strides[i]\n                a += 1\n            else:\n                ptr_where_out_shape[b] = where.shape[i]\n                ptr_where_out_strides[b] = where.strides[i]\n                b += 1\n\n    if arr._should_transpose():\n        new_shape = new_shape[::-1]\n        out_strides = out_strides[::-1]\n        where_out_strides = where_out_strides[::-1]\n        ans1 = ndarray(ans.shape, ans.strides[::-1], ans.data)\n    else:\n        ans1 = ans\n\n    for idx in util.multirange(new_shape):\n        offset = 0\n        for i in static.range(static.len(out_strides)):\n            offset += out_strides[i] * idx[i]\n\n        sub_arr_ptr = _increment_ptr(arr.data, offset)\n        sub_arr = ndarray(idx_bound, sub_strides, sub_arr_ptr)\n\n        if where is None:\n            sub_where = None\n        else:\n            offset = 0\n            for i in static.range(static.len(where_out_strides)):\n                offset += where_out_strides[i] * idx[i]\n\n            sub_where_ptr = _increment_ptr(where.data, offset)\n            sub_where = ndarray(where_sub_shape, where_sub_strides, sub_where_ptr)\n\n        sub_rdx = _reduce_all(sub_arr,\n                              R=R,\n                              empty=None,\n                              dtype=dtype,\n                              out=None,\n                              keepdims=False,\n                              where=sub_where,\n                              conv_to_float=conv_to_float,\n                              bool_to_int=bool_to_int,\n                              **kwargs)\n\n        ans1._ptr(idx)[0] = util.cast(sub_rdx, ans.dtype)\n\n    if out is not None:\n        return out\n    elif keepdims:\n        return ans.reshape(ans_shape)\n    elif ans.ndim == 0:\n        return ans.item()\n    else:\n        return ans\n\nclass _FlattenFunctor:\n    buffer: Ptr[dtype]\n    k: int\n    dtype: type\n\n    def __init__(self, buffer: Ptr[dtype]):\n        self.buffer = buffer\n        self.k = 0\n\n    def __call__(self, x):\n        self.buffer[self.k] = x[0]\n        self.k += 1\n\nclass _FlattenWhereFunctor:\n    buffer: Ptr[dtype]\n    k: int\n    dtype: type\n\n    def __init__(self, buffer: Ptr[dtype]):\n        self.buffer = buffer\n        self.k = 0\n\n    def __call__(self, x, w):\n        if w[0]:\n            self.buffer[self.k] = x[0]\n            self.k += 1\n\ndef _reduce_buffered(arr,\n                     reducer,\n                     dtype: type,\n                     axis=None,\n                     out=None,\n                     overwrite_input: bool = False,\n                     force_contig: bool = True,\n                     keepdims: Literal[bool] = False,\n                     where=util._NoValue(),\n                     **kwargs):\n    data = arr.data\n    shape = arr.shape\n    strides = arr.strides\n    where = _where_to_array(where, arr)\n\n    if axis is None:\n        ax = util.tuple_range(arr.ndim)\n    elif isinstance(axis, int):\n        ax = (util.normalize_axis_index(axis, arr.ndim), )\n    else:\n        ax = util.normalize_axis_tuple(axis, arr.ndim)\n\n    if static.len(ax) == static.len(shape):\n        sz = arr.size\n        if arr._is_contig and overwrite_input and where is None:\n            result = reducer(arr.data, util.sizeof(arr.dtype), sz, dtype,\n                             **kwargs)\n        else:\n            buffer = Ptr[arr.dtype](sz)\n            if where is None:\n                fn = _FlattenFunctor(buffer)\n                ndarray._loop((arr,), fn, broadcast='none')\n                n = sz\n            else:\n                fn = _FlattenWhereFunctor(buffer)\n                ndarray._loop((arr, where), fn, broadcast='none')\n                n = fn.k\n            result = reducer(buffer, util.sizeof(arr.dtype), n, dtype,\n                             **kwargs)\n            util.free(buffer)\n\n        if out is None:\n            if keepdims:\n                return asarray(result).reshape((1, ) * arr.ndim)\n            else:\n                return result\n        else:\n            if keepdims:\n                _check_out(out, (1, ) * arr.ndim)\n            else:\n                _check_out(out, ())\n            out.data[0] = util.cast(result, out.dtype)\n            return out\n\n    new_shape = (0, ) * (static.len(shape) - static.len(ax))\n    idx_bound = (0, ) * static.len(ax)\n    out_strides = (0, ) * (static.len(shape) - static.len(ax))\n    stride_bound = (0, ) * static.len(ax)\n    mask = (False, ) * static.len(shape)\n    ptr_new_shape = Ptr[int](__ptr__(new_shape).as_byte())\n    ptr_idx_bound = Ptr[int](__ptr__(idx_bound).as_byte())\n    ptr_out_strides = Ptr[int](__ptr__(out_strides).as_byte())\n    ptr_stride_bound = Ptr[int](__ptr__(stride_bound).as_byte())\n    ptr_mask = Ptr[bool](__ptr__(mask).as_byte())\n\n    shape_size = 1\n    bound_size = 1\n    a = 0\n    b = 0\n\n    for i in static.range(static.len(shape)):\n        s = shape[i]\n        stride = strides[i]\n\n        if i in ax:\n            bound_size *= s\n            ptr_idx_bound[a] = s\n            ptr_stride_bound[a] = stride\n            ptr_mask[i] = False\n            a += 1\n        else:\n            shape_size *= s\n            ptr_new_shape[b] = s\n            ptr_out_strides[b] = stride\n            ptr_mask[i] = True\n            b += 1\n\n    if keepdims:\n        ones = (1, ) * static.len(idx_bound)\n        ans_shape = util.reconstruct_index(new_shape, ones, mask)\n    else:\n        ans_shape = new_shape\n\n    if out is None:\n        out_type = type(reducer(Ptr[arr.dtype](), 0, 0, dtype, **kwargs))\n        ans = _empty_like(arr, new_shape, out_type)\n    else:\n        _check_out(out, ans_shape)\n        if keepdims:\n            if static.len(ax) == 1:\n                sub_ans_strides = util.tuple_delete(out.strides, ax[0])\n            else:\n                sub_ans_strides = (0, ) * static.len(new_shape)\n                ptr_sub_ans_strides = Ptr[int](__ptr__(sub_ans_strides).as_byte())\n                a = 0\n                for i in static.range(out.ndim):\n                    if i not in ax:\n                        ptr_sub_ans_strides[a] = out.strides[i]\n                        a += 1\n            ans = ndarray(new_shape, sub_ans_strides, out.data)\n        else:\n            ans = out\n\n    inplace = False\n    stride = 0\n\n    if where is not None or not overwrite_input:\n        inplace = False\n        stride = util.sizeof(arr.dtype)\n    else:\n        if static.len(ax) == 1:\n            inplace = True\n            stride = stride_bound[0]\n        else:\n            if stride_bound == util.strides(idx_bound, False, arr.dtype):\n                inplace = True\n                stride = stride_bound[-1]\n            elif stride_bound == util.strides(idx_bound, True, arr.dtype):\n                inplace = True\n                stride = stride_bound[0]\n            else:\n                inplace = False\n                stride = util.sizeof(arr.dtype)\n\n        if force_contig and stride != util.sizeof(arr.dtype):\n            inplace = False\n            stride = util.sizeof(arr.dtype)\n\n    where_out_shape = (0, ) * (static.len(shape) - static.len(ax))\n    where_sub_shape = (0, ) * static.len(ax)\n    where_out_strides = (0, ) * (static.len(shape) - static.len(ax))\n    where_sub_strides = (0, ) * static.len(ax)\n\n    if where is not None and static.len(shape) > 0:\n        ptr_where_out_shape = Ptr[int](__ptr__(where_out_shape).as_byte())\n        ptr_where_sub_shape = Ptr[int](__ptr__(where_sub_shape).as_byte())\n        ptr_where_out_strides = Ptr[int](__ptr__(where_out_strides).as_byte())\n        ptr_where_sub_strides = Ptr[int](__ptr__(where_sub_strides).as_byte())\n        a = 0\n        b = 0\n        for i in static.range(static.len(shape)):\n            if i in ax:\n                ptr_where_sub_shape[a] = where.shape[i]\n                ptr_where_sub_strides[a] = where.strides[i]\n                a += 1\n            else:\n                ptr_where_out_shape[b] = where.shape[i]\n                ptr_where_out_strides[b] = where.strides[i]\n                b += 1\n\n    buffer = Ptr[arr.dtype]() if inplace else Ptr[arr.dtype](bound_size)\n\n    if arr._should_transpose():\n        new_shape = new_shape[::-1]\n        out_strides = out_strides[::-1]\n        where_out_strides = where_out_strides[::-1]\n        ans1 = ndarray(ans.shape, ans.strides[::-1], ans.data)\n    else:\n        ans1 = ans\n\n    for idx in util.multirange(new_shape):\n        n = bound_size\n        offset = 0\n        for i in static.range(static.len(out_strides)):\n            offset += out_strides[i] * idx[i]\n        sub_arr_ptr = _increment_ptr(arr.data, offset)\n\n        if inplace:\n            data_ptr = sub_arr_ptr\n        else:\n            sub_arr = ndarray(idx_bound, stride_bound, sub_arr_ptr)\n            if where is None:\n                fn = _FlattenFunctor(buffer)\n                ndarray._loop((sub_arr,), fn, broadcast='none')\n            else:\n                offset = 0\n                for i in static.range(static.len(where_out_strides)):\n                    offset += where_out_strides[i] * idx[i]\n\n                sub_where_ptr = _increment_ptr(where.data, offset)\n                sub_where = ndarray(where_sub_shape, where_sub_strides, sub_where_ptr)\n\n                fn = _FlattenWhereFunctor(buffer)\n                ndarray._loop((sub_arr, sub_where), fn, broadcast='none')\n                n = fn.k\n            data_ptr = buffer\n\n        result = reducer(data_ptr, stride, n, dtype, **kwargs)\n        ans1._ptr(idx)[0] = util.cast(result, ans.dtype)\n\n    if not inplace:\n        util.free(buffer)\n\n    if out is not None:\n        return out\n    elif keepdims:\n        return ans.reshape(ans_shape)\n    elif ans.ndim == 0:\n        return ans.item()\n    else:\n        return ans\n\ndef _reduce_buffered_multi(arr,\n                           reducer,\n                           multi_num: int,\n                           dtype: type,\n                           axis=None,\n                           out=None,\n                           overwrite_input: bool = False,\n                           force_contig: bool = True,\n                           keepdims: Literal[bool] = False,\n                           where=util._NoValue(),\n                           **kwargs):\n    data = arr.data\n    shape = arr.shape\n    strides = arr.strides\n    where = _where_to_array(where, arr)\n\n    if axis is None:\n        ax = util.tuple_range(arr.ndim)\n    elif isinstance(axis, int):\n        ax = (util.normalize_axis_index(axis, arr.ndim), )\n    else:\n        ax = util.normalize_axis_tuple(axis, arr.ndim)\n\n    new_shape = (0, ) * (static.len(shape) - static.len(ax))\n    idx_bound = (0, ) * static.len(ax)\n    stride_bound = (0, ) * static.len(ax)\n    mask = (False, ) * static.len(shape)\n    ptr_new_shape = Ptr[int](__ptr__(new_shape).as_byte())\n    ptr_idx_bound = Ptr[int](__ptr__(idx_bound).as_byte())\n    ptr_stride_bound = Ptr[int](__ptr__(stride_bound).as_byte())\n    ptr_mask = Ptr[bool](__ptr__(mask).as_byte())\n\n    shape_size = 1\n    bound_size = 1\n    a = 0\n    b = 0\n\n    for i in static.range(static.len(shape)):\n        s = shape[i]\n\n        if i in ax:\n            bound_size *= s\n            ptr_idx_bound[a] = s\n            ptr_stride_bound[a] = strides[i]\n            ptr_mask[i] = False\n            a += 1\n        else:\n            shape_size *= s\n            ptr_new_shape[b] = s\n            ptr_mask[i] = True\n            b += 1\n\n    if keepdims:\n        ones = (1, ) * static.len(idx_bound)\n        ans_shape = (multi_num, ) + util.reconstruct_index(\n            new_shape, ones, mask)\n    else:\n        ans_shape = (multi_num, ) + new_shape\n\n    if out is None:\n        ans = _empty_like(arr, ans_shape, dtype)\n    else:\n        _check_out(out, ans_shape)\n        ans = out\n\n    if static.len(ax) == static.len(shape):\n        sz = arr.size\n        if arr._is_contig and overwrite_input and where is None:\n            reducer(arr.data, util.sizeof(arr.dtype), sz, ans.data, dtype,\n                    **kwargs)\n        else:\n            buffer = Ptr[arr.dtype](sz)\n            k = 0\n            for idx in util.multirange(shape):\n                if where is not None:\n                    if not where._ptr(idx)[0]:\n                        continue\n                buffer[k] = arr._ptr(idx)[0]\n                k += 1\n            n = sz if where is None else k\n            reducer(buffer, util.sizeof(arr.dtype), n, ans.data, dtype,\n                    **kwargs)\n            util.free(buffer)\n        return ans\n\n    inplace = False\n    stride = 0\n\n    if where is not None or not overwrite_input:\n        inplace = False\n        stride = util.sizeof(arr.dtype)\n    else:\n        if static.len(ax) == 1:\n            inplace = True\n            stride = stride_bound[0]\n        else:\n            if stride_bound == util.strides(idx_bound, False, arr.dtype):\n                inplace = True\n                stride = stride_bound[-1]\n            elif stride_bound == util.strides(idx_bound, True, arr.dtype):\n                inplace = True\n                stride = stride_bound[0]\n            else:\n                inplace = False\n                stride = util.sizeof(arr.dtype)\n\n        if force_contig and stride != util.sizeof(arr.dtype):\n            inplace = False\n            stride = util.sizeof(arr.dtype)\n\n    buffer = Ptr[arr.dtype]() if inplace else Ptr[arr.dtype](bound_size)\n    out_buffer = Ptr[ans.dtype](multi_num)\n\n    for t1 in util.multirange(new_shape):\n        n = bound_size\n        if inplace:\n            idx = util.reconstruct_index(t1, (0, ) * static.len(idx_bound),\n                                         mask)\n            subdata = arr._ptr(idx)\n        else:\n            k = 0\n            for t2 in util.multirange(idx_bound):\n                idx = util.reconstruct_index(t1, t2, mask)\n                if where is not None:\n                    if not where._ptr(idx)[0]:\n                        continue\n                e = arr._ptr(idx)[0]\n                buffer[k] = e\n                k += 1\n            subdata = buffer\n            if where is not None:\n                n = k\n\n        reducer(subdata, stride, n, out_buffer, dtype, **kwargs)\n\n        if keepdims:\n            zeros = (0, ) * static.len(idx_bound)\n            t3 = util.reconstruct_index(t1, zeros, mask)\n        else:\n            t3 = t1\n\n        for i in range(multi_num):\n            ans._ptr((i, ) + t3)[0] = util.cast(out_buffer[i], ans.dtype)\n\n    if not inplace:\n        util.free(buffer)\n    util.free(out_buffer)\n\n    return ans\n\nclass SumRedux:\n    total: T\n    T: type\n\n    def create(T: type, **kwargs):\n        return SumRedux[T](**kwargs)\n\n    def __init__(self, **kwargs):\n        initial = kwargs.get(\"initial\", T())\n        self.total = util.cast(initial, T)\n\n    def accept(self, item, index: int):\n        self.total += util.cast(item, T)\n\n    def result(self, count: int):\n        return self.total\n\n    def empty(**kwargs):\n        pass\n\n    def done(self):\n        return False\n\n    def gradual_init(self, **kwargs):\n        return util.cast(kwargs.get(\"initial\", T()), T)\n\n    def gradual_accept(self, curr, item, index: int, **kwargs):\n        return curr + util.cast(item, T)\n\n    def _loop(a: Ptr[S], n: int, stride: int, S: type):\n        ans = T()\n\n        if (T is float or T is float32 or T is float16 or\n            T is complex or T is complex64):\n            ans += _pairwise_sum(a, n, stride, dtype=T)\n        else:\n            for i in range(n):\n                item = _increment_ptr(a, i * stride)[0]\n                ans += util.cast(item, T)\n\n        return ans\n\n    def loop(self, a: Ptr[S], n: int, stride: int, partial: Literal[bool], S: type):\n        self.total += SumRedux[T]._loop(a, n, stride, S)\n\nclass NanSumRedux:\n    total: T\n    T: type\n\n    def create(T: type, **kwargs):\n        return NanSumRedux[T](**kwargs)\n\n    def __init__(self, **kwargs):\n        initial = kwargs.get(\"initial\", T())\n        self.total = util.cast(initial, T)\n\n    def accept(self, item, index: int):\n        if not _isnan(item):\n            self.total += util.cast(item, T)\n\n    def result(self, count: int):\n        return self.total\n\n    def empty(**kwargs):\n        pass\n\n    def done(self):\n        return False\n\n    def gradual_init(self, **kwargs):\n        return util.cast(kwargs.get(\"initial\", T()), T)\n\n    def gradual_accept(self, curr, item, index: int, **kwargs):\n        return curr if _isnan(item) else curr + util.cast(item, T)\n\n    def loop(self, a: Ptr[S], n: int, stride: int, partial: Literal[bool], S: type):\n        ans = T()\n\n        for i in range(n):\n            item = _increment_ptr(a, i * stride)[0]\n            if not _isnan(item):\n                ans += util.cast(item, T)\n\n        self.total += ans\n\nclass ProdRedux:\n    total: T\n    T: type\n\n    def create(T: type, **kwargs):\n        return ProdRedux[T](**kwargs)\n\n    def __init__(self, **kwargs):\n        initial = kwargs.get(\"initial\", T(1))\n        self.total = util.cast(initial, T)\n\n    def accept(self, item, index: int):\n        self.total *= util.cast(item, T)\n\n    def result(self, count: int):\n        return self.total\n\n    def empty(**kwargs):\n        pass\n\n    def done(self):\n        return False\n\n    def gradual_init(self, **kwargs):\n        return util.cast(kwargs.get(\"initial\", T(1)), T)\n\n    def gradual_accept(self, curr, item, index: int, **kwargs):\n        return curr * util.cast(item, T)\n\n    def loop(self, a: Ptr[S], n: int, stride: int, partial: Literal[bool], S: type):\n        ans = T(1)\n\n        for i in range(n):\n            item = _increment_ptr(a, i * stride)[0]\n            ans *= util.cast(item, T)\n\n        self.total *= ans\n\nclass NanProdRedux:\n    total: T\n    T: type\n\n    def create(T: type, **kwargs):\n        return NanProdRedux[T](**kwargs)\n\n    def __init__(self, **kwargs):\n        initial = kwargs.get(\"initial\", T(1))\n        self.total = util.cast(initial, T)\n\n    def accept(self, item, index: int):\n        if not _isnan(item):\n            self.total *= util.cast(item, T)\n\n    def result(self, count: int):\n        return self.total\n\n    def empty(**kwargs):\n        pass\n\n    def done(self):\n        return False\n\n    def gradual_init(self, **kwargs):\n        return util.cast(kwargs.get(\"initial\", T(1)), T)\n\n    def gradual_accept(self, curr, item, index: int, **kwargs):\n        return curr if _isnan(item) else curr * util.cast(item, T)\n\n    def loop(self, a: Ptr[S], n: int, stride: int, partial: Literal[bool], S: type):\n        ans = T(1)\n\n        for i in range(n):\n            item = _increment_ptr(a, i * stride)[0]\n            if not _isnan(item):\n                ans *= util.cast(item, T)\n\n        self.total *= ans\n\nclass MeanRedux:\n    total: T\n    T: type\n\n    def create(T: type, **kwargs):\n        return MeanRedux[T](**kwargs)\n\n    def __init__(self, **kwargs):\n        self.total = T()\n\n    def accept(self, item: T, index: int):\n        self.total += item\n\n    def result(self, count: int):\n        return self.total / T(count) if count else _nan(T)\n\n    def empty(**kwargs):\n        pass\n\n    def done(self):\n        return False\n\n    def gradual_init(self, **kwargs):\n        return T()\n\n    def gradual_accept(self, curr, item, index: int, **kwargs):\n        return curr + util.cast(item, T)\n\n    def gradual_result(self, curr, count: int):\n        return curr / T(count) if count else _nan(T)\n\n    def loop(self, a: Ptr[S], n: int, stride: int, partial: Literal[bool], S: type):\n        self.total += SumRedux[T]._loop(a, n, stride, S)\n\nclass NanMeanRedux:\n    total: T\n    T: type\n    nan_count: int\n\n    def create(T: type, **kwargs):\n        return NanMeanRedux[T](**kwargs)\n\n    def __init__(self, **kwargs):\n        self.total = T()\n        self.nan_count = 0\n\n    def accept(self, item: T, index: int):\n        if not _isnan(item):\n            self.total += item\n        else:\n            self.nan_count += 1\n\n    def result(self, count: int):\n        count -= self.nan_count\n        return self.total / T(count) if count else _nan(T)\n\n    def empty(**kwargs):\n        pass\n\n    def done(self):\n        return False\n\n    def loop(self, a: Ptr[S], n: int, stride: int, partial: Literal[bool], S: type):\n        ans = T()\n        nan_count = 0\n\n        for i in range(n):\n            item = _increment_ptr(a, i * stride)[0]\n            if _isnan(item):\n                nan_count += 1\n            else:\n                ans += util.cast(item, T)\n\n        self.total += ans\n        self.nan_count += nan_count\n\nclass MinRedux:\n    m: Optional[T]\n    T: type\n\n    def create(T: type, **kwargs):\n        return MinRedux[T](**kwargs)\n\n    def __init__(self, **kwargs):\n        initial = kwargs.get(\"initial\", util._NoValue())\n        if isinstance(initial, util._NoValue):\n            self.m = None\n        else:\n            self.m = util.cast(initial, T)\n\n    def accept(self, item: T, index: int):\n        if self.m is None:\n            self.m = item\n        else:\n            self.m = MinRedux[T]._min(self.m, item)\n\n    def result(self, count: int) -> T:\n        return self.m\n\n    def empty(**kwargs):\n        if isinstance(kwargs.get(\"initial\", util._NoValue()), util._NoValue):\n            raise ValueError(\n                \"zero-size array to reduction operation minimum which has no identity\"\n            )\n\n    def done(self):\n        return False\n\n    def gradual_init(self, **kwargs):\n        initial = kwargs.get(\"initial\", util._NoValue())\n        if isinstance(initial, util._NoValue):\n            return None\n        else:\n            return util.cast(initial, T)\n\n    def gradual_accept(self, curr, item, index: int, **kwargs):\n        item = util.cast(item, T)\n        initial = kwargs.get(\"initial\", util._NoValue())\n        if isinstance(initial, util._NoValue):\n            if index == 0:\n                return item\n        return MinRedux[T]._min(curr, item)\n\n    def _min(m: T, x):\n        x = util.cast(x, T)\n        if T is float or T is float32 or T is float16:\n            return util.fmin(m, x)\n        else:\n            return x if x < m else m\n\n    def loop(self, a: Ptr[S], n: int, stride: int, partial: Literal[bool], S: type):\n        if self.m is None:\n            m: T = util.cast(a[0], T)\n            a = _increment_ptr(a, stride)\n            n -= 1\n        else:\n            m: T = self.m\n\n        for i in range(n):\n            m = MinRedux[T]._min(m, a[0])\n            a = _increment_ptr(a, stride)\n\n        if partial:\n            self.accept(m, 0)\n        else:\n            self.m = m\n\nclass MaxRedux:\n    m: Optional[T]\n    T: type\n\n    def create(T: type, **kwargs):\n        return MaxRedux[T](**kwargs)\n\n    def __init__(self, **kwargs):\n        initial = kwargs.get(\"initial\", util._NoValue())\n        if isinstance(initial, util._NoValue):\n            self.m = None\n        else:\n            self.m = util.cast(initial, T)\n\n    def accept(self, item: T, index: int):\n        if self.m is None:\n            self.m = item\n        else:\n            self.m = MaxRedux[T]._max(self.m, item)\n\n    def result(self, count: int) -> T:\n        return self.m\n\n    def empty(**kwargs):\n        if isinstance(kwargs.get(\"initial\", util._NoValue()), util._NoValue):\n            raise ValueError(\n                \"zero-size array to reduction operation maximum which has no identity\"\n            )\n\n    def done(self):\n        return False\n\n    def gradual_init(self, **kwargs):\n        initial = kwargs.get(\"initial\", util._NoValue())\n        if isinstance(initial, util._NoValue):\n            return None\n        else:\n            return util.cast(initial, T)\n\n    def gradual_accept(self, curr, item, index: int, **kwargs):\n        item = util.cast(item, T)\n        initial = kwargs.get(\"initial\", util._NoValue())\n        if isinstance(initial, util._NoValue):\n            if index == 0:\n                return item\n        return MaxRedux[T]._max(curr, item)\n\n    def _max(m: T, x):\n        x = util.cast(x, T)\n        if T is float or T is float32 or T is float16:\n            return util.fmax(m, x)\n        else:\n            return x if x > m else m\n\n    def loop(self, a: Ptr[S], n: int, stride: int, partial: Literal[bool], S: type):\n        if self.m is None and n > 0:\n            m: T = util.cast(a[0], T)\n            a = _increment_ptr(a, stride)\n            n -= 1\n        else:\n            m: T = self.m\n\n        for i in range(n):\n            m = MaxRedux[T]._max(m, a[0])\n            a = _increment_ptr(a, stride)\n\n        if partial:\n            self.accept(m, 0)\n        else:\n            self.m = m\n\nclass PTPRedux:\n    hi: Optional[T]\n    lo: Optional[T]\n    T: type\n\n    def create(T: type, **kwargs):\n        return PTPRedux[T](**kwargs)\n\n    def __init__(self, **kwargs):\n        self.hi = None\n        self.lo = None\n\n    def accept(self, item: T, index: int):\n        if self.hi is None:\n            self.hi = item\n        else:\n            self.hi = MaxRedux[T]._max(self.hi, item)\n\n        if self.lo is None:\n            self.lo = item\n        else:\n            self.lo = MinRedux[T]._min(self.lo, item)\n\n    def result(self, count: int) -> T:\n        return self.hi - self.lo\n\n    def empty(**kwargs):\n        raise ValueError(\n            \"zero-size array to reduction operation maximum which has no identity\"\n        )\n\n    def done(self):\n        return False\n\n    def loop(self, a: Ptr[S], n: int, stride: int, partial: Literal[bool], S: type):\n        # n must be >0 here or we would've thrown an exception earlier\n        m = util.cast(a[0], T)\n        M = m\n        a = _increment_ptr(a, stride)\n        n -= 1\n\n        for i in range(n):\n            m = MinRedux[T]._min(m, a[0])\n            M = MaxRedux[T]._max(M, a[0])\n            a = _increment_ptr(a, stride)\n\n        if partial:\n            if self.hi is None or (M > self.hi):\n                self.hi = M\n            if self.lo is None or (m < self.lo):\n                self.lo = m\n        else:\n            self.hi = M\n            self.lo = m\n\nclass ArgMinRedux:\n    m: Optional[T]\n    i: int\n    T: type\n\n    def create(T: type, **kwargs):\n        return ArgMinRedux[T](**kwargs)\n\n    def __init__(self, **kwargs):\n        self.m = None\n        self.i = 0\n\n    def accept(self, item: T, index: int):\n        if self.m is None or (item < self.m):\n            self.m = item\n            self.i = index\n\n    def result(self, count: int):\n        return self.i\n\n    def empty(**kwargs):\n        raise ValueError(\"attempt to get argmin of an empty sequence\")\n\n    def done(self):\n        return False\n\nclass ArgMaxRedux:\n    m: Optional[T]\n    i: int\n    T: type\n\n    def create(T: type, **kwargs):\n        return ArgMaxRedux[T](**kwargs)\n\n    def __init__(self, **kwargs):\n        self.m = None\n        self.i = 0\n\n    def accept(self, item: T, index: int):\n        if self.m is None or (item > self.m):\n            self.m = item\n            self.i = index\n\n    def result(self, count: int):\n        return self.i\n\n    def empty(**kwargs):\n        raise ValueError(\"attempt to get argmax of an empty sequence\")\n\n    def done(self):\n        return False\n\nclass AnyRedux:\n    a: bool\n    T: type\n\n    def create(T: type, **kwargs):\n        return AnyRedux[T](**kwargs)\n\n    def __init__(self, **kwargs):\n        self.a = False\n\n    def accept(self, item: T, index: int):\n        if item:\n            self.a = True\n\n    def result(self, count: int):\n        return self.a\n\n    def empty(**kwargs):\n        pass\n\n    def done(self):\n        return self.a\n\nclass AllRedux:\n    a: bool\n    T: type\n\n    def create(T: type, **kwargs):\n        return AllRedux[T](**kwargs)\n\n    def __init__(self, **kwargs):\n        self.a = True\n\n    def accept(self, item: T, index: int):\n        if not item:\n            self.a = False\n\n    def result(self, count: int):\n        return self.a\n\n    def empty(**kwargs):\n        pass\n\n    def done(self):\n        return not self.a\n\nclass NonZeroRedux:\n    nonzero: int\n    T: type\n\n    def create(T: type, **kwargs):\n        return NonZeroRedux[T](**kwargs)\n\n    def __init__(self, **kwargs):\n        self.nonzero = 0\n\n    def accept(self, item: T, index: int):\n        if item:\n            self.nonzero += 1\n\n    def result(self, count: int):\n        return self.nonzero\n\n    def empty(**kwargs):\n        pass\n\n    def done(self):\n        return False\n\n    def gradual_init(self, **kwargs):\n        return 0\n\n    def gradual_accept(self, curr, item, index: int, **kwargs):\n        if item:\n            curr += 1\n        return curr\n\ndef sum(\n        a,\n        axis=None,\n        dtype: type = NoneType,\n        out=None,\n        keepdims: Literal[bool] = False,\n        initial=0,\n        where=util._NoValue(),\n):\n    a = asarray(a)\n    return _reduce(\n        a,\n        R=SumRedux.create,\n        empty=SumRedux.empty,\n        axis=axis,\n        dtype=dtype,\n        out=out,\n        keepdims=keepdims,\n        where=where,\n        initial=initial,\n        bool_to_int=True,\n    )\n\ndef nansum(\n        a,\n        axis=None,\n        dtype: type = NoneType,\n        out=None,\n        keepdims: Literal[bool] = False,\n        initial=0,\n        where=util._NoValue(),\n):\n    a = asarray(a)\n    return _reduce(\n        a,\n        R=NanSumRedux.create,\n        empty=NanSumRedux.empty,\n        axis=axis,\n        dtype=dtype,\n        out=out,\n        keepdims=keepdims,\n        where=where,\n        initial=initial,\n        bool_to_int=True,\n    )\n\ndef prod(\n        a,\n        axis=None,\n        dtype: type = NoneType,\n        out=None,\n        keepdims: Literal[bool] = False,\n        initial=1,\n        where=util._NoValue(),\n):\n    a = asarray(a)\n    return _reduce(\n        a,\n        R=ProdRedux.create,\n        empty=ProdRedux.empty,\n        axis=axis,\n        dtype=dtype,\n        out=out,\n        keepdims=keepdims,\n        where=where,\n        initial=initial,\n        bool_to_int=True,\n    )\n\ndef nanprod(\n        a,\n        axis=None,\n        dtype: type = NoneType,\n        out=None,\n        keepdims: Literal[bool] = False,\n        initial=1,\n        where=util._NoValue(),\n):\n    a = asarray(a)\n    return _reduce(\n        a,\n        R=NanProdRedux.create,\n        empty=NanProdRedux.empty,\n        axis=axis,\n        dtype=dtype,\n        out=out,\n        keepdims=keepdims,\n        where=where,\n        initial=initial,\n        bool_to_int=True,\n    )\n\ndef mean(\n        a,\n        axis=None,\n        dtype: type = NoneType,\n        out=None,\n        keepdims: Literal[bool] = False,\n        where=util._NoValue(),\n):\n    a = asarray(a)\n    return _reduce(\n        a,\n        R=MeanRedux.create,\n        empty=MeanRedux.empty,\n        axis=axis,\n        dtype=dtype,\n        out=out,\n        keepdims=keepdims,\n        where=where,\n        conv_to_float=True,\n    )\n\ndef nanmean(\n        a,\n        axis=None,\n        dtype: type = NoneType,\n        out=None,\n        keepdims: Literal[bool] = False,\n        where=util._NoValue(),\n):\n    a = asarray(a)\n    return _reduce(\n        a,\n        R=NanMeanRedux.create,\n        empty=NanMeanRedux.empty,\n        axis=axis,\n        dtype=dtype,\n        out=out,\n        keepdims=keepdims,\n        where=where,\n        conv_to_float=True,\n    )\n\ndef _var_reducer(p: Ptr[T], s: int, n: int, dtype: type, T: type, **kwargs):\n    if dtype is NoneType:\n        zero = _cast_elem(util.zero(T), dtype, conv_to_float=True)\n    else:\n        zero = _cast_elem(util.zero(dtype), dtype, conv_to_float=True)\n\n    Z = type(zero)\n    u = zero\n    q = p\n    u = _pairwise_sum(p, n, s, dtype=Z)\n    u /= util.cast(n, Z)\n\n    if Z is complex:\n        v = 0.0\n    elif Z is complex64:\n        v = float32(0.0)\n    else:\n        v = zero\n\n    q = p\n    for _ in range(n):\n        t = util.cast(q[0], Z) - u\n        if Z is complex or Z is complex64:\n            r = abs(t)\n            v += r * r\n        else:\n            v += t * t\n        q = _increment_ptr(q, s)\n\n    v /= util.cast(n - kwargs['ddof'], type(v))\n    return v\n\ndef var(\n        a,\n        axis=None,\n        dtype: type = NoneType,\n        out=None,\n        ddof: int = 0,\n        keepdims: Literal[bool] = False,\n        where=util._NoValue(),\n):\n    a = asarray(a)\n    return _reduce_buffered(a,\n                            _var_reducer,\n                            dtype=dtype,\n                            axis=axis,\n                            out=out,\n                            overwrite_input=True,\n                            force_contig=False,\n                            keepdims=keepdims,\n                            where=where,\n                            ddof=ddof)\n\ndef _nanvar_reducer(p: Ptr[T], s: int, n: int, dtype: type, T: type, **kwargs):\n    if dtype is NoneType:\n        zero = _cast_elem(util.zero(T), dtype, conv_to_float=True)\n    else:\n        zero = _cast_elem(util.zero(dtype), dtype, conv_to_float=True)\n\n    Z = type(zero)\n    u = zero\n    q = p\n    nans = 0\n    for _ in range(n):\n        e = util.cast(q[0], Z)\n        if _isnan(e):\n            nans += 1\n        else:\n            u += e\n        q = _increment_ptr(q, s)\n    u /= util.cast(n - nans, Z)\n\n    if Z is complex:\n        v = 0.0\n    elif Z is complex64:\n        v = float32(0.0)\n    else:\n        v = zero\n\n    q = p\n    for _ in range(n):\n        e = util.cast(q[0], Z)\n        if not _isnan(e):\n            t = util.cast(q[0], Z) - u\n            if Z is complex or Z is complex64:\n                r = abs(t)\n                v += r * r\n            else:\n                v += t * t\n        q = _increment_ptr(q, s)\n\n    v /= util.cast(n - nans - kwargs['ddof'], type(v))\n    return v\n\ndef nanvar(\n        a,\n        axis=None,\n        dtype: type = NoneType,\n        out=None,\n        ddof: int = 0,\n        keepdims: Literal[bool] = False,\n        where=util._NoValue(),\n):\n    a = asarray(a)\n    return _reduce_buffered(a,\n                            _nanvar_reducer,\n                            dtype=dtype,\n                            axis=axis,\n                            out=out,\n                            overwrite_input=True,\n                            force_contig=False,\n                            keepdims=keepdims,\n                            where=where,\n                            ddof=ddof)\n\ndef _std_reducer(p: Ptr[T], s: int, n: int, dtype: type, T: type, **kwargs):\n    x = _var_reducer(p=p, s=s, n=n, dtype=dtype, T=T, **kwargs)\n    return util.sqrt(x)\n\ndef std(\n        a,\n        axis=None,\n        dtype: type = NoneType,\n        out=None,\n        ddof: int = 0,\n        keepdims: Literal[bool] = False,\n        where=util._NoValue(),\n):\n    a = asarray(a)\n    return _reduce_buffered(a,\n                            _std_reducer,\n                            dtype=dtype,\n                            axis=axis,\n                            out=out,\n                            overwrite_input=True,\n                            force_contig=False,\n                            keepdims=keepdims,\n                            where=where,\n                            ddof=ddof)\n\ndef _nanstd_reducer(p: Ptr[T], s: int, n: int, dtype: type, T: type, **kwargs):\n    x = _nanvar_reducer(p=p, s=s, n=n, dtype=dtype, T=T, **kwargs)\n    return util.sqrt(x)\n\ndef nanstd(\n        a,\n        axis=None,\n        dtype: type = NoneType,\n        out=None,\n        ddof: int = 0,\n        keepdims: Literal[bool] = False,\n        where=util._NoValue(),\n):\n    a = asarray(a)\n    return _reduce_buffered(a,\n                            _nanstd_reducer,\n                            dtype=dtype,\n                            axis=axis,\n                            out=out,\n                            overwrite_input=True,\n                            force_contig=False,\n                            keepdims=keepdims,\n                            where=where,\n                            ddof=ddof)\n\ndef min(\n        a,\n        axis=None,\n        dtype: type = NoneType,\n        out=None,\n        ddof: int = 0,\n        keepdims: Literal[bool] = False,\n        initial=util._NoValue(),\n        where=util._NoValue(),\n):\n    if not isinstance(where, util._NoValue) and isinstance(\n            initial, util._NoValue):\n        compile_error(\n            \"reduction operation 'minimum' does not have an identity, so to use a where mask one has to specify 'initial'\"\n        )\n    a = asarray(a)\n    return _reduce(\n        a,\n        R=MinRedux.create,\n        empty=MinRedux.empty,\n        axis=axis,\n        dtype=dtype,\n        out=out,\n        keepdims=keepdims,\n        where=where,\n        initial=initial,\n    )\n\ndef max(\n        a,\n        axis=None,\n        dtype: type = NoneType,\n        out=None,\n        ddof: int = 0,\n        keepdims: Literal[bool] = False,\n        initial=util._NoValue(),\n        where=util._NoValue(),\n):\n    if not isinstance(where, util._NoValue) and isinstance(\n            initial, util._NoValue):\n        compile_error(\n            \"reduction operation 'maximum' does not have an identity, so to use a where mask one has to specify 'initial'\"\n        )\n    a = asarray(a)\n    return _reduce(\n        a,\n        R=MaxRedux.create,\n        empty=MaxRedux.empty,\n        axis=axis,\n        dtype=dtype,\n        out=out,\n        keepdims=keepdims,\n        where=where,\n        initial=initial,\n    )\n\ndef ptp(a, axis=None, out=None, keepdims: Literal[bool] = False):\n    a = asarray(a)\n    return _reduce(\n        a,\n        R=PTPRedux.create,\n        empty=PTPRedux.empty,\n        axis=axis,\n        out=out,\n        keepdims=keepdims,\n    )\n\ndef argmin(a, axis=None, out=None, keepdims: Literal[bool] = False):\n    a = asarray(a)\n    return _reduce(\n        a,\n        R=ArgMinRedux.create,\n        empty=ArgMinRedux.empty,\n        axis=axis,\n        out=out,\n        keepdims=keepdims,\n    )\n\ndef argmax(a, axis=None, out=None, keepdims: Literal[bool] = False):\n    a = asarray(a)\n    return _reduce(\n        a,\n        R=ArgMaxRedux.create,\n        empty=ArgMaxRedux.empty,\n        axis=axis,\n        out=out,\n        keepdims=keepdims,\n    )\n\ndef any(a,\n        axis=None,\n        out=None,\n        keepdims: Literal[bool] = False,\n        where=util._NoValue()):\n    a = asarray(a)\n    return _reduce(\n        a,\n        R=AnyRedux.create,\n        empty=AnyRedux.empty,\n        axis=axis,\n        out=out,\n        keepdims=keepdims,\n        where=where,\n    )\n\ndef all(a,\n        axis=None,\n        out=None,\n        keepdims: Literal[bool] = False,\n        where=util._NoValue()):\n    a = asarray(a)\n    return _reduce(\n        a,\n        R=AllRedux.create,\n        empty=AllRedux.empty,\n        axis=axis,\n        out=out,\n        keepdims=keepdims,\n        where=where,\n    )\n\ndef count_nonzero(a, axis=None, keepdims: Literal[bool] = False):\n    a = asarray(a)\n    return _reduce(a,\n                   R=NonZeroRedux.create,\n                   empty=NonZeroRedux.empty,\n                   axis=axis,\n                   keepdims=keepdims)\n\ndef _median_reducer_no_nan(v: Ptr[T], s: int, n: int, dtype: type, T: type,\n                           **kwargs):\n    if n == 0:\n        if T is complex or T is complex64:\n            return _nan(T)\n        else:\n            return _nan(float)\n\n    m1, m2 = util.median(v, n)\n    if n & 1 == 0:\n        if T is complex:\n            return (m1 + m2) / 2.0\n        elif T is complex64:\n            return (m1 + m2) / float32(2.0)\n        else:\n            return (util.cast(m1, float) + util.cast(m2, float)) / 2.0\n    else:\n        if T is complex or T is complex64:\n            return m1\n        else:\n            return util.cast(m1, float)\n\ndef _median_reducer(v: Ptr[T], s: int, n: int, dtype: type, T: type, **kwargs):\n    if _supports_nan(T):\n        for i in range(n):\n            if _isnan(v[i]):\n                if T is complex or T is complex64:\n                    return _nan(T)\n                else:\n                    return _nan(float)\n\n    return _median_reducer_no_nan(v, s, n, dtype, T, **kwargs)\n\ndef median(a,\n           axis=None,\n           out=None,\n           overwrite_input: bool = False,\n           keepdims: Literal[bool] = False):\n    a = asarray(a)\n    return _reduce_buffered(a,\n                            _median_reducer,\n                            dtype=a.dtype,\n                            axis=axis,\n                            out=out,\n                            overwrite_input=overwrite_input,\n                            force_contig=True,\n                            keepdims=keepdims,\n                            where=None)\n\ndef _nanmedian_reducer(v: Ptr[T], s: int, n: int, dtype: type, T: type,\n                       **kwargs):\n    n = _nan_to_back(v, n)\n    return _median_reducer_no_nan(v, s, n, dtype, T, **kwargs)\n\ndef nanmedian(a,\n              axis=None,\n              out=None,\n              overwrite_input: bool = False,\n              keepdims: Literal[bool] = False):\n    a = asarray(a)\n    return _reduce_buffered(a,\n                            _nanmedian_reducer,\n                            dtype=a.dtype,\n                            axis=axis,\n                            out=out,\n                            overwrite_input=overwrite_input,\n                            force_contig=True,\n                            keepdims=keepdims,\n                            where=None)\n\ndef _sorted(ar):\n    if isinstance(ar, ndarray):\n        x = ar.flatten()\n        x.sort()\n        return x\n    else:\n        x = asarray(ar).ravel()\n        x.sort()\n        return x\n\ndef _check_interpolation_as_method(method, interpolation):\n    if method != \"linear\":\n        # sanity check, we assume this basically never happens\n        raise TypeError(\n            \"You shall not pass both `method` and `interpolation`!\\n\"\n            \"(`interpolation` is Deprecated in favor of `method`)\")\n    return interpolation\n\ndef _quantile_is_valid(q):\n    # avoid expensive reductions, relevant for arrays with < O(1000) elements\n    if static.len(q.shape) == 1 and q.size < 10:\n        for i in range(q.size):\n            if not (0.0 <= q[i] <= 1.0):\n                return False\n    else:\n        for idx in util.multirange(q.shape):\n            if not ((0.0 <= q._ptr(idx)[0]) and (q._ptr(idx)[0] <= 1.0)):\n                return False\n    return True\n\ndef _get_gamma_mask(shape, default_value, conditioned_value, where):\n    out = full(shape, default_value)\n    copyto(out, conditioned_value, where=where)\n    return out\n\ndef _discret_interpolation_to_boundaries(index, gamma_condition_fun):\n    if not isinstance(index, ndarray):\n        shape = ()\n    else:\n        shape = index.shape\n    previous = floor(index)\n    next = previous + 1\n    gamma = index - previous\n    res = _get_gamma_mask(shape=shape,\n                          default_value=next,\n                          conditioned_value=previous,\n                          where=gamma_condition_fun(gamma, index)).astype(int)\n    # Some methods can lead to out-of-bound integers so we clip them\n    res[res < 0] = 0\n    return res\n\ndef _inverted_cdf(n, quantiles):\n    gamma_fun = lambda gamma, _: (gamma == 0)\n    return _discret_interpolation_to_boundaries((n * quantiles) - 1, gamma_fun)\n\ndef _closest_observation(n, quantiles):\n    gamma_fun = lambda gamma, index: (gamma == 0) & (floor(index) % 2 == 0)\n    return _discret_interpolation_to_boundaries((n * quantiles) - 1 - 0.5,\n                                                gamma_fun)\n\ndef _compute_virtual_index(n, quantiles, alpha: float, beta: float):\n    return n * quantiles + (alpha + quantiles * (1 - alpha - beta)) - 1\n\ndef _get_indexes(arr, virtual_indexes, valid_values_count, supports_nan: bool):\n    previous_indexes = asarray(floor(virtual_indexes))\n    next_indexes = asarray(previous_indexes + 1)\n    indexes_above_bounds = virtual_indexes >= valid_values_count - 1\n    # When indexes is above max index, take the max value of the array\n    if indexes_above_bounds.any():\n        previous_indexes[indexes_above_bounds] = -1\n        next_indexes[indexes_above_bounds] = -1\n    # When indexes is below min index, take the min value of the array\n    indexes_below_bounds = virtual_indexes < 0\n    if indexes_below_bounds.any():\n        previous_indexes[indexes_below_bounds] = 0\n        next_indexes[indexes_below_bounds] = 0\n    if supports_nan:\n        # After the sort, slices having NaNs will have for last element a NaN\n        virtual_indexes_nans = _isnan(virtual_indexes)\n        if isinstance(virtual_indexes_nans, bool):\n            if virtual_indexes_nans:\n                previous_indexes[()] = -1\n                next_indexes[()] = -1\n        elif isinstance(virtual_indexes_nans, ndarray):\n            if virtual_indexes_nans.any():\n                previous_indexes[virtual_indexes_nans] = -1\n                next_indexes[virtual_indexes_nans] = -1\n    previous_indexes = previous_indexes.astype(int)\n    next_indexes = next_indexes.astype(int)\n    return previous_indexes, next_indexes\n\ndef _lerp(a, b, t, out=None):\n    diff_b_a = subtract(b, a)\n    lerp_interpolation = asarray(add(a, diff_b_a * t, out=out))\n    subtract(b, diff_b_a * (1 - t), out=lerp_interpolation, where=t >= 0.5)\n    if static.len(lerp_interpolation.shape) == 0 and out is None:\n        lerp_interpolation2 = lerp_interpolation[()]  # unpack 0d arrays\n    else:\n        lerp_interpolation2 = lerp_interpolation\n    return lerp_interpolation2\n\ndef _quantile(\n    arr,\n    quantiles,\n    axis: int = -1,\n    method: str = \"linear\",\n    out=None,\n):\n    supports_nan = _supports_nan(arr.dtype)\n    arr = asarray(arr, float)\n    values_count = arr.shape[axis]\n    if axis != 0:\n        arr = moveaxis(arr, axis, destination=0)\n\n    def compute_quantile(arr, quantiles, axis: int, method: str, out,\n                         virtual_indexes, supports_nan: bool):\n        virtual_indexes = asarray(virtual_indexes)\n\n        if (virtual_indexes.dtype is int\n                or isinstance(virtual_indexes.dtype, Int)):\n            # No interpolation needed, take the points along axis\n            if supports_nan:\n                # may contain nan, which would sort to the end\n                arr.partition(concatenate((virtual_indexes.ravel(), [-1])),\n                              axis=0)\n                slices_having_nans = _isnan(arr[-1, ...])\n            else:\n                # cannot contain nan\n                arr.partition(virtual_indexes.ravel(), axis=0)\n            result = take(arr, virtual_indexes, axis=0, out=out)\n        else:\n            previous_indexes, next_indexes = _get_indexes(\n                arr, virtual_indexes, values_count, supports_nan)\n            # --- Sorting\n            arr.partition(unique(\n                concatenate((\n                    [0, -1],\n                    previous_indexes.ravel(),\n                    next_indexes.ravel(),\n                ))),\n                          axis=0)\n            if supports_nan:\n                slices_having_nans = _isnan(arr[-1, ...])\n            # --- Get values from indexes\n            previous = arr[previous_indexes]\n            next = arr[next_indexes]\n\n            # --- Linear interpolation\n            def _get_gamma(virtual_indexes, previous_indexes, method: str):\n                gamma = asarray(virtual_indexes - previous_indexes)\n                if (method == 'inverted_cdf' or method == 'closest_observation'\n                        or method == 'interpolated_inverted_cdf'\n                        or method == 'hazen' or method == 'weibull'\n                        or method == 'linear' or method == 'median_unbiased'\n                        or method == 'normal_unbiased' or method == 'lower'\n                        or method == 'higher' or method == 'nearest'):\n                    return gamma\n                elif method == 'averaged_inverted_cdf':\n                    return _get_gamma_mask(shape=gamma.shape,\n                                           default_value=1.,\n                                           conditioned_value=0.5,\n                                           where=gamma == 0)\n                elif method == 'midpoint':\n                    return _get_gamma_mask(shape=gamma.shape,\n                                           default_value=0.5,\n                                           conditioned_value=0.,\n                                           where=virtual_indexes % 1 == 0)\n\n            gamma = _get_gamma(virtual_indexes, previous_indexes, method)\n            gamma = asarray(gamma)\n            result_shape = virtual_indexes.shape + (1, ) * (arr.ndim - 1)\n            gamma = gamma.reshape(result_shape)\n            result = _lerp(previous, next, gamma, out=out)\n\n        if supports_nan:\n            if any(slices_having_nans):\n                if isinstance(result, ndarray):\n                    if result.ndim == 0 and out is None:\n                        # can't write to a scalar, but indexing will be correct\n                        result = arr[-1]\n                    else:\n                        copyto(result, arr[-1, ...], where=slices_having_nans)\n                else:\n                    if out is None:\n                        result = util.nan64()\n                    else:\n                        out[()] = util.nan64()\n        return result\n\n    # --- Computation of indexes\n    # Index where to find the value in the sorted array.\n    # Virtual because it is a floating point value, not an valid index.\n    # The nearest neighbours are used for interpolation\n    if method == 'inverted_cdf':\n        virtual_indexes = _inverted_cdf(values_count, quantiles)\n        return compute_quantile(arr,\n                                quantiles=quantiles,\n                                axis=axis,\n                                method=method,\n                                out=out,\n                                virtual_indexes=virtual_indexes,\n                                supports_nan=supports_nan)\n    elif method == 'averaged_inverted_cdf':\n        virtual_indexes = (values_count * quantiles) - 1\n        return compute_quantile(arr,\n                                quantiles=quantiles,\n                                axis=axis,\n                                method=method,\n                                out=out,\n                                virtual_indexes=virtual_indexes,\n                                supports_nan=supports_nan)\n    elif method == 'closest_observation':\n        virtual_indexes = _closest_observation(values_count, quantiles)\n        return compute_quantile(arr,\n                                quantiles=quantiles,\n                                axis=axis,\n                                method=method,\n                                out=out,\n                                virtual_indexes=virtual_indexes,\n                                supports_nan=supports_nan)\n    elif method == 'interpolated_inverted_cdf':\n        virtual_indexes = _compute_virtual_index(values_count, quantiles, 0, 1)\n        return compute_quantile(arr,\n                                quantiles=quantiles,\n                                axis=axis,\n                                method=method,\n                                out=out,\n                                virtual_indexes=virtual_indexes,\n                                supports_nan=supports_nan)\n    elif method == 'hazen':\n        virtual_indexes = _compute_virtual_index(values_count, quantiles, 0.5,\n                                                 0.5)\n        return compute_quantile(arr,\n                                quantiles=quantiles,\n                                axis=axis,\n                                method=method,\n                                out=out,\n                                virtual_indexes=virtual_indexes,\n                                supports_nan=supports_nan)\n    elif method == 'weibull':\n        virtual_indexes = _compute_virtual_index(values_count, quantiles, 0, 0)\n        return compute_quantile(arr,\n                                quantiles=quantiles,\n                                axis=axis,\n                                method=method,\n                                out=out,\n                                virtual_indexes=virtual_indexes,\n                                supports_nan=supports_nan)\n    elif method == 'linear':\n        virtual_indexes = (values_count - 1) * quantiles\n        return compute_quantile(arr,\n                                quantiles=quantiles,\n                                axis=axis,\n                                method=method,\n                                out=out,\n                                virtual_indexes=virtual_indexes,\n                                supports_nan=supports_nan)\n    elif method == 'median_unbiased':\n        virtual_indexes = _compute_virtual_index(values_count, quantiles,\n                                                 1 / 3.0, 1 / 3.0)\n        return compute_quantile(arr,\n                                quantiles=quantiles,\n                                axis=axis,\n                                method=method,\n                                out=out,\n                                virtual_indexes=virtual_indexes,\n                                supports_nan=supports_nan)\n    elif method == 'normal_unbiased':\n        virtual_indexes = _compute_virtual_index(values_count, quantiles,\n                                                 3 / 8.0, 3 / 8.0)\n        return compute_quantile(arr,\n                                quantiles=quantiles,\n                                axis=axis,\n                                method=method,\n                                out=out,\n                                virtual_indexes=virtual_indexes,\n                                supports_nan=supports_nan)\n    elif method == 'lower':\n\n        def get_virtual_indexes(quantiles, values_count):\n            if isinstance(quantiles, ndarray):\n                return floor((values_count - 1) * quantiles).astype(int)\n            else:\n                return int(floor((values_count - 1) * quantiles))\n\n        virtual_indexes2 = get_virtual_indexes(quantiles, values_count)\n        return compute_quantile(arr,\n                                quantiles=quantiles,\n                                axis=axis,\n                                method=method,\n                                out=out,\n                                virtual_indexes=virtual_indexes2,\n                                supports_nan=supports_nan)\n    elif method == 'higher':\n\n        def get_virtual_indexes(quantiles, values_count):\n            if isinstance(quantiles, ndarray):\n                return ceil((values_count - 1) * quantiles).astype(int)\n            else:\n                return int(ceil((values_count - 1) * quantiles))\n\n        virtual_indexes2 = get_virtual_indexes(quantiles, values_count)\n        return compute_quantile(arr,\n                                quantiles=quantiles,\n                                axis=axis,\n                                method=method,\n                                out=out,\n                                virtual_indexes=virtual_indexes2,\n                                supports_nan=supports_nan)\n    elif method == 'midpoint':\n        virtual_indexes = 0.5 * (floor((values_count - 1) * quantiles) + ceil(\n            (values_count - 1) * quantiles))\n        return compute_quantile(arr,\n                                quantiles=quantiles,\n                                axis=axis,\n                                method=method,\n                                out=out,\n                                virtual_indexes=virtual_indexes,\n                                supports_nan=supports_nan)\n    elif method == 'nearest':\n\n        def get_virtual_indexes(quantiles, values_count):\n            if isinstance(quantiles, ndarray):\n                return round((values_count - 1) * quantiles).astype(int)\n            else:\n                return int(round((values_count - 1) * quantiles))\n\n        virtual_indexes2 = get_virtual_indexes(quantiles, values_count)\n        return compute_quantile(arr,\n                                quantiles=quantiles,\n                                axis=axis,\n                                method=method,\n                                out=out,\n                                virtual_indexes=virtual_indexes2,\n                                supports_nan=supports_nan)\n    else:\n        raise ValueError(f\"{method} is not a valid method.\")\n\ndef _quantile_reducer(v: Ptr[T], s: int, n: int, dtype: type, T: type,\n                      **kwargs):\n    return _quantile(ndarray((n, ), (s, ), v),\n                     quantiles=kwargs['q'],\n                     axis=0,\n                     method=kwargs['method'],\n                     out=None)\n\ndef _quantile_reducer_multi(v: Ptr[T], s: int, n: int, out: Ptr[float],\n                            dtype: type, T: type, **kwargs):\n    q = kwargs['q']\n    _quantile(ndarray((n, ), (s, ), v),\n              quantiles=q,\n              axis=0,\n              method=kwargs['method'],\n              out=ndarray((q.size, ), (util.sizeof(dtype), ), out))\n\ndef _quantile_unchecked(a,\n                        q,\n                        axis=None,\n                        out=None,\n                        overwrite_input: bool = False,\n                        method: str = \"linear\",\n                        keepdims: Literal[bool] = False):\n    # Assumes that q is in [0, 1], and is an ndarray\n    if q.ndim == 0:\n        return _reduce_buffered(a,\n                                _quantile_reducer,\n                                dtype=a.dtype,\n                                axis=axis,\n                                out=out,\n                                overwrite_input=overwrite_input,\n                                keepdims=keepdims,\n                                q=q.item(),\n                                method=method)\n    elif q.ndim == 1:\n        return _reduce_buffered_multi(a,\n                                      _quantile_reducer_multi,\n                                      multi_num=q.size,\n                                      dtype=float,\n                                      axis=axis,\n                                      out=out,\n                                      overwrite_input=overwrite_input,\n                                      keepdims=keepdims,\n                                      q=q,\n                                      method=method)\n    else:\n        compile_error(\"q must be a scalar or 1d\")\n\ndef _asarray_no_complex(a):\n    a = asarray(a)\n    if a.dtype is complex or a.dtype is complex64:\n        compile_error(\"a must be an array of real numbers\")\n    return a\n\ndef quantile(a,\n             q,\n             axis=None,\n             out=None,\n             overwrite_input: bool = False,\n             method: str = \"linear\",\n             keepdims: Literal[bool] = False,\n             interpolation=None):\n    if interpolation is not None:\n        method = _check_interpolation_as_method(method, interpolation)\n\n    a = _asarray_no_complex(a)\n    q = asarray(q)\n\n    if not _quantile_is_valid(q):\n        raise ValueError(\"Quantiles must be in the range [0, 1]\")\n    return _quantile_unchecked(a, q, axis, out, overwrite_input, method,\n                               keepdims)\n\ndef _nanquantile_reducer(v: Ptr[T], s: int, n: int, dtype: type, T: type,\n                         **kwargs):\n    n = _nan_to_back(v, n)\n    return _quantile(ndarray((n, ), (s, ), v),\n                     quantiles=kwargs['q'],\n                     axis=0,\n                     method=kwargs['method'],\n                     out=None)\n\ndef _nanquantile_reducer_multi(v: Ptr[T], s: int, n: int, out: Ptr[dtype],\n                               dtype: type, T: type, **kwargs):\n    n = _nan_to_back(v, n)\n    q = kwargs['q']\n    _quantile(ndarray((n, ), (s, ), v),\n              quantiles=q,\n              axis=0,\n              method=kwargs['method'],\n              out=ndarray((q.size, ), (util.sizeof(dtype), ), out))\n\ndef _nanquantile_unchecked(a,\n                           q,\n                           axis=None,\n                           out=None,\n                           overwrite_input: bool = False,\n                           method: str = \"linear\",\n                           keepdims: Literal[bool] = False):\n    # Assumes that q is in [0, 1], and is an ndarray\n    if q.ndim == 0:\n        return _reduce_buffered(a,\n                                _nanquantile_reducer,\n                                dtype=a.dtype,\n                                axis=axis,\n                                out=out,\n                                overwrite_input=overwrite_input,\n                                keepdims=keepdims,\n                                q=q.item(),\n                                method=method)\n    elif q.ndim == 1:\n        return _reduce_buffered_multi(a,\n                                      _nanquantile_reducer_multi,\n                                      multi_num=q.size,\n                                      dtype=float,\n                                      axis=axis,\n                                      out=out,\n                                      overwrite_input=overwrite_input,\n                                      keepdims=keepdims,\n                                      q=q,\n                                      method=method)\n    else:\n        compile_error(\"q must be a scalar or 1d\")\n\ndef nanquantile(a,\n                q,\n                axis=None,\n                out=None,\n                overwrite_input: bool = False,\n                method: str = \"linear\",\n                keepdims: Literal[bool] = False,\n                interpolation=None):\n    if interpolation is not None:\n        method = _check_interpolation_as_method(method, interpolation)\n\n    a = _asarray_no_complex(a)\n\n    if not _supports_nan(a.dtype):\n        return quantile(a,\n                        q,\n                        axis=axis,\n                        out=out,\n                        overwrite_input=overwrite_input,\n                        method=method,\n                        keepdims=keepdims,\n                        interpolation=interpolation)\n\n    q = asarray(q)\n\n    if not _quantile_is_valid(q):\n        raise ValueError(\"Quantiles must be in the range [0, 1]\")\n    return _nanquantile_unchecked(a, q, axis, out, overwrite_input, method,\n                                  keepdims)\n\ndef percentile(a,\n               q,\n               axis=None,\n               out=None,\n               overwrite_input: bool = False,\n               method: str = \"linear\",\n               keepdims: Literal[bool] = False,\n               interpolation=None):\n    if interpolation is not None:\n        method = _check_interpolation_as_method(method, interpolation)\n\n    a = _asarray_no_complex(a)\n    q = true_divide(q, 100)\n    q = asarray(q)\n\n    if not _quantile_is_valid(q):\n        raise ValueError(\"Percentiles must be in the range [0, 100]\")\n    return _quantile_unchecked(a, q, axis, out, overwrite_input, method,\n                               keepdims)\n\ndef nanpercentile(a,\n                  q,\n                  axis=None,\n                  out=None,\n                  overwrite_input: bool = False,\n                  method: str = \"linear\",\n                  keepdims: Literal[bool] = False,\n                  interpolation=None):\n    if interpolation is not None:\n        method = _check_interpolation_as_method(method, interpolation)\n\n    a = _asarray_no_complex(a)\n\n    if not _supports_nan(a.dtype):\n        return percentile(a,\n                          q,\n                          axis=axis,\n                          out=out,\n                          overwrite_input=overwrite_input,\n                          method=method,\n                          keepdims=keepdims,\n                          interpolation=interpolation)\n\n    q = true_divide(q, 100)\n    q = asarray(q)\n\n    if not _quantile_is_valid(q):\n        raise ValueError(\"Percentiles must be in the range [0, 100]\")\n    return _nanquantile_unchecked(a, q, axis, out, overwrite_input, method,\n                                  keepdims)\n\n@extend\nclass ndarray:\n\n    def sum(\n            self,\n            axis=None,\n            dtype: type = NoneType,\n            out=None,\n            keepdims: Literal[bool] = False,\n            initial=0,\n            where=util._NoValue(),\n    ):\n        return sum(\n            self,\n            axis=axis,\n            dtype=dtype,\n            out=out,\n            keepdims=keepdims,\n            initial=initial,\n            where=where,\n        )\n\n    def prod(\n            self,\n            axis=None,\n            dtype: type = NoneType,\n            out=None,\n            keepdims: Literal[bool] = False,\n            initial=1,\n            where=util._NoValue(),\n    ):\n        return prod(\n            self,\n            axis=axis,\n            dtype=dtype,\n            out=out,\n            keepdims=keepdims,\n            initial=initial,\n            where=where,\n        )\n\n    def mean(\n            self,\n            axis=None,\n            dtype: type = NoneType,\n            out=None,\n            keepdims: Literal[bool] = False,\n            where=util._NoValue(),\n    ):\n        return mean(self,\n                    axis=axis,\n                    dtype=dtype,\n                    out=out,\n                    keepdims=keepdims,\n                    where=where)\n\n    def nanmean(\n            self,\n            axis=None,\n            dtype: type = NoneType,\n            out=None,\n            keepdims: Literal[bool] = False,\n            where=util._NoValue(),\n    ):\n        return nanmean(self,\n                       axis=axis,\n                       dtype=dtype,\n                       out=out,\n                       keepdims=keepdims,\n                       where=where)\n\n    def var(\n            self,\n            axis=None,\n            dtype: type = NoneType,\n            out=None,\n            ddof: int = 0,\n            keepdims: Literal[bool] = False,\n            where=util._NoValue(),\n    ):\n        return var(\n            self,\n            axis=axis,\n            dtype=dtype,\n            out=out,\n            ddof=ddof,\n            keepdims=keepdims,\n            where=where,\n        )\n\n    def nanvar(\n            self,\n            axis=None,\n            dtype: type = NoneType,\n            out=None,\n            ddof: int = 0,\n            keepdims: Literal[bool] = False,\n            where=util._NoValue(),\n    ):\n        return nanvar(\n            self,\n            axis=axis,\n            dtype=dtype,\n            out=out,\n            ddof=ddof,\n            keepdims=keepdims,\n            where=where,\n        )\n\n    def std(\n            self,\n            axis=None,\n            dtype: type = NoneType,\n            out=None,\n            ddof: int = 0,\n            keepdims: Literal[bool] = False,\n            where=util._NoValue(),\n    ):\n        return std(\n            self,\n            axis=axis,\n            dtype=dtype,\n            out=out,\n            ddof=ddof,\n            keepdims=keepdims,\n            where=where,\n        )\n\n    def nanstd(\n            self,\n            axis=None,\n            dtype: type = NoneType,\n            out=None,\n            ddof: int = 0,\n            keepdims: Literal[bool] = False,\n            where=util._NoValue(),\n    ):\n        return nanstd(\n            self,\n            axis=axis,\n            dtype=dtype,\n            out=out,\n            ddof=ddof,\n            keepdims=keepdims,\n            where=where,\n        )\n\n    def min(\n            self,\n            axis=None,\n            dtype: type = NoneType,\n            out=None,\n            ddof: int = 0,\n            keepdims: Literal[bool] = False,\n            initial=util._NoValue(),\n            where=util._NoValue(),\n    ):\n        return min(\n            self,\n            axis=axis,\n            dtype=dtype,\n            out=out,\n            ddof=ddof,\n            keepdims=keepdims,\n            initial=initial,\n            where=where,\n        )\n\n    def ptp(self, axis=None, out=None, keepdims: Literal[bool] = False):\n        return ptp(self, axis=axis, out=out, keepdims=keepdims)\n\n    def max(\n            self,\n            axis=None,\n            dtype: type = NoneType,\n            out=None,\n            ddof: int = 0,\n            keepdims: Literal[bool] = False,\n            initial=util._NoValue(),\n            where=util._NoValue(),\n    ):\n        return max(\n            self,\n            axis=axis,\n            dtype=dtype,\n            out=out,\n            ddof=ddof,\n            keepdims=keepdims,\n            initial=initial,\n            where=where,\n        )\n\n    def argmin(self, axis=None, out=None, keepdims: Literal[bool] = False):\n        return argmin(self, axis=axis, out=out, keepdims=keepdims)\n\n    def argmax(self, axis=None, out=None, keepdims: Literal[bool] = False):\n        return argmax(self, axis=axis, out=out, keepdims=keepdims)\n\n    def any(self,\n            axis=None,\n            out=None,\n            keepdims: Literal[bool] = False,\n            where=util._NoValue()):\n        return any(self, axis=axis, out=out, keepdims=keepdims, where=where)\n\n    def all(self,\n            axis=None,\n            out=None,\n            keepdims: Literal[bool] = False,\n            where=util._NoValue()):\n        return all(self, axis=axis, out=out, keepdims=keepdims, where=where)\n"
  },
  {
    "path": "stdlib/numpy/routines.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport util\nimport internal.static as static\n\nfrom .npdatetime import datetime64, timedelta64, busdaycalendar, _apply_busines_day_offset, \\\n                        _apply_busines_day_count, _apply_is_business_day, _BUSDAY_FORWARD, \\\n                        _BUSDAY_FOLLOWING, _BUSDAY_BACKWARD, _BUSDAY_PRECEDING, \\\n                        _BUSDAY_MODIFIEDFOLLOWING, _BUSDAY_MODIFIEDPRECEDING, _BUSDAY_NAT, \\\n                        _BUSDAY_RAISE\nfrom .ndarray import ndarray, flagsobj\n\ndef _check_out(out, shape):\n    if not isinstance(out, ndarray):\n        compile_error(\"output must be an array\")\n\n    if out.ndim != static.len(shape):\n        compile_error(\"output parameter has the wrong number of dimensions\")\n\n    if out.shape != shape:\n        raise ValueError(\"output parameter has incorrect shape\")\n\n############\n# Creation #\n############\n\ndef _inner_type(t):\n    if isinstance(t, ndarray):\n        return t.dtype()\n    elif isinstance(t, List) or isinstance(t, Tuple):\n        return _inner_type(t[0])\n    else:\n        return t\n\ndef _extract_shape(t):\n    if isinstance(t, ndarray):\n        return t.shape\n    elif isinstance(t, List) or isinstance(t, Tuple):\n        rest = _extract_shape(t[0])\n        return (len(t), *rest)\n    else:\n        return ()\n\ndef _flatten(t, shape, D: type):\n    def helper(t, p, start: int, D: type):\n        for s in t:\n            if isinstance(s, ndarray):\n                cc, fc = s._contig\n                if cc:\n                    str.memcpy((p + start).as_byte(), s.data.as_byte(), s.nbytes)\n                else:\n                    for idx in util.multirange(s.shape):\n                        p[start] = s._ptr(idx)[0]\n                        start += 1\n                start += s.size\n            elif isinstance(s, List) or isinstance(s, Tuple):\n                start = helper(s, p, start, D)\n            else:\n                p[start] = util.cast(s, D)\n                start += 1\n        return start\n\n    p = Ptr[D](util.count(shape))\n    helper(t, p, 0, D)\n    return p\n\ndef _validate_shape(t, shape):\n    def error():\n        raise ValueError('array dimensions mismatch (jagged arrays are not allowed)')\n\n    if static.len(shape) == 0:\n        return\n    else:\n        if not hasattr(type(t), \"__getitem__\"):\n            error()\n        else:\n            if isinstance(t, ndarray):\n                if not util.tuple_equal(t.shape, shape):\n                    error()\n            else:\n                if len(t) != shape[0]:\n                    error()\n                for s in t:\n                    _validate_shape(s, shape[1:])\n\ndef _array(a, dtype: type = NoneType, copy: bool = True, order: str = 'K'):\n    if dtype is NoneType:\n        if isinstance(a, ndarray):\n            return _array(a, a.dtype, copy, order)\n        else:\n            return _array(a, type(_inner_type(a)), copy, order)\n\n    ndarray._check_order(order)\n\n    if isinstance(a, ndarray):\n        if copy:\n            return a.astype(dtype, order=order, copy=True)\n        else:\n            if order == 'K' or order == 'A':\n                return a.astype(dtype, copy=False)\n\n            cc, fc = a._contig\n\n            if order == 'C':\n                if cc:\n                    return a.astype(dtype, copy=False)\n                else:\n                    return a.astype(dtype, order='C', copy=False)\n\n            if order == 'F':\n                if fc:\n                    return a.astype(dtype, copy=False)\n                else:\n                    return a.astype(dtype, order='F', copy=False)\n    elif isinstance(a, List) or isinstance(a, Tuple):\n        shape = _extract_shape(a)\n        data = _flatten(a, shape, dtype)\n        _validate_shape(a, shape)\n        result = ndarray(shape, data)\n\n        if order == 'F':\n            return result.astype(dtype, order='F')\n        else:\n            return result\n    else:\n        shape = ()\n        data = Ptr[dtype](1)\n        data[0] = util.cast(a, dtype)\n        return ndarray(shape, data)\n\ndef array(a, dtype: type = NoneType, copy: bool = True, order: str = 'K', ndmin: Literal[int] = 0):\n    result = _array(a, dtype=dtype, copy=copy, order=order)\n    if static.len(result.shape) < ndmin:\n        o = (1,) * (ndmin - static.len(result.shape))\n        return result.reshape((*o, *result.shape))\n    return result\n\ndef asarray(a, dtype: type = NoneType, order: str = 'K'):\n    return array(a, dtype=dtype, copy=False, order=order)\n\ndef asanyarray(a, dtype: type = NoneType, order: str = 'K'):\n    return asarray(a, dtype=dtype, order=order)\n\ndef asarray_chkfinite(a, dtype: type = NoneType, order: str = 'K'):\n    # Note: this is c/p from ndmath\n    def isfinite(x):\n        if isinstance(x, float) or isinstance(x, float32):\n            return util.isfinite(x)\n        elif isinstance(x, complex) or isinstance(x, complex64):\n            return util.isfinite(x.real) and util.isfinite(x.imag)\n        else:\n            return True\n\n    a = asarray(a, dtype=dtype, order=order)\n    for idx in util.multirange(a.shape):\n        if not isfinite(a._ptr(idx)[0]):\n            raise ValueError(\"array must not contain infs or NaNs\")\n    return a\n\ndef empty(shape, dtype: type = float, order: str = 'C'):\n    if isinstance(shape, int):\n        return empty((shape,), dtype, order)\n\n    for s in shape:\n        if s < 0:\n            raise ValueError('negative dimensions are not allowed')\n\n    ccontig = (order == 'C')\n    fcontig = (order == 'F')\n    if not (ccontig or fcontig):\n        raise ValueError(\"'order' must be 'C' or 'F'\")\n\n    data = Ptr[dtype](util.count(shape))\n    return ndarray(shape, data, fcontig=fcontig)\n\ndef empty_like(prototype, dtype: type = NoneType, order: str = 'K', shape = None):\n    prototype = asarray(prototype)\n\n    if dtype is NoneType:\n        return empty_like(prototype, dtype=prototype.dtype, order=order, shape=None)\n\n    if shape is None:\n        return empty_like(prototype, dtype=dtype, order=order, shape=prototype.shape)\n\n    cc, fc = prototype._contig\n\n    if order == 'A':\n        order = 'F' if fc else 'C'\n    elif order == 'K':\n        if static.len(shape) == prototype.ndim:\n            if cc or prototype.ndim <= 1:\n                order = 'C'\n            elif fc:\n                order = 'F'\n            else:\n                strides = (0,) * static.len(prototype.strides)\n                pstrides = Ptr[int](__ptr__(strides).as_byte())\n                r = util.tuple_range(static.len(shape))\n                perm, strides_sorted = util.sort_by_stride(r, prototype.strides)\n                stride = util.sizeof(dtype)\n                ndim = prototype.ndim\n\n                for idim in range(ndim - 1, -1, -1):\n                    iperm = perm[idim]\n                    pstrides[iperm] = stride\n                    stride *= shape[iperm]\n\n                data = Ptr[dtype](util.count(shape))\n                return ndarray(shape, strides, data)\n        else:\n            order = 'C'\n\n    return empty(shape, dtype, order)\n\ndef zeros(shape, dtype: type = float, order: str = 'C'):\n    result = empty(shape, dtype, order)\n    str.memset(result.data.as_byte(), byte(0), result.nbytes)\n    return result\n\ndef zeros_like(prototype, dtype: type = NoneType, order: str = 'K'):\n    result = empty_like(prototype, dtype, order)\n    str.memset(result.data.as_byte(), byte(0), result.nbytes)\n    return result\n\ndef ones(shape, dtype: type = float, order: str = 'C'):\n    result = empty(shape, dtype, order)\n    result.map(lambda x: util.cast(1, result.dtype), inplace=True)\n    return result\n\ndef ones_like(prototype, dtype: type = NoneType, order: str = 'K'):\n    result = empty_like(prototype, dtype, order)\n    result.map(lambda x: util.cast(1, result.dtype), inplace=True)\n    return result\n\ndef identity(n: int, dtype: type = float):\n    result = zeros((n, n), dtype)\n    p = result.data\n    for i in range(n):\n        p[i * (n + 1)] = dtype(1)\n    return result\n\ndef _diag_count(k: int, n: int, m: int):\n    count = 0\n    if k < m and k > -n:\n        count = min(n, m)\n        if k > 0:\n            d = max(m - n, 0)\n            if k > d:\n                count -= (k - d)\n        elif k < 0:\n            d = max(n - m, 0)\n            if k < -d:\n                count -= (-k - d)\n    return count\n\ndef eye(N: int, M: Optional[int] = None, k: int = 0, dtype: type = float, order: str = 'C'):\n    n: int = N\n    m: int = M if M is not None else n\n    result = zeros((n, m), dtype=dtype, order=order)\n    p = result.data\n\n    for i in range(_diag_count(k, n, m)):\n        if k >= 0:\n            result[i, i + k] = dtype(1)\n        else:\n            j = n - i - 1\n            result[i - k, i] = dtype(1)\n\n    return result\n\ndef diag(v, k: int = 0):\n    v = asarray(v)\n    data = v.data\n    T = type(data[0])\n\n    if static.len(v.shape) == 1:\n        count = v.shape[0]\n        n = count + abs(k)\n        result = zeros((n, n), dtype=T)\n        p = result.data\n        if k > 0:\n            p += k\n        elif k < 0:\n            p += (-k) * n\n\n        for i in range(count):\n            q = v._ptr((i,))\n            p[0] = q[0]\n            p += n + 1\n\n        return result\n    elif static.len(v.shape) == 2:\n        n, m = v.shape\n        sn, sm = v.strides\n        new_shape = (_diag_count(k, n, m),)\n        new_strides = (sn + sm,)\n        new_data = data\n        if new_shape[0] > 0:\n            if k >= 0:\n                new_data = v._ptr((0, k))\n            else:\n                new_data = v._ptr((-k, 0))\n        return ndarray(new_shape, new_strides, new_data)\n    else:\n        compile_error('Input must be 1- or 2-d.')\n\ndef diagflat(v, k: int = 0):\n    return diag(asarray(v).flatten(), k)\n\ndef tri(N: int, M: Optional[int] = None, k: int = 0, dtype: type = float):\n    n: int = N\n    m: int = M if M is not None else n\n    result = zeros((n, m), dtype=dtype)\n    p = result.data\n    for i in range(n):\n        for j in range(min(i + k + 1, m)):\n            p[i*m + j] = dtype(1)\n    return result\n\ndef triu(x, k: int = 0):\n    x = asarray(x)\n    T = x.dtype\n\n    if static.len(x.shape) == 0:\n        compile_error('Cannot call triu on 0-d array.')\n    elif static.len(x.shape) == 1:\n        n = x.shape[0]\n        result = zeros((n, n), dtype=T)\n        p = result.data\n        for i in range(n):\n            for j in range(max(0, i + k), n):\n                p[i*n + j] = x[j]\n        return result\n    else:\n        y = x.copy()\n        n, m = x.shape[-2], x.shape[-1]\n        pre = (slice(None, None, None),) * (static.len(x.shape) - 2)\n        for i in range(n):\n            for j in range(min(i + k, m)):\n                y[(*pre, i, j)] = T(0)\n        return y\n\ndef tril(x, k: int = 0):\n    x = asarray(x)\n    T = x.dtype\n\n    if static.len(x.shape) == 0:\n        compile_error('Cannot call tril on 0-d array.')\n    elif static.len(x.shape) == 1:\n        n = x.shape[0]\n        result = zeros((n, n), dtype=T)\n        p = result.data\n        for i in range(n):\n            for j in range(min(i + k + 1, n)):\n                p[i*n + j] = x[j]\n        return result\n    else:\n        y = x.copy()\n        n, m = x.shape[-2], x.shape[-1]\n        pre = (slice(None, None, None),) * (static.len(x.shape) - 2)\n        for i in range(n):\n            for j in range(max(0, i + k + 1), m):\n                y[(*pre, i, j)] = T(0)\n        return y\n\ndef vander(x, N: Optional[int] = None, increasing: bool = False):\n    x = asarray(x)\n\n    if static.len(x.shape) != 1:\n        compile_error('x must be a one-dimensional array or sequence.')\n\n    T = x.dtype\n    n: int = x.shape[0]\n    m: int = N if N is not None else n\n    result = zeros((n, m), dtype=T)\n    p = result.data\n\n    for i in range(n):\n        base = x._ptr((i,))[0]\n        for j in range(m):\n            power = j if increasing else m - j - 1\n            p[i*m + j] = base ** power\n\n    return result\n\n@pure\n@llvm\ndef _signbit(x: float) -> bool:\n    %y = bitcast double %x to i64\n    %z = icmp slt i64 %y, 0\n    %b = zext i1 %z to i8\n    ret i8 %b\n\n_imin: Literal[int] = -9223372036854775808\n_imax: Literal[int] = 9223372036854775807\n\ndef _safe_ceil(value: float):\n    ivalue = util.ceil64(value)\n    if util.isnan64(ivalue):\n        raise ValueError('arange: cannot compute array length')\n\n    if not (float(_imin) <= ivalue <= float(_imax)):\n        raise OverflowError('arange: overflow while computing length')\n\n    return int(ivalue)\n\ndef _datetime_arange(start, stop, step, dtype: type):\n    def convert(x, dtype: type):\n        if isinstance(x, datetime64) or isinstance(x, timedelta64):\n            return x.value\n        elif isinstance(x, int):\n            return x\n        elif isinstance(x, str):\n            return dtype(x, dtype.base).value\n        else:\n            compile_error(\"datetime_arange inputs must be datetime64, timedelta64 or int\")\n\n    def nat(x: int):\n        return timedelta64(x, 'generic')._nat\n\n    if not (isinstance(dtype, datetime64) or isinstance(dtype, timedelta64)):\n        compile_error(\"datetime_arange was given a non-datetime dtype\")\n\n    a = convert(start, dtype)\n    b = convert(stop, dtype)\n    c = 0\n\n    if step is None:\n        c = 1\n    elif isinstance(step, datetime64) or isinstance(step, str):\n        compile_error(\"cannot use a datetime as a step in arange\")\n    else:\n        c = convert(step, dtype)\n\n    if ((isinstance(start, datetime64) or isinstance(start, str)) and not\n        (isinstance(stop, datetime64) or isinstance(stop, str))):\n        b += a\n\n    if nat(a) or nat(b) or nat(c):\n        raise ValueError(\"arange: cannot use NaT (not-a-time) datetime values\")\n\n    length = len(range(a, b, c))\n    ans = empty(length, dtype)\n\n    for i in range(length):\n        ans.data[i] = dtype(a, dtype.base)\n        a += c\n\n    return ans\n\ndef arange(start: float, stop: float, step: float, dtype: type = float):\n    if step == 0.0:\n        raise ValueError('step cannot be zero')\n\n    delta = stop - start\n    tmp_len = delta / step\n\n    length = 0\n    if tmp_len == 0.0 and delta != 0.0:\n        if _signbit(tmp_len):\n            length = 0\n        else:\n            length = 1\n    else:\n        length = _safe_ceil(tmp_len)\n\n    if length <= 0:\n        return empty(0, dtype=dtype)\n\n    result = empty(length, dtype=dtype)\n    p = result.data\n    i = start\n    j = 0\n    while (i < stop) if step > 0.0 else (i > stop):\n        p[j] = util.cast(i, dtype)\n        j += 1\n        i += step\n\n    return result\n\n@overload\ndef arange(stop: float, step: float, dtype: type = float):\n    return arange(0.0, stop, step, dtype)\n\n@overload\ndef arange(start: float, stop: float, dtype: type = float):\n    return arange(start, stop, 1.0, dtype)\n\n@overload\ndef arange(stop: float, dtype: type = float):\n    return arange(0.0, stop, 1.0, dtype)\n\n@overload\ndef arange(start: int, stop: int, step: int, dtype: type = int):\n    if isinstance(dtype, datetime64) or isinstance(dtype, timedelta64):\n        return _datetime_arange(start, stop, step, dtype)\n\n    length = len(range(start, stop, step))\n    result = empty(length, dtype=dtype)\n    p = result.data\n    j = 0\n\n    for i in range(start, stop, step):\n        p[j] = util.cast(i, dtype)\n        j += 1\n\n    return result\n\n@overload\ndef arange(stop: int, step: int, dtype: type = int):\n    return arange(0, stop, step, dtype)\n\n@overload\ndef arange(start: int, stop: int, dtype: type = int):\n    return arange(start, stop, 1, dtype)\n\n@overload\ndef arange(stop: int, dtype: type = int):\n    return arange(0, stop, 1, dtype)\n\n@overload\ndef arange(start: datetime64, stop, step = None, dtype: type = NoneType):\n    if dtype is NoneType or (isinstance(dtype, datetime64) and dtype.base == 'generic'):\n        return _datetime_arange(start, stop, step, type(start))\n    else:\n        return _datetime_arange(start, stop, step, dtype)\n\n@overload\ndef arange(start: timedelta64, stop, step, dtype: type = NoneType):\n    if dtype is NoneType or (isinstance(dtype, timedelta64) and dtype.base == 'generic'):\n        return _datetime_arange(start, stop, step, type(start))\n    else:\n        return _datetime_arange(start, stop, step, dtype)\n\n@overload\ndef arange(start: timedelta64, stop, dtype: type = NoneType):\n    if dtype is NoneType or (isinstance(dtype, timedelta64) and dtype.base == 'generic'):\n        return _datetime_arange(start, stop, 1, type(start))\n    else:\n        return _datetime_arange(start, stop, 1, dtype)\n\n@overload\ndef arange(stop: timedelta64, dtype: type = NoneType):\n    if dtype is NoneType or (isinstance(dtype, timedelta64) and dtype.base == 'generic'):\n        return _datetime_arange(0, stop, 1, type(stop))\n    else:\n        return _datetime_arange(0, stop, 1, dtype)\n\n@overload\ndef arange(start: str, stop, step = None, dtype: type = datetime64['D', 1]):\n    return _datetime_arange(start, stop, step, dtype)\n\ndef linspace(start: float, stop: float, num: int = 50,\n             endpoint: bool = True, retstep: Literal[bool] = False,\n             dtype: type = float):\n    if num < 0:\n        raise ValueError(f'Number of samples, {num}, must be non-negative.')\n\n    delta = stop - start\n    div = (num - 1) if endpoint else num\n    step = delta / div\n\n    result = empty(num, dtype=dtype)\n    p = result.data\n\n    if div > 0:\n        if step == 0:\n            for i in range(num):\n                p[i] = util.cast(((i / div) * delta) + start, dtype)\n        else:\n            for i in range(num):\n                p[i] = util.cast((i * step) + start, dtype)\n    else:\n        for i in range(num):\n            p[i] = util.cast((i * delta) + start, dtype)\n\n        step = util.nan64()\n\n    if endpoint and num > 1:\n        p[num - 1] = stop\n\n    if retstep:\n        return result, step\n    else:\n        return result\n\ndef _linlogspace(start: float, stop: float, num: int = 50, base: float = 10.0,\n                 out_sign: int = 1, endpoint: bool = True, retstep: Literal[bool] = False,\n                 dtype: type = float, log: Literal[bool] = False):\n    if num < 0:\n        raise ValueError(f'Number of samples, {num}, must be non-negative.')\n\n    delta = stop - start\n    div = (num - 1) if endpoint else num\n    step = delta / div\n\n    result = empty(num, dtype=dtype)\n    p = result.data\n\n    if div > 0:\n        if step == 0:\n            for i in range(num):\n                y = ((i / div) * delta) + start\n                if log:\n                    y = util.pow64(base, y)\n                y *= out_sign\n                p[i] = util.cast(y, dtype)\n        else:\n            for i in range(num):\n                y = (i * step) + start\n                if log:\n                    y = util.pow64(base, y)\n                y *= out_sign\n                p[i] = util.cast(y, dtype)\n    else:\n        for i in range(num):\n            y = (i * delta) + start\n            if log:\n                y = util.pow64(base, y)\n            y *= out_sign\n            p[i] = util.cast(y, dtype)\n\n        step = util.nan64()\n\n    if endpoint and num > 1:\n        y = stop\n        if log:\n            y = util.pow64(base, y)\n        y *= out_sign\n        p[num - 1] = util.cast(y, dtype)\n\n    if retstep:\n        return result, step\n    else:\n        return result\n\ndef linspace(start: float, stop: float, num: int = 50,\n             endpoint: bool = True, retstep: Literal[bool] = False,\n             dtype: type = float):\n    return _linlogspace(start=start, stop=stop, num=num,\n                        endpoint=endpoint, retstep=retstep,\n                        dtype=dtype, log=False)\n\ndef logspace(start: float, stop: float, num: int = 50,\n             endpoint: bool = True, base: float = 10.0,\n             retstep: Literal[bool] = False,\n             dtype: type = float):\n    return _linlogspace(start=start, stop=stop, num=num,\n                        endpoint=endpoint, retstep=retstep,\n                        dtype=dtype, base=base, log=True)\n\ndef geomspace(start: float, stop: float, num: int = 50,\n              endpoint: bool = True, dtype: type = float):\n    if start == 0 or stop == 0:\n        raise ValueError('Geometric sequence cannot include zero')\n\n    out_sign = 1\n    if start < 0 and stop < 0:\n        start, stop = -start, -stop\n        out_sign = -out_sign\n\n    start = start + (stop - stop)\n    stop = stop + (start - start)\n    log_start = util.log10_64(start)\n    log_stop = util.log10_64(stop)\n\n    return _linlogspace(start=log_start, stop=log_stop, num=num,\n                        endpoint=endpoint, retstep=False,\n                        dtype=dtype, base=10.0, out_sign=out_sign,\n                        log=True)\n\ndef fromfunction(function, shape, dtype: type = float, **kwargs):\n    result_dtype = type(\n                    function(\n                     *tuple(util.zero(dtype)\n                            for _ in static.range(static.len(shape))),\n                     **kwargs))\n    result = empty(shape, dtype=result_dtype)\n    for idx in util.multirange(shape):\n        p = result._ptr(idx)\n        args = tuple(util.cast(i, dtype) for i in idx)\n        p[0] = function(*args, **kwargs)\n    return result\n\ndef fromiter(iterable, dtype: type, count: int = -1):\n    if count < 0:\n        return array([a for a in iterable], dtype=dtype)\n    else:\n        result = empty((count,), dtype=dtype)\n        if count:\n            p = result.data\n            i = 0\n            for a in iterable:\n                p[i] = util.cast(a, dtype)\n                i += 1\n                if i == count:\n                    break\n            if i != count:\n                raise ValueError(f'iterator too short: Expected {count} but iterator had only {i} items.')\n        return result\n\ndef frombuffer(buffer: str, dtype: type = float, count: int = -1, offset: int = 0):\n    if count < 0:\n        count = (len(buffer) - offset) // util.sizeof(dtype)\n\n    p = Ptr[dtype](buffer.ptr + offset)\n    return ndarray((count,), p)\n\n################\n# Broadcasting #\n################\n\ndef broadcast_shapes(*args):\n    def _largest(args):\n        if static.len(args) == 1:\n            return args[0]\n\n        a = args[0]\n        b = _largest(args[1:])\n        if static.len(b) > static.len(a):\n            return b\n        else:\n            return a\n\n    def _ensure_tuple(x):\n        if isinstance(x, Tuple):\n            return x\n        else:\n            return (x,)\n\n    if static.len(args) == 0:\n        return ()\n\n    args = tuple(_ensure_tuple(a) for a in args)\n    for a in args:\n        for i in a:\n            if i < 0:\n                raise ValueError('negative dimensions are not allowed')\n\n    t = _largest(args)\n    N: Literal[int] = static.len(t)\n    ans = (0,) * N\n    p = Ptr[int](__ptr__(ans).as_byte())\n\n    for i in static.range(N):\n        p[i] = t[i]\n\n    for a in args:\n        for i in static.range(static.len(a)):\n            x = a[len(a) - 1 - i]\n            q = p + (len(t) - 1 - i)\n            y = q[0]\n\n            if y == 1:\n                q[0] = x\n            elif x != 1 and x != y:\n                raise ValueError('shape mismatch: objects cannot be broadcast to a single shape')\n\n    return ans\n\ndef broadcast_to(x, shape):\n    x = asarray(x)\n\n    if not isinstance(shape, Tuple):\n        return broadcast_to(x, (shape,))\n\n    N: Literal[int] = static.len(x.shape)\n\n    if static.len(shape) < N:\n        compile_error('input operand has more dimensions than allowed by the axis remapping')\n\n    shape1, shape2 = shape[:-N], shape[-N:]\n    substrides = (0,) * N\n    p = Ptr[int](__ptr__(substrides).as_byte())\n\n    if N > 0:\n        for i in range(N):\n            a = x.shape[i]\n            b = shape2[i]\n\n            if a == b:\n                p[i] = x.strides[i]\n            elif a == 1:\n                p[i] = 0\n            else:\n                raise ValueError(f'cannot broadcast array of shape {x.shape} to shape {shape}')\n\n    z = (0,) * (static.len(shape) - N)\n    new_strides = (*z, *substrides)\n    return ndarray(shape, new_strides, x.data)\n\ndef broadcast_arrays(*args):\n    def _ensure_array(x):\n        if isinstance(x, ndarray):\n            return x\n        else:\n            return array(x)\n\n    args = tuple(_ensure_array(a) for a in args)\n    shapes = tuple(a.shape for a in args)\n    bshape = broadcast_shapes(*shapes)\n    return [broadcast_to(a, bshape) for a in args]\n\ndef meshgrid(*xi, copy: bool = True, sparse: Literal[bool] = False, indexing: Literal[str] = 'xy'):\n    def make_shape(i, ndim: Literal[int]):\n        t = (1,) * ndim\n        p = Ptr[int](__ptr__(t).as_byte())\n        p[i] = -1\n        return t\n\n    def build_output(xi, i: int = 0, ndim: Literal[int]):\n        if static.len(xi) == 0:\n            return ()\n\n        x = xi[0]\n        y = array(x).reshape(make_shape(i, ndim))\n        rest = build_output(xi[1:], i + 1, ndim)\n        return (y, *rest)\n\n    if indexing != 'xy' and indexing != 'ij':\n        compile_error(\"Valid values for `indexing` are 'xy' and 'ij'.\")\n\n    ndim: Literal[int] = static.len(xi)\n    s0 = (1,) * ndim\n    output = build_output(xi, ndim=ndim)\n\n    if indexing == 'xy' and ndim > 1:\n        # switch first and second axis\n        output0 = output[0].reshape(1, -1, *s0[2:])\n        output1 = output[1].reshape(-1, 1, *s0[2:])\n        output = (output0, output1, *output[2:])\n\n    if not sparse:\n        # Return the full N-D matrix (not only the 1-D vector)\n        return [a for a in broadcast_arrays(*output)]\n\n    if copy:\n        return [a.copy() for a in output]\n\n    return [a for a in output]\n\nclass _broadcast[A, S]:\n    _arrays: A\n    _shape: S\n    _index: int\n\n    def __init__(self, arrays: A, shape: S):\n        self._arrays = arrays\n        self._shape = shape\n        self._index = 0\n\n    @property\n    def shape(self):\n        return self._shape\n\n    @property\n    def index(self):\n        return self._index\n\n    @property\n    def size(self):\n        return util.count(self.shape)\n\n    @property\n    def ndim(self):\n        return static.len(self.shape)\n\n    @property\n    def nd(self):\n        return self.ndim\n\n    @property\n    def numiter(self):\n        return static.len(self._arrays)\n\n    @property\n    def iters(self):\n        def get_flat(arr, index, bshape):\n            f = broadcast_to(arr, bshape).flat\n            f.index = index\n            return f\n\n        return tuple(get_flat(a, self.index, self.shape) for a in self._arrays)\n\n    def __iter__(self):\n        arrays = self._arrays\n        n = self.size\n\n        while self.index < n:\n            idx = util.index_to_coords(self.index, self.shape)\n            self._index += 1\n            yield tuple(a._ptr(idx, broadcast=True)[0] for a in arrays)\n\n    def reset(self):\n        self._index = 0\n\ndef broadcast(*args):\n    arrays = tuple(asarray(a) for a in args)\n    shape = broadcast_shapes(*tuple(a.shape for a in arrays))\n    return _broadcast(arrays, shape)\n\ndef full(shape, fill_value, dtype: type = NoneType, order: str = 'C'):\n    if isinstance(shape, int):\n        sh = (shape,)\n    else:\n        sh = shape\n\n    fv = asarray(fill_value)\n    if fv.ndim != 0:\n        broadcast_to(fv, shape)  # error check\n\n    if dtype is NoneType:\n        result = empty(shape, fv.dtype, order)\n        if fv.ndim == 0:\n            e = fv.item()\n            result.map(lambda x: e, inplace=True)\n        else:\n            for idx in util.multirange(shape):\n                result._ptr(idx)[0] = fv._ptr(idx, broadcast=True)[0]\n        return result\n    else:\n        result = empty(shape, dtype, order)\n        if fv.ndim == 0:\n            e = fv.item()\n            result.map(lambda x: util.cast(e, result.dtype), inplace=True)\n        else:\n            for idx in util.multirange(shape):\n                result._ptr(idx)[0] = util.cast(fv._ptr(idx, broadcast=True)[0], result.dtype)\n        return result\n\ndef full_like(prototype, fill_value, dtype: type = NoneType, order: str = 'K'):\n    prototype = asarray(prototype)\n    shape = prototype.shape\n\n    fv = asarray(fill_value)\n    if fv.ndim != 0:\n        broadcast_to(fv, shape)  # error check\n\n    if dtype is NoneType:\n        result = empty_like(prototype, fv.dtype, order)\n        if fv.ndim == 0:\n            e = fv.item()\n            result.map(lambda x: e, inplace=True)\n        else:\n            for idx in util.multirange(shape):\n                result._ptr(idx)[0] = fv._ptr(idx, broadcast=True)[0]\n        return result\n    else:\n        result = empty_like(prototype, dtype, order)\n        if fv.ndim == 0:\n            e = fv.item()\n            result.map(lambda x: util.cast(e, result.dtype), inplace=True)\n        else:\n            for idx in util.multirange(shape):\n                result._ptr(idx)[0] = util.cast(fv._ptr(idx, broadcast=True)[0], result.dtype)\n        return result\n\n################\n# Manipulation #\n################\n\ndef copyto(dst: ndarray, src, where = True):\n    src = asarray(src)\n    dst_dtype = dst.dtype\n    src_dtype = src.dtype\n\n    if isinstance(where, bool):\n        if not where:\n            return\n\n        if dst_dtype is src_dtype and src._contig_match(dst):\n            str.memcpy(dst.data.as_byte(), src.data.as_byte(), dst.nbytes)\n            return\n\n    src = broadcast_to(src, dst.shape)\n    where = broadcast_to(asarray(where), dst.shape)\n\n    for idx in util.multirange(dst.shape):\n        w = where._ptr(idx)\n        if w[0]:\n            p = src._ptr(idx)\n            q = dst._ptr(idx)\n            q[0] = util.cast(p[0], dst_dtype)\n\ndef ndim(a):\n    return asarray(a).ndim\n\ndef size(a, axis: Optional[int] = None):\n    a = asarray(a)\n    if axis is None:\n        return a.size\n    else:\n        return a.shape[axis]\n\ndef shape(a):\n    if isinstance(a, ndarray):\n        return a.shape\n    else:\n        shape = _extract_shape(a)\n        _validate_shape(a, shape)\n        return shape\n\ndef reshape(a, newshape, order: str = 'C'):\n    return asarray(a).reshape(newshape, order=order)\n\ndef transpose(a, axes=None):\n    return a.transpose(axes)\n\ndef ravel(a, order: str = 'C'):\n    return asarray(a).ravel(order=order)\n\ndef ascontiguousarray(a, dtype: type = NoneType):\n    return asarray(a, dtype=dtype, order='C')\n\ndef asfortranarray(a, dtype: type = NoneType):\n    return asarray(a, dtype=dtype, order='F')\n\ndef asfarray(a, dtype: type = float):\n    if (dtype is not float and\n        dtype is not float32 and\n        dtype is not complex and\n        dtype is not complex64):\n        return asfarray(a, float)\n    return asarray(a, dtype=dtype)\n\ndef moveaxis(a, source, destination):\n    a = asarray(a)\n    source = util.normalize_axis_tuple(source, a.ndim, 'source')\n    destination = util.normalize_axis_tuple(destination, a.ndim, 'destination')\n    if len(source) != len(destination):\n        raise ValueError('`source` and `destination` arguments must have '\n                         'the same number of elements')\n\n    order = [n for n in range(a.ndim) if n not in source]\n    for dest, src in sorted(zip(destination, source)):\n        order.insert(dest, src)\n\n    order = Ptr[type(a.shape)](order.arr.ptr.as_byte())[0]\n    return a.transpose(order)\n\ndef swapaxes(a, axis1: int, axis2: int):\n    return asarray(a).swapaxes(axis1, axis2)\n\ndef atleast_1d(*arys):\n    def atl1d(a):\n        a = asarray(a)\n        if static.len(a.shape) == 0:\n            return a.reshape(1)\n        else:\n            return a\n\n    if static.len(arys) == 1:\n        return atl1d(arys[0])\n    else:\n        return tuple(atl1d(a) for a in arys)\n\ndef atleast_2d(*arys):\n    def atl2d(a):\n        a = asarray(a)\n        if static.len(a.shape) == 0:\n            return a.reshape(1, 1)\n        elif static.len(a.shape) == 1:\n            return a[None, :]\n        else:\n            return a\n\n    if static.len(arys) == 1:\n        return atl2d(arys[0])\n    else:\n        return tuple(atl2d(a) for a in arys)\n\ndef atleast_3d(*arys):\n    def atl3d(a):\n        a = asarray(a)\n        if static.len(a.shape) == 0:\n            return a.reshape(1, 1, 1)\n        elif static.len(a.shape) == 1:\n            return a[None, :, None]\n        elif static.len(a.shape) == 2:\n            return a[:, :, None]\n        else:\n            return a\n\n    if static.len(arys) == 1:\n        return atl3d(arys[0])\n    else:\n        return tuple(atl3d(a) for a in arys)\n\ndef require(a, dtype: type = NoneType, requirements = None):\n    REQ_C: Literal[int] = 1\n    REQ_F: Literal[int] = 2\n    REQ_A: Literal[int] = 4\n    REQ_W: Literal[int] = 8\n    REQ_O: Literal[int] = 16\n    REQ_E: Literal[int] = 32\n\n    if requirements is None:\n        return asarray(a, dtype=dtype)\n\n    if not requirements:\n        return asarray(a, dtype=dtype)\n\n    req = 0\n    for x in requirements:\n        if x in ('C', 'C_CONTIGUOUS', 'CONTIGUOUS'):\n            req |= REQ_C\n        elif x in ('F', 'F_CONTIGUOUS', 'FORTRAN'):\n            req |= REQ_F\n        elif x in ('A', 'ALIGNED'):\n            req |= REQ_A\n        elif x in ('W', 'WRITEABLE'):\n            req |= REQ_W\n        elif x in ('O', 'OWNDATA'):\n            req |= REQ_O\n        elif x in ('E', 'ENSUREARRAY'):\n            req |= REQ_E\n        else:\n            raise ValueError(\"invalid requirement: \" + repr(x))\n\n    order = 'A'\n    if (req & REQ_C) and (req & REQ_F):\n        raise ValueError('Cannot specify both \"C\" and \"F\" order')\n    elif req & REQ_F:\n        order = 'F'\n        req &= ~REQ_F\n    elif req & REQ_C:\n        order = 'C'\n        req &= ~REQ_C\n\n    # Codon-NumPy ignores other flags/properties currently\n\n    copy = ((req & REQ_O) != 0)\n    arr = array(a, dtype=dtype, order=order, copy=copy)\n    return arr\n\ndef _copy_data_c(dst: cobj, src: cobj, shape: Ptr[int],\n                strides: Ptr[int], dst_strides: Ptr[int],\n                ndim: Literal[int], element_size: int,\n                block: int, block_dim: int):\n    if ndim < 0:\n        return\n\n    if ndim == block_dim:\n        str.memcpy(dst, src, block)\n    else:\n        src_stride = strides[0]\n        dst_stride = dst_strides[0]\n        for j in range(shape[0]):\n            _copy_data_c(dst + j * dst_stride, src + j * src_stride,\n                         shape + 1, strides + 1, dst_strides + 1,\n                         ndim - 1, element_size, block, block_dim)\n\ndef _copy_data_f(dst: cobj, src: cobj, shape: Ptr[int],\n                strides: Ptr[int], dst_strides: Ptr[int],\n                ndim: Literal[int], element_size: int,\n                block: int, block_dim: int):\n    if ndim < 0:\n        return\n\n    if ndim == block_dim:\n        str.memcpy(dst, src, block)\n    else:\n        src_stride = strides[ndim - 1]\n        dst_stride = dst_strides[ndim - 1]\n        for j in range(shape[ndim - 1]):\n            _copy_data_f(dst + j * dst_stride, src + j * src_stride,\n                         shape, strides, dst_strides,\n                         ndim - 1, element_size, block, block_dim)\n\ndef concatenate(arrays, axis = 0, out = None, dtype: type = NoneType):\n    def check_array_dims_static(arrays, ndim: Literal[int]):\n        if static.len(arrays) > 0:\n            if static.len(arrays[0].shape) != ndim:\n                compile_error(\"all the input arrays must have same number of dimensions\")\n            check_array_dims_static(arrays[1:], ndim)\n\n    def find_out_type(arrays, dtype: type):\n        if static.len(arrays) == 0:\n            return dtype()\n        else:\n            x = util.coerce(arrays[0].dtype, dtype)\n            return find_out_type(arrays[1:], type(x))\n\n    def concat_inner(arrays, axis: int, out, dtype: type):\n        if out is not None and dtype is NoneType:\n            return concat_inner(arrays, axis=axis, out=out, dtype=out.dtype)\n\n        if out is None and dtype is NoneType:\n            compile_error(\"[internal error] bad out and dtype given to concat_inner\")\n\n        arr0 = asarray(arrays[0])\n        ndim: Literal[int] = arrays[0].ndim\n        axis = util.normalize_axis_index(axis, ndim)\n        shape = arr0.shape\n        pshape = Ptr[int](__ptr__(shape).as_byte())\n\n        def loop_inner(arr, axis: int, pshape: Ptr[int], ndim: Literal[int]):\n            if arr.ndim != ndim:\n                compile_error(\"all the input arrays must have same number of dimensions\")\n\n            arr_shape = arr.shape\n\n            for idim in static.range(ndim):\n                if idim == axis:\n                    pshape[idim] += arr_shape[idim]\n                elif pshape[idim] != arr_shape[idim]:\n                    raise ValueError(\"all the input array dimensions except for \"\n                                     \"the concatenation axis must match exactly\")\n\n        if isinstance(arrays, Tuple):\n            for i in static.range(1, static.len(arrays)):\n                loop_inner(arrays[i], axis, pshape, ndim)\n        else:\n            for i in range(1, len(arrays)):\n                loop_inner(arrays[i], axis, pshape, ndim)\n\n        corder = True\n        if out is not None:\n            if dtype is not NoneType:\n                compile_error(\"can only specify one of 'out' and 'dtype'\")\n            _check_out(out, shape)\n            ret = out\n        else:\n            num_c = 0\n            num_f = 0\n            for array in arrays:\n                cc, fc = array._contig\n                if cc:\n                    num_c += 1\n                if fc:\n                    num_f += 1\n            corder = (num_c >= num_f)\n            ret = empty(shape, dtype, order=('C' if corder else 'F'))\n\n        element_size = util.sizeof(dtype)\n        offset = 0\n        dst_strides = ret.strides\n        dst_stride_axis = dst_strides[axis]\n        element_size = util.sizeof(dtype)\n\n        for array in arrays:\n            array_shape = array.shape\n\n            if array.dtype is dtype:\n                strides = array.strides\n                dst = ret.data.as_byte() + offset * dst_stride_axis\n                src = array.data.as_byte()\n                block = element_size\n                block_dim = ndim\n\n                if corder:\n                    i = ndim - 1\n                    while i >= 0:\n                        if strides[i] == block and dst_strides[i] == block:\n                            block *= array_shape[i]\n                        else:\n                            block_dim = ndim - 1 - i\n                            break\n                        i -= 1\n                    _copy_data_c(dst, src, Ptr[int](__ptr__(array_shape).as_byte()),\n                                 Ptr[int](__ptr__(strides).as_byte()),\n                                 Ptr[int](__ptr__(dst_strides).as_byte()),\n                                 array.ndim, element_size, block, block_dim)\n                else:\n                    i = 0\n                    while i < ndim:\n                        if strides[i] == block and dst_strides[i] == block:\n                            block *= array_shape[i]\n                        else:\n                            block_dim = i\n                            break\n                        i += 1\n                    _copy_data_f(dst, src, Ptr[int](__ptr__(array_shape).as_byte()),\n                                 Ptr[int](__ptr__(strides).as_byte()),\n                                 Ptr[int](__ptr__(dst_strides).as_byte()),\n                                 array.ndim, element_size, block, block_dim)\n            else:\n                for src_idx in util.multirange(array_shape):\n                    dst_idx = util.tuple_add(src_idx, axis, offset)\n                    ret._ptr(dst_idx)[0] = util.cast(array._ptr(src_idx)[0], dtype)\n\n            offset += array_shape[axis]\n\n        return ret\n\n    def concat_flatten(arrays, out):\n        dtype = out.dtype\n        out_ccontig = out._contig[0]\n        i = 0\n\n        for a in arrays:\n            if a.dtype is dtype and out_ccontig and a._contig[0]:\n                q = out._ptr((i,))\n                n = a.size\n                str.memcpy(q.as_byte(), a.data.as_byte(), n * util.sizeof(dtype))\n                i += n\n            else:\n                for idx in util.multirange(a.shape):\n                    p = a._ptr(idx)\n                    q = out._ptr((i,))\n                    q[0] = util.cast(p[0], dtype)\n                    i += 1\n\n        return out\n\n    def concat_tuple(arrays, axis = 0, out = None, dtype: type = NoneType):\n        if static.len(arrays) == 0:\n            compile_error(\"need at least one array to concatenate\")\n\n        arrays = tuple(asarray(arr) for arr in arrays)\n\n        if axis is None:\n            tot = 0\n            for a in arrays:\n                tot += a.size\n            shape = (tot,)\n\n            if out is None:\n                if dtype is NoneType:\n                    x = find_out_type(arrays[1:], arrays[0].dtype)\n                    return concat_flatten(arrays, empty(shape, dtype=type(x)))\n                else:\n                    return concat_flatten(arrays, empty(shape, dtype=dtype))\n            else:\n                if static.len(out.shape) != 1:\n                    compile_error(\"Output array has wrong dimensionality\")\n\n                if not util.tuple_equal(out.shape, shape):\n                    raise ValueError(\"Output array is the wrong shape\")\n\n                return concat_flatten(arrays, out)\n        else:\n            ndim: Literal[int] = static.len(arrays[0].shape)\n            if ndim == 0:\n                compile_error(\"zero-dimensional arrays cannot be concatenated\")\n\n            check_array_dims_static(arrays[1:], ndim)\n\n            if out is None:\n                if dtype is NoneType:\n                    x = find_out_type(arrays[1:], arrays[0].dtype)\n                    return concat_inner(arrays, axis, out=None, dtype=type(x))\n                else:\n                    return concat_inner(arrays, axis, out=None, dtype=dtype)\n            else:\n                return concat_inner(arrays, axis, out, dtype=out.dtype)\n\n    def concat_list(arrays, axis = 0, out = None, dtype: type = NoneType):\n        if len(arrays) == 0:\n            raise ValueError(\"need at least one array to concatenate\")\n\n        if not isinstance(arrays[0], ndarray):\n            arrays = [asarray(arr) for arr in arrays]\n\n        if axis is None:\n            if axis is not None and len(arrays) == 1:\n                util.normalize_axis_index(axis, 1)  # error check\n\n            tot = 0\n            for a in arrays:\n                tot += a.size\n            shape = (tot,)\n\n            if out is None:\n                if dtype is NoneType:\n                    return concat_flatten(arrays, empty(shape, dtype=arrays[0].dtype))\n                else:\n                    return concat_flatten(arrays, empty(shape, dtype=dtype))\n            else:\n                if static.len(out.shape) != 1:\n                    compile_error(\"Output array has wrong dimensionality\")\n\n                if not util.tuple_equal(out.shape, shape):\n                    raise ValueError(\"Output array is the wrong shape\")\n\n                return concat_flatten(arrays, out)\n        else:\n            ndim: Literal[int] = static.len(arrays[0].shape)\n            if ndim == 0:\n                compile_error(\"zero-dimensional arrays cannot be concatenated\")\n\n            for arr in arrays:\n                if arr.ndim != arrays[0].ndim:\n                    raise ValueError(\"all the input arrays must have same number of dimensions\")\n\n            shape = arrays[0].shape\n            pshape = Ptr[int](__ptr__(shape).as_byte())\n            axis = util.normalize_axis_index(axis, ndim)\n\n            for iarrays in range(1, len(arrays)):\n                arr_shape = arrays[iarrays].shape\n\n                for idim in range(ndim):\n                    if idim == axis:\n                        pshape[idim] += arr_shape[idim]\n                    elif pshape[idim] != arr_shape[idim]:\n                        raise ValueError(\"all the input array dimensions except for the \"\n                                         \"concatenation axis must match exactly\")\n\n            if out is None:\n                if dtype is NoneType:\n                    return concat_inner(arrays, axis, out=None, dtype=arrays[0].dtype)\n                else:\n                    return concat_inner(arrays, axis, out=None, dtype=dtype)\n            else:\n                return concat_inner(arrays, axis, out=out, dtype=out.dtype)\n\n    if out is not None:\n        if dtype is not NoneType:\n            compile_error(\"concatenate() only takes `out` or `dtype` as an argument, but both were provided.\")\n\n        if not isinstance(out, ndarray):\n            compile_error(\"'out' must be an array\")\n\n    if isinstance(arrays, Tuple):\n        return concat_tuple(arrays, axis=axis, out=out, dtype=dtype)\n    else:\n        return concat_list(arrays, axis=axis, out=out, dtype=dtype)\n\ndef expand_dims(a, axis):\n    a = asarray(a)\n    old_ndims: Literal[int] = static.len(a.shape)\n    new_ndims: Literal[int] = (old_ndims +\n        static.len(util.normalize_axis_tuple(axis, 9999999)))\n    axis = util.normalize_axis_tuple(axis, new_ndims)\n\n    old_shape = a.shape\n    pold_shape = Ptr[int](__ptr__(old_shape).as_byte())\n    new_shape = (1,) * new_ndims\n    pnew_shape = Ptr[int](__ptr__(new_shape).as_byte())\n\n    j = 0\n    for i in range(new_ndims):\n        if i not in axis:\n            pnew_shape[i] = pold_shape[j]\n            j += 1\n\n    return a.reshape(new_shape)\n\ndef _apply(fn, seq):\n    if isinstance(seq, Tuple):\n        return tuple(fn(a) for a in seq)\n    elif isinstance(seq, List):\n        return [fn(a) for a in seq]\n    else:\n        compile_error(\"expected a tuple or a list as input\")\n\ndef stack(arrays, axis: int = 0, out = None, dtype: type = NoneType):\n    if not (isinstance(arrays, Tuple) or\n            (isinstance(arrays, List) and\n             isinstance(arrays[0], ndarray))):\n        return asarray(arrays)\n\n    if len(arrays) == 0:\n        raise ValueError(\"need at least one array to stack\")\n\n    arrays = _apply(asarray, arrays)\n    arr0 = arrays[0]\n    for arr in arrays:\n        if not util.tuple_equal(arr0.shape, arr.shape):\n            raise ValueError(\"all input arrays must have the same shape\")\n\n    result_ndim = arrays[0].ndim + 1\n    axis = util.normalize_axis_index(axis, result_ndim)\n    expanded_arrays = _apply(lambda arr: expand_dims(arr, axis=axis), arrays)\n    return concatenate(expanded_arrays, axis=axis, out=out, dtype=dtype)\n\ndef vstack(tup, dtype: type = NoneType):\n    if not (isinstance(tup, Tuple) or\n            (isinstance(tup, List) and\n             isinstance(tup[0], ndarray))):\n        return asarray(tup)\n\n    arrs = _apply(atleast_2d, tup)\n    return concatenate(arrs, axis=0, dtype=dtype)\n\ndef hstack(tup, dtype: type = NoneType):\n    if not (isinstance(tup, Tuple) or\n            (isinstance(tup, List) and\n             isinstance(tup[0], ndarray))):\n        return asarray(tup)\n\n    arrs = _apply(atleast_1d, tup)\n    axis = 0 if (len(arrs) > 0 and arrs[0].ndim == 1) else 1\n    return concatenate(arrs, axis=axis, dtype=dtype)\n\ndef dstack(tup):\n    arrs = _apply(atleast_3d, tup)\n    return concatenate(arrs, axis=2)\n\nrow_stack = vstack\n\ndef column_stack(tup):\n    def fix_array(v):\n        arr = asarray(v)\n        if static.len(arr.shape) < 2:\n            return array(arr, copy=False, ndmin=2).T\n        else:\n            return arr\n\n    arrs = _apply(fix_array, tup)\n    return concatenate(arrs, axis=1)\n\ndef repeat(a, repeats, axis = None):\n    def neg_rep_error():\n        raise ValueError(\"negative dimensions are not allowed\")\n\n    a = asarray(a, order='C')\n    dtype = a.dtype\n    shape = a.shape\n    pshape = Ptr[int](__ptr__(shape).as_byte())\n\n    if isinstance(repeats, int):\n        if repeats < 0:\n            neg_rep_error()\n    else:\n        for rep in repeats:\n            if rep < 0:\n                neg_rep_error()\n\n    if isinstance(axis, int):\n        axis = util.normalize_axis_index(axis, a.ndim)\n        n = pshape[axis]\n        nel = 1\n        for i in range(axis + 1, a.ndim):\n            nel *= pshape[i]\n        chunk = nel * a.itemsize\n\n        n_outer = 1\n        for i in range(axis):\n            n_outer *= pshape[i]\n\n        rep_shape = shape\n        prep_shape = Ptr[int](__ptr__(rep_shape).as_byte())\n\n        if isinstance(repeats, int):\n            prep_shape[axis] *= repeats\n            repeated = empty(rep_shape, dtype=dtype)\n            old_data = a.data.as_byte()\n            new_data = repeated.data.as_byte()\n\n            for i in range(n_outer):\n                for j in range(n):\n                    for k in range(repeats):\n                        str.memcpy(new_data, old_data, chunk)\n                        new_data += chunk\n                    old_data += chunk\n\n            for src in util.multirange(shape):\n                e = a._ptr(src)[0]\n                off = src[axis]\n                for r in range(repeats):\n                    dst = util.tuple_set(src, axis, off * repeats + r)\n                    p = repeated._ptr(dst)\n                    p[0] = e\n\n            return repeated\n        else:\n            axis_dim = prep_shape[axis]\n\n            if len(repeats) != axis_dim:\n                raise ValueError(\"length of 'repeats' does not match axis size\")\n\n            prep_shape[axis] = 0\n            for rep in repeats:\n                prep_shape[axis] += rep\n\n            repeated = empty(rep_shape, dtype=dtype)\n            old_data = a.data.as_byte()\n            new_data = repeated.data.as_byte()\n\n            for i in range(n_outer):\n                for j in range(n):\n                    for k in range(repeats[j]):\n                        str.memcpy(new_data, old_data, chunk)\n                        new_data += chunk\n                    old_data += chunk\n\n            return repeated\n    elif axis is None:\n        if isinstance(repeats, int):\n            a_size = a.size\n            rep_size = a_size * repeats\n            repeated = empty((rep_size,), dtype=dtype)\n            p = a.data\n            q = repeated.data\n            off = 0\n\n            for i in range(a_size):\n                elem = p[i]\n                for _ in range(repeats):\n                    q[off] = elem\n                    off += 1\n\n            return repeated\n        else:\n            a_size = a.size\n\n            if len(repeats) != a_size:\n                raise ValueError(\"length of 'repeats' does not match array size\")\n\n            rep_tot = 0\n            for rep in repeats:\n                rep_tot += rep\n\n            repeated = empty((rep_tot,), dtype=dtype)\n            p = a.data\n            q = repeated.data\n            rep_idx = 0\n            off = 0\n\n            for i in range(a_size):\n                elem = p[i]\n                for _ in range(repeats[i]):\n                    q[off] = elem\n                    off += 1\n\n            return repeated\n    else:\n        compile_error(\"'axis' must be None or an int\")\n\ndef delete(arr, obj, axis = None):\n    arr = asarray(arr)\n    dtype = arr.dtype\n    shape = arr.shape\n    newshape = shape\n    pnewshape = Ptr[int](__ptr__(newshape).as_byte())\n\n    cc, fc = arr._contig\n    arrorder = 'F' if (fc and not cc) else 'C'\n\n    if isinstance(axis, int):\n        axis = util.normalize_axis_index(axis, arr.ndim)\n        shape_no_axis = util.tuple_delete(shape, axis)\n        N = arr.shape[axis]\n\n        if isinstance(obj, int):\n            pnewshape[axis] -= 1\n            new = empty(newshape, dtype, arrorder)\n            obj = util.normalize_index(obj, axis, N)\n\n            for i in range(obj):\n                for idx0 in util.multirange(shape_no_axis):\n                    idx = util.tuple_insert(idx0, axis, i)\n                    p = arr._ptr(idx)\n                    q = new._ptr(idx)\n                    q[0] = p[0]\n\n            for i in range(obj + 1, N):\n                for idx0 in util.multirange(shape_no_axis):\n                    idx1 = util.tuple_insert(idx0, axis, i)\n                    idx2 = util.tuple_insert(idx0, axis, i - 1)\n                    p = arr._ptr(idx1)\n                    q = new._ptr(idx2)\n                    q[0] = p[0]\n\n            return new\n        elif isinstance(obj, slice):\n            start, stop, step = obj.adjust_indices(N)\n            xr = range(start, stop, step)\n            numtodel = len(xr)\n\n            if numtodel <= 0:\n                return arr.copy(order=arrorder)\n\n            if step < 0:\n                step = -step\n                start = xr[-1]\n                stop = xr[0] + 1\n\n            pnewshape[axis] -= numtodel\n            new = empty(newshape, dtype, arrorder)\n\n            if start:\n                for i in range(start):\n                    for idx0 in util.multirange(shape_no_axis):\n                        idx = util.tuple_insert(idx0, axis, i)\n                        p = arr._ptr(idx)\n                        q = new._ptr(idx)\n                        q[0] = p[0]\n\n            if stop != N:\n                for i in range(stop, N):\n                    for idx0 in util.multirange(shape_no_axis):\n                        idx1 = util.tuple_insert(idx0, axis, i)\n                        idx2 = util.tuple_insert(idx0, axis, i - numtodel)\n                        p = arr._ptr(idx1)\n                        q = new._ptr(idx2)\n                        q[0] = p[0]\n\n            if step != 1:\n                off = start\n                for i in range(start, stop):\n                    if i in xr:\n                        continue\n\n                    for idx0 in util.multirange(shape_no_axis):\n                        idx1 = util.tuple_insert(idx0, axis, i)\n                        idx2 = util.tuple_insert(idx0, axis, off)\n                        p = arr._ptr(idx1)\n                        q = new._ptr(idx2)\n                        q[0] = p[0]\n\n                    off += 1\n\n            return new\n        else:\n            if isinstance(obj[0], int):\n                remove = [util.normalize_index(r, axis, N) for r in obj]\n                remove.sort()\n\n                # remove duplicates\n                n = len(remove)\n                numtodel = 0\n\n                if n < 2:\n                    numtodel = n\n                else:\n                    j = 0\n                    for i in range(n - 1):\n                        if remove[i] != remove[i+1]:\n                            remove[j] = remove[i]\n                            j += 1\n\n                    remove[j] = remove[n-1]\n                    j += 1\n                    numtodel = j\n\n                if numtodel == 0:\n                    return arr.copy(order=arrorder)\n\n                pnewshape[axis] -= numtodel\n                new = empty(newshape, dtype, arrorder)\n\n                curr = 0\n                skip = remove[curr]\n\n                for i in range(N):\n                    if i == skip:\n                        curr += 1\n                        if curr < numtodel:\n                            skip = remove[curr]\n                        continue\n\n                    for idx0 in util.multirange(shape_no_axis):\n                        idx1 = util.tuple_insert(idx0, axis, i)\n                        idx2 = util.tuple_insert(idx0, axis, i - curr)\n                        p = arr._ptr(idx1)\n                        q = new._ptr(idx2)\n                        q[0] = p[0]\n\n                return new\n            elif isinstance(obj[0], bool):\n                if len(obj) != N:\n                    raise ValueError(\n                        f\"boolean array argument obj to delete must be one dimensional and match the axis length of {N}\")\n\n                numtodel = 0\n                for r in obj:\n                    if r:\n                        numtodel += 1\n\n                pnewshape[axis] -= numtodel\n                new = empty(newshape, dtype, arrorder)\n                off = 0\n\n                for i in range(N):\n                    if obj[i]:\n                        continue\n\n                    for idx0 in util.multirange(shape_no_axis):\n                        idx1 = util.tuple_insert(idx0, axis, i)\n                        idx2 = util.tuple_insert(idx0, axis, off)\n                        p = arr._ptr(idx1)\n                        q = new._ptr(idx2)\n                        q[0] = p[0]\n\n                    off += 1\n\n                return new\n            else:\n                compile_error(\"arrays used as indices must be of integer (or boolean) type\")\n    elif axis is None:\n        return delete(arr.ravel(), obj, axis=0)\n    else:\n        compile_error(\"'axis' must be None or an int\")\n\ndef append(arr, values, axis = None):\n    arr = asarray(arr)\n    values = asarray(values)\n\n    if static.len(arr.shape) != static.len(values.shape):\n        compile_error(\"'arr' and 'values' must have the same number of dimensions\")\n\n    dtype1 = arr.dtype\n    dtype2 = values.dtype\n    dtype_out = type(util.coerce(dtype1, dtype2))\n\n    ndim: Literal[int] = static.len(arr.shape)\n    shape = arr.shape\n    val_shape = values.shape\n    arr_nbytes = arr.nbytes\n    val_nbytes = values.nbytes\n\n    newshape = shape\n    pnewshape = Ptr[int](__ptr__(newshape).as_byte())\n\n    cc, fc = arr._contig\n    arrorder = 'F' if (fc and not cc) else 'C'\n\n    if isinstance(axis, int):\n        axis = util.normalize_axis_index(axis, ndim)\n\n        if util.tuple_delete(val_shape, axis) != util.tuple_delete(shape, axis):\n            raise ValueError(\"'arr' and 'values' must have the same shape aside from specified axis\")\n\n        pnewshape[axis] += val_shape[axis]\n        new = empty(newshape, dtype_out, arrorder)\n        off = shape[axis]\n\n        if dtype1 is dtype_out and ((axis == 0 and cc) or (axis == ndim - 1 and fc)):\n            str.memcpy(new.data.as_byte(), arr.data.as_byte(), arr_nbytes)\n        else:\n            for idx in util.multirange(shape):\n                p = arr._ptr(idx)\n                q = new._ptr(idx)\n                q[0] = util.cast(p[0], dtype_out)\n\n        if dtype2 is dtype_out and ((axis == 0 and cc) or (axis == ndim - 1 and fc)):\n            q = new.data.as_byte() + arr_nbytes\n            str.memcpy(q, values.data.as_byte(), val_nbytes)\n        else:\n            for idx1 in util.multirange(val_shape):\n                idx2 = util.tuple_add(idx1, axis, off)\n                p = values._ptr(idx1)\n                q = new._ptr(idx2)\n                q[0] = util.cast(p[0], dtype_out)\n\n        return new\n    elif axis is None:\n        new = empty((arr.size + values.size,), dtype_out, arrorder)\n        q = new.data\n        off = 0\n        val_cc = values._contig[0]\n\n        if dtype1 is dtype_out and cc:\n            str.memcpy(q.as_byte(), arr.data.as_byte(), arr_nbytes)\n        else:\n            for idx in util.multirange(shape):\n                p = arr._ptr(idx)\n                q[off] = util.cast(p[0], dtype_out)\n                off += 1\n\n        if dtype2 is dtype_out and val_cc:\n            str.memcpy(q.as_byte() + arr_nbytes, values.data.as_byte(), val_nbytes)\n        else:\n            for idx in util.multirange(val_shape):\n                p = values._ptr(idx)\n                q[off] = util.cast(p[0], dtype_out)\n                off += 1\n\n        return new\n    else:\n        compile_error(\"'axis' must be None or an int\")\n\ndef insert(arr, obj, values, axis = None):\n    def normalize_special(idx: int, axis: int, n: int, numnew: int):\n        idx0 = idx\n        if idx < 0:\n            idx += n\n        if idx < 0 or idx >= n + numnew:\n            raise IndexError(f'index {idx0} is out of bounds for axis {axis} with size {n + numnew}')\n        return idx\n\n    arr = asarray(arr)\n    values = asarray(values)\n    dtype = arr.dtype\n    shape = arr.shape\n    newshape = shape\n    pnewshape = Ptr[int](__ptr__(newshape).as_byte())\n\n    cc, fc = arr._contig\n    arrorder = 'F' if (fc and not cc) else 'C'\n    ndim: Literal[int] = static.len(arr.shape)\n\n    if isinstance(axis, int):\n        axis = util.normalize_axis_index(axis, arr.ndim)\n        N = pnewshape[axis]\n        numnew = 0\n        indices: List[Tuple[int,int]]\n\n        if isinstance(obj, slice):\n            start, stop, step, length = obj.adjust_indices(N)\n            numnew = length\n            indices = [(normalize_special(a, axis, N, numnew), i)\n                         for i, a in enumerate(range(start, stop, step))]\n            indices.sort()\n        elif isinstance(obj, int):\n            numnew = 1\n            indices = [(normalize_special(obj, axis, N, numnew), 0)]\n        else:\n            numnew = len(obj)\n            indices = [(normalize_special(a, axis, N, numnew), i)\n                         for i, a in enumerate(obj)]\n            indices.sort()\n\n        numnew = len(indices)\n\n        for i in range(numnew):\n            idx, rank = indices[i]\n            idx += i\n            util.normalize_index(idx, axis, N + numnew)  # error check\n            indices[i] = (idx, rank)\n\n        if numnew == 0:\n            return arr.copy(order=arrorder)\n\n        pnewshape[axis] += numnew\n        new = empty(newshape, dtype, arrorder)\n        newshape_no_axis = util.tuple_delete(newshape, axis)\n        curr = 0\n        off = 0\n        next_index, next_rank = indices[curr]\n        multiple_values = False\n        if static.len(values.shape) > 0:\n            multiple_values = (len(values) == numnew)\n\n        for ai in range(pnewshape[axis]):\n            if ai == next_index:\n                for idx0 in util.multirange(newshape_no_axis):\n                    idx1 = util.tuple_insert(idx0, axis, 0)\n                    idx2 = util.tuple_insert(idx0, axis, ai)\n                    q = new._ptr(idx2)\n\n                    if static.len(values.shape) == 0:\n                        q[0] = util.cast(values.data[0], dtype)\n                    else:\n                        if multiple_values:\n                            p = asarray(values[next_rank])._ptr(idx1, broadcast=True)\n                            q[0] = util.cast(p[0], dtype)\n                        else:\n                            p = values._ptr(idx1, broadcast=True)\n                            q[0] = util.cast(p[0], dtype)\n\n                curr += 1\n                if curr < numnew:\n                    next_index, next_rank = indices[curr]\n            else:\n                for idx0 in util.multirange(newshape_no_axis):\n                    idx1 = util.tuple_insert(idx0, axis, off)\n                    idx2 = util.tuple_insert(idx0, axis, ai)\n                    p = arr._ptr(idx1)\n                    q = new._ptr(idx2)\n                    q[0] = util.cast(p[0], dtype)\n\n                off += 1\n\n        return new\n    elif axis is None:\n        return insert(arr.ravel(), obj, values, axis=0)\n    else:\n        compile_error(\"'axis' must be None or an int\")\n\ndef array_split(ary, indices_or_sections, axis: int = 0):\n    def correct(idx: int, n: int):\n        if idx > n:\n            return n\n        elif idx < -n:\n            return 0\n        elif idx < 0:\n            return idx + n\n        else:\n            return idx\n\n    def slice_axis(arr, axis: int, start: int, stop: int):\n        ndim: Literal[int] = static.len(arr.shape)\n        dtype = arr.dtype\n\n        shape = arr.shape\n        pshape = Ptr[int](__ptr__(shape).as_byte())\n        limit = pshape[axis]\n        start = correct(start, limit)\n        stop = correct(stop, limit)\n\n        base = (0,) * ndim\n        pbase = Ptr[int](__ptr__(base).as_byte())\n        pbase[axis] = start\n        pshape[axis] = stop - start if stop > start else 0\n        sub = arr._ptr(base) if stop > start else Ptr[dtype]()\n\n        return ndarray(shape, arr.strides, sub)\n\n    ary = asarray(ary)\n    axis = util.normalize_axis_index(axis, ary.ndim)\n    ntotal = ary.shape[axis]\n\n    if isinstance(indices_or_sections, int):\n        nsections = indices_or_sections\n        if nsections <= 0:\n            raise ValueError(\"number sections must be larger than 0.\")\n\n        neach, extras = divmod(ntotal, nsections)\n        result = List(capacity=nsections)\n        start = 0\n\n        for i in range(nsections):\n            stop = start + neach + (1 if i < extras else 0)\n            result.append(slice_axis(ary, axis, start, stop))\n            start = stop\n\n        return result\n    else:\n        result = List(capacity=(len(indices_or_sections) + 1))\n        prev = 0\n\n        for s in indices_or_sections:\n            result.append(slice_axis(ary, axis, prev, s))\n            prev = s\n\n        result.append(slice_axis(ary, axis, prev, ntotal))\n        return result\n\ndef split(ary, indices_or_sections, axis: int = 0):\n    ary = asarray(ary)\n    if isinstance(indices_or_sections, int):\n        sections = indices_or_sections\n        axis = util.normalize_axis_index(axis, ary.ndim)\n        N = ary.shape[axis]\n        if N % sections:\n            raise ValueError(\"array split does not result in an equal division\")\n    return array_split(ary, indices_or_sections, axis)\n\ndef vsplit(ary, indices_or_sections):\n    ary = asarray(ary)\n\n    if static.len(ary.shape) < 2:\n        compile_error(\"vsplit only works on arrays of 2 or more dimensions\")\n\n    return split(ary, indices_or_sections, 0)\n\ndef hsplit(ary, indices_or_sections):\n    ary = asarray(ary)\n\n    if static.len(ary.shape) == 0:\n        compile_error(\"hsplit only works on arrays of 1 or more dimensions\")\n\n    return split(ary, indices_or_sections, 1 if ary.ndim > 1 else 0)\n\ndef dsplit(ary, indices_or_sections):\n    ary = asarray(ary)\n\n    if static.len(ary.shape) < 3:\n        compile_error(\"dsplit only works on arrays of 3 or more dimensions\")\n\n    return split(ary, indices_or_sections, 2)\n\ndef trim_zeros(filt, trim: str = 'fb'):\n    filt = asarray(filt)\n\n    if static.len(filt.shape) != 1:\n        compile_error(\"trim_zeros() only takes 1-dimensional arrays as input\")\n\n    fb = (trim == 'fb' or trim == 'bf')\n    just_f = (trim == 'f')\n    just_b = (trim == 'b')\n    trim_front = fb or just_f\n    trim_back = fb or just_b\n\n    if trim_front:\n        n = len(filt)\n        i = 0\n        while i < n and not filt[i]:\n            i += 1\n        filt = filt[i:]\n\n    if trim_back:\n        n = len(filt)\n        i = n - 1\n        while i >= 0 and not filt[i]:\n            i -= 1\n        filt = filt[:i+1]\n\n    return filt\n\ndef flip(m, axis = None):\n    m = asarray(m)\n    ndim: Literal[int] = static.len(m.shape)\n\n    if axis is None:\n        return flip(m, util.tuple_range(ndim))\n    elif isinstance(axis, int):\n        return flip(m, (axis,))\n\n    axis = util.normalize_axis_tuple(axis, ndim)\n    dtype = m.dtype\n    offset = 0\n    shape = m.shape\n    strides = m.strides\n    pshape = Ptr[int](__ptr__(shape).as_byte())\n    pstrides = Ptr[int](__ptr__(strides).as_byte())\n\n    for i in static.range(ndim):\n        if i in axis:\n            st = pstrides[i]\n            offset += (pshape[i] - 1) * st\n            pstrides[i] = -st\n\n    return ndarray(shape, strides, Ptr[dtype](m.data.as_byte() + offset))\n\ndef fliplr(m):\n    m = asarray(m)\n    if static.len(m.shape) < 2:\n        compile_error(\"Input must be >= 2-d.\")\n    return flip(m, axis=1)\n\ndef flipud(m):\n    m = asarray(m)\n    if static.len(m.shape) < 1:\n        compile_error(\"Input must be >= 1-d.\")\n    return flip(m, axis=0)\n\ndef rot90(m, k: int = 1, axes: Tuple[int,int] = (0, 1)):\n    m = asarray(m)\n    ndim: Literal[int] = static.len(m.shape)\n\n    if axes[0] == axes[1] or abs(axes[0] - axes[1]) == ndim:\n        raise ValueError(\"Axes must be different.\")\n\n    if (axes[0] >= ndim or axes[0] < -ndim\n        or axes[1] >= ndim or axes[1] < -ndim):\n        raise ValueError(f\"Axes={axes} out of range for array of ndim={m.ndim}.\")\n\n    k %= 4\n\n    if k == 0:\n        return m[:]\n    if k == 2:\n        return flip(flip(m, axes[0]), axes[1])\n\n    axes_list = util.tuple_range(ndim)\n    axes_list = util.tuple_swap(axes_list, axes[0], axes[1])\n\n    if k == 1:\n        return flip(m, axes[1]).transpose(axes_list)\n    else:\n        # k == 3\n        return flip(m.transpose(axes_list), axes[1])\n\ndef resize(a, new_shape):\n    if isinstance(new_shape, int):\n        return resize(a, (new_shape,))\n\n    a = asarray(a)\n\n    new_size = 1\n    for dim_length in new_shape:\n        new_size *= dim_length\n        if dim_length < 0:\n            raise ValueError('all elements of `new_shape` must be non-negative')\n\n    if a.size == 0 or new_size == 0:\n        # First case must zero fill. The second would have repeats == 0.\n        return zeros(new_shape, dtype=a.dtype)\n\n    new = empty(new_shape, dtype=a.dtype)\n    off = 0\n    go = True\n\n    while go:\n        for idx in util.multirange(a.shape):\n            p = a._ptr(idx)\n            new.data[off] = p[0]\n            off += 1\n\n            if off == new_size:\n                go = False\n                break\n\n    return new\n\ndef tile(A, reps):\n    if isinstance(reps, int):\n        return tile(A, (reps,))\n\n    A = asarray(A)\n    dtype = A.dtype\n    ndim: Literal[int] = static.len(A.shape)\n    d: Literal[int] = static.len(reps)\n\n    if ndim < d:\n        new_shape = ((1,) * (d - ndim)) + A.shape\n        return tile(A.reshape(new_shape), reps)\n\n    if ndim > d:\n        new_reps = ((1,) * (ndim - d)) + reps\n        return tile(A, new_reps)\n\n    out_shape = util.tuple_apply(int.__mul__, A.shape, reps)\n    new = empty(out_shape, dtype=dtype)\n\n    for idx1 in util.multirange(out_shape):\n        idx2 = util.tuple_apply(int.__mod__, idx1, A.shape)\n        q = new._ptr(idx1)\n        p = A._ptr(idx2)\n        q[0] = p[0]\n\n    return new\n\ndef roll(a, shift, axis = None):\n    a = asarray(a)\n    ndim: Literal[int] = static.len(a.shape)\n\n    if axis is None:\n        return roll(a.ravel(), shift, axis=0).reshape(a.shape)\n\n    if isinstance(axis, int):\n        return roll(a, shift, (axis,))\n\n    if isinstance(shift, int):\n        return roll(a, (shift,), axis)\n\n    na: Literal[int] = static.len(axis)\n    ns: Literal[int] = static.len(shift)\n\n    if na == 0:\n        compile_error(\"empty tuple given for 'axis'\")\n\n    if ns == 0:\n        compile_error(\"empty tuple given for 'shift'\")\n\n    if na == 1 and ns != 1:\n        return roll(a, shift, axis * ns)\n\n    if na != 1 and ns == 1:\n        return roll(a, shift * na, axis)\n\n    if static.len(axis) != static.len(shift):\n        compile_error(\"'shift' and 'axis' must be tuples of the same size\")\n\n    axis = util.normalize_axis_tuple(axis, a.ndim, allow_duplicates=True)\n\n    shifts = (0,) * ndim\n    pshifts = Ptr[int](__ptr__(shifts).as_byte())\n\n    for i in static.range(static.len(axis)):\n        pshifts[axis[i]] += shift[i]\n\n    new = empty_like(a)\n\n    @pure\n    @llvm\n    def cdiv(a: int, b: int) -> int:\n        %0 = sdiv i64 %a, %b\n        ret i64 %0\n\n    def pymod(a: int, b: int):\n        d = cdiv(a, b)\n        m = a - d * b\n        if m and ((b ^ m) < 0):\n            m += b\n        return m\n\n    for idx1 in util.multirange(a.shape):\n        idx2 = util.tuple_apply(int.__add__, idx1, shifts)\n        idx2 = util.tuple_apply(pymod, idx2, a.shape)\n        p = a._ptr(idx1)\n        q = new._ptr(idx2)\n        q[0] = p[0]\n\n    return new\n\ndef _atleast_nd(a, ndim: Literal[int]):\n    return array(a, ndmin=ndim, copy=False)\n\ndef _accumulate(values):\n    import itertools\n    return list(itertools.accumulate(values))\n\ndef _longest_shape(a):\n    if isinstance(a, ndarray):\n        return (0,) * static.len(a.shape)\n    elif isinstance(a, List):\n        if len(a) == 0:\n            raise ValueError(\"Lists passed to block cannot be empty\")\n        return _longest_shape(a[0])\n    elif isinstance(a, Tuple):\n        if static.len(a) == 0:\n            compile_error(\"Tuples passed to block cannot be empty\")\n        if static.len(a) == 1:\n            return _longest_shape(a[0])\n        ls1 = _longest_shape(a[0])\n        ls2 = _longest_shape(a[1:])\n        if static.len(ls1) > static.len(ls2):\n            return ls1\n        else:\n            return ls2\n    else:\n        return ()\n\ndef _bottom_index(a):\n    if isinstance(a, ndarray):\n        return ()\n    elif isinstance(a, List):\n        return (0,) + _bottom_index(a[0])\n    elif isinstance(a, Tuple):\n        bi0 = _bottom_index(a[0])\n        for i in static.range(1, static.len(a)):\n            if static.len(_bottom_index(a[i])) != static.len(bi0):\n                compile_error(\"Depths of block argument are mismatched\")\n\n        return (0,) + bi0\n    else:\n        return ()\n\ndef _final_size(a):\n    if isinstance(a, ndarray):\n        return a.size\n    elif isinstance(a, List) or isinstance(a, Tuple):\n        ans = 0\n        for x in a:\n            ans += _final_size(x)\n        return ans\n    else:\n        return 1\n\ndef _block(arrays, max_depth: Literal[int], result_ndim: Literal[int], depth: Literal[int] = 0):\n    if depth < max_depth:\n        arrs = [_block(arr, max_depth, result_ndim, depth+1)\n                for arr in arrays]\n        return concatenate(arrs, axis=-(max_depth-depth))\n    else:\n        return _atleast_nd(arrays, result_ndim)\n\ndef _block_concatenate(arrays, list_ndim: Literal[int], result_ndim: Literal[int]):\n    result = _block(arrays, list_ndim, result_ndim)\n    if list_ndim == 0:\n        result = result.copy()\n    return result\n\ndef _concatenate_shapes(shapes, axis: Literal[int]):\n    # Cache a result that will be reused.\n    shape_at_axis = [shape[axis] for shape in shapes]\n\n    # Take a shape, any shape\n    first_shape = shapes[0]\n    first_shape_pre = first_shape[:axis]\n    first_shape_post = first_shape[axis+1:]\n\n    if any(shape[:axis] != first_shape_pre or\n           shape[axis+1:] != first_shape_post for shape in shapes):\n        raise ValueError(\n            f\"Mismatched array shapes in block along axis {axis}.\")\n\n    shape = (first_shape_pre + (sum(shape_at_axis),) + first_shape[axis+1:])\n\n    offsets_at_axis = _accumulate(shape_at_axis)\n    slice_prefixes = [(slice(start, end),)\n                      for start, end in zip([0] + offsets_at_axis,\n                                            offsets_at_axis)]\n    return shape, slice_prefixes\n\ndef _block_info_recursion(arrs, max_depth: Literal[int], result_ndim: Literal[int], depth: Literal[int] = 0):\n    if depth < max_depth:\n        shapes = []\n        slices = []\n        arrays = []\n\n        for arr in arrs:\n            sh, sl, ar = _block_info_recursion(arr, max_depth, result_ndim, depth+1)\n            shapes.append(sh)\n            slices.append(sl)\n            arrays.append(ar)\n\n        axis: Literal[int] = result_ndim - max_depth + depth\n        shape, slice_prefixes = _concatenate_shapes(shapes, axis)\n\n        # Prepend the slice prefix and flatten the slices\n        slices = [slice_prefix + the_slice\n                  for slice_prefix, inner_slices in zip(slice_prefixes, slices)\n                  for the_slice in inner_slices]\n\n        # Flatten the array list\n        arrays_flat = []\n        for arr in arrays:\n            for a in arr:\n                arrays_flat.append(a)\n\n        return shape, slices, arrays_flat\n    else:\n        arr = _atleast_nd(arrs, result_ndim)\n        return arr.shape, [()], [arr]\n\ndef _block_slicing(arrays, list_ndim: Literal[int], result_ndim: Literal[int]):\n    shape, slices, arrays = _block_info_recursion(\n        arrays, list_ndim, result_ndim)\n    dtype = arrays[0].dtype\n\n    C_order = True\n    F_order = True\n    for arr in arrays:\n        cc, fc = arr._contig\n        C_order = C_order and cc\n        F_order = F_order and fc\n        if not C_order and not F_order:\n            break\n\n    order = 'F' if F_order and not C_order else 'C'\n    result = empty(shape=shape, dtype=dtype, order=order)\n\n    for the_slice, arr in zip(slices, arrays):\n        result[(Ellipsis,) + the_slice] = arr\n    return result\n\ndef block(arrays):\n    ls = _longest_shape(arrays)\n    bi = _bottom_index(arrays)\n    final_size = _final_size(arrays)\n\n    list_ndim: Literal[int] = static.len(bi)\n    arr_ndim: Literal[int] = static.len(ls)\n    result_ndim: Literal[int] = arr_ndim if arr_ndim > list_ndim else list_ndim\n\n    # Note: This is just the heuristic NumPy uses. I have not tested how\n    # good it is for Codon.\n    if list_ndim * final_size > (2 * 512 * 512):\n        return _block_slicing(arrays, list_ndim, result_ndim)\n    else:\n        return _block_concatenate(arrays, list_ndim, result_ndim)\n\ndef _close(a, b, rtol: float = 1e-05, atol: float = 1e-08, equal_nan: bool = False):\n    A = type(a)\n    B = type(b)\n    if A is not B:\n        C = type(util.coerce(A, B))\n        if (C is not float and\n            C is not float32 and\n            C is not float16 and\n            C is not complex and\n            C is not complex64):\n            return _close(util.to_float(a),\n                          util.to_float(b),\n                          rtol=rtol,\n                          atol=atol,\n                          equal_nan=equal_nan)\n        else:\n            return _close(util.cast(a, C),\n                          util.cast(b, C),\n                          rtol=rtol,\n                          atol=atol,\n                          equal_nan=equal_nan)\n    elif (A is not float and\n          A is not float32 and\n          A is not float16 and\n          A is not complex and\n          A is not complex64):\n        return _close(util.to_float(a),\n                      util.to_float(b),\n                      rtol=rtol,\n                      atol=atol,\n                      equal_nan=equal_nan)\n\n    if A is float or A is float32 or A is float16:\n        fin_a = util.isfinite(a)\n        fin_b = util.isfinite(b)\n        if fin_a and fin_b:\n            return abs(a - b) <= A(atol) + A(rtol) * abs(b)\n        else:\n            nan_a = util.isnan(a)\n            nan_b = util.isnan(b)\n            if nan_a or nan_b:\n                if equal_nan:\n                    return nan_a and nan_b\n                else:\n                    return False\n            else:\n                return a == b\n    else:  # complex or complex64\n        R = type(a.real)\n        fin_a = util.isfinite(a.real) and util.isfinite(a.imag)\n        fin_b = util.isfinite(b.real) and util.isfinite(b.imag)\n        if fin_a and fin_b:\n            return abs(a - b) <= R(atol) + R(rtol) * abs(b)\n        else:\n            nan_a = util.isnan(a.real) or util.isnan(a.imag)\n            nan_b = util.isnan(b.real) or util.isnan(b.imag)\n            if nan_a or nan_b:\n                if equal_nan:\n                    return nan_a and nan_b\n                else:\n                    return False\n            else:\n                return a == b\n\ndef isclose(a, b, rtol: float = 1e-05, atol: float = 1e-08, equal_nan: bool = False):\n    a = asarray(a)\n    b = asarray(b)\n\n    if a.ndim == 0 and b.ndim == 0:\n        return _close(a.item(), b.item(), rtol=rtol, atol=atol, equal_nan=equal_nan)\n\n    ans_shape = broadcast_shapes(a.shape, b.shape)\n    ans = empty(ans_shape, bool)\n\n    for idx in util.multirange(ans_shape):\n        xa = a._ptr(idx, broadcast=True)[0]\n        xb = b._ptr(idx, broadcast=True)[0]\n        xans = _close(xa, xb, rtol=rtol, atol=atol, equal_nan=equal_nan)\n        ans._ptr(idx)[0] = xans\n\n    return ans\n\ndef allclose(a, b, rtol: float = 1e-05, atol: float = 1e-08, equal_nan: bool = False):\n    a = asarray(a)\n    b = asarray(b)\n\n    if a.ndim == 0 and b.ndim == 0:\n        return _close(a.item(), b.item(), rtol=rtol, atol=atol, equal_nan=equal_nan)\n\n    ans_shape = broadcast_shapes(a.shape, b.shape)\n\n    for idx in util.multirange(ans_shape):\n        xa = a._ptr(idx, broadcast=True)[0]\n        xb = b._ptr(idx, broadcast=True)[0]\n        if not _close(xa, xb, rtol=rtol, atol=atol, equal_nan=equal_nan):\n            return False\n\n    return True\n\ndef array_equal(a1, a2, equal_nan: bool = False):\n    from .ndmath import isnan\n    a1 = asarray(a1)\n    a2 = asarray(a2)\n\n    if a1.ndim != a2.ndim:\n        return False\n\n    if a1.shape != a2.shape:\n        return False\n\n    dtype = type(util.coerce(a1.dtype, a2.dtype))\n\n    if a1._contig_match(a2):\n        p1 = a1.data\n        p2 = a2.data\n        n = a1.size\n        for i in range(n):\n            e1 = util.cast(p1[i], dtype)\n            e2 = util.cast(p2[i], dtype)\n            if equal_nan:\n                if e1 != e2 and not (isnan(e1) and isnan(e2)):\n                    return False\n            else:\n                if e1 != e2:\n                    return False\n    else:\n        for idx in util.multirange(a1.shape):\n            e1 = util.cast(a1._ptr(idx)[0], dtype)\n            e2 = util.cast(a2._ptr(idx)[0], dtype)\n            if equal_nan:\n                if e1 != e2 and not (isnan(e1) and isnan(e2)):\n                    return False\n            else:\n                if e1 != e2:\n                    return False\n\n    return True\n\ndef array_equiv(a1, a2):\n    def can_broadcast(s1, s2):\n        if static.len(s1) > static.len(s2):\n            return can_broadcast(s1[-static.len(s2):], s2)\n        elif static.len(s1) < static.len(s2):\n            return can_broadcast(s1, s2[-static.len(s1):])\n        else:\n            for i in static.range(static.len(s1)):\n                d1 = s1[i]\n                d2 = s2[i]\n                if d1 != d2 and not (d1 == 1 or d2 == 1):\n                    return False\n            return True\n\n    a1 = asarray(a1)\n    a2 = asarray(a2)\n\n    if not can_broadcast(a1.shape, a2.shape):\n        return False\n\n    bshape = broadcast_shapes(a1.shape, a2.shape)\n    dtype = type(util.coerce(a1.dtype, a2.dtype))\n\n    for idx in util.multirange(bshape):\n        e1 = util.cast(a1._ptr(idx, broadcast=True)[0], dtype)\n        e2 = util.cast(a2._ptr(idx, broadcast=True)[0], dtype)\n        if e1 != e2:\n            return False\n\n    return True\n\ndef squeeze(a, axis = None):\n    if axis is None:\n        compile_error(\"squeeze() must specify 'axis' argument in Codon-NumPy\")\n\n    if isinstance(axis, int):\n        return squeeze(a, (axis,))\n\n    if not isinstance(axis, Tuple):\n        compile_error(\"'axis' must be an int or a tuple of ints\")\n\n    a = asarray(a)\n    axis = util.normalize_axis_tuple(axis, a.ndim)\n    shape = a.shape\n    strides = a.strides\n\n    new_shape = (0,) * (static.len(shape) - static.len(axis))\n    new_strides = (0,) * (static.len(shape) - static.len(axis))\n    pnew_shape = Ptr[int](__ptr__(new_shape).as_byte())\n    pnew_strides = Ptr[int](__ptr__(new_strides).as_byte())\n\n    j = 0\n    for i in static.range(static.len(shape)):\n        if i in axis:\n            if shape[i] != 1:\n                raise ValueError(\"cannot select an axis to squeeze out which has size not equal to one\")\n        else:\n            pnew_shape[j] = shape[i]\n            pnew_strides[j] = strides[i]\n            j += 1\n\n    return ndarray(new_shape, new_strides, a.data)\n\ndef pad(array, pad_width, mode = 'constant', **kwargs):\n    from .ndmath import isnan\n\n    def unpack_params(x, iaxis: int, name: Literal[str]):\n        if isinstance(x, Tuple):\n            if static.len(x) == 1:\n                if isinstance(x[0], Tuple):\n                    if static.len(x[0]) == 1:\n                        return x[0][0], x[0][0]\n                    elif static.len(x[0]) == 2:\n                        return x[0]\n                    else:\n                        compile_error(\"invalid parameter '\" + name + \"' given\")\n                else:\n                    return x[0], x[0]\n            elif static.len(x) == 2:\n                if isinstance(x[0], Tuple):\n                    return x[iaxis]\n                else:\n                    return x\n            else:\n                return x[iaxis]\n        else:\n            return x, x\n\n    def unpack_stat_length(k: int, iaxis: int, kwargs):\n        stat_length = kwargs.get('stat_length', k)\n        s1, s2 = unpack_params(stat_length, iaxis, 'stat_length')\n\n        if s1 <= 0 or s2 <= 0:\n            raise ValueError(\"'stat_length' must contain positive values\")\n\n        s1 = min(s1, k)\n        s2 = min(s2, k)\n\n        return s1, s2\n\n    def round_if_needed(x, dtype: type):\n        if dtype is int or isinstance(dtype, Int) or isinstance(dtype, UInt):\n            return util.cast(util.rint(x), dtype)\n        else:\n            return util.cast(x, dtype)\n\n    def pad_from_function(a: ndarray, pw, padding_func, kwargs, extra = None):\n        shape = a.shape\n        strides = a.strides\n        ndim: Literal[int] = static.len(shape)\n\n        for i in static.range(ndim):\n            length = shape[i]\n            stride = strides[i]\n\n            for idx in util.multirange(util.tuple_delete(shape, i)):\n                p = a._ptr(util.tuple_insert(idx, i, 0))\n                vector = ndarray((length,), (stride,), p)\n                padding_func(vector, pw[i], i, kwargs, extra)\n\n    def pad_constant(vector: ndarray,\n                     iaxis_pad_width: Tuple[int, int],\n                     iaxis: int,\n                     kwargs,\n                     extra):\n        p1, p2 = iaxis_pad_width\n        cval = kwargs.get('constant_values', 0)\n        dtype = vector.dtype\n        c1, c2 = unpack_params(cval, iaxis, 'constant_values')\n        n = vector.size\n\n        # Note we don't need to do range checks since padded\n        # array size will always ensure we're not out of bounds,\n        # as long as each `iaxis_pad_width` is non-negative.\n\n        for i in range(0, p1):\n            vector._ptr((i,))[0] = util.cast(c1, dtype)\n\n        for i in range(n - p2, n):\n            vector._ptr((i,))[0] = util.cast(c2, dtype)\n\n    def pad_wrap(vector: ndarray,\n                 iaxis_pad_width: Tuple[int, int],\n                 iaxis: int,\n                 kwargs,\n                 extra):\n        p1, p2 = iaxis_pad_width\n        n = vector.size\n        k = n - (p1 + p2)\n\n        for i in range(n - p2, n):\n            vector._ptr((i,))[0] = vector._ptr((i - k,))[0]\n\n        for i in range(p1 - 1, -1, -1):\n            vector._ptr((i,))[0] = vector._ptr((i + k,))[0]\n\n    def pad_edge(vector: ndarray,\n                 iaxis_pad_width: Tuple[int, int],\n                 iaxis: int,\n                 kwargs,\n                 extra):\n        p1, p2 = iaxis_pad_width\n        n = vector.size\n        c1 = vector._ptr((p1,))[0]\n        c2 = vector._ptr((n - 1 - p2,))[0]\n\n        for i in range(0, p1):\n            vector._ptr((i,))[0] = c1\n\n        for i in range(n - p2, n):\n            vector._ptr((i,))[0] = c2\n\n    def pad_max(vector: ndarray,\n                iaxis_pad_width: Tuple[int, int],\n                iaxis: int,\n                kwargs,\n                extra):\n        p1, p2 = iaxis_pad_width\n        n = vector.size\n        k = n - (p1 + p2)\n        s1, s2 = unpack_stat_length(k, iaxis, kwargs)\n\n        m1 = vector._ptr((p1,))[0]\n        for i in range(p1 + 1, p1 + s1):\n            e = vector._ptr((i,))[0]\n            if e > m1:\n                m1 = e\n\n        if s1 == k and s2 == k:\n            m2 = m1\n        else:\n            m2 = vector._ptr((n - p2 - s2,))[0]\n            for i in range(n - p2 - s2 + 1, n - p2):\n                e = vector._ptr((i,))[0]\n                if e > m2:\n                    m2 = e\n\n        for i in range(0, p1):\n            vector._ptr((i,))[0] = m1\n\n        for i in range(n - p2, n):\n            vector._ptr((i,))[0] = m2\n\n    def pad_min(vector: ndarray,\n                iaxis_pad_width: Tuple[int, int],\n                iaxis: int,\n                kwargs,\n                extra):\n        p1, p2 = iaxis_pad_width\n        n = vector.size\n        k = n - (p1 + p2)\n        s1, s2 = unpack_stat_length(k, iaxis, kwargs)\n\n        m1 = vector._ptr((p1,))[0]\n        for i in range(p1 + 1, p1 + s1):\n            e = vector._ptr((i,))[0]\n            if e < m1:\n                m1 = e\n\n        if s1 == k and s2 == k:\n            m2 = m1\n        else:\n            m2 = vector._ptr((n - p2 - s2,))[0]\n            for i in range(n - p2 - s2 + 1, n - p2):\n                e = vector._ptr((i,))[0]\n                if e < m2:\n                    m2 = e\n\n        for i in range(0, p1):\n            vector._ptr((i,))[0] = m1\n\n        for i in range(n - p2, n):\n            vector._ptr((i,))[0] = m2\n\n    def pad_mean(vector: ndarray,\n                 iaxis_pad_width: Tuple[int, int],\n                 iaxis: int,\n                 kwargs,\n                 extra):\n        p1, p2 = iaxis_pad_width\n        n = vector.size\n        k = n - (p1 + p2)\n        s1, s2 = unpack_stat_length(k, iaxis, kwargs)\n\n        m1 = vector._ptr((p1,))[0]\n        for i in range(p1 + 1, p1 + s1):\n            m1 += vector._ptr((i,))[0]\n\n        if s1 == k and s2 == k:\n            m2 = m1\n        else:\n            m2 = vector._ptr((n - p2 - s2,))[0]\n            for i in range(n - p2 - s2 + 1, n - p2):\n                m2 += vector._ptr((i,))[0]\n\n        dtype = vector.dtype\n        av1 = round_if_needed(m1 / util.cast(s1, dtype), dtype)\n        av2 = round_if_needed(m2 / util.cast(s2, dtype), dtype)\n\n        for i in range(0, p1):\n            vector._ptr((i,))[0] = av1\n\n        for i in range(n - p2, n):\n            vector._ptr((i,))[0] = av2\n\n    def pad_median(vector: ndarray,\n                   iaxis_pad_width: Tuple[int, int],\n                   iaxis: int,\n                   kwargs,\n                   extra):\n        p1, p2 = iaxis_pad_width\n        n = vector.size\n        k = n - (p1 + p2)\n        s1, s2 = unpack_stat_length(k, iaxis, kwargs)\n\n        buf = extra\n        nan_idx = -1\n        for i in range(p1, p1 + s1):\n            e = vector._ptr((i,))[0]\n            if isnan(e):\n                nan_idx = i\n                break\n            else:\n                buf[i - p1] = e\n\n        dtype = vector.dtype\n        if nan_idx >= 0:\n            m1 = vector._ptr((nan_idx,))[0]\n        else:\n            m1a, m1b = util.median(buf, s1)\n            if m1a == m1b:\n                m1 = m1a\n            else:\n                m1 = round_if_needed((m1a + m1b) / util.cast(2, dtype), dtype)\n\n        if s1 == k and s2 == k:\n            m2 = m1\n        else:\n            nan_idx = -1\n            base = n - p2 - s2\n            for i in range(base, n - p2):\n                e = vector._ptr((i,))[0]\n                if isnan(e):\n                    nan_idx = i\n                    break\n                else:\n                    buf[i - base] = e\n\n            if nan_idx >= 0:\n                m2 = vector._ptr((nan_idx,))[0]\n            else:\n                m2a, m2b = util.median(buf, s2)\n                if m2a == m2b:\n                    m2 = m2a\n                else:\n                    m2 = round_if_needed((m2a + m2b) / util.cast(2, dtype), dtype)\n\n        for i in range(0, p1):\n            vector._ptr((i,))[0] = m1\n\n        for i in range(n - p2, n):\n            vector._ptr((i,))[0] = m2\n\n    def pad_linear_ramp(vector: ndarray,\n                        iaxis_pad_width: Tuple[int, int],\n                        iaxis: int,\n                        kwargs,\n                        extra):\n        def fill_linear(vec: ndarray, offset: int, start: float,\n                        stop: float, num: int, rev: bool):\n            if num == 0:\n                return\n\n            dtype = vec.dtype\n            delta = stop - start\n            step = delta / num\n\n            for i in range(num):\n                j = num - 1 - i if rev else i\n                p = vec._ptr((j + offset,))\n                e = ((i / num) * delta) + start if not step else (i * step) + start\n                p[0] = round_if_needed(e, dtype)\n\n        p1, p2 = iaxis_pad_width\n        n = vector.size\n        end_values = kwargs.get('end_values', 0)\n        start1, end2 = unpack_params(end_values, iaxis, 'end_values')\n        end1 = vector._ptr((p1,))[0]\n        start2 = vector._ptr((n - 1 - p2,))[0]\n        fill_linear(vector,\n                    offset=0,\n                    start=util.cast(start1, float),\n                    stop=util.cast(end1, float),\n                    num=p1,\n                    rev=False)\n        fill_linear(vector,\n                    offset=(n - p2),\n                    start=util.cast(end2, float),\n                    stop=util.cast(start2, float),\n                    num=p2,\n                    rev=True)\n\n    def pad_reflect_or_symmetric(vector: ndarray,\n                                 iaxis_pad_width: Tuple[int, int],\n                                 iaxis: int,\n                                 kwargs,\n                                 extra):\n        p1, p2 = iaxis_pad_width\n        n = vector.size\n        k = n - (p1 + p2)\n        diff = extra\n\n        if k == 1:\n            e = vector._ptr((p1,))[0]\n\n            for i in range(0, p1):\n                vector._ptr((i,))[0] = e\n\n            for i in range(n - p2, n):\n                vector._ptr((i,))[0] = e\n\n            return\n\n        even = kwargs.get('reflect_type', 'even') != 'odd'\n\n        # left side\n        i = p1 - 1\n        while i >= 0:\n            z = i\n            edge = vector._ptr((z + 1,))[0]\n\n            for j in range(k - diff):\n                e = vector._ptr((z + j + (1 + diff),))[0]\n                if not even:\n                    e = (edge + edge) - e\n\n                vector._ptr((i,))[0] = e\n                i -= 1\n                if i < 0:\n                    break\n\n        # right side\n        i = n - p2\n        while i < n:\n            z = i\n            edge = vector._ptr((z - 1,))[0]\n\n            for j in range(k - diff):\n                e = vector._ptr((z - j - (1 + diff),))[0]\n                if not even:\n                    e = (edge + edge) - e\n\n                vector._ptr((i,))[0] = e\n                i += 1\n                if i >= n:\n                    break\n\n    a = asarray(array)\n    s = a.shape\n    dtype = a.dtype\n    ndim: Literal[int] = static.len(s)\n\n    if ndim == 0:\n        return a\n\n    if isinstance(mode, str):\n        if a.size == 0 and mode not in ('empty', 'constant'):\n            raise ValueError(\"can't pad empty array using modes other than 'constant' or 'empty'\")\n\n    if isinstance(pad_width, int):\n        pw = ((pad_width, pad_width),) * ndim\n    elif isinstance(pad_width, Tuple[int]):\n        pw = ((pad_width[0], pad_width[0]),) * ndim\n    elif isinstance(pad_width, Tuple[int, int]):\n        pw = (pad_width,) * ndim\n    elif isinstance(pad_width, Tuple[Tuple[int, int]]):\n        pw = (pad_width[0],) * ndim\n    elif static.len(pad_width) == 1:\n        pw = pad_width * ndim\n    else:\n        pw = pad_width\n\n    if static.len(pw) != ndim:\n        compile_error(\"invalid pad_width\")\n\n    for p in pw:\n        if p[0] < 0 or p[1] < 0:\n            raise ValueError(\"padding can't be negative\")\n\n    new_shape = tuple(s[i] + pw[i][0] + pw[i][1] for i in static.range(ndim))\n\n    if isinstance(mode, str):\n        ans = empty(new_shape, dtype)\n    else:\n        ans = zeros(new_shape, dtype)\n\n    # copy in original array\n    for idx in util.multirange(s):\n        idx1 = tuple(idx[i] + pw[i][0] for i in static.range(ndim))\n        p = a._ptr(idx)\n        q = ans._ptr(idx1)\n        q[0] = p[0]\n\n    if isinstance(mode, str):\n        if mode == 'empty':\n            pass  # do nothing\n        elif mode == 'constant':\n            pad_from_function(ans, pw, pad_constant, kwargs)\n        elif mode == 'wrap':\n            pad_from_function(ans, pw, pad_wrap, kwargs)\n        elif mode == 'edge':\n            pad_from_function(ans, pw, pad_edge, kwargs)\n        elif mode == 'linear_ramp':\n            pad_from_function(ans, pw, pad_linear_ramp, kwargs)\n        elif mode == 'maximum':\n            pad_from_function(ans, pw, pad_max, kwargs)\n        elif mode == 'minimum':\n            pad_from_function(ans, pw, pad_min, kwargs)\n        elif mode == 'mean':\n            pad_from_function(ans, pw, pad_mean, kwargs)\n        elif mode == 'median':\n            buf_size = 0\n            for iaxis in range(ndim):\n                s1, s2 = unpack_stat_length(s[iaxis], iaxis, kwargs)\n                if s1 > buf_size:\n                    buf_size = s1\n                if s2 > buf_size:\n                    buf_size = s2\n            buf = Ptr[dtype](buf_size)\n            pad_from_function(ans, pw, pad_median, kwargs, extra=buf)\n        elif mode == 'reflect':\n            pad_from_function(ans, pw, pad_reflect_or_symmetric, kwargs, extra=1)\n        elif mode == 'symmetric':\n            pad_from_function(ans, pw, pad_reflect_or_symmetric, kwargs, extra=0)\n        else:\n            raise ValueError(f\"mode {repr(mode)} is not supported\")\n    else:\n        pad_from_function(ans, pw, mode, kwargs)\n\n    return ans\n\n#############\n# Searching #\n#############\n\ndef nonzero(a):\n    a = asarray(a)\n\n    if static.len(a.shape) == 0:\n        # Note: this is technically deprecated behavior\n        if bool(a.item()):\n            ans = empty(1, int)\n            ans.data[0] = 0\n            return (ans,)\n        else:\n            return (empty(0, int),)\n    else:\n        num_true = 0\n        if a._is_contig:\n            for i in range(a.size):\n                if bool(a.data[i]):\n                    num_true += 1\n        else:\n            for idx in util.multirange(a.shape):\n                if bool(a._ptr(idx)[0]):\n                    num_true += 1\n\n        ans = tuple(empty(num_true, int) for _ in a.shape)\n        k = 0\n\n        for idx in util.multirange(a.shape):\n            if bool(a._ptr(idx)[0]):\n                for i in static.range(static.len(ans)):\n                    ans[i].data[k] = idx[i]\n                k += 1\n\n        return ans\n\ndef flatnonzero(a):\n    a = asarray(a)\n\n    if static.len(a.shape) <= 1:\n        return nonzero(a)[0]\n    else:\n        sz = a.size\n        cc, fc = a._contig\n\n        num_true = 0\n        if cc or fc:\n            for i in range(sz):\n                if bool(a.data[i]):\n                    num_true += 1\n        else:\n            for idx in util.multirange(a.shape):\n                if bool(a._ptr(idx)[0]):\n                    num_true += 1\n\n        ans = empty(num_true, int)\n        k = 0\n\n        if cc:\n            for i in range(sz):\n                if bool(a.data[i]):\n                    ans.data[k] = i\n                    k += 1\n        else:\n            j = 0\n            for idx in util.multirange(a.shape):\n                if bool(a._ptr(idx)[0]):\n                    ans.data[k] = j\n                    k += 1\n                j += 1\n\n        return ans\n\ndef argwhere(a):\n    a = asarray(a)\n\n    if static.len(a.shape) == 0:\n        m = 1 if bool(a.item()) else 0\n        return empty((m, 0), int)\n    else:\n        num_true = 0\n        if a._is_contig:\n            for i in range(a.size):\n                if bool(a.data[i]):\n                    num_true += 1\n        else:\n            for idx in util.multirange(a.shape):\n                if bool(a._ptr(idx)[0]):\n                    num_true += 1\n\n        ans = empty((num_true, a.ndim), int)\n        k = 0\n\n        for idx in util.multirange(a.shape):\n            if bool(a._ptr(idx)[0]):\n                for i in range(a.ndim):\n                    ans._ptr((k, i))[0] = idx[i]\n                k += 1\n\n        return ans\n\ndef where(condition, x, y):\n    condition = asarray(condition)\n    x = asarray(x)\n    y = asarray(y)\n    T = type(util.coerce(x.dtype, y.dtype))\n\n    if condition._contig_match(x) and condition._contig_match(y):\n        cc, fc = condition._contig\n        ans = empty(condition.shape, dtype=T, order=('C' if cc else 'F'))\n\n        sz = condition.size\n        pc = condition.data\n        px = x.data\n        py = y.data\n        q = ans.data\n\n        for i in range(sz):\n            if pc[i]:\n                q[i] = util.cast(px[i], T)\n            else:\n                q[i] = util.cast(py[i], T)\n\n        return ans\n    else:\n        bshape = broadcast_shapes(condition.shape, x.shape, y.shape)\n        ans = empty(bshape, dtype=T)\n\n        for idx in util.multirange(bshape):\n            q = ans._ptr(idx)\n            if condition._ptr(idx, broadcast=True)[0]:\n                q[0] = util.cast(x._ptr(idx, broadcast=True)[0], T)\n            else:\n                q[0] = util.cast(y._ptr(idx, broadcast=True)[0], T)\n\n        return ans\n\n@overload\ndef where(condition):\n    return nonzero(asarray(condition))\n\ndef extract(condition, arr):\n    condition = ravel(asarray(condition))\n    arr = ravel(asarray(arr))\n\n    num_true = 0\n    for i in range(condition.size):\n        if condition.data[i]:\n            num_true += 1\n\n    if num_true > arr.size:\n        raise IndexError(f\"index {num_true} is out of bounds for axis 0 with size {arr.size}\")\n\n    ans = empty(num_true, arr.dtype)\n    k = 0\n    i = 0\n    m = min(arr.size, condition.size)\n\n    while i < m and k < num_true:\n        if condition.data[i]:\n            ans.data[k] = arr.data[i]\n            k += 1\n        i += 1\n\n    return ans\n\ndef _less_than(a, b):\n    T = type(util.coerce(type(a), type(b)))\n    return util.cast(a, T) < util.cast(b, T)\n\ndef _bisect_left(p: Ptr[T], s: int, n: int, x: X, lo: int, hi: int, T: type, X: type):\n    while lo < hi:\n        mid = (lo + hi) >> 1\n        elem = Ptr[T](p.as_byte() + (mid * s))[0]\n\n        if _less_than(elem, x):\n            lo = mid + 1\n        else:\n            hi = mid\n    return lo, hi\n\ndef _bisect_right(p: Ptr[T], s: int, n: int, x: X, lo: int, hi: int, T: type, X: type):\n    while lo < hi:\n        mid = (lo + hi) >> 1\n        elem = Ptr[T](p.as_byte() + (mid * s))[0]\n\n        if _less_than(x, elem):\n            hi = mid\n        else:\n            lo = mid + 1\n    return lo, hi\n\ndef _bisect_left_perm(p: Ptr[T], s: int, n: int, x: X, perm: Ptr[int],\n                      sp: int, lo: int, hi: int, T: type, X: type):\n    while lo < hi:\n        mid = (lo + hi) >> 1\n        mid_remap = Ptr[int](perm.as_byte() + (mid * sp))[0]\n        elem = Ptr[T](p.as_byte() + (mid_remap * s))[0]\n\n        if _less_than(elem, x):\n            lo = mid + 1\n        else:\n            hi = mid\n    return lo, hi\n\ndef _bisect_right_perm(p: Ptr[T], s: int, n: int, x: X, perm: Ptr[int],\n                       sp: int, lo: int, hi: int, T: type, X: type):\n    while lo < hi:\n        mid = (lo + hi) >> 1\n        mid_remap = Ptr[int](perm.as_byte() + (mid * sp))[0]\n        elem = Ptr[T](p.as_byte() + (mid_remap * s))[0]\n\n        if _less_than(x, elem):\n            hi = mid\n        else:\n            lo = mid + 1\n    return lo, hi\n\ndef searchsorted(a, v, side: str = 'left', sorter = None):\n    a = asarray(a)\n    v = asarray(v)\n\n    if static.len(a.shape) != 1:\n        compile_error(\"searchsorted requires 1-dimensional input array\")\n\n    n = a.size\n    s = a.strides[0]\n\n    left = False\n    if side == 'left':\n        left = True\n    elif side == 'right':\n        left = False\n    else:\n        raise ValueError(f\"side must be 'left' or 'right' (got {repr(side)})\")\n\n    if sorter is None:\n        perm = None\n        sp = 0\n    else:\n        perm = asarray(sorter)\n\n        if static.len(perm.shape) != 1:\n            compile_error(\"sorter argument must be 1-dimensional\")\n\n        if perm.dtype is not int:\n            compile_error(\"sorter must only contain integers\")\n\n        if perm.size != n:\n            raise ValueError(\"sorter.size must equal a.size\")\n\n        for sorter_idx in perm:\n            if sorter_idx < 0 or sorter_idx >= n:\n                raise ValueError(\"Sorter index out of range.\")\n\n        sp = perm.strides[0]\n\n    # copy for large needles to improve cache performance\n    if v.size > a.size:\n        a = ascontiguousarray(a)\n        s = a.strides[0]\n\n    if static.len(v.shape) == 0:\n        x = v.item()\n        if sorter is None:\n            if left:\n                return _bisect_left(a.data, s, n, x, lo=0, hi=n)[0]\n            else:\n                return _bisect_right(a.data, s, n, x, lo=0, hi=n)[0]\n        else:\n            if left:\n                return _bisect_left_perm(a.data, s, n, x, perm.data, sp, lo=0, hi=n)[0]\n            else:\n                return _bisect_right_perm(a.data, s, n, x, perm.data, sp, lo=0, hi=n)[0]\n    else:\n        ans = empty(v.shape, int)\n\n        if v.size == 0:\n            return ans\n\n        last_x = v._ptr((0,) * static.len(v.shape))[0]\n        lo = 0\n        hi = n\n\n        for idx in util.multirange(v.shape):\n            x = v._ptr(idx)[0]\n\n            if last_x < x:\n                hi = n\n            else:\n                lo = 0\n                hi = hi + 1 if hi < n else n\n\n            last_x = x\n\n            if sorter is None:\n                if left:\n                    lo, hi = _bisect_left(a.data, s, n, x, lo=lo, hi=hi)\n                else:\n                    lo, hi = _bisect_right(a.data, s, n, x, lo=lo, hi=hi)\n            else:\n                if left:\n                    lo, hi = _bisect_left_perm(a.data, s, n, x, perm.data, sp, lo=lo, hi=hi)\n                else:\n                    lo, hi = _bisect_right_perm(a.data, s, n, x, perm.data, sp, lo=lo, hi=hi)\n\n            ans._ptr(idx)[0] = lo\n\n        return ans\n\n############\n# Indexing #\n############\n\ndef take(a, indices, axis = None, out = None, mode: str = 'raise'):\n    a = atleast_1d(asarray(a, order='C'))\n\n    if axis is None:\n        return take(a.ravel(), indices, axis=0, out=out, mode=mode)\n\n    indices = asarray(indices, order='C')\n\n    axis = util.normalize_axis_index(axis, a.ndim)\n    n = 1\n    m = 1\n    chunk = 1\n    nd: Literal[int] = static.len(a.shape) + static.len(indices.shape) - 1\n    shape = (0,) * nd\n    ashape0 = a.shape\n    ishape0 = indices.shape\n    ashape = Ptr[int](__ptr__(ashape0).as_byte())\n    ishape = Ptr[int](__ptr__(ishape0).as_byte())\n    pshape = Ptr[int](__ptr__(shape).as_byte())\n\n    for i in static.range(nd):\n        if i < axis:\n            pshape[i] = ashape[i]\n            n *= pshape[i]\n        else:\n            if i < axis + indices.ndim:\n                pshape[i] = ishape[i - axis]\n                m *= pshape[i]\n            else:\n                pshape[i] = ashape[i - indices.ndim + 1]\n                chunk *= pshape[i]\n\n    if out is None:\n        res = empty(shape, dtype=a.dtype)\n    elif isinstance(out, ndarray):\n        if static.len(out.shape) != static.len(shape) or out.dtype is not a.dtype:\n            compile_error(\"output array does not match result of ndarray.take\")\n\n        if out.shape != shape:\n            raise ValueError(\"output array does not match result of ndarray.take\")\n\n        res = asarray(out, order='C')\n    else:\n        compile_error(\"output must be an array\")\n\n    max_item = a.shape[axis]\n    nelem = chunk\n    itemsize = res.itemsize\n    chunk *= itemsize\n    src = a.data.as_byte()\n    dest = res.data.as_byte()\n    indices_data = indices.data\n\n    if max_item == 0 and res.size != 0:\n        raise IndexError(\"cannot do a non-empty take from an empty axes.\")\n\n    if mode == 'raise':\n        for i in range(n):\n            for j in range(m):\n                tmp = indices_data[j]\n                tmp = util.normalize_index(tmp, axis, max_item)\n                tmp_src = src + tmp * chunk\n                str.memcpy(dest, tmp_src, chunk)\n                dest += chunk\n            src += chunk * max_item\n    elif mode == 'wrap':\n        for i in range(n):\n            for j in range(m):\n                tmp = indices_data[j]\n                if tmp < 0:\n                    while tmp < 0:\n                        tmp += max_item\n                elif tmp >= max_item:\n                    while tmp >= max_item:\n                        tmp -= max_item\n\n                tmp_src = src + tmp * chunk\n                str.memcpy(dest, tmp_src, chunk)\n                dest += chunk\n            src += chunk * max_item\n    elif mode == 'clip':\n        for i in range(n):\n            for j in range(m):\n                tmp = indices_data[j]\n                if tmp < 0:\n                    tmp = 0\n                elif tmp >= max_item:\n                    tmp = max_item - 1\n\n                tmp_src = src + tmp * chunk\n                str.memcpy(dest, tmp_src, chunk)\n                dest += chunk\n            src += chunk * max_item\n    else:\n        raise ValueError(f\"clipmode must be one of 'clip', 'raise', or 'wrap' (got {repr(mode)})\")\n\n    if res.ndim == 0:\n        return res.item()\n    else:\n        return res\n\ndef indices(dimensions, dtype: type = int, sparse: Literal[bool] = False):\n    if not isinstance(dimensions, Tuple):\n        compile_error(\"dimensions must be a tuple of integers\")\n\n    N: Literal[int] = static.len(dimensions)\n    shape = (1,) * N\n\n    if sparse:\n        return tuple(\n            arange(dimensions[i], dtype=dtype).reshape(\n                shape[:i] + (dimensions[i],) + shape[i+1:]) for i in static.range(N))\n\n    res = empty((N,) + dimensions, dtype=dtype)\n\n    for i in static.range(N):\n        dim = dimensions[i]\n        idx = arange(dim, dtype=dtype).reshape(shape[:i] + (dim,) + shape[i+1:])\n        res[i] = idx\n\n    return res\n\ndef ix_(*args):\n    def ix_one(new, nd: Literal[int], k: Literal[int]):\n        new = asarray(new)\n        if static.len(new.shape) != 1:\n            compile_error(\"Cross index must be 1 dimensional\")\n\n        if new.dtype is bool:\n            newx = new.nonzero()[0]\n        else:\n            newx = new\n\n        return newx.reshape((1,)*k + (newx.size,) + (1,)*(nd-k-1))\n\n    nd: Literal[int] = static.len(args)\n    return tuple(ix_one(args[k], nd, k) for k in static.range(nd))\n\ndef ravel_multi_index(multi_index, dims, mode: str = 'raise', order: str = 'C'):\n    def fix_index(idx: int, axis: int, dim: int, mode: str):\n        if mode == 'raise':\n            if idx < 0 or idx >= dim:\n                raise ValueError(\"invalid entry in coordinates array\")\n        elif mode == 'wrap':\n            if idx < 0:\n                while idx < 0:\n                    idx += dim\n            elif idx >= dim:\n                while idx >= dim:\n                    idx -= dim\n        elif mode == 'clip':\n            if idx < 0:\n                idx = 0\n            elif idx >= dim:\n                idx = dim - 1\n\n        return idx\n\n    def gather_non_ints(x):\n        if static.len(x) == 0:\n            return ()\n\n        i = x[0]\n        rest = gather_non_ints(x[1:])\n\n        if isinstance(i, int):\n            return rest\n        else:\n            return (i,) + rest\n\n    if isinstance(dims, int):\n        return ravel_multi_index(multi_index, (dims,), mode=mode, order=order)\n\n    for d in dims:\n        if d <= 0:\n            raise ValueError(\"dimensions must be positive\")\n\n    if mode not in ('raise', 'wrap', 'clip'):\n        raise ValueError(f\"clipmode must be one of 'clip', 'raise', or 'wrap' (got {repr(mode)})\")\n\n    corder = True\n    if order == 'C':\n        corder = True\n    elif order == 'F':\n        corder = False\n    else:\n        raise ValueError(\"only 'C' or 'F' order is permitted\")\n\n    N: Literal[int] = static.len(dims)\n\n    if isinstance(multi_index, List[int]):\n        if len(multi_index) != N:\n            raise ValueError(f\"parameter multi_index must be a sequence of length {N}\")\n\n        idx = tuple(fix_index(multi_index[j], j, dims[j], mode) for j in static.range(N))\n        return util.coords_to_index(idx, dims) if corder else util.coords_to_findex(idx, dims)\n    elif isinstance(multi_index, Tuple):\n        if static.len(gather_non_ints(multi_index)) == 0:\n            if static.len(multi_index) != static.len(dims):\n                compile_error(\"parameter multi_index does not match dims in size\")\n\n            idx = tuple(fix_index(multi_index[j], j, dims[j], mode) for j in static.range(N))\n            return util.coords_to_index(idx, dims) if corder else util.coords_to_findex(idx, dims)\n\n        midx = vstack(multi_index)\n    else:\n        midx = asarray(multi_index)\n\n    if static.len(midx.shape) != 2:\n        compile_error(\"multi_index must be 2 dimensional\")\n\n    if len(multi_index) != N:\n        raise ValueError(f\"parameter multi_index must be a sequence of length {N}\")\n\n    ans = empty(midx.shape[1], int)\n\n    for i in range(ans.size):\n        idx = tuple(midx._ptr((j, i))[0] for j in static.range(N))\n        idx = tuple(fix_index(idx[j], j, dims[j], mode) for j in static.range(N))\n        res = util.coords_to_index(idx, dims) if corder else util.coords_to_findex(idx, dims)\n        ans.data[i] = res\n\n    return ans\n\ndef unravel_index(indices, shape, order: str = 'C'):\n    def check(idx: int, n: int):\n        if idx < 0 or idx >= n:\n            raise ValueError(f\"index {idx} is out of bounds for array with size {n}\")\n        return idx\n\n    if isinstance(shape, int):\n        return unravel_index(indices, (shape,), order=order)\n\n    corder = True\n    if order == 'C':\n        corder = True\n    elif order == 'F':\n        corder = False\n    else:\n        raise ValueError(\"only 'C' or 'F' order is permitted\")\n\n    N: Literal[int] = static.len(shape)\n    if N == 0:\n        if not isinstance(indices, int):\n            compile_error(\"multiple indices are not supported for 0d arrays\")\n\n        check(indices, 1)\n        return ()\n\n    n = 1\n    for s in shape:\n        if s < 0:\n            raise ValueError(\"dimensions must be non-negative\")\n        n *= s\n\n    if isinstance(indices, int):\n        check(indices, n)\n        return util.index_to_coords(indices, shape) if corder else util.index_to_fcoords(indices, shape)\n\n    indices = asarray(indices)\n    N: Literal[int] = static.len(shape)\n    ans = tuple(empty(indices.shape, int) for _ in shape)\n\n    for idx in util.multirange(indices.shape):\n        index = indices._ptr(idx)[0]\n        check(index, n)\n        res = util.index_to_coords(index, shape) if corder else util.index_to_fcoords(index, shape)\n\n        for i in static.range(N):\n            ans[i]._ptr(idx)[0] = res[i]\n\n    return ans\n\ndef diag_indices(n: int, ndim: int):\n    idx = arange(n)\n    return [idx for _ in range(ndim)]\n\n@overload\ndef diag_indices(n: int, ndim: Literal[int] = 2):\n    idx = arange(n)\n    return (idx,) * ndim\n\ndef diag_indices_from(arr):\n    arr = asarray(arr)\n    shape = arr.shape\n    ndim: Literal[int] = static.len(shape)\n\n    if ndim < 2:\n        compile_error(\"input array must be at least 2-d\")\n\n    for i in static.range(1, ndim):\n        if shape[i] != shape[0]:\n            raise ValueError(\"All dimensions of input must be of equal length\")\n\n    return diag_indices(shape[0], ndim)\n\ndef mask_indices(n: int, mask_func, k = 0):\n    m = ones((n, n), int)\n    a = mask_func(m, k)\n    return nonzero(a != 0)\n\ndef tril_indices(n: int, k: int = 0, m: Optional[int] = None):\n    tri_ = tri(n, m, k=k, dtype=bool)\n    return tuple(broadcast_to(inds, tri_.shape)[tri_]\n                 for inds in indices(tri_.shape, sparse=True))\n\ndef tril_indices_from(arr, k: int = 0):\n    arr = asarray(arr)\n    if static.len(arr.shape) != 2:\n        compile_error(\"input array must be 2-d\")\n    return tril_indices(arr.shape[-2], k=k, m=arr.shape[-1])\n\ndef triu_indices(n: int, k: int = 0, m: Optional[int] = None):\n    tri_ = ~tri(n, m, k=k - 1, dtype=bool)\n    return tuple(broadcast_to(inds, tri_.shape)[tri_]\n                 for inds in indices(tri_.shape, sparse=True))\n\ndef triu_indices_from(arr, k: int = 0):\n    arr = asarray(arr)\n    if static.len(arr.shape) != 2:\n        compile_error(\"input array must be 2-d\")\n    return triu_indices(arr.shape[-2], k=k, m=arr.shape[-1])\n\ndef take_along_axis(arr, indices, axis):\n    def dim_mismatch():\n        compile_error(\"`indices` and `arr` must have the same number of dimensions\")\n\n    arr = asarray(arr)\n    indices = asarray(indices)\n\n    if indices.dtype is not int:\n        compile_error(\"`indices` must be an integer array\")\n\n    if axis is None:\n        if static.len(indices.shape) != 1:\n            dim_mismatch()\n\n        out = empty(indices.size, arr.dtype)\n        for i in range(indices.size):\n            out.data[i] = arr._get_flat(indices._ptr((i,))[0], check=True)\n\n        return out\n\n    if static.len(arr.shape) != static.len(indices.shape):\n        dim_mismatch()\n\n    axis = util.normalize_axis_index(axis, arr.ndim)\n    M = arr.shape[axis]\n    J = indices.shape[axis]\n\n    bshape = broadcast_shapes(util.tuple_delete(arr.shape, axis),\n                              util.tuple_delete(indices.shape, axis))\n    out_shape = util.tuple_insert(bshape, axis, J)\n\n    out = empty(out_shape, arr.dtype)\n    a_stride = arr.strides[axis]\n    indices_stride = indices.strides[axis]\n    out_stride = out.strides[axis]\n\n    for idx in util.multirange(bshape):\n        base_idx   = util.tuple_insert(idx, axis, 0)\n        a_1d       = ndarray((M,), (a_stride,), arr._ptr(base_idx, broadcast=True))\n        indices_1d = ndarray((J,), (indices_stride,), indices._ptr(base_idx, broadcast=True))\n        out_1d     = ndarray((J,), (out_stride,), out._ptr(base_idx))\n\n        for j in range(J):\n            out_1d._ptr((j,))[0] = a_1d[indices_1d._ptr((j,))[0]]\n\n    return out\n\ndef choose(a, choices, out = None, mode: str = 'raise'):\n    MODE_RAISE: Literal[int] = 0\n    MODE_WRAP : Literal[int] = 1\n    MODE_CLIP : Literal[int] = 2\n\n    a = asarray(a)\n\n    if a.dtype is not int:\n        compile_error(\"first argument must be an integer array\")\n\n    xmode = 0\n    if mode == 'raise':\n        xmode = MODE_RAISE\n    elif mode == 'wrap':\n        xmode = MODE_WRAP\n    elif mode == 'clip':\n        xmode = MODE_CLIP\n    else:\n        raise ValueError(f\"clipmode must be one of 'clip', 'raise', or 'wrap' (got {repr(mode)})\")\n\n    n = len(choices)\n    if n == 0:\n        raise ValueError(\"0-length sequence.\")\n\n    if isinstance(choices, Tuple):\n        xchoices1 = tuple(asarray(c) for c in choices)\n        bshape = broadcast_shapes(*(tuple(c.shape for c in xchoices1) + (a.shape,)))\n        xchoices = tuple(broadcast_to(c, bshape) for c in xchoices1)\n    elif isinstance(choices, List):\n        if not isinstance(choices[0], ndarray):\n            xchoices = [asarray(c) for c in choices]\n        else:\n            xchoices = choices\n        bshape = broadcast_shapes(xchoices[0].shape, a.shape)\n    elif isinstance(choices, ndarray):\n        xchoices = choices\n        bshape = broadcast_shapes(xchoices[0].shape, a.shape)\n    else:\n        compile_error(\"'choices' must be a list, tuple or array\")\n\n    dtype = xchoices[0].dtype\n\n    if out is None:\n        ans = empty(bshape, dtype)\n    else:\n        if not isinstance(out, ndarray):\n            compile_error(\"'out' must be an array\")\n\n        if out.dtype is not dtype:\n            compile_error(\"'out' has incorrect dtype\")\n\n        if static.len(bshape) != static.len(out.shape):\n            compile_error(\"'out' has incorrect number of dimensions\")\n\n        if bshape != out.shape:\n            raise ValueError(\"'out' has incorrect shape\")\n\n        ans = out\n\n    for idx in util.multirange(bshape):\n        mi = a._ptr(idx, broadcast=True)[0]\n\n        if mi < 0 or mi >= n:\n            if xmode == MODE_RAISE:\n                raise ValueError(\"invalid entry in choice array\")\n            elif xmode == MODE_WRAP:\n                if mi < 0:\n                    while mi < 0:\n                        mi += n;\n                else:\n                    while mi >= n:\n                        mi -= n\n            elif xmode == MODE_CLIP:\n                if mi < 0:\n                    mi = 0\n                elif mi >= n:\n                    mi = n - 1\n\n        choice = xchoices[mi]._ptr(idx, broadcast=True)[0]\n        ans._ptr(idx)[0] = choice\n\n    return ans\n\ndef compress(condition, a, axis = None, out = None):\n    condition = asarray(condition)\n    a = asarray(a)\n\n    if static.len(condition.shape) != 1:\n        compile_error(\"condition must be a 1-d array\")\n\n    if axis is None:\n        num_true = 0\n        i = 0\n        n = a.size\n        for c in condition:\n            if c:\n                if i >= n:\n                    raise IndexError(f\"index {i} is out of bounds for axis 0 with size {n}\")\n                num_true += 1\n            i += 1\n\n        if out is None:\n            ans = empty(num_true, a.dtype)\n            ans_cc = True\n        else:\n            if not isinstance(out, ndarray):\n                compile_error(\"'out' must be an array\")\n\n            if static.len(out.shape) != 1:\n                compile_error(\"'out' must be 1-dimensional\")\n\n            if out.size != num_true:\n                raise ValueError(\"'out' has incorrect length\")\n\n            ans = out\n            ans_cc = ans._contig[0]\n\n        a_cc = a._contig[0]\n        k = 0\n        if a_cc and ans_cc:\n            for i in range(min(condition.size, n)):\n                if condition._ptr((i,))[0]:\n                    ans.data[k] = util.cast(a.data[i], ans.dtype)\n                    k += 1\n        else:\n            i = 0\n            for idx in util.multirange(a.shape):\n                if i >= condition.size:\n                    break\n\n                if condition[i]:\n                    ans._ptr((k,))[0] = util.cast(a._ptr(idx)[0], ans.dtype)\n                    k += 1\n\n                i += 1\n\n        return ans\n\n    axis = util.normalize_axis_index(axis, a.ndim)\n    num_true = 0\n    i = 0\n    n = a.shape[axis]\n    for c in condition:\n        if c:\n            if i >= n:\n                raise IndexError(f\"index {i} is out of bounds for axis {axis} with size {n}\")\n            num_true += 1\n        i += 1\n\n    ans_shape = util.tuple_set(a.shape, axis, num_true)\n\n    if out is None:\n        ans = empty(ans_shape, a.dtype)\n    else:\n        if not isinstance(out, ndarray):\n            compile_error(\"'out' must be an array\")\n\n        if static.len(out.shape) != static.len(ans_shape):\n            compile_error(\"'out' has incorrect number of dimensions\")\n\n        if out.shape != ans_shape:\n            raise ValueError(\"'out' has incorrect shape\")\n\n        ans = out\n\n    sub_shape = util.tuple_delete(ans_shape, axis)\n    k = 0\n\n    for i in range(min(condition.size, n)):\n        if condition._ptr((i,))[0]:\n            for idx in util.multirange(sub_shape):\n                idx1 = util.tuple_insert(idx, axis, i)\n                idx2 = util.tuple_insert(idx, axis, k)\n\n                p = a._ptr(idx1)\n                q = ans._ptr(idx2)\n                q[0] = util.cast(p[0], ans.dtype)\n            k += 1\n\n    return ans\n\ndef diagonal(a, offset: int = 0, axis1: int = 0, axis2: int = 1):\n    a = asarray(a)\n    shape = a.shape\n    strides = a.strides\n    ndim: Literal[int] = static.len(shape)\n\n    if ndim < 2:\n        compile_error(\"diag requires an array of at least two dimensions\")\n\n    axis1 = util.normalize_axis_index(axis1, ndim)\n    axis2 = util.normalize_axis_index(axis2, ndim)\n\n    if axis1 == axis2:\n        raise ValueError(\"axis1 and axis2 cannot be the same\")\n\n    if axis1 > axis2:\n        axis1, axis2 = axis2, axis1\n\n    dim1 = shape[axis1]\n    dim2 = shape[axis2]\n    stride1 = strides[axis1]\n    stride2 = strides[axis2]\n    data = a.data\n\n    if offset >= 0:\n        offset_stride = stride2\n        dim2 -= offset\n    else:\n        offset = -offset\n        offset_stride = stride1\n        dim1 -= offset\n\n    diag_size = dim2 if dim2 < dim1 else dim1\n    if diag_size < 0:\n        diag_size = 0\n    else:\n        data = Ptr[a.dtype](data.as_byte() + (offset * offset_stride))\n\n    ret_shape = util.tuple_delete(util.tuple_delete(shape, axis2), axis1) + (diag_size,)\n    ret_strides = util.tuple_delete(util.tuple_delete(strides, axis2), axis1) + (stride1 + stride2,)\n\n    return ndarray(ret_shape, ret_strides, data)\n\ndef select(condlist: List, choicelist: List, default = 0):\n    n = len(condlist)\n    if n != len(choicelist):\n        raise ValueError(\n            \"list of cases must be same length as list of conditions\")\n\n    if n == 0:\n        raise ValueError(\"select with an empty condition list is not possible\")\n\n    condlist = [asarray(cond) for cond in condlist]\n\n    if condlist[0].dtype is not bool:\n        compile_error(\"condlist entries should be boolean ndarray\")\n\n    cond_bshape = condlist[0].shape\n\n    for cond in condlist:\n        cond_bshape = broadcast_shapes(cond_bshape, cond.shape)\n\n    for i in range(n):\n        condlist[i] = broadcast_to(condlist[i], cond_bshape)\n\n    choicelist = [asarray(choice) for choice in choicelist]\n    choice_bshape = choicelist[0].shape\n\n    for choice in choicelist:\n        choice_bshape = broadcast_shapes(choice_bshape, choice.shape)\n\n    for i in range(n):\n        choicelist[i] = broadcast_to(choicelist[i], choice_bshape)\n\n    default = asarray(default)\n    ans_shape = broadcast_shapes(cond_bshape, choice_bshape, default.shape)\n    dtype = type(util.coerce(choicelist[0].dtype, default.dtype))\n    ans = empty(ans_shape, dtype)\n\n    for idx in util.multirange(ans_shape):\n        found = False\n        p = ans._ptr(idx)\n        for i in range(n):\n            if condlist[i]._ptr(idx, broadcast=True)[0]:\n                p[0] = util.cast(choicelist[i]._ptr(idx, broadcast=True)[0], dtype)\n                found = True\n                break\n        if not found:\n            p[0] = util.cast(default._ptr(idx, broadcast=True)[0], dtype)\n\n    return ans\n\ndef place(arr: ndarray, mask, vals):\n    mask = asarray(mask)\n    vals = asarray(vals)\n\n    ni = arr.size\n    nm = mask.size\n    nv = vals.size\n\n    if nm != ni:\n        raise ValueError(\"place: mask and data must be the same size\")\n\n    if nv <= 0:\n        if mask.any():\n            raise ValueError(\"Cannot insert from an empty array!\");\n        return\n\n    cc1, _ = arr._contig\n    cc2, _ = mask._contig\n    cc3, _ = vals._contig\n    j = 0\n\n    if cc1 and cc2 and cc3:\n        for i in range(ni):\n            if mask.data[i]:\n                if j >= nv:\n                    j = 0\n\n                arr.data[i] = util.cast(vals.data[j], arr.dtype)\n                j += 1\n    else:\n        for i in range(ni):\n            if mask._get_flat(i, check=False):\n                if j >= nv:\n                    j = 0\n\n                arr._set_flat(i, vals._get_flat(j, check=False), check=False)\n                j += 1\n\ndef put(a: ndarray, ind, v, mode: str = 'raise'):\n    def fix_index(idx: int, dim: int, mode: str):\n        if mode == 'raise':\n            if idx < 0 or idx >= dim:\n                raise ValueError(\"invalid entry in coordinates array\")\n        elif mode == 'wrap':\n            if idx < 0:\n                while idx < 0:\n                    idx += dim\n            elif idx >= dim:\n                while idx >= dim:\n                    idx -= dim\n        elif mode == 'clip':\n            if idx < 0:\n                idx = 0\n            elif idx >= dim:\n                idx = dim - 1\n\n        return idx\n\n    if mode not in ('raise', 'wrap', 'clip'):\n        raise ValueError(f\"clipmode must be one of 'clip', 'raise', or 'wrap' (got {repr(mode)})\")\n\n    ind = asarray(ind)\n    v = asarray(v)\n\n    na = a.size\n    ni = ind.size\n    nv = v.size\n\n    if ni == 0 or nv == 0:\n        return\n\n    if na == 0 and (mode == 'wrap' or mode == 'clip'):\n        raise ValueError(\"empty array given to put\")\n\n    cc1, _ = a._contig\n    cc2, _ = ind._contig\n    cc3, _ = v._contig\n    j = 0\n\n    if cc1 and cc2 and cc3:\n        for i in range(ni):\n            idx = util.cast(ind.data[i], int)\n            idx = fix_index(idx, na, mode)\n\n            if j >= nv:\n                j = 0\n\n            a.data[idx] = util.cast(v.data[j], a.dtype)\n            j += 1\n    else:\n        for i in range(ni):\n            idx = util.cast(ind._get_flat(i, check=False), int)\n            idx = fix_index(idx, na, mode)\n\n            if j >= nv:\n                j = 0\n\n            a._set_flat(idx, v._get_flat(j, check=False), check=True)\n            j += 1\n\ndef put_along_axis(arr: ndarray, indices, values, axis):\n    def dim_mismatch():\n        compile_error(\"`indices` and `arr` must have the same number of dimensions\")\n\n    indices = asarray(indices)\n    values = asarray(values)\n\n    if indices.dtype is not int:\n        compile_error(\"`indices` must be an integer array\")\n\n    if axis is None:\n        nv = values.size\n        j = 0\n\n        for i in range(indices.size):\n            if j >= nv:\n                j = 0\n            arr._set_flat(indices._get_flat(i, check=False),\n                          values._get_flat(j, check=False),\n                          check=True)\n            j += 1\n\n        return\n\n    bshape = broadcast_shapes(indices.shape, values.shape)\n    indices = broadcast_to(indices, bshape)\n    values = broadcast_to(values, bshape)\n\n    if static.len(arr.shape) != static.len(indices.shape):\n        dim_mismatch()\n\n    axis = util.normalize_axis_index(axis, arr.ndim)\n    M = arr.shape[axis]\n    J = indices.shape[axis]\n\n    bshape = broadcast_shapes(util.tuple_delete(arr.shape, axis),\n                              util.tuple_delete(indices.shape, axis))\n\n    a_stride = arr.strides[axis]\n    indices_stride = indices.strides[axis]\n    values_stride = values.strides[axis]\n\n    for idx in util.multirange(bshape):\n        base_idx   = util.tuple_insert(idx, axis, 0)\n        a_1d       = ndarray((M,), (a_stride,), arr._ptr(base_idx, broadcast=True))\n        indices_1d = ndarray((J,), (indices_stride,), indices._ptr(base_idx, broadcast=True))\n        values_1d  = ndarray((J,), (values_stride,), values._ptr(base_idx, broadcast=True))\n\n        for j in range(J):\n            a_1d[indices_1d._ptr((j,))[0]] = util.cast(values_1d._ptr((j,))[0], arr.dtype)\n\ndef putmask(a: ndarray, mask, values):\n    mask = asarray(mask)\n    values = asarray(values)\n    na = a.size\n    nv = values.size\n\n    if na != mask.size:\n        raise ValueError(\"putmask: mask and data must be the same size\")\n\n    if na == 0 or nv == 0:\n        return\n\n    cc1, _ = a._contig\n    cc2, _ = mask._contig\n    cc3, _ = values._contig\n    j = 0\n\n    if cc1 or cc2 or cc3:\n        for i in range(na):\n            if mask.data[i]:\n                a.data[i] = values.data[j]\n            j += 1\n            if j >= nv:\n                j = 0\n    else:\n        for i in range(na):\n            if mask._get_flat(i, check=False):\n                a._set_flat(i, values._get_flat(j, check=False), check=False)\n            j += 1\n            if j >= nv:\n                j = 0\n\ndef fill_diagonal(a: ndarray, val, wrap: bool = False):\n    if static.len(a.shape) < 2:\n        compile_error(\"array must be at least 2-d\")\n\n    val = asarray(val)\n    nv = val.size\n    end = a.size\n\n    if nv == 0 or end == 0:\n        return\n\n    if static.len(a.shape) == 2:\n        step = a.shape[1] + 1\n        if not wrap:\n            end = min(end, a.shape[1] * a.shape[1])\n    else:\n        for i in range(1, a.ndim):\n            if a.shape[i] != a.shape[0]:\n                raise ValueError(\"All dimensions of input must be of equal length\")\n        step = 1\n        acc = 1\n        for s in a.shape[:-1]:\n            acc *= s\n            step += acc\n\n    j = 0\n    for i in range(0, end, step):\n        a._set_flat(i, val._get_flat(j, check=False), check=False)\n        j += 1\n        if j >= nv:\n            j = 0\n\n########\n# Misc #\n########\n\ndef _power_of_ten(n: int):\n    p10 = (1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8)\n    if n < 9:\n        return Ptr[float](__ptr__(p10).as_byte())[n]\n    else:\n        ret = 1e9\n        while n > 9:\n            ret *= 10.\n            n -= 1\n        return ret\n\ndef _round_int(x: T, decimals: int, T: type):\n    if decimals >= 0:\n        return x\n    else:\n        f = _power_of_ten(-decimals)\n        y = util.cast(x, float)\n        return T(int(util.rint(y / f) * f))\n\ndef _round(x: T, decimals: int, T: type):\n    if isinstance(x, complex) or isinstance(x, complex64):\n        return T(_round(x.real, decimals), _round(x.imag, decimals))\n\n    if (isinstance(x, int) or\n        isinstance(x, Int) or\n        isinstance(x, UInt) or\n        isinstance(x, byte) or\n        isinstance(x, bool)):\n        return _round_int(x, decimals)\n\n    if not (isinstance(x, float) or isinstance(x, float32)):\n        compile_error(\"don't know how to round type '\" + T.__name__ + \"'\")\n\n    if decimals == 0:\n        return util.rint(x)\n    elif decimals > 0:\n        f = T(_power_of_ten(decimals))\n        return util.rint(x * f) / f\n    else:\n        f = T(_power_of_ten(-decimals))\n        return util.rint(x / f) * f\n\ndef round(a, decimals: int = 0, out = None):\n    a = asarray(a)\n\n    if static.len(a.shape) == 0:\n        return _round(a.data[0], decimals)\n\n    cc, fc = a._contig\n    n = a.size\n\n    if out is None:\n        ans = empty_like(a, order=('F' if fc and not cc else 'C'))\n    else:\n        if not isinstance(out, ndarray):\n            compile_error(\"output must be an array\")\n\n        if out.dtype is not a.dtype:\n            compile_error(\"output has wrong type\")\n\n        if not util.tuple_equal(a.shape, out.shape):\n            raise ValueError(\"invalid output shape\")\n\n        ans = out\n\n    cc1, fc1 = ans._contig\n\n    if (cc and cc1) or (fc and fc1):\n        p = a.data\n        q = ans.data\n        for i in range(n):\n            q[i] = _round(p[i], decimals)\n    else:\n        for idx in util.multirange(a.shape):\n            p = a._ptr(idx)\n            q = ans._ptr(idx)\n            q[0] = _round(p[0], decimals)\n\n    return ans\n\naround = round\n\ndef _clip_full(a, a_min, a_max, out):\n    a = asarray(a)\n    a_min = asarray(a_min)\n    a_max = asarray(a_max)\n    bshape = broadcast_shapes(a.shape, a_min.shape, a_max.shape)\n\n    if out is None:\n        ans = empty(bshape, a.dtype)\n    elif isinstance(out, ndarray):\n        if static.len(bshape) != static.len(out.shape):\n            compile_error(\"'out' has incorrect number of dimensions\")\n\n        if bshape != out.shape:\n            raise ValueError(\"'out' has incorrect shape\")\n\n        ans = out\n    else:\n        compile_error(\"'out' must be an array\")\n\n    maxf = lambda a, b: a if a > b else b\n    minf = lambda a, b: a if a < b else b\n\n    for idx in util.multirange(bshape):\n        x = a._ptr(idx, broadcast=True)[0]\n        xmin = a_min._ptr(idx, broadcast=True)[0]\n        xmax = a_max._ptr(idx, broadcast=True)[0]\n\n        x = util.cast(x, ans.dtype)\n        xmin = util.cast(xmin, ans.dtype)\n        xmax = util.cast(xmax, ans.dtype)\n        ans._ptr(idx)[0] = minf(xmax, maxf(x, xmin))\n\n    return ans\n\ndef _clip_min(a, a_min, out):\n    a = asarray(a)\n    a_min = asarray(a_min)\n    bshape = broadcast_shapes(a.shape, a_min.shape)\n\n    if out is None:\n        ans = empty(bshape, a.dtype)\n    elif isinstance(out, ndarray):\n        if static.len(bshape) != static.len(out.shape):\n            compile_error(\"'out' has incorrect number of dimensions\")\n\n        if bshape != out.shape:\n            raise ValueError(\"'out' has incorrect shape\")\n\n        ans = out\n    else:\n        compile_error(\"'out' must be an array\")\n\n    maxf = lambda a, b: a if a > b else b\n\n    for idx in util.multirange(bshape):\n        x = a._ptr(idx, broadcast=True)[0]\n        xmin = a_min._ptr(idx, broadcast=True)[0]\n\n        x = util.cast(x, ans.dtype)\n        xmin = util.cast(xmin, ans.dtype)\n        ans._ptr(idx)[0] = maxf(x, xmin)\n\n    return ans\n\ndef _clip_max(a, a_max, out):\n    a = asarray(a)\n    a_max = asarray(a_max)\n    bshape = broadcast_shapes(a.shape, a_max.shape)\n\n    if out is None:\n        ans = empty(bshape, a.dtype)\n    elif isinstance(out, ndarray):\n        if static.len(bshape) != static.len(out.shape):\n            compile_error(\"'out' has incorrect number of dimensions\")\n\n        if bshape != out.shape:\n            raise ValueError(\"'out' has incorrect shape\")\n\n        ans = out\n    else:\n        compile_error(\"'out' must be an array\")\n\n    minf = lambda a, b: a if a < b else b\n\n    for idx in util.multirange(bshape):\n        x = a._ptr(idx, broadcast=True)[0]\n        xmax = a_max._ptr(idx, broadcast=True)[0]\n\n        x = util.cast(x, ans.dtype)\n        xmax = util.cast(xmax, ans.dtype)\n        ans._ptr(idx)[0] = minf(xmax, x)\n\n    return ans\n\ndef clip(a, a_min, a_max, out = None):\n    if a_min is None and a_max is None:\n        compile_error(\"One of max or min must be given\")\n    elif a_max is None:\n        return _clip_min(a, a_min, out)\n    elif a_min is None:\n        return _clip_max(a, a_max, out)\n    else:\n        return _clip_full(a, a_min, a_max, out)\n\ndef ndenumerate(arr):\n    arr = asarray(arr)\n    for idx in util.multirange(arr.shape):\n        yield (idx, arr._ptr(idx)[0])\n\ndef ndindex(*shape):\n    if static.len(shape) == 1:\n        if isinstance(shape[0], Tuple):\n            return ndindex(*shape[0])\n\n    for s in shape:\n        if s < 0:\n            raise ValueError(\"negative dimensions are not allowed\")\n    return util.multirange(shape)\n\ndef iterable(y):\n    if not hasattr(y, \"__iter__\"):\n        return False\n    elif isinstance(y, ndarray):\n        if static.len(y.shape) == 0:\n            return False\n        else:\n            return True\n    else:\n        return True\n\ndef packbits(a, axis = None, bitorder: str = 'big'):\n    a = asarray(a)\n\n    if (a.dtype is not bool and\n        a.dtype is not int and\n        a.dtype is not byte and\n        not isinstance(a.dtype, Int) and\n        not isinstance(a.dtype, UInt)):\n        compile_error(\"Expected an input array of integer or boolean data type\")\n\n    little_endian = False\n    if bitorder == 'big':\n        little_endian = False\n    elif bitorder == 'little':\n        little_endian = True\n    else:\n        raise ValueError(\"'order' must be either 'little' or 'big'\")\n\n    if axis is None:\n        pack_size = ((a.size - 1) >> 3) + 1\n        ans = empty(pack_size, dtype=u8)\n        m = 0\n        k = 0\n        e = u8(0)\n\n        for idx in util.multirange(a.shape):\n            if m == 8:\n                ans.data[k] = e\n                k += 1\n                e = u8(0)\n                m = 0\n\n            b = u8(1 if a._ptr(idx)[0] else 0)\n            if little_endian:\n                e = (e >> u8(1)) | (b << u8(7))\n            else:\n                e = (e << u8(1)) | b\n            m += 1\n\n        if k < pack_size:\n            if little_endian:\n                e >>= u8(8 - m)\n            else:\n                e <<= u8(8 - m)\n            ans.data[k] = e\n\n        return ans\n\n    if not isinstance(axis, int):\n        compile_error(\"'axis' must be an int or None\")\n\n    axis = util.normalize_axis_index(axis, a.ndim)\n    n = a.shape[axis]\n    pack_size = ((n - 1) >> 3) + 1\n    ans_shape = util.tuple_set(a.shape, axis, pack_size)\n    ans = empty(ans_shape, dtype=u8)\n\n    for idx0 in util.multirange(util.tuple_delete(a.shape, axis)):\n        m = 0\n        k = 0\n        e = u8(0)\n\n        for i in range(n):\n            if m == 8:\n                ans._ptr(util.tuple_insert(idx0, axis, k))[0] = e\n                k += 1\n                e = u8(0)\n                m = 0\n\n            b = u8(1 if a._ptr(util.tuple_insert(idx0, axis, i))[0] else 0)\n            if little_endian:\n                e = (e >> u8(1)) | (b << u8(7))\n            else:\n                e = (e << u8(1)) | b\n            m += 1\n\n        if k < pack_size:\n            if little_endian:\n                e >>= u8(8 - m)\n            else:\n                e <<= u8(8 - m)\n            ans._ptr(util.tuple_insert(idx0, axis, k))[0] = e\n\n    return ans\n\ndef unpackbits(a, axis = None, count = None, bitorder: str = 'big'):\n    a = asarray(a)\n\n    if a.dtype is not u8:\n        compile_error(\"Expected an input array of unsigned byte data type\")\n\n    little_endian = False\n    if bitorder == 'big':\n        little_endian = False\n    elif bitorder == 'little':\n        little_endian = True\n    else:\n        raise ValueError(\"'order' must be either 'little' or 'big'\")\n\n    if axis is None:\n        unpack_size = a.size * 8\n\n        if count is not None:\n            if count < 0:\n                if -count > unpack_size:\n                    raise ValueError(\"-count larger than number of elements\")\n\n                unpack_size += count\n            else:\n                unpack_size = count\n\n        ans = empty(unpack_size, dtype=u8)\n        k = 0\n\n        for idx in util.multirange(a.shape):\n            e = a._ptr(idx)[0]\n\n            for i in range(8):\n                if k >= unpack_size:\n                    break\n                sh = u8(i if little_endian else 7 - i)\n                ans.data[k] = (e & (u8(1) << sh)) >> sh\n                k += 1\n\n            if k >= unpack_size:\n                break\n\n        while k < unpack_size:\n            ans.data[k] = u8(0)\n            k += 1\n\n        return ans\n\n    if not isinstance(axis, int):\n        compile_error(\"'axis' must be an int or None\")\n\n    axis = util.normalize_axis_index(axis, a.ndim)\n    n = a.shape[axis]\n    unpack_size = n * 8\n\n    if count is not None:\n        if count < 0:\n            if -count > unpack_size:\n                raise ValueError(\"-count larger than number of elements\")\n\n            unpack_size += count\n        else:\n            unpack_size = count\n\n    ans_shape = util.tuple_set(a.shape, axis, unpack_size)\n    ans = empty(ans_shape, dtype=u8)\n\n    if unpack_size == 0:\n        return ans\n\n    for idx0 in util.multirange(util.tuple_delete(a.shape, axis)):\n        k = 0\n\n        for m in range(n):\n            e = a._ptr(util.tuple_insert(idx0, axis, m))[0]\n\n            for i in range(8):\n                if k >= unpack_size:\n                    break\n                sh = u8(i if little_endian else 7 - i)\n                ans._ptr(util.tuple_insert(idx0, axis, k))[0] = (e & (u8(1) << sh)) >> sh\n                k += 1\n\n            if k >= unpack_size:\n                break\n\n        while k < unpack_size:\n            ans._ptr(util.tuple_insert(idx0, axis, k))[0] = u8(0)\n            k += 1\n\n    return ans\n\ndef _is_pos_neg_inf(x, pos: bool, out = None):\n    from .ndmath import isinf, signbit\n    x = asarray(x)\n\n    if out is None:\n        ans = empty(x.shape, bool)\n    else:\n        if not isinstance(out, ndarray):\n            compile_error(\"'out' must be an array\")\n\n        if out.ndim != x.ndim:\n            compile_error(\"'out' has incorrect number of dimensions\")\n\n        if x.shape != out.shape:\n            raise ValueError(\"'out' has incorrect shape\")\n\n        ans = out\n\n    for idx in util.multirange(x.shape):\n        e = x._ptr(idx)[0]\n        b = isinf(e)\n        if pos:\n            b = b and not signbit(e)\n        else:\n            b = b and signbit(e)\n        ans._ptr(idx)[0] = util.cast(b, ans.dtype)\n\n    if out is None and ans.ndim == 0:\n        return ans.item()\n    else:\n        return ans\n\ndef isposinf(x, out = None):\n    return _is_pos_neg_inf(x, pos=True, out=out)\n\ndef isneginf(x, out = None):\n    return _is_pos_neg_inf(x, pos=False, out=out)\n\ndef iscomplex(x):\n    x = asarray(x)\n    if x.dtype is complex or x.dtype is complex64:\n        ans = x.map(lambda c: bool(c.imag))\n    else:\n        ans = zeros(x.shape, bool)\n\n    if ans.ndim == 0:\n        return ans.item()\n    else:\n        return ans\n\ndef iscomplexobj(x):\n    if isinstance(x, ndarray):\n        return x.dtype is complex or x.dtype is complex64\n    else:\n        dtype = asarray(x).dtype\n        return dtype is complex or dtype is complex64\n\ndef isreal(x):\n    x = asarray(x)\n    if x.dtype is complex or x.dtype is complex64:\n        ans = x.map(lambda c: not bool(c.imag))\n    else:\n        ans = ones(x.shape, bool)\n\n    if ans.ndim == 0:\n        return ans.item()\n    else:\n        return ans\n\ndef isrealobj(x):\n    return not iscomplexobj(x)\n\ndef isfortran(a: ndarray):\n    cc, fc = a._contig\n    return fc and not cc\n\ndef isscalar(element):\n    T = type(element)\n    return (T is int or\n            T is float or\n            T is complex or\n            T is complex64 or\n            T is bool or\n            T is byte or\n            isinstance(T, Int) or\n            isinstance(T, UInt) or\n            T is str or\n            T is NoneType)\n\ndef _array_get_part(arr: ndarray, imag: Literal[bool]):\n    if arr.dtype is complex:\n        offset = util.sizeof(float) if imag else 0\n        data = Ptr[float](arr.data.as_byte() + offset)\n        return ndarray(arr.shape, arr.strides, data)\n    elif arr.dtype is complex64:\n        offset = util.sizeof(float32) if imag else 0\n        data = Ptr[float32](arr.data.as_byte() + offset)\n        return ndarray(arr.shape, arr.strides, data)\n    else:\n        if imag:\n            n = arr.size\n            data = Ptr[arr.dtype](n)\n            str.memset(data.as_byte(), byte(0), n * arr.itemsize)\n            return ndarray(arr.shape, data)\n        else:\n            return arr\n\ndef real(val):\n    return _array_get_part(asarray(val), imag=False)\n\ndef imag(val):\n    return _array_get_part(asarray(val), imag=True)\n\ndef _real_set(arr: ndarray, val):\n    val = broadcast_to(asarray(val), arr.shape)\n    for idx in util.multirange(arr.shape):\n        x = val._ptr(idx)[0]\n        p = arr._ptr(idx)\n        if arr.dtype is complex:\n            p[0] = complex(util.cast(x, float), p[0].imag)\n        elif arr.dtype is complex64:\n            p[0] = complex64(util.cast(x, float32), p[0].imag)\n        else:\n            p[0] = util.cast(x, arr.dtype)\n\ndef _imag_set(arr: ndarray, val):\n    val = broadcast_to(asarray(val), arr.shape)\n    for idx in util.multirange(arr.shape):\n        x = val._ptr(idx)[0]\n        p = arr._ptr(idx)\n        if arr.dtype is complex:\n            p[0] = complex(p[0].real, util.cast(x, float))\n        elif arr.dtype is complex64:\n            p[0] = complex64(p[0].imag, util.cast(x, float32))\n        else:\n            compile_error(\"array does not have imaginary part to set\")\n\n@extend\nclass ndarray:\n    def take(self, indices, axis = None, out = None, mode: str = 'raise'):\n        return take(self, indices, axis=axis, out=out, mode=mode)\n\n    def squeeze(self, axis = None):\n        return squeeze(self, axis)\n\n    def nonzero(self):\n        return nonzero(self)\n\n    def searchsorted(self, v, side: str = 'left', sorter = None):\n        return searchsorted(self, v=v, side=side, sorter=sorter)\n\n    def repeat(self, repeats, axis = None):\n        return repeat(self, repeats, axis=axis)\n\n    def compress(self, condition, axis = None, out = None):\n        return compress(condition, self, axis=axis, out=out)\n\n    def choose(self, choices, out = None, mode: str = 'raise'):\n        return choose(self, choices, out, mode)\n\n    def diagonal(self, offset: int = 0, axis1: int = 0, axis2: int = 1):\n        return diagonal(self, offset, axis1=axis1, axis2=axis2)\n\n    def put(self, ind, v, mode: str = 'raise'):\n        return put(self, ind, v, mode)\n\n    def round(self, decimals: int = 0, out = None):\n        return round(self, decimals, out=out)\n\n    def clip(self, min = None, max = None, out = None):\n        return clip(self, min, max, out)\n\n    @property\n    def real(self):\n        return real(self)\n\n    @real.setter\n    def real(self, val):\n        _real_set(self, val)\n\n    @property\n    def imag(self):\n        return imag(self)\n\n    @imag.setter\n    def imag(self, val):\n        _imag_set(self, val)\n\n    def conj(self):\n        if hasattr(dtype(), 'conjugate'):\n            return self._op_unary(lambda a: a.conjugate())\n        else:\n            return self\n\n    def conjugate(self):\n        return self.conj()\n\n    @flat.setter\n    def flat(self, val):\n        val = asarray(val)\n        j = 0\n        m = val.size\n\n        if m == 0:\n            return\n\n        for i in range(self.size):\n            p = self._ptr_flat(i, check=False)\n            if j == m:\n                j = 0\n            q = val._ptr_flat(j, check=False)\n            p[0] = util.cast(q[0], dtype)\n            j += 1\n\n# Not supported:\n#   - rollaxis (use is discouraged in NumPy docs)\n#   - asmatrix (matrix class not supported)\n\n############\n# Datetime #\n############\n\n@extend\nclass busdaycalendar:\n\n    @property\n    def weekmask(self):\n        wm = empty(7, bool)\n        for i in range(7):\n            wm.data[i] = self._wm[i]\n        return wm\n\n    @property\n    def holidays(self):\n        hd = empty(self._nholidays, datetime64['D', 1])\n        for i in range(len(hd)):\n            hd.data[i] = self._holidays[i]\n        return hd\n\ndef _get_busdaycal(weekmask=None, holidays=None, busdaycal=None):\n    if busdaycal is None:\n        if weekmask is None:\n            w = \"1111100\"\n        else:\n            w = weekmask\n        return busdaycalendar(weekmask=w, holidays=holidays)\n    else:\n        if weekmask is not None or holidays is not None:\n            compile_error(\"Cannot supply both the weekmask/holidays and the \"\n                          \"busdaycal parameters to busday_offset()\")\n\n        if not isinstance(busdaycal, busdaycalendar):\n            compile_error(\"busdaycal parameter must be a busdaycalendar\")\n\n        return busdaycal\n\ndef busday_offset(dates, offsets, roll: str = \"raise\", weekmask=None,\n                  holidays=None, busdaycal=None, out=None):\n    cal = _get_busdaycal(weekmask=weekmask, holidays=holidays, busdaycal=busdaycal)\n    wm = cal._wm\n    busdays_in_weekmask = wm.count\n\n    dates = asarray(dates, dtype=datetime64['D', 1])\n    offsets = asarray(offsets)\n\n    if offsets.dtype is not int:\n        compile_error(\"offsets parameter must be an array of integers\")\n\n    roll_code = 0\n\n    if roll == \"forward\":\n        roll_code = _BUSDAY_FORWARD\n    elif roll == \"following\":\n        roll_code = _BUSDAY_FOLLOWING\n    elif roll == \"backward\":\n        roll_code = _BUSDAY_BACKWARD\n    elif roll == \"preceding\":\n        roll_code = _BUSDAY_PRECEDING\n    elif roll == \"modifiedfollowing\":\n        roll_code = _BUSDAY_MODIFIEDFOLLOWING\n    elif roll == \"modifiedpreceding\":\n        roll_code = _BUSDAY_MODIFIEDPRECEDING\n    elif roll == \"nat\":\n        roll_code = _BUSDAY_NAT\n    elif roll == \"raise\":\n        roll_code = _BUSDAY_RAISE\n    else:\n        raise ValueError(f\"Invalid business day roll parameter \\\"{roll}\\\"\")\n\n    bshape = broadcast_shapes(dates.shape, offsets.shape)\n\n    if out is not None:\n        if not isinstance(out, ndarray):\n            compile_error(\"'out' must be an array\")\n\n        if out.dtype is not datetime64['D', 1]:\n            compile_error(\"'out' must have dtype datetime64[D]\")\n\n        if out.ndim != static.len(bshape):\n            compile_error(\"'out' has incorrect number of dimensions\")\n\n        if out.shape != bshape:\n            raise ValueError(\"'out' has incorrect shape\")\n\n        ans = out\n    else:\n        ans = empty(bshape, datetime64['D', 1])\n\n    for idx in util.multirange(bshape):\n        d = dates._ptr(idx, broadcast=True)[0]\n        o = offsets._ptr(idx, broadcast=True)[0]\n        b = _apply_busines_day_offset(d, o, roll_code, wm, busdays_in_weekmask,\n                                      cal._holidays, cal._holidays + cal._nholidays)\n        ans._ptr(idx)[0] = b\n\n    if ans.ndim == 0 and out is None:\n        return ans.item()\n    else:\n        return ans\n\ndef busday_count(begindates, enddates, weekmask=None,\n                 holidays=None, busdaycal=None, out=None):\n    cal = _get_busdaycal(weekmask=weekmask, holidays=holidays, busdaycal=busdaycal)\n    wm = cal._wm\n    busdays_in_weekmask = wm.count\n    begindates = asarray(begindates, dtype=datetime64['D', 1])\n    enddates = asarray(enddates, dtype=datetime64['D', 1])\n\n    if out is not None:\n        if not isinstance(out, ndarray):\n            compile_error(\"'out' must be an array\")\n\n        if out.dtype is not int:\n            compile_error(\"'out' must have dtype int\")\n\n        bshape = broadcast_shapes(begindates.shape, enddates.shape, out.shape)\n        ans = out\n    else:\n        bshape = broadcast_shapes(begindates.shape, enddates.shape)\n        ans = empty(bshape, int)\n\n    for idx in util.multirange(bshape):\n        d1 = begindates._ptr(idx, broadcast=True)[0]\n        d2 = enddates._ptr(idx, broadcast=True)[0]\n        c = _apply_busines_day_count(d1, d2, wm, busdays_in_weekmask,\n                                     cal._holidays, cal._holidays + cal._nholidays)\n        ans._ptr(idx, broadcast=(out is not None))[0] = c\n\n    if ans.ndim == 0 and out is None:\n        return ans.item()\n    else:\n        return ans\n\ndef is_busday(dates, weekmask=None, holidays=None, busdaycal=None, out=None):\n    cal = _get_busdaycal(weekmask=weekmask, holidays=holidays, busdaycal=busdaycal)\n    wm = cal._wm\n    busdays_in_weekmask = wm.count\n    dates = asarray(dates, dtype=datetime64['D', 1])\n\n    if out is not None:\n        if not isinstance(out, ndarray):\n            compile_error(\"'out' must be an array\")\n\n        if out.dtype is not bool:\n            compile_error(\"'out' must have dtype bool\")\n\n        if out.ndim != dates.ndim:\n            compile_error(\"'out' has incorrect number of dimensions\")\n\n        if out.shape != dates.shape:\n            raise ValueError(\"'out' has incorrect shape\")\n\n        ans = out\n    else:\n        ans = empty(dates.shape, bool)\n\n    for idx in util.multirange(dates.shape):\n        d = dates._ptr(idx)[0]\n        ans._ptr(idx)[0] = _apply_is_business_day(d, wm, cal._holidays, cal._holidays + cal._nholidays)\n\n    if ans.ndim == 0 and out is None:\n        return ans.item()\n    else:\n        return ans\n\ndef datetime_data(dtype: type):\n    if not isinstance(dtype, datetime64) and not isinstance(dtype, timedelta64):\n        compile_error(\"cannot get datetime metadata from non-datetime type\")\n    return (dtype.base, dtype.num)\n\ndef datetime_as_string(arr, unit=None, timezone: str = \"naive\"):\n    arr = asarray(arr)\n    if not isinstance(arr.dtype, datetime64):\n        compile_error(\"input must have type NumPy datetime\")\n    return arr.map(lambda d: d._as_string(unit=unit, timezone=timezone))\n"
  },
  {
    "path": "stdlib/numpy/sorting.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .ndarray import ndarray\nfrom .routines import array, asarray, empty, zeros\n\nimport util\nimport internal.static as static\n\n##########\n# Common #\n##########\n\ndef _less(a: T, b: T, T: type):\n    if T is float or T is float32:\n        return a < b or (b != b and a == a)\n    elif T is complex or T is complex64:\n        if a.real < b.real:\n            return a.imag == a.imag or b.imag != b.imag\n        elif a.real > b.real:\n            return b.imag != b.imag and a.imag == a.imag\n        elif a.real == b.real or (a.real != a.real and b.real != b.real):\n            return a.imag < b.imag or (b.imag != b.imag and a.imag == a.imag)\n        else:\n            return b.real != b.real\n    else:\n        return a < b\n\n#############\n# Insertion #\n#############\n\ndef insertionsort(start: Ptr[T], n: int, T: type):\n    i = 1\n    while i < n:\n        x = start[i]\n        j = i - 1\n        while j >= 0 and _less(x, start[j]):\n            start[j + 1] = start[j]\n            j -= 1\n        start[j + 1] = x\n        i += 1\n\ndef ainsertionsort(v: Ptr[T], tosort: Ptr[int], n: int, T: type):\n    i = 1\n    while i < n:\n        x = tosort[i]\n        j = i - 1\n        while j >= 0 and _less(v[x], v[tosort[j]]):\n            tosort[j + 1] = tosort[j]\n            j -= 1\n        tosort[j + 1] = x\n        i += 1\n\n########\n# Heap #\n########\n\ndef heapsort(start: Ptr[T], n: int, T: type):\n    a = start - 1\n\n    for l in range(n >> 1, 0, -1):\n        tmp = a[l]\n        i = l\n        j = l << 1\n        while j <= n:\n            if j < n and _less(a[j], a[j + 1]):\n                j += 1\n            if _less(tmp, a[j]):\n                a[i] = a[j]\n                i = j\n                j += j\n            else:\n                break\n        a[i] = tmp\n\n    while n > 1:\n        tmp = a[n]\n        a[n] = a[1]\n        n -= 1\n        i = 1\n        j = 2\n        while j <= n:\n            if j < n and _less(a[j], a[j + 1]):\n                j += 1\n            if _less(tmp, a[j]):\n                a[i] = a[j]\n                i = j\n                j += j\n            else:\n                break\n        a[i] = tmp\n\ndef aheapsort(vv: Ptr[T], tosort: Ptr[int], n: int, T: type):\n    v = vv\n    a = tosort - 1\n\n    for l in range(n >> 1, 0, -1):\n        tmp = a[l]\n        i = l\n        j = l << 1\n        while j <= n:\n            if j < n and _less(v[a[j]], v[a[j + 1]]):\n                j += 1\n            if _less(v[tmp], v[a[j]]):\n                a[i] = a[j]\n                i = j\n                j += j\n            else:\n                break\n        a[i] = tmp\n\n    while n > 1:\n        tmp = a[n]\n        a[n] = a[1]\n        n -= 1\n        i = 1\n        j = 2\n        while j <= n:\n            if j < n and _less(v[a[j]], v[a[j + 1]]):\n                j += 1\n            if _less(v[tmp], v[a[j]]):\n                a[i] = a[j]\n                i = j\n                j += j\n            else:\n                break\n        a[i] = tmp\n\n#########\n# Merge #\n#########\n\nPYA_QS_STACK   : Literal[int] = 100\nSMALL_QUICKSORT: Literal[int] = 15\nSMALL_MERGESORT: Literal[int] = 20\nSMALL_STRING   : Literal[int] = 16\n\ndef _mergesort0(pl: Ptr[T], pr: Ptr[T], pw: Ptr[T], T: type):\n    if pr - pl > SMALL_MERGESORT:\n        pm = pl + ((pr - pl) >> 1)\n        _mergesort0(pl, pm, pw)\n        _mergesort0(pm, pr, pw)\n\n        pi = pw\n        pj = pl\n        while pj < pm:\n            pi[0] = pj[0]\n            pi += 1\n            pj += 1\n\n        pi = pw + (pm - pl)\n        pj = pw\n        pk = pl\n\n        while pj < pi and pm < pr:\n            if _less(pm[0], pj[0]):\n                pk[0] = pm[0]\n                pk += 1\n                pm += 1\n            else:\n                pk[0] = pj[0]\n                pk += 1\n                pj += 1\n\n        while pj < pi:\n            pk[0] = pj[0]\n            pk += 1\n            pj += 1\n    else:\n        pi = pl + 1\n        while pi < pr:\n            vp = pi[0]\n            pj = pi\n            pk = pi - 1\n            while pj > pl and _less(vp, pk[0]):\n                pj[0] = pk[0]\n                pj -= 1\n                pk -= 1\n            pj[0] = vp\n            pi += 1\n\ndef mergesort(start: Ptr[T], num: int, T: type):\n    pl = start\n    pr = pl + num\n    pw = Ptr[T](num // 2)\n    _mergesort0(pl, pr, pw)\n    util.free(pw)\n\ndef _amergesort0(pl: Ptr[int], pr: Ptr[int], v: Ptr[T], pw: Ptr[int], T: type):\n    if pr - pl > SMALL_MERGESORT:\n        pm = pl + ((pr - pl) >> 1)\n        _amergesort0(pl, pm, v, pw)\n        _amergesort0(pm, pr, v, pw)\n\n        pi = pw\n        pj = pl\n        while pj < pm:\n            pi[0] = pj[0]\n            pi += 1\n            pj += 1\n\n        pi = pw + (pm - pl)\n        pj = pw\n        pk = pl\n\n        while pj < pi and pm < pr:\n            if _less(v[pm[0]], v[pj[0]]):\n                pk[0] = pm[0]\n                pk += 1\n                pm += 1\n            else:\n                pk[0] = pj[0]\n                pk += 1\n                pj += 1\n\n        while pj < pi:\n            pk[0] = pj[0]\n            pk += 1\n            pj += 1\n    else:\n        pi = pl + 1\n        while pi < pr:\n            vi = pi[0]\n            vp = v[vi]\n            pj = pi\n            pk = pi - 1\n            while pj > pl and _less(vp, v[pk[0]]):\n                pj[0] = pk[0]\n                pj -= 1\n                pk -= 1\n            pj[0] = vi\n            pi += 1\n\ndef amergesort(v: Ptr[T], tosort: Ptr[int], num: int, T: type):\n    pl = tosort\n    pr = pl + num\n    pw = Ptr[int](num // 2)\n    _amergesort0(pl, pr, v, pw)\n    util.free(pw)\n\n###############\n# Quick (PDQ) #\n###############\n\nINSERTION_SORT_THRESHOLD    : Literal[int] = 24\nNINTHER_THRESHOLD           : Literal[int] = 128\nPARTIAL_INSERTION_SORT_LIMIT: Literal[int] = 8\n\ndef _floor_log2(n: int):\n    log = 0\n    while True:\n        n >>= 1\n        if n == 0:\n            break\n        log += 1\n    return log\n\ndef _partial_insertion_sort(arr: Ptr[T], begin: int, end: int, T: type):\n    if begin == end:\n        return True\n\n    limit = 0\n    cur = begin + 1\n    while cur != end:\n        if limit > PARTIAL_INSERTION_SORT_LIMIT:\n            return False\n\n        sift = cur\n        sift_1 = cur - 1\n\n        if _less(arr[sift], arr[sift_1]):\n            tmp = arr[sift]\n\n            while True:\n                arr[sift] = arr[sift_1]\n                sift -= 1\n                sift_1 -= 1\n                if sift == begin or not _less(tmp, arr[sift_1]):\n                    break\n\n            arr[sift] = tmp\n            limit += cur - sift\n\n        cur += 1\n\n    return True\n\ndef _partition_left(arr: Ptr[T], begin: int, end: int, T: type):\n    pivot = arr[begin]\n    first = begin\n    last = end\n\n    while True:\n        last -= 1\n        if not _less(pivot, arr[last]):\n            break\n\n    if last + 1 == end:\n        while first < last:\n            first += 1\n            if _less(pivot, arr[first]):\n                break\n    else:\n        while True:\n            first += 1\n            if _less(pivot, arr[first]):\n                break\n\n    while first < last:\n        arr[first], arr[last] = arr[last], arr[first]\n        while True:\n            last -= 1\n            if not _less(pivot, arr[last]):\n                break\n        while True:\n            first += 1\n            if _less(pivot, arr[first]):\n                break\n\n    pivot_pos = last\n    arr[begin] = arr[pivot_pos]\n    arr[pivot_pos] = pivot\n\n    return pivot_pos\n\ndef _partition_right(arr: Ptr[T], begin: int, end: int, T: type):\n    pivot = arr[begin]\n    first = begin\n    last = end\n\n    while True:\n        first += 1\n        if not _less(arr[first], pivot):\n            break\n\n    if first - 1 == begin:\n        while first < last:\n            last -= 1\n            if _less(arr[last], pivot):\n                break\n    else:\n        while True:\n            last -= 1\n            if _less(arr[last], pivot):\n                break\n\n    already_partitioned = 0\n    if first >= last:\n        already_partitioned = 1\n\n    while first < last:\n        arr[first], arr[last] = arr[last], arr[first]\n\n        while True:\n            first += 1\n            if not _less(arr[first], pivot):\n                break\n\n        while True:\n            last -= 1\n            if _less(arr[last], pivot):\n                break\n\n    pivot_pos = first - 1\n    arr[begin] = arr[pivot_pos]\n    arr[pivot_pos] = pivot\n\n    return (pivot_pos, already_partitioned)\n\ndef _sort2(arr: Ptr[T], i: int, j: int, T: type):\n    if _less(arr[j], arr[i]):\n        arr[i], arr[j] = arr[j], arr[i]\n\ndef _sort3(arr: Ptr[T], i: int, j: int, k: int, T: type):\n    _sort2(arr, i, j)\n    _sort2(arr, j, k)\n    _sort2(arr, i, j)\n\ndef _pdq_sort(\n    arr: Ptr[T],\n    begin: int,\n    end: int,\n    bad_allowed: int,\n    leftmost: bool,\n    T: type\n):\n    while True:\n        size = end - begin\n        if size < INSERTION_SORT_THRESHOLD:\n            insertionsort(arr + begin, size)\n            return\n\n        size_2 = size // 2\n        if size > NINTHER_THRESHOLD:\n            _sort3(arr, begin, begin + size_2, end - 1)\n            _sort3(arr, begin + 1, begin + (size_2 - 1), end - 2)\n            _sort3(arr, begin + 2, begin + (size_2 + 1), end - 3)\n            _sort3(\n                arr, begin + (size_2 - 1), begin + size_2, begin + (size_2 + 1)\n            )\n            arr[begin], arr[begin + size_2] = arr[begin + size_2], arr[begin]\n        else:\n            _sort3(arr, begin + size_2, begin, end - 1)\n\n        if not leftmost and not _less(arr[begin - 1], arr[begin]):\n            begin = _partition_left(arr, begin, end) + 1\n            continue\n\n        part_result = _partition_right(arr, begin, end)\n        pivot_pos = part_result[0]\n        already_partitioned = part_result[1] == 1\n\n        l_size = pivot_pos - begin\n        r_size = end - (pivot_pos + 1)\n        highly_unbalanced = (l_size < (size // 8)) or (r_size < (size // 8))\n\n        if highly_unbalanced:\n            bad_allowed -= 1\n            if bad_allowed == 0:\n                heapsort(arr + begin, end - begin)\n                return\n\n            if l_size >= INSERTION_SORT_THRESHOLD:\n                arr[begin], arr[begin + l_size // 4] = (\n                    arr[begin + l_size // 4],\n                    arr[begin],\n                )\n                arr[pivot_pos - 1], arr[pivot_pos - l_size // 4] = (\n                    arr[pivot_pos - l_size // 4],\n                    arr[pivot_pos - 1],\n                )\n\n                if l_size > NINTHER_THRESHOLD:\n                    arr[begin + 1], arr[begin + (l_size // 4 + 1)] = (\n                        arr[begin + (l_size // 4 + 1)],\n                        arr[begin + 1],\n                    )\n                    arr[begin + 2], arr[begin + (l_size // 4 + 2)] = (\n                        arr[begin + (l_size // 4 + 2)],\n                        arr[begin + 2],\n                    )\n                    arr[pivot_pos - 2], arr[pivot_pos - (l_size // 4 + 1)] = (\n                        arr[pivot_pos - (l_size // 4 + 1)],\n                        arr[pivot_pos - 2],\n                    )\n                    arr[pivot_pos - 3], arr[pivot_pos - (l_size // 4 + 2)] = (\n                        arr[pivot_pos - (l_size // 4 + 2)],\n                        arr[pivot_pos - 3],\n                    )\n\n            if r_size >= INSERTION_SORT_THRESHOLD:\n                arr[pivot_pos + 1], arr[pivot_pos + (1 + r_size // 4)] = (\n                    arr[pivot_pos + (1 + r_size // 4)],\n                    arr[pivot_pos + 1],\n                )\n                arr[end - 1], arr[end - r_size // 4] = (\n                    arr[end - r_size // 4],\n                    arr[end - 1],\n                )\n\n                if r_size > NINTHER_THRESHOLD:\n                    arr[pivot_pos + 2], arr[pivot_pos + (2 + r_size // 4)] = (\n                        arr[pivot_pos + (2 + r_size // 4)],\n                        arr[pivot_pos + 2],\n                    )\n                    arr[pivot_pos + 3], arr[pivot_pos + (3 + r_size // 4)] = (\n                        arr[pivot_pos + (3 + r_size // 4)],\n                        arr[pivot_pos + 3],\n                    )\n                    arr[end - 2], arr[end - (1 + r_size // 4)] = (\n                        arr[end - (1 + r_size // 4)],\n                        arr[end - 2],\n                    )\n                    arr[end - 3], arr[end - (2 + r_size // 4)] = (\n                        arr[end - (2 + r_size // 4)],\n                        arr[end - 3],\n                    )\n        else:\n            if (\n                already_partitioned\n                and _partial_insertion_sort(arr, begin, pivot_pos)\n                and _partial_insertion_sort(arr, pivot_pos + 1, end)\n            ):\n                return\n\n        _pdq_sort(arr, begin, pivot_pos, bad_allowed, leftmost)\n        begin = pivot_pos + 1\n        leftmost = False\n\n# C stubs for vectorized quicksort from Highway\nfrom C import cnp_sort_int16(cobj, int)\nfrom C import cnp_sort_uint16(cobj, int)\nfrom C import cnp_sort_int32(cobj, int)\nfrom C import cnp_sort_uint32(cobj, int)\nfrom C import cnp_sort_int64(cobj, int)\nfrom C import cnp_sort_uint64(cobj, int)\nfrom C import cnp_sort_uint128(cobj, int)\nfrom C import cnp_sort_float32(cobj, int)\nfrom C import cnp_sort_float64(cobj, int)\n\ndef quicksort(start: Ptr[T], n: int, T: type):\n    if T is int:\n        cnp_sort_int64(start.as_byte(), n)\n    elif T is i16:\n        cnp_sort_int16(start.as_byte(), n)\n    elif T is u16:\n        cnp_sort_uint16(start.as_byte(), n)\n    elif T is i32:\n        cnp_sort_int32(start.as_byte(), n)\n    elif T is u32:\n        cnp_sort_uint32(start.as_byte(), n)\n    elif T is i64:\n        cnp_sort_int64(start.as_byte(), n)\n    elif T is u64:\n        cnp_sort_uint64(start.as_byte(), n)\n    elif T is float32:\n        cnp_sort_float32(start.as_byte(), n)\n    elif T is float:\n        cnp_sort_float64(start.as_byte(), n)\n    else:\n        _pdq_sort(start, 0, n, _floor_log2(n), True)\n\ndef _apartial_insertion_sort(arr: Ptr[T], tosort: Ptr[int], begin: int, end: int, T: type):\n    if begin == end:\n        return True\n\n    limit = 0\n    cur = begin + 1\n    while cur != end:\n        if limit > PARTIAL_INSERTION_SORT_LIMIT:\n            return False\n\n        sift = cur\n        sift_1 = cur - 1\n\n        if _less(arr[tosort[sift]], arr[tosort[sift_1]]):\n            itmp = tosort[sift]\n            tmp = arr[itmp]\n\n            while True:\n                tosort[sift] = tosort[sift_1]\n                sift -= 1\n                sift_1 -= 1\n                if sift == begin or not _less(tmp, arr[tosort[sift_1]]):\n                    break\n\n            tosort[sift] = itmp\n            limit += cur - sift\n\n        cur += 1\n\n    return True\n\ndef _apartition_left(arr: Ptr[T], tosort: Ptr[int], begin: int, end: int, T: type):\n    ipivot = tosort[begin]\n    pivot = arr[ipivot]\n    first = begin\n    last = end\n\n    while True:\n        last -= 1\n        if not _less(pivot, arr[tosort[last]]):\n            break\n\n    if last + 1 == end:\n        while first < last:\n            first += 1\n            if _less(pivot, arr[tosort[first]]):\n                break\n    else:\n        while True:\n            first += 1\n            if _less(pivot, arr[tosort[first]]):\n                break\n\n    while first < last:\n        tosort[first], tosort[last] = tosort[last], tosort[first]\n        while True:\n            last -= 1\n            if not _less(pivot, arr[tosort[last]]):\n                break\n        while True:\n            first += 1\n            if _less(pivot, arr[tosort[first]]):\n                break\n\n    pivot_pos = last\n    tosort[begin] = tosort[pivot_pos]\n    tosort[pivot_pos] = ipivot\n\n    return pivot_pos\n\ndef _apartition_right(arr: Ptr[T], tosort: Ptr[int], begin: int, end: int, T: type):\n    ipivot = tosort[begin]\n    pivot = arr[ipivot]\n    first = begin\n    last = end\n\n    while True:\n        first += 1\n        if not _less(arr[tosort[first]], pivot):\n            break\n\n    if first - 1 == begin:\n        while first < last:\n            last -= 1\n            if _less(arr[tosort[last]], pivot):\n                break\n    else:\n        while True:\n            last -= 1\n            if _less(arr[tosort[last]], pivot):\n                break\n\n    already_partitioned = 0\n    if first >= last:\n        already_partitioned = 1\n\n    while first < last:\n        tosort[first], tosort[last] = tosort[last], tosort[first]\n\n        while True:\n            first += 1\n            if not _less(arr[tosort[first]], pivot):\n                break\n\n        while True:\n            last -= 1\n            if _less(arr[tosort[last]], pivot):\n                break\n\n    pivot_pos = first - 1\n    tosort[begin] = tosort[pivot_pos]\n    tosort[pivot_pos] = ipivot\n\n    return (pivot_pos, already_partitioned)\n\ndef _asort2(arr: Ptr[T], tosort: Ptr[int], i: int, j: int, T: type):\n    if _less(arr[tosort[j]], arr[tosort[i]]):\n        tosort[i], tosort[j] = tosort[j], tosort[i]\n\ndef _asort3(arr: Ptr[T], tosort: Ptr[int], i: int, j: int, k: int, T: type):\n    _asort2(arr, tosort, i, j)\n    _asort2(arr, tosort, j, k)\n    _asort2(arr, tosort, i, j)\n\ndef _apdq_sort(\n    arr: Ptr[T],\n    tosort: Ptr[int],\n    begin: int,\n    end: int,\n    bad_allowed: int,\n    leftmost: bool,\n    T: type\n):\n    while True:\n        size = end - begin\n        if size < INSERTION_SORT_THRESHOLD:\n            ainsertionsort(arr, tosort + begin, size)\n            return\n\n        size_2 = size // 2\n        if size > NINTHER_THRESHOLD:\n            _asort3(arr, tosort, begin, begin + size_2, end - 1)\n            _asort3(arr, tosort, begin + 1, begin + (size_2 - 1), end - 2)\n            _asort3(arr, tosort, begin + 2, begin + (size_2 + 1), end - 3)\n            _asort3(\n                arr, tosort, begin + (size_2 - 1), begin + size_2, begin + (size_2 + 1)\n            )\n            tosort[begin], tosort[begin + size_2] = tosort[begin + size_2], tosort[begin]\n        else:\n            _asort3(arr, tosort, begin + size_2, begin, end - 1)\n\n        if not leftmost and not _less(arr[tosort[begin - 1]], arr[tosort[begin]]):\n            begin = _apartition_left(arr, tosort, begin, end) + 1\n            continue\n\n        part_result = _apartition_right(arr, tosort, begin, end)\n        pivot_pos = part_result[0]\n        already_partitioned = part_result[1] == 1\n\n        l_size = pivot_pos - begin\n        r_size = end - (pivot_pos + 1)\n        highly_unbalanced = (l_size < (size // 8)) or (r_size < (size // 8))\n\n        if highly_unbalanced:\n            bad_allowed -= 1\n            if bad_allowed == 0:\n                aheapsort(arr, tosort + begin, end - begin)\n                return\n\n            if l_size >= INSERTION_SORT_THRESHOLD:\n                tosort[begin], tosort[begin + l_size // 4] = (\n                    tosort[begin + l_size // 4],\n                    tosort[begin],\n                )\n                tosort[pivot_pos - 1], tosort[pivot_pos - l_size // 4] = (\n                    tosort[pivot_pos - l_size // 4],\n                    tosort[pivot_pos - 1],\n                )\n\n                if l_size > NINTHER_THRESHOLD:\n                    tosort[begin + 1], tosort[begin + (l_size // 4 + 1)] = (\n                        tosort[begin + (l_size // 4 + 1)],\n                        tosort[begin + 1],\n                    )\n                    tosort[begin + 2], tosort[begin + (l_size // 4 + 2)] = (\n                        tosort[begin + (l_size // 4 + 2)],\n                        tosort[begin + 2],\n                    )\n                    tosort[pivot_pos - 2], tosort[pivot_pos - (l_size // 4 + 1)] = (\n                        tosort[pivot_pos - (l_size // 4 + 1)],\n                        tosort[pivot_pos - 2],\n                    )\n                    tosort[pivot_pos - 3], tosort[pivot_pos - (l_size // 4 + 2)] = (\n                        tosort[pivot_pos - (l_size // 4 + 2)],\n                        tosort[pivot_pos - 3],\n                    )\n\n            if r_size >= INSERTION_SORT_THRESHOLD:\n                tosort[pivot_pos + 1], tosort[pivot_pos + (1 + r_size // 4)] = (\n                    tosort[pivot_pos + (1 + r_size // 4)],\n                    tosort[pivot_pos + 1],\n                )\n                tosort[end - 1], tosort[end - r_size // 4] = (\n                    tosort[end - r_size // 4],\n                    tosort[end - 1],\n                )\n\n                if r_size > NINTHER_THRESHOLD:\n                    tosort[pivot_pos + 2], tosort[pivot_pos + (2 + r_size // 4)] = (\n                        tosort[pivot_pos + (2 + r_size // 4)],\n                        tosort[pivot_pos + 2],\n                    )\n                    tosort[pivot_pos + 3], tosort[pivot_pos + (3 + r_size // 4)] = (\n                        tosort[pivot_pos + (3 + r_size // 4)],\n                        tosort[pivot_pos + 3],\n                    )\n                    tosort[end - 2], tosort[end - (1 + r_size // 4)] = (\n                        tosort[end - (1 + r_size // 4)],\n                        tosort[end - 2],\n                    )\n                    tosort[end - 3], tosort[end - (2 + r_size // 4)] = (\n                        tosort[end - (2 + r_size // 4)],\n                        tosort[end - 3],\n                    )\n        else:\n            if (\n                already_partitioned\n                and _apartial_insertion_sort(arr, tosort, begin, pivot_pos)\n                and _apartial_insertion_sort(arr, tosort, pivot_pos + 1, end)\n            ):\n                return\n\n        _apdq_sort(arr, tosort, begin, pivot_pos, bad_allowed, leftmost)\n        begin = pivot_pos + 1\n        leftmost = False\n\ndef aquicksort(start: Ptr[T], tosort: Ptr[int], n: int, T: type):\n    _apdq_sort(start, tosort, 0, n, _floor_log2(n), True)\n\n#########\n# Radix #\n#########\n\ndef key_of(x: UT, T: type, UT: type):\n    if T is UT:\n        return x\n    else:\n        return x ^ (util.cast(1, UT) << util.cast(util.sizeof(UT) * 8 - 1, UT))\n\ndef nth_byte(key: T, l: int, T: type):\n    return int(key >> util.cast(l << 3, T)) & 0xFF\n\ndef _radixsort0(start: Ptr[UT], aux: Ptr[UT], num: int, T: type, UT: type):\n    m = util.sizeof(UT)\n    n = (1 << 8)\n    ncnt = m * n\n    cnt = Ptr[int](ncnt)  # note: compiler should put this on the stack\n    str.memset(cnt.as_byte(), byte(0), ncnt * util.sizeof(int))\n    key0 = key_of(start[0], T=T, UT=UT)\n\n    for i in range(num):\n        k = key_of(start[i], T=T, UT=UT)\n        for l in range(m):\n            cnt[l*n + nth_byte(k, l)] += 1\n\n    ncols = 0\n    cols = Ptr[u8](m)  # again, compiler should put on stack\n    for l in range(m):\n        if cnt[l*n + nth_byte(key0, l)] != num:\n            cols[ncols] = u8(l)\n            ncols += 1\n\n    for l in range(ncols):\n        a = 0\n        for i in range(256):\n            b = cnt[int(cols[l])*n + i]\n            cnt[int(cols[l])*n + i] = a\n            a += b\n\n    for l in range(ncols):\n        for i in range(num):\n            k = key_of(start[i], T=T, UT=UT)\n            q = cnt + (int(cols[l])*n + nth_byte(k, int(cols[l])))\n            dst = q[0]\n            q[0] += 1\n            aux[dst] = start[i]\n\n        temp = aux\n        aux = start\n        start = temp\n\n    return start\n\ndef _radixsort(start: Ptr[UT], num: int, T: type, UT: type):\n    if num < 2:\n        return\n\n    all_sorted = True\n    k1 = key_of(start[0], T=T, UT=UT)\n    for i in range(1, num):\n        k2 = key_of(start[i], T=T, UT=UT)\n        if k1 > k2:\n            all_sorted = False\n            break\n        k1 = k2\n\n    if all_sorted:\n        return\n\n    aux = Ptr[UT](num)\n    sort = _radixsort0(start, aux, num, T=T)\n    if sort != start:\n        str.memcpy(start.as_byte(), sort.as_byte(), num * util.sizeof(UT))\n\n    util.free(aux)\n\ndef radixsort(start: Ptr[T], num: int, T: type):\n    def wrap(start: Ptr, num: int, T: type, UT: type):\n        _radixsort(Ptr[UT](start.as_byte()), num, T=T, UT=UT)\n\n    if T is int:\n        wrap(start, num, int, u64)\n    elif isinstance(T, Int):\n        wrap(start, num, T, UInt[T.N])\n    elif isinstance(T, UInt):\n        wrap(start, num, T, T)\n    elif isinstance(T, byte):\n        wrap(start, num, i8, u8)\n    else:\n        raise ValueError(\"cannot radixsort type '\" + T.__name__ + \"'\")\n\ndef _aradixsort0(start: Ptr[UT], aux: Ptr[int], tosort: Ptr[int], num: int, T: type, UT: type):\n    m = util.sizeof(UT)\n    n = (1 << 8)\n    ncnt = m * n\n    cnt = Ptr[int](ncnt)  # note: compiler should put this on the stack\n    str.memset(cnt.as_byte(), byte(0), ncnt * util.sizeof(int))\n    key0 = key_of(start[0], T=T, UT=UT)\n\n    for i in range(num):\n        k = key_of(start[i], T=T, UT=UT)\n        for l in range(m):\n            cnt[l*n + nth_byte(k, l)] += 1\n\n    ncols = 0\n    cols = Ptr[u8](m)  # again, compiler should put on stack\n    for l in range(m):\n        if cnt[l*n + nth_byte(key0, l)] != num:\n            cols[ncols] = u8(l)\n            ncols += 1\n\n    for l in range(ncols):\n        a = 0\n        for i in range(256):\n            b = cnt[int(cols[l])*n + i]\n            cnt[int(cols[l])*n + i] = a\n            a += b\n\n    for l in range(ncols):\n        for i in range(num):\n            k = key_of(start[tosort[i]], T=T, UT=UT)\n            q = cnt + (int(cols[l])*n + nth_byte(k, int(cols[l])))\n            dst = q[0]\n            q[0] += 1\n            aux[dst] = tosort[i]\n\n        temp = aux\n        aux = tosort\n        tosort = temp\n\n    return tosort\n\ndef _aradixsort(start: Ptr[UT], tosort: Ptr[int], num: int, T: type, UT: type):\n    if num < 2:\n        return\n\n    all_sorted = True\n    k1 = key_of(start[tosort[0]], T=T, UT=UT)\n    for i in range(1, num):\n        k2 = key_of(start[tosort[i]], T=T, UT=UT)\n        if k1 > k2:\n            all_sorted = False\n            break\n        k1 = k2\n\n    if all_sorted:\n        return\n\n    aux = Ptr[int](num)\n    sort = _aradixsort0(start, aux, tosort, num, T=T)\n    if sort != tosort:\n        str.memcpy(tosort.as_byte(), sort.as_byte(), num * util.sizeof(int))\n\n    util.free(aux)\n\ndef aradixsort(start: Ptr[T], tosort: Ptr[int], num: int, T: type):\n    def wrap(start: Ptr, tosort: Ptr[int], num: int, T: type, UT: type):\n        _aradixsort(Ptr[UT](start.as_byte()), tosort, num, T=T, UT=UT)\n\n    if T is int:\n        wrap(start, tosort, num, int, u64)\n    elif isinstance(T, Int):\n        wrap(start, tosort, num, T, UInt[T.N])\n    elif isinstance(T, UInt):\n        wrap(start, tosort, num, T, T)\n    elif isinstance(T, byte):\n        wrap(start, tosort, num, i8, u8)\n    else:\n        raise ValueError(\"cannot radixsort type '\" + T.__name__ + \"'\")\n\n#######\n# Tim #\n#######\n\nTIMSORT_STACK_SIZE: Literal[int] = 128\n\ndef _compute_min_run(num: int):\n    r = 0\n\n    while 64 < num:\n        r |= num & 1\n        num >>= 1\n\n    return num + r\n\n# run    = 2-tuple of (start, length)\n# buffer = 2-tuple of (pw, size)\n\nclass Buffer[T]:\n    pw: Ptr[T]\n    size: int\n\n    def __init__(self):\n        self.pw = Ptr[T]()\n        self.size = 0\n\n    def resize(self, new_size: int):\n        buffer_pw = self.pw\n        buffer_size = self.size\n\n        if new_size <= buffer_size:\n            return\n        elif not buffer_pw:\n            self.pw = Ptr[T](new_size)\n        else:\n            self.pw = util.realloc(buffer_pw, new_size, buffer_size)\n\n        self.size = new_size\n\n    def free(self):\n        if self.pw:\n            util.free(self.pw)\n            self.pw = Ptr[T]()\n\ndef _count_run(arr: Ptr[T], l: int, num: int, minrun: int, T: type):\n    if num - l == 1:\n        return 1\n\n    pl = arr + l\n\n    if not _less(pl[1], pl[0]):\n        pi = pl + 1\n        while pi < arr + (num - 1) and not _less(pi[1], pi[0]):\n            pi += 1\n    else:\n        pi = pl + 1\n        while pi < arr + (num - 1) and _less(pi[1], pi[0]):\n            pi += 1\n\n        pj = pl\n        pr = pi\n        while pj < pr:\n            pj[0], pr[0] = pr[0], pj[0]\n            pj += 1\n            pr -= 1\n\n    pi += 1\n    sz = pi - pl\n\n    if sz < minrun:\n        if l + minrun < num:\n            sz = minrun\n        else:\n            sz = num - l\n\n        pr = pl + sz\n\n        while pi < pr:\n            vc = pi[0]\n            pj = pi\n\n            while pl < pj and _less(vc, pj[-1]):\n                pj[0] = pj[-1]\n                pj -= 1\n\n            pj[0] = vc\n            pi += 1\n\n    return sz\n\ndef _merge_left(p1: Ptr[T], l1: int, p2: Ptr[T], l2: int, p3: Ptr[T], T: type):\n    end = p2 + l2\n    str.memcpy(p3.as_byte(), p1.as_byte(), l1 * util.sizeof(T))\n    p1[0] = p2[0]\n    p1 += 1\n    p2 += 1\n\n    while p1 < p2 and p2 < end:\n        if _less(p2[0], p3[0]):\n            p1[0] = p2[0]\n            p1 += 1\n            p2 += 1\n        else:\n            p1[0] = p3[0]\n            p1 += 1\n            p3 += 1\n\n    if p1 != p2:\n        str.memcpy(p1.as_byte(), p3.as_byte(), (p2 - p1) * util.sizeof(T))\n\ndef _merge_right(p1: Ptr[T], l1: int, p2: Ptr[T], l2: int, p3: Ptr[T], T: type):\n    start = p1 - 1\n    str.memcpy(p3.as_byte(), p2.as_byte(), l2 * util.sizeof(T))\n    p1 += l1 - 1\n    p2 += l2 - 1\n    p3 += l2 - 1\n    p2[0] = p1[0]\n    p2 -= 1\n    p1 -= 1\n\n    while p1 < p2 and start < p1:\n        if _less(p3[0], p1[0]):\n            p2[0] = p1[0]\n            p2 -= 1\n            p1 -= 1\n        else:\n            p2[0] = p3[0]\n            p2 -= 1\n            p3 -= 1\n\n    if p1 != p2:\n        ofs = p2 - start\n        str.memcpy((start + 1).as_byte(), (p3 - ofs + 1).as_byte(), ofs * util.sizeof(T))\n\ndef _gallop_right(arr: Ptr[T], size: int, key: T, T: type):\n    if _less(key, arr[0]):\n        return 0\n\n    last_ofs = 0\n    ofs = 1\n\n    while True:\n        if size <= ofs or ofs < 0:\n            ofs = size\n            break\n\n        if _less(key, arr[ofs]):\n            break\n        else:\n            last_ofs = ofs\n            ofs = (ofs << 1) + 1\n\n    while last_ofs + 1 < ofs:\n        m = last_ofs + ((ofs - last_ofs) >> 1)\n\n        if _less(key, arr[m]):\n            ofs = m\n        else:\n            last_ofs = m\n\n    return ofs\n\ndef _gallop_left(arr: Ptr[T], size: int, key: T, T: type):\n    if _less(arr[size - 1], key):\n        return size\n\n    last_ofs = 0\n    ofs = 1\n\n    while True:\n        if size <= ofs or ofs < 0:\n            ofs = size\n            break\n\n        if _less(arr[size - ofs - 1], key):\n            break\n        else:\n            last_ofs = ofs\n            ofs = (ofs << 1) + 1\n\n    l = size - ofs - 1\n    r = size - last_ofs - 1\n\n    while l + 1 < r:\n        m = l + ((r - l) >> 1)\n\n        if _less(arr[m], key):\n            l = m\n        else:\n            r = m\n\n    return r\n\ndef _merge_at(arr: Ptr[T],\n              stack: Ptr[Tuple[int,int]],\n              at: int,\n              buffer: Buffer[T],\n              T: type):\n    s1, l1 = stack[at]\n    s2, l2 = stack[at + 1]\n    k = _gallop_right(arr + s1, l1, arr[s2])\n\n    if l1 == k:\n        return\n\n    p1 = arr + (s1 + k)\n    l1 -= k\n    p2 = arr + s2\n    l2 = _gallop_left(arr + s2, l2, arr[s2 - 1])\n\n    if l2 < l1:\n        buffer.resize(l2)\n        _merge_right(p1, l1, p2, l2, buffer.pw)\n    else:\n        buffer.resize(l1)\n        _merge_left(p1, l1, p2, l2, buffer.pw)\n\ndef _try_collapse(arr: Ptr[T],\n                  stack: Ptr[Tuple[int,int]],\n                  stack_ptr: Ptr[int],\n                  buffer: Buffer[T],\n                  T: type):\n    top = stack_ptr[0]\n\n    while 1 < top:\n        B = stack[top - 2][1]\n        C = stack[top - 1][1]\n\n        if ((2 < top and stack[top - 3][1] <= B + C) or\n            (3 < top and stack[top - 4][1] <= stack[top - 3][1] + B)):\n            A = stack[top - 3][1]\n\n            if A <= C:\n                _merge_at(arr, stack, top - 3, buffer)\n                s, l = stack[top - 3]\n                stack[top - 3] = (s, l + B)\n                stack[top - 2] = stack[top - 1]\n                top -= 1\n            else:\n                _merge_at(arr, stack, top - 2, buffer)\n                s, l = stack[top - 2]\n                stack[top - 2] = (s, l + C)\n                top -= 1\n        elif 1 < top and B <= C:\n            _merge_at(arr, stack, top - 2, buffer)\n            s, l = stack[top - 2]\n            stack[top - 2] = (s, l + C)\n            top -= 1\n        else:\n            break\n\n    stack_ptr[0] = top\n\ndef _force_collapse(arr: Ptr[T],\n                    stack: Ptr[Tuple[int,int]],\n                    stack_ptr: Ptr[int],\n                    buffer: Buffer[T],\n                    T: type):\n    top = stack_ptr[0]\n\n    while 2 < top:\n        if stack[top - 3][1] <= stack[top - 1][1]:\n            _merge_at(arr, stack, top - 3, buffer)\n            _, l1 = stack[top - 2]\n            s, l2 = stack[top - 3]\n            stack[top - 3] = (s, l1 + l2)\n            stack[top - 2] = stack[top - 1]\n            top -= 1\n        else:\n            _merge_at(arr, stack, top - 2, buffer)\n            _, l1 = stack[top - 1]\n            s, l2 = stack[top - 2]\n            stack[top - 2] = (s, l1 + l2)\n            top -= 1\n\n    if 1 < top:\n        _merge_at(arr, stack, top - 2, buffer)\n\ndef timsort(start: Ptr[T], num: int, T: type):\n    stack = __array__[Tuple[int,int]](TIMSORT_STACK_SIZE)\n    buffer = Buffer[T]()\n    stack_ptr = 0\n    minrun = _compute_min_run(num)\n\n    l = 0\n    while l < num:\n        n = _count_run(start, l, num, minrun)\n        stack[stack_ptr] = (l, n)\n        stack_ptr += 1\n        _try_collapse(start, stack.ptr, __ptr__(stack_ptr), buffer)\n        l += n\n\n    _force_collapse(start, stack.ptr, __ptr__(stack_ptr), buffer)\n    buffer.free()\n\ndef _acount_run(arr: Ptr[T], tosort: Ptr[int], l: int, num: int, minrun: int, T: type):\n    if num - l == 1:\n        return 1\n\n    pl = tosort + l\n\n    if not _less(arr[pl[1]], arr[pl[0]]):\n        pi = pl + 1\n        while pi < tosort + (num - 1) and not _less(arr[pi[1]], arr[pi[0]]):\n            pi += 1\n    else:\n        pi = pl + 1\n        while pi < tosort + (num - 1) and _less(arr[pi[1]], arr[pi[0]]):\n            pi += 1\n\n        pj = pl\n        pr = pi\n        while pj < pr:\n            pj[0], pr[0] = pr[0], pj[0]\n            pj += 1\n            pr -= 1\n\n    pi += 1\n    sz = pi - pl\n\n    if sz < minrun:\n        if l + minrun < num:\n            sz = minrun\n        else:\n            sz = num - l\n\n        pr = pl + sz\n\n        while pi < pr:\n            vi = pi[0]\n            vc = arr[vi]\n            pj = pi\n\n            while pl < pj and _less(vc, arr[pj[-1]]):\n                pj[0] = pj[-1]\n                pj -= 1\n\n            pj[0] = vi\n            pi += 1\n\n    return sz\n\ndef _amerge_left(arr: Ptr[T], p1: Ptr[int], l1: int, p2: Ptr[int], l2: int, p3: Ptr[int], T: type):\n    end = p2 + l2\n    str.memcpy(p3.as_byte(), p1.as_byte(), l1 * util.sizeof(int))\n    p1[0] = p2[0]\n    p1 += 1\n    p2 += 1\n\n    while p1 < p2 and p2 < end:\n        if _less(arr[p2[0]], arr[p3[0]]):\n            p1[0] = p2[0]\n            p1 += 1\n            p2 += 1\n        else:\n            p1[0] = p3[0]\n            p1 += 1\n            p3 += 1\n\n    if p1 != p2:\n        str.memcpy(p1.as_byte(), p3.as_byte(), (p2 - p1) * util.sizeof(int))\n\ndef _amerge_right(arr: Ptr[T], p1: Ptr[int], l1: int, p2: Ptr[int], l2: int, p3: Ptr[int], T: type):\n    start = p1 - 1\n    str.memcpy(p3.as_byte(), p2.as_byte(), l2 * util.sizeof(int))\n    p1 += l1 - 1\n    p2 += l2 - 1\n    p3 += l2 - 1\n    p2[0] = p1[0]\n    p2 -= 1\n    p1 -= 1\n\n    while p1 < p2 and start < p1:\n        if _less(arr[p3[0]], arr[p1[0]]):\n            p2[0] = p1[0]\n            p2 -= 1\n            p1 -= 1\n        else:\n            p2[0] = p3[0]\n            p2 -= 1\n            p3 -= 1\n\n    if p1 != p2:\n        ofs = p2 - start\n        str.memcpy((start + 1).as_byte(), (p3 - ofs + 1).as_byte(), ofs * util.sizeof(int))\n\ndef _agallop_right(arr: Ptr[T], tosort: Ptr[int], size: int, key: T, T: type):\n    if _less(key, arr[tosort[0]]):\n        return 0\n\n    last_ofs = 0\n    ofs = 1\n\n    while True:\n        if size <= ofs or ofs < 0:\n            ofs = size\n            break\n\n        if _less(key, arr[tosort[ofs]]):\n            break\n        else:\n            last_ofs = ofs\n            ofs = (ofs << 1) + 1\n\n    while last_ofs + 1 < ofs:\n        m = last_ofs + ((ofs - last_ofs) >> 1)\n\n        if _less(key, arr[tosort[m]]):\n            ofs = m\n        else:\n            last_ofs = m\n\n    return ofs\n\ndef _agallop_left(arr: Ptr[T], tosort: Ptr[int], size: int, key: T, T: type):\n    if _less(arr[tosort[size - 1]], key):\n        return size\n\n    last_ofs = 0\n    ofs = 1\n\n    while True:\n        if size <= ofs or ofs < 0:\n            ofs = size\n            break\n\n        if _less(arr[tosort[size - ofs - 1]], key):\n            break\n        else:\n            last_ofs = ofs\n            ofs = (ofs << 1) + 1\n\n    l = size - ofs - 1\n    r = size - last_ofs - 1\n\n    while l + 1 < r:\n        m = l + ((r - l) >> 1)\n\n        if _less(arr[tosort[m]], key):\n            l = m\n        else:\n            r = m\n\n    return r\n\ndef _amerge_at(arr: Ptr[T],\n               tosort: Ptr[int],\n               stack: Ptr[Tuple[int,int]],\n               at: int,\n               buffer: Buffer[int],\n               T: type):\n    s1, l1 = stack[at]\n    s2, l2 = stack[at + 1]\n    k = _agallop_right(arr, tosort + s1, l1, arr[tosort[s2]])\n\n    if l1 == k:\n        return\n\n    p1 = tosort + (s1 + k)\n    l1 -= k\n    p2 = tosort + s2\n    l2 = _agallop_left(arr, tosort + s2, l2, arr[tosort[s2 - 1]])\n\n    if l2 < l1:\n        buffer.resize(l2)\n        _amerge_right(arr, p1, l1, p2, l2, buffer.pw)\n    else:\n        buffer.resize(l1)\n        _amerge_left(arr, p1, l1, p2, l2, buffer.pw)\n\ndef _atry_collapse(arr: Ptr[T],\n                   tosort: Ptr[int],\n                   stack: Ptr[Tuple[int,int]],\n                   stack_ptr: Ptr[int],\n                   buffer: Buffer[int],\n                   T: type):\n    top = stack_ptr[0]\n\n    while 1 < top:\n        B = stack[top - 2][1]\n        C = stack[top - 1][1]\n\n        if ((2 < top and stack[top - 3][1] <= B + C) or\n            (3 < top and stack[top - 4][1] <= stack[top - 3][1] + B)):\n            A = stack[top - 3][1]\n\n            if A <= C:\n                _amerge_at(arr, tosort, stack, top - 3, buffer)\n                s, l = stack[top - 3]\n                stack[top - 3] = (s, l + B)\n                stack[top - 2] = stack[top - 1]\n                top -= 1\n            else:\n                _amerge_at(arr, tosort, stack, top - 2, buffer)\n                s, l = stack[top - 2]\n                stack[top - 2] = (s, l + C)\n                top -= 1\n        elif 1 < top and B <= C:\n            _amerge_at(arr, tosort, stack, top - 2, buffer)\n            s, l = stack[top - 2]\n            stack[top - 2] = (s, l + C)\n            top -= 1\n        else:\n            break\n\n    stack_ptr[0] = top\n\ndef _aforce_collapse(arr: Ptr[T],\n                     tosort: Ptr[int],\n                     stack: Ptr[Tuple[int,int]],\n                     stack_ptr: Ptr[int],\n                     buffer: Buffer[int],\n                     T: type):\n    top = stack_ptr[0]\n\n    while 2 < top:\n        if stack[top - 3][1] <= stack[top - 1][1]:\n            _amerge_at(arr, tosort, stack, top - 3, buffer)\n            _, l1 = stack[top - 2]\n            s, l2 = stack[top - 3]\n            stack[top - 3] = (s, l1 + l2)\n            stack[top - 2] = stack[top - 1]\n            top -= 1\n        else:\n            _amerge_at(arr, tosort, stack, top - 2, buffer)\n            _, l1 = stack[top - 1]\n            s, l2 = stack[top - 2]\n            stack[top - 2] = (s, l1 + l2)\n            top -= 1\n\n    if 1 < top:\n        _amerge_at(arr, tosort, stack, top - 2, buffer)\n\ndef atimsort(start: Ptr[T], tosort: Ptr[int], num: int, T: type):\n    stack = __array__[Tuple[int,int]](TIMSORT_STACK_SIZE)\n    buffer = Buffer[int]()\n    stack_ptr = 0\n    minrun = _compute_min_run(num)\n\n    l = 0\n    while l < num:\n        n = _acount_run(start, tosort, l, num, minrun)\n        stack[stack_ptr] = (l, n)\n        stack_ptr += 1\n        _atry_collapse(start, tosort, stack.ptr, __ptr__(stack_ptr), buffer)\n        l += n\n\n    _aforce_collapse(start, tosort, stack.ptr, __ptr__(stack_ptr), buffer)\n    buffer.free()\n\n##########\n# Stable #\n##########\n\ndef stablesort(start: Ptr[T], num: int, T: type):\n    if T is int or T is byte or isinstance(T, Int) or isinstance(T, UInt):\n        return radixsort(start, num)\n    else:\n        return timsort(start, num)\n\ndef astablesort(start: Ptr[T], tosort: Ptr[int], num: int, T: type):\n    if T is int or T is byte or isinstance(T, Int) or isinstance(T, UInt):\n        return aradixsort(start, tosort, num)\n    else:\n        return atimsort(start, tosort, num)\n\n#############\n# Selection #\n#############\n\nMAX_PIVOT_STACK: Literal[int] = 50\n\n@tuple\nclass Sortee:\n    v: Ptr[T]\n    T: type\n\n    def __call__(self, i: int):\n        return self.v[i]\n\n    def swap(self, i: int, j: int):\n        v = self.v\n        tmp = v[i]\n        v[i] = v[j]\n        v[j] = tmp\n\n@tuple\nclass Idx:\n    def __call__(self, i: int):\n        return i\n\n@tuple\nclass ArgSortee:\n    tosort: Ptr[int]\n\n    def __call__(self, i: int):\n        return self.tosort[i]\n\n    def swap(self, i: int, j: int):\n        v = self.tosort\n        tmp = v[i]\n        v[i] = v[j]\n        v[j] = tmp\n\n@tuple\nclass ArgIdx:\n    tosort: Ptr[int]\n\n    def __call__(self, i: int):\n        return self.tosort[i]\n\ndef _store_pivot(pivot: int, kth: int, pivots: Ptr[int], npiv: Ptr[int]):\n    if not pivots:\n        return\n\n    if pivot == kth and npiv[0] == MAX_PIVOT_STACK:\n        pivots[npiv[0] - 1] = pivot\n    elif pivot >= kth and npiv[0] < MAX_PIVOT_STACK:\n        pivots[npiv[0]] = pivot\n        npiv[0] += 1\n\ndef _median3_swap(v: Ptr[T], tosort: Ptr[int], low: int, mid: int, high: int, arg: Literal[bool], T: type):\n    if arg:\n        idx = ArgIdx(tosort)\n        sortee = ArgSortee(tosort)\n    else:\n        idx = Idx()\n        sortee = Sortee(v)\n\n    if _less(v[idx(high)], v[idx(mid)]):\n        sortee.swap(high, mid)\n\n    if _less(v[idx(high)], v[idx(low)]):\n        sortee.swap(high, low)\n\n    if _less(v[idx(low)], v[idx(mid)]):\n        sortee.swap(low, mid)\n\n    sortee.swap(mid, low + 1)\n\ndef _median5(v: Ptr[T], tosort: Ptr[int], arg: Literal[bool], T: type):\n    if arg:\n        idx = ArgIdx(tosort)\n        sortee = ArgSortee(tosort)\n    else:\n        idx = Idx()\n        sortee = Sortee(v)\n\n    if _less(v[idx(1)], v[idx(0)]):\n        sortee.swap(1, 0)\n\n    if _less(v[idx(4)], v[idx(3)]):\n        sortee.swap(4, 3)\n\n    if _less(v[idx(3)], v[idx(0)]):\n        sortee.swap(3, 0)\n\n    if _less(v[idx(4)], v[idx(1)]):\n        sortee.swap(4, 1)\n\n    if _less(v[idx(2)], v[idx(1)]):\n        sortee.swap(2, 1)\n\n    if _less(v[idx(3)], v[idx(2)]):\n        if _less(v[idx(3)], v[idx(1)]):\n            return 1\n        else:\n            return 3\n    else:\n        return 2\n\ndef _unguarded_partition(v: Ptr[T], tosort: Ptr[int], pivot: T, ll: int, hh: int, arg: Literal[bool], T: type):\n    if arg:\n        idx = ArgIdx(tosort)\n        sortee = ArgSortee(tosort)\n    else:\n        idx = Idx()\n        sortee = Sortee(v)\n\n    while True:\n        while True:\n            ll += 1\n            if not _less(v[idx(ll)], pivot):\n                break\n\n        while True:\n            hh -= 1\n            if not _less(pivot, v[idx(hh)]):\n                break\n\n        if hh < ll:\n            break\n\n        sortee.swap(ll, hh)\n\n    return ll, hh\n\ndef _median_of_median5(v: Ptr[T], tosort: Ptr[int], num: int, pivots: Ptr[int], npiv: Ptr[int], arg: Literal[bool], isel, T: type):\n    if arg:\n        idx = ArgIdx(tosort)\n        sortee = ArgSortee(tosort)\n    else:\n        idx = Idx()\n        sortee = Sortee(v)\n\n    right = num - 1\n    nmed = (right + 1) // 5\n    subleft = 0\n\n    for i in range(nmed):\n        m = _median5(v + (0 if arg else subleft), tosort + (subleft if arg else 0), arg)\n        sortee.swap(subleft + m, i)\n        subleft += 5\n\n    if nmed > 2:\n        isel(v, tosort, nmed, nmed // 2, pivots, npiv, arg)\n\n    return nmed // 2\n\ndef _dumb_select(v: Ptr[T], tosort: Ptr[int], num: int, kth: int, arg: Literal[bool], T: type):\n    if arg:\n        idx = ArgIdx(tosort)\n        sortee = ArgSortee(tosort)\n    else:\n        idx = Idx()\n        sortee = Sortee(v)\n\n    for i in range(kth + 1):\n        minidx = i\n        minval = v[idx(i)]\n        for k in range(i + 1, num):\n            if _less(v[idx(k)], minval):\n                minidx = k\n                minval = v[idx(k)]\n        sortee.swap(i, minidx)\n\ndef _msb(unum: u64):\n    depth_limit = 0\n    while unum >> u64(1):\n        depth_limit += 1\n        unum >>= u64(1)\n    return depth_limit\n\ndef _introselect(v: Ptr[T], tosort: Ptr[int], num: int, kth: int, pivots: Ptr[int], npiv: Ptr[int], arg: Literal[bool], T: type):\n    if arg:\n        idx = ArgIdx(tosort)\n        sortee = ArgSortee(tosort)\n    else:\n        idx = Idx()\n        sortee = Sortee(v)\n\n    low = 0\n    high = num - 1\n\n    if not npiv:\n        pivots = Ptr[int]()\n\n    while pivots and npiv[0] > 0:\n        if pivots[npiv[0] - 1] > kth:\n            high = pivots[npiv[0] - 1] - 1\n            break\n        elif pivots[npiv[0] - 1] == kth:\n            return\n\n        low = pivots[npiv[0] - 1] + 1\n        npiv[0] -= 1\n\n    if kth - low < 3:\n        _dumb_select(v + (0 if arg else low), tosort + (low if arg else 0), high - low + 1, kth - low, arg)\n        _store_pivot(kth, kth, pivots, npiv)\n        return\n    elif (T is float or T is float32) and kth == num - 1:\n        maxidx = low\n        maxval = v[idx(low)]\n        for k in range(low + 1, num):\n            if not _less(v[idx(k)], maxval):\n                maxidx = k\n                maxval = v[idx(k)]\n        sortee.swap(kth, maxidx)\n        return\n\n    depth_limit = _msb(u64(num)) * 2\n\n    while low + 1 < high:\n        ll = low + 1\n        hh = high\n\n        if depth_limit > 0 or hh - ll < 5:\n            mid = low + (high - low) // 2\n            _median3_swap(v, tosort, low, mid, high, arg)\n        else:\n            mid = ll + _median_of_median5(v + (0 if arg else ll), tosort + (ll if arg else 0), hh - ll, Ptr[int](), Ptr[int](), arg, _introselect)\n            sortee.swap(mid, low)\n            ll -= 1\n            hh += 1\n\n        depth_limit -= 1\n\n        ll, hh = _unguarded_partition(v, tosort, v[idx(low)], ll, hh, arg)\n\n        sortee.swap(low, hh)\n\n        if hh != kth:\n            _store_pivot(hh, kth, pivots, npiv)\n\n        if hh >= kth:\n            high = hh - 1\n\n        if hh <= kth:\n            low = ll\n\n    if high == low + 1:\n        if _less(v[idx(high)], v[idx(low)]):\n            sortee.swap(high, low)\n\n    _store_pivot(kth, kth, pivots, npiv)\n\ndef _partition(v: Ptr[T], num: int, kth: int, pivots: Ptr[int], npiv: Ptr[int], T: type):\n    _introselect(v, Ptr[int](), num, kth, pivots, npiv, arg=False)\n\ndef _argpartition(v: Ptr[T], tosort: Ptr[int], num: int, kth: int, pivots: Ptr[int], npiv: Ptr[int], T: type):\n    _introselect(v, tosort, num, kth, pivots, npiv, arg=True)\n\ndef _partition_compact(a: ndarray, kth: Ptr[int], nkth: int, axis: int):\n    n = a.shape[axis]\n    pivots = __array__[int](MAX_PIVOT_STACK)\n\n    for idx in util.multirange(util.tuple_delete(a.shape, axis)):\n        idx1 = util.tuple_insert(idx, axis, 0)\n        p = a._ptr(idx1)\n        npiv = 0\n\n        for i in range(nkth):\n            _partition(p, n, kth[i], pivots.ptr, __ptr__(npiv))\n\ndef _partition_buffered(a: ndarray, kth: Ptr[int], nkth: int, axis: int):\n    n = a.shape[axis]\n    st = a.strides[axis]\n    buf = Ptr[a.dtype](n)\n    pivots = __array__[int](MAX_PIVOT_STACK)\n\n    for idx in util.multirange(util.tuple_delete(a.shape, axis)):\n        npiv = 0\n        idx1 = util.tuple_insert(idx, axis, 0)\n        p = a._ptr(idx1).as_byte()\n        p0 = p\n\n        for i in range(n):\n            buf[i] = (Ptr[a.dtype](p))[0]\n            p += st\n\n        for i in range(nkth):\n            _partition(buf, n, kth[i], pivots.ptr, __ptr__(npiv))\n\n        p = p0\n        for i in range(n):\n            Ptr[a.dtype](p)[0] = buf[i]\n            p += st\n\n    util.free(buf)\n\ndef _fix_kth(kth, n: int):\n    kth = asarray(kth, order='C')\n\n    if kth.dtype is not int:\n        compile_error(\"Partition index must be integer\")\n\n    if kth.ndim > 1:\n        compile_error(\"kth array must have dimension <= 1\")\n\n    pkth = kth.data\n    nkth = kth.size\n\n    for i in range(nkth):\n        if pkth[i] < 0:\n            pkth[i] += n\n\n        if pkth[i] < 0 or pkth[i] >= n:\n            raise ValueError(f\"kth(={pkth[i]}) out of bounds ({n})\")\n\n    if kth.ndim == 1:\n        kth.sort()\n\n    return pkth, nkth\n\ndef _partition_helper(a: ndarray, kth, axis, kind: str, force_compact: Literal[bool] = False):\n    if axis is None:\n        _partition_helper(a.flatten(), kth=kth, axis=-1, kind=kind, force_compact=force_compact)\n        return\n\n    if kind != 'introselect':\n        raise ValueError(f\"select kind must be 'introselect' (got {repr(kind)})\")\n\n    axis = util.normalize_axis_index(axis, a.ndim)\n    n = a.shape[axis]\n    pkth, nkth = _fix_kth(kth, n)\n\n    if force_compact:\n        _partition_compact(a, pkth, nkth, axis)\n    else:\n        if a.strides[axis] == a.itemsize:\n            _partition_compact(a, pkth, nkth, axis)\n        else:\n            _partition_buffered(a, pkth, nkth, axis)\n\ndef partition(a, kth, axis = -1, kind: str = 'introselect'):\n    if isinstance(a, ndarray):\n        b = a.copy(order='C')\n    else:\n        b = asarray(a)\n\n    _partition_helper(b, kth=kth, axis=axis, kind=kind, force_compact=(b.ndim == 1))\n    return b\n\ndef argpartition(a: ndarray, kth, axis = -1, kind: str = 'introselect'):\n    if axis is None:\n        return _argpartition(a.flatten(), kth=kth, axis=-1, kind=kind)\n\n    if kind != 'introselect':\n        raise ValueError(f\"select kind must be 'introselect' (got {repr(kind)})\")\n\n    axis = util.normalize_axis_index(axis, a.ndim)\n    n = a.shape[axis]\n    pkth, nkth = _fix_kth(kth, n)\n    b = empty(a.shape, int)\n    a_stride = a.strides[axis]\n    b_stride = b.strides[axis]\n    a_compact = (a_stride == a.itemsize)\n    b_compact = (b_stride == b.itemsize)\n    a_buf = Ptr[a.dtype]() if a_compact else Ptr[a.dtype](n)\n    b_buf = Ptr[b.dtype]() if b_compact else Ptr[b.dtype](n)\n    pivots = __array__[int](MAX_PIVOT_STACK)\n\n    for idx in util.multirange(util.tuple_delete(a.shape, axis)):\n        npiv = 0\n        idx1 = util.tuple_insert(idx, axis, 0)\n        pa = a._ptr(idx1)\n        pb = b._ptr(idx1)\n        qa = pa.as_byte()\n        qb = pb.as_byte()\n\n        if not a_compact:\n            for i in range(n):\n                a_buf[i] = (Ptr[a.dtype](qa))[0]\n                qa += a_stride\n\n        if not b_compact:\n            for i in range(n):\n                b_buf[i] = i\n        else:\n            for i in range(n):\n                pb[i] = i\n\n        start = pa if a_compact else a_buf\n        tosort = pb if b_compact else b_buf\n\n        for i in range(nkth):\n            _argpartition(start, tosort, n, pkth[i], pivots.ptr, __ptr__(npiv))\n\n        if not a_compact:\n            qa = pa.as_byte()\n            for i in range(n):\n                Ptr[a.dtype](qa)[0] = a_buf[i]\n                qa += a_stride\n\n        if not b_compact:\n            qb = pb.as_byte()\n            for i in range(n):\n                Ptr[b.dtype](qb)[0] = b_buf[i]\n                qb += b_stride\n\n    if not a_compact:\n        util.free(a_buf)\n\n    if not b_compact:\n        util.free(b_buf)\n\n    return b\n\n###########\n# Lexsort #\n###########\n\ndef _lexsort(sort_keys, n: int, axis: int):\n    sort_keys0 = asarray(sort_keys[0])\n    nd: Literal[int] = sort_keys0.ndim\n\n    if nd == 0 and (axis == 0 or axis == -1):\n        pass\n    else:\n        axis = util.normalize_axis_index(axis, nd)\n\n    if nd == 0:\n        return 0\n\n    if sort_keys0.size <= 1:\n        return zeros(sort_keys[0].shape, int)\n\n    ret = empty(sort_keys0.shape, int)\n    rstride = ret.strides[axis]\n    maxelsize = sort_keys0.itemsize\n    needcopy = (rstride != ret.itemsize)\n    N = sort_keys0.shape[axis]\n\n    for j in range(n):\n        key = sort_keys[j]\n        needcopy |= (key.strides[axis] != key.itemsize)\n        maxelsize = max(maxelsize, key.itemsize)\n\n    valbuffer = cobj()\n    indbuffer = Ptr[int]()\n\n    if needcopy:\n        valbufsize = N * maxelsize\n        if valbufsize == 0:\n            valbufsize = 1\n        valbuffer = cobj(valbufsize)\n        indbufsize = N * ret.itemsize\n        indbuffer = Ptr[int](indbufsize)\n\n        for idx in util.multirange(util.tuple_delete(ret.shape, axis)):\n            idx1 = util.tuple_insert(idx, axis, 0)\n            q = ret._ptr(idx1).as_byte()\n\n            for i in range(N):\n                indbuffer[i] = i\n\n            for j in range(n):\n                key = sort_keys[j]\n                buf = Ptr[key.dtype](valbuffer.as_byte())\n                p = key._ptr(idx1).as_byte()\n                s = key.strides[axis]\n\n                for i in range(N):\n                    buf[i] = (Ptr[key.dtype](p))[0]\n                    p += s\n\n                astablesort(buf, indbuffer, N)\n\n            for i in range(N):\n                z = Ptr[int](q)\n                z[0] = indbuffer[i]\n                q += rstride\n    else:\n        for idx in util.multirange(util.tuple_delete(ret.shape, axis)):\n            idx1 = util.tuple_insert(idx, axis, 0)\n            q = ret._ptr(idx1)\n\n            for i in range(N):\n                q[i] = i\n\n            for j in range(n):\n                p = sort_keys[j]._ptr(idx1)\n                astablesort(p, q, N)\n\n    if valbuffer:\n        util.free(valbuffer)\n\n    if indbuffer:\n        util.free(indbuffer)\n\n    return ret\n\ndef lexsort(keys, axis: int = -1):\n    n = len(keys)\n\n    if n == 0:\n        raise ValueError(\"need sequence of keys with len > 0 in lexsort\")\n\n    if isinstance(keys, Tuple):\n        sort_keys = tuple(asarray(key) for key in keys)\n        for i in static.range(1, static.len(sort_keys)):\n            if sort_keys[i].ndim != sort_keys[0].ndim:\n                compile_error(\"all keys need to be the same shape\")\n            if sort_keys[i].shape != sort_keys[0].shape:\n                raise ValueError(\"all keys need to be the same shape\")\n        return _lexsort(sort_keys, n, axis)\n    elif isinstance(keys, List):\n        if isinstance(keys[0], ndarray):\n            sort_keys = keys\n        else:\n            sort_keys = [asarray(key) for key in keys]\n        for i in range(1, n):\n            if sort_keys[i].shape != sort_keys[0].shape:\n                raise ValueError(\"all keys need to be the same shape\")\n        return _lexsort(sort_keys, n, axis)\n    elif isinstance(keys, ndarray):\n        return _lexsort(keys, n, axis)\n    else:\n        compile_error(\"keys must be an ndarray or a tuple\")\n\n#######\n# API #\n#######\n\ndef _sort_compact(a: ndarray, axis: int, sorter):\n    n = a.shape[axis]\n\n    for idx in util.multirange(util.tuple_delete(a.shape, axis)):\n        idx1 = util.tuple_insert(idx, axis, 0)\n        p = a._ptr(idx1)\n        sorter(p, n)\n\ndef _sort_buffered(a: ndarray, axis: int, sorter):\n    n = a.shape[axis]\n    st = a.strides[axis]\n    buf = Ptr[a.dtype](n)\n\n    for idx in util.multirange(util.tuple_delete(a.shape, axis)):\n        idx1 = util.tuple_insert(idx, axis, 0)\n        p = a._ptr(idx1).as_byte()\n        p0 = p\n\n        for i in range(n):\n            buf[i] = (Ptr[a.dtype](p))[0]\n            p += st\n\n        sorter(buf, n)\n\n        p = p0\n        for i in range(n):\n            Ptr[a.dtype](p)[0] = buf[i]\n            p += st\n\n    util.free(buf)\n\ndef _sort_dispatch(a: ndarray, axis: int, sorter, force_compact: Literal[bool] = False):\n    axis = util.normalize_axis_index(axis, a.ndim)\n\n    if force_compact:\n        _sort_compact(a, axis, sorter)\n    else:\n        if a.strides[axis] == a.itemsize:\n            _sort_compact(a, axis, sorter)\n        else:\n            _sort_buffered(a, axis, sorter)\n\ndef _sort(a: ndarray, axis: int, kind: Optional[str], force_compact: Literal[bool] = False):\n    if kind is None or kind == 'quicksort' or kind == 'quick':\n        _sort_dispatch(a, axis, quicksort, force_compact)\n    elif kind == 'mergesort' or kind == 'merge' or kind == 'stable':\n        _sort_dispatch(a, axis, stablesort, force_compact)\n    elif kind == 'heapsort' or kind == 'heap':\n        _sort_dispatch(a, axis, heapsort, force_compact)\n    else:\n        raise ValueError(f\"sort kind must be one of 'quick', 'heap', or 'stable' (got {repr(kind)})\")\n\ndef sort(a, axis = -1, kind: Optional[str] = None):\n    if axis is None:\n        return sort(asarray(a).flatten(), axis=-1, kind=kind)\n\n    if isinstance(a, ndarray):\n        b = a.copy(order='C')\n    else:\n        b = asarray(a)\n    _sort(b, axis=axis, kind=kind, force_compact=(b.ndim == 1))\n    return b\n\ndef _asort(a: ndarray, axis: int, sorter):\n    axis = util.normalize_axis_index(axis, a.ndim)\n    n = a.shape[axis]\n    b = empty(a.shape, int)\n    a_stride = a.strides[axis]\n    b_stride = b.strides[axis]\n    a_compact = (a_stride == a.itemsize)\n    b_compact = (b_stride == b.itemsize)\n    a_buf = Ptr[a.dtype]() if a_compact else Ptr[a.dtype](n)\n    b_buf = Ptr[b.dtype]() if b_compact else Ptr[b.dtype](n)\n\n    for idx in util.multirange(util.tuple_delete(a.shape, axis)):\n        idx1 = util.tuple_insert(idx, axis, 0)\n        pa = a._ptr(idx1)\n        pb = b._ptr(idx1)\n        qa = pa.as_byte()\n        qb = pb.as_byte()\n\n        if not a_compact:\n            for i in range(n):\n                a_buf[i] = (Ptr[a.dtype](qa))[0]\n                qa += a_stride\n\n        if not b_compact:\n            for i in range(n):\n                b_buf[i] = i\n        else:\n            for i in range(n):\n                pb[i] = i\n\n        start = pa if a_compact else a_buf\n        tosort = pb if b_compact else b_buf\n        sorter(start, tosort, n)\n\n        if not a_compact:\n            qa = pa.as_byte()\n            for i in range(n):\n                Ptr[a.dtype](qa)[0] = a_buf[i]\n                qa += a_stride\n\n        if not b_compact:\n            qb = pb.as_byte()\n            for i in range(n):\n                Ptr[b.dtype](qb)[0] = b_buf[i]\n                qb += b_stride\n\n    if not a_compact:\n        util.free(a_buf)\n\n    if not b_compact:\n        util.free(b_buf)\n\n    return b\n\ndef argsort(a, axis = -1, kind: Optional[str] = None):\n    if axis is None:\n        return argsort(asarray(a).flatten(), axis=-1, kind=kind)\n\n    a = asarray(a)\n    if kind is None or kind == 'quicksort' or kind == 'quick':\n        return _asort(a, axis, aquicksort)\n    elif kind == 'mergesort' or kind == 'merge' or kind == 'stable':\n        return _asort(a, axis, astablesort)\n    elif kind == 'heapsort' or kind == 'heap':\n        return _asort(a, axis, aheapsort)\n    else:\n        raise ValueError(f\"sort kind must be one of 'quick', 'heap', or 'stable' (got {repr(kind)})\")\n\ndef sort_complex(a):\n    b = array(a, copy=True)\n    b.sort()\n    if not (b.dtype is complex or b.dtype is complex64):\n        if b.dtype is byte or b.dtype is i8 or b.dtype is u8 or b.dtype is i16 or b.dtype is u16:\n            return b.astype(complex64)\n        else:\n            return b.astype(complex)\n    else:\n        return b\n\n@extend\nclass ndarray:\n    def sort(self, axis: int = -1, kind: Optional[str] = None):\n        _sort(self, axis=axis, kind=kind, force_compact=False)\n\n    def argsort(self, axis: int = -1, kind: Optional[str] = None):\n        return argsort(self, axis=axis, kind=kind)\n\n    def partition(self, kth, axis: int = -1, kind: str = 'introselect'):\n        _partition_helper(self, kth=kth, axis=axis, kind=kind, force_compact=False)\n\n    def argpartition(self, kth, axis: int = -1, kind: str = 'introselect'):\n        return argpartition(self, kth=kth, axis=axis, kind=kind)\n\n# TODO: support 'order' argument on sort, argsort, ndarray.sort, partition and argpartition\n"
  },
  {
    "path": "stdlib/numpy/statistics.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport util\nimport internal.static as static\n\nfrom .reductions import mean, std, percentile\nfrom .ndarray import ndarray\nfrom .routines import array, asarray, full, broadcast_to, swapaxes, empty, concatenate, around, diag, clip, zeros, searchsorted, linspace\nfrom .ndmath import multiply, true_divide, conjugate, power, logical_and, ceil, sqrt, subtract, min\nfrom .linalg_sym import dot\nfrom .sorting import sort, argsort\n\ndef average(a,\n            axis=None,\n            weights=None,\n            returned: Literal[bool] = False,\n            keepdims: Literal[bool] = False):\n\n    def result_type(a_dtype: type, w_dtype: type):\n        common_dtype = type(util.coerce(a_dtype, w_dtype))\n        if isinstance(a_dtype, int) or isinstance(a_dtype, bool):\n            return type(util.coerce(common_dtype, float))()\n        else:\n            return common_dtype()\n\n    a = asarray(a)\n\n    if weights is None:\n        avg = a.mean(axis, keepdims=keepdims)\n        if returned:\n            if isinstance(avg, ndarray):\n                scl = a.size / avg.size\n                return avg, full(avg.shape, scl, dtype=avg.dtype)\n            else:\n                scl = a.size\n                return avg, util.cast(scl, type(avg))\n        else:\n            return avg\n    else:\n        w = asarray(weights)\n        result_dtype = type(result_type(a.dtype, w.dtype))\n\n        if a.ndim != w.ndim:\n            if axis is None:\n                compile_error(\n                    \"Axis must be specified when shapes of a and weights differ.\"\n                )\n\n            if w.ndim != 1:\n                compile_error(\n                    \"1D weights expected when shapes of a and weights differ.\")\n\n            wgt = broadcast_to(w, ((1, ) * (a.ndim - 1)) + w.shape).swapaxes(\n                -1, axis)\n            scl = wgt.sum(axis=axis, dtype=result_dtype, keepdims=keepdims)\n            avg = multiply(a, wgt, dtype=result_dtype).sum(\n                axis, keepdims=keepdims) / scl\n\n            if returned:\n                if scl.shape != avg.shape:\n                    scl = broadcast_to(scl, avg.shape).copy()\n                return avg, scl\n            else:\n                return avg\n        else:\n            ax = 0\n            if isinstance(axis, int):\n                ax = axis\n            elif axis is not None:\n                ax = axis[0]\n\n            if a.shape != w.shape:\n                if axis is None:\n                    raise TypeError(\n                        \"Axis must be specified when shapes of a and weights differ.\"\n                    )\n\n                if w.ndim != 1:\n                    raise TypeError(\n                        \"1D weights expected when shapes of a and weights differ.\"\n                    )\n\n                if w.shape[0] != a.shape[ax]:\n                    raise ValueError(\n                        \"Length of weights not compatible with specified axis.\"\n                    )\n\n            def get_axis(axis):\n                if axis is not None:\n                    return axis\n                else:\n                    return None\n\n            ax = get_axis(axis)\n\n            scl = w.sum(axis=ax, dtype=result_dtype, keepdims=keepdims)\n            avg = multiply(a, w, dtype=result_dtype).sum(\n                ax, keepdims=keepdims) / scl\n\n            if returned:\n                if isinstance(scl, ndarray) and isinstance(avg, ndarray):\n                    if scl.shape != avg.shape:\n                        scl = broadcast_to(scl, avg.shape).copy()\n                return avg, util.cast(scl, type(avg))\n            else:\n                return avg\n\ndef cov(m,\n        y=None,\n        rowvar: bool = True,\n        bias: bool = False,\n        ddof: Optional[int] = None,\n        fweights=None,\n        aweights=None,\n        dtype: type = NoneType):\n\n    def result_type(a_dtype: type, y_dtype: type):\n        common_dtype = type(util.coerce(a_dtype, y_dtype))\n        if isinstance(a_dtype, int) or isinstance(a_dtype, bool):\n            return util.coerce(common_dtype, float)\n        else:\n            return common_dtype()\n\n    def get_dtype(m, y, dtype: type):\n        if dtype is NoneType:\n            if y is None:\n                return result_type(m.dtype, float)\n            else:\n                tmp_dtype = result_type(m.dtype, y.dtype)\n                return result_type(type(tmp_dtype), float)\n        else:\n            return dtype()\n\n    m = asarray(m)\n    if m.ndim > 2:\n        compile_error(\"m has more than 2 dimensions\")\n\n    if y is not None:\n        y2 = asarray(y)\n        if y2.ndim > 2:\n            compile_error(\"y has more than 2 dimensions\")\n    else:\n        y2 = y\n\n    dtype2 = type(get_dtype(m, y2, dtype))\n    X = array(m, ndmin=2, dtype=dtype2)\n\n    if not rowvar and X.shape[0] != 1:\n        X = X.T\n\n    if X.shape[0] == 0:\n        if m.ndim == 1 and y is None:\n            return util.cast(util.nan64(), dtype2)\n        else:\n            return empty((0, 0), dtype2)\n\n    if y2 is not None:\n        y2 = array(y2, copy=False, ndmin=2, dtype=dtype2)\n        if not rowvar and y2.shape[0] != 1:\n            y2 = y2.T\n        X = concatenate((X, y2), axis=0)\n\n    ddof2: int = 0\n    if ddof is None:\n        if not bias:\n            ddof2 = 1\n        else:\n            ddof2 = 0\n    else:\n        ddof2 = ddof\n\n    # Get the product of frequencies and weights\n    w = ndarray[float, 1]((0, ), (0, ), Ptr[float]())\n\n    if fweights is not None:\n        fweights = asarray(fweights, dtype=float)\n\n        if fweights.ndim == 0:\n            compile_error(\"fweights must be at least 1-dimensional\")\n        elif fweights.ndim > 1:\n            compile_error(\"cannot handle multidimensional fweights\")\n\n        if fweights.shape[0] != X.shape[1]:\n            raise ValueError(\"incompatible numbers of samples and fweights\")\n\n        for item in fweights:\n            if item != around(item):\n                raise TypeError(\"fweights must be integer\")\n            elif item < 0:\n                raise ValueError(\"fweights cannot be negative\")\n\n        w = fweights\n\n    if aweights is not None:\n        aweights2 = asarray(aweights, dtype=float)\n\n        if aweights2.ndim == 0:\n            compile_error(\"aweights must be at least 1-dimensional\")\n        elif aweights2.ndim > 1:\n            compile_error(\"cannot handle multidimensional aweights\")\n\n        if aweights2.shape[0] != X.shape[1]:\n            raise ValueError(\"incompatible numbers of samples and aweights\")\n\n        for item in aweights2:\n            if item < 0:\n                raise ValueError(\"aweights cannot be negative\")\n\n        if w.size == 0:\n            w = aweights2\n        else:\n            w *= aweights2\n\n    if w.size == 0:\n        avg1, w_sum1 = average(X, axis=1, weights=None, returned=True)\n        avg = asarray(avg1, dtype=dtype2)\n        w_sum = asarray(w_sum1, dtype=dtype2)\n    else:\n        avg1, w_sum1 = average(X, axis=1, weights=w, returned=True)\n        avg = asarray(avg1, dtype=dtype2)\n        w_sum = asarray(w_sum1, dtype=dtype2)\n\n    w_sum = w_sum[0]\n    fact = util.cast(0, dtype2)\n\n    # Determine the normalization\n    if w.size == 0:\n        fact = util.cast(X.shape[1] - ddof2, dtype2)\n    elif ddof2 == 0:\n        fact = util.cast(w_sum, dtype2)\n    elif aweights is None:\n        fact = util.cast(w_sum - ddof2, dtype2)\n    else:\n        fact = util.cast(w_sum - ddof2 * sum(w * aweights2) / w_sum, dtype2)\n\n    if util.cast(fact, float) <= 0.0:\n        # warn \"Degrees of freedom <= 0 for slice\"\n        fact = util.cast(0, dtype2)\n\n    X -= avg[:, None]\n\n    if w.size == 0:\n        X_T = X.T\n    else:\n        Xw = multiply(X, w)\n        X_T = Xw.T\n\n    c = dot(X, X_T.conj())\n    c *= true_divide(1, fact)\n    if m.ndim == 1 and y is None:\n        return c.item()\n    else:\n        return c\n\ndef corrcoef(x, y=None, rowvar=True, dtype: type = NoneType):\n    c = cov(x, y=y, rowvar=rowvar, dtype=dtype)\n    b = asarray(c)\n    if b.ndim != 1 and b.ndim != 2:\n        # scalar covariance\n        # nan if incorrect value (nan, inf, 0), 1 otherwise\n        return c / c\n\n    d = diag(c)\n    stddev = sqrt(d.real)\n    c /= stddev[:, None]\n    c /= stddev[None, :]\n\n    # Clip real and imaginary parts to [-1, 1]\n    clip(c.real, -1, 1, out=c.real)\n    if c.dtype is complex or c.dtype is complex64:\n        clip(c.imag, -1, 1, out=c.imag)\n\n    return c\n\ndef _correlate(a, b, mode: str):\n\n    def kernel(d, dstride: int, nd: int, dtype: type,\n               k, kstride: int, nk: Literal[int], ktype: type,\n               out, ostride: int):\n        for i in range(nd):\n            acc = util.zero(dtype)\n            for j in static.range(nk):\n                acc += d[(i + j) * dstride] * k[j * kstride]\n            out[i * ostride] = acc\n\n    def small_correlate(d, dstride: int, nd: int, dtype: type,\n                        k, kstride: int, nk: int, ktype: type,\n                        out, ostride: int):\n        if dtype is not ktype:\n            return False\n\n        dstride //= util.sizeof(dtype)\n        kstride //= util.sizeof(dtype)\n        ostride //= util.sizeof(dtype)\n\n        if nk == 1:\n            kernel(d=d, dstride=dstride, nd=nd, dtype=dtype,\n                   k=k, kstride=kstride, nk=1, ktype=ktype,\n                   out=out, ostride=ostride)\n        elif nk == 2:\n            kernel(d=d, dstride=dstride, nd=nd, dtype=dtype,\n                   k=k, kstride=kstride, nk=2, ktype=ktype,\n                   out=out, ostride=ostride)\n        elif nk == 3:\n            kernel(d=d, dstride=dstride, nd=nd, dtype=dtype,\n                   k=k, kstride=kstride, nk=3, ktype=ktype,\n                   out=out, ostride=ostride)\n        elif nk == 4:\n            kernel(d=d, dstride=dstride, nd=nd, dtype=dtype,\n                   k=k, kstride=kstride, nk=4, ktype=ktype,\n                   out=out, ostride=ostride)\n        elif nk == 5:\n            kernel(d=d, dstride=dstride, nd=nd, dtype=dtype,\n                   k=k, kstride=kstride, nk=5, ktype=ktype,\n                   out=out, ostride=ostride)\n        elif nk == 6:\n            kernel(d=d, dstride=dstride, nd=nd, dtype=dtype,\n                   k=k, kstride=kstride, nk=6, ktype=ktype,\n                   out=out, ostride=ostride)\n        elif nk == 7:\n            kernel(d=d, dstride=dstride, nd=nd, dtype=dtype,\n                   k=k, kstride=kstride, nk=7, ktype=ktype,\n                   out=out, ostride=ostride)\n        elif nk == 8:\n            kernel(d=d, dstride=dstride, nd=nd, dtype=dtype,\n                   k=k, kstride=kstride, nk=8, ktype=ktype,\n                   out=out, ostride=ostride)\n        elif nk == 9:\n            kernel(d=d, dstride=dstride, nd=nd, dtype=dtype,\n                   k=k, kstride=kstride, nk=9, ktype=ktype,\n                   out=out, ostride=ostride)\n        elif nk == 10:\n            kernel(d=d, dstride=dstride, nd=nd, dtype=dtype,\n                   k=k, kstride=kstride, nk=10, ktype=ktype,\n                   out=out, ostride=ostride)\n        elif nk == 11:\n            kernel(d=d, dstride=dstride, nd=nd, dtype=dtype,\n                   k=k, kstride=kstride, nk=11, ktype=ktype,\n                   out=out, ostride=ostride)\n        else:\n            return False\n\n        return True\n\n    def dot(_ip1: Ptr[T1], is1: int, _ip2: Ptr[T2], is2: int, op: Ptr[T3], n: int,\n            T1: type, T2: type, T3: type):\n        ip1 = _ip1.as_byte()\n        ip2 = _ip2.as_byte()\n        ans = util.zero(T3)\n\n        for i in range(n):\n            e1 = Ptr[T1](ip1)[0]\n            e2 = Ptr[T2](ip2)[0]\n            ans += util.cast(e1, T3) * util.cast(e2, T3)\n            ip1 += is1\n            ip2 += is2\n\n        op[0] = ans\n\n    def incr(p: Ptr[T], s: int, T: type):\n        return Ptr[T](p.as_byte() + s)\n\n    n1 = a.size\n    n2 = b.size\n    length = n1\n    n = n2\n\n    if mode == 'valid':\n        length = length = length - n + 1\n        n_left = 0\n        n_right = 0\n    elif mode == 'same':\n        n_left = n >> 1\n        n_right = n - n_left - 1\n    elif mode == 'full':\n        n_right = n - 1\n        n_left = n - 1\n        length = length + n - 1\n    else:\n        raise ValueError(\n            f\"mode must be one of 'valid', 'same', or 'full' (got {repr(mode)})\"\n        )\n\n    dt = type(util.coerce(a.dtype, b.dtype))\n    ret = empty(length, dtype=dt)\n\n    is1 = a.strides[0]\n    is2 = b.strides[0]\n    op = ret.data\n    os = ret.itemsize\n    ip1 = a.data\n    ip2 = Ptr[b.dtype](b.data.as_byte() + n_left * is2)\n    n = n - n_left\n\n    for i in range(n_left):\n        dot(ip1, is1, ip2, is2, op, n)\n        n += 1\n        ip2 = incr(ip2, -is2)\n        op = incr(op, os)\n\n    if small_correlate(ip1, is1, n1 - n2 + 1, a.dtype,\n                       ip2, is2, n, b.dtype,\n                       op, os):\n        ip1 = incr(ip1, is1 * (n1 - n2 + 1))\n        op = incr(op, os * (n1 - n2 + 1))\n    else:\n        for i in range(n1 - n2 + 1):\n            dot(ip1, is1, ip2, is2, op, n)\n            ip1 = incr(ip1, is1)\n            op = incr(op, os)\n\n    for i in range(n_right):\n        n -= 1\n        dot(ip1, is1, ip2, is2, op, n)\n        ip1 = incr(ip1, is1)\n        op = incr(op, os)\n\n    return ret\n\ndef correlate(a, b, mode: str = 'valid'):\n    a = asarray(a)\n    b = asarray(b)\n\n    if a.ndim != 1 or b.ndim != 1:\n        compile_error('object too deep for desired array')\n\n    n1 = a.size\n    n2 = b.size\n\n    if n1 == 0:\n        raise ValueError(\"first argument cannot be empty\")\n\n    if n2 == 0:\n        raise ValueError(\"second argument cannot be empty\")\n\n    if b.dtype is complex or b.dtype is complex64:\n        b = b.conjugate()\n\n    if n1 < n2:\n        return _correlate(b, a, mode=mode)[::-1]\n    else:\n        return _correlate(a, b, mode=mode)\n\ndef bincount(x, weights=None, minlength: int = 0):\n    x = asarray(x).astype(int)\n\n    if x.ndim > 1:\n        compile_error(\"object too deep for desired array\")\n    elif x.ndim < 1:\n        compile_error(\"object of too small depth for desired array\")\n\n    if minlength < 0:\n        raise ValueError(\"'minlength' must not be negative\")\n\n    mn, mx = x._minmax()\n    if mn < 0:\n        raise ValueError(\"'list' argument must have no negative elements\")\n    max_val = mx + 1\n    if minlength > max_val:\n        max_val = minlength\n\n    if weights is None:\n        result = zeros(max_val, int)\n        for i in range(len(x)):\n            result._ptr((x._ptr((i, ))[0], ))[0] += 1\n        return result\n    else:\n        weights = asarray(weights).astype(float)\n\n        if weights.ndim > 1:\n            compile_error(\"object too deep for desired array\")\n        elif weights.ndim < 1:\n            compile_error(\"object of too small depth for desired array\")\n\n        if len(weights) != len(x):\n            raise ValueError(\n                \"The weights and list don't have the same length.\")\n\n        result = zeros(max_val, float)\n        for i in range(len(x)):\n            result._ptr((x._ptr((i, ))[0], ))[0] += weights._ptr((i, ))[0]\n        return result\n\ndef _monotonicity(bins):\n    if bins.ndim < 1:\n        compile_error(\"object of too small depth for desired array\")\n    elif bins.ndim > 1:\n        compile_error(\"object too deep for desired array\")\n\n    increasing = True\n    decreasing = True\n\n    for i in range(len(bins) - 1):\n        a = bins._ptr((i, ))[0]\n        b = bins._ptr((i + 1, ))[0]\n        if a < b:\n            decreasing = False\n        if b < a:\n            increasing = False\n        if not (increasing or decreasing):\n            break\n\n    if increasing:\n        return 1\n    elif decreasing:\n        return -1\n    else:\n        return 0\n\ndef digitize(x, bins, right: bool = False):\n    x = asarray(x)\n    bins = asarray(bins)\n\n    if x.dtype is complex or x.dtype is complex64:\n        compile_error(\"x may not be complex\")\n\n    mono = _monotonicity(bins)\n    if mono == 0:\n        raise ValueError(\"bins must be monotonically increasing or decreasing\")\n\n    # this is backwards because the arguments below are swapped\n    side = 'left' if right else 'right'\n    if mono == -1:\n        # reverse the bins, and invert the results\n        return len(bins) - searchsorted(bins[::-1], x, side=side)\n    else:\n        return searchsorted(bins, x, side=side)\n\ndef _ravel_and_check_weights(a, weights):\n    # Check a and weights have matching shapes, and ravel both\n    a = asarray(a)\n\n    if weights is not None:\n        weights2 = asarray(weights)\n        if weights2.shape != a.shape:\n            raise ValueError('weights should have the same shape as a.')\n        weights3 = weights2.ravel()\n    else:\n        weights3 = weights\n\n    # Ensure that the array is a \"subtractable\" dtype\n    if a.dtype is bool:\n        # TODO: change to unsigned int after\n        a2 = a.astype(int)\n    else:\n        a2 = a\n\n    a3 = a2.ravel()\n    return a3, weights3\n\n_range = range\n\ndef _unsigned_subtract(a, b):\n    if isinstance(a, ndarray):\n        dt = type(util.coerce(a.dtype, type(b)))\n        a = a.astype(dt)\n        b = util.cast(b, dt)\n\n        if dt is int:\n            c = a - b\n            return c.astype(u64)\n        elif isinstance(dt, Int):\n            return UInt[dt.N](a) - UInt[dt.N](b)\n        else:\n            return a - b\n    else:\n        dt = type(util.coerce(type(a), type(b)))\n        a = util.cast(a, dt)\n        b = util.cast(b, dt)\n\n        if dt is int:\n            return u64(a) - u64(b)\n        elif isinstance(dt, Int):\n            return UInt[dt.N](a) - UInt[dt.N](b)\n        else:\n            return a - b\n\ndef _get_outer_edges(a, range):\n    if range is not None:\n        first_edge = float(range[0])\n        last_edge = float(range[1])\n        if first_edge > last_edge:\n            raise ValueError('max must be larger than min in range parameter.')\n        if not (util.isfinite(first_edge) and util.isfinite(last_edge)):\n            raise ValueError(\n                f\"supplied range of [{first_edge}, {last_edge}] is not finite\")\n    elif a.size == 0:\n        # handle empty arrays. Can't determine range, so use 0-1.\n        if not (a.dtype is complex or a.dtype is complex64):\n            first_edge, last_edge = 0.0, 1.0\n        else:\n            first_edge, last_edge = 0 + 0j, 1 + 0j\n    else:\n        t = a._minmax()\n        if not (a.dtype is complex or a.dtype is complex64):\n            first_edge, last_edge = float(t[0]), float(t[1])\n        else:\n            first_edge, last_edge = a._minmax()\n        if not (util.isfinite(first_edge) and util.isfinite(last_edge)):\n            raise ValueError(\n                f\"autodetected range of [{first_edge}, {last_edge}] is not finite\"\n            )\n\n    # expand empty range to avoid divide by zero\n    if first_edge == last_edge:\n        first_edge = first_edge - 0.5\n        last_edge = last_edge + 0.5\n\n    return first_edge, last_edge\n\ndef _ptp(x):\n    xmin, xmax = x._minmax()\n    return _unsigned_subtract(xmax, xmin)\n\ndef _hist_bin_sqrt(x):\n    return float(_ptp(x)) / util.sqrt(float(x.size))\n\ndef _hist_bin_sturges(x, range):\n    return float(_ptp(x)) / (util.log2(float(x.size)) + 1.0)\n\ndef _hist_bin_rice(x):\n    return float(_ptp(x)) / (2.0 * x.size**(1.0 / 3))\n\ndef _hist_bin_scott(x):\n    return (24.0 * util.PI**0.5 / x.size)**(1.0 / 3.0) * std(x)\n\ndef _hist_bin_stone(x, range, histogram):\n    n = x.size\n    ptp_x = float(_ptp(x))\n    if n <= 1 or ptp_x == 0:\n        return 0.0\n\n    def jhat(nbins):\n        hh = ptp_x / nbins\n        p_k = histogram(x, bins=nbins, range=range)[0] / n\n        return (2 - (n + 1) * p_k.dot(p_k)) / hh\n\n    nbins_upper_bound = max(100, int(util.sqrt(float(n))))\n    min_nbins = 1.\n    min_value = float('inf')\n\n    for nbins in _range(1, nbins_upper_bound + 1):\n        current_value = jhat(nbins)\n        if current_value < min_value:\n            min_value = current_value\n            min_nbins = nbins\n\n    nbins = min_nbins\n    # if nbins == nbins_upper_bound:\n    #     warn \"The number of bins estimated may be suboptimal.\"\n    return ptp_x / nbins\n\ndef _hist_bin_doane(x):\n    if x.size > 2:\n        sg1 = util.sqrt(6.0 * (x.size - 2) / ((x.size + 1.0) * (x.size + 3)))\n        sigma = std(x)\n        if sigma > 0.0:\n            temp = x - mean(x)\n            true_divide(temp, sigma, temp)\n            power(temp, 3, temp)\n            g1 = mean(temp)\n            return float(_ptp(x)) / (float(1.0) + util.log2(float(x.size)) +\n                                     util.log2(1.0 + abs(g1) / sg1))\n    return 0.0\n\ndef _hist_bin_fd(x, range):\n    percentiles = percentile(x, [75, 25])\n    iqr = subtract(percentiles[0], percentiles[1])\n    return 2.0 * iqr * x.size**(-1.0 / 3.0)\n\ndef _hist_bin_auto(x, range):\n    fd_bw = _hist_bin_fd(x, range)\n    sturges_bw = _hist_bin_sturges(x, range)\n    if fd_bw:\n        return min(fd_bw, sturges_bw)\n    else:\n        # limited variance, so we return a len dependent bw estimator\n        return sturges_bw\n\ndef _diff(a):\n    a = asarray(a)\n    if a.ndim != 1:\n        compile_error(\"[internal error] expected 1-d array\")\n\n    ans = empty(a.size - 1, dtype=a.dtype)\n    for i in range(ans.size):\n        ans.data[i] = a._ptr((i + 1, ))[0] - a._ptr((i, ))[0]\n    return ans\n\ndef _get_bin_edges(a, bins, range, weights, histogram):\n\n    def get_bin_type(first_edge, last_edge, a):\n        T1 = type(util.coerce(type(first_edge), type(last_edge)))\n        T2 = type(util.coerce(T1, a.dtype))\n        if T2 is int or T2 is byte or isinstance(T2, Int) or isinstance(\n                T2, UInt):\n            return float()\n        else:\n            return T2()\n\n    def bin_edges_result(a,\n                         bin_edges=None,\n                         n_equal_bins=None,\n                         first_edge=None,\n                         last_edge=None):\n        if n_equal_bins is not None:\n            bin_type = type(get_bin_type(first_edge, last_edge, a))\n\n            return (linspace(first_edge,\n                             last_edge,\n                             n_equal_bins + 1,\n                             endpoint=True,\n                             dtype=bin_type), (first_edge, last_edge,\n                                               n_equal_bins))\n        else:\n            return bin_edges, None\n\n    if isinstance(bins, str):\n        bin_name = bins\n        # if `bins` is a string for an automatic method,\n        # this will replace it with the number of bins calculated\n        if bin_name not in ('stone', 'auto', 'doane', 'fd', 'rice', 'scott',\n                            'sqrt', 'sturges'):\n            raise ValueError(f\"{bin_name} is not a valid estimator for `bins`\")\n        if weights is not None:\n            compile_error(\n                \"Automated estimation of the number of bins is not supported for weighted data\"\n            )\n\n        tmp1, tmp2 = _get_outer_edges(a, range)\n        if not (tmp1 == 0 + 0j and tmp2 == 1 + 0j):\n            first_edge, last_edge = tmp1, tmp2\n        else:\n            first_edge, last_edge = 0, 1\n\n        # truncate the range if needed\n        if range is not None:\n            keep = (a >= first_edge)\n            keep &= (a <= last_edge)\n            if not logical_and.reduce(keep):\n                a = a[keep]\n\n        if a.size == 0:\n            n_equal_bins = 1\n        else:\n            # Do not call selectors on empty arrays\n            if bin_name == 'stone':\n                width = _hist_bin_stone(a, (first_edge, last_edge), histogram)\n            elif bin_name == 'auto':\n                width = _hist_bin_auto(a, (first_edge, last_edge))\n            elif bin_name == 'fd':\n                width = _hist_bin_fd(a, (first_edge, last_edge))\n            if bin_name == 'doane':\n                width = _hist_bin_doane(a)\n            elif bin_name == 'rice':\n                width = _hist_bin_rice(a)\n            elif bin_name == 'scott':\n                width = _hist_bin_scott(a)\n            elif bin_name == 'sqrt':\n                width = _hist_bin_sqrt(a)\n            elif bin_name == 'sturges':\n                width = _hist_bin_sturges(a, (first_edge, last_edge))\n\n            if width:\n                n_equal_bins = int(\n                    ceil(_unsigned_subtract(last_edge, first_edge) / width))\n            else:\n                # Width can be zero for some estimators, e.g. FD when the IQR of the data is zero.\n                n_equal_bins = 1\n\n        return bin_edges_result(a,\n                                n_equal_bins=n_equal_bins,\n                                first_edge=first_edge,\n                                last_edge=last_edge)\n\n    bins = asarray(bins)\n\n    if bins.ndim == 0:\n        n_equal_bins = bins.item()\n        if n_equal_bins < 1:\n            raise ValueError('`bins` must be positive, when an integer')\n\n        tmp1, tmp2 = _get_outer_edges(a, range)\n        if not (tmp1 == 0 + 0j and tmp2 == 1 + 0j):\n            first_edge, last_edge = tmp1, tmp2\n        else:\n            first_edge, last_edge = 0, 1\n        return bin_edges_result(a,\n                                n_equal_bins=n_equal_bins,\n                                first_edge=first_edge,\n                                last_edge=last_edge)\n\n    if bins.ndim == 1:\n        bin_edges = asarray(bins)\n\n        i = 0\n        while i < bin_edges.size - 1:  # range is shadowed by function arg\n            if bin_edges._ptr((i, ))[0] > bin_edges._ptr((i + 1, ))[0]:\n                raise ValueError(\n                    '`bins` must increase monotonically, when an array')\n            i += 1\n\n        return bin_edges_result(a, bin_edges=bin_edges)\n\ndef _search_sorted_inclusive(a, v):\n    # Like `searchsorted`, but where the last item in `v` is placed on the right.\n    return concatenate(\n        (a.searchsorted(v[:-1], 'left'), a.searchsorted(v[-1:], 'right')))\n\ndef _histogram_fast(a, bins=10, range=None):\n    # Adapted from Numba's implementation\n    # https://github.com/numba/numba/blob/main/numba/np/old_arraymath.py\n\n    def intermediate_type(bins, dtype: type):\n        if bins is None or isinstance(bins, int):\n            if dtype is float32 or dtype is float16:\n                return util.zero(dtype)\n            else:\n                return util.zero(float)\n        else:\n            return intermediate_type(None, type(asarray(bins).data[0]))\n\n    a = asarray(a)\n    T = type(intermediate_type(bins, a.dtype))\n\n    if range is None:\n        if a.size == 0:\n            bin_min = util.cast(0, T)\n            bin_max = util.cast(1, T)\n        else:\n            inf = util.inf(T)\n            bin_min = inf\n            bin_max = -inf\n            for idx in util.multirange(a.shape):\n                v = util.cast(a._ptr(idx)[0], T)\n                if bin_min > v:\n                    bin_min = v\n                if bin_max < v:\n                    bin_max = v\n        return _histogram_fast(a, bins, range=(bin_min, bin_max))\n\n    if isinstance(bins, int):\n        if bins <= 0:\n            raise ValueError(\"`bins` must be positive, when an integer\")\n\n        bin_min, bin_max = range\n        bin_min = util.cast(bin_min, T)\n        bin_max = util.cast(bin_max, T)\n\n        if not (util.isfinite(bin_min) and util.isfinite(bin_max)):\n            raise ValueError(f\"supplied range of [{bin_min}, {bin_max}] is not finite\")\n\n        if not bin_min <= bin_max:\n            raise ValueError(\"max must be larger than min in range parameter.\")\n\n        hist = zeros(bins, int)\n\n        if bin_min == bin_max:\n            bin_min -= T(0.5)\n            bin_max += T(0.5)\n\n        bin_ratio = util.cast(bins, T) / (bin_max - bin_min)\n        for idx in util.multirange(a.shape):\n            v = util.cast(a._ptr(idx)[0], T)\n            b = int(util.floor((v - bin_min) * bin_ratio))\n            if 0 <= b < bins:\n                hist.data[b] += 1\n            elif v == bin_max:\n                hist.data[bins - 1] += 1\n\n        bins_array = linspace(float(bin_min), float(bin_max), bins + 1, dtype=T)\n        return hist, bins_array\n    else:\n        bins = asarray(bins)\n        if bins.ndim != 1:\n            compile_error(\"`bins` must be 1d, when an array\")\n\n        nbins = len(bins) - 1\n        i = 0\n        while i < nbins:\n            # Note this also catches NaNs\n            if not bins._ptr((i, ))[0] <= bins._ptr((i + 1, ))[0]:\n                raise ValueError(\"`bins` must increase monotonically, when an array\")\n            i += 1\n\n        bin_min = util.cast(bins._ptr((0, ))[0], T)\n        bin_max = util.cast(bins._ptr((nbins, ))[0], T)\n        hist = zeros(nbins, int)\n\n        if nbins > 0:\n            for idx in util.multirange(a.shape):\n                v = util.cast(a._ptr(idx)[0], T)\n                if not bin_min <= v <= bin_max:\n                    # Value is out of bounds, ignore (also catches NaNs)\n                    continue\n                # Bisect in bins[:-1]\n                lo = 0\n                hi = nbins - 1\n                while lo < hi:\n                    # Note the `+ 1` is necessary to avoid an infinite\n                    # loop where mid = lo => lo = mid\n                    mid = (lo + hi + 1) >> 1\n                    if v < util.cast(bins._ptr((mid, ))[0], T):\n                        hi = mid - 1\n                    else:\n                        lo = mid\n                hist.data[lo] += 1\n\n        return hist, bins\n\ndef histogram(a,\n              bins=10,\n              range=None,\n              density: Literal[bool] = False,\n              weights=None):\n\n    def return_zeros(size, weights):\n        if weights is None:\n            return zeros(size, dtype=int)\n        else:\n            return zeros(size, dtype=weights.dtype)\n\n    def histogram_result(n, bin_edges, density: Literal[bool] = False):\n        if density:\n            db = array(_diff(bin_edges), float)\n            return (n / db / n.sum(), bin_edges)\n        else:\n            return (n, bin_edges)\n\n    a = asarray(a)\n\n    if (not density and\n        weights is None and\n        not isinstance(bins, str) and\n        not (a.dtype is complex or a.dtype is complex64)):\n        return _histogram_fast(a, bins=bins, range=range)\n\n    a, weights = _ravel_and_check_weights(a, weights)\n\n    bin_edges, uniform_bins = _get_bin_edges(a, bins, range, weights,\n                                             histogram)\n\n    # We set a block size, as this allows us to iterate over chunks when computing histograms, to minimize memory usage.\n    BLOCK = 65536\n\n    # The fast path uses bincount, but that only works for certain types of weight\n    simple_weights1: Literal[bool] = weights is None\n    if isinstance(weights, ndarray):\n        simple_weights2: Literal[bool] = (weights.dtype is float\n                                          or weights.dtype is complex\n                                          or weights.dtype is complex64)\n\n    if uniform_bins is not None and (simple_weights1 or simple_weights2):\n        # Fast algorithm for equal bins\n        # We now convert values of a to bin indices, under the assumption of equal bin widths (which is valid here).\n        first_edge, last_edge, n_equal_bins = uniform_bins\n\n        # Initialize empty histogram\n        n = return_zeros(n_equal_bins, weights)\n\n        # Pre-compute histogram scaling factor\n        norm_numerator = n_equal_bins\n        norm_denom = _unsigned_subtract(last_edge, first_edge)\n\n        # We iterate over blocks\n        for i in _range(0, len(a), BLOCK):\n            tmp_a = a[i:i + BLOCK]\n            if weights is None:\n                tmp_w = None\n            else:\n                tmp_w = weights[i:i + BLOCK]\n\n            # Only include values in the right range\n            keep = (tmp_a >= first_edge) & (tmp_a <= last_edge)\n            if not logical_and.reduce(keep):\n                tmp_a = tmp_a[keep]\n                if tmp_w is not None:\n                    tmp_w = tmp_w[keep]\n\n            # This cast ensures no type promotions occur below, which gh-10322 make unpredictable\n            tmp_a = tmp_a.astype(bin_edges.dtype, copy=False)\n\n            # Compute the bin indices, and for values that lie exactly on last_edge we need to subtract one\n            f_indices = ((_unsigned_subtract(tmp_a, first_edge) / norm_denom) *\n                         norm_numerator)\n            indices = f_indices.astype(int)\n            indices[indices == n_equal_bins] -= 1\n\n            decrement = tmp_a < bin_edges[indices]\n            indices[decrement] -= 1\n            # The last bin includes the right edge. The other bins do not.\n            increment = ((tmp_a >= bin_edges[indices + 1])\n                         & (indices != n_equal_bins - 1))\n            indices[increment] += 1\n\n            if weights is None:\n                n += bincount(indices, weights=tmp_w,\n                              minlength=n_equal_bins).astype(int)\n            elif weights.dtype is complex or weights.dtype is complex64:\n                n.real += bincount(indices,\n                                   weights=tmp_w.real,\n                                   minlength=n_equal_bins)\n                n.imag += bincount(indices,\n                                   weights=tmp_w.imag,\n                                   minlength=n_equal_bins)\n            else:\n                n += bincount(indices, weights=tmp_w,\n                              minlength=n_equal_bins).astype(weights.dtype)\n    else:\n        # Compute via cumulative histogram\n        cum_n = return_zeros(bin_edges.shape, weights)\n        if weights is None:\n            for i in _range(0, len(a), BLOCK):\n                sa = sort(a[i:i + BLOCK])\n                cum_n += _search_sorted_inclusive(sa, bin_edges)\n        else:\n            zero = return_zeros(1, weights)\n            for i in _range(0, len(a), BLOCK):\n                tmp_a = a[i:i + BLOCK]\n                tmp_w = weights[i:i + BLOCK]\n                sorting_index = argsort(tmp_a)\n                sa = tmp_a[sorting_index]\n                sw = tmp_w[sorting_index]\n                cw = concatenate((zero, sw.cumsum()))\n                bin_index = _search_sorted_inclusive(sa, bin_edges)\n                cum_n += cw[bin_index]\n\n        r = _diff(cum_n)\n        return histogram_result(r, bin_edges, density)\n\n    return histogram_result(n, bin_edges, density)\n\ndef histogram_bin_edges(a, bins=10, range=None, weights=None):\n    a, weights = _ravel_and_check_weights(a, weights)\n    bin_edges, _ = _get_bin_edges(a, bins, range, weights, histogram)\n    return bin_edges\n"
  },
  {
    "path": "stdlib/numpy/ufunc.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport util\nimport routines\nimport internal.static as static\n\nfrom .ndarray import ndarray\nfrom .npdatetime import datetime64, timedelta64\n\ndef _have_vectorized_loop(dtype: type, func: Literal[str]):\n    if not (dtype is float or dtype is float32):\n        return False\n    return (func == 'arccos' or func == 'arccosh' or func == 'arcsin'\n            or func == 'arcsinh' or func == 'arctan' or func == 'arctanh'\n            or func == 'arctan2' or func == 'cos' or func == 'exp'\n            or func == 'exp2' or func == 'expm1' or func == 'log'\n            or func == 'log10' or func == 'log1p' or func == 'log2'\n            or func == 'sin' or func == 'sinh' or func == 'tanh'\n            or func == 'hypot')\n\ndef _apply_vectorized_loop_unary(arr, out, func: Literal[str]):\n    if arr.ndim == 0 or out.ndim == 0 or arr.ndim > out.ndim:\n        compile_error(\"[internal error] bad array dims for vectorized loop\")\n\n    if out.ndim == 1:\n        util.call_vectorized_loop(arr.data, arr.strides[0], Ptr[arr.dtype](),\n                                  0, out.data, out.strides[0], out.size, func)\n        return\n\n    shape = arr.shape\n    arr = routines.broadcast_to(arr, shape)\n\n    if arr._contig_match(out):\n        s = util.sizeof(out.dtype)\n        util.call_vectorized_loop(arr.data, s, Ptr[arr.dtype](), 0, out.data,\n                                  s, out.size, func)\n    else:\n        # Find smallest stride to use in vectorized loop\n        arr_strides = arr.strides\n        out_strides = out.strides\n        n = 0\n        si = 0\n        so = 0\n        loop_axis = -1\n\n        for i in static.range(arr.ndim):\n            if shape[i] > 1 and (loop_axis == -1 or arr_strides[i] < si):\n                n = shape[i]\n                si = arr_strides[i]\n                so = out_strides[i]\n                loop_axis = i\n\n        if loop_axis == -1:\n            n = shape[0]\n            si = arr_strides[0]\n            so = out_strides[0]\n            loop_axis = 0\n\n        for idx in util.multirange(util.tuple_delete(shape, loop_axis)):\n            idx1 = util.tuple_insert(idx, loop_axis, 0)\n            p = arr._ptr(idx1)\n            q = out._ptr(idx1)\n            util.call_vectorized_loop(p, si, Ptr[arr.dtype](), 0, q, so, n,\n                                      func)\n\ndef _apply_vectorized_loop_binary(arr1, arr2, out, func: Literal[str]):\n    if (arr1.ndim == 0 and arr2.ndim == 0\n        ) or out.ndim == 0 or arr1.ndim > out.ndim or arr2.ndim > out.ndim:\n        compile_error(\"[internal error] bad array dims for vectorized loop\")\n\n    if arr1.ndim == 0:\n        st1 = 0\n    else:\n        st1 = arr1.strides[0]\n\n    if arr2.ndim == 0:\n        st2 = 0\n    else:\n        st2 = arr2.strides[0]\n\n    if out.ndim == 1:\n        util.call_vectorized_loop(arr1.data, st1, arr2.data, st2, out.data,\n                                  out.strides[0], out.size, func)\n        return\n\n    shape = out.shape\n    arr1 = routines.broadcast_to(arr1, shape)\n    arr2 = routines.broadcast_to(arr2, shape)\n\n    if arr1._contig_match(out) and arr2._contig_match(out):\n        s = util.sizeof(out.dtype)\n        util.call_vectorized_loop(arr1.data, s, arr2.data, s, out.data, s,\n                                  out.size, func)\n    else:\n        # Find smallest stride to use in vectorized loop\n        arr1_strides = arr1.strides\n        arr2_strides = arr2.strides\n        out_strides = out.strides\n        n = 0\n        si1 = 0\n        si2 = 0\n        so = 0\n        loop_axis = -1\n\n        for i in static.range(arr1.ndim):\n            if shape[i] > 1 and (loop_axis == -1 or arr1_strides[i] < si1):\n                n = shape[i]\n                si1 = arr1_strides[i]\n                si2 = arr2_strides[i]\n                so = out_strides[i]\n                loop_axis = i\n\n        if loop_axis == -1:\n            n = shape[0]\n            si1 = arr1_strides[0]\n            si2 = arr2_strides[0]\n            so = out_strides[0]\n            loop_axis = 0\n\n        for idx in util.multirange(util.tuple_delete(shape, loop_axis)):\n            idx1 = util.tuple_insert(idx, loop_axis, 0)\n            p1 = arr1._ptr(idx1)\n            p2 = arr2._ptr(idx1)\n            q = out._ptr(idx1)\n            util.call_vectorized_loop(p1, si1, p2, si2, q, so, n, func)\n\ndef _fix_scalar(x, A: type):\n    X = type(x)\n\n    a_is_int: Literal[bool] = (A is int or A is byte or isinstance(A, Int)\n                               or isinstance(A, UInt))\n    x_is_int: Literal[bool] = X is bool or X is int\n\n    a_is_float: Literal[bool] = (A is float or A is float32 or A is float16\n                                 or A is bfloat16 or A is float128)\n    x_is_float: Literal[bool] = X is float\n\n    a_is_complex: Literal[bool] = (A is complex or A is complex64)\n    x_is_complex: Literal[bool] = X is complex\n\n    should_cast: Literal[bool] = ((x_is_int and\n                                   (a_is_int or a_is_float or a_is_complex)) or\n                                  (x_is_float and (a_is_float or a_is_complex)) or\n                                  (x_is_complex and a_is_complex))\n\n    if (A is float16 or A is float32) and X is complex:\n        return util.cast(x, complex64)\n    elif should_cast:\n        return util.cast(x, A)\n    else:\n        return x\n\ndef decide_types(x, y, dtype: type):\n    def t1(T: type):\n        return (util.zero(T), util.zero(T))\n\n    def t2(S: type, T: type):\n        return (util.zero(S), util.zero(T))\n\n    if dtype is not NoneType:\n        return t1(dtype)\n\n    X = type(routines.asarray(x).data[0])\n    Y = type(routines.asarray(y).data[0])\n\n    x_scalar: Literal[bool] = (isinstance(x, bool) or isinstance(x, int)\n                              or isinstance(x, float) or isinstance(x, complex))\n    y_scalar: Literal[bool] = (isinstance(y, bool) or isinstance(y, int)\n                              or isinstance(y, float) or isinstance(y, complex))\n\n    if x_scalar and y_scalar:\n        return t1(util.coerce(X, Y))\n    elif x_scalar:\n        return t1(type(_fix_scalar(x, Y)))\n    elif y_scalar:\n        return t1(type(_fix_scalar(y, X)))\n    else:\n        ct1, ct2 = util.op_types(X, Y)\n        return t2(type(ct1), type(ct2))\n\ndef decide_types_copysign(x, y, dtype: type):\n    def t2(S: type, T: type):\n        return (util.zero(S), util.zero(T))\n\n    X = type(routines.asarray(x).data[0])\n    Y = type(routines.asarray(y).data[0])\n    XF = type(util.to_float(util.zero(X)))\n    YF = type(util.to_float(util.zero(Y)))\n\n    x_scalar: Literal[bool] = (isinstance(x, bool) or isinstance(x, int)\n                              or isinstance(x, float) or isinstance(x, complex))\n    y_scalar: Literal[bool] = (isinstance(y, bool) or isinstance(y, int)\n                              or isinstance(y, float) or isinstance(y, complex))\n\n    if dtype is float16 or dtype is float32 or dtype is float:\n        return t2(dtype, dtype)\n    elif dtype is NoneType:\n        if (x_scalar and y_scalar) or not (x_scalar or y_scalar):\n            Z = type(util.coerce(XF, YF))\n            return t2(Z, Z)\n        elif x_scalar:\n            return t2(YF, YF)\n        else:\n            return t2(XF, XF)\n    else:\n        compile_error(\"copysign dtype must be a floating-point type\")\n\ndef decide_types_ldexp(x, y, dtype: type):\n    def t2(S: type, T: type):\n        return (util.zero(S), util.zero(T))\n\n    X = type(routines.asarray(x).data[0])\n    Y = type(routines.asarray(y).data[0])\n    XF = type(util.to_float(util.zero(X)))\n    YF = type(util.to_float(util.zero(Y)))\n\n    if not (Y is int or Y is byte or isinstance(Y, Int)\n            or isinstance(Y, UInt)):\n        compile_error(\"ldexp 2nd argument must be of integral type\")\n\n    x_scalar: Literal[bool] = (isinstance(x, bool) or isinstance(x, int)\n                              or isinstance(x, float) or isinstance(x, complex))\n    y_scalar: Literal[bool] = isinstance(y, int)\n\n    if dtype is float16 or dtype is float32 or dtype is float:\n        return t2(dtype, int)\n    elif dtype is NoneType:\n        if x_scalar:\n            return t2(YF, int)\n        else:\n            return t2(XF, int)\n    else:\n        compile_error(\"ldexp dtype must be a floating-point type\")\n\n@tuple\nclass _UnaryFunctor:\n    ufunc: UF\n    dtype: type\n    UF: type\n\n    def __new__(ufunc: UF, dtype: type, UF: type) -> _UnaryFunctor[dtype, UF]:\n        return superf(ufunc)\n\n    def __call__(self, y, x):\n        y[0] = self.ufunc._f(x[0], dtype=self.dtype, dtype_out=type(y[0]))\n\n@tuple\nclass _UnaryWhereFunctor:\n    ufunc: UF\n    dtype: type\n    UF: type\n\n    def __new__(ufunc: UF, dtype: type,\n                UF: type) -> _UnaryWhereFunctor[dtype, UF]:\n        return superf(ufunc)\n\n    def __call__(self, y, x, w):\n        if w[0]:\n            y[0] = self.ufunc._f(x[0], dtype=self.dtype, dtype_out=type(y[0]))\n\n@tuple\nclass _Unary2Functor:\n    ufunc: UF\n    UF: type\n\n    def __new__(ufunc: UF, UF: type) -> _Unary2Functor[UF]:\n        return superf(ufunc)\n\n    def __call__(self, y1, y2, x):\n        e1, e2 = self.ufunc._op(x[0])\n        y1[0] = util.cast(e1, type(y1[0]))\n        y2[0] = util.cast(e2, type(y2[0]))\n\n@tuple\nclass _Unary2WhereFunctor:\n    ufunc: UF\n    UF: type\n\n    def __new__(ufunc: UF, UF: type) -> _Unary2WhereFunctor[UF]:\n        return superf(ufunc)\n\n    def __call__(self, y1, y2, x, w):\n        if w[0]:\n            e1, e2 = self.ufunc._op(x[0])\n            y1[0] = util.cast(e1, type(y1[0]))\n            y2[0] = util.cast(e2, type(y2[0]))\n\n@tuple\nclass _BinaryFunctor:\n    ufunc: UF\n    CT1: type\n    CT2: type\n    dtype: type\n    UF: type\n\n    def __new__(ufunc: UF, CT1: type, CT2: type, dtype: type,\n                UF: type) -> _BinaryFunctor[CT1, CT2, dtype, UF]:\n        return superf(ufunc)\n\n    def __call__(self, z, x, y):\n        z[0] = self.ufunc._f(util.cast(x[0], CT1),\n                             util.cast(y[0], CT2),\n                             dtype=self.dtype,\n                             dtype_out=type(z[0]))\n\n@tuple\nclass _BinaryScalar1Functor:\n    ufunc: UF\n    x: X\n    CT1: type\n    CT2: type\n    dtype: type\n    UF: type\n    X: type\n\n    def __new__(ufunc: UF, x: X, CT1: type, CT2: type, dtype: type, UF: type,\n                X: type) -> _BinaryScalar1Functor[CT1, CT2, dtype, UF, X]:\n        return superf(ufunc, x)\n\n    def __call__(self, z, y):\n        z[0] = self.ufunc._f(util.cast(self.x, CT1),\n                             util.cast(y[0], CT2),\n                             dtype=self.dtype,\n                             dtype_out=type(z[0]))\n\n@tuple\nclass _BinaryScalar2Functor:\n    ufunc: UF\n    y: Y\n    CT1: type\n    CT2: type\n    dtype: type\n    UF: type\n    Y: type\n\n    def __new__(ufunc: UF, y: Y, CT1: type, CT2: type, dtype: type, UF: type,\n                Y: type) -> _BinaryScalar2Functor[CT1, CT2, dtype, UF, Y]:\n        return superf(ufunc, y)\n\n    def __call__(self, z, x):\n        z[0] = self.ufunc._f(util.cast(x[0], CT1),\n                             util.cast(self.y, CT2),\n                             dtype=self.dtype,\n                             dtype_out=type(z[0]))\n\n@tuple\nclass _BinaryWhereFunctor:\n    ufunc: UF\n    CT1: type\n    CT2: type\n    dtype: type\n    UF: type\n\n    def __new__(ufunc: UF, CT1: type, CT2: type, dtype: type,\n                UF: type) -> _BinaryWhereFunctor[CT1, CT2, dtype, UF]:\n        return superf(ufunc)\n\n    def __call__(self, z, x, y, w):\n        if w[0]:\n            z[0] = self.ufunc._f(util.cast(x[0], CT1),\n                                 util.cast(y[0], CT2),\n                                 dtype=self.dtype,\n                                 dtype_out=type(z[0]))\n\n@tuple\nclass UnaryUFunc:\n    _op: F\n    __name__: Literal[str]\n    F: type\n\n    def __new__(op: F, name: Literal[str], F: type) -> UnaryUFunc[name, F]:\n        return superf(op)\n\n    @property\n    def nin(self):\n        return 1\n\n    @property\n    def nout(self):\n        return 1\n\n    def _f(self, x, dtype: type = NoneType, dtype_out: type = NoneType):\n        if dtype is NoneType:\n            x1 = x\n        else:\n            x1 = util.cast(x, dtype)\n\n        y = self._op(x1)\n\n        if dtype_out is NoneType:\n            return y\n        else:\n            return util.cast(y, dtype_out)\n\n    def __call__(self, x, out=None, where=True, dtype: type = NoneType):\n        fn = self._op\n        x = routines.asarray(x)\n\n        if x.ndim == 0:\n            if out is None:\n                return self._f(x.data[0], dtype=dtype)\n            elif isinstance(out, ndarray):\n                r = self._f(x.data[0], dtype=dtype, dtype_out=out.dtype)\n                out.map(lambda x: r, inplace=True)\n                return out\n            else:\n                compile_error(\"'out' argument must be ndarray or None\")\n\n        if out is None:\n            ans = routines.empty_like(x,\n                                      dtype=type(\n                                          self._f(x.data[0], dtype=dtype)))\n        elif isinstance(out, ndarray):\n            ans = out\n            bshape = util.broadcast(x.shape, ans.shape)\n            if bshape != ans.shape:\n                raise ValueError(\n                    f\"non-broadcastable output operand with shape {ans.shape} doesn't match the broadcast shape {bshape}\"\n                )\n        else:\n            compile_error(\"'out' argument must be ndarray or None\")\n\n        if isinstance(ans, ndarray):\n            a = x._data\n            b = ans._data\n            n = x.size\n\n            if isinstance(where, bool):\n                if where:\n                    if x.dtype is ans.dtype:\n                        if _have_vectorized_loop(x.dtype, __name__):\n                            _apply_vectorized_loop_unary(x, ans, __name__)\n                            return ans\n\n                    functor = _UnaryFunctor(self, dtype)\n                    ndarray._loop((ans, x), functor, check=False)\n            else:\n                where = routines.asarray(where)\n                functor = _UnaryWhereFunctor(self, dtype)\n                ndarray._loop((ans, x, where), functor, broadcast='first')\n\n            return ans\n\n    def at(self, a, indices):\n        if not isinstance(a, ndarray):\n            return self.at(routines.asarray(a), indices)\n\n        fn = self._op\n\n        for idx in indices:\n            a[idx] = fn(a[idx])\n\n@tuple\nclass UnaryUFunc2:\n    _op: F\n    __name__: Literal[str]\n    F: type\n\n    def __new__(op: F, name: Literal[str], F: type) -> UnaryUFunc2[name, F]:\n        return superf(op)\n\n    @property\n    def nin(self):\n        return 1\n\n    @property\n    def nout(self):\n        return 2\n\n    def __call__(self, x, out1=None, out2=None, out=None, where=True):\n        fn = self._op\n\n        if out is not None:\n            if not isinstance(out, Tuple):\n                compile_error(\"'out' must be a tuple of arrays\")\n\n            if not (out1 is None and out2 is None):\n                compile_error(\n                    \"cannot specify 'out' as both a positional and keyword argument\"\n                )\n\n            return self(x, out[0], out[1], out=None, where=where)\n\n        if not isinstance(x, ndarray):\n            return self(routines.asarray(x), out1=out1, out2=out2, where=where)\n\n        if x.ndim == 0 and out1 is None and out2 is None:\n            return fn(x.data[0])\n\n        if out1 is None:\n            T1 = type(fn(x.data[0])[0])\n            ans1 = routines.empty_like(x, dtype=T1)\n        elif isinstance(out1, ndarray):\n            ans1 = out1\n            bshape = util.broadcast(x.shape, ans1.shape)\n            if bshape != ans1.shape:\n                raise ValueError(\n                    f\"non-broadcastable output operand with shape {ans1.shape} doesn't match the broadcast shape {bshape}\"\n                )\n        else:\n            compile_error(\"'out1' argument must be ndarray or None\")\n\n        if out2 is None:\n            T2 = type(fn(x.data[0])[1])\n            ans2 = routines.empty_like(x, dtype=T2)\n        elif isinstance(out2, ndarray):\n            ans2 = out2\n            bshape = util.broadcast(x.shape, ans2.shape)\n            if bshape != ans2.shape:\n                raise ValueError(\n                    f\"non-broadcastable output operand with shape {ans2.shape} doesn't match the broadcast shape {bshape}\"\n                )\n        else:\n            compile_error(\"'out2' argument must be ndarray or None\")\n\n        if ans1.ndim != ans2.ndim:\n            compile_error(\"ufunc output arguments have different dimensions\")\n\n        if ans1.shape != ans2.shape:\n            raise ValueError(\n                f\"non-broadcastable output operand with shape {ans1.shape} doesn't match the broadcast shape {ans2.shape}\"\n            )\n\n        if isinstance(where, bool):\n            if where:\n                functor = _Unary2Functor(self)\n                ndarray._loop((ans1, ans2, x), functor, check=False)\n        else:\n            where = routines.asarray(where)\n            functor = _Unary2WhereFunctor(self)\n            ndarray._loop((ans1, ans2, x, where), functor)\n\n        return ans1, ans2\n\n@tuple\nclass BinaryUFunc:\n    _op: F\n    identity: I\n    __name__: Literal[str]\n    F: type\n    I: type\n\n    def __new__(op: F,\n                name: Literal[str],\n                identity: I = None,\n                F: type,\n                I: type) -> BinaryUFunc[name, F, I]:\n        return superf(op, identity)\n\n    @property\n    def nin(self):\n        return 2\n\n    @property\n    def nout(self):\n        return 1\n\n    def _f(self, x, y, dtype: type = NoneType, dtype_out: type = NoneType):\n        if dtype is NoneType:\n            x1 = x\n            y1 = y\n        else:\n            x1 = util.cast(x, dtype)\n            y1 = util.cast(y, dtype)\n\n        z = self._op(x1, y1)\n\n        if dtype_out is NoneType:\n            return z\n        else:\n            return util.cast(z, dtype_out)\n\n    def __call__(self, x, y, out=None, where=True, dtype: type = NoneType):\n        fn = self._op\n        if __name__ == 'ldexp':\n            ct1, ct2 = decide_types_ldexp(x, y, dtype)\n        elif __name__ == 'copysign':\n            ct1, ct2 = decide_types_copysign(x, y, dtype)\n        else:\n            ct1, ct2 = decide_types(x, y, dtype)\n        CT1 = type(ct1)\n        CT2 = type(ct2)\n\n        x = routines.asarray(x)\n        y = routines.asarray(y)\n\n        if out is None:\n            if x.ndim == 0 and y.ndim == 0:\n                x0 = util.cast(x.data[0], CT1)\n                y0 = util.cast(y.data[0], CT2)\n                return fn(x0, y0)\n\n            out_shape = util.broadcast(x.shape, y.shape)\n            fcontig = x._should_transpose(\n            ) if x.ndim >= y.ndim else y._should_transpose()\n            RT = type(\n                self._f(util.cast(x.data[0], CT1),\n                        util.cast(y.data[0], CT2),\n                        dtype=dtype))\n            ans = ndarray(out_shape,\n                          Ptr[RT](util.count(out_shape)),\n                          fcontig=fcontig)\n        elif isinstance(out, ndarray):\n            ans = out\n            bshape = util.broadcast(x.shape, ans.shape)\n            if bshape != ans.shape:\n                raise ValueError(\n                    f\"non-broadcastable output operand with shape {ans.shape} doesn't match the broadcast shape {bshape}\"\n                )\n        else:\n            compile_error(\"'out' argument must be ndarray or None\")\n\n        if isinstance(where, bool):\n            if where:\n                if x.dtype is ans.dtype and y.dtype is ans.dtype:\n                    if _have_vectorized_loop(x.dtype, __name__):\n                        _apply_vectorized_loop_binary(x, y, ans, __name__)\n                        return ans\n\n                if x.ndim == 0:\n                    functor = _BinaryScalar1Functor(self, x.data[0], CT1, CT2,\n                                                    dtype)\n                    ndarray._loop((ans, y),\n                                  functor,\n                                  broadcast='first',\n                                  check=False)\n                elif y.ndim == 0:\n                    functor = _BinaryScalar2Functor(self, y.data[0], CT1, CT2,\n                                                    dtype)\n                    ndarray._loop((ans, x),\n                                  functor,\n                                  broadcast='first',\n                                  check=False)\n                else:\n                    functor = _BinaryFunctor(self, CT1, CT2, dtype)\n                    ndarray._loop((ans, x, y),\n                                  functor,\n                                  broadcast='first',\n                                  check=False)\n        else:\n            where = routines.asarray(where)\n            functor = _BinaryWhereFunctor(self, CT1, CT2, dtype)\n            ndarray._loop((ans, x, y, where), functor, broadcast='first')\n\n        return ans\n\n    def _reduce_all(self,\n                    array,\n                    dtype: type = NoneType,\n                    keepdims: Literal[bool] = False,\n                    initial=util._NoValue(),\n                    where=True):\n        if isinstance(where, bool):\n            if not where:\n                if keepdims:\n                    return routines.empty_like(array, dtype=dtype)\n                else:\n                    return dtype()\n        else:\n            where = routines.asarray(where)\n            util.broadcast(where.shape, array.shape)  # error check\n\n        n = array.size\n        p = array._data\n        fn = self._op\n\n        if initial is None:\n            ans: Optional[dtype] = None\n        else:\n            ans: dtype = initial\n\n        if array._is_contig and isinstance(where, bool):\n            i = 0\n            while i < n:\n                e = p[i]\n                e = util.cast(e, dtype)\n                if initial is None:\n                    if ans is None:\n                        ans = e\n                    else:\n                        ans_e: dtype = ans\n                        ans = util.cast(fn(ans_e, e), dtype)\n                else:\n                    ans = util.cast(fn(ans, e), dtype)\n                i += 1\n        else:\n            for idx in util.multirange(array.shape):\n                if not isinstance(where, bool):\n                    if not where._ptr(idx, broadcast=True)[0]:\n                        continue\n\n                e = array._ptr(idx)[0]\n                e = util.cast(e, dtype)\n\n                if initial is None:\n                    if ans is None:\n                        ans = e\n                    else:\n                        ans_e: dtype = ans\n                        ans = util.cast(fn(ans_e, e), dtype)\n                else:\n                    ans = util.cast(fn(ans, e), dtype)\n\n        if keepdims:\n            shape = (1, ) * static.len(array.shape)\n            out = routines.empty(shape, dtype=dtype)\n            out.data[0] = ans\n            return out\n        else:\n            return ans\n\n    def reduce(self,\n               array,\n               axis=0,\n               dtype: type = NoneType,\n               out=None,\n               keepdims: Literal[bool] = False,\n               initial=util._NoValue(),\n               where=True):\n        if not isinstance(array, ndarray):\n            return self.reduce(routines.asarray(array),\n                               axis=axis,\n                               dtype=dtype,\n                               out=out,\n                               keepdims=keepdims,\n                               initial=initial,\n                               where=where)\n\n        if isinstance(axis, int):\n            return self.reduce(array,\n                               axis=(axis, ),\n                               dtype=dtype,\n                               out=out,\n                               keepdims=keepdims,\n                               initial=initial,\n                               where=where)\n\n        if isinstance(initial, util._NoValue):\n            return self.reduce(array,\n                               axis=axis,\n                               dtype=dtype,\n                               out=out,\n                               keepdims=keepdims,\n                               initial=self.identity,\n                               where=where)\n\n        if not isinstance(where, bool) and initial is None:\n            compile_error(\n                \"reduction operation does not have an identity, so to use a where mask one has to specify 'initial'\"\n            )\n\n        if out is not None and not isinstance(out, ndarray):\n            compile_error(\"output must be an array\")\n\n        if dtype is NoneType:\n            if out is None:\n                return self.reduce(array,\n                                   axis=axis,\n                                   dtype=array.dtype,\n                                   out=out,\n                                   keepdims=keepdims,\n                                   initial=initial,\n                                   where=where)\n            else:\n                return self.reduce(array,\n                                   axis=axis,\n                                   dtype=out.dtype,\n                                   out=out,\n                                   keepdims=keepdims,\n                                   initial=initial,\n                                   where=where)\n\n        data = array.data\n        shape = array.shape\n        axis = tuple(util.normalize_axis_index(a, len(shape)) for a in axis)\n        if util.has_duplicate(axis):\n            raise ValueError(\"duplicate value in 'axis'\")\n\n        if initial is None and self.identity is None:\n            if array.size == 0:\n                raise ValueError(\n                    \"zero-size array to reduction operation add which has no identity\"\n                )\n\n        if static.len(axis) == static.len(shape):\n            ans = self._reduce_all(array,\n                                   dtype=dtype,\n                                   keepdims=keepdims,\n                                   initial=initial,\n                                   where=where)\n            if out is None:\n                return ans\n            else:\n                compile_error(\n                    \"cannot specify output when reducing over all axes\")\n\n        fn = self._op\n        new_shape = (0, ) * (static.len(shape) - static.len(axis))\n        idx_bound = (0, ) * static.len(axis)\n        mask = (False, ) * static.len(shape)\n        ptr_new_shape = Ptr[int](__ptr__(new_shape).as_byte())\n        ptr_idx_bound = Ptr[int](__ptr__(idx_bound).as_byte())\n        ptr_mask = Ptr[bool](__ptr__(mask).as_byte())\n\n        shape_size = 1\n        bound_size = 1\n        a = 0\n        b = 0\n\n        for i in range(len(shape)):\n            s = shape[i]\n\n            if i in axis:\n                bound_size *= s\n                ptr_idx_bound[a] = s\n                ptr_mask[i] = False\n                a += 1\n            else:\n                shape_size *= s\n                ptr_new_shape[b] = s\n                ptr_mask[i] = True\n                b += 1\n\n        if out is None:\n            result = routines.empty(new_shape, dtype=dtype)\n        else:\n            util.broadcast(array.shape, out.shape)  # error check\n            result = out\n\n        calc = True\n        if isinstance(where, bool):\n            calc = where\n        else:\n            where = routines.asarray(where)\n            util.broadcast(where.shape, array.shape)  # error check\n\n        if calc:\n            for t1 in util.multirange(new_shape):\n                if initial is None:\n                    ans: Optional[dtype] = None\n                else:\n                    ans: dtype = initial\n\n                for t2 in util.multirange(idx_bound):\n                    idx = util.reconstruct_index(t1, t2, mask)\n                    e = array._ptr(idx)[0]\n                    e = util.cast(e, dtype)\n\n                    if not isinstance(where, bool):\n                        if not where._ptr(idx, broadcast=True)[0]:\n                            continue\n\n                    if initial is None:\n                        if ans is None:\n                            ans = e\n                        else:\n                            ans_e: dtype = ans\n                            ans = util.cast(fn(ans_e, e), dtype)\n                    else:\n                        ans = util.cast(fn(ans, e), dtype)\n                result._ptr(t1, broadcast=(out is not None))[0] = ans\n\n        if keepdims:\n            ones = (1, ) * static.len(idx_bound)\n            ans_shape = util.reconstruct_index(new_shape, ones, mask)\n            return result.reshape(ans_shape)\n        else:\n            return result\n\n    def accumulate(self,\n                   array,\n                   axis: int = 0,\n                   dtype: type = NoneType,\n                   out=None):\n        if not isinstance(array, ndarray):\n            return self.accumulate(routines.asarray(array),\n                                   axis=axis,\n                                   dtype=dtype,\n                                   out=out)\n\n        if out is not None and not isinstance(out, ndarray):\n            compile_error(\"output must be an array\")\n\n        if dtype is NoneType:\n            if out is None:\n                return self.accumulate(array,\n                                       axis=axis,\n                                       dtype=array.dtype,\n                                       out=out)\n            else:\n                return self.accumulate(array,\n                                       axis=axis,\n                                       dtype=out.dtype,\n                                       out=out)\n\n        shape = array.shape\n        axis = util.normalize_axis_index(axis, len(shape))\n\n        if static.len(shape) == 0:\n            compile_error(\"cannot accumulate on a scalar\")\n\n        if out is None:\n            result = routines.empty(shape, dtype=dtype)\n        else:\n            util.broadcast(shape, out.shape)  # error check\n            result = out\n\n        fn = self._op\n        new_shape = (0, ) * (static.len(shape) - 1)\n        idx_bound = 0\n        mask = (False, ) * static.len(shape)\n        ptr_new_shape = Ptr[int](__ptr__(new_shape).as_byte())\n        ptr_mask = Ptr[bool](__ptr__(mask).as_byte())\n        b = 0\n\n        for i in range(len(shape)):\n            s = shape[i]\n\n            if i == axis:\n                idx_bound = s\n                ptr_mask[i] = False\n            else:\n                ptr_new_shape[b] = s\n                ptr_mask[i] = True\n                b += 1\n\n        if out is None:\n            result = routines.empty(array.shape, dtype=dtype)\n        else:\n            util.broadcast(array.shape, out.shape)  # error check\n            result = out\n\n        for t1 in util.multirange(new_shape):\n            idx = util.reconstruct_index(t1, (0, ), mask)\n            curr = array._ptr(idx)[0]\n            result._ptr(idx, broadcast=(out is not None))[0] = curr\n\n            for t2 in range(1, idx_bound):\n                idx = util.reconstruct_index(t1, (t2, ), mask)\n                e = array._ptr(idx)[0]\n                e = util.cast(e, dtype)\n                curr = util.cast(fn(curr, e), dtype)\n                result._ptr(idx, broadcast=(out is not None))[0] = curr\n\n        return result\n\n    def at(self, a, indices, b):\n        if not isinstance(a, ndarray):\n            return self.at(routines.asarray(a), indices, b)\n\n        if not isinstance(b, ndarray):\n            return self.at(a, indices, routines.asarray(b))\n\n        fn = self._op\n        i = 0\n\n        for idx in indices:\n            if static.len(b.shape) == 0:\n                a[idx] = fn(a[idx], b.data[0])\n            else:\n                a[idx] = fn(a[idx], b[i])\n            i += 1\n\n    def outer(self, A, B, dtype: type = NoneType):\n        if not isinstance(A, ndarray):\n            return self.outer(routines.asarray(A), B, dtype=dtype)\n\n        if not isinstance(B, ndarray):\n            return self.outer(A, routines.asarray(B), dtype=dtype)\n\n        r1, r2 = util.op_types(A.dtype, B.dtype)\n        R1 = type(r1)\n        R2 = type(r2)\n        sa = A.shape\n        sb = B.shape\n        sc = sa + sb\n        fn = self._op\n\n        if dtype is NoneType:\n            out = routines.empty(sc, dtype=type(fn(r1, r2)))\n        else:\n            out = routines.empty(sc, dtype=dtype)\n\n        for idx1 in util.multirange(sa):\n            for idx2 in util.multirange(sb):\n                xa = util.cast(A._ptr(idx1)[0], R1)\n                xb = util.cast(B._ptr(idx2)[0], R2)\n                out._ptr(idx1 + idx2)[0] = util.cast(fn(xa, xb), out.dtype)\n\n        return out\n"
  },
  {
    "path": "stdlib/numpy/util.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\nfrom .npdatetime import datetime64, timedelta64, _promote as dt_promote, \\\n                        _parse_datetime_type as dt_parse\n\n##############\n# Exceptions #\n##############\n\nclass AxisError(Exception):\n    def __init__(self, message: str = ''):\n        super().__init__(message)\n\nclass TooHardError(Exception):\n    def __init__(self, message: str = ''):\n        super().__init__(message)\n\n\n##############\n# Singletons #\n##############\n\n@tuple\nclass _NoValue:\n    pass\n\n\n###################\n# Tuples / Shapes #\n###################\n\ndef tuple_get(tup, idx):\n    p = Ptr[type(tup[0])](__ptr__(tup).as_byte())\n    return p[idx]\n\ndef tuple_set(tup, idx, elm, init = 0):\n    tup2 = (init,) * static.len(tup)\n    p = Ptr[type(init)](__ptr__(tup).as_byte())\n    q = Ptr[type(init)](__ptr__(tup2).as_byte())\n\n    i = 0\n    while i < static.len(tup):\n        if i == idx:\n            q[i] = elm\n        else:\n            q[i] = p[i]\n        i += 1\n\n    return tup2\n\ndef tuple_add(tup, idx, inc, init = 0):\n    tup2 = (init,) * static.len(tup)\n    p = Ptr[type(init)](__ptr__(tup).as_byte())\n    q = Ptr[type(init)](__ptr__(tup2).as_byte())\n\n    i = 0\n    while i < static.len(tup):\n        if i == idx:\n            q[i] = p[i] + inc\n        else:\n            q[i] = p[i]\n        i += 1\n\n    return tup2\n\ndef tuple_delete(tup, idx, init = 0):\n    tup2 = (init,) * (static.len(tup) - 1)\n    p = Ptr[type(init)](__ptr__(tup).as_byte())\n    q = Ptr[type(init)](__ptr__(tup2).as_byte())\n\n    i = 0\n    j = 0\n    while i < static.len(tup) - 1:\n        if j != idx:\n            q[i] = p[j]\n            i += 1\n        j += 1\n\n    return tup2\n\ndef tuple_insert(tup, idx, elm, init = 0):\n    tup2 = (init,) * (static.len(tup) + 1)\n    p = Ptr[type(init)](__ptr__(tup).as_byte())\n    q = Ptr[type(init)](__ptr__(tup2).as_byte())\n\n    i = 0\n    j = 0\n    while i < static.len(tup) + 1:\n        if i == idx:\n            q[i] = elm\n        else:\n            q[i] = p[j]\n            j += 1\n        i += 1\n\n    return tup2\n\ndef tuple_swap(tup, idx1, idx2, init = 0):\n    tup2 = tup\n    p = Ptr[type(init)](__ptr__(tup2).as_byte())\n    tmp = p[idx1]\n    p[idx1] = p[idx2]\n    p[idx2] = tmp\n    return tup2\n\ndef tuple_perm(tup, perm):\n    if perm is None:\n        return tup\n\n    if static.len(tup) != static.len(perm):\n        compile_error(\"[internal error] tuple_perm got different tuple lengths\")\n\n    if static.len(tup) <= 1:\n        return tup\n\n    return tuple(tuple_get(tup, p) for p in perm)\n\ndef tuple_perm_inv(perm):\n    iperm = (0,) * static.len(perm)\n    p = Ptr[int](__ptr__(perm).as_byte())\n    q = Ptr[int](__ptr__(iperm).as_byte())\n    for i in static.range(static.len(perm)):\n        q[p[i]] = i\n    return iperm\n\ndef tuple_apply(fn, tup1, tup2):\n    if static.len(tup1) != static.len(tup2):\n        compile_error(\"[internal error] tuple_apply got different tuple lengths\")\n\n    if static.len(tup1) == 0:\n        return ()\n\n    T = type(tup1[0])\n    tup3 = tup1\n    p1 = Ptr[T](__ptr__(tup1).as_byte())\n    p2 = Ptr[T](__ptr__(tup2).as_byte())\n    p3 = Ptr[T](__ptr__(tup3).as_byte())\n\n    for i in range(len(tup1)):\n        p3[i] = fn(p1[i], p2[i])\n\n    return tup3\n\ndef tuple_insert_tuple(tup, idx, ins, init = 0):\n    tup2 = (init,) * (static.len(tup) + static.len(ins))\n    p1 = Ptr[type(init)](__ptr__(tup).as_byte())\n    p2 = Ptr[type(init)](__ptr__(ins).as_byte())\n    q = Ptr[type(init)](__ptr__(tup2).as_byte())\n\n    j = 0\n\n    for i in range(idx):\n        q[j] = p1[i]\n        j += 1\n\n    for i in range(len(ins)):\n        q[j] = p2[i]\n        j += 1\n\n    for i in range(idx, len(tup)):\n        q[j] = p1[i]\n        j += 1\n\n    return tup2\n\ndef count(shape):\n    if static.len(shape) == 0:\n        return 1\n\n    total = 1\n    for i in range(static.len(shape)):\n        total *= shape[i]\n    return total\n\ndef tuple_range(n: Literal[int]):\n    if n <= 0:\n        return ()\n    return (*tuple_range(n - 1), n - 1)\n\ndef tuple_equal(t1, t2):\n    if static.len(t1) != static.len(t2):\n        return False\n    return t1 == t2\n\ndef broadcast(shape1, shape2):\n    D1: Literal[int] = static.len(shape1)\n    D2: Literal[int] = static.len(shape2)\n    Dmin: Literal[int] = D1 if D1 < D2 else D2\n    Dmax: Literal[int] = D1 if D1 > D2 else D2\n    Diff: Literal[int] = Dmax - Dmin\n    t1 = shape1[-Dmin:]\n    t2 = shape2[-Dmin:]\n\n    ans = (0,) * Dmax\n    p = Ptr[int](__ptr__(ans).as_byte())\n\n    for i in static.range(Diff):\n        if D1 > D2:\n            p[i] = shape1[i]\n        else:\n            p[i] = shape2[i]\n\n    for i in static.range(Dmin):\n        a = t1[i]\n        b = t2[i]\n        dim = a\n        if a == 1:\n            dim = b\n        elif b != 1 and a != b:\n            raise ValueError(f'operands could not be broadcast together with shapes {shape1} {shape2}')\n        p[i + Diff] = dim\n\n    return ans\n\n\n###########\n# Indexes #\n###########\n\ndef normalize_index(idx: int, axis: int, n: int):\n    idx0 = idx\n    if idx < 0:\n        idx += n\n    if idx < 0 or idx >= n:\n        raise IndexError(f'index {idx0} is out of bounds for axis {axis} with size {n}')\n    return idx\n\ndef normalize_axis_index(axis: int, ndim: int, prefix: str = ''):\n    if axis < -ndim or axis >= ndim:\n        raise AxisError(f\"{prefix}{': ' if prefix else ''}axis {axis} is out of bounds for array of dimension {ndim}\")\n    if axis < 0:\n        axis += ndim\n    return axis\n\ndef has_duplicate(t):\n    # use O(n^2) method since this ndim should be small\n    n = len(t)\n    for i in range(1, n):\n        for j in range(i):\n            if t[i] == t[j]:\n                return True\n    return False\n\ndef normalize_axis_tuple(axis, ndim: int, argname: str = '', allow_duplicates: bool = False):\n    if isinstance(axis, Tuple):\n        t = tuple(normalize_axis_index(ax, ndim, argname) for ax in axis)\n    elif isinstance(axis, List[int]):\n        t = [normalize_axis_index(ax, ndim, argname) for ax in axis]\n    elif isinstance(axis, int):\n        t = (normalize_axis_index(axis, ndim, argname),)\n    else:\n        compile_error(\"invalid axis argument: type must be int, tuple of int, or list of int\")\n\n    if not allow_duplicates and has_duplicate(t):\n        if argname:\n            raise ValueError(f\"repeated axis in `{argname}` argument\")\n        else:\n            raise ValueError(\"repeated axis\")\n\n    return t\n\ndef reconstruct_index(t1, t2, mask):\n    if static.len(t1) + static.len(t2) != static.len(mask):\n        compile_error(\"[internal error] bad index reconstruction\")\n\n    t = (0,) * static.len(mask)\n    p1 = Ptr[int](__ptr__(t1).as_byte())\n    p2 = Ptr[int](__ptr__(t2).as_byte())\n    p = Ptr[int](__ptr__(t).as_byte())\n    m = Ptr[bool](__ptr__(mask).as_byte())\n    k1, k2 = 0, 0\n    for i in range(static.len(mask)):\n        if m[i]:\n            p[i] = p1[k1]\n            k1 += 1\n        else:\n            p[i] = p2[k2]\n            k2 += 1\n    return t\n\ndef index_to_coords(index: int, limits):\n    if static.len(limits) == 0:\n        return ()\n\n    coords = (0,) * static.len(limits)\n    pcoords = Ptr[int](__ptr__(coords).as_byte())\n\n    for i in range(static.len(coords) - 1, -1, -1):\n        d, m = divmod(index, limits[i])\n        pcoords[i] = m\n        index = d\n\n    return coords\n\ndef index_to_fcoords(index: int, limits):\n    if static.len(limits) == 0:\n        return ()\n\n    coords = (0,) * static.len(limits)\n    pcoords = Ptr[int](__ptr__(coords).as_byte())\n\n    for i in range(static.len(coords)):\n        d, m = divmod(index, limits[i])\n        pcoords[i] = m\n        index = d\n\n    return coords\n\ndef coords_to_index(coords: S, limits: S, S: type):\n    s = 1\n    idx = 0\n\n    for i in static.range(static.len(coords) - 1, -1, -1):\n        idx += coords[i] * s\n        s *= limits[i]\n\n    return idx\n\ndef coords_to_findex(coords: S, limits: S, S: type):\n    s = 1\n    idx = 0\n\n    for i in static.range(static.len(coords)):\n        idx += coords[i] * s\n        s *= limits[i]\n\n    return idx\n\n\n#######################\n# Sorting / Searching #\n#######################\n\ndef sort(p: Ptr[T], n: int, key, T: type):\n    from algorithms.pdqsort import pdq_sort_array\n    pdq_sort_array(Array(p, n), n, key)\n\ndef sort_by_stride(shape, strides):\n    if static.len(strides) <= 1:\n        return shape, strides\n\n    strides_sorted = strides\n    shape_sorted = shape\n\n    pstrides = Ptr[int](__ptr__(strides_sorted).as_byte())\n    pshape = Ptr[int](__ptr__(shape_sorted).as_byte())\n    n = len(strides)\n\n    while True:\n        swapped = False\n        for i in range(1, n):\n            if abs(pstrides[i - 1]) < abs(pstrides[i]):\n                tmp = pstrides[i]\n                pstrides[i] = pstrides[i - 1]\n                pstrides[i - 1] = tmp\n\n                tmp = pshape[i]\n                pshape[i] = pshape[i - 1]\n                pshape[i - 1] = tmp\n\n                swapped = True\n        if not swapped:\n            break\n        n -= 1\n\n    return shape_sorted, strides_sorted\n\n# Following is adapted from Numba - github.com/numba/numba\n\ndef _partition(A, low: int, high: int):\n    mid = (low + high) >> 1\n\n    if A[mid] < A[low]:\n        A[low], A[mid] = A[mid], A[low]\n    if A[high] < A[mid]:\n        A[high], A[mid] = A[mid], A[high]\n        if A[mid] < A[low]:\n            A[low], A[mid] = A[mid], A[low]\n    pivot = A[mid]\n\n    A[high], A[mid] = A[mid], A[high]\n\n    i = low\n    for j in range(low, high):\n        if A[j] <= pivot:\n            A[i], A[j] = A[j], A[i]\n            i += 1\n\n    A[i], A[high] = A[high], A[i]\n    return i\n\ndef _select(arry, k: int, low: int, high: int):\n    i = _partition(arry, low, high)\n    while i != k:\n        if i < k:\n            low = i + 1\n            i = _partition(arry, low, high)\n        else:\n            high = i - 1\n            i = _partition(arry, low, high)\n    return arry[k]\n\ndef _select_two(arry, k: int, low: int, high: int):\n    while True:\n        # assert high > low  # by construction\n        i = _partition(arry, low, high)\n        if i < k:\n            low = i + 1\n        elif i > k + 1:\n            high = i - 1\n        elif i == k:\n            _select(arry, k + 1, i + 1, high)\n            break\n        else:  # i == k + 1\n            _select(arry, k, low, i - 1)\n            break\n\n    return arry[k], arry[k + 1]\n\ndef median(arr, n: int):\n    low = 0\n    high = n - 1\n    half = n >> 1\n    if n & 1 == 0:\n        a, b = _select_two(arr, half - 1, low, high)\n        return (a, b)\n    else:\n        a = _select(arr, half, low, high)\n        return (a, a)\n\n\n#############\n# Iterators #\n#############\n\ndef multirange(limits):\n    if static.len(limits) == 0:\n        yield ()\n    elif static.len(limits) == 1:\n        x = limits[0]\n        for i in range(x):\n            yield (i,)\n    elif static.len(limits) == 2:\n        x, y = limits\n        for i in range(x):\n            for j in range(y):\n                yield (i, j)\n    elif static.len(limits) == 3:\n        x, y, z = limits\n        for i in range(x):\n            for j in range(y):\n                for k in range(z):\n                    yield (i, j, k)\n    elif static.len(limits) == 4:\n        n0, n1, n2, n3 = limits\n        for i0 in range(n0):\n            for i1 in range(n1):\n                for i2 in range(n2):\n                    for i3 in range(n3):\n                        yield (i0, i1, i2, i3)\n    elif static.len(limits) == 5:\n        n0, n1, n2, n3, n4 = limits\n        for i0 in range(n0):\n            for i1 in range(n1):\n                for i2 in range(n2):\n                    for i3 in range(n3):\n                        for i4 in range(n4):\n                            yield (i0, i1, i2, i3, i4)\n    elif static.len(limits) == 6:\n        n0, n1, n2, n3, n4, n5 = limits\n        for i0 in range(n0):\n            for i1 in range(n1):\n                for i2 in range(n2):\n                    for i3 in range(n3):\n                        for i4 in range(n4):\n                            for i5 in range(n5):\n                                yield (i0, i1, i2, i3, i4, i5)\n    else:\n        n = limits[0]\n        for i in range(n):\n            for idx in multirange(limits[1:]):\n                yield (i,) + idx\n\ndef fmultirange(limits):\n    if static.len(limits) == 0:\n        yield ()\n    elif static.len(limits) == 1:\n        x = limits[0]\n        for i in range(x):\n            yield (i,)\n    elif static.len(limits) == 2:\n        x, y = limits\n        for j in range(y):\n            for i in range(x):\n                yield (i, j)\n    elif static.len(limits) == 3:\n        x, y, z = limits\n        for k in range(z):\n            for j in range(y):\n                for i in range(x):\n                    yield (i, j, k)\n    elif static.len(limits) == 4:\n        n0, n1, n2, n3 = limits\n        for i3 in range(n3):\n            for i2 in range(n2):\n                for i1 in range(n1):\n                    for i0 in range(n0):\n                        yield (i0, i1, i2, i3)\n    elif static.len(limits) == 5:\n        n0, n1, n2, n3, n4 = limits\n        for i4 in range(n4):\n            for i3 in range(n3):\n                for i2 in range(n2):\n                    for i1 in range(n1):\n                        for i0 in range(n0):\n                            yield (i0, i1, i2, i3, i4)\n    elif static.len(limits) == 6:\n        n0, n1, n2, n3, n4, n5 = limits\n        for i5 in range(n5):\n            for i4 in range(n4):\n                for i3 in range(n3):\n                    for i2 in range(n2):\n                        for i1 in range(n1):\n                            for i0 in range(n0):\n                                yield (i0, i1, i2, i3, i4, i5)\n    else:\n        n = limits[0]\n        for idx in fmultirange(limits[1:]):\n            for i in range(n):\n                yield idx + (i,)\n\n\n########\n# Math #\n########\n\n@pure\n@llvm\ndef noop(x: T, D: type, T: type) -> D:\n    ret {=D} %x\n\n@pure\n@llvm\ndef zero(T: type) -> T:\n    ret {=T} zeroinitializer\n\n@pure\n@llvm\ndef inf64() -> float:\n    ret double 0x7FF0000000000000\n\n@pure\n@llvm\ndef inf32() -> float32:\n    ret float 0x7FF0000000000000\n\n@pure\n@llvm\ndef inf16() -> float16:\n    ret half 0x7FF0000000000000\n\ndef inf(T: type):\n    if T is float:\n        return inf64()\n    elif T is float32:\n        return inf32()\n    elif T is float16:\n        return inf16()\n\n@pure\n@llvm\ndef nan64() -> float:\n    ret double 0x7FF8000000000000\n\n@pure\n@llvm\ndef nan32() -> float32:\n    ret float 0x7FF8000000000000\n\n@pure\n@llvm\ndef nan16() -> float16:\n    ret half 0x7FF8000000000000\n\ndef nan(T: type):\n    if T is float:\n        return nan64()\n    elif T is float32:\n        return nan32()\n    elif T is float16:\n        return nan16()\n\n@pure\n@llvm\ndef minnum64() -> float:\n    ret double 0x10000000000000\n\n@pure\n@llvm\ndef minnum32() -> float32:\n    ret float 0x3810000000000000\n\n@pure\n@llvm\ndef minnum16() -> float16:\n    ret half 0xHFBFF\n\ndef minnum(T: type):\n    if T is float:\n        return minnum64()\n    elif T is float32:\n        return minnum32()\n    elif T is float16:\n        return minnum16()\n\n@pure\n@llvm\ndef maxnum64() -> float:\n    ret double 0x7FEFFFFFFFFFFFFF\n\n@pure\n@llvm\ndef maxnum32() -> float32:\n    ret float 0x47EFFFFFE0000000\n\n@pure\n@llvm\ndef maxnum16() -> float16:\n    ret half 0xH7BFF\n\ndef maxnum(T: type):\n    if T is float:\n        return maxnum64()\n    elif T is float32:\n        return maxnum32()\n    elif T is float16:\n        return maxnum16()\n\n@pure\n@llvm\ndef eps64() -> float:\n    ret double 0x3CB0000000000000\n\n@pure\n@llvm\ndef eps32() -> float32:\n    ret float 0x3E80000000000000\n\n@pure\n@llvm\ndef eps16() -> float16:\n    ret half 0xH1400\n\ndef eps(T: type):\n    if T is float:\n        return eps64()\n    elif T is float32:\n        return eps32()\n    elif T is float16:\n        return eps16()\n\ndef mantdig64():\n    return 53\n\ndef mantdig32():\n    return 24\n\ndef mantdig16():\n    return 11\n\ndef mantdig(T: type):\n    if T is float:\n        return mantdig64()\n    elif T is float32:\n        return mantdig32()\n    elif T is float16:\n        return mantdig16()\n\ndef maxexp64():\n    return 1024\n\ndef maxexp32():\n    return 128\n\ndef maxexp16():\n    return 16\n\ndef maxexp(T: type):\n    if T is float:\n        return maxexp64()\n    elif T is float32:\n        return maxexp32()\n    elif T is float16:\n        return maxexp16()\n\n@pure\n@llvm\ndef bitcast(x: T, D: type, T: type) -> D:\n    %y = bitcast {=T} %x to {=D}\n    ret {=D} %y\n\n@pure\n@llvm\ndef uitofp(x: T, D: type, T: type) -> D:\n    %y = uitofp {=T} %x to {=D}\n    ret {=D} %y\n\n@pure\n@llvm\ndef sitofp(x: T, D: type, T: type) -> D:\n    %y = sitofp {=T} %x to {=D}\n    ret {=D} %y\n\n@pure\n@llvm\ndef fptoui(x: T, D: type, T: type) -> D:\n    %y = fptoui {=T} %x to {=D}\n    ret {=D} %y\n\n@pure\n@llvm\ndef fptosi(x: T, D: type, T: type) -> D:\n    %y = fptosi {=T} %x to {=D}\n    ret {=D} %y\n\n@pure\n@llvm\ndef zext(x: T, D: type, T: type) -> D:\n    %y = zext {=T} %x to {=D}\n    ret {=D} %y\n\n@pure\n@llvm\ndef sext(x: T, D: type, T: type) -> D:\n    %y = sext {=T} %x to {=D}\n    ret {=D} %y\n\n@pure\n@llvm\ndef itrunc(x: T, D: type, T: type) -> D:\n    %y = trunc {=T} %x to {=D}\n    ret {=D} %y\n\n@pure\n@llvm\ndef fpext(x: T, D: type, T: type) -> D:\n    %y = fpext {=T} %x to {=D}\n    ret {=D} %y\n\n@pure\n@llvm\ndef fptrunc(x: T, D: type, T: type) -> D:\n    %y = fptrunc {=T} %x to {=D}\n    ret {=D} %y\n\n@pure\n@llvm\ndef fmin64(x: float, y: float) -> float:\n    declare double @llvm.minimum.f64(double, double)\n    %z = call double @llvm.minimum.f64(double %x, double %y)\n    ret double %z\n\n@pure\n@llvm\ndef fmin32(x: float32, y: float32) -> float32:\n    declare float @llvm.minimum.f32(float, float)\n    %z = call float @llvm.minimum.f32(float %x, float %y)\n    ret float %z\n\n@pure\n@llvm\ndef fmin16(x: float16, y: float16) -> float16:\n    declare half @llvm.minimum.f16(half, half)\n    %z = call half @llvm.minimum.f16(half %x, half %y)\n    ret half %z\n\ndef fmin(x, y):\n    if isinstance(x, float) and isinstance(y, float):\n        return fmin64(x, y)\n    elif isinstance(x, float32) and isinstance(y, float32):\n        return fmin32(x, y)\n    elif isinstance(x, float16) and isinstance(y, float16):\n        return fmin16(x, y)\n\n@pure\n@llvm\ndef fmax64(x: float, y: float) -> float:\n    declare double @llvm.maximum.f64(double, double)\n    %z = call double @llvm.maximum.f64(double %x, double %y)\n    ret double %z\n\n@pure\n@llvm\ndef fmax32(x: float32, y: float32) -> float32:\n    declare float @llvm.maximum.f32(float, float)\n    %z = call float @llvm.maximum.f32(float %x, float %y)\n    ret float %z\n\n@pure\n@llvm\ndef fmax16(x: float16, y: float16) -> float16:\n    declare half @llvm.maximum.f16(half, half)\n    %z = call half @llvm.maximum.f16(half %x, half %y)\n    ret half %z\n\ndef fmax(x, y):\n    if isinstance(x, float) and isinstance(y, float):\n        return fmax64(x, y)\n    elif isinstance(x, float32) and isinstance(y, float32):\n        return fmax32(x, y)\n    elif isinstance(x, float16) and isinstance(y, float16):\n        return fmax16(x, y)\n\n@pure\n@llvm\ndef fminnum64(x: float, y: float) -> float:\n    declare double @llvm.minimumnum.f64(double, double)\n    %z = call double @llvm.minimumnum.f64(double %x, double %y)\n    ret double %z\n\n@pure\n@llvm\ndef fminnum32(x: float32, y: float32) -> float32:\n    declare float @llvm.minimumnum.f32(float, float)\n    %z = call float @llvm.minimumnum.f32(float %x, float %y)\n    ret float %z\n\n@pure\n@llvm\ndef fminnum16(x: float16, y: float16) -> float16:\n    declare half @llvm.minimumnum.f16(half, half)\n    %z = call half @llvm.minimumnum.f16(half %x, half %y)\n    ret half %z\n\ndef fminnum(x, y):\n    if isinstance(x, float) and isinstance(y, float):\n        return fminnum64(x, y)\n    elif isinstance(x, float32) and isinstance(y, float32):\n        return fminnum32(x, y)\n    elif isinstance(x, float16) and isinstance(y, float16):\n        return fminnum16(x, y)\n\n@pure\n@llvm\ndef fmaxnum64(x: float, y: float) -> float:\n    declare double @llvm.maximumnum.f64(double, double)\n    %z = call double @llvm.maximumnum.f64(double %x, double %y)\n    ret double %z\n\n@pure\n@llvm\ndef fmaxnum32(x: float32, y: float32) -> float32:\n    declare float @llvm.maximumnum.f32(float, float)\n    %z = call float @llvm.maximumnum.f32(float %x, float %y)\n    ret float %z\n\n@pure\n@llvm\ndef fmaxnum16(x: float16, y: float16) -> float16:\n    declare half @llvm.maximumnum.f16(half, half)\n    %z = call half @llvm.maximumnum.f16(half %x, half %y)\n    ret half %z\n\ndef fmaxnum(x, y):\n    if isinstance(x, float) and isinstance(y, float):\n        return fmaxnum64(x, y)\n    elif isinstance(x, float32) and isinstance(y, float32):\n        return fmaxnum32(x, y)\n    elif isinstance(x, float16) and isinstance(y, float16):\n        return fmaxnum16(x, y)\n\ndef _fp16_op_via_fp32(x: float16, op) -> float16:\n    return fptrunc(op(fpext(x, float32)), float16)\n\ndef _fp16_op_via_fp32_2(x: float16, y: float16, op) -> float16:\n    return fptrunc(op(fpext(x, float32), fpext(y, float32)), float16)\n\n@pure\n@llvm\ndef bswap(x: T, T: type) -> T:\n    declare {=T} @llvm.bswap.{=T}({=T})\n    %y = call {=T} @llvm.bswap.{=T}({=T} %x)\n    ret {=T} %y\n\n@pure\n@llvm\ndef isinf64(x: float) -> bool:\n    declare double @llvm.fabs.f64(double)\n    %a = call double @llvm.fabs.f64(double %x)\n    %y = fcmp oeq double %a, 0x7FF0000000000000\n    %z = zext i1 %y to i8\n    ret i8 %z\n\n@pure\n@llvm\ndef isinf32(x: float32) -> bool:\n    declare float @llvm.fabs.f32(float)\n    %a = call float @llvm.fabs.f32(float %x)\n    %y = fcmp oeq float %a, 0x7FF0000000000000\n    %z = zext i1 %y to i8\n    ret i8 %z\n\n@pure\n@llvm\ndef isinf16(x: float16) -> bool:\n    declare half @llvm.fabs.f16(half)\n    %a = call half @llvm.fabs.f16(half %x)\n    %y = fcmp oeq half %a, 0x7FF0000000000000\n    %z = zext i1 %y to i8\n    ret i8 %z\n\ndef isinf(x):\n    if isinstance(x, float):\n        return isinf64(x)\n    elif isinstance(x, float32):\n        return isinf32(x)\n    elif isinstance(x, float16):\n        return isinf16(x)\n\n@pure\n@llvm\ndef isnan64(x: float) -> bool:\n    %y = fcmp uno double %x, 0.000000e+00\n    %z = zext i1 %y to i8\n    ret i8 %z\n\n@pure\n@llvm\ndef isnan32(x: float32) -> bool:\n    %y = fcmp uno float %x, 0.000000e+00\n    %z = zext i1 %y to i8\n    ret i8 %z\n\n@pure\n@llvm\ndef isnan16(x: float16) -> bool:\n    %y = fcmp uno half %x, 0.000000e+00\n    %z = zext i1 %y to i8\n    ret i8 %z\n\ndef isnan(x):\n    if isinstance(x, float):\n        return isnan64(x)\n    elif isinstance(x, float32):\n        return isnan32(x)\n    elif isinstance(x, float16):\n        return isnan16(x)\n\n@pure\n@llvm\ndef signbit64(x: float) -> bool:\n    %n = bitcast double %x to i64\n    %s = icmp slt i64 %n, 0\n    %b = zext i1 %s to i8\n    ret i8 %b\n\n@pure\n@llvm\ndef signbit32(x: float32) -> bool:\n    %n = bitcast float %x to i32\n    %s = icmp slt i32 %n, 0\n    %b = zext i1 %s to i8\n    ret i8 %b\n\n@pure\n@llvm\ndef signbit16(x: float16) -> bool:\n    %n = bitcast half %x to i16\n    %s = icmp slt i16 %n, 0\n    %b = zext i1 %s to i8\n    ret i8 %b\n\ndef signbit(x):\n    if isinstance(x, float):\n        return signbit64(x)\n    elif isinstance(x, float32):\n        return signbit32(x)\n    elif isinstance(x, float16):\n        return signbit16(x)\n\n@pure\n@llvm\ndef fabs64(x: float) -> float:\n    declare double @llvm.fabs.f64(double)\n    %y = call double @llvm.fabs.f64(double %x)\n    ret double %y\n\n@pure\n@llvm\ndef fabs32(x: float32) -> float32:\n    declare float @llvm.fabs.f32(float)\n    %y = call float @llvm.fabs.f32(float %x)\n    ret float %y\n\n@pure\n@llvm\ndef fabs16(x: float16) -> float16:\n    declare half @llvm.fabs.f16(half)\n    %y = call half @llvm.fabs.f16(half %x)\n    ret half %y\n\ndef fabs(x):\n    if isinstance(x, float):\n        return fabs64(x)\n    elif isinstance(x, float32):\n        return fabs32(x)\n    elif isinstance(x, float16):\n        return fabs16(x)\n\n@pure\n@llvm\ndef _ordered_not_equal64(x: float, y: float) -> bool:\n    %tmp = fcmp one double %x, %y\n    %res = zext i1 %tmp to i8\n    ret i8 %res\n\n@pure\n@llvm\ndef _ordered_not_equal32(x: float32, y: float32) -> bool:\n    %tmp = fcmp one float %x, %y\n    %res = zext i1 %tmp to i8\n    ret i8 %res\n\n@pure\n@llvm\ndef _ordered_not_equal16(x: float16, y: float16) -> bool:\n    %tmp = fcmp one half %x, %y\n    %res = zext i1 %tmp to i8\n    ret i8 %res\n\ndef isfinite64(x: float):\n    return _ordered_not_equal64(fabs64(x), inf64())\n\ndef isfinite32(x: float32):\n    return _ordered_not_equal32(fabs32(x), inf32())\n\ndef isfinite16(x: float16):\n    return _ordered_not_equal16(fabs16(x), inf16())\n\ndef isfinite(x):\n    if isinstance(x, float):\n        return isfinite64(x)\n    elif isinstance(x, float32):\n        return isfinite32(x)\n    elif isinstance(x, float16):\n        return isfinite16(x)\n\n@pure\n@llvm\ndef rint64(x: float) -> float:\n    declare double @llvm.rint.f64(double)\n    %y = call double @llvm.rint.f64(double %x)\n    ret double %y\n\n@pure\n@llvm\ndef rint32(x: float32) -> float32:\n    declare float @llvm.rint.f32(float)\n    %y = call float @llvm.rint.f32(float %x)\n    ret float %y\n\n@pure\n@llvm\ndef rint16(x: float16) -> float16:\n    declare half @llvm.rint.f16(half)\n    %y = call half @llvm.rint.f16(half %x)\n    ret half %y\n\ndef rint(x):\n    if isinstance(x, float):\n        return rint64(x)\n    elif isinstance(x, float32):\n        return rint32(x)\n    elif isinstance(x, float16):\n        return rint16(x)\n\n@pure\n@llvm\ndef floor64(x: float) -> float:\n    declare double @llvm.floor.f64(double)\n    %y = call double @llvm.floor.f64(double %x)\n    ret double %y\n\n@pure\n@llvm\ndef floor32(x: float32) -> float32:\n    declare float @llvm.floor.f32(float)\n    %y = call float @llvm.floor.f32(float %x)\n    ret float %y\n\n@pure\n@llvm\ndef floor16(x: float16) -> float16:\n    declare half @llvm.floor.f16(half)\n    %y = call half @llvm.floor.f16(half %x)\n    ret half %y\n\ndef floor(x):\n    if isinstance(x, float):\n        return floor64(x)\n    elif isinstance(x, float32):\n        return floor32(x)\n    elif isinstance(x, float16):\n        return floor16(x)\n\n@pure\n@llvm\ndef ceil64(x: float) -> float:\n    declare double @llvm.ceil.f64(double)\n    %y = call double @llvm.ceil.f64(double %x)\n    ret double %y\n\n@pure\n@llvm\ndef ceil32(x: float32) -> float32:\n    declare float @llvm.ceil.f32(float)\n    %y = call float @llvm.ceil.f32(float %x)\n    ret float %y\n\n@pure\n@llvm\ndef ceil16(x: float16) -> float16:\n    declare half @llvm.ceil.f16(half)\n    %y = call half @llvm.ceil.f16(half %x)\n    ret half %y\n\ndef ceil(x):\n    if isinstance(x, float):\n        return ceil64(x)\n    elif isinstance(x, float32):\n        return ceil32(x)\n    elif isinstance(x, float16):\n        return ceil16(x)\n\n@pure\n@llvm\ndef trunc64(x: float) -> float:\n    declare double @llvm.trunc.f64(double)\n    %y = call double @llvm.trunc.f64(double %x)\n    ret double %y\n\n@pure\n@llvm\ndef trunc32(x: float32) -> float32:\n    declare float @llvm.trunc.f32(float)\n    %y = call float @llvm.trunc.f32(float %x)\n    ret float %y\n\n@pure\n@llvm\ndef trunc16(x: float16) -> float16:\n    declare half @llvm.trunc.f16(half)\n    %y = call half @llvm.trunc.f16(half %x)\n    ret half %y\n\ndef trunc(x):\n    if isinstance(x, float):\n        return trunc64(x)\n    elif isinstance(x, float32):\n        return trunc32(x)\n    elif isinstance(x, float16):\n        return trunc16(x)\n\n@pure\n@llvm\ndef exp64(x: float) -> float:\n    declare double @llvm.exp.f64(double)\n    %y = call double @llvm.exp.f64(double %x)\n    ret double %y\n\n@pure\n@llvm\ndef exp32(x: float32) -> float32:\n    declare float @llvm.exp.f32(float)\n    %y = call float @llvm.exp.f32(float %x)\n    ret float %y\n\n@pure\n@llvm\ndef exp16(x: float16) -> float16:\n    declare half @llvm.exp.f16(half)\n    %y = call half @llvm.exp.f16(half %x)\n    ret half %y\n\ndef exp(x):\n    if isinstance(x, float):\n        return exp64(x)\n    elif isinstance(x, float32):\n        return exp32(x)\n    elif isinstance(x, float16):\n        return exp16(x)\n\n@pure\n@llvm\ndef exp2_64(x: float) -> float:\n    declare double @llvm.exp2.f64(double)\n    %y = call double @llvm.exp2.f64(double %x)\n    ret double %y\n\n@pure\n@llvm\ndef exp2_32(x: float32) -> float32:\n    declare float @llvm.exp2.f32(float)\n    %y = call float @llvm.exp2.f32(float %x)\n    ret float %y\n\n@pure\n@llvm\ndef exp2_16(x: float16) -> float16:\n    declare half @llvm.exp2.f16(half)\n    %y = call half @llvm.exp2.f16(half %x)\n    ret half %y\n\ndef exp2(x):\n    if isinstance(x, float):\n        return exp2_64(x)\n    elif isinstance(x, float32):\n        return exp2_32(x)\n    elif isinstance(x, float16):\n        return exp2_16(x)\n\ndef expm1_64(x: float):\n    return _C.expm1(x)\n\ndef expm1_32(x: float32):\n    return _C.expm1f(x)\n\ndef expm1(x):\n    if isinstance(x, float):\n        return expm1_64(x)\n    elif isinstance(x, float32):\n        return expm1_32(x)\n    elif isinstance(x, float16):\n        return _fp16_op_via_fp32(x, expm1_32)\n\n@pure\n@llvm\ndef log64(x: float) -> float:\n    declare double @llvm.log.f64(double)\n    %y = call double @llvm.log.f64(double %x)\n    ret double %y\n\n@pure\n@llvm\ndef log32(x: float32) -> float32:\n    declare float @llvm.log.f32(float)\n    %y = call float @llvm.log.f32(float %x)\n    ret float %y\n\n@pure\n@llvm\ndef log16(x: float16) -> float16:\n    declare half @llvm.log.f16(half)\n    %y = call half @llvm.log.f16(half %x)\n    ret half %y\n\ndef log(x):\n    if isinstance(x, float):\n        return log64(x)\n    elif isinstance(x, float32):\n        return log32(x)\n    elif isinstance(x, float16):\n        return log16(x)\n\n@pure\n@llvm\ndef log2_64(x: float) -> float:\n    declare double @llvm.log2.f64(double)\n    %y = call double @llvm.log2.f64(double %x)\n    ret double %y\n\n@pure\n@llvm\ndef log2_32(x: float32) -> float32:\n    declare float @llvm.log2.f32(float)\n    %y = call float @llvm.log2.f32(float %x)\n    ret float %y\n\n@pure\n@llvm\ndef log2_16(x: float16) -> float16:\n    declare half @llvm.log2.f16(half)\n    %y = call half @llvm.log2.f16(half %x)\n    ret half %y\n\ndef log2(x):\n    if isinstance(x, float):\n        return log2_64(x)\n    elif isinstance(x, float32):\n        return log2_32(x)\n    elif isinstance(x, float16):\n        return log2_16(x)\n\n@pure\n@llvm\ndef log10_64(x: float) -> float:\n    declare double @llvm.log10.f64(double)\n    %y = call double @llvm.log10.f64(double %x)\n    ret double %y\n\n@pure\n@llvm\ndef log10_32(x: float32) -> float32:\n    declare float @llvm.log10.f32(float)\n    %y = call float @llvm.log10.f32(float %x)\n    ret float %y\n\n@pure\n@llvm\ndef log10_16(x: float16) -> float16:\n    declare half @llvm.log10.f16(half)\n    %y = call half @llvm.log10.f16(half %x)\n    ret half %y\n\ndef log10(x):\n    if isinstance(x, float):\n        return log10_64(x)\n    elif isinstance(x, float32):\n        return log10_32(x)\n    elif isinstance(x, float16):\n        return log10_16(x)\n\ndef log1p64(x: float):\n    return _C.log1p(x)\n\ndef log1p32(x: float32):\n    return _C.log1pf(x)\n\ndef log1p(x):\n    if isinstance(x, float):\n        return log1p64(x)\n    elif isinstance(x, float32):\n        return log1p32(x)\n    elif isinstance(x, float16):\n        return _fp16_op_via_fp32(x, log1p32)\n\n@pure\n@llvm\ndef sqrt64(x: float) -> float:\n    declare double @llvm.sqrt.f64(double)\n    %y = call double @llvm.sqrt.f64(double %x)\n    ret double %y\n\n@pure\n@llvm\ndef sqrt32(x: float32) -> float32:\n    declare float @llvm.sqrt.f32(float)\n    %y = call float @llvm.sqrt.f32(float %x)\n    ret float %y\n\n@pure\n@llvm\ndef sqrt16(x: float16) -> float16:\n    declare half @llvm.sqrt.f16(half)\n    %y = call half @llvm.sqrt.f16(half %x)\n    ret half %y\n\ndef sqrt(x):\n    if isinstance(x, float):\n        return sqrt64(x)\n    elif isinstance(x, float32):\n        return sqrt32(x)\n    elif isinstance(x, float16):\n        return sqrt16(x)\n\ndef cbrt64(x: float):\n    return _C.cbrt(x)\n\ndef cbrt32(x: float32):\n    return _C.cbrtf(x)\n\ndef cbrt(x):\n    if isinstance(x, float):\n        return cbrt64(x)\n    elif isinstance(x, float32):\n        return cbrt32(x)\n    elif isinstance(x, float16):\n        return _fp16_op_via_fp32(x, cbrt32)\n\n@pure\n@llvm\ndef sin64(x: float) -> float:\n    declare double @llvm.sin.f64(double)\n    %y = call double @llvm.sin.f64(double %x)\n    ret double %y\n\n@pure\n@llvm\ndef sin32(x: float32) -> float32:\n    declare float @llvm.sin.f32(float)\n    %y = call float @llvm.sin.f32(float %x)\n    ret float %y\n\n@pure\n@llvm\ndef sin16(x: float16) -> float16:\n    declare half @llvm.sin.f16(half)\n    %y = call half @llvm.sin.f16(half %x)\n    ret half %y\n\ndef sin(x):\n    if isinstance(x, float):\n        return sin64(x)\n    elif isinstance(x, float32):\n        return sin32(x)\n    elif isinstance(x, float16):\n        return sin16(x)\n\n@pure\n@llvm\ndef cos64(x: float) -> float:\n    declare double @llvm.cos.f64(double)\n    %y = call double @llvm.cos.f64(double %x)\n    ret double %y\n\n@pure\n@llvm\ndef cos32(x: float32) -> float32:\n    declare float @llvm.cos.f32(float)\n    %y = call float @llvm.cos.f32(float %x)\n    ret float %y\n\n@pure\n@llvm\ndef cos16(x: float16) -> float16:\n    declare half @llvm.cos.f16(half)\n    %y = call half @llvm.cos.f16(half %x)\n    ret half %y\n\ndef cos(x):\n    if isinstance(x, float):\n        return cos64(x)\n    elif isinstance(x, float32):\n        return cos32(x)\n    elif isinstance(x, float16):\n        return cos16(x)\n\ndef tan64(x: float):\n    return _C.tan(x)\n\ndef tan32(x: float32):\n    return _C.tanf(x)\n\ndef tan(x):\n    if isinstance(x, float):\n        return tan64(x)\n    elif isinstance(x, float32):\n        return tan32(x)\n    elif isinstance(x, float16):\n        return _fp16_op_via_fp32(x, tan32)\n\ndef asin64(x: float):\n    return _C.asin(x)\n\ndef asin32(x: float32):\n    return _C.asinf(x)\n\ndef asin(x):\n    if isinstance(x, float):\n        return asin64(x)\n    elif isinstance(x, float32):\n        return asin32(x)\n    elif isinstance(x, float16):\n        return _fp16_op_via_fp32(x, asin32)\n\ndef acos64(x: float):\n    return _C.acos(x)\n\ndef acos32(x: float32):\n    return _C.acosf(x)\n\ndef acos(x):\n    if isinstance(x, float):\n        return acos64(x)\n    elif isinstance(x, float32):\n        return acos32(x)\n    elif isinstance(x, float16):\n        return _fp16_op_via_fp32(x, acos32)\n\ndef atan64(x: float):\n    return _C.atan(x)\n\ndef atan32(x: float32):\n    return _C.atanf(x)\n\ndef atan(x):\n    if isinstance(x, float):\n        return atan64(x)\n    elif isinstance(x, float32):\n        return atan32(x)\n    elif isinstance(x, float16):\n        return _fp16_op_via_fp32(x, atan32)\n\ndef sinh64(x: float):\n    return _C.sinh(x)\n\ndef sinh32(x: float32):\n    return _C.sinhf(x)\n\ndef sinh(x):\n    if isinstance(x, float):\n        return sinh64(x)\n    elif isinstance(x, float32):\n        return sinh32(x)\n    elif isinstance(x, float16):\n        return _fp16_op_via_fp32(x, sinh32)\n\ndef cosh64(x: float):\n    return _C.cosh(x)\n\ndef cosh32(x: float32):\n    return _C.coshf(x)\n\ndef cosh(x):\n    if isinstance(x, float):\n        return cosh64(x)\n    elif isinstance(x, float32):\n        return cosh32(x)\n    elif isinstance(x, float16):\n        return _fp16_op_via_fp32(x, cosh32)\n\ndef tanh64(x: float):\n    return _C.tanh(x)\n\ndef tanh32(x: float32):\n    return _C.tanhf(x)\n\ndef tanh(x):\n    if isinstance(x, float):\n        return tanh64(x)\n    elif isinstance(x, float32):\n        return tanh32(x)\n    elif isinstance(x, float16):\n        return _fp16_op_via_fp32(x, tanh32)\n\ndef asinh64(x: float):\n    return _C.asinh(x)\n\ndef asinh32(x: float32):\n    return _C.asinhf(x)\n\ndef asinh(x):\n    if isinstance(x, float):\n        return asinh64(x)\n    elif isinstance(x, float32):\n        return asinh32(x)\n    elif isinstance(x, float16):\n        return _fp16_op_via_fp32(x, asinh32)\n\ndef acosh64(x: float):\n    return _C.acosh(x)\n\ndef acosh32(x: float32):\n    return _C.acoshf(x)\n\ndef acosh(x):\n    if isinstance(x, float):\n        return acosh64(x)\n    elif isinstance(x, float32):\n        return acosh32(x)\n    elif isinstance(x, float16):\n        return _fp16_op_via_fp32(x, acosh32)\n\ndef atanh64(x: float):\n    return _C.atanh(x)\n\ndef atanh32(x: float32):\n    return _C.atanhf(x)\n\ndef atanh(x):\n    if isinstance(x, float):\n        return atanh64(x)\n    elif isinstance(x, float32):\n        return atanh32(x)\n    elif isinstance(x, float16):\n        return _fp16_op_via_fp32(x, atanh32)\n\ndef atan2_64(x: float, y: float):\n    return _C.atan2(x, y)\n\ndef atan2_32(x: float32, y: float32):\n    return _C.atan2f(x, y)\n\ndef atan2(x, y):\n    if isinstance(x, float) and isinstance(y, float):\n        return atan2_64(x, y)\n    elif isinstance(x, float32) and isinstance(y, float32):\n        return atan2_32(x, y)\n    elif isinstance(x, float16) and isinstance(y, float16):\n        return _fp16_op_via_fp32_2(x, y, atan2_32)\n\n@pure\n@llvm\ndef copysign64(x: float, y: float) -> float:\n    declare double @llvm.copysign.f64(double, double)\n    %z = call double @llvm.copysign.f64(double %x, double %y)\n    ret double %z\n\n@pure\n@llvm\ndef copysign32(x: float32, y: float32) -> float32:\n    declare float @llvm.copysign.f32(float, float)\n    %z = call float @llvm.copysign.f32(float %x, float %y)\n    ret float %z\n\n@pure\n@llvm\ndef copysign16(x: float16, y: float16) -> float16:\n    declare half @llvm.copysign.f16(half, half)\n    %z = call half @llvm.copysign.f16(half %x, half %y)\n    ret half %z\n\ndef copysign(x, y):\n    if isinstance(x, float) and isinstance(y, float):\n        return copysign64(x, y)\n    elif isinstance(x, float32) and isinstance(y, float32):\n        return copysign32(x, y)\n    elif isinstance(x, float16) and isinstance(y, float16):\n        return copysign16(x, y)\n\n@pure\n@llvm\ndef pow64(x: float, y: float) -> float:\n    declare double @llvm.pow.f64(double, double)\n    %z = call double @llvm.pow.f64(double %x, double %y)\n    ret double %z\n\n@pure\n@llvm\ndef pow32(x: float32, y: float32) -> float32:\n    declare float @llvm.pow.f32(float, float)\n    %z = call float @llvm.pow.f32(float %x, float %y)\n    ret float %z\n\n@pure\n@llvm\ndef pow16(x: float16, y: float16) -> float16:\n    declare half @llvm.pow.f16(half, half)\n    %z = call half @llvm.pow.f16(half %x, half %y)\n    ret half %z\n\ndef pow(x, y):\n    if isinstance(x, float) and isinstance(y, float):\n        return pow64(x, y)\n    elif isinstance(x, float32) and isinstance(y, float32):\n        return pow32(x, y)\n    elif isinstance(x, float16) and isinstance(y, float16):\n        return pow16(x, y)\n\n@pure\n@llvm\ndef frexp64(x: float) -> Tuple[float, int]:\n    declare { double, i32 } @llvm.frexp.f64(double)\n    %y = call { double, i32 } @llvm.frexp.f64(double %x)\n    %y0 = extractvalue { double, i32 } %y, 0\n    %y1 = extractvalue { double, i32 } %y, 1\n    %z1 = sext i32 %y1 to i64\n    %r0 = insertvalue { double, i64 } undef, double %y0, 0\n    %r1 = insertvalue { double, i64 } %r0, i64 %z1, 1\n    ret { double, i64 } %r1\n\n@pure\n@llvm\ndef frexp32(x: float32) -> Tuple[float32, int]:\n    declare { float, i32 } @llvm.frexp.f32(float)\n    %y = call { float, i32 } @llvm.frexp.f32(float %x)\n    %y0 = extractvalue { float, i32 } %y, 0\n    %y1 = extractvalue { float, i32 } %y, 1\n    %z1 = sext i32 %y1 to i64\n    %r0 = insertvalue { float, i64 } undef, float %y0, 0\n    %r1 = insertvalue { float, i64 } %r0, i64 %z1, 1\n    ret { float, i64 } %r1\n\n@pure\n@llvm\ndef frexp16(x: float16) -> Tuple[float16, int]:\n    declare { half, i32 } @llvm.frexp.f16(half)\n    %y = call { half, i32 } @llvm.frexp.f16(half %x)\n    %y0 = extractvalue { half, i32 } %y, 0\n    %y1 = extractvalue { half, i32 } %y, 1\n    %z1 = sext i32 %y1 to i64\n    %r0 = insertvalue { half, i64 } undef, half %y0, 0\n    %r1 = insertvalue { half, i64 } %r0, i64 %z1, 1\n    ret { half, i64 } %r1\n\ndef frexp(x):\n    if isinstance(x, float):\n        return frexp64(x)\n    elif isinstance(x, float32):\n        return frexp32(x)\n    elif isinstance(x, float16):\n        return frexp16(x)\n\n@pure\n@llvm\ndef ldexp64(x: float, exp: int) -> float:\n    declare double @llvm.ldexp.f64(double, i32)\n    %e = trunc i64 %exp to i32\n    %y = call double @llvm.ldexp.f64(double %x, i32 %e)\n    ret double %y\n\n@pure\n@llvm\ndef ldexp32(x: float32, exp: int) -> float32:\n    declare float @llvm.ldexp.f32(float, i32)\n    %e = trunc i64 %exp to i32\n    %y = call float @llvm.ldexp.f32(float %x, i32 %e)\n    ret float %y\n\ndef ldexp16(x: float16, exp: int) -> float16:\n    return fptrunc(ldexp32(fpext(x, float32), exp), float16)\n\ndef ldexp(x, exp: int):\n    if isinstance(x, float):\n        return ldexp64(x, exp)\n    elif isinstance(x, float32):\n        return ldexp32(x, exp)\n    elif isinstance(x, float16):\n        return ldexp16(x, exp)\n\nLOGE2  = 0.693147180559945309417232121458176568\nLOGE10 = 2.302585092994045684017991454684364208\nLOG2E  = 1.442695040888963407359924681001892137\nLOG10E = 0.434294481903251827651128918916605082\nSQRT2  = 1.414213562373095048801688724209698079\nPI     = 3.141592653589793238462643383279502884\nPI_2   = 1.570796326794896619231321691639751442\nE      = 2.718281828459045235360287471352662498\n\ndef logaddexp64(x: float, y: float):\n    if x == y:\n        return x + LOGE2\n\n    tmp = x - y\n\n    if tmp > 0:\n        return x + _C.log1p(exp64(-tmp))\n    elif tmp <= 0:\n        return y + _C.log1p(exp64(tmp))\n    else:\n        return tmp\n\ndef logaddexp32(x: float32, y: float32):\n    if x == y:\n        return float32(float(x) + LOGE2)\n\n    tmp = x - y\n\n    if tmp > float32(0):\n        return x + _C.log1pf(exp32(-tmp))\n    elif tmp <= float32(0):\n        return y + _C.log1pf(exp32(tmp))\n    else:\n        return tmp\n\ndef logaddexp(x, y):\n    if isinstance(x, float) and isinstance(y, float):\n        return logaddexp64(x, y)\n    elif isinstance(x, float32) and isinstance(y, float32):\n        return logaddexp32(x, y)\n    elif isinstance(x, float16) and isinstance(y, float16):\n        return _fp16_op_via_fp32_2(x, y, logaddexp32)\n\ndef logaddexp2_64(x: float, y: float):\n    if x == y:\n        return x + 1\n\n    tmp = x - y\n\n    if tmp > 0:\n        return x + LOG2E * _C.log1p(exp2_64(-tmp))\n    elif tmp <= 0:\n        return y + LOG2E * _C.log1p(exp2_64(tmp))\n    else:\n        return tmp\n\ndef logaddexp2_32(x: float32, y: float32):\n    if x == y:\n        return x + float32(1)\n\n    tmp = x - y\n\n    if tmp > float32(0):\n        return x + float32(LOG2E * float(_C.log1pf(exp2_32(-tmp))))\n    elif tmp <= float32(0):\n        return y + float32(LOG2E * float(_C.log1pf(exp2_32(tmp))))\n    else:\n        return tmp\n\ndef logaddexp2(x, y):\n    if isinstance(x, float) and isinstance(y, float):\n        return logaddexp2_64(x, y)\n    elif isinstance(x, float32) and isinstance(y, float32):\n        return logaddexp2_32(x, y)\n    elif isinstance(x, float16) and isinstance(y, float16):\n        return _fp16_op_via_fp32_2(x, y, logaddexp2_32)\n\ndef hypot64(x: float, y: float):\n    return _C.hypot(x, y)\n\ndef hypot32(x: float32, y: float32):\n    return _C.hypotf(x, y)\n\ndef hypot(x, y):\n    if isinstance(x, float) and isinstance(y, float):\n        return hypot64(x, y)\n    elif isinstance(x, float32) and isinstance(y, float32):\n        return hypot32(x, y)\n    elif isinstance(x, float16) and isinstance(y, float16):\n        return _fp16_op_via_fp32_2(x, y, hypot32)\n\ndef modf64(x: float):\n    i = float()\n    a = _C.modf(x, __ptr__(i))\n    return (a, i)\n\ndef modf32(x: float32):\n    i = float32()\n    a = _C.modff(x, __ptr__(i))\n    return (a, i)\n\ndef modf16(x: float16):\n    y = fpext(x, float32)\n    i = float32()\n    a = _C.modff(y, __ptr__(i))\n    return (fptrunc(a, float16), fptrunc(i, float16))\n\ndef modf(x):\n    if isinstance(x, float):\n        return modf64(x)\n    elif isinstance(x, float32):\n        return modf32(x)\n    elif isinstance(x, float16):\n        return modf16(x)\n\ndef nextafter64(x: float, y: float):\n    return _C.nextafter(x, y)\n\ndef nextafter32(x: float32, y: float32):\n    return _C.nextafterf(x, y)\n\ndef nextafter16(x: float16, y: float16):\n    y_u16 = bitcast(y, u16)\n    x_u16 = bitcast(x, u16)\n    one = u16(1)\n\n    if isnan16(x) or isnan16(y):\n        return nan16()\n    elif x_u16 == y_u16 or (x_u16 & y_u16) == u16(0x8000):  # x == y (non-nan)\n        return x\n    elif not (x_u16 & u16(0x7fff)):  # x == 0\n        return bitcast((y_u16 & u16(0x8000)) + one, float16)\n    elif not (x_u16 & u16(0x8000)):  # x > 0\n        if i16(x_u16) > i16(y_u16):  # x > y\n            return bitcast(x_u16 - one, float16)\n        else:\n            return bitcast(x_u16 + one, float16)\n    else:\n        if (not (y_u16 & u16(0x8000))) or (x_u16 & u16(0x7fff)) > (y_u16 & u16(0x7fff)):  # x < y\n            return bitcast(x_u16 - one, float16)\n        else:\n            return bitcast(x_u16 + one, float16)\n\ndef nextafter(x, y):\n    if isinstance(x, float) and isinstance(y, float):\n        return nextafter64(x, y)\n    elif isinstance(x, float32) and isinstance(y, float32):\n        return nextafter32(x, y)\n    elif isinstance(x, float16) and isinstance(y, float16):\n        return nextafter16(x, y)\n\n@pure\n@llvm\ndef cdiv_int(x: Int[N], y: Int[N], N: Literal[int]) -> Int[N]:\n    %z = sdiv i{=N} %x, %y\n    ret i{=N} %z\n\n@overload\n@pure\n@llvm\ndef cdiv_int(x: int, y: int) -> int:\n    %z = sdiv i64 %x, %y\n    ret i64 %z\n\n@pure\n@llvm\ndef cmod_int(x: Int[N], y: Int[N], N: Literal[int]) -> Int[N]:\n    %z = srem i{=N} %x, %y\n    ret i{=N} %z\n\n@overload\n@pure\n@llvm\ndef cmod_int(x: int, y: int) -> int:\n    %z = srem i64 %x, %y\n    ret i64 %z\n\ndef pydiv(x: Int[N], y: Int[N], N: Literal[int]):\n    div = cdiv_int(x, y)\n    mask = Int[N](1) << Int[N](N - 1)\n\n    if ((x ^ y) & mask) and div * y != x:\n        div -= Int[N](1)\n\n    return div\n\n@overload\ndef pydiv(x: int, y: int) -> int:\n    div = cdiv_int(x, y)\n    mask = 1 << 63\n\n    if ((x ^ y) & mask) and div * y != x:\n        div -= 1\n\n    return div\n\ndef pymod(x: Int[N], y: Int[N], N: Literal[int]):\n    mod = cmod_int(x, y)\n    mask = Int[N](1) << Int[N](N - 1)\n\n    if mod and ((x ^ y) & mask):\n        mod += y\n\n    return mod\n\n@overload\ndef pymod(x: int, y: int) -> int:\n    mod = cmod_int(x, y)\n    mask = 1 << 63\n\n    if mod and ((x ^ y) & mask):\n        mod += y\n\n    return mod\n\n@pure\n@llvm\ndef cdiv64(x: float, y: float) -> float:\n    %z = fdiv double %x, %y\n    ret double %z\n\n@pure\n@llvm\ndef cdiv32(x: float32, y: float32) -> float32:\n    %z = fdiv float %x, %y\n    ret float %z\n\n@pure\n@llvm\ndef cdiv16(x: float16, y: float16) -> float16:\n    %z = fdiv half %x, %y\n    ret half %z\n\ndef cdiv(x, y):\n    if isinstance(x, float) and isinstance(y, float):\n        return cdiv64(x, y)\n    elif isinstance(x, float32) and isinstance(y, float32):\n        return cdiv32(x, y)\n    elif isinstance(x, float16) and isinstance(y, float16):\n        return cdiv16(x, y)\n\n@pure\n@llvm\ndef cmod64(x: float, y: float) -> float:\n    %z = frem double %x, %y\n    ret double %z\n\n@pure\n@llvm\ndef cmod32(x: float32, y: float32) -> float32:\n    %z = frem float %x, %y\n    ret float %z\n\n@pure\n@llvm\ndef cmod16(x: float16, y: float16) -> float16:\n    %z = frem half %x, %y\n    ret half %z\n\ndef cmod(x, y):\n    if isinstance(x, float) and isinstance(y, float):\n        return cmod64(x, y)\n    elif isinstance(x, float32) and isinstance(y, float32):\n        return cmod32(x, y)\n    elif isinstance(x, float16) and isinstance(y, float16):\n        return cmod16(x, y)\n\ndef pyfmod(x, y):\n    X = type(x)\n    zero = X()\n    mod = cmod(x, y)\n    if mod:\n        if (y < zero) != (mod < zero):\n            mod += y\n    else:\n        mod = copysign(zero, y)\n    return mod\n\n@pure\n@llvm\ndef _i0A(idx: int) -> float:\n    @data = private unnamed_addr constant [30 x double] [double 0xBC545CB72134D0EF, double 0x3C833362977DA589, double 0xBCB184EB721EBBB4, double 0x3CDEE6D893F65EBA, double 0xBD0A5022C297FBEB, double 0x3D359B464B262627, double 0xBD61164C62EE1AF0, double 0x3D89FE2FE19BD324, double 0xBDB2FC957A946ABC, double 0x3DDA98BECC743C10, double 0xBE01D4FE13AE9556, double 0x3E26D903A454CB34, double 0xBE4BEAF68C0B30AB, double 0x3E703B769D4D6435, double 0xBE91EC638F227F8D, double 0x3EB2BF24978CF4AC, double 0xBED2866FCBA56427, double 0x3EF13F58BE9A2859, double 0xBF0E2B2659C41D5A, double 0x3F28B51B74107CAB, double 0xBF42E2FD1F15EB52, double 0x3F5ADC758A12100E, double 0xBF71B65E201AA849, double 0x3F859961F3DDE3DD, double 0xBF984E9EF121B6F0, double 0x3FA93E8ACEA8A32D, double 0xBFB84B70342D06EA, double 0x3FC5F7AC77AC88C0, double 0xBFD37FEBC057CD8D, double 0x3FE5A84E9035A22A], align 16\n    %p = getelementptr inbounds [256 x double], ptr @data, i64 0, i64 %idx\n    %x = load double, ptr %p, align 8\n    ret double %x\n\n@pure\n@llvm\ndef _i0B(idx: int) -> float:\n    @data = private unnamed_addr constant [25 x double] [double 0xBC60ADB754CA8B19, double 0xBC5646DA66119130, double 0x3C89BE1812D98421, double 0x3C83F3DD076041CD, double 0xBCB4600BABD21FE4, double 0xBCB8AEE7D908DE38, double 0x3CDFEE7DA3EAFB1F, double 0x3CF12A919094E6D7, double 0xBD0583FE7E65629A, double 0xBD275D99CF68BB32, double 0x3D1156FF0D5FC545, double 0x3D5B1C8C6B83C073, double 0x3D694347FA268CEC, double 0xBD7F904303178D66, double 0xBDAD0FD7357E7BF2, double 0xBDC1511D08397425, double 0x3DAA24FEABE8004F, double 0x3E00F9CCC0F46F75, double 0x3E2D2C64A9225B87, double 0x3E58569280D6D56D, double 0x3E8B8007D9CD616E, double 0x3EC8412BC101C586, double 0x3F120FA378999E52, double 0x3F6B998CA2E59049, double 0x3FE9BE62ACA809CB], align 16\n    %p = getelementptr inbounds [256 x double], ptr @data, i64 0, i64 %idx\n    %x = load double, ptr %p, align 8\n    ret double %x\n\n_NUM_I0A: Literal[int] = 30\n_NUM_I0B: Literal[int] = 25\n\ndef _chbevl64(x: float, vals, nvals: int):\n    b0 = vals(0)\n    b1 = 0.0\n\n    for i in range(1, nvals):\n        b2 = b1\n        b1 = b0\n        b0 = x*b1 - b2 + vals(i)\n\n    return 0.5*(b0 - b2)\n\ndef _chbevl32(x: float32, vals, nvals: int):\n    b0 = float32(vals(0))\n    b1 = float32(0.0)\n\n    for i in range(1, nvals):\n        b2 = b1\n        b1 = b0\n        b0 = x*b1 - b2 + float32(vals(i))\n\n    return float32(0.5)*(b0 - b2)\n\ndef _i0_1_64(x: float):\n    return exp64(x) * _chbevl64(x/2.0-2, _i0A, _NUM_I0A)\n\ndef _i0_2_64(x: float):\n    return exp64(x) * _chbevl64(32.0/x - 2.0, _i0B, _NUM_I0B) / sqrt64(x)\n\ndef _i0_1_32(x: float32):\n    return exp32(x) * _chbevl32(x/float32(2.0)-float32(2), _i0A, _NUM_I0A)\n\ndef _i0_2_32(x: float32):\n    return exp32(x) * _chbevl32(float32(32.0)/x - float32(2.0), _i0B, _NUM_I0B) / sqrt32(x)\n\ndef i0_64(x: float):\n    x = fabs64(x)\n    if x <= 8.0:\n        return _i0_1_64(x)\n    else:\n        return _i0_2_64(x)\n\ndef i0_32(x: float32):\n    x = fabs32(x)\n    if x <= float32(8.0):\n        return _i0_1_32(x)\n    else:\n        return _i0_2_32(x)\n\ndef i0(x):\n    if isinstance(x, float):\n        return i0_64(x)\n    elif isinstance(x, float32):\n        return i0_32(x)\n    elif isinstance(x, float16):\n        return _fp16_op_via_fp32(x, i0_32)\n\ndef call_vectorized_loop(in1: Ptr[T], is1: int, in2: Ptr[T], is2: int, out: Ptr[T],\n                         os: int, n: int, func: Literal[str], T: type):\n    from C import cnp_acos_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_acos_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_acosh_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_acosh_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_asin_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_asin_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_asinh_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_asinh_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_atan_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_atan_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_atanh_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_atanh_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_atan2_float64(Ptr[float], int, Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_atan2_float32(Ptr[float32], int, Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_cos_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_cos_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_exp_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_exp_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_exp2_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_exp2_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_expm1_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_expm1_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_log_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_log_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_log10_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_log10_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_log1p_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_log1p_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_log2_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_log2_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_sin_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_sin_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_sinh_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_sinh_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_tanh_float64(Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_tanh_float32(Ptr[float32], int, Ptr[float32], int, int)\n    from C import cnp_hypot_float64(Ptr[float], int, Ptr[float], int, Ptr[float], int, int)\n    from C import cnp_hypot_float32(Ptr[float32], int, Ptr[float32], int, Ptr[float32], int, int)\n\n    if func == 'arccos' and T is float:\n        cnp_acos_float64(in1, is1, out, os, n)\n    elif func == 'arccos' and T is float32:\n        cnp_acos_float32(in1, is1, out, os, n)\n    elif func == 'arccosh' and T is float:\n        cnp_acosh_float64(in1, is1, out, os, n)\n    elif func == 'arccosh' and T is float32:\n        cnp_acosh_float32(in1, is1, out, os, n)\n    elif func == 'arcsin' and T is float:\n        cnp_asin_float64(in1, is1, out, os, n)\n    elif func == 'arcsin' and T is float32:\n        cnp_asin_float32(in1, is1, out, os, n)\n    elif func == 'arcsinh' and T is float:\n        cnp_asinh_float64(in1, is1, out, os, n)\n    elif func == 'arcsinh' and T is float32:\n        cnp_asinh_float32(in1, is1, out, os, n)\n    elif func == 'arctan' and T is float:\n        cnp_atan_float64(in1, is1, out, os, n)\n    elif func == 'arctan' and T is float32:\n        cnp_atan_float32(in1, is1, out, os, n)\n    elif func == 'arctanh' and T is float:\n        cnp_atanh_float64(in1, is1, out, os, n)\n    elif func == 'arctanh' and T is float32:\n        cnp_atanh_float32(in1, is1, out, os, n)\n    elif func == 'arctan2' and T is float:\n        cnp_atan2_float64(in1, is1, in2, is2, out, os, n)\n    elif func == 'arctan2' and T is float32:\n        cnp_atan2_float32(in1, is1, in2, is2, out, os, n)\n    elif func == 'cos' and T is float:\n        cnp_cos_float64(in1, is1, out, os, n)\n    elif func == 'cos' and T is float32:\n        cnp_cos_float32(in1, is1, out, os, n)\n    elif func == 'exp' and T is float:\n        cnp_exp_float64(in1, is1, out, os, n)\n    elif func == 'exp' and T is float32:\n        cnp_exp_float32(in1, is1, out, os, n)\n    elif func == 'exp2' and T is float:\n        cnp_exp2_float64(in1, is1, out, os, n)\n    elif func == 'exp2' and T is float32:\n        cnp_exp2_float32(in1, is1, out, os, n)\n    elif func == 'expm1' and T is float:\n        cnp_expm1_float64(in1, is1, out, os, n)\n    elif func == 'expm1' and T is float32:\n        cnp_expm1_float32(in1, is1, out, os, n)\n    elif func == 'log' and T is float:\n        cnp_log_float64(in1, is1, out, os, n)\n    elif func == 'log' and T is float32:\n        cnp_log_float32(in1, is1, out, os, n)\n    elif func == 'log10' and T is float:\n        cnp_log10_float64(in1, is1, out, os, n)\n    elif func == 'log10' and T is float32:\n        cnp_log10_float32(in1, is1, out, os, n)\n    elif func == 'log1p' and T is float:\n        cnp_log1p_float64(in1, is1, out, os, n)\n    elif func == 'log1p' and T is float32:\n        cnp_log1p_float32(in1, is1, out, os, n)\n    elif func == 'log2' and T is float:\n        cnp_log2_float64(in1, is1, out, os, n)\n    elif func == 'log2' and T is float32:\n        cnp_log2_float32(in1, is1, out, os, n)\n    elif func == 'sin' and T is float:\n        cnp_sin_float64(in1, is1, out, os, n)\n    elif func == 'sin' and T is float32:\n        cnp_sin_float32(in1, is1, out, os, n)\n    elif func == 'sinh' and T is float:\n        cnp_sinh_float64(in1, is1, out, os, n)\n    elif func == 'sinh' and T is float32:\n        cnp_sinh_float32(in1, is1, out, os, n)\n    elif func == 'tanh' and T is float:\n        cnp_tanh_float64(in1, is1, out, os, n)\n    elif func == 'tanh' and T is float32:\n        cnp_tanh_float32(in1, is1, out, os, n)\n    elif func == 'hypot' and T is float:\n        cnp_hypot_float64(in1, is1, in2, is2, out, os, n)\n    elif func == 'hypot' and T is float32:\n        cnp_hypot_float32(in1, is1, in2, is2, out, os, n)\n    else:\n        pass\n\n\n#########\n# Types #\n#########\n\ndef sizeof(X: type):\n    from internal.gc import sizeof as sz\n    return sz(X)\n\ndef atomic(X: type):\n    from internal.gc import atomic as atm\n    return atm(X)\n\ndef free(p: Ptr[T], T: type):\n    from internal.gc import free as fr\n    fr(p.as_byte())\n\ndef realloc(p: Ptr[T], newsize: int, oldsize: int, T: type):\n    from internal.gc import realloc as re\n    sz = sizeof(T)\n    return Ptr[T](re(p.as_byte(), newsize * sz, oldsize * sz))\n\ndef strides(shape, forder: bool, X: type):\n    if static.len(shape) == 0:\n        return ()\n\n    strides = (0,) * static.len(shape)\n    p = Ptr[int](__ptr__(strides).as_byte())\n    n = len(shape)\n    curr = sizeof(X)\n\n    for i in range(n):\n        j = i if forder else n - 1 - i\n        p[j] = curr\n        curr *= shape[j]\n\n    return strides\n\ndef cast(x, T: type):\n    X = type(x)\n\n    # Need to support specially:\n    #   - bool\n    #   - int\n    #   - byte\n    #   - Int[N]\n    #   - UInt[N]\n    #   - float\n    #   - float32\n    #   - complex\n    #   - complex64\n\n    if ((isinstance(X, datetime64) and isinstance(T, datetime64)) or\n        (isinstance(X, timedelta64) and isinstance(T, timedelta64))):\n        return x._cast(T)\n    elif isinstance(X, datetime64) or isinstance(X, timedelta64):\n        if T is float or T is float32 or T is float16:\n            return nan(T) if x._nat else cast(x.value, T)\n        else:\n            return cast(x.value, T)\n    elif T is X or T is NoneType:\n        return x\n    elif T is bool:\n        return bool(x)\n    elif isinstance(T, datetime64) or isinstance(T, timedelta64):\n        _DATETIME_NAT: Literal[int] = -9_223_372_036_854_775_808\n        if X is float or X is float32 or X is float16:\n            return T(_DATETIME_NAT, T.base, T.num) if isnan(x) else T(cast(x, int), T.base, T.num)\n        elif X is complex or X is complex64:\n            return T(_DATETIME_NAT, T.base, T.num) if (isnan(x.real) or isnan(x.imag)) else T(cast(x, int), T.base, T.num)\n        elif X is str:\n            return T(x, T.base, T.num)\n        else:\n            return T(cast(x, int), T.base, T.num)\n    elif T is int:\n        if X is bool:\n            return 1 if x else 0\n        elif X is int:\n            return x\n        elif X is byte:\n            return int(x)\n        elif isinstance(X, Int):\n            return int(x)\n        elif isinstance(X, UInt):\n            return int(x)\n        elif X is float:\n            return int(x)\n        elif X is float16:\n            return int(float(x))\n        elif X is float32:\n            return int(float(x))\n        elif X is complex:\n            return int(x.real)\n        elif X is complex64:\n            return int(float(x.real))\n        else:\n            return T(x)\n    elif T is byte:\n        return byte(int(x))\n    elif isinstance(T, Int):\n        if X is bool:\n            return T(1 if x else 0)\n        elif X is int:\n            return T(x)\n        elif X is byte:\n            if T.N > 8:\n                return zext(x, T)\n            elif T.N < 8:\n                return itrunc(x, T)\n            else:\n                return noop(x, T)\n        elif isinstance(X, Int):\n            if T.N > X.N:\n                return sext(x, T)\n            elif T.N < X.N:\n                return itrunc(x, T)\n            else:\n                return noop(x, T)\n        elif isinstance(X, UInt):\n            if T.N > X.N:\n                return zext(x, T)\n            elif T.N < X.N:\n                return itrunc(x, T)\n            else:\n                return noop(x, T)\n        elif X is float:\n            return fptosi(x, T)\n        elif X is float16:\n            return fptosi(x, T)\n        elif X is float32:\n            return fptosi(x, T)\n        elif X is complex:\n            return cast(x.real, T)\n        elif X is complex64:\n            return cast(x.real, T)\n        else:\n            return T(x)\n    elif isinstance(T, UInt):\n        if X is bool:\n            return T(1 if x else 0)\n        elif X is int:\n            return T(x)\n        elif X is byte:\n            if T.N > 8:\n                return zext(x, T)\n            elif T.N < 8:\n                return itrunc(x, T)\n            else:\n                return noop(x, T)\n        elif isinstance(X, Int):\n            if T.N > X.N:\n                return sext(x, T)\n            elif T.N < X.N:\n                return itrunc(x, T)\n            else:\n                return noop(x, T)\n        elif isinstance(X, UInt):\n            if T.N > X.N:\n                return zext(x, T)\n            elif T.N < X.N:\n                return itrunc(x, T)\n            else:\n                return noop(x, T)\n        elif X is float:\n            return fptoui(x, T)\n        elif X is float16:\n            return fptoui(x, T)\n        elif X is float32:\n            return fptoui(x, T)\n        elif X is complex:\n            return cast(x.real, T)\n        elif X is complex64:\n            return cast(x.real, T)\n        else:\n            return T(x)\n    elif T is float:\n        if X is bool:\n            return 1. if x else 0.\n        elif X is int:\n            return sitofp(x, T)\n        elif X is byte:\n            return uitofp(x, T)\n        elif isinstance(X, Int):\n            return sitofp(x, T)\n        elif isinstance(X, UInt):\n            return uitofp(x, T)\n        elif X is float:\n            return x\n        elif X is float16:\n            return float(x)\n        elif X is float32:\n            return float(x)\n        elif X is complex:\n            return x.real\n        elif X is complex64:\n            return float(x.real)\n        else:\n            return T(x)\n    elif T is float16:\n        if X is bool:\n            return float16(1. if x else 0.)\n        elif X is int:\n            return sitofp(x, T)\n        elif X is byte:\n            return uitofp(x, T)\n        elif isinstance(X, Int):\n            return sitofp(x, T)\n        elif isinstance(X, UInt):\n            return uitofp(x, T)\n        elif X is float:\n            return float16(x)\n        elif X is float16:\n            return x\n        elif X is float32:\n            return fptrunc(x, T)\n        elif X is complex:\n            return float16(x.real)\n        elif X is complex64:\n            return fptrunc(x.real, T)\n        else:\n            return T(x)\n    elif T is float32:\n        if X is bool:\n            return float32(1. if x else 0.)\n        elif X is int:\n            return sitofp(x, T)\n        elif X is byte:\n            return uitofp(x, T)\n        elif isinstance(X, Int):\n            return sitofp(x, T)\n        elif isinstance(X, UInt):\n            return uitofp(x, T)\n        elif X is float:\n            return float32(x)\n        elif X is float16:\n            return fpext(x, T)\n        elif X is float32:\n            return x\n        elif X is complex:\n            return float32(x.real)\n        elif X is complex64:\n            return x.real\n        else:\n            return T(x)\n    elif T is complex:\n        if X is complex64:\n            return complex(x)\n        else:\n            return complex(cast(x, float), 0.0)\n    elif T is complex64:\n        if X is complex:\n            return complex64(x)\n        else:\n            return complex64(cast(x, float32), float32(0))\n    else:\n        return T(x)\n\ndef coerce(T1: type, T2: type):\n    def coerce_error(T1: type, T2: type):\n        compile_error(\"cannot coerce types '\" + T1.__name__ + \"' and '\" + T2.__name__ + \"'\")\n\n    def coerce_ints_helper(T1: type, T2: type):\n        if isinstance(T1, Int) and isinstance(T2, Int):\n            if T1.N >= T2.N:\n                return T1()\n            else:\n                return T2()\n        elif isinstance(T1, UInt) and isinstance(T2, UInt):\n            if T1.N >= T2.N:\n                return T1()\n            else:\n                return T2()\n        elif isinstance(T1, Int) and isinstance(T2, UInt):\n            if T1.N > T2.N:\n                return T1()\n            else:\n                if T2.N == 8 or T2.N == 16 or T2.N == 32:\n                    return Int[2 * T2.N]()\n                elif T2.N >= 64:\n                    return float()\n        elif isinstance(T1, UInt) and isinstance(T2, Int):\n            return coerce_ints_helper(T2, T1)\n        else:\n            coerce_error(T1, T2)\n\n    def coerce_ints(T1: type, T2: type, I1: type = T1, I2: type = T2):\n        if T1 is T2:\n            return T1()\n\n        if T1 is I1 and T2 is I2:\n            return coerce_ints_helper(T1, T2)\n\n        if T1 is I1:\n            x = coerce_ints_helper(T1, I2)\n            if type(x) is I2:\n                return T2()\n            else:\n                return x\n\n        if T2 is I2:\n            x = coerce_ints_helper(I1, T2)\n            if type(x) is I1:\n                return T1()\n            else:\n                return x\n\n        x = coerce_ints_helper(I1, I2)\n        if type(x) is I1:\n            return T1()\n        elif type(x) is I2:\n            return T2()\n        else:\n            return x\n\n    # Need to support specially:\n    #   - bool\n    #   - int\n    #   - byte\n    #   - Int[N]\n    #   - UInt[N]\n    #   - float\n    #   - float32\n    #   - complex\n    #   - complex64\n    if T1 is T2:\n        return T1()\n\n    if T1 is NoneType:\n        return T2()\n\n    if T2 is NoneType:\n        return T1()\n\n    if ((isinstance(T1, datetime64) or isinstance(T1, timedelta64)) and\n        (isinstance(T2, datetime64) or isinstance(T2, timedelta64))):\n        return dt_promote(T1(), T2())\n\n    if T1 is bool:\n        if T2 is bool:\n            return coerce(T2, T1)\n        elif T2 is int:\n            return int()\n        elif T2 is byte:\n            return byte()\n        elif isinstance(T2, Int) or isinstance(T2, UInt):\n            return T2()\n        elif T2 is float:\n            return float()\n        elif T2 is float16:\n            return float16()\n        elif T2 is float32:\n            return float32()\n        elif T2 is complex:\n            return complex()\n        elif T2 is complex64:\n            return complex64()\n        elif isinstance(T2, datetime64):\n            return T2()\n        elif isinstance(T2, timedelta64):\n            return T2()\n    elif T1 is int:\n        if T2 is bool:\n            return coerce(T2, T1)\n        elif T2 is int:\n            return coerce(T2, T1)\n        elif T2 is byte:\n            return int()\n        elif isinstance(T2, Int) or isinstance(T2, UInt):\n            return coerce_ints(int, T2, I1=Int[64])\n        elif T2 is float:\n            return float()\n        elif T2 is float16:\n            return float()\n        elif T2 is float32:\n            return float()\n        elif T2 is complex:\n            return complex()\n        elif T2 is complex64:\n            return complex()\n        elif isinstance(T2, datetime64):\n            return T2()\n        elif isinstance(T2, timedelta64):\n            return T2()\n    elif T1 is byte:\n        if T2 is bool:\n            return coerce(T2, T1)\n        elif T2 is int:\n            return coerce(T2, T1)\n        elif T2 is byte:\n            return coerce(T2, T1)\n        elif isinstance(T2, Int) or isinstance(T2, UInt):\n            return coerce_ints(int, T2, I1=UInt[8])\n        elif T2 is float:\n            return float()\n        elif T2 is float16:\n            return float16()\n        elif T2 is float32:\n            return float32()\n        elif T2 is complex:\n            return complex()\n        elif T2 is complex64:\n            return complex64()\n        elif isinstance(T2, datetime64):\n            return T2()\n        elif isinstance(T2, timedelta64):\n            return T2()\n    elif isinstance(T1, Int):\n        if T2 is bool:\n            return coerce(T2, T1)\n        elif T2 is int:\n            return coerce(T2, T1)\n        elif T2 is byte:\n            return coerce(T2, T1)\n        elif isinstance(T2, Int) or isinstance(T2, UInt):\n            return coerce_ints(T1, T2)\n        elif T2 is float:\n            return float()\n        elif T2 is float16:\n            if T1.N >= 32:\n                return float()\n            elif T1.N >= 16:\n                return float32()\n            else:\n                return float16()\n        elif T2 is float32:\n            if T1.N >= 32:\n                return float()\n            else:\n                return float32()\n        elif T2 is complex:\n            return complex()\n        elif T2 is complex64:\n            if T1.N >= 32:\n                return complex()\n            else:\n                return complex64()\n        elif isinstance(T2, datetime64):\n            return T2()\n        elif isinstance(T2, timedelta64):\n            return T2()\n    elif isinstance(T1, UInt):\n        if T2 is bool:\n            return coerce(T2, T1)\n        elif T2 is int:\n            return coerce(T2, T1)\n        elif T2 is byte:\n            return coerce(T2, T1)\n        elif isinstance(T2, Int) or isinstance(T2, UInt):\n            return coerce_ints(T1, T2)\n        elif T2 is float:\n            return float()\n        elif T2 is float16:\n            if T1.N >= 32:\n                return float()\n            elif T1.N >= 16:\n                return float32()\n            else:\n                return float16()\n        elif T2 is float32:\n            if T1.N >= 32:\n                return float()\n            else:\n                return float32()\n        elif T2 is complex:\n            return complex()\n        elif T2 is complex64:\n            if T1.N >= 32:\n                return complex()\n            else:\n                return complex64()\n        elif isinstance(T2, datetime64):\n            return T2()\n        elif isinstance(T2, timedelta64):\n            return T2()\n    elif T1 is float:\n        if T2 is bool:\n            return coerce(T2, T1)\n        elif T2 is int:\n            return coerce(T2, T1)\n        elif T2 is byte:\n            return coerce(T2, T1)\n        elif isinstance(T2, Int) or isinstance(T2, UInt):\n            return coerce(T2, T1)\n        elif T2 is float:\n            return coerce(T2, T1)\n        elif T2 is float16:\n            return float()\n        elif T2 is float32:\n            return float()\n        elif T2 is complex:\n            return complex()\n        elif T2 is complex64:\n            return complex()\n        elif isinstance(T2, datetime64):\n            return T2()\n        elif isinstance(T2, timedelta64):\n            return T2()\n    elif T1 is float16:\n        if T2 is bool:\n            return coerce(T2, T1)\n        elif T2 is int:\n            return coerce(T2, T1)\n        elif T2 is byte:\n            return coerce(T2, T1)\n        elif isinstance(T2, Int) or isinstance(T2, UInt):\n            return coerce(T2, T1)\n        elif T2 is float:\n            return coerce(T2, T1)\n        elif T2 is float16:\n            return coerce(T2, T1)\n        elif T2 is float32:\n            return float32()\n        elif T2 is complex:\n            return complex()\n        elif T2 is complex64:\n            return complex64()\n        elif isinstance(T2, datetime64):\n            return T2()\n        elif isinstance(T2, timedelta64):\n            return T2()\n    elif T1 is float32:\n        if T2 is bool:\n            return coerce(T2, T1)\n        elif T2 is int:\n            return coerce(T2, T1)\n        elif T2 is byte:\n            return coerce(T2, T1)\n        elif isinstance(T2, Int) or isinstance(T2, UInt):\n            return coerce(T2, T1)\n        elif T2 is float:\n            return coerce(T2, T1)\n        elif T2 is float16:\n            return coerce(T2, T1)\n        elif T2 is float32:\n            return coerce(T2, T1)\n        elif T2 is complex:\n            return complex()\n        elif T2 is complex64:\n            return complex64()\n        elif isinstance(T2, datetime64):\n            return T2()\n        elif isinstance(T2, timedelta64):\n            return T2()\n    elif T1 is complex:\n        if T2 is bool:\n            return coerce(T2, T1)\n        elif T2 is int:\n            return coerce(T2, T1)\n        elif T2 is byte:\n            return coerce(T2, T1)\n        elif isinstance(T2, Int) or isinstance(T2, UInt):\n            return coerce(T2, T1)\n        elif T2 is float:\n            return coerce(T2, T1)\n        elif T2 is float16:\n            return coerce(T2, T1)\n        elif T2 is float32:\n            return coerce(T2, T1)\n        elif T2 is complex:\n            return coerce(T2, T1)\n        elif T2 is complex64:\n            return complex()\n        elif isinstance(T2, datetime64):\n            return T2()\n        elif isinstance(T2, timedelta64):\n            return T2()\n    elif T1 is complex64:\n        if T2 is bool:\n            return coerce(T2, T1)\n        elif T2 is int:\n            return coerce(T2, T1)\n        elif T2 is byte:\n            return coerce(T2, T1)\n        elif isinstance(T2, Int) or isinstance(T2, UInt):\n            return coerce(T2, T1)\n        elif T2 is float:\n            return coerce(T2, T1)\n        elif T2 is float16:\n            return coerce(T2, T1)\n        elif T2 is float32:\n            return coerce(T2, T1)\n        elif T2 is complex:\n            return coerce(T2, T1)\n        elif T2 is complex64:\n            return coerce(T2, T1)\n        elif isinstance(T2, datetime64):\n            return T2()\n        elif isinstance(T2, timedelta64):\n            return T2()\n    elif isinstance(T1, datetime64):\n        if T2 is bool:\n            return coerce(T2, T1)\n        elif T2 is int:\n            return coerce(T2, T1)\n        elif T2 is byte:\n            return coerce(T2, T1)\n        elif isinstance(T2, Int) or isinstance(T2, UInt):\n            return coerce(T2, T1)\n        elif T2 is float:\n            return coerce(T2, T1)\n        elif T2 is float16:\n            return coerce(T2, T1)\n        elif T2 is float32:\n            return coerce(T2, T1)\n        elif T2 is complex:\n            return coerce(T2, T1)\n        elif T2 is complex64:\n            return coerce(T2, T1)\n    elif isinstance(T1, timedelta64):\n        if T2 is bool:\n            return coerce(T2, T1)\n        elif T2 is int:\n            return coerce(T2, T1)\n        elif T2 is byte:\n            return coerce(T2, T1)\n        elif isinstance(T2, Int) or isinstance(T2, UInt):\n            return coerce(T2, T1)\n        elif T2 is float:\n            return coerce(T2, T1)\n        elif T2 is float16:\n            return coerce(T2, T1)\n        elif T2 is float32:\n            return coerce(T2, T1)\n        elif T2 is complex:\n            return coerce(T2, T1)\n        elif T2 is complex64:\n            return coerce(T2, T1)\n\n    coerce_error(T1, T2)\n\ndef op_types(T1: type, T2: type):\n    if (isinstance(T1, datetime64) or isinstance(T2, datetime64) or\n        isinstance(T1, timedelta64) or isinstance(T2, timedelta64)):\n        return T1(), T2()\n    else:\n        ct = coerce(T1, T2)\n        return (ct, ct)\n\ndef to_float(x):\n    if (isinstance(x, float) or\n        isinstance(x, float32) or\n        isinstance(x, float16)):\n        return x\n\n    if isinstance(x, float128):\n        return fptrunc(x, float)\n\n    if (isinstance(x, int) or\n        isinstance(x, i64) or\n        isinstance(x, u64) or\n        isinstance(x, i32) or\n        isinstance(x, u32)):\n        return cast(x, float)\n\n    if (isinstance(x, i16) or\n        isinstance(x, u16)):\n        return cast(x, float32)\n\n    if (isinstance(x, i8) or\n        isinstance(x, u8) or\n        isinstance(x, byte)):\n        return cast(x, float16)\n\n    if (isinstance(x, datetime64) or\n        isinstance(x, timedelta64)):\n        return nan64() if x._nat else cast(x.value, float)\n\n    return float(x)\n\ndef str_to_dtype(s: Literal[str]):\n    if s[:1] == '>':\n        compile_error(\"big-endian data types are not supported\")\n\n    if s[:1] == '<' or s[:1] == '|' or s[:1] == '=':\n        return str_to_dtype(s[1:])\n\n    if s == '?' or s == 'bool' or s == 'bool_':\n        return bool()\n\n    if s == 'b' or s == 'byte':\n        return i8()\n\n    if s == 'B' or s == 'ubyte':\n        return u8()\n\n    if s == 'u1' or s == 'uint8':\n        return u8()\n\n    if s == 'u2' or s == 'uint16' or s == 'ushort':\n        return u16()\n\n    if s == 'u4' or s == 'uint32':\n        return u32()\n\n    if s == 'u8' or s == 'uint64' or s == 'ulong' or s == 'ulonglong':\n        return u64()\n\n    if s == 'i1' or s == 'int8':\n        return i8()\n\n    if s == 'i2' or s == 'int16' or s == 'short':\n        return i16()\n\n    if s == 'i4' or s == 'int32':\n        return i32()\n\n    if s == 'i8' or s == 'int64' or s == 'long' or s == 'longlong' or s == 'int' or s == 'int_':\n        return int()\n\n    if s == 'f2' or s == 'float16' or s == 'half':\n        return float16()\n\n    if s == 'f4' or s == 'float32':\n        return float32()\n\n    if s == 'f8' or s == 'float64' or s == 'float' or s == 'float_':\n        return float()\n\n    if s == 'c8' or s == 'complex64':\n        return complex64()\n\n    if s == 'c16' or s == 'complex128' or s == 'complex' or s == 'complex_':\n        return complex()\n\n    if s == 'm8':\n        return timedelta64['generic', 1]()\n\n    if s == 'M8':\n        return datetime64['generic', 1]()\n\n    if s[:11] == 'datetime64[' or s[:12] == 'timedelta64[' or s[:3] == 'm8[' or s[:3] == 'M8[':\n        return dt_parse(s)\n\n    # TODO: add these when applicable\n    # O -> Python objects\n    # S / a -> zero-terminated bytes\n    # U -> Unicode string\n    # V -> raw data\n\n    compile_error(\"data type '\" + s + \"' not understood\")\n\ndef dtype_to_str(dtype: type, include_byteorder: bool = True):\n    if dtype is bool:\n        return '|b1' if include_byteorder else 'b1'\n\n    if dtype is byte:\n        return '|u1' if include_byteorder else 'u1'\n\n    if dtype is i8:\n        return '|i1' if include_byteorder else 'i1'\n\n    if dtype is u8:\n        return '|u1' if include_byteorder else 'u1'\n\n    if dtype is i16:\n        return '<i2' if include_byteorder else 'i2'\n\n    if dtype is u16:\n        return '<u2' if include_byteorder else 'u2'\n\n    if dtype is i32:\n        return '<i4' if include_byteorder else 'i4'\n\n    if dtype is u32:\n        return '<u4' if include_byteorder else 'u4'\n\n    if dtype is i64 or dtype is int:\n        return '<i8' if include_byteorder else 'i8'\n\n    if dtype is u64:\n        return '<u8' if include_byteorder else 'u8'\n\n    if dtype is float16:\n        return '<f2' if include_byteorder else 'f2'\n\n    if dtype is float32:\n        return '<f4' if include_byteorder else 'f4'\n\n    if dtype is float:\n        return '<f8' if include_byteorder else 'f8'\n\n    if dtype is complex64:\n        return '<c8' if include_byteorder else 'c8'\n\n    if dtype is complex:\n        return '<c16' if include_byteorder else 'c16'\n\n    if isinstance(dtype, timedelta64):\n        if dtype.base == 'generic':\n            return '<m8' if include_byteorder else 'm8'\n        elif dtype.num == 1:\n            return f'<m8[{dtype.base}]' if include_byteorder else f'm8[{dtype.base}]'\n        else:\n            return f'<m8[{dtype.num}{dtype.base}]' if include_byteorder else f'm8[{dtype.num}{dtype.base}]'\n\n    if isinstance(dtype, datetime64):\n        if dtype.base == 'generic':\n            return '<M8' if include_byteorder else 'M8'\n        elif dtype.num == 1:\n            return f'<M8[{dtype.base}]' if include_byteorder else f'M8[{dtype.base}]'\n        else:\n            return f'<M8[{dtype.num}{dtype.base}]' if include_byteorder else f'M8[{dtype.num}{dtype.base}]'\n\n    # TODO: add these when applicable\n    # O -> Python objects\n    # S / a -> zero-terminated bytes\n    # U -> Unicode string\n    # V -> raw data\n\n    compile_error(\"unsupported data type: '\" + dtype.__name__ + \"'\")\n"
  },
  {
    "path": "stdlib/numpy/window.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom .ndarray import ndarray\nfrom .routines import *\nfrom .const import pi\nimport util\n\ndef bartlett(M: int):\n    if M < 1:\n        return empty(0, float)\n    if M == 1:\n        return ones(1, float)\n\n    ans = empty(M, float)\n    p = ans.data\n    i = 0\n\n    for n in range(1 - M, M, 2):\n        if n <= 0:\n            p[i] = 1 + n/(M-1)\n        else:\n            p[i] = 1 - n/(M-1)\n        i += 1\n\n    return ans\n\ndef blackman(M: int):\n    if M < 1:\n        return empty(0, float)\n    if M == 1:\n        return ones(1, float)\n\n    ans = empty(M, float)\n    p = ans.data\n    i = 0\n\n    for n in range(1 - M, M, 2):\n        p[i] = 0.42 + 0.5*util.cos(pi*n/(M-1)) + 0.08*util.cos(2.0*pi*n/(M-1))\n        i += 1\n\n    return ans\n\ndef hamming(M: int):\n    if M < 1:\n        return empty(0, float)\n    if M == 1:\n        return ones(1, float)\n\n    ans = empty(M, float)\n    p = ans.data\n    i = 0\n\n    for n in range(1 - M, M, 2):\n        p[i] = 0.54 + 0.46*util.cos(pi*n/(M-1))\n        i += 1\n\n    return ans\n\ndef hanning(M: int):\n    if M < 1:\n        return empty(0, float)\n    if M == 1:\n        return ones(1, float)\n\n    ans = empty(M, float)\n    p = ans.data\n    i = 0\n\n    for n in range(1 - M, M, 2):\n        p[i] = 0.5 + 0.5*util.cos(pi*n/(M-1))\n        i += 1\n\n    return ans\n\ndef kaiser(M: int, beta: float):\n    if M < 1:\n        return empty(0, float)\n    if M == 1:\n        return ones(1, float)\n\n    alpha = (M-1)/2.0\n    denom = util.i0(beta)\n    ans = empty(M, float)\n    p = ans.data\n    i = 0\n\n    for n in range(0, M):\n        p[i] = util.i0(beta * util.sqrt(1-((n-alpha)/alpha)**2.0))/denom\n        i += 1\n\n    return ans\n"
  },
  {
    "path": "stdlib/numpy/zmath.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n# Adapted from NumPy's \"npy_core_math_complex\"\n\nimport util\n\nSCALED_CEXP_LOWERF = 88.722839  # f\nSCALED_CEXP_UPPERF = 192.69492  # f\nSCALED_CEXP_LOWER = 710.47586007394386\nSCALED_CEXP_UPPER = 1454.9159319953251\n#SCALED_CEXP_LOWERL = 11357.216553474703895  # L\n#SCALED_CEXP_UPPERL = 22756.021937783004509  # L\n\ndef _bad_input(F: type):\n    compile_error(\"internal error: bad input type '\" + F.__name__ + \"'\")\n\ndef scaled_cexp_lower(z):\n    if isinstance(z, complex):\n        return SCALED_CEXP_LOWER\n    elif isinstance(z, complex64):\n        return float32(SCALED_CEXP_LOWERF)\n    else:\n        _bad_input(type(z))\n\ndef scaled_cexp_upper(z):\n    if isinstance(z, complex):\n        return SCALED_CEXP_UPPER\n    elif isinstance(z, complex64):\n        return float32(SCALED_CEXP_UPPERF)\n    else:\n        _bad_input(type(z))\n\ndef scaled_cexp_k(z):\n    if isinstance(z, float):\n        return 1799\n    elif isinstance(z, float32):\n        return 235\n    else:\n        _bad_input(type(z))\n\ndef scaled_cexp(x, y, expt: int, C: type):\n    F = type(x)\n    k = scaled_cexp_k(x)\n    kln2 = F(k * util.LOGE2)\n\n    mant, ex = util.frexp(util.exp(x - kln2))\n    mantcos, excos = util.frexp(util.cos(y))\n    mantsin, exsin = util.frexp(util.sin(y))\n\n    expt += ex + k\n    return C(util.ldexp(mant * mantcos, expt + excos),\n             util.ldexp(mant * mantsin, expt + exsin))\n\ndef cosh_big(z):\n    if isinstance(z, float):\n        return 22.0\n    elif isinstance(z, float32):\n        return float32(9.0)\n    else:\n        _bad_input(type(z))\n\ndef cosh_huge(z):\n    if isinstance(z, float):\n        return 8.9884656743115795e+307\n    elif isinstance(z, float32):\n        return float32(1.70141183e+38)\n    else:\n        _bad_input(type(z))\n\ndef tanh_huge(z):\n    if isinstance(z, float):\n        return 22.0\n    elif isinstance(z, float32):\n        return float32(11.0)\n    else:\n        _bad_input(type(z))\n\ndef arg(z):\n    return util.atan2(z.imag, z.real)\n\ndef exp(z):\n    @pure\n    @C\n    def cexp(r: float, i: float) -> complex:\n        pass\n\n    @C\n    def cnp_cexpf(r: float32, i: float32, z: Ptr[complex64]) -> None:\n        pass\n\n    if isinstance(z, complex):\n        return cexp(z.real, z.imag)\n    elif isinstance(z, complex64):\n        o = complex64()\n        cnp_cexpf(z.real, z.imag, __ptr__(o))\n        return o\n    else:\n        compile_error(\"complex exp(): got non-complex input type\")\n\ndef _exp_impl(z):\n    r = z.real\n    i = z.imag\n    C = type(z)\n    F = type(r)\n\n    ret = C()\n    nan = util.nan(F)\n\n    if util.isfinite(r):\n        if r >= scaled_cexp_lower(z) and r <= scaled_cexp_upper(z):\n            ret = scaled_cexp(r, i, 0, C)\n        else:\n            x = util.exp(r)\n            c = util.cos(i)\n            s = util.sin(i)\n\n            if util.isfinite(i):\n                ret = C(x * c, x * s)\n            else:\n                ret = C(nan, util.copysign(nan, i))\n    elif util.isnan(r):\n        if i == F(0):\n            ret = z\n        else:\n            ret = C(r, util.copysign(nan, i))\n    else:\n        if r > F(0):\n            if i == F(0):\n                return C(r, i)\n            elif util.isfinite(i):\n                c = util.cos(i)\n                s = util.sin(i)\n                ret = C(r * c, r * s)\n            else:\n                # npy_set_floatstatus_invalid()\n                ret = C(r, nan)\n        else:\n            if util.isfinite(i):\n                x = util.exp(r)\n                c = util.cos(i)\n                s = util.sin(i)\n                r = C(x * c, x * s)\n            else:\n                ret = C()\n\n    return ret\n\ndef log(z):\n    @pure\n    @C\n    def clog(r: float, i: float) -> complex:\n        pass\n\n    @C\n    def cnp_clogf(r: float32, i: float32, z: Ptr[complex64]) -> None:\n        pass\n\n    if isinstance(z, complex):\n        return clog(z.real, z.imag)\n    elif isinstance(z, complex64):\n        o = complex64()\n        cnp_clogf(z.real, z.imag, __ptr__(o))\n        return o\n    else:\n        compile_error(\"complex log(): got non-complex input type\")\n\ndef _log_impl(z):\n    C = type(z)\n    F = type(z.real)\n    ax = util.fabs(z.real)\n    ay = util.fabs(z.imag)\n    rr = F()\n    ri = F()\n\n    maxnum = util.maxnum(F)\n    minnum = util.minnum(F)\n    mantdig = util.mantdig(F)\n\n    if ax > maxnum / F(4) or ay > maxnum / F(4):\n        rr = util.log(util.hypot(ax / F(2), ay / F(2))) + F(util.LOGE2)\n    elif ax < minnum and ay < minnum:\n        if ax > F(0) or ay > F(0):\n            rr = util.log(util.hypot(util.ldexp(ax, mantdig),\n                 util.ldexp(ay, mantdig))) - F(mantdig)*F(util.LOGE2)\n        else:\n            rr = F(-1) / z.real\n            rr = util.copysign(rr, F(-1))\n            ri = arg(z)\n            return C(rr, ri)\n    else:\n        h = util.hypot(ax, ay)\n        if F(0.71) <= h <= F(1.73):\n            am = ax if ax > ay else ay\n            an = ay if ax > ay else ax\n            rr = util.log1p((am-F(1))*(am+F(1))+an*an)/F(2)\n        else:\n            rr = util.log(h)\n    ri = arg(z)\n\n    return C(rr, ri)\n\ndef sqrt(z):\n    @pure\n    @C\n    def csqrt(r: float, i: float) -> complex:\n        pass\n\n    @C\n    def cnp_csqrtf(r: float32, i: float32, z: Ptr[complex64]) -> None:\n        pass\n\n    if isinstance(z, complex):\n        return csqrt(z.real, z.imag)\n    elif isinstance(z, complex64):\n        o = complex64()\n        cnp_csqrtf(z.real, z.imag, __ptr__(o))\n        return o\n    else:\n        compile_error(\"complex sqrt(): got non-complex input type\")\n\ndef _sqrt_impl(z):\n    a = z.real\n    b = z.imag\n    C = type(z)\n    F = type(a)\n    thresh = util.maxnum(F) / F(1 + util.SQRT2)\n    scale = False\n    result = C()\n\n    if a == F(0) and b == F(0):\n        return C(F(0), b)\n\n    if util.isinf(b):\n        return C(util.inf(F), b)\n\n    if util.isnan(a):\n        t = (b - b) / (b - b)\n        return C(a, t)\n\n    if util.isinf(a):\n        if util.signbit(a):\n            return C(util.fabs(b - b), util.copysign(a, b))\n        else:\n            return C(a, util.copysign(b - b, b))\n\n    if util.fabs(a) >= thresh or util.fabs(b) >= thresh:\n        a *= F(0.25)\n        b *= F(0.25)\n        scale = True\n    else:\n        scale = False\n\n    if a >= F(0):\n        t = util.sqrt((a + util.hypot(a, b)) * F(0.5))\n        result = C(t, b / (F(2) * t))\n    else:\n        t = util.sqrt((-a + util.hypot(a, b)) * F(0.5))\n        result = C(util.fabs(b) / (F(2) * t), util.copysign(t, b))\n\n    if scale:\n        return C(result.real * F(2), result.imag)\n    else:\n        return result\n\ndef cosh(z):\n    @pure\n    @C\n    def ccosh(r: float, i: float) -> complex:\n        pass\n\n    @C\n    def cnp_ccoshf(r: float32, i: float32, z: Ptr[complex64]) -> None:\n        pass\n\n    if isinstance(z, complex):\n        return ccosh(z.real, z.imag)\n    elif isinstance(z, complex64):\n        o = complex64()\n        cnp_ccoshf(z.real, z.imag, __ptr__(o))\n        return o\n    else:\n        compile_error(\"complex cosh(): got non-complex input type\")\n\ndef _cosh_impl(z):\n    x = z.real\n    y = z.imag\n    xfinite = util.isfinite(x)\n    yfinite = util.isfinite(y)\n    big = cosh_big(x)\n    huge = cosh_huge(x)\n    C = type(z)\n    F = type(x)\n\n    if xfinite and yfinite:\n        if y == F(0):\n            return C(util.cosh(x), x * y)\n        absx = util.fabs(x)\n        if absx < big:\n            return C(util.cosh(x) * util.cos(y),\n                     util.sinh(x) * util.sin(y))\n\n        if absx < scaled_cexp_lower(z):\n            h = util.exp(absx) * F(0.5)\n            return C(h * util.cos(y),\n                     util.copysign(h, x) * util.sin(y))\n        elif absx < scaled_cexp_upper(z):\n            z = scaled_cexp(absx, y, -1, C)\n            return C(z.real, z.imag * util.copysign(F(1), x))\n        else:\n            h = huge * x\n            return C(h * h * util.cos(y), h * util.sin(y))\n\n\n    if x == F(0) and not yfinite:\n        return C(y - y, util.copysign(F(0), x * (y - y)))\n\n    if y == F(0) and not xfinite:\n        return C(x * x, util.copysign(F(0), x) * y)\n\n    if xfinite and not yfinite:\n        return C(y - y, x * (y - y))\n\n    if util.isinf(x):\n        if not yfinite:\n            return C(x * x, x * (y - y))\n        return C((x * x) * util.cos(y), x * util.sin(y))\n\n    return C((x * x) * (y - y), (x + x) * (y - y))\n\ndef sinh(z):\n    @pure\n    @C\n    def csinh(r: float, i: float) -> complex:\n        pass\n\n    @C\n    def cnp_csinhf(r: float32, i: float32, z: Ptr[complex64]) -> None:\n        pass\n\n    if isinstance(z, complex):\n        return csinh(z.real, z.imag)\n    elif isinstance(z, complex64):\n        o = complex64()\n        cnp_csinhf(z.real, z.imag, __ptr__(o))\n        return o\n    else:\n        compile_error(\"complex sinh(): got non-complex input type\")\n\ndef _sinh_impl(z):\n    x = z.real\n    y = z.imag\n    xfinite = util.isfinite(x)\n    yfinite = util.isfinite(y)\n    big = cosh_big(x)\n    huge = cosh_huge(x)\n    C = type(z)\n    F = type(x)\n\n    if xfinite and yfinite:\n        if y == F(0):\n            return C(util.sinh(x), y)\n        absx = util.fabs(x)\n        if absx < big:\n            return C(util.sinh(x) * util.cos(y),\n                     util.cosh(x) * util.sin(y))\n\n        if absx < scaled_cexp_lower(z):\n            h = util.exp(absx) * F(0.5)\n            return C(util.copysign(h, x) * util.cos(y),\n                     h * util.sin(y))\n        elif absx < scaled_cexp_upper(z):\n            z = scaled_cexp(absx, y, -1, C)\n            return C(z.real * util.copysign(F(1), x), z.imag)\n        else:\n            h = huge * x\n            return C(h * util.cos(y), h * h * util.sin(y))\n\n\n    if x == F(0) and not yfinite:\n        return C(util.copysign(F(0), x * (y - y)), y - y)\n\n    if y == F(0) and not xfinite:\n        if util.isnan(x):\n            return z\n        return C(x, util.copysign(F(0), y))\n\n    if xfinite and not yfinite:\n        return C(y - y, x * (y - y))\n\n    if not xfinite and not util.isnan(x):\n        if not yfinite:\n            return C(x * x, x * (y - y))\n        return C(x * util.cos(y),\n                 util.inf(F) * util.sin(y))\n\n    return C((x * x) * (y - y), (x + x) * (y - y))\n\ndef tanh(z):\n    @pure\n    @C\n    def ctanh(r: float, i: float) -> complex:\n        pass\n\n    @C\n    def cnp_ctanhf(r: float32, i: float32, z: Ptr[complex64]) -> None:\n        pass\n\n    if isinstance(z, complex):\n        return ctanh(z.real, z.imag)\n    elif isinstance(z, complex64):\n        o = complex64()\n        cnp_ctanhf(z.real, z.imag, __ptr__(o))\n        return o\n    else:\n        compile_error(\"complex tanh(): got non-complex input type\")\n\ndef _tanh_impl(z):\n    x = z.real\n    y = z.imag\n    xfinite = util.isfinite(x)\n    yfinite = util.isfinite(y)\n    huge = tanh_huge(x)\n    C = type(z)\n    F = type(x)\n\n    if not util.isfinite(x):\n        if util.isnan(x):\n            return C(x, y if y == F(0) else x * y)\n        return C(util.copysign(F(1), x),\n                 util.copysign(F(0), y if util.isinf(y) else util.sin(y) * util.cos(y)))\n\n    if not util.isfinite(y):\n        return C(y - y, y - y)\n\n    if util.fabs(x) >= huge:\n        exp_mx = util.exp(-util.fabs(x))\n        return C(util.copysign(F(1), x),\n                 F(4) * util.sin(y) * util.cos(y) *\n                        exp_mx * exp_mx)\n\n    t = util.tan(y)\n    beta = F(1) + t * t\n    s = util.sinh(x)\n    rho = util.sqrt(F(1) + s * s)\n    denom = F(1) + beta * s * s\n    return C((beta * rho * s) / denom, t / denom)\n\ndef _f(a, b, hypot_a_b):\n    F = type(a)\n\n    if b < F(0):\n        return (hypot_a_b - b) / F(2)\n\n    if b == F(0):\n        return a / F(2)\n\n    return a * a / (hypot_a_b + b) / F(2);\n\ndef _hard_work_params(F: type):  # -> (A_crossover, B_crossover, FOUR_SQRT_MIN)\n    if F is float:\n        return (10.0, 0.6417, 5.9666725849601654e-154)\n    elif F is float32:\n        return (float32(10.0), float32(0.6417), float32(4.3368086899420177e-19))\n    else:\n        _bad_input(F)\n\ndef _do_hard_work(x, y):  # -> (rx, B_is_usable, B, sqrt_A2my2, new_y)\n    F = type(x)\n    A_crossover, B_crossover, four_sqrt_min = _hard_work_params(F)\n    eps = util.eps(F)\n\n    rx = F()\n    sqrt_A2my2 = F()\n\n    R = util.hypot(x, y + F(1))\n    S = util.hypot(x, y - F(1))\n    A = (R + S) / F(2)\n    B = F()\n\n    if A < F(1):\n        A = F(1)\n\n    if A < A_crossover:\n        if y == F(1) and x < eps * eps / F(128):\n            rx = util.sqrt(x)\n        elif x >= eps * util.fabs(y - F(1)):\n            Am1 = _f(x, F(1) + y, R) + _f(x, F(1) - y, S)\n            rx = util.log1p(Am1 + util.sqrt(Am1 * (A + F(1))))\n        elif y < F(1):\n            rx = x / util.sqrt((F(1) - y) * (F(1) + y))\n        else:\n            rx = util.log1p(y - F(1) + util.sqrt((y - F(1)) * (y + F(1))))\n    else:\n        rx = util.log(A + util.sqrt(A * A - F(1)))\n\n    new_y = y\n    B_is_usable = False\n\n    if y < four_sqrt_min:\n        B_is_usable = False\n        sqrt_A2my2 = A * (F(2) / eps)\n        new_y = y * (F(2) / eps)\n        return rx, B_is_usable, B, sqrt_A2my2, new_y\n\n    B = y / A\n    B_is_usable = True\n\n    if B > B_crossover:\n        B_is_usable = False\n\n        if y == F(1) and x < eps / F(128):\n            sqrt_A2my2 = util.sqrt(x) * util.sqrt((A + y) / F(2))\n        elif x >= eps * util.fabs(y - F(1)):\n            Amy = _f(x, y + F(1), R) + _f(x, y - F(1), S)\n            sqrt_A2my2 = util.sqrt(Amy * (A + y))\n        elif y > F(1):\n            sqrt_A2my2 = (x * (F(4) / eps / eps) * y /\n                util.sqrt((y + F(1)) * (y - F(1))))\n            new_y = y * (F(4) / eps / eps)\n        else:\n            sqrt_A2my2 = util.sqrt((F(1) - y) * (F(1) + y))\n\n    return rx, B_is_usable, B, sqrt_A2my2, new_y\n\ndef _clog_for_large_values_params(F: type):  # -> (QUARTER_SQRT_MAX, SQRT_MIN)\n    if F is float:\n        return (3.3519519824856489e+153, 1.4916681462400413e-154)\n    elif F is float32:\n        return (float32(4.611685743549481e+18), float32(1.0842021724855044e-19))\n    else:\n        _bad_input(F)\n\ndef _clog_for_large_values(x: F, y: F, F: type):\n    quarter_sqrt_max, sqrt_min = _clog_for_large_values_params(F)\n    rr = F()\n    ri = F()\n\n    ax = util.fabs(x)\n    ay = util.fabs(y)\n    if ax < ay:\n        ax, ay = ay, ax\n\n    if ax > util.maxnum(F) / F(2):\n        rr = util.log(util.hypot(x / F(util.E), y / F(util.E))) + F(1)\n    elif ax > quarter_sqrt_max or ay < sqrt_min:\n        rr = util.log(util.hypot(x, y))\n    else:\n        rr = util.log(ax * ax + ay * ay) / F(2)\n    ri = util.atan2(y, x)\n\n    return rr, ri\n\ndef _acos_params(F: type):  # -> (SQRT_6_EPSILON, pio2_lo)\n    if F is float:\n        return (3.65002414998885671e-08, 6.1232339957367659e-17)\n    elif F is float32:\n        return (float32(8.4572793338e-4), float32(7.5497899549e-9))\n    else:\n        _bad_input(F)\n\ndef acos(z):\n    @pure\n    @C\n    def cacos(r: float, i: float) -> complex:\n        pass\n\n    @C\n    def cnp_cacosf(r: float32, i: float32, z: Ptr[complex64]) -> None:\n        pass\n\n    if isinstance(z, complex):\n        return cacos(z.real, z.imag)\n    elif isinstance(z, complex64):\n        o = complex64()\n        cnp_cacosf(z.real, z.imag, __ptr__(o))\n        return o\n    else:\n        compile_error(\"complex acos(): got non-complex input type\")\n\ndef _acos_impl(z):\n    x = z.real\n    y = z.imag\n    C = type(z)\n    F = type(x)\n    sqrt_6_epsilon, pio2_lo = _acos_params(F)\n    pio2_hi = F(util.PI_2)\n    eps = util.eps(F)\n    recip_epsilon = F(1) / eps\n\n    sx = util.signbit(x)\n    sy = util.signbit(y)\n    ax = util.fabs(x)\n    ay = util.fabs(y)\n\n    rx = F()\n    ry = F()\n\n    if util.isnan(x) or util.isnan(y):\n        if util.isinf(x):\n            return C(y + y, -util.inf(F))\n\n        if util.isinf(y):\n            return C(x + x, -y)\n\n        if x == F(0):\n            return C(pio2_hi + pio2_lo, y + y)\n\n        return C(util.nan(F), util.nan(F))\n\n    if ax > recip_epsilon or ay > recip_epsilon:\n        wx, wy = _clog_for_large_values(x, y)\n        rx = util.fabs(wy)\n        ry = wx + F(util.LOGE2)\n        if not sy:\n            ry = -ry\n        return C(rx, ry)\n\n    if x == F(1) and y == F(0):\n        return C(F(0), -y)\n\n    # raise inexact\n\n    if ax < sqrt_6_epsilon / F(4) and ay < sqrt_6_epsilon / F(4):\n        return C(pio2_hi - (x - pio2_lo), -y)\n\n    ry, B_is_usable, B, sqrt_A2my2, new_x = _do_hard_work(ay, ax)\n    if B_is_usable:\n        if not sx:\n            rx = util.acos(B)\n        else:\n            rx = util.acos(-B)\n    else:\n        if not sx:\n            rx = util.atan2(sqrt_A2my2, new_x)\n        else:\n            rx = util.atan2(sqrt_A2my2, -new_x)\n\n    if not sy:\n        ry = -ry\n\n    return C(rx, ry)\n\ndef acosh(z):\n    @pure\n    @C\n    def cacosh(r: float, i: float) -> complex:\n        pass\n\n    @C\n    def cnp_cacoshf(r: float32, i: float32, z: Ptr[complex64]) -> None:\n        pass\n\n    if isinstance(z, complex):\n        return cacosh(z.real, z.imag)\n    elif isinstance(z, complex64):\n        o = complex64()\n        cnp_cacoshf(z.real, z.imag, __ptr__(o))\n        return o\n    else:\n        compile_error(\"complex acosh(): got non-complex input type\")\n\ndef _acosh_impl(z):\n    C = type(z)\n    w = acos(z)\n    rx = w.real\n    ry = w.imag\n\n    if util.isnan(rx) and util.isnan(ry):\n        return C(ry, rx)\n\n    if util.isnan(rx):\n        return C(util.fabs(ry), rx)\n\n    if util.isnan(ry):\n        return C(ry, ry)\n\n    return C(util.fabs(ry), util.copysign(rx, z.imag))\n\ndef _asinh_params(F: type):  # -> SQRT_6_EPSILON\n    if F is float:\n        return 3.65002414998885671e-08\n    elif F is float32:\n        return float32(8.4572793338e-4)\n    else:\n        _bad_input(F)\n\ndef asinh(z):\n    @pure\n    @C\n    def casinh(r: float, i: float) -> complex:\n        pass\n\n    @C\n    def cnp_casinhf(r: float32, i: float32, z: Ptr[complex64]) -> None:\n        pass\n\n    if isinstance(z, complex):\n        return casinh(z.real, z.imag)\n    elif isinstance(z, complex64):\n        o = complex64()\n        cnp_casinhf(z.real, z.imag, __ptr__(o))\n        return o\n    else:\n        compile_error(\"complex asinh(): got non-complex input type\")\n\ndef _asinh_impl(z):\n    x = z.real\n    y = z.imag\n    C = type(z)\n    F = type(x)\n    sqrt_6_epsilon = _asinh_params(F)\n    recip_epsilon = F(1) / util.eps(F)\n\n    ax = util.fabs(x)\n    ay = util.fabs(y)\n    rx = F()\n    ry = F()\n\n    if util.isnan(x) or util.isnan(y):\n        if util.isinf(x):\n            return C(x, y + y)\n\n        if util.isinf(y):\n            return C(y, x + x)\n\n        if y == F(0):\n            return C(x + x, y)\n\n        return C(util.nan(F), util.nan(F))\n\n    if ax > recip_epsilon or ay > recip_epsilon:\n        if not util.signbit(x):\n            wx, wy = _clog_for_large_values(x, y)\n            wx += F(util.LOGE2)\n        else:\n            wx, wy = _clog_for_large_values(-x, -y)\n            wx += F(util.LOGE2)\n\n        return C(util.copysign(wx, x), util.copysign(wy, y))\n\n    if x == F(0) and y == F(0):\n        return z\n\n    # raise_inexact()\n\n    if ax < sqrt_6_epsilon / F(4) and ay < sqrt_6_epsilon / F(4):\n        return z\n\n    rx, B_is_usable, B, sqrt_A2my2, new_y = _do_hard_work(ax, ay)\n    if B_is_usable:\n        ry = util.asin(B)\n    else:\n        ry = util.atan2(new_y, sqrt_A2my2)\n\n    return C(util.copysign(rx, x), util.copysign(ry, y))\n\ndef _sum_squares_params(F: type):  # -> SQRT_MIN\n    if F is float:\n        return 1.4916681462400413e-154\n    elif F is float32:\n        return float32(1.0842022e-19)\n    else:\n        _bad_input(F)\n\ndef _sum_squares(x, y):\n    F = type(x)\n    sqrt_min = _sum_squares_params(F)\n\n    if y < sqrt_min:\n        return x * x\n\n    return x * x + y * y\n\ndef _bitcast(x, T: type):\n    return Ptr[T](__ptr__(x).as_byte())[0]\n\ndef _get_float_word(x: float32):\n    return _bitcast(x, u32)\n\ndef _set_float_word(x: u32):\n    return _bitcast(x, float32)\n\ndef _real_part_reciprocal32(x, y):\n    F = type(x)\n    bias = util.maxexp(F) - 1\n    cutoff = util.mantdig(F) // 2 + 1\n    hx = _get_float_word(x)\n    ix = hx & u32(0x7f800000)\n    hy = _get_float_word(y)\n    iy = hy & u32(0x7f800000)\n\n    if ix - iy >= u32(cutoff << 23) or util.isinf(x):\n        return F(1) / x\n\n    if iy - ix >= u32(cutoff << 23):\n        return x / y / y\n\n    if ix <= u32((bias + util.maxexp(F) // 2 - cutoff) << 23):\n        return (x / (x * x + y * y))\n\n    scale = _set_float_word(u32(0x7f800000) - ix)\n    x *= scale\n    y *= scale\n    return x / (x * x + y * y) * scale\n\ndef _get_low_word(x):\n    return _bitcast(x, Tuple[u32, u32])[0]\n\ndef _get_high_word(x):\n    return _bitcast(x, Tuple[u32, u32])[1]\n\ndef _set_high_word(d, v):\n    w = (_get_low_word(d), v)\n    return _bitcast(w, float)\n\ndef _real_part_reciprocal64(x, y):\n    F = type(x)\n    bias = util.maxexp(F) - 1\n    cutoff = util.mantdig(F) // 2 + 1\n    hx = _get_high_word(x)\n    ix = hx & u32(0x7ff00000)\n    hy = _get_high_word(y)\n    iy = hy & u32(0x7ff00000)\n\n    if ix - iy >= u32(cutoff << 20) or util.isinf(x):\n        return F(1) / x\n\n    if iy - ix >= u32(cutoff << 20):\n        return x / y / y\n\n    if ix <= u32((bias + util.maxexp(F) // 2 - cutoff) << 20):\n        return (x / (x * x + y * y))\n\n    scale = _set_high_word(1.0, u32(0x7ff00000) - ix)\n    x *= scale\n    y *= scale\n    return x / (x * x + y * y) * scale\n\ndef _real_part_reciprocal(x, y):\n    if isinstance(x, float) and isinstance(y, float):\n        return _real_part_reciprocal64(x, y)\n    elif isinstance(x, float32) and isinstance(y, float32):\n        return _real_part_reciprocal32(x, y)\n    else:\n        _bad_input(type(x))\n\ndef _atanh_params(F: type):  # -> (SQRT_3_EPSILON, pio2_lo)\n    if F is float:\n        return (2.5809568279517849e-8, 6.1232339957367659e-17)\n    elif F is float32:\n        return (float32(5.9801995673e-4), float32(7.5497899549e-9))\n    else:\n        _bad_input(F)\n\ndef atanh(z):\n    @pure\n    @C\n    def catanh(r: float, i: float) -> complex:\n        pass\n\n    @C\n    def cnp_catanhf(r: float32, i: float32, z: Ptr[complex64]) -> None:\n        pass\n\n    if isinstance(z, complex):\n        return catanh(z.real, z.imag)\n    elif isinstance(z, complex64):\n        o = complex64()\n        cnp_catanhf(z.real, z.imag, __ptr__(o))\n        return o\n    else:\n        compile_error(\"complex atanh(): got non-complex input type\")\n\ndef _atanh_impl(z):\n    x = z.real\n    y = z.imag\n    C = type(z)\n    F = type(x)\n    sqrt_3_epsilon, pio2_lo = _atanh_params(F)\n    pio2_hi = F(util.PI_2)\n    recip_epsilon = F(1) / util.eps(F)\n\n    ax = util.fabs(x)\n    ay = util.fabs(y)\n    rx = F()\n    ry = F()\n\n    if y == F(0) and ax <= F(1):\n        return C(util.atanh(x), y)\n\n    if x == F(0):\n        return C(x, util.atan(y))\n\n    if util.isnan(x) or util.isnan(y):\n        if util.isinf(x):\n            return C(util.copysign(F(0), x), y + y)\n\n        if util.isinf(y):\n            return C(util.copysign(F(0), x),\n                     util.copysign(pio2_hi + pio2_lo, y))\n\n        return C(util.nan(F), util.nan(F))\n\n    if ax > recip_epsilon or ay > recip_epsilon:\n        return C(_real_part_reciprocal(x, y),\n                 util.copysign(pio2_hi + pio2_lo, y))\n\n    if ax < sqrt_3_epsilon / F(2) and ay < sqrt_3_epsilon / F(2):\n        # raise_inexact()\n        return z\n\n    if ax == F(1) and ay < util.eps(F):\n        rx = F(util.LOGE2) - util.log(ay) / F(2)\n    else:\n        rx = util.log1p(F(4) * ax / _sum_squares(ax - F(1), ay)) / F(4)\n\n    if ax == F(1):\n        ry = util.atan2(F(2), -ay) / F(2)\n    elif ay < util.eps(F):\n        ry = util.atan2(F(2) * ay, (F(1) - ax) * (F(1) + ax)) / F(2)\n    else:\n        ry = util.atan2(F(2) * ay, (F(1) - ax) * (F(1) + ax) - ay * ay) / F(2)\n\n    return C(util.copysign(rx, x), util.copysign(ry, y))\n\ndef asin(z):\n    @pure\n    @C\n    def casin(r: float, i: float) -> complex:\n        pass\n\n    @C\n    def cnp_casinf(r: float32, i: float32, z: Ptr[complex64]) -> None:\n        pass\n\n    if isinstance(z, complex):\n        return casin(z.real, z.imag)\n    elif isinstance(z, complex64):\n        o = complex64()\n        cnp_casinf(z.real, z.imag, __ptr__(o))\n        return o\n    else:\n        compile_error(\"complex asin(): got non-complex input type\")\n\ndef _asin_impl(z):\n    C = type(z)\n    z = asinh(C(z.imag, z.real))\n    return C(z.imag, z.real)\n\ndef atan(z):\n    @pure\n    @C\n    def catan(r: float, i: float) -> complex:\n        pass\n\n    @C\n    def cnp_catanf(r: float32, i: float32, z: Ptr[complex64]) -> None:\n        pass\n\n    if isinstance(z, complex):\n        return catan(z.real, z.imag)\n    elif isinstance(z, complex64):\n        o = complex64()\n        cnp_catanf(z.real, z.imag, __ptr__(o))\n        return o\n    else:\n        compile_error(\"complex atan(): got non-complex input type\")\n\ndef _atan_impl(z):\n    C = type(z)\n    z = atanh(C(z.imag, z.real))\n    return C(z.imag, z.real)\n\ndef cos(z):\n    @pure\n    @C\n    def ccos(r: float, i: float) -> complex:\n        pass\n\n    @C\n    def cnp_ccosf(r: float32, i: float32, z: Ptr[complex64]) -> None:\n        pass\n\n    if isinstance(z, complex):\n        return ccos(z.real, z.imag)\n    elif isinstance(z, complex64):\n        o = complex64()\n        cnp_ccosf(z.real, z.imag, __ptr__(o))\n        return o\n    else:\n        compile_error(\"complex cos(): got non-complex input type\")\n\ndef _cos_impl(z):\n    C = type(z)\n    return cosh(C(-z.imag, z.real))\n\ndef sin(z):\n    @pure\n    @C\n    def csin(r: float, i: float) -> complex:\n        pass\n\n    @C\n    def cnp_csinf(r: float32, i: float32, z: Ptr[complex64]) -> None:\n        pass\n\n    if isinstance(z, complex):\n        return csin(z.real, z.imag)\n    elif isinstance(z, complex64):\n        o = complex64()\n        cnp_csinf(z.real, z.imag, __ptr__(o))\n        return o\n    else:\n        compile_error(\"complex sin(): got non-complex input type\")\n\ndef _sin_impl(z):\n    C = type(z)\n    z = sinh(C(-z.imag, z.real))\n    return C(z.imag, -z.real)\n\ndef tan(z):\n    @pure\n    @C\n    def ctan(r: float, i: float) -> complex:\n        pass\n\n    @C\n    def cnp_ctanf(r: float32, i: float32, z: Ptr[complex64]) -> None:\n        pass\n\n    if isinstance(z, complex):\n        return ctan(z.real, z.imag)\n    elif isinstance(z, complex64):\n        o = complex64()\n        cnp_ctanf(z.real, z.imag, __ptr__(o))\n        return o\n    else:\n        compile_error(\"complex tan(): got non-complex input type\")\n\ndef _tan_impl(z):\n    C = type(z)\n    z = tanh(C(-z.imag, z.real))\n    return C(z.imag, -z.real)\n\ndef log2(z):\n    C = type(z)\n    F = type(z.real)\n    z = log(z)\n    z = C(z.real * F(util.LOG2E), z.imag * F(util.LOG2E))\n    return z\n\ndef log10(z):\n    C = type(z)\n    F = type(z.real)\n    z = log(z)\n    z = C(z.real * F(util.LOG10E), z.imag * F(util.LOG10E))\n    return z\n\ndef exp2(z):\n    C = type(z)\n    F = type(z.real)\n    z = C(z.real * F(util.LOGE2), z.imag * F(util.LOGE2))\n    return exp(z)\n\ndef expm1(z):\n    x = z.real\n    y = z.imag\n    C = type(z)\n    F = type(x)\n    a = util.sin(y / F(2))\n    rx = util.expm1(x) * util.cos(y) - F(2) * a * a\n    ry = util.exp(x) * util.sin(y)\n    return C(rx, ry)\n\ndef log1p(z):\n    x = z.real\n    y = z.imag\n    C = type(z)\n    F = type(x)\n\n    l = util.hypot(x + F(1), y)\n    ry = util.atan2(y, x + F(1))\n    rx = util.log(l)\n    return C(rx, ry)\n"
  },
  {
    "path": "stdlib/openmp.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n# OpenMP interface\n# Ref: https://github.com/llvm/llvm-project/tree/main/openmp\n\nimport internal.static as static\n\nRoutine = Function[[i32, cobj], i32]\n\n_KMP_IDENT_IMB = 0x01\n_KMP_IDENT_KMPC = 0x02\n_KMP_IDENT_AUTOPAR = 0x08\n_KMP_IDENT_ATOMIC_REDUCE = 0x10\n_KMP_IDENT_BARRIER_EXPL = 0x20\n_KMP_IDENT_BARRIER_IMPL = 0x0040\n_KMP_IDENT_BARRIER_IMPL_MASK = 0x01C0\n_KMP_IDENT_BARRIER_IMPL_FOR = 0x0040\n_KMP_IDENT_BARRIER_IMPL_SECTIONS = 0x00C0\n_KMP_IDENT_BARRIER_IMPL_SINGLE = 0x0140\n_KMP_IDENT_BARRIER_IMPL_WORKSHARE = 0x01C0\n_KMP_IDENT_WORK_LOOP = 0x200\n_KMP_IDENT_WORK_SECTIONS = 0x400\n_KMP_IDENT_WORK_DISTRIBUTE = 0x800\n_KMP_IDENT_ATOMIC_HINT_MASK = 0xFF0000\n_KMP_IDENT_ATOMIC_HINT_UNCONTENDED = 0x010000\n_KMP_IDENT_ATOMIC_HINT_CONTENDED = 0x020000\n_KMP_IDENT_ATOMIC_HINT_NONSPECULATIVE = 0x040000\n_KMP_IDENT_ATOMIC_HINT_SPECULATIVE = 0x080000\n_KMP_IDENT_OPENMP_SPEC_VERSION_MASK = 0xFF000000\n\n@tuple\nclass Lock:\n    a1: i32\n    a2: i32\n    a3: i32\n    a4: i32\n    a5: i32\n    a6: i32\n    a7: i32\n    a8: i32\n\n    def __new__() -> Lock:\n        z = i32(0)\n        return Lock(z, z, z, z, z, z, z, z)\n\n@tuple\nclass Ident:\n    reserved_1: i32\n    flags: i32\n    reserved_2: i32\n    reserved_3: i32\n    psource: cobj\n\n    def __new__(flags: int = 0, source: str = \";unknown;unknown;0;0;;\") -> Ident:\n        return Ident(i32(0), i32(flags | _KMP_IDENT_KMPC), i32(0), i32(0), source.ptr)\n\n@tuple\nclass LRData:\n    routine: Routine\n\n@tuple\nclass Task:\n    shareds: cobj\n    routine: Routine\n    flags: i32\n    x: LRData\n    y: LRData\n\n@tuple\nclass TaskWithPrivates:\n    task: Task\n    data: T\n    T: type\n\n@tuple\nclass TaskReductionInput:\n    reduce_shar: cobj\n    reduce_orig: cobj\n    reduce_size: int\n    reduce_init: cobj\n    reduce_fini: cobj\n    reduce_comb: cobj\n    flags: u32\n\n    def __new__(reduce_shar, reduce_orig, reduce_size: int,\n                reduce_init: cobj, reduce_comb: cobj):\n        return TaskReductionInput(reduce_shar.as_byte(), reduce_orig.as_byte(), reduce_size,\n                                  reduce_init, cobj(), reduce_comb, u32(0))\n\n@tuple\nclass TaskReductionInputArray:\n    len: int\n    ptr: Ptr[TaskReductionInput]\n\n    def __setitem__(self, idx: int, x: TaskReductionInput):\n        self.ptr[idx] = x\n\n_DEFAULT_IDENT = Ident()\n_STATIC_LOOP_IDENT = Ident(_KMP_IDENT_WORK_LOOP)\n_REDUCTION_IDENT = Ident(_KMP_IDENT_ATOMIC_REDUCE)\n\ndef _default_loc():\n    return __ptr__(_DEFAULT_IDENT)\n\n_default_loc()\n\ndef _static_loop_loc():\n    return __ptr__(_STATIC_LOOP_IDENT)\n\n_static_loop_loc()\n\ndef _reduction_loc():\n    return __ptr__(_REDUCTION_IDENT)\n\n_reduction_loc()\n\ndef _critical_begin(loc_ref: Ptr[Ident], gtid: int, lck: Ptr[Lock]):\n    from C import __kmpc_critical(Ptr[Ident], i32, Ptr[Lock])\n    __kmpc_critical(loc_ref, i32(gtid), lck)\n\ndef _critical_end(loc_ref: Ptr[Ident], gtid: int, lck: Ptr[Lock]):\n    from C import __kmpc_end_critical(Ptr[Ident], i32, Ptr[Lock])\n    __kmpc_end_critical(loc_ref, i32(gtid), lck)\n\ndef _single_begin(loc_ref: Ptr[Ident], gtid: int):\n    from C import __kmpc_single(Ptr[Ident], i32) -> i32\n    return int(__kmpc_single(loc_ref, i32(gtid)))\n\ndef _single_end(loc_ref: Ptr[Ident], gtid: int):\n    from C import __kmpc_end_single(Ptr[Ident], i32)\n    __kmpc_end_single(loc_ref, i32(gtid))\n\ndef _master_begin(loc_ref: Ptr[Ident], gtid: int):\n    from C import __kmpc_master(Ptr[Ident], i32) -> i32\n    return int(__kmpc_master(loc_ref, i32(gtid)))\n\ndef _master_end(loc_ref: Ptr[Ident], gtid: int):\n    from C import __kmpc_end_master(Ptr[Ident], i32)\n    __kmpc_end_master(loc_ref, i32(gtid))\n\ndef _ordered_begin(loc_ref: Ptr[Ident], gtid: int):\n    from C import __kmpc_ordered(Ptr[Ident], i32)\n    __kmpc_ordered(loc_ref, i32(gtid))\n\ndef _ordered_end(loc_ref: Ptr[Ident], gtid: int):\n    from C import __kmpc_end_ordered(Ptr[Ident], i32)\n    __kmpc_end_ordered(loc_ref, i32(gtid))\n\ndef _taskwait(loc_ref: Ptr[Ident], gtid: int):\n    from C import __kmpc_omp_taskwait(Ptr[Ident], i32)\n    __kmpc_omp_taskwait(loc_ref, i32(gtid))\n\ndef _taskgroup_begin(loc_ref: Ptr[Ident], gtid: int):\n    from C import __kmpc_taskgroup(Ptr[Ident], i32)\n    __kmpc_taskgroup(loc_ref, i32(gtid))\n\ndef _taskgroup_end(loc_ref: Ptr[Ident], gtid: int):\n    from C import __kmpc_end_taskgroup(Ptr[Ident], i32)\n    __kmpc_end_taskgroup(loc_ref, i32(gtid))\n\ndef _task_alloc_size(\n    size_of_task: int,\n    size_of_shareds: int,\n):\n    from C import __kmpc_omp_task_alloc_size(int, int) -> int\n    return __kmpc_omp_task_alloc_size(size_of_task, size_of_shareds)\n\ndef _task_alloc(\n    loc_ref: Ptr[Ident],\n    gtid: int,\n    flags: int,\n    size_of_task: int,\n    size_of_shareds: int,\n    task_entry: Routine,\n):\n    from internal.gc import alloc\n    from C import __kmpc_omp_task_alloc(Ptr[Ident], i32, i32, int, int, Routine, cobj) -> cobj\n\n    taskdata = alloc(_task_alloc_size(size_of_task, size_of_shareds))\n    return __kmpc_omp_task_alloc(\n        loc_ref, i32(gtid), i32(flags), size_of_task, size_of_shareds, task_entry, taskdata\n    )\n\ndef _task_run(loc_ref: Ptr[Ident], gtid: int, new_task: cobj):\n    from C import __kmpc_omp_task(Ptr[Ident], i32, cobj) -> i32\n    return int(__kmpc_omp_task(loc_ref, i32(gtid), new_task))\n\ndef _barrier(loc_ref: Ptr[Ident], gtid: int):\n    from C import __kmpc_barrier(Ptr[Ident], i32)\n    __kmpc_barrier(loc_ref, i32(gtid))\n\ndef _flush(loc_ref: Ptr[Ident]):\n    from C import __kmpc_flush(Ptr[Ident])\n    __kmpc_flush(loc_ref)\n\ndef flush():\n    _flush(_default_loc())\n\ndef _static_init(\n    loc_ref: Ptr[Ident], gtid: int, schedtype: int, loop: range, incr: int, chunk: int\n):\n    from C import __kmpc_for_static_init_8(Ptr[Ident], i32, i32, Ptr[i32], Ptr[int], Ptr[int], Ptr[int], int, int)\n    last = i32(0)\n    lower = 0\n    upper = len(loop) - 1\n    stride = 1\n    __kmpc_for_static_init_8(\n        loc_ref,\n        i32(gtid),\n        i32(schedtype),\n        __ptr__(last),\n        __ptr__(lower),\n        __ptr__(upper),\n        __ptr__(stride),\n        incr,\n        chunk,\n    )\n    return bool(last), range(loop._get(lower), loop._get(upper + 1), loop.step), stride\n\ndef _static_fini(loc_ref: Ptr[Ident], gtid: int):\n    from C import __kmpc_for_static_fini(Ptr[Ident], i32)\n    __kmpc_for_static_fini(loc_ref, i32(gtid))\n\ndef _dynamic_init(\n    loc_ref: Ptr[Ident], gtid: int, schedtype: int, loop: range, chunk: int\n):\n    from C import __kmpc_dispatch_init_8(Ptr[Ident], i32, i32, int, int, int, int)\n    lower = 0\n    upper = len(loop) - 1\n    stride = 1\n    __kmpc_dispatch_init_8(\n        loc_ref, i32(gtid), i32(schedtype), lower, upper, stride, chunk\n    )\n\ndef _dynamic_next(loc_ref: Ptr[Ident], gtid: int, loop: range):\n    from C import __kmpc_dispatch_next_8(Ptr[Ident], i32, Ptr[i32], Ptr[int], Ptr[int], Ptr[int]) -> i32\n    last = i32(0)\n    lower = 0\n    upper = 0\n    stride = 0\n    more = __kmpc_dispatch_next_8(\n        loc_ref,\n        i32(gtid),\n        __ptr__(last),\n        __ptr__(lower),\n        __ptr__(upper),\n        __ptr__(stride),\n    )\n    return (\n        bool(more),\n        bool(last),\n        range(loop._get(lower), loop._get(upper + 1), loop.step),\n    )\n\ndef _dynamic_fini(loc_ref: Ptr[Ident], gtid: int):\n    from C import __kmpc_dispatch_fini_8(Ptr[Ident], i32)\n    __kmpc_dispatch_fini_8(loc_ref, i32(gtid))\n\ndef _reduce(\n    loc_ref: Ptr[Ident],\n    gtid: int,\n    reduce_data: T,\n    reduce_func: cobj,\n    lck: cobj,\n    T: type,\n):\n    from internal.gc import sizeof\n\n    from C import __kmpc_reduce(Ptr[Ident], i32, i32, int, cobj, cobj, cobj) -> i32\n    num_vars = static.len(reduce_data)\n    reduce_size = sizeof(T)\n    return int(\n        __kmpc_reduce(\n            loc_ref,\n            i32(gtid),\n            i32(num_vars),\n            reduce_size,\n            __ptr__(reduce_data).as_byte(),\n            reduce_func,\n            lck,\n        )\n    )\n\ndef _end_reduce(loc_ref: Ptr[Ident], gtid: int, lck: cobj):\n    from C import __kmpc_end_reduce(Ptr[Ident], i32, cobj)\n    __kmpc_end_reduce(loc_ref, i32(gtid), lck)\n\ndef _reduce_nowait(\n    loc_ref: Ptr[Ident],\n    gtid: int,\n    reduce_data: T,\n    reduce_func: cobj,\n    lck: Ptr[Lock],\n    T: type,\n):\n    from internal.gc import sizeof\n\n    from C import __kmpc_reduce_nowait(Ptr[Ident], i32, i32, int, cobj, cobj, Ptr[Lock]) -> i32\n    num_vars = static.len(reduce_data)\n    reduce_size = sizeof(T)\n    return int(\n        __kmpc_reduce_nowait(\n            loc_ref,\n            i32(gtid),\n            i32(num_vars),\n            reduce_size,\n            __ptr__(reduce_data).as_byte(),\n            reduce_func,\n            lck,\n        )\n    )\n\ndef _end_reduce_nowait(loc_ref: Ptr[Ident], gtid: int, lck: Ptr[Lock]):\n    from C import __kmpc_end_reduce_nowait(Ptr[Ident], i32, Ptr[Lock])\n    __kmpc_end_reduce_nowait(loc_ref, i32(gtid), lck)\n\ndef _taskred_init(loc_ref: Ptr[Ident], gtid: int, num: int, data):\n    from C import __kmpc_taskred_modifier_init(Ptr[Ident], i32, i32, i32, cobj) -> cobj\n    return __kmpc_taskred_modifier_init(loc_ref, i32(gtid), i32(0), i32(num), data.as_byte())\n\ndef _taskred_fini(loc_ref: Ptr[Ident], gtid: int):\n    from C import __kmpc_task_reduction_modifier_fini(Ptr[Ident], i32, i32)\n    __kmpc_task_reduction_modifier_fini(loc_ref, i32(gtid), i32(0))\n\n# add tskgrp arg?\ndef _taskred_data(gtid: int, data):\n    from C import __kmpc_task_reduction_get_th_data(i32, cobj, cobj) -> cobj\n    T = type(data)\n    return T(__kmpc_task_reduction_get_th_data(i32(gtid), cobj(), data.as_byte()))\n\ndef _fork_call(microtask: cobj, args):\n    from C import __kmpc_fork_call(Ptr[Ident], i32, cobj, ...)\n    loc_ref = _default_loc()  # TODO: pass real loc?\n    __kmpc_fork_call(loc_ref, i32(1), microtask, __ptr__(args))\n\ndef _static_loop_outline_template(gtid_ptr: Ptr[i32], btid_ptr: Ptr[i32], args):\n    @nonpure\n    def _loop_step():\n        return 1\n\n    @nonpure\n    def _loop_loc_and_gtid(\n        loc_ref: Ptr[Ident], reduction_loc_ref: Ptr[Ident], gtid: int\n    ):\n        pass\n\n    @nonpure\n    def _loop_body_stub(i, args):\n        pass\n\n    @nonpure\n    def _loop_schedule():\n        return (1 << 30) | 35  # nonmonotonic, dynamic chunked\n\n    @nonpure\n    def _loop_shared_updates(args):\n        pass\n\n    @nonpure\n    def _loop_reductions(args):\n        pass\n\n    chunk, start, stop, extra = args[0]\n    step = _loop_step()\n    gtid = int(gtid_ptr[0])\n    loc_ref = _default_loc()\n    static_loop_loc_ref = _static_loop_loc()\n    reduction_loc_ref = _reduction_loc()\n    _loop_loc_and_gtid(loc_ref, reduction_loc_ref, gtid)\n    loop = range(start, stop, step)\n    schedule = _loop_schedule()\n\n    last, subloop, stride = _static_init(\n        static_loop_loc_ref, gtid, schedtype=schedule, loop=loop, incr=1, chunk=1\n    )\n    i = subloop.start\n    stop = min(subloop.stop, loop.stop) if step >= 0 else max(subloop.stop, loop.stop)\n\n    while (step >= 0 and i < stop) or (step < 0 and i > stop):\n        _loop_body_stub(i, extra)\n        i += step\n    _static_fini(static_loop_loc_ref, gtid)\n\n    if last:\n        _loop_shared_updates(extra)\n\n    _loop_reductions(extra)\n\ndef _static_chunked_loop_outline_template(gtid_ptr: Ptr[i32], btid_ptr: Ptr[i32], args):\n    @nonpure\n    def _loop_step():\n        return 1\n\n    @nonpure\n    def _loop_loc_and_gtid(\n        loc_ref: Ptr[Ident], reduction_loc_ref: Ptr[Ident], gtid: int\n    ):\n        pass\n\n    @nonpure\n    def _loop_body_stub(i, args):\n        pass\n\n    @nonpure\n    def _loop_schedule():\n        return (1 << 30) | 35  # nonmonotonic, dynamic chunked\n\n    @nonpure\n    def _loop_shared_updates(args):\n        pass\n\n    @nonpure\n    def _loop_reductions(args):\n        pass\n\n    chunk, start, stop, extra = args[0]\n    step = _loop_step()\n    gtid = int(gtid_ptr[0])\n    loc_ref = _default_loc()\n    static_loop_loc_ref = _static_loop_loc()\n    reduction_loc_ref = _reduction_loc()\n    _loop_loc_and_gtid(loc_ref, reduction_loc_ref, gtid)\n    loop = range(start, stop, step)\n    schedule = _loop_schedule()\n\n    last, subloop, stride = _static_init(\n        static_loop_loc_ref, gtid, schedtype=schedule, loop=loop, incr=1, chunk=chunk\n    )\n    start = subloop.start\n    stop = min(subloop.stop, loop.stop) if step >= 0 else max(subloop.stop, loop.stop)\n\n    while (step >= 0 and start < loop.stop) or (step < 0 and start > loop.stop):\n        i = start\n        while (step >= 0 and i < stop) or (step < 0 and i > stop):\n            _loop_body_stub(i, extra)\n            i += step\n\n        start += stride * step\n        stop += stride * step\n        stop = min(stop, loop.stop) if step >= 0 else max(stop, loop.stop)\n    _static_fini(static_loop_loc_ref, gtid)\n\n    if last:\n        _loop_shared_updates(extra)\n\n    _loop_reductions(extra)\n\ndef _dynamic_loop_outline_template(gtid_ptr: Ptr[i32], btid_ptr: Ptr[i32], args):\n    @nonpure\n    def _loop_step():\n        return 1\n\n    @nonpure\n    def _loop_loc_and_gtid(\n        loc_ref: Ptr[Ident], reduction_loc_ref: Ptr[Ident], gtid: int\n    ):\n        pass\n\n    @nonpure\n    def _loop_body_stub(i, args):\n        pass\n\n    @nonpure\n    def _loop_schedule():\n        return (1 << 30) | 35  # nonmonotonic, dynamic chunked\n\n    @nonpure\n    def _loop_shared_updates(args):\n        pass\n\n    @nonpure\n    def _loop_reductions(args):\n        pass\n\n    @nonpure\n    def _loop_ordered():\n        return False\n\n    chunk, start, stop, extra = args[0]\n    step = _loop_step()\n    gtid = int(gtid_ptr[0])\n    loc_ref = _default_loc()\n    reduction_loc_ref = _reduction_loc()\n    _loop_loc_and_gtid(loc_ref, reduction_loc_ref, gtid)\n    loop = range(start, stop, step)\n    schedule = _loop_schedule()\n    ordered = _loop_ordered()\n\n    _dynamic_init(loc_ref, gtid, schedtype=schedule, loop=loop, chunk=chunk)\n    while True:\n        more, last, subloop = _dynamic_next(loc_ref, gtid, loop)\n        if not more:\n            break\n        i = subloop.start\n        while (step >= 0 and i < subloop.stop) or (step < 0 and i > subloop.stop):\n            _loop_body_stub(i, extra)\n            i += step\n            if ordered:\n                _dynamic_fini(loc_ref, gtid)\n        if last:\n            _loop_shared_updates(extra)\n\n    _loop_reductions(extra)\n\n# P = privates; tuple of types\n# S = shareds; tuple of pointers\ndef _spawn_and_run_task(\n    loc_ref: Ptr[Ident], gtid: int, routine: cobj, priv: P, shared: S, P: type, S: type\n):\n    from internal.gc import sizeof\n\n    TaskThunk = TaskWithPrivates[P]\n    flags = 1\n    size_of_kmp_task_t = sizeof(TaskThunk)\n    size_of_privs = sizeof(P)\n    size_of_shareds = sizeof(S)\n    loc_ref = _default_loc()\n\n    task = Ptr[TaskThunk](\n        _task_alloc(\n            loc_ref, gtid, flags, size_of_kmp_task_t, size_of_shareds, Routine(routine)\n        )\n    )\n    if static.len(shared) != 0:\n        shared_ptr = task[0].task.shareds\n        str.memcpy(shared_ptr, __ptr__(shared).as_byte(), size_of_shareds)\n    if static.len(priv) != 0:\n        priv_ptr = task.as_byte() + sizeof(Task)\n        str.memcpy(priv_ptr, __ptr__(priv).as_byte(), size_of_privs)\n\n    _task_run(loc_ref, gtid, task.as_byte())\n\n# Note: this is different than OpenMP's \"taskloop\" -- this template simply\n# spawns a new task for each loop iteration.\ndef _task_loop_outline_template(gtid_ptr: Ptr[i32], btid_ptr: Ptr[i32], args):\n    def _routine_stub(gtid: i32, data: cobj, P: type, S: type):\n        @nonpure\n        def _task_loop_body_stub(gtid: int, priv, shared):\n            pass\n\n        task = Ptr[TaskWithPrivates[P]](data)[0]\n        priv = task.data\n        gtid64 = int(gtid)\n        if static.len(S) != 0:\n            shared = Ptr[S](task.task.shareds)[0]\n            _task_loop_body_stub(gtid64, priv, shared)\n        else:\n            shared = ()\n            _task_loop_body_stub(gtid64, priv, shared)\n        return i32(0)\n\n    @nonpure\n    def _loop_loc_and_gtid(\n        loc_ref: Ptr[Ident], reduction_loc_ref: Ptr[Ident], gtid: int\n    ):\n        pass\n\n    @nonpure\n    def _fix_privates_and_shareds(i, priv, shared):\n        return priv, shared\n\n    @nonpure\n    def _taskred_setup(args):\n        pass\n\n    @nonpure\n    def _taskred_finish():\n        pass\n\n    @nonpure\n    def _loop_reductions(args):\n        pass\n\n    iterable, priv, shared = args[0]\n    P = type(priv)\n    S = type(shared)\n\n    gtid = int(gtid_ptr[0])\n    loc_ref = _default_loc()\n    reduction_loc_ref = _reduction_loc()\n    _loop_loc_and_gtid(loc_ref, reduction_loc_ref, gtid)\n\n    _taskred_setup(shared)\n\n    if _single_begin(loc_ref, gtid) != 0:\n        _taskgroup_begin(loc_ref, gtid)\n        try:\n            for i in iterable:\n                priv_fixed, shared_fixed = _fix_privates_and_shareds(i, priv, shared)\n                _spawn_and_run_task(\n                    loc_ref, gtid, _routine_stub(P=P, S=S, ...).F.T.__raw__(), priv_fixed, shared_fixed\n                )\n        finally:\n            _taskgroup_end(loc_ref, gtid)\n            _single_end(loc_ref, gtid)\n\n    _taskred_finish()\n    _loop_reductions(shared)\n    _barrier(loc_ref, gtid)\n\n@pure\ndef get_num_threads():\n    from C import omp_get_num_threads() -> i32\n    return int(omp_get_num_threads())\n\n@pure\ndef get_thread_num():\n    from C import omp_get_thread_num() -> i32\n    return int(omp_get_thread_num())\n\n@pure\ndef get_max_threads():\n    from C import omp_get_max_threads() -> i32\n    return int(omp_get_max_threads())\n\n@pure\ndef get_num_procs():\n    from C import omp_get_num_procs() -> i32\n    return int(omp_get_num_procs())\n\ndef set_num_threads(num_threads: int):\n    from C import omp_set_num_threads(i32)\n    omp_set_num_threads(i32(num_threads))\n\n@pure\ndef in_parallel():\n    from C import omp_in_parallel() -> i32\n    return bool(omp_in_parallel())\n\ndef set_dynamic(dynamic_threads: bool = True):\n    from C import omp_set_dynamic(i32)\n    omp_set_dynamic(i32(1 if dynamic_threads else 0))\n\n@pure\ndef get_dynamic():\n    from C import omp_get_dynamic() -> i32\n    return bool(omp_get_dynamic())\n\n@pure\ndef get_cancellation():\n    from C import omp_get_cancellation() -> i32\n    return bool(omp_get_cancellation())\n\ndef set_schedule(kind: str, chunk_size: int = 0):\n    from C import omp_set_schedule(i32, i32)\n    if kind == \"static\":\n        omp_set_schedule(i32(1), i32(chunk_size))\n    elif kind == \"dynamic\":\n        omp_set_schedule(i32(2), i32(chunk_size))\n    elif kind == \"guided\":\n        omp_set_schedule(i32(3), i32(chunk_size))\n    elif kind == \"auto\":\n        if chunk_size != 0:\n            raise ValueError(\"cannot specify chunk size for auto schedule\")\n        omp_set_schedule(i32(4), i32(chunk_size))\n    else:\n        raise ValueError(\n            \"invalid schedule kind; valid ones are: 'static', 'dynamic', 'guided', 'auto'\"\n        )\n\n@pure\ndef get_schedule():\n    from C import omp_get_schedule(Ptr[i32], Ptr[i32])\n    kind_code = i32(0)\n    chunk_size = i32(0)\n    omp_get_schedule(__ptr__(kind_code), __ptr__(chunk_size))\n    idx = int(kind_code)\n    kind = (\n        (\"static\", \"dynamic\", \"guided\", \"auto\")[idx - 1] if 1 < idx <= 4 else \"unknown\"\n    )\n    return kind, int(chunk_size)\n\n@pure\ndef get_thread_limit():\n    from C import omp_get_thread_limit() -> i32\n    return int(omp_get_thread_limit())\n\ndef set_max_active_levels(max_levels: int):\n    from C import omp_set_max_active_levels(i32)\n    omp_set_max_active_levels(i32(max_levels))\n\n@pure\ndef get_max_active_levels():\n    from C import omp_get_max_active_levels() -> i32\n    return int(omp_get_max_active_levels())\n\n@pure\ndef get_level():\n    from C import omp_get_level() -> i32\n    return int(omp_get_level())\n\n@pure\ndef get_ancestor_thread_num(level: int):\n    from C import omp_get_ancestor_thread_num(i32) -> i32\n    return int(omp_get_ancestor_thread_num(i32(level)))\n\n@pure\ndef get_team_size(level: int):\n    from C import omp_get_team_size(i32) -> i32\n    return int(omp_get_team_size(i32(level)))\n\n@pure\ndef get_active_level():\n    from C import omp_get_active_level() -> i32\n    return int(omp_get_active_level())\n\n@pure\ndef in_final():\n    from C import omp_in_final() -> i32\n    return bool(omp_in_final())\n\n@pure\ndef get_proc_bind():\n    from C import omp_get_proc_bind() -> i32\n    result = int(omp_get_proc_bind())\n    if result < 0 or result > 4:\n        return \"unknown\"\n    return (\"false\", \"true\", \"master\", \"close\", \"spread\")[result]\n\ndef set_default_device(device_num: int):\n    from C import omp_set_default_device(i32)\n    omp_set_default_device(i32(device_num))\n\n@pure\ndef get_default_device():\n    from C import omp_get_default_device() -> i32\n    return int(omp_get_default_device())\n\n@pure\ndef get_num_devices():\n    from C import omp_get_num_devices() -> i32\n    return int(omp_get_num_devices())\n\n@pure\ndef get_num_teams():\n    from C import omp_get_num_teams() -> i32\n    return int(omp_get_num_teams())\n\n@pure\ndef get_team_num():\n    from C import omp_get_team_num() -> i32\n    return int(omp_get_team_num())\n\n@pure\ndef is_initial_device():\n    from C import omp_is_initial_device() -> i32\n    return bool(omp_is_initial_device())\n\n@pure\ndef get_wtime():\n    from C import omp_get_wtime() -> float\n    return omp_get_wtime()\n\n@pure\ndef get_wtick():\n    from C import omp_get_wtick() -> float\n    return omp_get_wtick()\n\ndef single(func):\n    def _wrapper(*args, **kwargs):\n        gtid = get_thread_num()\n        loc = _default_loc()\n        if _single_begin(loc, gtid) != 0:\n            try:\n                func(*args, **kwargs)\n            finally:\n                _single_end(loc, gtid)\n\n    return _wrapper\n\ndef master(func):\n    def _wrapper(*args, **kwargs):\n        gtid = get_thread_num()\n        loc = _default_loc()\n        if _master_begin(loc, gtid) != 0:\n            try:\n                func(*args, **kwargs)\n            finally:\n                _master_end(loc, gtid)\n\n    return _wrapper\n\ndef ordered(func):\n    def _wrapper(*args, **kwargs):\n        gtid = get_thread_num()\n        loc = _default_loc()\n        _ordered_begin(loc, gtid)\n        try:\n            func(*args, **kwargs)\n        finally:\n            _ordered_end(loc, gtid)\n\n    return _wrapper\n\n_default_lock = Lock()\n\ndef critical(func):\n    def _wrapper(*args, **kwargs):\n        gtid = get_thread_num()\n        loc = _default_loc()\n        _critical_begin(loc, gtid, __ptr__(_default_lock))\n        try:\n            func(*args, **kwargs)\n        finally:\n            _critical_end(loc, gtid, __ptr__(_default_lock))\n\n    return _wrapper\n\ndef _push_num_threads(num_threads: int):\n    from C import __kmpc_push_num_threads(Ptr[Ident], i32, i32)\n    gtid = get_thread_num()\n    loc = _default_loc()\n    __kmpc_push_num_threads(loc, i32(gtid), i32(num_threads))\n\n@llvm\ndef _atomic_int_add(a: Ptr[int], b: int) -> None:\n    %old = atomicrmw add ptr %a, i64 %b monotonic\n    ret {} {}\n\ndef _atomic_int_mul(a: Ptr[int], b: int):\n    from C import __kmpc_atomic_fixed8_mul(Ptr[Ident], i32, Ptr[int], int)\n    __kmpc_atomic_fixed8_mul(_default_loc(), i32(0), a, b)\n\n@llvm\ndef _atomic_int_and(a: Ptr[int], b: int) -> None:\n    %old = atomicrmw and ptr %a, i64 %b monotonic\n    ret {} {}\n\n@llvm\ndef _atomic_int_or(a: Ptr[int], b: int) -> None:\n    %old = atomicrmw or ptr %a, i64 %b monotonic\n    ret {} {}\n\n@llvm\ndef _atomic_int_xor(a: Ptr[int], b: int) -> None:\n    %old = atomicrmw xor ptr %a, i64 %b monotonic\n    ret {} {}\n\n@llvm\ndef _atomic_int_min(a: Ptr[int], b: int) -> None:\n    %old = atomicrmw min ptr %a, i64 %b monotonic\n    ret {} {}\n\n@llvm\ndef _atomic_int_max(a: Ptr[int], b: int) -> None:\n    %old = atomicrmw max ptr %a, i64 %b monotonic\n    ret {} {}\n\ndef _atomic_float_add(a: Ptr[float], b: float) -> None:\n    from C import __kmpc_atomic_float8_add(Ptr[Ident], i32, Ptr[float], float)\n    __kmpc_atomic_float8_add(_default_loc(), i32(0), a, b)\n\ndef _atomic_float_mul(a: Ptr[float], b: float):\n    from C import __kmpc_atomic_float8_mul(Ptr[Ident], i32, Ptr[float], float)\n    __kmpc_atomic_float8_mul(_default_loc(), i32(0), a, b)\n\ndef _atomic_float_min(a: Ptr[float], b: float):\n    from C import __kmpc_atomic_float8_min(Ptr[Ident], i32, Ptr[float], float)\n    __kmpc_atomic_float8_min(_default_loc(), i32(0), a, b)\n\ndef _atomic_float_max(a: Ptr[float], b: float):\n    from C import __kmpc_atomic_float8_max(Ptr[Ident], i32, Ptr[float], float)\n    __kmpc_atomic_float8_max(_default_loc(), i32(0), a, b)\n\ndef _atomic_float32_add(a: Ptr[float32], b: float32) -> None:\n    from C import __kmpc_atomic_float4_add(Ptr[Ident], i32, Ptr[float32], float32)\n    __kmpc_atomic_float4_add(_default_loc(), i32(0), a, b)\n\ndef _atomic_float32_mul(a: Ptr[float32], b: float32):\n    from C import __kmpc_atomic_float4_mul(Ptr[Ident], i32, Ptr[float32], float32)\n    __kmpc_atomic_float4_mul(_default_loc(), i32(0), a, b)\n\ndef _atomic_float32_min(a: Ptr[float32], b: float32) -> None:\n    from C import __kmpc_atomic_float4_min(Ptr[Ident], i32, Ptr[float32], float32)\n    __kmpc_atomic_float4_min(_default_loc(), i32(0), a, b)\n\ndef _atomic_float32_max(a: Ptr[float32], b: float32) -> None:\n    from C import __kmpc_atomic_float4_max(Ptr[Ident], i32, Ptr[float32], float32)\n    __kmpc_atomic_float4_max(_default_loc(), i32(0), a, b)\n\ndef _range_len(start: int, stop: int, step: int):\n    if step > 0 and start < stop:\n        return 1 + (stop - 1 - start) // step\n    elif step < 0 and start > stop:\n        return 1 + (start - 1 - stop) // (-step)\n    else:\n        return 0\n\ndef for_par(\n    num_threads: int = -1,\n    chunk_size: int = -1,\n    schedule: Literal[str] = \"static\",\n    ordered: Literal[bool] = False,\n    collapse: Literal[int] = 0,\n    gpu: Literal[bool] = False,\n):\n    pass\n"
  },
  {
    "path": "stdlib/operator.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\nlt = lambda a, b: a < b\nle = lambda a, b: a <= b\neq = lambda a, b: a == b\nne = lambda a, b: a != b\ngt = lambda a, b: a > b\nge = lambda a, b: a >= b\n__lt__ = lt\n__le__ = le\n__eq__ = eq\n__ne__ = ne\n__gt__ = gt\n__ge__ = ge\n\ndef not_(a) -> bool:\n    if hasattr(a, \"__bool__\"):\n        return not bool(a)\n    elif hasattr(a, \"__len__\"):\n        return len(a) == 0\n    else:\n        compile_error(\"argument has no __bool__ or __len__ methods\")\n\ndef truth(a) -> bool:\n    return bool(a)\n\ndef is_(a, b) -> bool:\n    return a is b\n\ndef is_not(a, b) -> bool:\n    return a is not b\n\ndef abs(a):\n    return a.__abs__()\n\n__abs__ = abs\n\ndef add(a, b):\n    return a + b\n\n__add__ = add\n\ndef and_(a, b):\n    return a & b\n\n__and__ = and_\n\ndef floordiv(a, b):\n    return a // b\n\n__floordiv__ = floordiv\n\ndef index(a):\n    return a.__index__()\n\n__index__ = index\n\ndef inv(a):\n    return ~a\n\ninvert = inv\n__inv__ = inv\n__invert__ = inv\n\ndef lshift(a, b):\n    return a << b\n\n__lshift__ = lshift\n\ndef mod(a, b):\n    return a % b\n\n__mod__ = mod\n\ndef mul(a, b):\n    return a * b\n\n__mul__ = mul\n\ndef matmul(a, b):\n    return a @ b\n\n__matmul__ = matmul\n\ndef neg(a):\n    return -a\n\n__neg__ = neg\n\ndef or_(a, b):\n    return a | b\n\n__or__ = or_\n\ndef pos(a):\n    return +a\n\n__pos__ = pos\n\ndef pow(a, b):\n    return a ** b\n\n__pow__ = pow\n\ndef rshift(a, b):\n    return a >> b\n\n__rshift__ = rshift\n\ndef sub(a, b):\n    return a - b\n\n__sub__ = sub\n\ndef truediv(a, b):\n    return a / b\n\n__truediv__ = truediv\n\ndef xor(a, b):\n    return a ^ b\n\n__xor__ = xor\n\ndef concat(a, b):\n    return a + b\n\n__concat__ = concat\n\ndef contains(a, b):\n    return b in a  # intentionally reversed\n\n__contains__ = contains\n\ndef countOf(a, b):\n    n = 0\n    for x in a:\n        if x == b:\n            n += 1\n    return n\n\ndef delitem(a, b):\n    del a[b]\n\n__delitem__ = delitem\n\ndef getitem(a, b):\n    return a[b]\n\n__getitem__ = getitem\n\ndef indexOf(a, b):\n    n = 0\n    for x in a:\n        if x == b:\n            return n\n        n += 1\n    raise ValueError(f\"sequence.index(x): x not in sequence\")\n\ndef setitem(a, b, c):\n    a[b] = c\n\n__setitem__ = setitem\n\ndef length_hint(a, default=0):\n    if hasattr(a, \"__len__\"):\n        return len(a)\n    elif hasattr(a, \"__length_hint__\"):\n        return a.__length_hint__()\n    else:\n        return default\n\ndef attrgetter(attr: Literal[str]):\n    def getter(obj):\n        return getattr(obj, attr)\n    return getter\n\ndef itemgetter(*items):\n    if static.len(items) == 1:\n        item = items[0]\n\n        def g(obj):\n            return obj[item]\n\n        return g\n    else:\n\n        def g(obj):\n            return tuple(obj[item] for item in items)\n\n        return g\n\n@overload\ndef itemgetter(item: Literal[int]):\n    return lambda o: o[item]\n\ndef methodcaller(name: Literal[str], *args, **kwargs):\n    def caller(obj):\n        return getattr(obj, name)(*args, **kwargs)\n    return caller\n\ndef iadd(a, b):\n    a += b\n    return a\n\n__iadd__ = iadd\n\ndef iand(a, b):\n    a &= b\n    return a\n\n__iand__ = iand\n\ndef iconcat(a, b):\n    a += b\n    return a\n\n__iconcat__ = iconcat\n\ndef ifloordiv(a, b):\n    a //= b\n    return a\n\n__ifloordiv__ = ifloordiv\n\ndef ilshift(a, b):\n    a <<= b\n    return a\n\n__ilshift__ = ilshift\n\ndef imod(a, b):\n    a %= b\n    return a\n\n__imod__ = imod\n\ndef imul(a, b):\n    a *= b\n    return a\n\n__imul__ = imul\n\ndef imatmul(a, b):\n    a @= b\n    return a\n\n__imatmul__ = imatmul\n\ndef ior(a, b):\n    a |= b\n    return a\n\n__ior__ = ior\n\ndef ipow(a, b):\n    a **= b\n    return a\n\n__ipow__ = ipow\n\ndef irshift(a, b):\n    a >>= b\n    return a\n\n__irshift__ = irshift\n\ndef isub(a, b):\n    a -= b\n    return a\n\n__isub__ = isub\n\ndef itruediv(a, b):\n    a /= b\n    return a\n\n__itruediv__ = itruediv\n\ndef ixor(a, b):\n    a ^= b\n    return a\n\n__ixor__ = ixor\n"
  },
  {
    "path": "stdlib/os/__init__.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\ndef system(cmd: str) -> int:\n    return _C.system(cmd.c_str())\n\nSEEK_SET = 0\nSEEK_CUR = 1\nSEEK_END = 2\n\n@tuple\nclass EnvMap:\n    _map: Dict[str, str]\n\n    def __new__() -> EnvMap:\n        return EnvMap(Dict[str, str]())\n\n    def _init_if_needed(self):\n        if len(self._map) == 0:\n            env = _C.seq_env()\n            p = env[0]\n            i = 0\n            while p:\n                s = str.from_ptr(p)\n                if s:\n                    j = 0\n                    found = False\n                    while j < len(s):\n                        if s[j] == \"=\":\n                            found = True\n                            break\n                        j += 1\n                    k = s[0:j] if found else s\n                    v = s[j + 1 :] if found else \"\"\n                    self._map[k] = v\n                i += 1\n                p = env[i]\n\n    def __getitem__(self, key: str) -> str:\n        self._init_if_needed()\n        return self._map[key]\n\n    def __repr__(self) -> str:\n        self._init_if_needed()\n        return repr(self._map)\n\n    def __contains__(self, key: str) -> bool:\n        self._init_if_needed()\n        return key in self._map\n\n    def __iter__(self) -> Generator[Tuple[str, str]]:\n        self._init_if_needed()\n        return self._map.items()\n\nenviron = EnvMap()\n\ndef getenv(key: str, default: str = \"\") -> str:\n    return environ[key] if key in environ else default\n\ndef mkdir(name: str, mode: int = 0x1FF) -> int:\n    # TODO: use errno\n    from C import mkdir(cobj, int) -> int\n    ret = mkdir(name.ptr, mode)\n    if ret != 0:\n        raise OSError(\"mkdir failed\")\n"
  },
  {
    "path": "stdlib/os/path.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\ndef splitext(p: str) -> Tuple[str, str]:\n    \"\"\"\n    Split the extension from a pathname.\n    Extension is everything from the last dot to the end, ignoring\n    leading dots.  Returns \"(root, ext)\"; ext may be empty.\"\"\"\n    sep = \"/\"\n    extsep = \".\"\n\n    sepIndex = p.rfind(sep)\n    dotIndex = p.rfind(extsep)\n    if dotIndex > sepIndex:\n        # skip all leading dots\n        filenameIndex = sepIndex + 1\n        while filenameIndex < dotIndex:\n            if p[filenameIndex : filenameIndex + 1] != extsep:\n                return p[:dotIndex], p[dotIndex:]\n            filenameIndex += 1\n    return p, p[:0]\n"
  },
  {
    "path": "stdlib/pickle.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom internal.file import _gz_errcheck\nfrom internal.gc import sizeof, atomic\n\ndef pickle(x: T, jar: Jar, T: type):\n    x.__pickle__(jar)\n\ndef unpickle(jar: Jar, T: type) -> T:\n    return T.__unpickle__(jar)\n\ndef dump(x: T, f, T: type):\n    x.__pickle__(f.fp)\n\ndef load(f, T: type) -> T:\n    return T.__unpickle__(f.fp)\n\ndef _write_raw(jar: Jar, p: cobj, n: int):\n    LIMIT = 0x7FFFFFFF\n    while n > 0:\n        b = n if n < LIMIT else LIMIT\n        status = int(_C.gzwrite(jar, p, u32(b)))\n        if status != b:\n            _gz_errcheck(jar)\n            raise IOError(f\"pickle error: gzwrite returned {status}\")\n        p += b\n        n -= b\n\ndef _read_raw(jar: Jar, p: cobj, n: int):\n    LIMIT = 0x7FFFFFFF\n    while n > 0:\n        b = n if n < LIMIT else LIMIT\n        status = int(_C.gzread(jar, p, u32(b)))\n        if status != b:\n            _gz_errcheck(jar)\n            raise IOError(f\"pickle error: gzread returned {status}\")\n        p += b\n        n -= b\n\ndef _write(jar: Jar, x: T, T: type):\n    y = __ptr__(x)\n    _write_raw(jar, y.as_byte(), sizeof(T))\n\ndef _read(jar: Jar, T: type) -> T:\n    x = T()\n    y = __ptr__(x)\n    _read_raw(jar, y.as_byte(), sizeof(T))\n    return x\n\n# Extend core types to allow pickling\n\n@extend\nclass int:\n    def __pickle__(self, jar: Jar):\n        _write(jar, self)\n\n    def __unpickle__(jar: Jar) -> int:\n        return _read(jar, int)\n\n@extend\nclass Int:\n    def __pickle__(self, jar: Jar):\n        _write(jar, self)\n\n    def __unpickle__(jar: Jar) -> Int[N]:\n        return _read(jar, Int[N])\n\n@extend\nclass UInt:\n    def __pickle__(self, jar: Jar):\n        _write(jar, self)\n\n    def __unpickle__(jar: Jar) -> UInt[N]:\n        return _read(jar, UInt[N])\n\n@extend\nclass float:\n    def __pickle__(self, jar: Jar):\n        _write(jar, self)\n\n    def __unpickle__(jar: Jar) -> float:\n        return _read(jar, float)\n\n@extend\nclass float32:\n    def __pickle__(self, jar: Jar):\n        _write(jar, self)\n\n    def __unpickle__(jar: Jar) -> float32:\n        return _read(jar, float32)\n\n@extend\nclass bool:\n    def __pickle__(self, jar: Jar):\n        _write(jar, self)\n\n    def __unpickle__(jar: Jar) -> bool:\n        return _read(jar, bool)\n\n@extend\nclass byte:\n    def __pickle__(self, jar: Jar):\n        _write(jar, self)\n\n    def __unpickle__(jar: Jar) -> byte:\n        return _read(jar, byte)\n\n@extend\nclass str:\n    def __pickle__(self, jar: Jar):\n        _write(jar, self.len)\n        _write_raw(jar, self.ptr, self.len)\n\n    def __unpickle__(jar: Jar) -> str:\n        n = _read(jar, int)\n        p = Ptr[byte](n)\n        _read_raw(jar, p, n)\n        return str(p, n)\n\n@extend\nclass List:\n    def __pickle__(self, jar: Jar):\n        n = len(self)\n        pickle(n, jar)\n        if atomic(T):\n            _write_raw(jar, (self.arr.ptr).as_byte(), n * sizeof(T))\n        else:\n            for i in range(n):\n                pickle(self.arr[i], jar)\n\n    def __unpickle__(jar: Jar) -> List[T]:\n        n = unpickle(jar, int)\n        arr = Array[T](n)\n        if atomic(T):\n            _read_raw(jar, (arr.ptr).as_byte(), n * sizeof(T))\n        else:\n            for i in range(n):\n                arr[i] = unpickle(jar, T)\n        return List[T](arr, n)\n\n@extend\nclass DynamicTuple:\n    def __pickle__(self, jar: Jar):\n        n = len(self)\n        pickle(n, jar)\n        if atomic(T):\n            _write_raw(jar, (self._ptr).as_byte(), n * sizeof(T))\n        else:\n            for i in range(n):\n                pickle(self._ptr[i], jar)\n\n    def __unpickle__(jar: Jar) -> DynamicTuple[T]:\n        n = unpickle(jar, int)\n        p = Ptr[T](n)\n        if atomic(T):\n            _read_raw(jar, p.as_byte(), n * sizeof(T))\n        else:\n            for i in range(n):\n                p[i] = unpickle(jar, T)\n        return DynamicTuple[T](p, n)\n\n@extend\nclass Dict:\n    def __pickle__(self, jar: Jar):\n        import internal.khash as khash\n\n        if atomic(K) and atomic(V):\n            pickle(self._n_buckets, jar)\n            pickle(self._size, jar)\n            pickle(self._n_occupied, jar)\n            pickle(self._upper_bound, jar)\n            fsize = khash.__ac_fsize(self._n_buckets) if self._n_buckets > 0 else 0\n            _write_raw(jar, self._flags.as_byte(), fsize * sizeof(u32))\n            _write_raw(jar, self._keys.as_byte(), self._n_buckets * sizeof(K))\n            _write_raw(jar, self._vals.as_byte(), self._n_buckets * sizeof(V))\n        else:\n            pickle(self._n_buckets, jar)\n            size = len(self)\n            pickle(size, jar)\n\n            for k, v in self.items():\n                pickle(k, jar)\n                pickle(v, jar)\n\n    def __unpickle__(jar: Jar) -> Dict[K, V]:\n        import internal.khash as khash\n\n        d = {}\n        if atomic(K) and atomic(V):\n            n_buckets = unpickle(jar, int)\n            size = unpickle(jar, int)\n            n_occupied = unpickle(jar, int)\n            upper_bound = unpickle(jar, int)\n            fsize = khash.__ac_fsize(n_buckets) if n_buckets > 0 else 0\n            flags = Ptr[u32](fsize)\n            keys = Ptr[K](n_buckets)\n            vals = Ptr[V](n_buckets)\n            _read_raw(jar, flags.as_byte(), fsize * sizeof(u32))\n            _read_raw(jar, keys.as_byte(), n_buckets * sizeof(K))\n            _read_raw(jar, vals.as_byte(), n_buckets * sizeof(V))\n\n            d._n_buckets = n_buckets\n            d._size = size\n            d._n_occupied = n_occupied\n            d._upper_bound = upper_bound\n            d._flags = flags\n            d._keys = keys\n            d._vals = vals\n        else:\n            n_buckets = unpickle(jar, int)\n            size = unpickle(jar, int)\n            d.resize(n_buckets)\n            i = 0\n            while i < size:\n                k = unpickle(jar, K)\n                v = unpickle(jar, V)\n                d[k] = v\n                i += 1\n        return d\n\n@extend\nclass Set:\n    def __pickle__(self, jar: Jar):\n        import internal.khash as khash\n\n        if atomic(K):\n            pickle(self._n_buckets, jar)\n            pickle(self._size, jar)\n            pickle(self._n_occupied, jar)\n            pickle(self._upper_bound, jar)\n            fsize = khash.__ac_fsize(self._n_buckets) if self._n_buckets > 0 else 0\n            _write_raw(jar, self._flags.as_byte(), fsize * sizeof(u32))\n            _write_raw(jar, self._keys.as_byte(), self._n_buckets * sizeof(K))\n        else:\n            pickle(self._n_buckets, jar)\n            size = len(self)\n            pickle(size, jar)\n\n            for k in self:\n                pickle(k, jar)\n\n    def __unpickle__(jar: Jar) -> Set[K]:\n        import internal.khash as khash\n\n        s = set[K]()\n        if atomic(K):\n            n_buckets = unpickle(jar, int)\n            size = unpickle(jar, int)\n            n_occupied = unpickle(jar, int)\n            upper_bound = unpickle(jar, int)\n            fsize = khash.__ac_fsize(n_buckets) if n_buckets > 0 else 0\n            flags = Ptr[u32](fsize)\n            keys = Ptr[K](n_buckets)\n            _read_raw(jar, flags.as_byte(), fsize * sizeof(u32))\n            _read_raw(jar, keys.as_byte(), n_buckets * sizeof(K))\n\n            s._n_buckets = n_buckets\n            s._size = size\n            s._n_occupied = n_occupied\n            s._upper_bound = upper_bound\n            s._flags = flags\n            s._keys = keys\n        else:\n            n_buckets = unpickle(jar, int)\n            size = unpickle(jar, int)\n            s.resize(n_buckets)\n            i = 0\n            while i < size:\n                k = unpickle(jar, K)\n                s.add(k)\n                i += 1\n        return s\n"
  },
  {
    "path": "stdlib/python.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom internal.python import ensure_initialized\n\nensure_initialized()\n"
  },
  {
    "path": "stdlib/random.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport sys\nfrom math import inf as INF, sqrt as _sqrt, acos as _acos, cos as _cos, sin as _sin\nfrom math import log as _log, exp as _exp, pi as _pi, e as _e, ceil as _ceil\nfrom bisect import bisect as _bisect\nfrom time import time as _time\n\nN = 624\nM = 397\nLOG4 = _log(4.0)\nNV_MAGICCONST = 4 * _exp(-0.5) / _sqrt(2.0)\nSG_MAGICCONST = 1.0 + _log(4.5)\nTWOPI = 2.0 * _pi\n\nMATRIX_A = u32(0x9908b0df)    # constant vector a\nUPPER_MASK = u32(0x80000000)  # most significant w-r bits\nLOWER_MASK = u32(0x7fffffff)  # least significant r bits\n\n@tuple\nclass RandomGenerator:\n    data: Ptr[u32]\n\n    def __new__():\n        return RandomGenerator(Ptr[u32](N + 1))\n\n    @property\n    def index(self):\n        return int(self.data[0])\n\n    @property\n    def state(self):\n        return self.data + 1\n\n    def getstate(self):\n        from internal.gc import sizeof\n        p = Ptr[u32](N + 1)\n        str.memcpy(p.as_byte(), self.data.as_byte(), (N + 1) * sizeof(u32))\n        return p\n\n    def setstate(self, state):\n        from internal.gc import sizeof\n        str.memcpy(self.data.as_byte(), state.as_byte(), (N + 1) * sizeof(u32))\n\n    def genrand_int32(self) -> u32:\n        mag01 = (u32(0), MATRIX_A)\n        mt = self.state\n\n        if self.index >= N:\n            kk = 0\n\n            while kk < int(N - M):\n                y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK)\n                mt[kk] = mt[kk + M] ^ (y >> u32(1)) ^ mag01[int(y & u32(1))]\n                kk += 1\n\n            while kk < int(N - 1):\n                y = (mt[kk] & UPPER_MASK) | (mt[kk+1] & LOWER_MASK)\n                mt[kk] = mt[kk+(M-N)] ^ (y >> u32(1)) ^ mag01[int(y & u32(1))]\n                kk += 1\n\n            y = (mt[N-1] & UPPER_MASK) | (mt[0] & LOWER_MASK)\n            mt[N-1] = mt[M-1] ^ (y >> u32(1)) ^ mag01[int(y & u32(1))]\n            self.data[0] = u32(0)\n\n        i = self.index\n        y = mt[i]\n        self.data[0] = u32(i + 1)\n        y ^= (y >> u32(11))\n        y ^= (y << u32(7)) & u32(0x9d2c5680)\n        y ^= (y << u32(15)) & u32(0xefc60000)\n        y ^= (y >> u32(18))\n        return y\n\n    def genrand_res53(self) -> float:\n        a = self.genrand_int32() >> u32(5)\n        b = self.genrand_int32() >> u32(6)\n        return (int(a) * 67108864.0 + int(b)) * (1.0 / 9007199254740992.0)\n\n    def random(self):\n        return self.genrand_res53()\n\n    def init_u32(self, s: u32):\n        mt = self.state\n        mt[0] = s\n        for mti in range(1, N):\n            mt[mti] = (u32(1812433253) * (mt[mti-1] ^ (mt[mti-1] >> u32(30))) + u32(mti))\n        self.data[0] = u32(N)\n\n    def init_array(self, init_key: Ptr[u32], key_length: int):\n        mt = self.state\n        self.init_u32(u32(19650218))\n        i = 1\n        j = 0\n\n        k = N if N > key_length else key_length\n        while k:\n            mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> u32(30))) * u32(1664525))) + init_key[j] + u32(j)\n            i += 1\n            j += 1\n            if i >= N:\n                mt[0] = mt[N - 1]\n                i = 1\n            if j >= key_length:\n                j = 0\n            k -= 1\n\n        k = N - 1\n        while k:\n            mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> u32(30))) * u32(1566083941))) - u32(i)\n            i += 1\n            if i >= N:\n                mt[0] = mt[N - 1]\n                i = 1\n            k -= 1\n\n        mt[0] = u32(0x80000000)\n\n    def init_int(self, s: int):\n        init_key = (u32(s & ((1 << 32) - 1)), u32(s >> 32))\n        self.init_array(Ptr[u32](__ptr__(init_key).as_byte()), 2 if init_key[1] else 1)\n\n    def random_seed_time_pid(self):\n        now = _C.seq_time() * 1000\n        key = __array__[u32](5)\n        key[0] = u32(now & 0xFFFFFFFF)\n        key[1] = u32(now >> 32)\n        key[2] = u32(_C.seq_pid())\n        now = _C.seq_time_monotonic()\n        key[3] = u32(now & 0xFFFFFFFF)\n        key[4] = u32(now >> 32)\n        self.init_array(key.ptr, len(key))\n\n    def seed(self, s: int):\n        self.init_int(s)\n\n    def seed(self):\n        self.random_seed_time_pid()\n\nclass Random:\n    gen: RandomGenerator\n    gauss_next: Optional[float]\n\n    def __init__(self, seed: Optional[int] = None):\n        self.gen = RandomGenerator()\n        self.seed(seed)\n\n    def seed(self, a: Optional[int]):\n        if a is not None:\n            self.gen.seed(abs(a))\n        else:\n            self.gen.seed()\n        self.gauss_next = None\n\n    def getstate(self):\n        return self.gen.getstate(), self.gauss_next\n\n    def setstate(self, state):\n        gen_state, gauss_next = state\n        self.gen.setstate(gen_state)\n        self.gauss_next = gauss_next\n\n    def getrandbits(self, k: int) -> int:\n        if k == 0:\n            return 0\n\n        if k < 0:\n            raise ValueError(\"number of bits must be non-negative\")\n\n        if k > 64:\n            raise ValueError(\"number of bits cannot be greater than 64\")\n\n        if k <= 32:  # Fast path\n            r = int(self.gen.genrand_int32())\n            m = r >> (32 - k)\n            return m\n\n        lo = u64(int(self.gen.genrand_int32()))\n        hi = u64(int(self.gen.genrand_int32()))\n        mask = ~((u64(1) << u64(64 - k)) - u64(1))\n        hi &= mask\n        hi >>= u64(64 - k)\n        return int((hi << u64(32)) | lo)\n\n    def bit_length(self, n: int) -> int:\n        len = 0\n        while n:\n            len += 1\n            n = int(u64(n) >> u64(1))\n        return len\n\n    def _randbelow_with_getrandbits(self, n: int) -> int:\n        getrandbits = self.getrandbits\n        k = self.bit_length(n)  # don't use (n-1) here because n can be 1\n        r = getrandbits(k)  # 0 <= r < 2**k\n        while r >= n:\n            r = getrandbits(k)\n        return r\n\n    def randrange(self, start: int, stop: int, step: int = 1) -> int:\n        if stop == 0:\n            if start > 0:\n                return self._randbelow_with_getrandbits(start)\n            raise ValueError(\"empty range for randrange()\")\n\n        # stop argument supplied.\n        width = stop - start\n        if step == 1 and width > 0:\n            return start + self._randbelow_with_getrandbits(width)\n        if step == 1:\n            raise ValueError(\"empty range for randrange()\")\n\n        # Non-unit step argument supplied.\n        n = INF\n        if step > 0:\n            n = float((width + step - 1) // step)\n        elif step < 0:\n            n = float((width + step + 1) // step)\n        else:\n            raise ValueError(\"zero step for randrange()\")\n\n        if n <= 0:\n            raise ValueError(\"empty range for randrange()\")\n\n        return start + step * self._randbelow_with_getrandbits(int(n))\n\n    def randint(self, a: int, b: int):\n        return self.randrange(a, b + 1, 1)\n\n    def random(self) -> float:\n        return self.gen.genrand_res53()\n\n    def choice(self, sequence: Generator[T], T: type) -> T:\n        return self.choice(list(sequence))\n\n    @overload\n    def choice(self, sequence: List[T], T: type) -> T:\n        if not sequence:\n            raise IndexError(\"Cannot choose from an empty sequence\")\n        i = self._randbelow_with_getrandbits(len(sequence))\n        return sequence._get(i)\n\n    def shuffle(self, x):\n        random = 0\n        if random == 0:\n            randbelow = self._randbelow_with_getrandbits\n            for i in reversed(range(1, len(x))):\n                # pick an element in x[:i+1] with which to exchange x[i]\n                j = randbelow(i + 1)\n                x[i], x[j] = x[j], x[i]\n        else:\n            for i in reversed(range(1, len(x))):\n                # pick an element in x[:i+1] with which to exchange x[i]\n                j = int(self.random() * (i + 1))\n                x[i], x[j] = x[j], x[i]\n\n    def uniform(self, a, b) -> float:\n        return a + (b - a) * self.random()\n\n    def triangular(self, low: float, high: float, mode: float) -> float:\n        if high == low:\n            return low\n        u = self.random()\n        c = (mode - low) / (high - low)\n        if u > c:\n            u = 1.0 - u\n            c = 1.0 - c\n            low, high = high, low\n        return low + (high - low) * _sqrt(u * c)\n\n    def gammavariate(self, alpha: float, beta: float) -> float:\n        # alpha > 0, beta > 0, mean is alpha*beta, variance is alpha*beta**2\n\n        # Warning: a few older sources define the gamma distribution in terms\n        # of alpha > -1.0\n        if alpha <= 0.0 or beta <= 0.0:\n            raise ValueError(\"gammavariate: alpha and beta must be > 0.0\")\n\n        if alpha > 1.0:\n\n            # Uses R.C.H. Cheng, \"The generation of Gamma\n            # variables with non-integral shape parameters\",\n            # Applied Statistics, (1977), 26, No. 1, p71-74\n\n            ainv = _sqrt(2.0 * alpha - 1.0)\n            bbb = alpha - LOG4\n            ccc = alpha + ainv\n\n            while 1:\n                u1 = self.random()\n                if not 1e-7 < u1 < 0.9999999:\n                    continue\n                u2 = 1.0 - self.random()\n                v = _log(u1 / (1.0 - u1)) / ainv\n                x = alpha * _exp(v)\n                z = u1 * u1 * u2\n                r = bbb + ccc * v - x\n                if r + SG_MAGICCONST - 4.5 * z >= 0.0 or r >= _log(z):\n                    return x * beta\n\n        elif alpha == 1.0:\n            # expovariate(1/beta)\n            return -_log(1.0 - self.random()) * beta\n\n        else:  # alpha is between 0 and 1 (exclusive)\n\n            # Uses ALGORITHM GS of Statistical Computing - Kennedy & Gentle\n            x = 0.0\n            while 1:\n                u = self.random()\n                b = (_e + alpha) / _e\n                p = b * u\n                if p <= 1.0:\n                    x = p ** (1.0 / alpha)\n                else:\n                    x = -_log((b - p) / alpha)\n                u1 = self.random()\n                if p > 1.0:\n                    if u1 <= x ** (alpha - 1.0):\n                        break\n                elif u1 <= _exp(-x):\n                    break\n            return x * beta\n\n    def betavariate(self, alpha: float, beta: float) -> float:\n        # This version due to Janne Sinkkonen, and matches all the std\n        # texts (e.g., Knuth Vol 2 Ed 3 pg 134 \"the beta distribution\").\n        y = self.gammavariate(alpha, 1.0)\n        if y == 0:\n            return 0.0\n        else:\n            return y / (y + self.gammavariate(beta, 1.0))\n\n    def expovariate(self, lambd: float) -> float:\n        if lambd == 0.0:\n            raise ZeroDivisionError(\"Cannot divide by zero\")\n        # lambd: rate lambd = 1/mean\n        # we use 1-random() instead of random() to preclude the\n        # possibility of taking the log of zero.\n        return -_log(1.0 - self.random()) / lambd\n\n    def gauss(self, mu: float = 0.0, sigma: float = 1.0) -> float:\n        z = self.gauss_next\n        self.gauss_next = None\n        if z is None:\n            x2pi = self.random() * TWOPI\n            g2rad = _sqrt(-2.0 * _log(1.0 - self.random()))\n            z = _cos(x2pi) * g2rad\n            self.gauss_next = _sin(x2pi) * g2rad\n        return mu + z * sigma\n\n    def paretovariate(self, alpha: float) -> float:\n        u = 1.0 - self.random()\n        return 1.0 / u ** (1.0 / alpha)\n\n    def weibullvariate(self, alpha: float, beta: float) -> float:\n        u = 1.0 - self.random()\n        return alpha * (-_log(u)) ** (1.0 / beta)\n\n    def normalvariate(self, mu: float = 0.0, sigma: float = 1.0) -> float:\n        z = 0.0\n        while 1:\n            u1 = self.random()\n            u2 = 1.0 - self.random()\n            z = NV_MAGICCONST * (u1 - 0.5) / u2\n            zz = z * z / 4.0\n            if zz <= -_log(u2):\n                break\n        return mu + z * sigma\n\n    def lognormvariate(self, mu: float, sigma: float) -> float:\n        return _exp(self.normalvariate(mu, sigma))\n\n    def vonmisesvariate(self, mu: float, kappa: float) -> float:\n        def _mod(a: float, b: float):\n            @pure\n            @llvm\n            def _truediv_float_float(self: float, other: float) -> float:\n                %0 = fdiv double %self, %other\n                ret double %0\n\n            @pure\n            @llvm\n            def _mod_float_float(self: float, other: float) -> float:\n                %0 = frem double %self, %other\n                ret double %0\n\n            mod = _mod_float_float(a, b)\n            div = _truediv_float_float(a - mod, b)\n            if mod:\n                if (b < 0) != (mod < 0):\n                    mod += b\n                    div -= 1.0\n            else:\n                mod = (0.0).copysign(b)\n            return mod\n\n        z = 0.0\n        theta = 0.0\n\n        if kappa <= 1e-6:\n            return TWOPI * self.random()\n\n        s = 0.5 / kappa\n        r = s + _sqrt(1.0 + s * s)\n\n        while 1:\n            u1 = self.random()\n            z = _cos(_pi * u1)\n\n            d = z / (r + z)\n            u2 = self.random()\n            if u2 < 1.0 - d * d or u2 <= (1.0 - d) * _exp(d):\n                break\n\n        q = 1.0 / r\n        f = (q + z) / (1.0 + q * z)\n        u3 = self.random()\n        if u3 > 0.5:\n            theta = _mod(mu + _acos(f), TWOPI)\n        else:\n            theta = _mod(mu - _acos(f), TWOPI)\n\n        return theta\n\n    def sample(self, population, k: int):\n        randbelow = self._randbelow_with_getrandbits\n        n = len(population)\n        if not 0 <= k <= n:\n            raise ValueError(\"Sample larger than population or is negative\")\n        result = [population[0] for _ in range(k)]\n        setsize = 21.0  # size of a small set minus size of an empty list\n        if k > 5:\n            # Should be _log(k * 3, 4)\n            setsize += 4 ** _ceil(_log(float(k * 3)))  # table size for big sets\n        if n <= setsize:\n            # An n-length list is smaller than a k-length set\n            pool = list(population)\n            for i in range(k):  # invariant:  non-selected at [0,n-i)\n                j = randbelow(n - i)\n                result[i] = pool[j]\n                pool[j] = pool[n - i - 1]  # move non-selected item into vacancy\n        else:\n            selected = Set[int]()\n            selected_add = selected.add\n            for i in range(k):\n                j = randbelow(n)\n                while j in selected:\n                    j = randbelow(n)\n                selected_add(j)\n                result[i] = population[j]\n        return result\n\n    def choices(\n        self,\n        population,\n        weights: Optional[List[T]],\n        cum_weights: Optional[List[T]],\n        k: int,\n        T: type\n    ):\n\n        def accumulate(weights: list[T], T: type) -> list[T]:\n            n = len(weights)\n            cum_weight = list[T](n)\n            accum = T(0)\n            if n > 0:\n                for i in range(n):\n                    accum += weights[i]\n                    cum_weight.append(accum)\n\n            return cum_weight\n\n        n = len(population)\n        if cum_weights is None:\n            if weights is None:\n                return [population[int(self.random() * n)] for i in range(k)]\n            cum_weights = accumulate(weights)\n        elif weights is not None:\n            raise TypeError(\"Cannot specify both weights and cumulative weights\")\n        if len(cum_weights) != n:\n            raise ValueError(\"The number of weights does not match the population\")\n\n        total = float(cum_weights[-1])  # convert to float\n        hi = n - 1\n        return [\n            population[_bisect(cum_weights, self.random() * total, 0, hi)]\n            for i in range(k)\n        ]\n\n_rnd = Random()\n\ndef seed(a: int):\n    _rnd.seed(a)\n\ndef getrandbits(k: int):\n    return _rnd.getrandbits(k)\n\ndef randrange(start: int, stop: Optional[int] = None, step: int = 1):\n    return _rnd.randrange(start, stop, step) if stop is not None else _rnd.randrange(0, start, step)\n\ndef randint(a: int, b: int):\n    return _rnd.randint(a, b)\n\ndef choice(s):\n    return _rnd.choice(s)\n\ndef choices(\n    population,\n    weights: Optional[List[T]] = None,\n    cum_weights: Optional[List[T]] = None,\n    k: int = 1,\n    T: type = int\n):\n    return _rnd.choices(population, weights, cum_weights, k)\n\ndef shuffle(s):\n    _rnd.shuffle(s)\n\ndef sample(population, k: int):\n    return _rnd.sample(population, k)\n\ndef random():\n    return _rnd.random()\n\ndef uniform(a, b):\n    return _rnd.uniform(a, b)\n\ndef triangular(low: float = 0.0, high: float = 1.0, mode: Optional[float] = None):\n    return _rnd.triangular(low, high, mode if mode is not None else (low + high) / 2)\n\ndef betavariate(alpha: float, beta: float):\n    return _rnd.betavariate(alpha, beta)\n\ndef expovariate(lambd: float):\n    return _rnd.expovariate(lambd)\n\ndef gammavariate(alpha: float, beta: float):\n    return _rnd.gammavariate(alpha, beta)\n\ndef gauss(mu: float, sigma: float):\n    return _rnd.gauss(mu, sigma)\n\ndef lognormvariate(mu: float, sigma: float):\n    return _rnd.lognormvariate(mu, sigma)\n\ndef normalvariate(mu: float, sigma: float):\n    return _rnd.normalvariate(mu, sigma)\n\ndef vonmisesvariate(mu: float, kappa: float):\n    return _rnd.vonmisesvariate(mu, kappa)\n\ndef paretovariate(alpha: float):\n    return _rnd.paretovariate(alpha)\n\ndef weibullvariate(alpha: float, beta: float):\n    return _rnd.weibullvariate(alpha, beta)\n"
  },
  {
    "path": "stdlib/re.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n# Adapted in part from Google's Python re2 wrapper\n# https://github.com/google/re2/blob/abseil/python/re2.py\n\nimport internal.static as static\n\nA          = (1 << 0)\nASCII      = (1 << 0)\nDEBUG      = (1 << 1)\nI          = (1 << 2)\nIGNORECASE = (1 << 2)\nL          = (1 << 3)\nLOCALE     = (1 << 3)\nM          = (1 << 4)\nMULTILINE  = (1 << 4)\nS          = (1 << 5)\nDOTALL     = (1 << 5)\nX          = (1 << 6)\nVERBOSE    = (1 << 6)\n\n_ANCHOR_NONE  = 0\n_ANCHOR_START = 1\n_ANCHOR_BOTH  = 2\n\n@tuple\nclass Span:\n    start: int\n    end: int\n\n    def __bool__(self):\n        return not (self.start == -1 and self.end == -1)\n\n@C\n@pure\ndef seq_re_match(re: cobj,\n                 anchor: int,\n                 string: str,\n                 pos: int,\n                 endpos: int) -> Ptr[Span]:\n    pass\n\n@C\n@pure\ndef seq_re_match_one(re: cobj,\n                     anchor: int,\n                     string: str,\n                     pos: int,\n                     endpos: int) -> Span:\n    pass\n\n@C\n@pure\ndef seq_re_pattern_groups(re: cobj) -> int:\n    pass\n\n@C\n@pure\ndef seq_re_group_name_to_index(re: cobj, name: str) -> int:\n    pass\n\n@C\n@pure\ndef seq_re_group_index_to_name(re: cobj, index: int) -> str:\n    pass\n\n@C\n@pure\ndef seq_re_pattern_error(re: cobj) -> str:\n    pass\n\n@C\n@pure\ndef seq_re_escape(pattern: str) -> str:\n    pass\n\n@C\ndef seq_re_purge() -> None:\n    pass\n\n@C\n@pure\ndef seq_re_compile(pattern: str, flags: int) -> cobj:\n    pass\n\nclass error(Exception):\n    pattern: str\n\n    def __init__(self, message: str = \"\", pattern: str = \"\"):\n        super().__init__(message)\n        self.pattern = pattern\n\n    @property\n    def msg(self):\n        return self.message\n\n@tuple\nclass Pattern:\n    pattern: str\n    flags: int\n    _re: cobj\n\ndef compile(pattern: str, flags: int = 0):\n    re = seq_re_compile(pattern, flags)\n    err_msg = seq_re_pattern_error(re)\n    if err_msg:\n        raise error(err_msg, pattern)\n    return Pattern(pattern, flags, re)\n\ndef search(pattern: str, string: str, flags: int = 0):\n    return compile(pattern, flags).search(string)\n\ndef match(pattern: str, string: str, flags: int = 0):\n    return compile(pattern, flags).match(string)\n\ndef fullmatch(pattern: str, string: str, flags: int = 0):\n    return compile(pattern, flags).fullmatch(string)\n\ndef finditer(pattern: str, string: str, flags: int = 0):\n    return compile(pattern, flags).finditer(string)\n\ndef findall(pattern: str, string: str, flags: int = 0):\n    return compile(pattern, flags).findall(string)\n\ndef split(pattern: str, string: str, maxsplit: int = 0, flags: int = 0):\n    return compile(pattern, flags).split(string, maxsplit)\n\ndef sub(pattern: str, repl, string: str, count: int = 0, flags: int = 0):\n    return compile(pattern, flags).sub(repl, string, count)\n\ndef subn(pattern: str, repl, string: str, count: int = 0, flags: int = 0):\n    return compile(pattern, flags).subn(repl, string, count)\n\ndef escape(pattern: str):\n    return seq_re_escape(pattern)\n\ndef purge():\n    seq_re_purge()\n\n@tuple\nclass Match:\n    _spans: Ptr[Span]\n    pos: int\n    endpos: int\n    re: Pattern\n    string: str\n\n    def _get_group_int(self, g: int, n: int):\n        if not (0 <= g <= n):\n            raise IndexError(\"no such group\")\n        return self._spans[g]\n\n    def _get_group_str(self, g: str, n: int):\n        return self._get_group_int(seq_re_group_name_to_index(self.re._re, g), n)\n\n    def _get_group(self, g, n: int):\n        if isinstance(g, int):\n            return self._get_group_int(g, n)\n        elif isinstance(g, str):\n            return self._get_group_str(g, n)\n        else:\n            return self._get_group(g.__index__(), n)\n\n    def _span_match(self, span: Span):\n        if not span:\n            return None\n        return self.string._slice(span.start, span.end)\n\n    def _get_match(self, g, n: int):\n        span = self._get_group(g, n)\n        return self._span_match(span)\n\n    def _group_multi(self, n: int, *args):\n        if static.len(args) == 1:\n            return (self._get_match(args[0], n),)\n        else:\n            return (self._get_match(args[0], n), *self._group_multi(n, *args[1:]))\n\n    def group(self, *args):\n        if static.len(args) == 0:\n            return self._get_match(0, 1).__val__()\n        elif static.len(args) == 1:\n            return self._get_match(args[0], self.re.groups)\n        else:\n            return self._group_multi(self.re.groups, *args)\n\n    def __getitem__(self, g):\n        return self._get_match(g, self.re.groups)\n\n    def start(self, group = 0):\n        return self._get_group(group, self.re.groups).start\n\n    def end(self, group = 0):\n        return self._get_group(group, self.re.groups).end\n\n    def span(self, group = 0):\n        start, end = self._get_group(group, self.re.groups)\n        return start, end\n\n    def _split(template: str):\n        backslash = '\\\\'\n        pieces = ['']\n        index = template.find(backslash)\n\n        OCTAL = compile(r'\\\\[0-7][0-7][0-7]')\n        GROUP = compile(r'\\\\[1-9][0-9]?|\\\\g<\\w+>')\n\n        while index != -1:\n            piece, template = template[:index], template[index:]\n            pieces[-1] += piece\n\n            octal_match = OCTAL.match(template)\n            group_match = GROUP.match(template)\n\n            if (not octal_match) and group_match:\n                index = group_match.end()\n                piece, template = template[:index], template[index:]\n                pieces.extend((piece, ''))\n            else:\n                index = 2\n                piece, template = template[:index], template[index:]\n                pieces[-1] += piece\n\n            index = template.find(backslash)\n\n        pieces[-1] += template\n        return pieces\n\n    def _unescape(s: str):\n        r = []\n        n = len(s)\n        i = 0\n        while i < n:\n            if s[i] == '\\\\' and i + 1 < n:\n                c = s[i + 1]\n                if c == 'a':\n                    r.append('\\a')\n                    i += 1\n                elif c == 'b':\n                    r.append('\\b')\n                    i += 1\n                elif c == 'f':\n                    r.append('\\f')\n                    i += 1\n                elif c == 'n':\n                    r.append('\\n')\n                    i += 1\n                elif c == 'r':\n                    r.append('\\r')\n                    i += 1\n                elif c == 't':\n                    r.append('\\t')\n                    i += 1\n                elif c == 'v':\n                    r.append('\\v')\n                    i += 1\n                elif c == '\"':\n                    r.append('\\\"')\n                    i += 1\n                elif c == '\\'':\n                    r.append('\\'')\n                    i += 1\n                elif c == '\\\\':\n                    r.append('\\\\')\n                    i += 1\n                elif '0' <= c <= '7':\n                        k = i + 2\n                        while k < n and k - i <= 4 and '0' <= s[k] <= '7':\n                            k += 1\n                        code = int(s[i+1:k], 8)\n                        p = Ptr[byte](1)\n                        p[0] = byte(code)\n                        r.append(str(p, 1))\n                        i = k - 1\n                elif c.isalpha():\n                    raise error(f\"bad escape \\\\{c} at position {i}\")\n                else:\n                    r.append(s[i])\n            else:\n                r.append(s[i])\n            i += 1\n\n        return str.cat(r)\n\n    def expand(self, template: str):\n        def get_or_empty(s: Optional[str]):\n            return s if s is not None else ''\n\n        pieces = list(Match._split(template))\n        INT = compile(r'[+-]?\\d+')\n\n        for index, piece in enumerate(pieces):\n            if not (index % 2):\n                pieces[index] = Match._unescape(piece)\n            else:\n                if len(piece) <= 3:\n                    pieces[index] = get_or_empty(self[int(piece[1:])])\n                else:\n                    group = piece[3:-1]\n                    if INT.fullmatch(group):\n                        pieces[index] = get_or_empty(self[int(group)])\n                    else:\n                        pieces[index] = get_or_empty(self[group])\n        return str.cat(pieces)\n\n    @property\n    def lastindex(self):\n        max_end = -1\n        max_group = None\n        for group in range(1, self.re.groups + 1):\n            end = self._spans[group].end\n            if max_end < end:\n                max_end = end\n                max_group = group\n        return max_group\n\n    @property\n    def lastgroup(self):\n        max_group = self.lastindex\n        if max_group is None:\n            return None\n        return seq_re_group_index_to_name(self.re._re, max_group)\n\n    def groups(self, default: Optional[str] = None):\n        def get_or_default(item, default):\n            return item if item is not None else default\n\n        n = self.re.groups\n        return [get_or_default(self._span_match(self._spans[i]), default)\n                for i in range(1, n + 1)]\n\n    def groupdict(self, default: Optional[str] = None):\n        d = {}\n        for group, index in self.re.groupindex.items():\n            item = self[index]\n            d[group] = item if item is not None else default\n        return d\n\n    def __copy__(self):\n        return self\n\n    def __deepcopy__(self):\n        return self\n\n    def __bool__(self):\n        return True\n\n@extend\nclass Pattern:\n    @property\n    def groups(self):\n        return seq_re_pattern_groups(self._re)\n\n    @property\n    def groupindex(self):\n        d = {}\n        for i in range(1, self.groups + 1):\n            name = seq_re_group_index_to_name(self._re, i)\n            if name:\n                d[name] = i\n        return d\n\n    def _match_one(self, anchor: int, string: str, pos: Optional[int], endpos: Optional[int]):\n        posx = 0 if pos is None else max(0, min(pos.__val__(), len(string)))\n        endposx = len(string) if endpos is None else max(0, min(endpos.__val__(), len(string)))\n\n        if posx > endposx:\n            return None\n\n        spans = seq_re_match(self._re, anchor, string, posx, endposx)\n        if not spans[0]:\n            return None\n\n        return Match(spans, posx, endposx, self, string)\n\n    def _match(self, anchor: int, string: str, pos: Optional[int], endpos: Optional[int]):\n        posx = 0 if pos is None else max(0, min(pos.__val__(), len(string)))\n        endposx = len(string) if endpos is None else max(0, min(endpos.__val__(), len(string)))\n\n        if posx > endposx:\n            return\n\n        while True:\n            spans = seq_re_match(self._re, anchor, string, posx, endposx)\n\n            if not spans[0]:\n                break\n\n            yield Match(spans, posx, endposx, self, string)\n\n            if posx == endposx:\n                break\n\n            elif posx == spans[0][1]:\n                # We matched the empty string at pos and would be stuck, so in order\n                # to make forward progress, increment the bytes offset.\n                posx += 1\n            else:\n                posx = spans[0][1]\n\n    def search(self, string: str, pos: Optional[int] = None, endpos: Optional[int] = None):\n        return self._match_one(_ANCHOR_NONE, string, pos, endpos)\n\n    def match(self, string: str, pos: Optional[int] = None, endpos: Optional[int] = None):\n        return self._match_one(_ANCHOR_START, string, pos, endpos)\n\n    def fullmatch(self, string: str, pos: Optional[int] = None, endpos: Optional[int] = None):\n        return self._match_one(_ANCHOR_BOTH, string, pos, endpos)\n\n    def finditer(self, string: str, pos: Optional[int] = None, endpos: Optional[int] = None):\n        return self._match(_ANCHOR_NONE, string, pos, endpos)\n\n    def findall(self, string: str, pos: Optional[int] = None, endpos: Optional[int] = None):\n        return [m.group() for m in self.finditer(string, pos, endpos)]\n\n    def _split(self, cb, string: str, maxsplit: int = 0, T: type = str):\n        if maxsplit < 0:\n            return [T(string)], 0\n\n        pieces: List[T] = []\n        end = 0\n        numsplit = 0\n        for match in self.finditer(string):\n            if (maxsplit > 0 and numsplit >= maxsplit):\n                break\n            pieces.append(string[end:match.start()])\n            pieces.extend(cb(match))\n            end = match.end()\n            numsplit += 1\n        pieces.append(string[end:])\n        return pieces, numsplit\n\n    def split(self, string: str, maxsplit: int = 0):\n        cb = lambda match: [match[group] for group in range(1, self.groups + 1)]\n        pieces, _ = self._split(cb, string, maxsplit, Optional[str])\n        return pieces\n\n    def _repl(match, repl):\n        if isinstance(repl, str):\n            return match.expand(repl)\n        else:\n            return repl(match)\n\n    def subn(self, repl, string: str, count: int = 0):\n        cb = lambda match: [Pattern._repl(match, repl)]\n        pieces, numsplit = self._split(cb, string, count, str)\n        joined_pieces = str.cat(pieces)\n        return joined_pieces, numsplit\n\n    def sub(self, repl, string: str, count: int = 0):\n        joined_pieces, _ = self.subn(repl, string, count)\n        return joined_pieces\n\n    def __bool__(self):\n        return True\n"
  },
  {
    "path": "stdlib/simd.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport internal.static as static\n\n@tuple(container=False)  # disallow default __getitem__\nclass Vec[T, N: Literal[int]]:\n    def _is_int(T: type) -> Literal[bool]:\n        return isinstance(T, int) or isinstance(T, byte) or \\\n               isinstance(T, Int) or isinstance(T, UInt)\n\n    def _is_sint(T: type) -> Literal[bool]:\n        return isinstance(T, int) or isinstance(T, Int)\n\n    def _is_uint(T: type) -> Literal[bool]:\n        return isinstance(T, UInt) or isinstance(T, byte)\n\n    def _int_prefix(T: type) -> Literal[str]:\n        return 'u' if isinstance(T, UInt) else 's'\n\n    def _is_float(T: type) -> Literal[bool]:\n        return isinstance(T, float16) \\\n            or isinstance(T, float32) \\\n            or isinstance(T, float) \\\n            or isinstance(T, float128) \\\n            or isinstance(T, bfloat16)\n\n    def _is_valid(T: type) -> Literal[bool]:\n        return Vec._is_int(T) or Vec._is_float(T)\n\n    def _sizeof(T: type) -> Literal[int]:\n        if isinstance(T, Int):\n            return T.N\n        if isinstance(T, UInt):\n            return T.N\n        if isinstance(T, byte):\n            return 8\n        if isinstance(T, int):\n            return 64\n        if isinstance(T, float16):\n            return 16\n        if isinstance(T, float32):\n            return 32\n        if isinstance(T, float):\n            return 64\n        if isinstance(T, float128):\n            return 128\n        if isinstance(T, bfloat16):\n            return 16\n        compile_error(\"invalid type\")\n\n    @pure\n    @llvm\n    def _set(val: T) -> Vec[T, N]:\n        %0 = insertelement <{=N} x {=T}> undef, {=T} %val, i32 0\n        %1 = shufflevector <{=N} x {=T}> %0, <{=N} x {=T}> undef, <{=N} x i32> zeroinitializer\n        ret <{=N} x {=T}> %1\n\n    def _load(data) -> Vec[T, N]:\n        return Ptr[Vec[T, N]](data)[0]\n\n    def __new__(x) -> Vec[T, N]:\n        if Vec._is_valid(x):\n            return Vec[T, N]._set(x)\n        if isinstance(x, Ptr) and Vec._is_valid(x.T):\n            return Vec[T, N]._load(x)\n        if isinstance(x, str) and (\n            isinstance(T, i8) or isinstance(T, u8) or isinstance(T, byte)\n        ):\n            return Vec[T, N]._load(x.ptr)\n        if isinstance(x, List) and Vec._is_valid(x.T):\n            return Vec[T, N]._load(x.arr.ptr)\n        if isinstance(x, Vec) and N == x.N:\n            return x.cast(T)\n        compile_error(\"invalid SIMD vector constructor\")\n\n    def __new__(x: str, offset: int = 0) -> Vec[u8, N]:\n        return Vec[u8, N](x.ptr + offset)\n\n    def __new__(x: List[T], offset: int = 0) -> Vec[T, N]:\n        return Vec[T, N](x.arr.ptr + offset)\n\n    @pure\n    @llvm\n    def _zext(self: Vec[T, N], T2: type) -> Vec[T2, N]:\n        %0 = zext <{=N} x {=T}> %self to <{=N} x {=T2}>\n        ret <{=N} x {=T2}> %0\n\n    @pure\n    @llvm\n    def _sext(self: Vec[T, N], T2: type) -> Vec[T2, N]:\n        %0 = sext <{=N} x {=T}> %self to <{=N} x {=T2}>\n        ret <{=N} x {=T2}> %0\n\n    @pure\n    @llvm\n    def _uitofp(self: Vec[T, N], T2: type) -> Vec[T2, N]:\n        %0 = uitofp <{=N} x {=T}> %self to <{=N} x {=T2}>\n        ret <{=N} x {=T2}> %0\n\n    @pure\n    @llvm\n    def _sitofp(self: Vec[T, N], T2: type) -> Vec[T2, N]:\n        %0 = sitofp <{=N} x {=T}> %self to <{=N} x {=T2}>\n        ret <{=N} x {=T2}> %0\n\n    @pure\n    @llvm\n    def _fpext(self: Vec[T, N], T2: type) -> Vec[T2, N]:\n        %0 = fpext <{=N} x {=T}> %self to <{=N} x {=T2}>\n        ret <{=N} x {=T2}> %0\n\n    @pure\n    @llvm\n    def _trunc(self: Vec[T, N], T2: type) -> Vec[T2, N]:\n        %0 = trunc <{=N} x {=T}> %self to <{=N} x {=T2}>\n        ret <{=N} x {=T2}> %0\n\n    @pure\n    @llvm\n    def _fptrunc(self: Vec[T, N], T2: type) -> Vec[T2, N]:\n        %0 = fptrunc <{=N} x {=T}> %self to <{=N} x {=T2}>\n        ret <{=N} x {=T2}> %0\n\n    @pure\n    @llvm\n    def _fptoui(self: Vec[T, N], T2: type) -> Vec[T2, N]:\n        %0 = fptoui <{=N} x {=T}> %self to <{=N} x {=T2}>\n        ret <{=N} x {=T2}> %0\n\n    @pure\n    @llvm\n    def _fptosi(self: Vec[T, N], T2: type) -> Vec[T2, N]:\n        %0 = fptosi <{=N} x {=T}> %self to <{=N} x {=T2}>\n        ret <{=N} x {=T2}> %0\n\n    @pure\n    @llvm\n    def _bitcast(self: Vec[T, N], T2: type) -> Vec[T2, N]:\n        %0 = bitcast <{=N} x {=T}> %self to <{=N} x {=T2}>\n        ret <{=N} x {=T2}> %0\n\n    def cast(self, T2: type) -> Vec[T2, N]:\n        if not Vec._is_valid(T2):\n            compile_error(\"invalid cast\")\n        if isinstance(T, T2):\n            return self\n        if Vec._is_sint(T) and Vec._is_sint(T2):\n            return self._sext(T2) if Vec._sizeof(T) < Vec._sizeof(T2) \\\n              else self._trunc(T2)\n        if Vec._is_uint(T) and Vec._is_uint(T2):\n            return self._zext(T2) if Vec._sizeof(T) < Vec._sizeof(T2) \\\n              else self._trunc(T2)\n        if Vec._is_uint(T) and Vec._is_sint(T2):\n            if Vec._sizeof(T) != Vec._sizeof(T2):\n                return self.cast(UInt[Vec._sizeof(T2)])._bitcast(T2)\n            return self._bitcast(T2)\n        if Vec._is_sint(T) and Vec._is_uint(T2):\n            if Vec._sizeof(T) != Vec._sizeof(T2):\n                return self.cast(Int[Vec._sizeof(T2)])._bitcast(T2)\n            return self._bitcast(T2)\n        if Vec._is_float(T) and Vec._is_float(T2):\n            return self._fpext(T2) if Vec._sizeof(T) < Vec._sizeof(T2) \\\n              else self._fptrunc(T2)\n        if Vec._is_float(T) and Vec._is_int(T2):\n            return self._fptoui(T2) if Vec._is_uint(T2) else self._fptosi(T2)\n        if Vec._is_int(T) and Vec._is_float(T2):\n            return self._uitofp(T2) if Vec._is_uint(T) else self._sitofp(T2)\n        compile_error(\"invalid cast\")\n\n    @pure\n    @llvm\n    def cast_ptr(ptr: Ptr[T], T: type, T2: type) -> Ptr[T2]:\n        ret ptr %ptr\n\n    def __copy__(self) -> Vec[T, N]:\n        return self\n\n    def __neg__(self) -> Vec[T, N]:\n        return Vec[T, N](T(0)) - self\n\n    @pure\n    @llvm\n    def __getitem__(self, n: Literal[int]) -> T:\n        %0 = extractelement <{=N} x {=T}> %self, i32 {=n}\n        ret {=T} %0\n\n    def __repr__(self):\n        return f\"<{','.join(repr(i) for i in self.scatter())}>\"\n\n    @pure\n    @llvm\n    def _eq(self: Vec[T, N], other: Vec[T, N]) -> Vec[u1, N]:\n        %0 = icmp eq <{=N} x {=T}> %self, %other\n        ret <{=N} x i1> %0\n\n    def __eq__(self, other: Vec[T, N]) -> Vec[u1, N]:\n        return self._eq(other)\n\n    def __eq__(self, other: T) -> Vec[u1, N]:\n        return self == Vec[T, N](other)\n\n    def __ne__(self: Vec[T, N], other: Vec[T, N]) -> Vec[u1, N]:\n        return (self == other) ^ Vec[u1, N](1u1)\n\n    def __ne__(self, other: T) -> Vec[u1, N]:\n        return self != Vec[T, N](other)\n\n    @pure\n    @llvm\n    def _and(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = and <{=N} x {=T}> %self, %other\n        ret <{=N} x {=T}> %0\n\n    def __and__(self, other: Vec[T, N]) -> Vec[T, N]:\n        if Vec._is_float(T):\n            return Vec[T, N](self._as_int()._and(other._as_int()))\n        return self._and(other)\n\n    @pure\n    @llvm\n    def _or(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = or <{=N} x {=T}> %self, %other\n        ret <{=N} x {=T}> %0\n\n    def __or__(self, other: Vec[T, N]) -> Vec[T, N]:\n        if Vec._is_float(T):\n            return Vec[T, N](self._as_int()._or(other._as_int()))\n        return self._or(other)\n\n    @pure\n    @llvm\n    def _xor(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = xor <{=N} x {=T}> %self, %other\n        ret <{=N} x {=T}> %0\n\n    def __xor__(self, other: Vec[T, N]) -> Vec[T, N]:\n        if Vec._is_float(T):\n            return Vec[T, N](self._as_int()._xor(other._as_int()))\n        return self._xor(other)\n\n    @pure\n    @llvm\n    def _lsh(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = shl <{=N} x {=T}> %self, %other\n        ret <{=N} x {=T}> %0\n\n    @pure\n    @llvm\n    def _lshuffle(self: Vec[T, N], I: Literal[int]) -> Vec[T, N]:\n        %0 = shufflevector <{=N} x {=T}> %self, <{=N} x {=T}> zeroinitializer, \\\n            <{=N} x i32> < {=static.loop_string(I, N, \"i32 %%\", \", \")}, \\\n                           {=static.loop_string(N, N + I, \"i32 %%\", \", \")} >\n        ret <{=N} x {=T}> %0\n\n    def __lshift__(self, other: Vec[T, N]) -> Vec[T, N]:\n        return self._lsh(other)\n\n    def lsh(self, other: Literal[int]) -> Vec[T, N]:\n        if N == 0:\n            return self\n        return self._lshuffle(other)\n\n    @pure\n    @llvm\n    def _rsh(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = lshr <{=N} x {=T}> %self, %other\n        ret <{=N} x {=T}> %0\n\n    @pure\n    @llvm\n    def _rshuffle(self: Vec[T, N], I: Literal[int]) -> Vec[T, N]:\n        %0 = shufflevector <{=N} x {=T}> %self, <{=N} x {=T}> zeroinitializer, \\\n            <{=N} x i32> < {=static.loop_string(N, N + I, \"i32 %%\", \", \")}, \\\n                           {=static.loop_string(0, N - I, \"i32 %%\", \", \")} >\n        ret <{=N} x {=T}> %0\n\n    def __rshift__(self, other: Vec[T, N]) -> Vec[T, N]:\n        return self._rsh(other)\n\n    def rsh(self, other: Literal[int]) -> Vec[T, N]:\n        if N == 0:\n            return self\n        return self._rshuffle(other)\n\n    @pure\n    @llvm\n    def _add(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = add <{=N} x {=T}> %self, %other\n        ret <{=N} x {=T}> %0\n\n    @pure\n    @llvm\n    def _fadd(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = fadd <{=N} x {=T}> %self, %other\n        ret <{=N} x {=T}> %0\n\n    def __add__(self, other: Vec[T, N]) -> Vec[T, N]:\n        if Vec._is_float(T):\n            return self._fadd(other)\n        return self._add(other)\n\n    def __add__(self, other: T) -> Vec[T, N]:\n        return self + Vec[T, N](other)\n\n    @pure\n    @llvm\n    def _sub(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = sub <{=N} x {=T}> %self, %other\n        ret <{=N} x {=T}> %0\n\n    @pure\n    @llvm\n    def _fsub(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = fsub <{=N} x {=T}> %self, %other\n        ret <{=N} x {=T}> %0\n\n    def __sub__(self, other: Vec[T, N]) -> Vec[T, N]:\n        if Vec._is_float(T):\n            return self._fsub(other)\n        return self._sub(other)\n\n    def __sub__(self, other: T) -> Vec[T, N]:\n        return self - Vec[T, N](other)\n\n    @pure\n    @llvm\n    def _mul(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = mul <{=N} x {=T}> %self, %other\n        ret <{=N} x {=T}> %0\n\n    @pure\n    @llvm\n    def _fmul(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = fmul <{=N} x {=T}> %self, %other\n        ret <{=N} x {=T}> %0\n\n    def __mul__(self, other: Vec[T, N]) -> Vec[T, N]:\n        if Vec._is_float(T):\n            return self._fmul(other)\n        return self._mul(other)\n\n    def __mul__(self, other: T) -> Vec[T, N]:\n        return self - Vec[T, N](other)\n\n    @pure\n    @llvm\n    def _sdiv(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = sdiv <{=N} x {=T}> %self, %other\n        ret <{=N} x {=T}> %0\n\n    @pure\n    @llvm\n    def _udiv(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = udiv <{=N} x {=T}> %self, %other\n        ret <{=N} x {=T}> %0\n\n    @pure\n    @llvm\n    def _fdiv(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = fdiv <{=N} x {=T}> %self, %other\n        ret <{=N} x {=T}> %0\n\n    def __truediv__(self, other: Vec[T, N]) -> Vec[T, N]:\n        if not Vec._is_float(T):\n            compile_error(\"true division requires a float type\")\n        return self._fdiv(other)\n\n    def __truediv__(self, other: T) -> Vec[T, N]:\n        return self / Vec[T, N](other)\n\n    def __floordiv__(self, other: Vec[T, N]) -> Vec[T, N]:\n        if not Vec._is_int(T):\n            compile_error(\"integer division requires an integer type\")\n        if Vec._is_uint(T):\n            return self._udiv(other)\n        return self._sdiv(other)\n\n    def __floordiv__(self, other: T) -> Vec[T, N]:\n        return self // Vec[T, N](other)\n\n    @pure\n    @llvm\n    def _mod(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = urem <{=N} x {=T}> %self, %other\n        ret <{=N} x {=T}> %0\n\n    def __mod__(self, other: Vec[T, N]) -> Vec[T, N]:\n        return self._mod(other)\n\n    def __mod__(self, other: T) -> Vec[T, N]:\n        return self._mod(Vec[T, N](other))\n\n    @pure\n    @llvm\n    def _lt(self: Vec[T, N], other: Vec[T, N]) -> Vec[u1, N]:\n        %0 = icmp ult <{=N} x {=T}> %self, %other\n        ret <{=N} x i1> %0\n\n    @pure\n    @llvm\n    def _flt(self: Vec[T, N], other: Vec[T, N]) -> Vec[u1, N]:\n        %0 = fcmp olt <{=N} x {=T}> %self, %other\n        ret <{=N} x i1> %0\n\n    def __lt__(self, other: Vec[T, N]) -> Vec[u1, N]:\n        if Vec._is_float(T):\n            return self._flt(other)\n        return self._lt(other)\n\n    def __lt__(self, other: T) -> Vec[u1, N]:\n        return self < Vec[T, N](other)\n\n    @pure\n    @llvm\n    def _le(self: Vec[T, N], other: Vec[T, N]) -> Vec[u1, N]:\n        %0 = icmp ule <{=N} x {=T}> %self, %other\n        ret <{=N} x i1> %0\n\n    @pure\n    @llvm\n    def _fle(self: Vec[T, N], other: Vec[T, N]) -> Vec[u1, N]:\n        %0 = fcmp ole <{=N} x {=T}> %self, %other\n        ret <{=N} x i1> %0\n\n    def __le__(self, other: Vec[T, N]) -> Vec[u1, N]:\n        if Vec._is_float(T):\n            return self._fle(other)\n        return self._le(other)\n\n    def __le__(self, other: T) -> Vec[u1, N]:\n        return self <= Vec[T, N](other)\n\n    @pure\n    @llvm\n    def _gt(self: Vec[T, N], other: Vec[T, N]) -> Vec[u1, N]:\n        %0 = icmp ugt <{=N} x {=T}> %self, %other\n        ret <{=N} x i1> %0\n\n    @pure\n    @llvm\n    def _fgt(self: Vec[T, N], other: Vec[T, N]) -> Vec[u1, N]:\n        %0 = fcmp ogt <{=N} x {=T}> %self, %other\n        ret <{=N} x i1> %0\n\n    def __gt__(self, other: Vec[T, N]) -> Vec[u1, N]:\n        if Vec._is_float(T):\n            return self._fgt(other)\n        return self._gt(other)\n\n    def __gt__(self, other: T) -> Vec[u1, N]:\n        return self > Vec[T, N](other)\n\n    @pure\n    @llvm\n    def _ge(self: Vec[T, N], other: Vec[T, N]) -> Vec[u1, N]:\n        %0 = icmp uge <{=N} x {=T}> %self, %other\n        ret <{=N} x i1> %0\n\n    @llvm\n    def _fge(self: Vec[T, N], other: Vec[T, N]) -> Vec[u1, N]:\n        %0 = fcmp oge <{=N} x {=T}> %self, %other\n        ret <{=N} x i1> %0\n\n    def __ge__(self, other: Vec[T, N]) -> Vec[u1, N]:\n        if Vec._is_float(T):\n            return self._fge(other)\n        return self._ge(other)\n\n    def __ge__(self, other: T) -> Vec[u1, N]:\n        return self >= Vec[T, N](other)\n\n    @pure\n    @llvm\n    def _sqrt(self: Vec[T, N]) -> Vec[T, N]:\n        declare <{=N} x {=T}> @llvm.sqrt.v{=N}{=T}(<{=N} x {=T}>)\n        %0 = call <{=N} x {=T}> @llvm.sqrt.v{=N}{=T}(<{=N} x {=T}> %self)\n        ret <{=N} x {=T}> %0\n\n    def sqrt(self):\n        if Vec._is_float(T):\n            return self._sqrt()\n        compile_error(\"vector type must be a float\")\n\n    @pure\n    @llvm\n    def _log(self: Vec[T, N]) -> Vec[T, N]:\n        declare <{=N} x {=T}> @llvm.log.v{=N}{=T}(<{=N} x {=T}>)\n        %0 = call <{=N} x {=T}> @llvm.log.v{=N}{=T}(<{=N} x {=T}> %self)\n        ret <{=N} x {=T}> %0\n\n    def log(self):\n        if Vec._is_float(T):\n            return self._log()\n        compile_error(\"vector type must be a float\")\n\n    @pure\n    @llvm\n    def _sin(self: Vec[T, N]) -> Vec[T, N]:\n        declare <{=N} x {=T}> @llvm.sin.v{=N}{=T}(<{=N} x {=T}>)\n        %0 = call <{=N} x {=T}> @llvm.sin.v{=N}{=T}(<{=N} x {=T}> %self)\n        ret <{=N} x {=T}> %0\n\n    def sin(self):\n        if Vec._is_float(T):\n            return self._sin()\n        compile_error(\"vector type must be a float\")\n\n    @pure\n    @llvm\n    def _cos(self: Vec[T, N]) -> Vec[T, N]:\n        declare <{=N} x {=T}> @llvm.cos.v{=N}{=T}(<{=N} x {=T}>)\n        %0 = call <{=N} x {=T}> @llvm.cos.v{=N}{=T}(<{=N} x {=T}> %self)\n        ret <{=N} x {=T}> %0\n\n    def cos(self):\n        if Vec._is_float(T):\n            return self._cos()\n        compile_error(\"vector type must be a float\")\n\n    @pure\n    @llvm\n    def _fabs(self: Vec[T, N]) -> Vec[T, N]:\n        declare <{=N} x {=T}> @llvm.fabs.v{=N}{=T}(<{=N} x {=T}>)\n        %0 = call <{=N} x {=T}> @llvm.fabs.v{=N}{=T}(<{=N} x {=T}> %self)\n        ret <{=N} x {=T}> %0\n\n    @pure\n    @llvm\n    def _abs(self: Vec[T, N]) -> Vec[T, N]:\n        declare <{=N} x {=T}> @llvm.abs.v{=N}{=T}(<{=N} x {=T}>, i1)\n        %0 = call <{=N} x {=T}> @llvm.abs.v{=N}{=T}(<{=N} x {=T}> %self, i1 false)\n        ret <{=N} x {=T}> %0\n\n    def __abs__(self):\n        if Vec._is_float(T):\n            return self._fabs()\n        return self._abs()\n\n    @pure\n    @llvm\n    def _min(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        declare <{=N} x {=T}> @llvm.smin.v{=N}{=T}(<{=N} x {=T}>, <{=N} x {=T}>)\n        %0 = call <{=N} x {=T}> @llvm.smin.v{=N}{=T}(<{=N} x {=T}> %self, <{=N} x {=T}> %other)\n        ret <{=N} x {=T}> %0\n\n    def min(self, other: Vec[T, N]) -> Vec[T, N]:\n        if Vec._is_int(T):\n            return self._min(other)\n        compile_error(\"vector type must be an integer\")\n\n    @pure\n    @llvm\n    def _max(self: Vec[T, N], other: Vec[T, N]) -> Vec[T, N]:\n        declare <{=N} x {=T}> @llvm.smax.v{=N}{=T}(<{=N} x {=T}>, <{=N} x {=T}>)\n        %0 = call <{=N} x {=T}> @llvm.smax.v{=N}{=T}(<{=N} x {=T}> %self, <{=N} x {=T}> %other)\n        ret <{=N} x {=T}> %0\n\n    def max(self, other: Vec[T, N]) -> Vec[T, N]:\n        if Vec._is_int(T):\n            return self._max(other)\n        compile_error(\"vector type must be an integer\")\n\n    @pure\n    @llvm\n    def _mask(self: Vec[T, N], mask: Vec[u1, N], other: Vec[T, N]) -> Vec[T, N]:\n        %0 = select <{=N} x i1> %mask, <{=N} x {=T}> %self, <{=N} x {=T}> %other\n        ret <{=N} x {=T}> %0\n\n    def mask(self, other: Vec[T, N], mask) -> Vec[T, N]:\n        return self._mask(mask.cast(u1), other)\n\n    @pure\n    @llvm\n    def _add_overflow(self: Vec[T, N], other: Vec[T, N]) -> Tuple[Vec[T, N], Vec[u1, N]]:\n        declare {<{=N} x {=T}>, <{=N} x i1>} @llvm.uadd.with.overflow.v{=N}{=T}(<{=N} x {=T}>, <{=N} x {=T}>)\n        %0 = call {<{=N} x {=T}>, <{=N} x i1>} @llvm.uadd.with.overflow.v{=N}{=T}(<{=N} x {=T}> %self, <{=N} x {=T}> %other)\n        ret {<{=N} x {=T}>, <{=N} x i1>} %0\n\n    def add(self, other: Vec[T, N], overflow: Literal[bool] = False):\n        if overflow:\n            return self._add_overflow(other)\n        else:\n            return self + other\n\n    @pure\n    @llvm\n    def _sub_overflow(self: Vec[T, N], other: Vec[T, N]) -> Tuple[Vec[T, N], Vec[u1, N]]:\n        declare {<{=N} x {=T}>, <{=N} x i1>} @llvm.usub.with.overflow.v{=N}{=T}(<{=N} x {=T}>, <{=N} x {=T}>)\n        %0 = call {<{=N} x {=T}>, <{=N} x i1>} @llvm.usub.with.overflow.v{=N}{=T}(<{=N} x {=T}> %self, <{=N} x {=T}> %other)\n        ret {<{=N} x {=T}>, <{=N} x i1>} %0\n\n    def sub(self, other: Vec[T, N], overflow: Literal[bool] = False):\n        if overflow:\n            return self._sub_overflow(other)\n        else:\n            return self - other\n\n    @pure\n    @llvm\n    def _sum(self: Vec[T, N]) -> T:\n        declare {=T} @llvm.vector.reduce.add.v{=N}{=T}(<{=N} x {=T}>)\n        %0 = call {=T} @llvm.vector.reduce.add.v{=N}{=T}(<{=N} x {=T}> %self)\n        ret {=T} %0\n\n    @pure\n    @llvm\n    def _fsum(self: Vec[T, N], S: Literal[int]) -> T:\n        declare {=T} @llvm.vector.reduce.fadd.v{=N}f{=S}({=T}, <{=N} x {=T}>)\n        %0 = call {=T} @llvm.vector.reduce.fadd.v{=N}f{=S}({=T} 0.0, <{=N} x {=T}> %self)\n        ret {=T} %0\n\n    def sum(self) -> T:\n        if Vec._is_float(T):\n            return self._fsum(S=Vec._sizeof(T))\n        return self._sum()\n\n    # Utilities\n    def scatter(self: Vec[T, N]) -> List[T]:\n        return [self[i] for i in static.range(N)]\n\n    def bitflip(self) -> Vec[T, N]:\n        if Vec._is_int(T):\n            return self ^ Vec[T, N](T(-1))\n        compile_error(\"vector type must be an integer\")\n"
  },
  {
    "path": "stdlib/sortedlist.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nfrom bisect import bisect_right, bisect_left, insort\nfrom collections import deque\n\nDEFAULT_LOAD_FACTOR = 1000\n\nclass SortedList:\n    _len: int\n    _load: int\n    _lists: List[List[T]]\n    _maxes: List[T]\n    _offset: int\n    T: type\n\n    def __init__(self):\n        self._len = 0\n        self._load = DEFAULT_LOAD_FACTOR\n        self._lists = []\n        self._maxes = []\n        self._offset = 0\n\n    def clear(self):\n        \"\"\"\n        Remove all values from sorted list.\n        Runtime complexity: $O(n)$\n        \"\"\"\n        self._len = 0\n        self._lists.clear()\n        self._maxes.clear()\n        self._offset = 0\n\n    @property\n    def left(self) -> T:\n        if not self._lists:\n            raise IndexError(\"list index out of range\")\n        return self._lists[0][0]\n\n    def add(self, value: T):\n        \"\"\"\n        Add `value` to sorted list.\n        Runtime complexity: $O(\\\\log(n))$ (approximate).\n\n        ``` python\n        >>> sl = SortedList()\n        >>> sl.add(3)\n        >>> sl.add(1)\n        >>> sl.add(2)\n        >>> sl\n        SortedList([1, 2, 3])\n        ```\n        \"\"\"\n        if self._maxes:\n            pos = bisect_right(self._maxes, value)\n            if pos == len(self._maxes):\n                pos -= 1\n                self._lists[pos].append(value)\n                self._maxes[pos] = value\n            else:\n                insort(self._lists[pos], value)\n            self._expand(pos)\n        else:\n            self._lists.append([value])\n            self._maxes.append(value)\n        self._len += 1\n\n    def _expand(self, pos: int):\n        \"\"\"\n        Split sublists with length greater than double the load-factor.\n        Updates the index when the sublist length is less than double the load\n        level. This requires incrementing the nodes in a traversal from the\n        leaf node to the root. For an example traversal see\n        ``SortedList._loc``.\n        \"\"\"\n        if len(self._lists[pos]) > (self._load << 1):\n            _maxes = self._maxes\n\n            _lists_pos = self._lists[pos]\n            half = _lists_pos[self._load :]\n            del _lists_pos[self._load :]\n            _maxes[pos] = _lists_pos[-1]\n\n            self._lists.insert(pos + 1, half)\n            _maxes.insert(pos + 1, half[-1])\n\n    def _delete(self, pos: int, idx: int):\n        \"\"\"\n        Delete value at the given `(pos, idx)`.\n        Combines lists that are less than half the load level.\n        Updates the index when the sublist length is more than half the load\n        level. This requires decrementing the nodes in a traversal from the\n        leaf node to the root. For an example traversal see\n        `SortedList._loc`.\n        \"\"\"\n        _lists_pos = self._lists[pos]\n        del _lists_pos[idx]\n        self._len -= 1\n\n        len_lists_pos = len(_lists_pos)\n\n        if len_lists_pos > (self._load >> 1):\n            self._maxes[pos] = _lists_pos[-1]\n        elif len(self._lists) > 1:\n            if not pos:\n                pos += 1\n\n            prev = pos - 1\n            self._lists[prev].extend(self._lists[pos])\n            self._maxes[prev] = self._lists[prev][-1]\n\n            del self._lists[pos]\n            del self._maxes[pos]\n\n            self._expand(prev)\n        elif len_lists_pos:\n            self._maxes[pos] = _lists_pos[-1]\n        else:\n            del self._lists[pos]\n            del self._maxes[pos]\n\n    def __iter__(self) -> Generator[T]:\n        for l in self._lists:\n            yield from l\n\n    def __len__(self) -> int:\n        return self._len\n\n    def __bool__(self) -> bool:\n        return self._len > 0\n"
  },
  {
    "path": "stdlib/statistics.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nimport bisect\nimport random\nfrom math import (\n    gcd as _gcd,\n    erf as _erf,\n    exp as _exp,\n    fabs as _fabs,\n    floor as _floor,\n    frexp as _frexp,\n    fsum as _fsum,\n    log as _log,\n    sqrt as _sqrt,\n    tau as _tau,\n    hypot as _hypot,\n)\n\nclass StatisticsError(Exception):\n    def __init__(self, message: str = \"\"):\n        super().__init__(message)\n\ndef median(data: List[T], T: type) -> float:\n    \"\"\"\n    Return the median (middle value) of numeric data.\n\n    When the number of data points is odd, return the middle data point.\n    When the number of data points is even, the median is interpolated by\n    taking the average of the two middle values\n    \"\"\"\n    data = sorted(data)\n    n = len(data)\n    if n == 0:\n        raise StatisticsError(\"no median for empty data\")\n    if n % 2 == 1:\n        return float(data[n // 2])\n    else:\n        i = n // 2\n        return (data[i - 1] + data[i]) / 2\n\ndef median_low(data: List[T], T: type) -> float:\n    \"\"\"\n    Return the low median of numeric data.\n\n    When the number of data points is odd, the middle value is returned.\n    When it is even, the smaller of the two middle values is returned.\n    \"\"\"\n    data = sorted(data)\n    n = len(data)\n    if n == 0:\n        raise StatisticsError(\"no median for empty data\")\n    if n % 2 == 1:\n        return float(data[n // 2])\n    else:\n        return float(data[n // 2 - 1])\n\ndef median_high(data: List[T], T: type) -> float:\n    \"\"\"\n    Return the high median of data.\n\n    When the number of data points is odd, the middle value is returned.\n    When it is even, the larger of the two middle values is returned.\n    \"\"\"\n    data = sorted(data)\n    n = len(data)\n    if n == 0:\n        raise StatisticsError(\"no median for empty data\")\n    return float(data[n // 2])\n\ndef _find_lteq(a: List[T], x: float, T: type):\n    \"\"\"\n    Locate the leftmost value exactly equal to x\n    \"\"\"\n    i = bisect.bisect_left(a, x)\n    if i != len(a) and a[i] == x:\n        return i\n    assert False\n\ndef _find_rteq(a: List[T], l: int, x: float, T: type):\n    \"\"\"\n    Locate the rightmost value exactly equal to x\n    \"\"\"\n    i = bisect.bisect_right(a, x, lo=l)\n    if i != (len(a) + 1) and a[i - 1] == x:\n        return i - 1\n    assert False\n\ndef median_grouped(data: List[T], interval: S = 1, T: type, S: type = int) -> float:\n    \"\"\"\n    Return the 50th percentile (median) of grouped continuous data.\n    \"\"\"\n    data = sorted(data)\n    n = len(data)\n    if n == 0:\n        raise StatisticsError(\"no median for empty data\")\n    elif n == 1:\n        return float(data[0])\n\n    # Find the value at the midpoint.\n    x = float(data[n // 2])\n    L = x - float(interval) / 2  # The lower limit of the median interval.\n\n    # Find the position of leftmost occurrence of x in data\n    l1 = _find_lteq(data, x)\n    # Find the position of rightmost occurrence of x in data[l1...len(data)]\n    # Assuming always l1 <= l2\n    l2 = _find_rteq(data, l1, x)\n    cf = l1\n    f = l2 - l1 + 1\n    return L + interval * (n / 2 - cf) / f\n\ndef mode(data: List[T], T: type) -> T:\n    \"\"\"\n    Return the most common data point from discrete or nominal data.\n    \"\"\"\n    counter = 0\n    elem = data[0]\n\n    for i in data:\n        curr_frequency = data.count(i)\n        if curr_frequency > counter:\n            counter = curr_frequency\n            elem = i\n    return elem\n\ndef multimode(data: List[T], T: type):\n    \"\"\"\n    Return a list of the most frequently occurring values.\n\n    Will return more than one result if there are multiple modes\n    or an empty list if *data* is empty.\n    \"\"\"\n    elem = data[0]\n    counter = data.count(elem)\n    li = sorted(data)\n    mulmode = []\n\n    for i in li:\n        curr_frequency = data.count(i)\n        if curr_frequency > counter:\n            mulmode = []\n            mulmode.append(i)\n            counter = curr_frequency\n        elif curr_frequency == counter and i not in mulmode:\n            mulmode.append(i)\n    return mulmode\n\ndef quantiles(\n    data: List[T], n: int = 4, method: str = \"exclusive\", T: type\n) -> List[float]:\n    \"\"\"\n    Divide *data* into *n* continuous intervals with equal probability.\n\n    Returns a list of (n - 1) cut points separating the intervals.\n\n    Set *n* to 4 for quartiles (the default).  Set *n* to 10 for deciles.\n    Set *n* to 100 for percentiles which gives the 99 cuts points that\n    separate *data* into 100 equal sized groups.\n\n    The *data* can be any iterable containing sample.\n    The cut points are linearly interpolated between data points.\n\n    If *method* is set to *inclusive*, *data* is treated as population\n    data.  The minimum value is treated as the 0th percentile and the\n    maximum value is treated as the 100th percentile.\n    \"\"\"\n    if n < 1:\n        raise StatisticsError(\"n must be at least 1\")\n    data = sorted(data)\n    ld = len(data)\n    if ld < 2:\n        raise StatisticsError(\"must have at least two data points\")\n\n    if method == \"inclusive\":\n        m = ld - 1\n        result = []\n        for i in range(1, n):\n            j = i * m // n\n            delta = (i * m) - (j * n)\n            interpolated = (data[j] * (n - delta) + data[j + 1] * delta) / n\n            result.append(interpolated)\n        return result\n    if method == \"exclusive\":\n        m = ld + 1\n        result = []\n        for i in range(1, n):\n            j = i * m // n  # rescale i to m/n\n            j = 1 if j < 1 else ld - 1 if j > ld - 1 else j  # clamp to 1 .. ld-1\n            delta = (i * m) - (j * n)  # exact integer math\n            interpolated = (data[j - 1] * (n - delta) + data[j] * delta) / n\n            result.append(interpolated)\n        return result\n    raise ValueError(f\"Unknown method: {method}\")\n\ndef _lcm(x: int, y: int):\n    \"\"\"\n    Returns the lowest common multiple between x and y\n    \"\"\"\n    greater = 0\n    if x > y:\n        greater = x\n    else:\n        greater = y\n\n    while True:\n        if greater % x == 0 and greater % y == 0:\n            lcm = greater\n            return lcm\n        greater += 1\n\ndef _sum(data: List[float]) -> float:\n    \"\"\"\n    Return a high-precision sum of the given numeric data as a fraction,\n    together with the type to be converted to and the count of items.\n\n    If optional argument ``start`` is given, it is added to the total.\n    If ``data`` is empty, ``start`` (defaulting to 0) is returned.\n\n    TODO/CAVEATS\n      - The start argument should default to 0 or 0.0\n      - Assumes input is floats\n    \"\"\"\n    # Neumaier sum\n    # https://en.wikipedia.org/wiki/Kahan_summation_algorithm#Further_enhancements\n    # https://www.mat.univie.ac.at/~neum/scan/01.pdf (German)\n    s = 0.0\n    c = 0.0\n    i = 0\n    N = len(data)\n    while i < N:\n        x = data[i]\n        t = s + x\n        if abs(s) >= abs(x):\n            c += (s - t) + x\n        else:\n            c += (x - t) + s\n        s = t\n        i += 1\n    return s + c\n\ndef mean(data: List[float]) -> float:\n    \"\"\"\n    Return the sample arithmetic mean of data.\n\n    TODO/CAVEATS\n      - Assumes input is floats\n      - Does not address NAN or INF\n    \"\"\"\n    n = len(data)\n    if n < 1:\n        raise StatisticsError(\"mean requires at least one data point\")\n    total = _sum(data)\n    return total / n\n\n'''\ndef fmean(data: List[float]) -> float:\n    \"\"\"\n    Convert data to floats and compute the arithmetic mean.\n\n    TODO/CAVEATS\n    @jordan- fsum is not implemented in math.seq yet and the above\n             mean(data) deals with only floats. Thus this function is passed for now.\n    \"\"\"\n    pass\n'''\n\ndef geometric_mean(data: List[float]) -> float:\n    \"\"\"\n    Convert data to floats and compute the geometric mean.\n\n    Raises a StatisticsError if the input dataset is empty,\n\n    TODO/CAVEATS:\n      - Assumes input is a list of floats\n      - Uses mean instead of fmean for now\n      - Does not handle data that contains a zero, or if it contains a negative value.\n    \"\"\"\n    if len(data) < 1:\n        raise StatisticsError(\"geometric mean requires a non-empty dataset\")\n    return _exp(mean(list(map(_log, data))))\n\ndef _fail_neg(values: List[float], errmsg: str):\n    \"\"\"\n    Iterate over values, failing if any are less than zero.\n    \"\"\"\n    for x in values:\n        if x < 0:\n            raise StatisticsError(errmsg)\n        yield x\n\ndef harmonic_mean(data: List[float]) -> float:\n    \"\"\"\n    Return the harmonic mean of data.\n\n    The harmonic mean, sometimes called the subcontrary mean, is the\n    reciprocal of the arithmetic mean of the reciprocals of the data,\n    and is often appropriate when averaging quantities which are rates\n    or ratios.\n    \"\"\"\n    errmsg = \"harmonic mean does not support negative values\"\n    n = len(data)\n    if n < 1:\n        raise StatisticsError(\"harmonic_mean requires at least one data point\")\n\n    x = data[0]\n\n    if n == 1:\n        return x\n    total = 0.0\n\n    li = List[float](n)\n    for x in _fail_neg(data, errmsg):\n        if x == 0.0:\n            return 0.0\n        li.append(1 / x)\n    total = _sum(li)\n    return n / total\n\ndef _ss(data: List[float], c: float):\n    \"\"\"\n    Return sum of square deviations of sequence data.\n\n    If c is None, the mean is calculated in one pass, and the deviations\n    from the mean are calculated in a second pass. Otherwise, deviations are\n    calculated from c as given.\n    \"\"\"\n    total = _sum([(x - c) ** 2 for x in data])\n    total2 = _sum([(x - c) for x in data])\n\n    total -= total2 ** 2 / len(data)\n    return total\n\ndef pvariance(data: List[float], mu: Optional[float] = None):\n    \"\"\"\n    Return the population variance of `data`.\n\n    Should contain atleast one value.\n    The optional argument mu, if given, should be the mean of\n    the data. If it is missing or None, the mean is automatically calculated.\n\n    TODO/CAVEATS:\n      - Assumes input is a list of floats\n    \"\"\"\n    if mu is None:\n        mu = mean(data)\n\n    n = len(data)\n    if n < 1:\n        raise StatisticsError(\"pvariance requires at least one data point\")\n\n    ss = _ss(data, mu)\n    return ss / n\n\ndef pstdev(data: List[float], mu: Optional[float] = None):\n    \"\"\"\n    Return the square root of the population variance.\n    \"\"\"\n    if mu is None:\n        mu = mean(data)\n    var = pvariance(data, mu)\n    return _sqrt(var)\n\ndef variance(data: List[float], xbar: Optional[float] = None):\n    \"\"\"\n    Return the sample variance of data.\n\n    Shoulw contain atleast two values.\n    The optional argument xbar, if given, should be the mean of\n    the data. If it is missing or None, the mean is automatically calculated.\n    \"\"\"\n    if xbar is None:\n        xbar = mean(data)\n    n = len(data)\n    if n < 2:\n        raise StatisticsError(\"variance requires at least two data points\")\n    ss = _ss(data, xbar)\n    return ss / (n - 1)\n\ndef stdev(data, xbar: Optional[float] = None):\n    \"\"\"\n    Return the square root of the sample variance.\n    \"\"\"\n    if xbar is None:\n        xbar = mean(data)\n    var = variance(data, xbar)\n    return _sqrt(var)\n\nclass NormalDist:\n    \"\"\"\n    Normal distribution of a random variable\n    \"\"\"\n\n    PRECISION: float\n    _mu: float\n    _sigma: float\n\n    def __eq__(self, other: NormalDist):\n        return (self._mu - other._mu) < self.PRECISION and (\n            self._sigma - other._sigma\n        ) < self.PRECISION\n\n    def _init(self, mu: float, sigma: float):\n        self.PRECISION = 1e-6\n        if sigma < 0.0:\n            raise StatisticsError(\"sigma must be non-negative\")\n        self._mu = mu\n        self._sigma = sigma\n\n    def __init__(self, mu, sigma):\n        self._init(float(mu), float(sigma))\n\n    def __init__(self, mu):\n        self._init(float(mu), 1.0)\n\n    def __init__(self):\n        self._init(0.0, 1.0)\n\n    @property\n    def mean(self):\n        \"\"\"\n        Arithmetic mean of the normal distribution.\n        \"\"\"\n        return self._mu\n\n    @property\n    def median(self):\n        \"\"\"\n        Return the median of the normal distribution\n        \"\"\"\n        return self._mu\n\n    @property\n    def mode(self):\n        \"\"\"\n        Return the mode of the normal distribution\n\n        The mode is the value x where which the probability density\n        function (pdf) takes its maximum value.\n        \"\"\"\n        return self._mu\n\n    @property\n    def stdev(self):\n        \"\"\"\n        Standard deviation of the normal distribution.\n        \"\"\"\n        return self._sigma\n\n    @property\n    def variance(self):\n        \"\"\"\n        Square of the standard deviation.\n        \"\"\"\n        return self._sigma ** 2.0\n\n    def pdf(self, x):\n        \"\"\"\n        Probability density function.  P(x <= X < x+dx) / dx\n        \"\"\"\n        variance = self._sigma ** 2.0\n        if not variance:\n            raise StatisticsError(\"pdf() not defined when sigma is zero\")\n        return _exp((x - self._mu) ** 2.0 / (-2.0 * variance)) / _sqrt(_tau * variance)\n\n    def cdf(self, x):\n        \"\"\"\n        Cumulative distribution function.  P(X <= x)\n        \"\"\"\n        if not self._sigma:\n            raise StatisticsError(\"cdf() not defined when sigma is zero\")\n        return 0.5 * (1.0 + _erf((x - self._mu) / (self._sigma * _sqrt(2.0))))\n\n    def _normal_dist_inv_cdf(self, p: float, mu: float, sigma: float):\n        \"\"\"\n        Wichura, M.J. (1988). \"Algorithm AS241: The Percentage Points of the\n        Normal Distribution\".  Applied Statistics. Blackwell Publishing. 37\n        (3): 477–484. doi:10.2307/2347330. JSTOR 2347330.\n        \"\"\"\n        q = p - 0.5\n        num = 0.0\n        den = 0.0\n        if _fabs(q) <= 0.425:\n            r = 0.180625 - q * q\n            # Hash sum: 55.88319_28806_14901_4439\n            num = (((((((2.5090809287301226727e+3 * r +\n                    3.3430575583588128105e+4) * r +\n                    6.7265770927008700853e+4) * r +\n                    4.5921953931549871457e+4) * r +\n                    1.3731693765509461125e+4) * r +\n                    1.9715909503065514427e+3) * r +\n                    1.3314166789178437745e+2) * r +\n                    3.3871328727963666080e+0) * q\n            den = (((((((5.2264952788528545610e+3 * r +\n                    2.8729085735721942674e+4) * r +\n                    3.9307895800092710610e+4) * r +\n                    2.1213794301586595867e+4) * r +\n                    5.3941960214247511077e+3) * r +\n                    6.8718700749205790830e+2) * r +\n                    4.2313330701600911252e+1) * r +\n                    1.0)\n            x = num / den\n            return mu + (x * sigma)\n        r = p if q <= 0.0 else 1.0 - p\n        r = _sqrt(-_log(r))\n        if r <= 5.0:\n            r = r - 1.6\n            # Hash sum: 49.33206_50330_16102_89036\n            num = (((((((7.74545014278341407640e-4 * r +\n                    2.27238449892691845833e-2) * r +\n                    2.41780725177450611770e-1) * r +\n                    1.27045825245236838258e+0) * r +\n                    3.64784832476320460504e+0) * r +\n                    5.76949722146069140550e+0) * r +\n                    4.63033784615654529590e+0) * r +\n                    1.42343711074968357734e+0)\n            den = (((((((1.05075007164441684324e-9 * r +\n                    5.47593808499534494600e-4) * r +\n                    1.51986665636164571966e-2) * r +\n                    1.48103976427480074590e-1) * r +\n                    6.89767334985100004550e-1) * r +\n                    1.67638483018380384940e+0) * r +\n                    2.05319162663775882187e+0) * r +\n                    1.0)\n        else:\n            r = r - 5.0\n            # Hash sum: 47.52583_31754_92896_71629\n            num = (((((((2.01033439929228813265e-7 * r +\n                    2.71155556874348757815e-5) * r +\n                    1.24266094738807843860e-3) * r +\n                    2.65321895265761230930e-2) * r +\n                    2.96560571828504891230e-1) * r +\n                    1.78482653991729133580e+0) * r +\n                    5.46378491116411436990e+0) * r +\n                    6.65790464350110377720e+0)\n            den = (((((((2.04426310338993978564e-15 * r +\n                    1.42151175831644588870e-7) * r +\n                    1.84631831751005468180e-5) * r +\n                    7.86869131145613259100e-4) * r +\n                    1.48753612908506148525e-2) * r +\n                    1.36929880922735805310e-1) * r +\n                    5.99832206555887937690e-1) * r +\n                    1.0)\n        x = num / den\n        if q < 0.0:\n            x = -x\n        return mu + (x * sigma)\n\n    def inv_cdf(self, p: float):\n        \"\"\"\n        Inverse cumulative distribution function.  x : P(X <= x) = p\n\n        Finds the value of the random variable such that the probability of\n        the variable being less than or equal to that value equals the given\n        probability.\n        \"\"\"\n        if p <= 0.0 or p >= 1.0:\n            raise StatisticsError(\"p must be in the range 0.0 < p < 1.0\")\n        if self._sigma <= 0.0:\n            raise StatisticsError(\"cdf() not defined when sigma at or below zero\")\n        return self._normal_dist_inv_cdf(p, self._mu, self._sigma)\n\n    def quantiles(self, n: int = 4):\n        \"\"\"\n        Divide into *n* continuous intervals with equal probability.\n\n        Returns a list of (n - 1) cut points separating the intervals.\n\n        Set *n* to 4 for quartiles (the default).  Set *n* to 10 for deciles.\n        Set *n* to 100 for percentiles which gives the 99 cuts points that\n        separate the normal distribution into 100 equal sized groups.\n        \"\"\"\n        return [self.inv_cdf(float(i) / float(n)) for i in range(1, n)]\n\n    def overlap(self, other: NormalDist) -> float:\n        \"\"\"\n        Compute the overlapping coefficient (OVL) between two normal distributions.\n\n        Measures the agreement between two normal probability distributions.\n        Returns a value between 0.0 and 1.0 giving the overlapping area in\n        the two underlying probability density functions.\n        \"\"\"\n        X, Y = self, other\n\n        if (Y._sigma, Y._mu) < (X._sigma, X._mu):  # sort to assure commutativity\n            X, Y = Y, X\n\n        X_var, Y_var = X.variance, Y.variance\n\n        if not X_var or not Y_var:\n            raise StatisticsError(\"overlap() not defined when sigma is zero\")\n\n        dv = Y_var - X_var\n        dm = _fabs(Y._mu - X._mu)\n\n        if not dv:\n            return 1.0 - _erf(dm / (2.0 * X._sigma * _sqrt(2.0)))\n\n        a = X._mu * Y_var - Y._mu * X_var\n        b = X._sigma * Y._sigma * _sqrt(dm ** 2.0 + dv * _log(Y_var / X_var))\n        x1 = (a + b) / dv\n        x2 = (a - b) / dv\n\n        return 1.0 - (_fabs(Y.cdf(x1) - X.cdf(x1)) + _fabs(Y.cdf(x2) - X.cdf(x2)))\n\n    def samples(self, n: int):\n        \"\"\"\n        Generate *n* samples for a given mean and standard deviation.\n        \"\"\"\n        gauss = random.gauss\n        return [gauss(self._mu, self._sigma) for i in range(n)]\n\n    def from_samples(data: List[float]):\n        \"\"\"\n        Make a normal distribution instance from sample data.\n        TODO/CAVEATS:\n          - Assumes input is a list of floats\n          - Uses mean instead of fmean for now\n        \"\"\"\n        xbar = mean(data)\n        return NormalDist(xbar, stdev(data, xbar))\n\n    def __add__(x1: NormalDist, x2: NormalDist):\n        \"\"\"\n        Add a constant or another NormalDist instance.\n        If *other* is a constant, translate mu by the constant,\n        leaving sigma unchanged.\n        If *other* is a NormalDist, add both the means and the variances.\n        Mathematically, this works only if the two distributions are\n        independent or if they are jointly normally distributed.\n        \"\"\"\n        return NormalDist(x1._mu + x2._mu, _hypot(x1._sigma, x2._sigma))\n\n    def __add__(x1: NormalDist, x2: float):\n        \"\"\"\n        Add a constant or another NormalDist instance.\n        If *other* is a constant, translate mu by the constant,\n        leaving sigma unchanged.\n        If *other* is a NormalDist, add both the means and the variances.\n        Mathematically, this works only if the two distributions are\n        independent or if they are jointly normally distributed.\n        \"\"\"\n        return NormalDist(x1._mu + x2, x1._sigma)\n\n    def __sub__(x1: NormalDist, x2: NormalDist):\n        \"\"\"\n        Subtract a constant or another NormalDist instance.\n        If *other* is a constant, translate by the constant mu,\n        leaving sigma unchanged.\n        If *other* is a NormalDist, subtract the means and add the variances.\n        Mathematically, this works only if the two distributions are\n        independent or if they are jointly normally distributed.\n        \"\"\"\n        return NormalDist(x1._mu - x2._mu, _hypot(x1._sigma, x2._sigma))\n\n    def __sub__(x1: NormalDist, x2: float):\n        \"\"\"\n        Subtract a constant or another NormalDist instance.\n        If *other* is a constant, translate by the constant mu,\n        leaving sigma unchanged.\n        If *other* is a NormalDist, subtract the means and add the variances.\n        Mathematically, this works only if the two distributions are\n        independent or if they are jointly normally distributed.\n        \"\"\"\n        return NormalDist(x1._mu - x2, x1._sigma)\n\n    def __mul__(x1: NormalDist, x2: float):\n        \"\"\"\n        Multiply both mu and sigma by a constant.\n        Used for rescaling, perhaps to change measurement units.\n        Sigma is scaled with the absolute value of the constant.\n        \"\"\"\n        return NormalDist(x1._mu * x2, x1._sigma * _fabs(x2))\n\n    def __truediv__(x1: NormalDist, x2: float):\n        \"\"\"\n        Divide both mu and sigma by a constant.\n        Used for rescaling, perhaps to change measurement units.\n        Sigma is scaled with the absolute value of the constant.\n        \"\"\"\n        return NormalDist(x1._mu / x2, x1._sigma / _fabs(x2))\n\n    def __pos__(x1: NormalDist):\n        return NormalDist(x1._mu, x1._sigma)\n\n    def __neg__(x1: NormalDist):\n        return NormalDist(-x1._mu, x1._sigma)\n\n    def __radd__(x1: NormalDist, x2: float):\n        return x1 + x2\n\n    def __rsub__(x1: NormalDist, x2: NormalDist):\n        return -(x1 - x2)\n\n    def __rmul__(x1: NormalDist, x2: float):\n        return x1 * x2\n\n    def __eq__(x1: NormalDist, x2: NormalDist):\n        return x1._mu == x2._mu and x1._sigma == x2._sigma\n\n    def __hash__(self):\n        return hash((self._mu, self._sigma))\n\n    def __repr__(self):\n        return f\"NormalDist(mu={self._mu}, sigma={self._sigma})\"\n"
  },
  {
    "path": "stdlib/string.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nascii_letters = \"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\"\nascii_lowercase = \"abcdefghijklmnopqrstuvwxyz\"\nascii_uppercase = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n\ndigits = \"0123456789\"\nhexdigits = \"0123456789abcdefABCDEF\"\noctdigits = \"01234567\"\n\npunctuation = \"!\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~\"\nprintable = \"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!\\\"#$%&'()*+,-./:;<=>?@[\\\\]^_`{|}~ \\t\\n\\r\\x0b\\x0c\"\nwhitespace = \" \\t\\n\\r\\x0b\\x0c\"\n"
  },
  {
    "path": "stdlib/sys.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\nargv = list(__argv__, len(__argv__))\n\nstdin = File(_C.seq_stdin())\nstdout = File(_C.seq_stdout())\nstderr = File(_C.seq_stderr())\n\ndef exit(status: int = 0):\n    raise SystemExit(status)\n\nmaxsize = 0x7FFFFFFFFFFFFFFF\n"
  },
  {
    "path": "stdlib/threading.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n# Thread-local variable indicator. Used internally by the compiler to mark ThreadLocal variables.\n# Semantically identical to T.\n@tuple\nclass ThreadLocal[T]:\n    def __new__(T: type) -> ThreadLocal[T]:\n        compile_error(\"this type cannot be instantiated\")\n\n@tuple\nclass Lock:\n    p: cobj\n\n    def __new__() -> Lock:\n        return Lock(_C.seq_lock_new(),)\n\n    def acquire(self, block: bool = True, timeout: float = -1.0) -> bool:\n        if timeout >= 0.0 and not block:\n            raise ValueError(\"can't specify a timeout for a non-blocking call\")\n        return _C.seq_lock_acquire(self.p, block, timeout)\n\n    def release(self):\n        _C.seq_lock_release(self.p)\n\n    def __enter__(self):\n        self.acquire()\n\n    def __exit__(self):\n        self.release()\n\n@tuple\nclass RLock:\n    p: cobj\n\n    def __new__() -> RLock:\n        return RLock(_C.seq_rlock_new(),)\n\n    def acquire(self, block: bool = True, timeout: float = -1.0) -> bool:\n        if timeout >= 0.0 and not block:\n            raise ValueError(\"can't specify a timeout for a non-blocking call\")\n        return _C.seq_rlock_acquire(self.p, block, timeout)\n\n    def release(self):\n        _C.seq_rlock_release(self.p)\n\n    def __enter__(self):\n        self.acquire()\n\n    def __exit__(self):\n        self.release()\n\ndef active_count() -> int:\n    from openmp import get_num_threads\n    return get_num_threads()\n\ndef get_native_id() -> int:\n    from openmp import get_thread_num\n    return get_thread_num()\n\ndef get_ident() -> int:\n    return get_native_id() + 1\n"
  },
  {
    "path": "stdlib/time.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\ndef time() -> float:\n    return _C.seq_time() / 1e9\n\ndef time_ns() -> int:\n    return _C.seq_time()\n\ndef monotonic() -> float:\n    return _C.seq_time_monotonic() / 1e9\n\ndef monotonic_ns() -> int:\n    return _C.seq_time_monotonic()\n\ndef perf_counter() -> float:\n    return _C.seq_time_highres() / 1e9\n\ndef perf_counter_ns() -> int:\n    return _C.seq_time_highres()\n\ndef sleep(secs: float):\n    if secs < 0:\n        raise ValueError(\"sleep length must be non-negative\")\n    _C.seq_sleep(secs)\n\nclass TimeInterval:\n    \"\"\"\n    Utility class for timing Codon code\n    \"\"\"\n\n    start: int\n    msg: str\n\n    def __init__(self):\n        self.start = _C.seq_time()\n        self.msg = \"\"\n\n    def __enter__(self):\n        self.start = _C.seq_time()\n\n    def __exit__(self):\n        from sys import stderr\n        print(self.report(self.msg), file=stderr)\n\n    def report(self, msg=\"\", memory=False) -> str:\n        msg = f\"{'Block' if not self.msg else self.msg} took {self.elapsed():.3f} s\"\n        # if memory:\n        # msg = f'{msg} ({_C.memory()} MB)'\n        return msg\n\n    def elapsed(self) -> float:\n        return float(_C.seq_time() - self.start) / 1e9\n\n    def tick(self, msg, memory=False):\n        ret = self.report(msg)\n        self.start = _C.seq_time()\n\ndef timing(msg: str = \"\") -> TimeInterval:\n    \"\"\"\n    Example usage:\n\n    ``` python\n    from time import timing\n    with timing('foo function'):\n        foo()  # prints runtime of foo\n    ```\n    \"\"\"\n    return TimeInterval(0, msg)\n\n@tuple\nclass struct_time:\n    _year: i16\n    _yday: i16\n    _sec: i8\n    _min: i8\n    _hour: i8\n    _mday: i8\n    _mon: i8\n    _wday: i8\n    _isdst: i8\n\n    # (sunday=0) --> (monday=0)\n    def _wday_adjust_monday_start(wday: int) -> int:\n        x = wday - 1\n        if x < 0:\n            x = 6\n        return x\n\n    # (monday=0) --> (sunday=0)\n    def _wday_adjust_sunday_start(wday: int) -> int:\n        x = wday + 1\n        if x > 6:\n            x = 0\n        return x\n\n    def __new__(\n        year: int = 0,\n        mon: int = 0,\n        mday: int = 0,\n        hour: int = 0,\n        min: int = 0,\n        sec: int = 0,\n        wday: int = 0,\n        yday: int = 0,\n        isdst: int = 0,\n    ) -> struct_time:\n        return struct_time(\n            i16(year - 1900),\n            i16(yday - 1),\n            i8(sec),\n            i8(min),\n            i8(hour),\n            i8(mday),\n            i8(mon - 1),\n            i8(struct_time._wday_adjust_sunday_start(wday)),\n            i8(isdst),\n        )\n\n    @property\n    def tm_year(self) -> int:\n        return int(self._year) + 1900\n\n    @property\n    def tm_yday(self) -> int:\n        return int(self._yday) + 1\n\n    @property\n    def tm_sec(self) -> int:\n        return int(self._sec)\n\n    @property\n    def tm_min(self) -> int:\n        return int(self._min)\n\n    @property\n    def tm_hour(self) -> int:\n        return int(self._hour)\n\n    @property\n    def tm_mday(self) -> int:\n        return int(self._mday)\n\n    @property\n    def tm_mon(self) -> int:\n        return int(self._mon) + 1\n\n    @property\n    def tm_wday(self) -> int:\n        return struct_time._wday_adjust_monday_start(int(self._wday))\n\n    @property\n    def tm_isdst(self) -> int:\n        return int(self._isdst)\n\n\ndef localtime(secs: int = -1) -> struct_time:\n    tm = struct_time()\n    worked = _C.seq_localtime(secs, __ptr__(tm).as_byte())\n    if not worked:\n        raise OSError(\"localtime failed\")\n    return tm\n\n\ndef gmtime(secs: int = -1) -> struct_time:\n    tm = struct_time()\n    worked = _C.seq_gmtime(secs, __ptr__(tm).as_byte())\n    if not worked:\n        raise OSError(\"localtime failed\")\n    return tm\n\n\ndef mktime(t) -> int:\n    if isinstance(t, struct_time):\n        return _C.seq_mktime(__ptr__(t).as_byte())\n    else:\n        tm = struct_time(*t)\n        return _C.seq_mktime(__ptr__(tm).as_byte())\n\n# pytime.h funcs\n\n_ROUND_HALF_EVEN = 0\n_ROUND_CEILING = 1\n_ROUND_FLOOR = 2\n_ROUND_UP = 3\n\n_MIN = 0x8000000000000000\n_MAX = 0x7FFFFFFFFFFFFFFF\n\ndef _overflow():\n    raise OverflowError(\"timestamp too large\")\n\ndef _add(t1: int, t2: int) -> int:\n    if t2 > 0 and t1 > _MAX - t2:\n        return _MAX\n    elif t2 < 0 and t1 < _MIN - t2:\n        return _MIN\n    else:\n        return t1 + t2\n\ndef _mul_check_overflow(a: int, b: int) -> bool:\n    if b != 0:\n        # assert b > 0\n        return (a < _MIN // b) or (_MAX // b < a)\n    else:\n        return False\n\ndef _mul(t: int, k: int) -> int:\n    # assert k >= 0\n    if _mul_check_overflow(t, k):\n        return _MAX if t >= 0 else _MIN\n    else:\n        return t * k\n\ndef _muldiv(ticks: int, mul: int, div: int) -> int:\n    intpart = ticks / div\n    ticks %= div\n    remaining = _mul(ticks, mul) // div\n    return _add(_mul(intpart, mul), remaining)\n\ndef _round_half_even(x: float) -> float:\n    from math import fabs\n\n    rounded = x.__round__()\n    if fabs(x - rounded) == 0.5:\n        rounded = 2.0 * (x / 2.0).__round__()\n    return rounded\n\ndef _round(x: float, mode: int) -> float:\n    d = x\n    if mode == _ROUND_HALF_EVEN:\n        d = _round_half_even(d)\n    elif mode == _ROUND_CEILING:\n        d = d.__ceil__()\n    elif mode == _ROUND_FLOOR:\n        d = d.__floor__()\n    elif mode == _ROUND_UP:\n        d = d.__ceil__() if d >= 0 else d.__floor__()\n    return d\n\ndef _double_to_denominator(d: float, idenominator: int, mode: int) -> Tuple[int, int]:\n    from math import modf\n\n    denominator = float(idenominator)\n    floatpart, intpart = modf(d)\n\n    floatpart *= denominator\n    floatpart = _round(floatpart, mode)\n    if floatpart >= denominator:\n        floatpart -= denominator\n        intpart += 1.0\n    elif floatpart < 0.0:\n        floatpart += denominator\n        intpart -= 1.0\n    # assert 0.0 <= floatpart < denominator\n\n    if intpart < _MIN or intpart > _MAX:\n        _overflow()\n\n    sec = int(intpart)\n    numerator = int(floatpart)\n    # assert 0 <= numerator < idenominator\n    return sec, numerator\n\ndef _time_to_timespec(t: float, mode: int) -> Tuple[int, int]:\n    return _double_to_denominator(t, 1000000000, mode)\n\ndef _time_to_timeval(t: float, mode: int) -> Tuple[int, int]:\n    return _double_to_denominator(t, 1000000, mode)\n"
  },
  {
    "path": "stdlib/typing.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n# (empty)\n"
  },
  {
    "path": "stdlib/unittest.codon",
    "content": "# Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n# Simplified version of Python's unittest.TestCase to allow\n# copy/pasting tests directly from CPython's test suite.\n\nclass TestCase:\n    def fail(self, standard_message: str, special_message: str = \"\"):\n        print(\"TEST FAILED:\", special_message if special_message else standard_message)\n\n    def assertTrue(self, obj, msg=\"\"):\n        if not bool(obj):\n            self.fail(f\"expected object to be true: {obj}\", msg)\n\n    def assertFalse(self, obj, msg=\"\"):\n        if bool(obj):\n            self.fail(f\"expected object to be false: {obj}\", msg)\n\n    def assertEqual(self, first, second, msg=\"\"):\n        result = first == second\n        if not result:\n            self.fail(f\"expected equality of:\\n  1: {first}\\n  2: {second}\", msg)\n\n    def assertNotEqual(self, first, second, msg=\"\"):\n        result = first != second\n        if not result:\n            self.fail(f\"expected inequality of:\\n  1: {first}\\n  2: {second}\", msg)\n\n    def assertSequenceEqual(self, seq1, seq2, msg=\"\"):\n        len1 = len(seq1)\n        len2 = len(seq2)\n        if len1 != len2:\n            self.fail(\n                f\"expected equality of sequences (len1={len1}, len2={len2}):\\n  1: {seq1}\\n  2: {seq2}\",\n                msg,\n            )\n\n        for i in range(len1):\n            a, b = seq1[i], seq2[i]\n            if a != b:\n                self.fail(\n                    f\"expected equality of sequences (diff at elem {i}):\\n  1: {seq1}\\n  2: {seq2}\",\n                    msg,\n                )\n\n    def assertIn(self, member, container, msg=\"\"):\n        if member not in container:\n            self.fail(f\"expected {member} to be in {container}\", msg)\n\n    def assertNotIn(self, member, container, msg=\"\"):\n        if member in container:\n            self.fail(f\"expected {member} to not be in {container}\", msg)\n\n    def assertIs(self, expr1, expr2, msg=\"\"):\n        if expr1 is not expr2:\n            self.fail(f\"expected {expr1} to be identical to {expr2}\", msg)\n\n    def assertIsNot(self, expr1, expr2, msg=\"\"):\n        if expr1 is expr2:\n            self.fail(f\"expected {expr1} to not be identical to {expr2}\", msg)\n\n    def assertIsNot(self, expr1, expr2, msg=\"\"):\n        if expr1 is expr2:\n            self.fail(f\"expected {expr1} to not be identical to {expr2}\", msg)\n\n    def assertCountEqual(self, first, second, msg=\"\"):\n        from collections import Counter\n\n        first_seq, second_seq = list(first), list(second)\n\n        first_counter = Counter(first_seq)\n        second_counter = Counter(second_seq)\n\n        if first_counter != second_counter:\n            self.fail(f\"expected equal counts:\\n  1: {first}\\n  2: {second}\", msg)\n\n    def assertLess(self, a, b, msg=\"\"):\n        if not (a < b):\n            self.fail(f\"expected less-than:\\n  1: {a}\\n  2: {b}\", msg)\n\n    def assertLessEqual(self, a, b, msg=\"\"):\n        if not (a <= b):\n            self.fail(f\"expected less-than-or-equal:\\n  1: {a}\\n  2: {b}\", msg)\n\n    def assertGreater(self, a, b, msg=\"\"):\n        if not (a > b):\n            self.fail(f\"expected greater-than:\\n  1: {a}\\n  2: {b}\", msg)\n\n    def assertGreaterEqual(self, a, b, msg=\"\"):\n        if not (a >= b):\n            self.fail(f\"expected greater-than-or-equal:\\n  1: {a}\\n  2: {b}\", msg)\n\n    def assertIsNone(self, obj, msg=\"\"):\n        if obj is not None:\n            self.fail(f\"expected {obj} to be None\", msg)\n\n    def assertIsNotNone(self, obj, msg=\"\"):\n        if obj is None:\n            self.fail(f\"expected {obj} to not be None\", msg)\n\n    def assertRaises(self, exception: type, function, *args, **kwargs):\n        try:\n            function(*args, **kwargs)\n        except exception:\n            return\n        except:\n            pass\n        self.fail(f\"call to function did not raise the given exception\")\n\n    def assertAlmostEqual(\n        self, first, second, places: int = 0, msg=\"\", delta=None\n    ):\n        if first == second:\n            # shortcut\n            return\n        if places <= 0 and delta is None:\n            raise ValueError(\"specify delta or places not both\")\n\n        standard_msg = \"\"\n        diff = abs(first - second)\n        if delta is not None:\n            if diff <= delta:\n                return\n            standard_msg = (\n                f\"{first} != {second} within {delta} delta ({diff} difference)\"\n            )\n        else:\n            if places <= 0:\n                places = 7\n            if round(diff, places) == 0:\n                return\n            standard_msg = (\n                f\"{first} != {second} within {places} places ({diff} difference)\"\n            )\n        self.fail(standard_msg, msg)\n"
  },
  {
    "path": "test/CMakeLists.txt.in",
    "content": "cmake_minimum_required(VERSION 2.8.2)\n\nproject(googletest-download NONE)\n\ninclude(ExternalProject)\nExternalProject_Add(googletest\n  GIT_REPOSITORY    https://github.com/google/googletest.git\n  GIT_TAG           release-1.10.0\n  SOURCE_DIR        \"${CMAKE_CURRENT_BINARY_DIR}/googletest-src\"\n  BINARY_DIR        \"${CMAKE_CURRENT_BINARY_DIR}/googletest-build\"\n  CONFIGURE_COMMAND \"\"\n  BUILD_COMMAND     \"\"\n  INSTALL_COMMAND   \"\"\n  TEST_COMMAND      \"\"\n)\n"
  },
  {
    "path": "test/app/argv.codon",
    "content": "from sys import argv\nprint(','.join(argv[1:]))\n"
  },
  {
    "path": "test/app/build.codon",
    "content": "print('hello')\n"
  },
  {
    "path": "test/app/exit.codon",
    "content": "import sys\nsys.exit(42)\n"
  },
  {
    "path": "test/app/export.codon",
    "content": "a = ['a', 'b', 'c']\n\n@export\ndef foo(n: int):\n    print(''.join(a) * n)\n\n@export\ndef main():\n    a.append('d')\n"
  },
  {
    "path": "test/app/input.codon",
    "content": "print(input(\"input: \"), end=',')\nprint(input(), end=',')\nprint(input(), end=',')\ntry:\n    input()\nexcept EOFError:\n    print('X')\n"
  },
  {
    "path": "test/app/input.txt",
    "content": "aa bb\n\ncc"
  },
  {
    "path": "test/app/test.c",
    "content": "extern void foo(long);\nint main(void) { foo(3); }\n"
  },
  {
    "path": "test/app/test.sh",
    "content": "#!/bin/bash -l\n\nexport arg=$1\nexport testdir=$(dirname $0)\nexport codon=\"$arg/codon\"\n\n# argv test\n[ \"$($codon run \"$testdir/argv.codon\" aa bb cc)\" == \"aa,bb,cc\" ] || exit 1\n\n# build test\n$codon build -release -o \"$arg/test_binary\" \"$testdir/build.codon\"\n[ \"$($arg/test_binary)\" == \"hello\" ] || exit 2\n\n# library test\n$codon build -relocation-model=pic -o \"$arg/libcodon_export_test.so\" \"$testdir/export.codon\"\ngcc \"$testdir/test.c\" -L\"$arg\" -Wl,-rpath,\"$arg\" -lcodon_export_test -o \"$arg/test_binary\"\n[ \"$($arg/test_binary)\" == \"abcabcabc\" ] || exit 3\n\n# exit code test\n$codon run \"$testdir/exit.codon\" || if [[ $? -ne 42 ]]; then exit 4; fi\n\n# input test\n[ \"$($codon run \"$testdir/input.codon\" < \"$testdir/input.txt\")\" == \"input: aa bb,,cc,X\" ] || exit 5\n"
  },
  {
    "path": "test/cir/analyze/dominator.cpp",
    "content": "#include \"test.h\"\n\n#include \"codon/cir/analyze/dataflow/cfg.h\"\n#include \"codon/cir/analyze/dataflow/dominator.h\"\n\nusing namespace codon::ir;\n\nTEST_F(CIRCoreTest, DominatorAnalysisSimple) {\n  auto *f = module->Nr<BodiedFunc>(\"test_f\");\n  auto *b = module->Nr<SeriesFlow>();\n  f->setBody(b);\n\n  auto *v = module->Nr<Var>(module->getIntType());\n  f->push_back(v);\n\n  auto *start = module->getBool(false);\n  auto *end = module->getBool(false);\n\n  b->push_back(start);\n  b->push_back(end);\n\n  auto c = analyze::dataflow::buildCFGraph(f);\n  analyze::dataflow::DominatorInspector dom(c.get());\n  dom.analyze();\n\n  ASSERT_TRUE(dom.isDominated(end, start));\n  ASSERT_FALSE(dom.isDominated(start, end));\n}\n\nTEST_F(CIRCoreTest, DominatorAnalysisTernary) {\n  auto *f = module->Nr<BodiedFunc>(\"test_f\");\n  auto *b = module->Nr<SeriesFlow>();\n  f->setBody(b);\n\n  auto *v = module->Nr<Var>(module->getIntType());\n  f->push_back(v);\n\n  auto *start = module->getBool(false);\n  auto *middle = module->getBool(false);\n  auto *end =\n      module->Nr<TernaryInstr>(module->getBool(true), middle, module->getBool(true));\n\n  b->push_back(start);\n  b->push_back(end);\n\n  auto c = analyze::dataflow::buildCFGraph(f);\n  analyze::dataflow::DominatorInspector dom(c.get());\n  dom.analyze();\n\n  ASSERT_TRUE(dom.isDominated(end, start));\n  ASSERT_TRUE(dom.isDominated(middle, start));\n  ASSERT_FALSE(dom.isDominated(start, end));\n  ASSERT_FALSE(dom.isDominated(end, middle));\n}\n"
  },
  {
    "path": "test/cir/analyze/reaching.cpp",
    "content": "#include \"test.h\"\n\n#include \"codon/cir/analyze/dataflow/cfg.h\"\n#include \"codon/cir/analyze/dataflow/reaching.h\"\n\nusing namespace codon::ir;\n\nnamespace {\nstd::unordered_set<codon::ir::id_t> rdset(analyze::dataflow::RDInspector &rd, Var *var,\n                                          Value *loc) {\n  auto defs = rd.getReachingDefinitions(var, loc);\n  std::unordered_set<codon::ir::id_t> set;\n  for (auto &def : defs) {\n    if (def.known())\n      set.insert(def.assignee->getId());\n  }\n  return set;\n}\n} // namespace\n\nTEST_F(CIRCoreTest, RDAnalysisSimple) {\n  auto *f = module->Nr<BodiedFunc>(\"test_f\");\n  auto *b = module->Nr<SeriesFlow>();\n  f->setBody(b);\n\n  auto *v = module->Nr<Var>(module->getIntType());\n  f->push_back(v);\n\n  auto *first = module->getInt(1);\n  auto *second = module->getInt(2);\n\n  auto *start = module->getBool(false);\n  auto *firstAssign = module->Nr<AssignInstr>(v, first);\n  auto *secondAssign = module->Nr<AssignInstr>(v, second);\n  auto *end = module->getBool(false);\n\n  b->push_back(start);\n  b->push_back(firstAssign);\n  b->push_back(secondAssign);\n  b->push_back(end);\n\n  auto c = analyze::dataflow::buildCFGraph(f);\n  analyze::dataflow::RDInspector rd(c.get());\n  rd.analyze();\n\n  auto startRd = rdset(rd, v, start);\n  auto firstRd = rdset(rd, v, firstAssign);\n  auto secondRd = rdset(rd, v, secondAssign);\n  auto endRd = rdset(rd, v, end);\n  auto firstRhsRd = rdset(rd, v, first);\n  auto secondRhsRd = rdset(rd, v, second);\n\n  ASSERT_EQ(0, startRd.size());\n  ASSERT_EQ(1, firstRd.size());\n  ASSERT_EQ(1, secondRd.size());\n  ASSERT_EQ(1, endRd.size());\n  ASSERT_EQ(0, firstRhsRd.size());\n  ASSERT_EQ(1, secondRhsRd.size());\n\n  ASSERT_EQ(first->getId(), *firstRd.begin());\n  ASSERT_EQ(second->getId(), *secondRd.begin());\n  ASSERT_EQ(second->getId(), *endRd.begin());\n}\n\nTEST_F(CIRCoreTest, RDAnalysisIfConditional) {\n  auto *f = module->Nr<BodiedFunc>(\"test_f\");\n\n  auto *trueBranch = module->Nr<SeriesFlow>();\n  auto *falseBranch = module->Nr<SeriesFlow>();\n  auto *ifFlow = module->Nr<IfFlow>(module->getBool(false), trueBranch, falseBranch);\n  auto *b = module->Nr<SeriesFlow>();\n  f->setBody(b);\n\n  auto *v = module->Nr<Var>(module->getIntType());\n  f->push_back(v);\n\n  auto *first = module->getInt(1);\n  auto *second = module->getInt(2);\n\n  auto *start = module->getBool(false);\n  auto *firstAssign = module->Nr<AssignInstr>(v, first);\n  auto *secondAssign = module->Nr<AssignInstr>(v, second);\n  auto *end = module->getBool(false);\n\n  b->push_back(start);\n  b->push_back(ifFlow);\n  trueBranch->push_back(firstAssign);\n  falseBranch->push_back(secondAssign);\n  b->push_back(end);\n\n  auto c = analyze::dataflow::buildCFGraph(f);\n  analyze::dataflow::RDInspector rd(c.get());\n  rd.analyze();\n\n  auto startRd = rdset(rd, v, start);\n  auto endRd = rdset(rd, v, end);\n\n  ASSERT_EQ(0, startRd.size());\n  ASSERT_EQ(2, endRd.size());\n  ASSERT_TRUE(endRd.find(first->getId()) != endRd.end());\n  ASSERT_TRUE(endRd.find(second->getId()) != endRd.end());\n}\n\nTEST_F(CIRCoreTest, RDAnalysisTryCatch) {\n  auto *f = module->Nr<BodiedFunc>(\"test_f\");\n\n  auto *body = module->Nr<SeriesFlow>();\n  auto *except = module->Nr<SeriesFlow>();\n  auto *finally = module->Nr<SeriesFlow>();\n  auto *tc = module->Nr<TryCatchFlow>(body, finally);\n  tc->emplace_back(except, nullptr);\n  auto *b = module->Nr<SeriesFlow>();\n  f->setBody(b);\n\n  auto *v = module->Nr<Var>(module->getIntType());\n  f->push_back(v);\n\n  auto *first = module->getInt(1);\n  auto *second = module->getInt(2);\n  auto *third = module->getInt(3);\n\n  auto *initial = module->getInt(0);\n  auto *zeroAssign = module->Nr<AssignInstr>(v, initial);\n  auto *start = module->getBool(false);\n  auto *firstAssign = module->Nr<AssignInstr>(v, first);\n  auto *secondAssign = module->Nr<AssignInstr>(v, second);\n  auto *middle = module->getBool(false);\n  auto *thirdAssign = module->Nr<AssignInstr>(v, third);\n  auto *end = module->getBool(false);\n\n  b->push_back(start);\n  b->push_back(zeroAssign);\n  b->push_back(tc);\n  body->push_back(firstAssign);\n  except->push_back(thirdAssign);\n  finally->push_back(middle);\n  finally->push_back(secondAssign);\n  b->push_back(end);\n\n  auto c = analyze::dataflow::buildCFGraph(f);\n  analyze::dataflow::RDInspector rd(c.get());\n  rd.analyze();\n\n  auto startRd = rdset(rd, v, start);\n  auto middleRd = rdset(rd, v, middle);\n  auto endRd = rdset(rd, v, end);\n\n  ASSERT_EQ(0, startRd.size());\n  ASSERT_EQ(1, endRd.size());\n  ASSERT_TRUE(endRd.find(second->getId()) != endRd.end());\n  ASSERT_TRUE(middleRd.find(first->getId()) != endRd.end());\n  ASSERT_TRUE(middleRd.find(third->getId()) != endRd.end());\n  ASSERT_TRUE(middleRd.find(initial->getId()) != endRd.end());\n}\n\nTEST_F(CIRCoreTest, RDAnalysisWhileLoop) {\n  auto *f = module->Nr<BodiedFunc>(\"test_f\");\n\n  auto *loopBody = module->Nr<SeriesFlow>();\n  auto *whileFlow = module->Nr<WhileFlow>(module->getBool(false), loopBody);\n  auto *b = module->Nr<SeriesFlow>();\n  f->setBody(b);\n\n  auto *v = module->Nr<Var>(module->getIntType());\n  f->push_back(v);\n\n  auto *first = module->getInt(1);\n  auto *second = module->getInt(2);\n\n  auto *start = module->getBool(false);\n  auto *firstAssign = module->Nr<AssignInstr>(v, first);\n  auto *secondAssign = module->Nr<AssignInstr>(v, second);\n  auto *end = module->getBool(false);\n\n  b->push_back(start);\n  b->push_back(firstAssign);\n  b->push_back(whileFlow);\n  loopBody->push_back(secondAssign);\n  b->push_back(end);\n\n  auto c = analyze::dataflow::buildCFGraph(f);\n  analyze::dataflow::RDInspector rd(c.get());\n  rd.analyze();\n\n  auto startRd = rdset(rd, v, start);\n  auto endRd = rdset(rd, v, end);\n\n  ASSERT_EQ(0, startRd.size());\n  ASSERT_EQ(2, endRd.size());\n  ASSERT_TRUE(endRd.find(first->getId()) != endRd.end());\n  ASSERT_TRUE(endRd.find(second->getId()) != endRd.end());\n}\n"
  },
  {
    "path": "test/cir/base.cpp",
    "content": "#include \"test.h\"\n\n#include <algorithm>\n\nnamespace {\nclass TestVisitor : public codon::ir::util::Visitor {\npublic:\n  void visit(codon::ir::IntConst *) override { FAIL(); }\n  void visit(codon::ir::BoolConst *) override {}\n};\n\nclass ConstTestVisitor : public codon::ir::util::ConstVisitor {\npublic:\n  void visit(const codon::ir::IntConst *) override { FAIL(); }\n  void visit(const codon::ir::BoolConst *) override {}\n};\n\n} // namespace\n\nusing namespace codon::ir;\n\nTEST_F(CIRCoreTest, NodeNoReplacementRTTI) {\n  auto *derived = module->Nr<IntConst>(1, module->getIntType());\n  ASSERT_TRUE(derived);\n  ASSERT_FALSE(derived->hasReplacement());\n  auto *base = cast<Value>(derived);\n  ASSERT_TRUE(base);\n  ASSERT_TRUE(isA<IntConst>(base));\n  ASSERT_TRUE(isA<Const>(base));\n  ASSERT_TRUE(isA<Value>(base));\n  ASSERT_FALSE(isA<Flow>(base));\n\n  const auto *constBase = base;\n  ASSERT_TRUE(isA<Const>(constBase));\n  ASSERT_TRUE(cast<Const>(constBase));\n}\n\nTEST_F(CIRCoreTest, NodeNoReplacementAttributes) {\n  auto *node = module->Nr<IntConst>(1, module->getIntType());\n  ASSERT_FALSE(node->hasReplacement());\n  ASSERT_FALSE(node->hasAttribute<KeyValueAttribute>());\n\n  ASSERT_TRUE(node->hasAttribute<SrcInfoAttribute>());\n  ASSERT_TRUE(node->getAttribute<SrcInfoAttribute>());\n  ASSERT_EQ(1, std::distance(node->attributes_begin(), node->attributes_end()));\n}\n\nTEST_F(CIRCoreTest, NodeReplacementRTTI) {\n  Value *node = module->Nr<IntConst>(1, module->getIntType());\n  ASSERT_TRUE(node);\n  ASSERT_FALSE(node->hasReplacement());\n  ASSERT_TRUE(isA<IntConst>(node));\n\n  node->replaceAll(module->Nr<BoolConst>(false, module->getBoolType()));\n  ASSERT_TRUE(node->hasReplacement());\n  ASSERT_FALSE(isA<IntConst>(node));\n  ASSERT_TRUE(isA<BoolConst>(node));\n  ASSERT_TRUE(cast<BoolConst>(node));\n}\n\nTEST_F(CIRCoreTest, NodeReplacementDelegates) {\n  auto NODE_NAME = \"foo\";\n\n  Value *originalNode = module->Nr<IntConst>(1, module->getIntType());\n  Value *newNode = module->Nr<BoolConst>(false, module->getBoolType(), NODE_NAME);\n  newNode->setAttribute(std::make_unique<KeyValueAttribute>());\n\n  ASSERT_EQ(0, originalNode->getName().size());\n  ASSERT_EQ(1, std::distance(originalNode->attributes_begin(),\n                             originalNode->attributes_end()));\n\n  originalNode->replaceAll(newNode);\n  ASSERT_EQ(NODE_NAME, originalNode->getName());\n  ASSERT_EQ(2, std::distance(originalNode->attributes_begin(),\n                             originalNode->attributes_end()));\n\n  TestVisitor v;\n  originalNode->accept(v);\n  newNode->accept(v);\n\n  ConstTestVisitor v2;\n  originalNode->accept(v2);\n  newNode->accept(v2);\n}\n\nTEST_F(CIRCoreTest, NodeNonReplaceableFails) {\n  Value *originalNode = module->Nr<IntConst>(1, module->getIntType());\n  originalNode->setReplaceable(false);\n  ASSERT_DEATH(originalNode->replaceAll(originalNode), \"\");\n}\n"
  },
  {
    "path": "test/cir/constant.cpp",
    "content": "#include \"test.h\"\n\n#include <fmt/format.h>\n#include <sstream>\n\nusing namespace codon::ir;\n\nTEST_F(CIRCoreTest, ConstTypeQueryAndReplace) {\n  auto *node = module->Nr<IntConst>(1, module->getIntType());\n  ASSERT_EQ(module->getIntType(), node->getType());\n\n  auto usedTypes = node->getUsedTypes();\n  ASSERT_EQ(1, usedTypes.size());\n  ASSERT_EQ(module->getIntType(), usedTypes[0]);\n  ASSERT_EQ(1, node->replaceUsedType(module->getIntType(), module->getIntType()));\n}\n\nTEST_F(CIRCoreTest, ConstCloning) {\n  auto VALUE = 1;\n  auto *node = module->Nr<IntConst>(VALUE, module->getIntType());\n  auto *clone = cast<IntConst>(cv->clone(node));\n\n  ASSERT_TRUE(clone);\n  ASSERT_EQ(VALUE, clone->getVal());\n  ASSERT_EQ(module->getIntType(), clone->getType());\n}\n"
  },
  {
    "path": "test/cir/flow.cpp",
    "content": "#include \"test.h\"\n\n#include <algorithm>\n#include <vector>\n\n#include \"codon/cir/util/matching.h\"\n\nusing namespace codon::ir;\n\nTEST_F(CIRCoreTest, SeriesFlowInsertionEraseAndIterators) {\n  auto FIRST_VALUE = 2;\n  auto SECOND_VALUE = 1;\n\n  auto *f = module->Nr<SeriesFlow>();\n  f->push_back(module->Nr<IntConst>(SECOND_VALUE, module->getIntType()));\n  f->insert(f->begin(), module->Nr<IntConst>(FIRST_VALUE, module->getIntType()));\n\n  std::vector<Value *> contents(f->begin(), f->end());\n\n  ASSERT_EQ(2, contents.size());\n  ASSERT_EQ(FIRST_VALUE, cast<IntConst>(contents[0])->getVal());\n  ASSERT_EQ(SECOND_VALUE, cast<IntConst>(contents[1])->getVal());\n\n  f->erase(f->begin());\n  ASSERT_EQ(SECOND_VALUE, cast<IntConst>(*f->begin())->getVal());\n}\n\nTEST_F(CIRCoreTest, SeriesFlowQueryAndReplace) {\n  auto FIRST_VALUE = 2;\n  auto SECOND_VALUE = 1;\n\n  auto *f = module->Nr<SeriesFlow>();\n  f->push_back(module->Nr<IntConst>(SECOND_VALUE, module->getIntType()));\n  f->insert(f->begin(), module->Nr<IntConst>(FIRST_VALUE, module->getIntType()));\n\n  std::vector<Value *> contents(f->begin(), f->end());\n  auto used = f->getUsedValues();\n\n  ASSERT_TRUE(std::equal(used.begin(), used.end(), contents.begin(), contents.end()));\n  ASSERT_EQ(0, f->getUsedTypes().size());\n  ASSERT_EQ(0, f->getUsedVariables().size());\n\n  ASSERT_EQ(1, f->replaceUsedValue(contents[0], contents[1]));\n}\n\nTEST_F(CIRCoreTest, SeriesFlowCloning) {\n  auto FIRST_VALUE = 2;\n  auto SECOND_VALUE = 1;\n\n  auto *f = module->Nr<SeriesFlow>();\n  f->push_back(module->Nr<IntConst>(SECOND_VALUE, module->getIntType()));\n  f->insert(f->begin(), module->Nr<IntConst>(FIRST_VALUE, module->getIntType()));\n\n  ASSERT_TRUE(util::match(f, cv->clone(f)));\n}\n\nTEST_F(CIRCoreTest, WhileFlowQueryAndReplace) {\n  auto *cond = module->Nr<BoolConst>(true, module->getBoolType());\n  auto *body = module->Nr<SeriesFlow>();\n  auto *f = module->Nr<WhileFlow>(cond, body);\n\n  ASSERT_EQ(cond, f->getCond());\n  ASSERT_EQ(body, f->getBody());\n\n  std::vector<Value *> usedValues = {cond, body};\n  auto queried = f->getUsedValues();\n  ASSERT_TRUE(\n      std::equal(usedValues.begin(), usedValues.end(), queried.begin(), queried.end()));\n  ASSERT_EQ(0, f->getUsedTypes().size());\n  ASSERT_EQ(0, f->getUsedVariables().size());\n\n  ASSERT_EQ(\n      1, f->replaceUsedValue(cond, module->Nr<BoolConst>(true, module->getBoolType())));\n  ASSERT_EQ(1, f->replaceUsedValue(body, module->Nr<SeriesFlow>()));\n  ASSERT_DEATH(f->replaceUsedValue(f->getBody(),\n                                   module->Nr<BoolConst>(true, module->getBoolType())),\n               \"\");\n  queried = f->getUsedValues();\n  ASSERT_FALSE(\n      std::equal(usedValues.begin(), usedValues.end(), queried.begin(), queried.end()));\n}\n\nTEST_F(CIRCoreTest, WhileFlowCloning) {\n  auto *cond = module->Nr<BoolConst>(true, module->getBoolType());\n  auto *body = module->Nr<SeriesFlow>();\n  auto *f = module->Nr<WhileFlow>(cond, body);\n  ASSERT_TRUE(util::match(f, cv->clone(f)));\n}\n\nTEST_F(CIRCoreTest, ForFlowQueryAndReplace) {\n  auto *iter = module->Nr<StringConst>(\"hi\", module->getStringType());\n  auto *body = module->Nr<SeriesFlow>();\n  auto *var = module->Nr<Var>(module->getStringType(), false, \"x\");\n  auto *f = module->Nr<ForFlow>(iter, body, var);\n\n  ASSERT_EQ(iter, f->getIter());\n  ASSERT_EQ(body, f->getBody());\n  ASSERT_EQ(var, f->getVar());\n\n  std::vector<Value *> usedValues = {iter, body};\n  auto qVal = f->getUsedValues();\n  ASSERT_TRUE(\n      std::equal(usedValues.begin(), usedValues.end(), qVal.begin(), qVal.end()));\n  ASSERT_EQ(0, f->getUsedTypes().size());\n\n  std::vector<Var *> usedVars = {var};\n  auto qVar = f->getUsedVariables();\n  ASSERT_TRUE(std::equal(usedVars.begin(), usedVars.end(), qVar.begin(), qVar.end()));\n\n  ASSERT_EQ(1, f->replaceUsedValue(\n                   iter, module->Nr<StringConst>(\"hi\", module->getStringType())));\n  ASSERT_EQ(1, f->replaceUsedValue(body, module->Nr<SeriesFlow>()));\n  qVal = f->getUsedValues();\n  ASSERT_FALSE(\n      std::equal(usedValues.begin(), usedValues.end(), qVal.begin(), qVal.end()));\n\n  ASSERT_EQ(1, f->replaceUsedVariable(\n                   var, module->Nr<Var>(module->getStringType(), false, \"x\")));\n  ASSERT_NE(var, f->getVar());\n}\n\nTEST_F(CIRCoreTest, ForFlowCloning) {\n  auto *iter = module->Nr<StringConst>(\"hi\", module->getStringType());\n  auto *body = module->Nr<SeriesFlow>();\n  auto *var = module->Nr<Var>(module->getStringType(), false, \"x\");\n  auto *f = module->Nr<ForFlow>(iter, body, var);\n\n  ASSERT_TRUE(util::match(f, cv->clone(f)));\n}\n\nTEST_F(CIRCoreTest, IfFlowQueryAndReplace) {\n  auto *cond = module->Nr<BoolConst>(true, module->getBoolType());\n  auto *tBody = module->Nr<SeriesFlow>();\n  auto *fBody = module->Nr<SeriesFlow>();\n  auto *f = module->Nr<IfFlow>(cond, tBody, fBody);\n\n  ASSERT_EQ(cond, f->getCond());\n  ASSERT_EQ(tBody, f->getTrueBranch());\n  ASSERT_EQ(fBody, f->getFalseBranch());\n\n  std::vector<Value *> usedValues = {cond, tBody, fBody};\n  auto qVal = f->getUsedValues();\n  ASSERT_TRUE(\n      std::equal(usedValues.begin(), usedValues.end(), qVal.begin(), qVal.end()));\n  ASSERT_EQ(0, f->getUsedTypes().size());\n  ASSERT_EQ(0, f->getUsedVariables().size());\n\n  usedValues.pop_back();\n  f->setFalseBranch(nullptr);\n  qVal = f->getUsedValues();\n  ASSERT_TRUE(\n      std::equal(usedValues.begin(), usedValues.end(), qVal.begin(), qVal.end()));\n  f->setFalseBranch(fBody);\n\n  ASSERT_EQ(\n      1, f->replaceUsedValue(cond, module->Nr<BoolConst>(true, module->getBoolType())));\n  ASSERT_EQ(1, f->replaceUsedValue(tBody, module->Nr<SeriesFlow>()));\n  ASSERT_EQ(1, f->replaceUsedValue(fBody, module->Nr<SeriesFlow>()));\n\n  ASSERT_DEATH(f->replaceUsedValue(f->getTrueBranch(),\n                                   module->Nr<BoolConst>(true, module->getBoolType())),\n               \"\");\n  ASSERT_DEATH(f->replaceUsedValue(f->getFalseBranch(),\n                                   module->Nr<BoolConst>(true, module->getBoolType())),\n               \"\");\n\n  qVal = f->getUsedValues();\n  ASSERT_FALSE(\n      std::equal(usedValues.begin(), usedValues.end(), qVal.begin(), qVal.end()));\n}\n\nTEST_F(CIRCoreTest, IfFlowCloning) {\n  auto *cond = module->Nr<BoolConst>(true, module->getBoolType());\n  auto *tBody = module->Nr<SeriesFlow>();\n  auto *fBody = module->Nr<SeriesFlow>();\n  auto *f = module->Nr<IfFlow>(cond, tBody, fBody);\n\n  ASSERT_TRUE(util::match(f, cv->clone(f)));\n}\n\nTEST_F(CIRCoreTest, TryCatchFlowSingleCatchQueryAndReplace) {\n  auto *body = module->Nr<SeriesFlow>();\n  auto *finally = module->Nr<SeriesFlow>();\n  auto *f = module->Nr<TryCatchFlow>(body, finally);\n  auto *handler = module->Nr<SeriesFlow>();\n  auto *var = module->Nr<Var>(module->getIntType());\n\n  f->emplace_back(handler, module->getIntType(), var);\n\n  ASSERT_EQ(1, f->replaceUsedVariable(var, module->Nr<Var>(module->getFloatType())));\n  ASSERT_EQ(1, f->replaceUsedType(module->getIntType(), module->getFloatType()));\n  ASSERT_EQ(1, f->replaceUsedValue(handler, module->Nr<SeriesFlow>()));\n}\n"
  },
  {
    "path": "test/cir/func.cpp",
    "content": "#include \"test.h\"\n\n#include <algorithm>\n\n#include \"codon/cir/util/matching.h\"\n\nusing namespace codon::ir;\n\nTEST_F(CIRCoreTest, FuncRealizationAndVarInsertionEraseAndIterators) {\n  auto *fn = module->Nr<BodiedFunc>();\n  fn->realize(module->unsafeGetDummyFuncType(), {});\n\n  auto *fnType = module->unsafeGetFuncType(\"**test_type**\", module->getIntType(),\n                                           {module->getIntType()});\n  std::vector<std::string> names = {\"foo\"};\n  fn->realize(cast<types::FuncType>(fnType), names);\n  ASSERT_TRUE(fn->isGlobal());\n\n  ASSERT_EQ(1, std::distance(fn->arg_begin(), fn->arg_end()));\n  ASSERT_EQ(module->getIntType(), fn->arg_front()->getType());\n\n  auto *var = module->Nr<Var>(module->getIntType(), false, \"hi\");\n  fn->push_back(var);\n  ASSERT_EQ(1, std::distance(fn->begin(), fn->end()));\n  fn->erase(fn->begin());\n  ASSERT_EQ(0, std::distance(fn->begin(), fn->end()));\n  fn->insert(fn->begin(), var);\n  ASSERT_EQ(1, std::distance(fn->begin(), fn->end()));\n  ASSERT_EQ(module->getIntType(), fn->front()->getType());\n}\n\nTEST_F(CIRCoreTest, BodiedFuncQueryAndReplace) {\n  auto *fn = module->Nr<BodiedFunc>();\n  fn->realize(module->unsafeGetDummyFuncType(), {});\n  fn->setJIT();\n  ASSERT_TRUE(fn->isJIT());\n\n  auto *body = fn->getBody();\n  ASSERT_FALSE(body);\n  ASSERT_EQ(0, fn->getUsedValues().size());\n\n  body = module->Nr<SeriesFlow>();\n  fn->setBody(body);\n  ASSERT_EQ(body, fn->getBody());\n\n  auto used = fn->getUsedValues();\n  ASSERT_EQ(1, used.size());\n  ASSERT_EQ(body, used[0]);\n\n  ASSERT_EQ(1, fn->replaceUsedValue(body, module->Nr<SeriesFlow>()));\n  ASSERT_DEATH(fn->replaceUsedValue(fn->getBody(), module->Nr<VarValue>(nullptr)), \"\");\n  ASSERT_NE(fn->getBody(), body);\n}\n\nTEST_F(CIRCoreTest, BodiedFuncUnmangledName) {\n  auto *fn = module->Nr<BodiedFunc>(\"Int.foo\");\n  fn->setUnmangledName(\"foo\");\n  fn->realize(module->unsafeGetDummyFuncType(), {});\n  ASSERT_EQ(\"foo\", fn->getUnmangledName());\n}\n\nTEST_F(CIRCoreTest, BodiedFuncCloning) {\n  auto *fn = module->Nr<BodiedFunc>(\"fn\");\n  fn->realize(module->unsafeGetDummyFuncType(), {});\n\n  fn->setJIT();\n  fn->setBody(module->Nr<SeriesFlow>());\n  ASSERT_TRUE(util::match(fn, cv->clone(fn)));\n}\n\nTEST_F(CIRCoreTest, ExternalFuncUnmangledNameAndCloning) {\n  auto *fn = module->Nr<ExternalFunc>(\"fn\");\n  fn->realize(module->unsafeGetDummyFuncType(), {});\n\n  fn->setUnmangledName(\"foo\");\n  ASSERT_EQ(\"foo\", fn->getUnmangledName());\n  ASSERT_TRUE(util::match(fn, cv->clone(fn)));\n}\n\nTEST_F(CIRCoreTest, InternalFuncParentTypeUnmangledNameAndCloning) {\n  auto *fn = module->Nr<InternalFunc>(\"fn.1\");\n  fn->setUnmangledName(\"fn\");\n  fn->realize(module->unsafeGetDummyFuncType(), {});\n\n  fn->setParentType(module->getIntType());\n  ASSERT_EQ(\"fn\", fn->getUnmangledName());\n  ASSERT_EQ(fn->getParentType(), module->getIntType());\n  ASSERT_TRUE(util::match(fn, cv->clone(fn)));\n}\n\nTEST_F(CIRCoreTest, LLVMFuncUnmangledNameQueryAndReplace) {\n  auto *fn = module->Nr<LLVMFunc>(\"fn\");\n  fn->realize(module->unsafeGetDummyFuncType(), {});\n\n  fn->setLLVMBody(\"body\");\n  fn->setLLVMDeclarations(\"decl\");\n\n  std::vector<types::Generic> literals = {types::Generic(1),\n                                          types::Generic(module->getIntType())};\n  fn->setLLVMLiterals(literals);\n\n  ASSERT_EQ(\"body\", fn->getLLVMBody());\n  ASSERT_EQ(\"decl\", fn->getLLVMDeclarations());\n\n  ASSERT_EQ(1, fn->replaceUsedType(module->getIntType(), module->getFloatType()));\n  ASSERT_EQ(module->getFloatType(), fn->literal_back().getTypeValue());\n}\n"
  },
  {
    "path": "test/cir/instr.cpp",
    "content": "#include \"test.h\"\n\n#include \"codon/cir/util/matching.h\"\n\nusing namespace codon::ir;\n\nTEST_F(CIRCoreTest, AssignInstrQueryAndReplace) {\n  auto *var = module->Nr<Var>(module->getIntType());\n  auto *val = module->Nr<IntConst>(1, module->getIntType());\n  auto *instr = module->Nr<AssignInstr>(var, val);\n\n  ASSERT_EQ(var, instr->getLhs());\n  ASSERT_EQ(val, instr->getRhs());\n\n  auto usedVals = instr->getUsedValues();\n  ASSERT_EQ(1, usedVals.size());\n  ASSERT_EQ(val, usedVals[0]);\n\n  auto usedVars = instr->getUsedVariables();\n  ASSERT_EQ(1, usedVars.size());\n  ASSERT_EQ(var, usedVars[0]);\n\n  ASSERT_EQ(\n      1, instr->replaceUsedValue(val, module->Nr<IntConst>(1, module->getIntType())));\n  ASSERT_EQ(1, instr->replaceUsedVariable(var, module->Nr<Var>(module->getIntType())));\n}\n\nTEST_F(CIRCoreTest, AssignInstrCloning) {\n  auto *var = module->Nr<Var>(module->getIntType());\n  auto *val = module->Nr<IntConst>(1, module->getIntType());\n  auto *instr = module->Nr<AssignInstr>(var, val);\n\n  ASSERT_TRUE(util::match(instr, cv->clone(instr)));\n}\n\nTEST_F(CIRCoreTest, ExtractInstrQueryAndReplace) {\n  auto FIELD = \"foo\";\n  auto *type = cast<types::RecordType>(module->unsafeGetMemberedType(\"**internal**\"));\n  type->realize({module->getIntType()}, {FIELD});\n  auto *var = module->Nr<Var>(type);\n  auto *val = module->Nr<VarValue>(var);\n  auto *instr = module->Nr<ExtractInstr>(val, FIELD);\n\n  ASSERT_EQ(FIELD, instr->getField());\n  ASSERT_EQ(val, instr->getVal());\n  ASSERT_EQ(type->getMemberType(FIELD), instr->getType());\n\n  auto usedVals = instr->getUsedValues();\n  ASSERT_EQ(1, usedVals.size());\n  ASSERT_EQ(val, usedVals[0]);\n\n  ASSERT_EQ(1, instr->replaceUsedValue(val, module->Nr<VarValue>(var)));\n}\n\nTEST_F(CIRCoreTest, ExtractInstrCloning) {\n  auto FIELD = \"foo\";\n  auto *type = cast<types::RecordType>(module->unsafeGetMemberedType(\"**internal**\"));\n  type->realize({module->getIntType()}, {FIELD});\n  auto *var = module->Nr<Var>(type);\n  auto *val = module->Nr<VarValue>(var);\n  auto *instr = module->Nr<ExtractInstr>(val, FIELD);\n\n  ASSERT_TRUE(util::match(instr, cv->clone(instr)));\n}\n\nTEST_F(CIRCoreTest, InsertInstrQueryAndReplace) {\n  auto FIELD = \"foo\";\n  auto *type =\n      cast<types::RefType>(module->unsafeGetMemberedType(\"**internal**\", true));\n  type->realize({module->getIntType()}, {FIELD});\n  auto *var = module->Nr<Var>(type);\n  auto *lhs = module->Nr<VarValue>(var);\n  auto *rhs = module->Nr<IntConst>(1, module->getIntType());\n  auto *instr = module->Nr<InsertInstr>(lhs, FIELD, rhs);\n\n  ASSERT_EQ(type, instr->getType());\n\n  auto usedVals = instr->getUsedValues();\n  ASSERT_EQ(2, usedVals.size());\n\n  ASSERT_EQ(1, instr->replaceUsedValue(lhs, module->Nr<VarValue>(var)));\n  ASSERT_EQ(\n      1, instr->replaceUsedValue(rhs, module->Nr<IntConst>(1, module->getIntType())));\n}\n\nTEST_F(CIRCoreTest, InsertInstrCloning) {\n  auto FIELD = \"foo\";\n  auto *type =\n      cast<types::RefType>(module->unsafeGetMemberedType(\"**internal**\", true));\n  type->realize({module->getIntType()}, {FIELD});\n  auto *var = module->Nr<Var>(type);\n  auto *lhs = module->Nr<VarValue>(var);\n  auto *rhs = module->Nr<IntConst>(1, module->getIntType());\n  auto *instr = module->Nr<InsertInstr>(lhs, FIELD, rhs);\n\n  ASSERT_TRUE(util::match(instr, cv->clone(instr)));\n}\n\nTEST_F(CIRCoreTest, CallInstrQueryAndReplace) {\n  auto *type = cast<types::FuncType>(module->unsafeGetDummyFuncType());\n  auto *func = module->Nr<BodiedFunc>();\n  func->realize(type, {});\n  auto *funcVal = module->Nr<VarValue>(func);\n  auto *instr = module->Nr<CallInstr>(funcVal);\n\n  ASSERT_EQ(funcVal, instr->getCallee());\n  ASSERT_EQ(type->getReturnType(), instr->getType());\n\n  auto usedVals = instr->getUsedValues();\n  ASSERT_EQ(1, usedVals.size());\n  ASSERT_EQ(1, instr->replaceUsedValue(funcVal, module->Nr<VarValue>(func)));\n}\n\nTEST_F(CIRCoreTest, CallInstrCloning) {\n  auto *type = module->unsafeGetDummyFuncType();\n  auto *func = module->Nr<BodiedFunc>();\n  func->realize(type, {});\n  auto *funcVal = module->Nr<VarValue>(func);\n  auto *instr = module->Nr<CallInstr>(funcVal);\n\n  ASSERT_TRUE(util::match(instr, cv->clone(instr)));\n}\n\nTEST_F(CIRCoreTest, StackAllocInstrQueryAndReplace) {\n  auto COUNT = 1;\n\n  auto *arrayType = module->unsafeGetArrayType(module->getIntType());\n  auto *instr = module->Nr<StackAllocInstr>(arrayType, COUNT);\n\n  ASSERT_EQ(COUNT, instr->getCount());\n  ASSERT_EQ(arrayType, instr->getType());\n\n  auto usedTypes = instr->getUsedTypes();\n  ASSERT_EQ(1, usedTypes.size());\n  ASSERT_EQ(arrayType, usedTypes[0]);\n\n  ASSERT_EQ(1, instr->replaceUsedType(\n                   arrayType, module->unsafeGetArrayType(module->getFloatType())));\n}\n\nTEST_F(CIRCoreTest, StackAllocInstrCloning) {\n  auto COUNT = 1;\n  auto *arrayType = module->unsafeGetArrayType(module->getIntType());\n  auto *instr = module->Nr<StackAllocInstr>(arrayType, COUNT);\n  ASSERT_TRUE(util::match(instr, cv->clone(instr)));\n}\n\nTEST_F(CIRCoreTest, TypePropertyInstrQueryAndReplace) {\n  auto *type = module->unsafeGetArrayType(module->getIntType());\n  auto *instr =\n      module->Nr<TypePropertyInstr>(type, TypePropertyInstr::Property::IS_ATOMIC);\n\n  ASSERT_EQ(type, instr->getInspectType());\n  ASSERT_EQ(TypePropertyInstr::Property::IS_ATOMIC, instr->getProperty());\n  ASSERT_EQ(module->getBoolType(), instr->getType());\n\n  instr->setProperty(TypePropertyInstr::Property::SIZEOF);\n  ASSERT_EQ(module->getIntType(), instr->getType());\n\n  auto usedTypes = instr->getUsedTypes();\n  ASSERT_EQ(1, usedTypes.size());\n  ASSERT_EQ(type, usedTypes[0]);\n\n  ASSERT_EQ(1, instr->replaceUsedType(\n                   type, module->unsafeGetArrayType(module->getFloatType())));\n}\n\nTEST_F(CIRCoreTest, TypePropertyInstrCloning) {\n  auto *type = module->unsafeGetArrayType(module->getIntType());\n  auto *instr =\n      module->Nr<TypePropertyInstr>(type, TypePropertyInstr::Property::IS_ATOMIC);\n  ASSERT_TRUE(util::match(instr, cv->clone(instr)));\n}\n\nTEST_F(CIRCoreTest, YieldInInstrQueryAndReplace) {\n  auto *type = module->unsafeGetArrayType(module->getIntType());\n  auto *instr = module->Nr<YieldInInstr>(type);\n\n  ASSERT_EQ(type, instr->getType());\n\n  auto usedTypes = instr->getUsedTypes();\n  ASSERT_EQ(1, usedTypes.size());\n  ASSERT_EQ(type, usedTypes[0]);\n\n  ASSERT_EQ(1, instr->replaceUsedType(\n                   type, module->unsafeGetArrayType(module->getFloatType())));\n}\n\nTEST_F(CIRCoreTest, YieldInInstrCloning) {\n  auto *type = module->unsafeGetArrayType(module->getIntType());\n  auto *instr = module->Nr<YieldInInstr>(type);\n  ASSERT_TRUE(util::match(instr, cv->clone(instr)));\n}\n\nTEST_F(CIRCoreTest, TernaryInstrQueryAndReplace) {\n  auto *trueValue = module->Nr<BoolConst>(true, module->getBoolType());\n  auto *falseValue = module->Nr<BoolConst>(false, module->getBoolType());\n  auto *cond = module->Nr<BoolConst>(true, module->getBoolType());\n  auto *instr = module->Nr<TernaryInstr>(cond, trueValue, falseValue);\n\n  ASSERT_EQ(trueValue, instr->getTrueValue());\n  ASSERT_EQ(falseValue, instr->getFalseValue());\n  ASSERT_EQ(cond, instr->getCond());\n\n  ASSERT_EQ(3, instr->getUsedValues().size());\n\n  ASSERT_EQ(1, instr->replaceUsedValue(\n                   cond, module->Nr<BoolConst>(true, module->getBoolType())));\n  ASSERT_EQ(1, instr->replaceUsedValue(\n                   trueValue, module->Nr<BoolConst>(true, module->getBoolType())));\n  ASSERT_EQ(1, instr->replaceUsedValue(\n                   falseValue, module->Nr<BoolConst>(true, module->getBoolType())));\n}\n\nTEST_F(CIRCoreTest, TernaryInstrCloning) {\n  auto *trueValue = module->Nr<BoolConst>(true, module->getBoolType());\n  auto *falseValue = module->Nr<BoolConst>(false, module->getBoolType());\n  auto *cond = module->Nr<BoolConst>(true, module->getBoolType());\n  auto *instr = module->Nr<TernaryInstr>(cond, trueValue, falseValue);\n\n  ASSERT_TRUE(util::match(instr, cv->clone(instr)));\n}\n\nTEST_F(CIRCoreTest, ContinueInstrQueryReplaceAndCloning) {\n  auto *instr = module->Nr<ContinueInstr>();\n  ASSERT_TRUE(util::match(instr, cv->clone(instr)));\n}\n\nTEST_F(CIRCoreTest, BreakInstrQueryReplaceAndCloning) {\n  auto *instr = module->Nr<BreakInstr>();\n  ASSERT_TRUE(util::match(instr, cv->clone(instr)));\n}\n\nTEST_F(CIRCoreTest, ReturnInstrQueryReplaceAndCloning) {\n  auto *val = module->Nr<IntConst>(1, module->getIntType());\n  auto *instr = module->Nr<ReturnInstr>(val);\n  ASSERT_EQ(val, instr->getValue());\n\n  ASSERT_EQ(1, instr->getUsedValues().size());\n  ASSERT_EQ(1, instr->replaceUsedValue(val, nullptr));\n  ASSERT_EQ(0, instr->getUsedValues().size());\n\n  ASSERT_TRUE(util::match(instr, cv->clone(instr)));\n}\n\nTEST_F(CIRCoreTest, YieldInstrQueryReplaceAndCloning) {\n  auto *val = module->Nr<IntConst>(1, module->getIntType());\n  auto *instr = module->Nr<YieldInstr>(val);\n  ASSERT_EQ(val, instr->getValue());\n\n  ASSERT_EQ(1, instr->getUsedValues().size());\n  ASSERT_EQ(1, instr->replaceUsedValue(val, nullptr));\n  ASSERT_EQ(0, instr->getUsedValues().size());\n\n  ASSERT_TRUE(util::match(instr, cv->clone(instr)));\n}\n\nTEST_F(CIRCoreTest, ThrowInstrQueryReplaceAndCloning) {\n  auto *val = module->Nr<IntConst>(1, module->getIntType());\n  auto *instr = module->Nr<ThrowInstr>(val);\n  ASSERT_EQ(val, instr->getValue());\n\n  ASSERT_EQ(1, instr->getUsedValues().size());\n  ASSERT_EQ(1, instr->replaceUsedValue(val, nullptr));\n  ASSERT_EQ(0, instr->getUsedValues().size());\n\n  ASSERT_TRUE(util::match(instr, cv->clone(instr)));\n}\n\nTEST_F(CIRCoreTest, FlowInstrQueryAndReplace) {\n  auto *flow = module->Nr<SeriesFlow>();\n  auto *val = module->Nr<IntConst>(1, module->getIntType());\n  auto *instr = module->Nr<FlowInstr>(flow, val);\n\n  ASSERT_EQ(module->getIntType(), instr->getType());\n  ASSERT_EQ(val, instr->getValue());\n  ASSERT_EQ(flow, instr->getFlow());\n\n  ASSERT_EQ(2, instr->getUsedValues().size());\n  ASSERT_EQ(\n      1, instr->replaceUsedValue(val, module->Nr<IntConst>(2, module->getIntType())));\n  ASSERT_EQ(1, instr->replaceUsedValue(flow, module->Nr<SeriesFlow>()));\n}\n\nTEST_F(CIRCoreTest, FlowInstrCloning) {\n  auto *flow = module->Nr<SeriesFlow>();\n  auto *val = module->Nr<IntConst>(1, module->getIntType());\n  auto *instr = module->Nr<FlowInstr>(flow, val);\n  ASSERT_TRUE(util::match(instr, cv->clone(instr)));\n}\n"
  },
  {
    "path": "test/cir/module.cpp",
    "content": "#include \"test.h\"\n\n#include \"codon/cir/util/matching.h\"\n\nusing namespace codon::ir;\n\nTEST_F(CIRCoreTest, ModuleNodeBuildingRemovalAndIterators) {\n  {\n    auto n1 = module->Nr<types::OptionalType>(module->getIntType());\n    ASSERT_EQ(n1->getModule(), module.get());\n    auto numTypes = std::distance(module->types_begin(), module->types_end());\n    ASSERT_TRUE(std::find(module->types_begin(), module->types_end(), n1) !=\n                module->types_end());\n    module->remove(n1);\n    ASSERT_EQ(numTypes - 1, std::distance(module->types_begin(), module->types_end()));\n  }\n  {\n    auto n1 = module->N<IntConst>(codon::SrcInfo{}, 1, module->getIntType());\n    ASSERT_EQ(n1->getModule(), module.get());\n    auto numVals = std::distance(module->values_begin(), module->values_end());\n    ASSERT_TRUE(std::find(module->values_begin(), module->values_end(), n1) !=\n                module->values_end());\n    module->remove(n1);\n    ASSERT_EQ(numVals - 1, std::distance(module->values_begin(), module->values_end()));\n  }\n  {\n    auto n1 = module->Nr<Var>(module->getIntType());\n    ASSERT_EQ(n1->getModule(), module.get());\n    auto numVars = std::distance(module->begin(), module->end());\n    ASSERT_TRUE(std::find(module->begin(), module->end(), n1) != module->end());\n    module->remove(n1);\n    ASSERT_EQ(numVars - 1, std::distance(module->begin(), module->end()));\n  }\n}\n\nTEST_F(CIRCoreTest, ModuleMainFunctionAndArgVar) {\n  auto *main = module->getMainFunc();\n  ASSERT_TRUE(main);\n  auto *mainType = cast<types::FuncType>(main->getType());\n  ASSERT_TRUE(mainType);\n  ASSERT_TRUE(util::match(mainType->getReturnType(), module->getVoidType()));\n  ASSERT_EQ(0, std::distance(mainType->begin(), mainType->end()));\n  ASSERT_FALSE(main->isReplaceable());\n\n  auto *argVar = module->getArgVar();\n  ASSERT_TRUE(argVar);\n  ASSERT_TRUE(util::match(argVar->getType(),\n                          module->unsafeGetArrayType(module->getStringType())));\n  ASSERT_FALSE(argVar->isReplaceable());\n}\n\nTEST_F(CIRCoreTest, ModuleTypeGetAndLookup) {\n  auto TYPE_NAME = \"**test_type**\";\n  auto *newType = module->unsafeGetMemberedType(TYPE_NAME);\n  ASSERT_TRUE(isA<types::RecordType>(newType));\n  ASSERT_EQ(newType, module->getType(TYPE_NAME));\n  module->remove(newType);\n\n  newType = module->unsafeGetMemberedType(TYPE_NAME, true);\n  ASSERT_TRUE(isA<types::RefType>(newType));\n  ASSERT_EQ(newType, module->getType(TYPE_NAME));\n}\n"
  },
  {
    "path": "test/cir/test.h",
    "content": "#include <algorithm>\n\n#include \"codon/cir/cir.h\"\n#include \"codon/cir/util/cloning.h\"\n#include \"gtest/gtest.h\"\n\nclass CIRCoreTest : public testing::Test {\nprotected:\n  std::unique_ptr<codon::ir::Module> module;\n  std::unique_ptr<codon::ir::util::CloneVisitor> cv;\n\n  void SetUp() override {\n    codon::ir::IdMixin::resetId();\n    module = std::make_unique<codon::ir::Module>(\"test\");\n    cv = std::make_unique<codon::ir::util::CloneVisitor>(module.get());\n  }\n};\n"
  },
  {
    "path": "test/cir/transform/manager.cpp",
    "content": "#include \"test.h\"\n\n#include \"codon/cir/transform/manager.h\"\n#include \"codon/cir/transform/pass.h\"\n\nusing namespace codon::ir;\n\nstd::string ANALYSIS_KEY = \"**test_analysis**\";\nstd::string PASS_KEY = \"**test_pass**\";\n\nclass DummyResult : public analyze::Result {};\n\nclass DummyAnalysis : public analyze::Analysis {\nprivate:\n  int &counter;\n\npublic:\n  static int runCounter;\n\n  explicit DummyAnalysis(int &counter) : counter(counter) {}\n\n  std::string getKey() const override { return ANALYSIS_KEY; }\n\n  std::unique_ptr<analyze::Result> run(const Module *) override {\n    runCounter = counter++;\n    return std::make_unique<DummyResult>();\n  }\n};\n\nint DummyAnalysis::runCounter = 0;\n\nclass DummyPass : public transform::Pass {\nprivate:\n  int &counter;\n  std::string required;\n\npublic:\n  static int runCounter;\n\n  explicit DummyPass(int &counter, std::string required)\n      : counter(counter), required(std::move(required)) {}\n\n  std::string getKey() const override { return PASS_KEY; }\n\n  void run(Module *) override {\n    runCounter = counter++;\n    ASSERT_TRUE(getAnalysisResult<DummyResult>(required));\n  }\n};\n\nint DummyPass::runCounter = 0;\n\nTEST_F(CIRCoreTest, PassManagerNoInvalidations) {\n  int counter = 0;\n\n  auto manager =\n      std::make_unique<transform::PassManager>(transform::PassManager::Init::EMPTY);\n  manager->registerAnalysis(std::make_unique<DummyAnalysis>(counter));\n  manager->registerPass(std::make_unique<DummyPass>(counter, ANALYSIS_KEY), \"\",\n                        {ANALYSIS_KEY});\n  manager->run(module.get());\n\n  ASSERT_EQ(0, DummyAnalysis::runCounter);\n  ASSERT_EQ(1, DummyPass::runCounter);\n}\n\nTEST_F(CIRCoreTest, PassManagerInvalidations) {\n  int counter = 0;\n\n  auto manager =\n      std::make_unique<transform::PassManager>(transform::PassManager::Init::EMPTY);\n  manager->registerAnalysis(std::make_unique<DummyAnalysis>(counter));\n  manager->registerPass(std::make_unique<DummyPass>(counter, ANALYSIS_KEY), \"\",\n                        {ANALYSIS_KEY}, {ANALYSIS_KEY});\n  manager->registerPass(std::make_unique<DummyPass>(counter, ANALYSIS_KEY), \"\",\n                        {ANALYSIS_KEY});\n\n  manager->run(module.get());\n\n  ASSERT_EQ(2, DummyAnalysis::runCounter);\n  ASSERT_EQ(3, DummyPass::runCounter);\n}\n"
  },
  {
    "path": "test/cir/types/types.cpp",
    "content": "#include \"test.h\"\n\n#include <algorithm>\n\nusing namespace codon::ir;\n\nTEST_F(CIRCoreTest, RecordTypeQuery) {\n  auto MEMBER_NAME = \"1\";\n  auto *type = module->Nr<types::RecordType>(\n      \"foo\", std::vector<types::Type *>{module->getIntType()});\n\n  ASSERT_EQ(module->getIntType(), type->getMemberType(MEMBER_NAME));\n  ASSERT_EQ(0, type->getMemberIndex(MEMBER_NAME));\n\n  ASSERT_EQ(1, std::distance(type->begin(), type->end()));\n  ASSERT_EQ(module->getIntType(), type->front().getType());\n\n  MEMBER_NAME = \"baz\";\n  type->realize({module->getIntType()}, {MEMBER_NAME});\n\n  ASSERT_TRUE(type->isAtomic());\n\n  ASSERT_EQ(1, type->getUsedTypes().size());\n  ASSERT_EQ(module->getIntType(), type->getUsedTypes()[0]);\n}\n\nTEST_F(CIRCoreTest, RefTypeQuery) {\n  auto MEMBER_NAME = \"1\";\n  auto *contents = module->Nr<types::RecordType>(\n      \"foo\", std::vector<types::Type *>{module->getIntType()});\n  auto *type = module->Nr<types::RefType>(\"baz\", contents);\n\n  ASSERT_EQ(module->getIntType(), type->getMemberType(MEMBER_NAME));\n  ASSERT_EQ(0, type->getMemberIndex(MEMBER_NAME));\n\n  ASSERT_EQ(1, std::distance(type->begin(), type->end()));\n  ASSERT_EQ(module->getIntType(), type->front().getType());\n\n  MEMBER_NAME = \"baz\";\n  type->realize({module->getIntType()}, {MEMBER_NAME});\n\n  ASSERT_FALSE(type->isAtomic());\n\n  ASSERT_EQ(1, type->getUsedTypes().size());\n  ASSERT_EQ(contents, type->getUsedTypes()[0]);\n}\n\nTEST_F(CIRCoreTest, FuncTypeQuery) {\n  auto *type = module->Nr<types::FuncType>(\n      \"foo\", module->getIntType(), std::vector<types::Type *>{module->getFloatType()});\n\n  ASSERT_EQ(1, std::distance(type->begin(), type->end()));\n  ASSERT_EQ(module->getFloatType(), type->front());\n\n  ASSERT_EQ(2, type->getUsedTypes().size());\n}\n"
  },
  {
    "path": "test/cir/util/matching.cpp",
    "content": "#include \"test.h\"\n\n#include \"codon/cir/util/matching.h\"\n\nusing namespace codon::ir;\n\nTEST_F(CIRCoreTest, MatchingEquivalentVar) {\n  auto *first = module->Nr<Var>(module->getIntType());\n  auto *second = module->Nr<Var>(module->getIntType());\n  auto *third = module->Nr<Var>(module->getFloatType());\n  ASSERT_TRUE(util::match(first, second));\n  ASSERT_FALSE(util::match(first, third));\n}\n\nTEST_F(CIRCoreTest, MatchingNonEquivalentVar) {\n  auto *first = module->Nr<Var>(module->getIntType());\n  auto *second = module->Nr<Var>(module->getFloatType());\n  ASSERT_FALSE(util::match(first, second));\n}\n\nTEST_F(CIRCoreTest, MatchingEquivalentFunc) {\n  {\n    auto *first = module->Nr<BodiedFunc>();\n    first->realize(module->unsafeGetDummyFuncType(), {});\n    auto *second = module->Nr<BodiedFunc>();\n    second->realize(module->unsafeGetDummyFuncType(), {});\n\n    first->setJIT();\n    second->setJIT();\n\n    ASSERT_TRUE(util::match(first, second));\n  }\n  {\n    auto *first = module->Nr<ExternalFunc>();\n    first->realize(module->unsafeGetDummyFuncType(), {});\n    auto *second = module->Nr<ExternalFunc>();\n    second->realize(module->unsafeGetDummyFuncType(), {});\n\n    first->setUnmangledName(\"baz\");\n    second->setUnmangledName(\"baz\");\n\n    ASSERT_TRUE(util::match(first, second));\n  }\n  {\n    auto *first = module->Nr<LLVMFunc>();\n    first->realize(module->unsafeGetDummyFuncType(), {});\n    auto *second = module->Nr<LLVMFunc>();\n    second->realize(module->unsafeGetDummyFuncType(), {});\n\n    ASSERT_TRUE(util::match(first, second));\n  }\n}\n\nTEST_F(CIRCoreTest, MatchingNonEquivalentFunc) {\n  {\n    auto *first = module->Nr<BodiedFunc>();\n    first->realize(module->unsafeGetDummyFuncType(), {});\n    auto *second = module->Nr<BodiedFunc>();\n    second->realize(module->unsafeGetDummyFuncType(), {});\n\n    first->setJIT();\n\n    ASSERT_FALSE(util::match(first, second));\n  }\n  {\n    auto *first = module->Nr<ExternalFunc>();\n    first->realize(module->unsafeGetDummyFuncType(), {});\n    auto *second = module->Nr<ExternalFunc>();\n    second->realize(module->unsafeGetDummyFuncType(), {});\n\n    first->setUnmangledName(\"baz\");\n    second->setUnmangledName(\"bar\");\n\n    ASSERT_FALSE(util::match(first, second));\n  }\n  {\n    auto *first = module->Nr<LLVMFunc>();\n    first->realize(module->unsafeGetDummyFuncType(), {});\n    auto *second = module->Nr<LLVMFunc>();\n    second->realize(module->unsafeGetDummyFuncType(), {});\n\n    first->setLLVMLiterals({types::Generic(1)});\n\n    ASSERT_FALSE(util::match(first, second));\n  }\n}\n\nTEST_F(CIRCoreTest, MatchingAnyValue) {\n  auto *first = module->Nr<VarValue>(module->Nr<Var>(module->getIntType()));\n  ASSERT_TRUE(util::match(first, module->Nr<util::AnyValue>()));\n}\n\nTEST_F(CIRCoreTest, MatchingVarValue) {\n  auto *first = module->Nr<VarValue>(module->Nr<Var>(module->getIntType()));\n  auto *second = module->Nr<VarValue>(module->Nr<Var>(module->getIntType()));\n  ASSERT_TRUE(util::match(first, second));\n  first->setVar(module->Nr<Var>(module->getFloatType()));\n  ASSERT_FALSE(util::match(first, second));\n}\n\nTEST_F(CIRCoreTest, MatchingPointerValue) {\n  auto *first = module->Nr<PointerValue>(module->Nr<Var>(module->getIntType()));\n  auto *second = module->Nr<PointerValue>(module->Nr<Var>(module->getIntType()));\n  ASSERT_TRUE(util::match(first, second));\n  first->setVar(module->Nr<Var>(module->getFloatType()));\n  ASSERT_FALSE(util::match(first, second));\n}\n\nTEST_F(CIRCoreTest, MatchingSeriesFlow) {\n  auto *first = module->Nr<SeriesFlow>();\n  auto *second = module->Nr<SeriesFlow>();\n\n  first->push_back(module->Nr<IntConst>(1, module->getIntType()));\n  second->push_back(module->Nr<IntConst>(1, module->getIntType()));\n  ASSERT_TRUE(util::match(first, second));\n  second->push_back(module->Nr<IntConst>(1, module->getIntType()));\n  ASSERT_FALSE(util::match(first, second));\n}\n\nTEST_F(CIRCoreTest, MatchingIfFlow) {\n  auto *cond = module->Nr<BoolConst>(true, module->getBoolType());\n  auto *tVal = module->Nr<SeriesFlow>();\n  auto *first = module->Nr<IfFlow>(cond, tVal);\n  auto *second = module->Nr<IfFlow>(cv->clone(cond), cast<Flow>(cv->clone(tVal)));\n\n  ASSERT_TRUE(util::match(first, second));\n  second->setFalseBranch(cast<Flow>(cv->clone(tVal)));\n  ASSERT_FALSE(util::match(first, second));\n}\n\nTEST_F(CIRCoreTest, MatchingForFlow) {\n  auto *body = module->Nr<SeriesFlow>();\n  auto *var = module->Nr<Var>(module->getIntType());\n  auto *iter = module->Nr<StringConst>(\"hello\", module->getStringType());\n  auto *first = module->Nr<ForFlow>(iter, body, var);\n  auto *second =\n      module->Nr<ForFlow>(cv->clone(iter), cast<Flow>(cv->clone(body)), cv->clone(var));\n\n  ASSERT_TRUE(util::match(first, second));\n  second->setIter(module->Nr<StringConst>(\"foo\", module->getStringType()));\n  ASSERT_FALSE(util::match(first, second));\n}\n\nTEST_F(CIRCoreTest, MatchingIntConst) {\n  auto *first = module->Nr<IntConst>(0, module->getIntType());\n  auto *second = module->Nr<IntConst>(0, module->getIntType());\n  ASSERT_TRUE(util::match(first, second));\n  first->setVal(2);\n  ASSERT_FALSE(util::match(first, second));\n}\n\nTEST_F(CIRCoreTest, MatchingFloatConst) {\n  auto *first = module->Nr<FloatConst>(0.0, module->getFloatType());\n  auto *second = module->Nr<FloatConst>(0.0, module->getFloatType());\n  ASSERT_TRUE(util::match(first, second));\n  first->setVal(2.0);\n  ASSERT_FALSE(util::match(first, second));\n}\n\nTEST_F(CIRCoreTest, MatchingBoolConst) {\n  auto *first = module->Nr<BoolConst>(false, module->getBoolType());\n  auto *second = module->Nr<BoolConst>(false, module->getBoolType());\n  ASSERT_TRUE(util::match(first, second));\n  first->setVal(true);\n  ASSERT_FALSE(util::match(first, second));\n}\n\nTEST_F(CIRCoreTest, MatchingStringConst) {\n  auto *first = module->Nr<StringConst>(\"hi\", module->getStringType());\n  auto *second = module->Nr<StringConst>(\"hi\", module->getStringType());\n  ASSERT_TRUE(util::match(first, second));\n  first->setVal(\"bye\");\n  ASSERT_FALSE(util::match(first, second));\n}\n\nTEST_F(CIRCoreTest, MatchingAssignInstr) {\n  auto *var = module->Nr<Var>(module->getIntType());\n  auto *val = module->Nr<IntConst>(1, module->getIntType());\n  auto *first = module->Nr<AssignInstr>(var, val);\n  auto *second = module->Nr<AssignInstr>(cv->clone(var), cv->clone(val));\n\n  ASSERT_TRUE(util::match(first, second));\n  second->setRhs(module->Nr<IntConst>(5, module->getIntType()));\n  ASSERT_FALSE(util::match(first, second));\n}\n\nTEST_F(CIRCoreTest, MatchingExtractInstr) {\n  auto FIELD = \"foo\";\n  auto *type = cast<types::RecordType>(module->unsafeGetMemberedType(\"**internal**\"));\n  type->realize({module->getIntType()}, {FIELD});\n  auto *var = module->Nr<Var>(type);\n  auto *val = module->Nr<VarValue>(var);\n  auto *first = module->Nr<ExtractInstr>(val, FIELD);\n  auto *second = module->Nr<ExtractInstr>(cv->clone(val), FIELD);\n\n  ASSERT_TRUE(util::match(first, second));\n  second->setField(\"\");\n  ASSERT_FALSE(util::match(first, second));\n}\n\nTEST_F(CIRCoreTest, MatchingInsertInstr) {\n  auto FIELD = \"foo\";\n  auto *type = cast<types::RecordType>(module->unsafeGetMemberedType(\"**internal**\"));\n  type->realize({module->getIntType()}, {FIELD});\n  auto *var = module->Nr<Var>(type);\n  auto *val = module->Nr<VarValue>(var);\n  auto *toInsert = module->Nr<IntConst>(1, module->getIntType());\n  auto *first = module->Nr<InsertInstr>(val, FIELD, toInsert);\n  auto *second = module->Nr<InsertInstr>(cv->clone(val), FIELD, cv->clone(toInsert));\n\n  ASSERT_TRUE(util::match(first, second));\n  second->setField(\"\");\n  ASSERT_FALSE(util::match(first, second));\n}\n\nTEST_F(CIRCoreTest, MatchingCallInstr) {\n  auto *type = module->unsafeGetDummyFuncType();\n  auto *func = module->Nr<BodiedFunc>();\n  func->realize(type, {});\n  auto *func2 = module->Nr<BodiedFunc>();\n  func2->realize(module->unsafeGetFuncType(\"baz\", module->getIntType(), {}), {});\n\n  auto *funcVal = module->Nr<VarValue>(func);\n  auto *first = module->Nr<CallInstr>(funcVal);\n  auto *second = module->Nr<CallInstr>(cv->clone(funcVal));\n\n  ASSERT_TRUE(util::match(first, second));\n  second->setCallee(module->Nr<VarValue>(func2));\n  ASSERT_FALSE(util::match(first, second));\n}\n\nTEST_F(CIRCoreTest, MatchingTernaryInstr) {\n  auto *trueValue = module->Nr<BoolConst>(true, module->getBoolType());\n  auto *falseValue = module->Nr<BoolConst>(false, module->getBoolType());\n  auto *cond = module->Nr<BoolConst>(true, module->getBoolType());\n\n  auto *first = module->Nr<TernaryInstr>(cond, trueValue, falseValue);\n  auto *second = module->Nr<TernaryInstr>(cv->clone(cond), cv->clone(trueValue),\n                                          cv->clone(falseValue));\n\n  ASSERT_TRUE(util::match(first, second));\n  second->setFalseValue(module->Nr<BoolConst>(true, module->getBoolType()));\n  ASSERT_FALSE(util::match(first, second));\n}\n"
  },
  {
    "path": "test/cir/value.cpp",
    "content": "#include \"test.h\"\n\nusing namespace codon::ir;\n\nTEST_F(CIRCoreTest, ValueQueryMethodsDelegate) {\n  Value *original = module->Nr<IntConst>(1, module->getIntType(), \"foo\");\n  auto originalRef = original->referenceString();\n\n  auto *fn = module->Nr<BodiedFunc>();\n  fn->realize(module->unsafeGetDummyFuncType(), {});\n  Value *replacement = module->Nr<VarValue>(fn, \"baz\");\n  original->replaceAll(replacement);\n\n  ASSERT_NE(originalRef, original->referenceString());\n  ASSERT_EQ(original->referenceString(), replacement->referenceString());\n\n  ASSERT_EQ(1, original->getUsedVariables().size());\n\n  replacement = module->Nr<CallInstr>(replacement);\n  original->replaceAll(replacement);\n  ASSERT_EQ(0, original->getUsedVariables().size());\n  ASSERT_EQ(1, original->getUsedValues().size());\n  ASSERT_EQ(module->getVoidType(), original->getType());\n\n  replacement = module->Nr<TypePropertyInstr>(module->getIntType(),\n                                              TypePropertyInstr::Property::SIZEOF);\n  original->replaceAll(replacement);\n  ASSERT_EQ(0, original->getUsedVariables().size());\n  ASSERT_EQ(0, original->getUsedValues().size());\n  ASSERT_EQ(1, original->getUsedTypes().size());\n}\n\nTEST_F(CIRCoreTest, ValueReplaceMethodsDelegate) {\n  Value *original = module->Nr<IntConst>(1, module->getIntType(), \"foo\");\n  auto originalRef = original->referenceString();\n  auto originalId = original->getId();\n  auto *var = module->Nr<BodiedFunc>();\n  Value *replacement = module->Nr<VarValue>(var, \"baz\");\n  original->replaceAll(replacement);\n\n  ASSERT_EQ(1, original->replaceUsedVariable(var, var));\n\n  auto *val = replacement;\n  replacement = module->Nr<CallInstr>(replacement);\n  original->replaceAll(replacement);\n  ASSERT_EQ(1, original->replaceUsedValue(val, val));\n\n  replacement = module->Nr<TypePropertyInstr>(module->getIntType(),\n                                              TypePropertyInstr::Property::SIZEOF);\n  original->replaceAll(replacement);\n  ASSERT_EQ(1, original->replaceUsedType(module->getIntType(), module->getFloatType()));\n\n  ASSERT_NE(originalId, original->getId());\n  ASSERT_EQ(original->getId(), replacement->getId());\n}\n"
  },
  {
    "path": "test/cir/var.cpp",
    "content": "#include \"test.h\"\n\nusing namespace codon::ir;\n\nTEST_F(CIRCoreTest, VarQueryMethodsDelegate) {\n  Var *original = module->Nr<Var>(module->getIntType());\n  Var *replacement = module->Nr<Var>(module->getFloatType());\n  original->replaceAll(replacement);\n\n  ASSERT_EQ(module->getFloatType(), original->getType());\n  ASSERT_EQ(module->getFloatType(), original->getUsedTypes().back());\n}\n\nTEST_F(CIRCoreTest, VarReplaceMethodsDelegate) {\n  Var *original = module->Nr<Var>(module->getIntType());\n  Var *replacement = module->Nr<Var>(module->getFloatType());\n\n  auto originalId = original->getId();\n  original->replaceAll(replacement);\n\n  ASSERT_EQ(1, original->replaceUsedType(module->getFloatType(), module->getIntType()));\n  ASSERT_NE(originalId, original->getId());\n  ASSERT_EQ(original->getId(), replacement->getId());\n}\n"
  },
  {
    "path": "test/core/arguments.codon",
    "content": "@test\ndef t1():\n    def baz(x, y, z = 3):\n        return x, y, z\n    assert baz(1, 2, 3) == (1, 2, 3)\n    assert baz(1, 3) == (1, 3, 3)\n    assert baz(z = 'z', y = 'y', x = 'x') == ('x', 'y', 'z')\n    assert baz(y = 'y', x = 'x') == ('x', 'y', 3)\n    assert baz('x', y = 'y') == ('x', 'y', 3)\nt1()\n\nclass A:\n    def foo(self: A, x = 3, y = 'hello'):\n        return x, y\n\n@test\ndef t2():\n    assert A().foo(y = 3.14, x = 42) == (42, 3.14)\nt2()\n\ndef g[T](a: T, b: optional[T] = None):\n    b: T = b if b else T()\n    return a, b\n\n@test\ndef t3():\n    assert g(99, 4242) == (99, 4242)\n    assert g(99) == (99, 0)\n    assert (None |> g(a=1, b=...)) == (1, 0)\nt3()\n\ndef _unwrap(opt: Optional[T], T: type) -> T:\n    return opt\n\ndef foo(x: int, y: int, z: optional[list[float]]):\n    xs = str(x)\n    ys = str(y)\n    zs = str(_unwrap(z)) if z else 'None'\n    return xs, ys, zs\n\n@test\ndef t4():\n    assert foo(1, 2, [3.14]) == ('1', '2', '[3.14]')\n    assert foo(77, 99, None) == ('77', '99', 'None')\nt4()\n\nclass A:\n    def __init__(self: A):\n        pass\n\n    def foo(x: int, y: int, z: optional[list[float]]):\n        xs = str(x)\n        ys = str(y)\n        zs = str(_unwrap(z)) if z else 'None'\n        return xs, ys, zs\n\n    # TODO: def bar[S](self: A, x: S, y: S, z: optional[type(S() + 0.0)] = None)\n    def bar[S](self: A, x: S, y: S, z: optional[S] = None):\n        xs = str(x)\n        ys = str(y)\n        zs = str(_unwrap(z)) if z else 'None'\n        return xs, ys, zs\n\n@test\ndef t5():\n    assert A.foo(1, 2, [3.14]) == ('1', '2', '[3.14]')\n    assert A.foo(77, 99, None) == ('77', '99', 'None')\n\n    assert A().bar(1.0, 2.0, 3.14) == ('1', '2', '3.14')\n    assert A().bar(77, 99, None) == ('77', '99', 'None')\n    assert A().bar(77, 99) == ('77', '99', 'None')\n\n    assert (1 |> foo(2, [3.14])) == ('1', '2', '[3.14]')\n    assert (1 |> foo(77, ..., None)) == ('77', '1', 'None')\n    assert (None |> foo(-5, -1, ...)) == ('-5', '-1', 'None')\n    assert ([1.23] |> foo(-5, -1, ...)) == ('-5', '-1', '[1.23]')\nt5()\n\n# test auto-iter\n@test\ndef t6():\n    assert list('abc') == ['a', 'b', 'c']\n\n    def it1[T](v: generator[T]):\n        return list(v)\n\n    def it2[T](v: T):\n        return list(v)\n\n    def it3[T](v: generator[T] = 'xyz'):\n        return list(v)\n\n    assert it1([1, 2, 3]) == [1, 2, 3]\n    assert it2(iter([1.1, 2.2, 3.3])) == [1.1, 2.2, 3.3]\n    assert it3() == ['x', 'y', 'z']\nt6()\n\nclass B[T]:\n    a: T\n    b: tuple[int,int,int]\n\n    def __init__(self: B[T], y: T):\n        self.a = y\n        self.b = 0, 0, 0\n\n    def __init__(self: B[T], y: T, foo: int):\n        self.a = y\n        self.b = foo, 1, 0\n\n    def __init__(self: B[T], x: list[T], a: int, b: int, c: int):\n        self.a = x[0]\n        self.b = a, b, c\n\n    def __init__(self: B[T], a: T, b: T):\n        self.a, self.b = a + b, (-1, -1, -1)\n\n    @property\n    def val(self: B[T]):\n        return self.a, self.b\n\n@test\ndef test_named_construct1(_):\n    assert B(10).val == (10, (0, 0, 0))\n    assert B(y=-10).val == (-10, (0, 0, 0))\n    assert B[bool](y=False).val == (False, (0, 0, 0))\n    assert B(b=2, x=[3.14], c=3, a=1).val == (3.14, (1, 2, 3))\n    assert B(x=[3.14], b=2, c=3, a=1).val == (3.14, (1, 2, 3))\n    assert B([3.14], b=2, c=3, a=1).val == (3.14, (1, 2, 3))\n    assert B([3.14], 1, b=2, c=3).val == (3.14, (1, 2, 3))\n    assert B[float]([3.14], 1, b=2, c=3).val == (3.14, (1, 2, 3))\n    assert B([3.14], 1, 2, c=3).val == (3.14, (1, 2, 3))\n    assert B(foo=42, y='hello').val == ('hello', (42, 1, 0))\n    assert B(a='a', b='b').val == ('ab', (-1, -1, -1))\n    assert B(b='b', a='a').val == ('ab', (-1, -1, -1))\ntest_named_construct1(0)\n\n@tuple\nclass C:\n    a: float\n    b: tuple[int,int,int]\n\n    def __new__(y: float) -> C:\n        return C(y, (0, 0, 0))\n\n    def __new__(y: float, foo: int) -> C:\n        return C(y, (foo, 1, 0))\n\n    def __new__(x: list[float], a: int, b: int, c: int) -> C:\n        return C(x[0], (a, b, c))\n\n    def __new__(a: int, b: int):\n        return (a, b)\n\n    @property\n    def val(self: C):\n        return self.a, self.b\n\n@test\ndef test_named_construct2(_):\n    assert C(10.0).val == (10.0, (0, 0, 0))\n    assert C(y=-10.0).val == (-10.0, (0, 0, 0))\n    assert C(b=2, x=[3.14], c=3, a=1).val == (3.14, (1, 2, 3))\n    assert C(x=[3.14], b=2, c=3, a=1).val == (3.14, (1, 2, 3))\n    assert C([3.14], b=2, c=3, a=1).val == (3.14, (1, 2, 3))\n    assert C([3.14], 1, b=2, c=3).val == (3.14, (1, 2, 3))\n    assert C([3.14], 1, b=2, c=3).val == (3.14, (1, 2, 3))\n    assert C([3.14], 1, 2, c=3).val == (3.14, (1, 2, 3))\n    assert C(foo=42, y=-4.2).val == (-4.2, (42, 1, 0))\n    assert C(a=111, b=222) == (111, 222)\n    assert C(b=222, a=111) == (111, 222)\ntest_named_construct2(0)\n"
  },
  {
    "path": "test/core/arithmetic.codon",
    "content": "@test\ndef t1():\n    assert 2 + 2 == 4\n    assert 3.14 * 2 == 6.28\n    assert 2 + 3*2 == 8\n    assert 1.0/0 == float('inf')\n    assert str(0.0/0) == 'nan'\n\n    assert 5 // 2 == 2\n    assert 5 / 2 == 2.5\n    assert 5.0 // 2.0 == 2\n    assert 5.0 / 2.0 == 2.5\n    assert 5 // 2.0 == 2\n    assert 5 / 2.0 == 2.5\n    assert 5.0 // 2 == 2\n    assert 5.0 / 2 == 2.5\n    assert int(Int[128](5) // Int[128](2)) == 2\n    assert Int[128](5) / Int[128](2) == 2.5\nt1()\n\n@test\ndef test_popcnt():\n    assert (42).popcnt() == 3\n    assert (123).popcnt() == 6\n    assert (0).popcnt() == 0\n    assert int.popcnt(-1) == 64\n    assert u8(-1).popcnt() == 8\n    assert (UInt[1024](0xfffffffffffffff3) * UInt[1024](0xfffffffffffffff3)).popcnt() == 4\n    assert UInt[128](-1).popcnt() == 128\ntest_popcnt()\n\n@test\ndef test_conversions():\n    # int -> int, float, bool, str\n    assert int(-42) == -42\n    assert float(-42) == -42.0\n    assert bool(0) == False\n    assert bool(-1) == bool(1) == True\n    assert str(-42) == '-42'\n\n    # float -> int, float, bool, str\n    assert int(-4.2) == -4\n    assert int(4.2) == 4\n    assert float(-4.2) == -4.2\n    assert bool(0.0) == False\n    assert bool(-0.1) == bool(0.1) == True\n    assert str(-4.2) == '-4.2'\n\n    # bool -> int, float, bool, str\n    assert int(False) == 0\n    assert int(True) == 1\n    assert float(False) == 0.0\n    assert float(True) == 1.0\n    assert bool(False) == False\n    assert bool(True) == True\n    assert str(False) == 'False'\n    assert str(True) == 'True'\n\n    # byte -> int, float, bool, str\n    assert int(byte(42)) == 42\n    assert float(byte(42)) == 42.0\n    assert bool(byte(0)) == False\n    assert bool(byte(42)) == True\n    assert str(byte(42)) == '*'\n\n    # intN -> int, float, bool, str | N < 64\n    assert int(i32(-42)) == -42\n    assert float(i32(-42)) == -42.0\n    assert bool(i32(0)) == False\n    assert bool(i32(-1)) == bool(i32(1)) == True\n    assert str(i32(-42)) == '-42'\n\n    # intN -> int, float, bool, str | N == 64\n    assert int(Int[64](-42)) == -42\n    assert float(Int[64](-42)) == -42.0\n    assert bool(Int[64](0)) == False\n    assert bool(Int[64](-1)) == bool(Int[64](1)) == True\n    assert str(Int[64](-42)) == '-42'\n\n    # intN -> int, float, bool, str | N > 64\n    assert int(Int[80](-42)) == -42\n    assert float(Int[80](-42)) == -42.0\n    assert bool(Int[80](0)) == False\n    assert bool(Int[80](-1)) == bool(Int[80](1)) == True\n    assert str(Int[80](-42)) == '-42'\n\n    # uintN -> int, float, bool, str | N < 64\n    assert int(u32(42)) == 42\n    assert float(u32(42)) == 42.0\n    assert bool(u32(0)) == False\n    assert bool(u32(42)) == True\n    assert str(u32(42)) == '42'\n\n    # uintN -> int, float, bool, str | N == 64\n    assert int(UInt[64](42)) == 42\n    assert float(UInt[64](42)) == 42.0\n    assert bool(UInt[64](0)) == False\n    assert bool(UInt[64](42)) == True\n    assert str(UInt[64](42)) == '42'\n\n    # uintN -> int, float, bool, str | N > 64\n    assert int(UInt[80](42)) == 42\n    assert float(UInt[80](42)) == 42.0\n    assert bool(UInt[80](0)) == False\n    assert bool(Int[80](42)) == True\n    assert str(Int[80](42)) == '42'\n\n    # float -> intN\n    assert i32(3.14) == i32(3)\n\n    # float -> uintN\n    assert u32(3.14) == u32(3)\n\n    # [u]intN -> [u]intN\n    a = Int[16]('42')\n    b = UInt[16](42.0)\n    c = 42\n    e = Int[64](42.0)\n\n    assert i32(a) == i32(42)\n    assert u32(a) == u32(42)\n    assert i8(a) == i8(42)\n    assert u8(a) == u8(42)\n    assert i16(a) == i16(42)\n    assert u16(a) == u16(42)\n\n    assert i32(b) == i32(42)\n    assert u32(b) == u32(42)\n    assert i8(b) == i8(42)\n    assert u8(b) == u8(42)\n    assert i16(b) == i16(42)\n    assert u16(b) == u16(42)\n\n    assert i32(c) == i32(42)\n    assert u32(c) == u32(42)\n    assert i8(c) == i8(42)\n    assert u8(c) == u8(42)\n    assert i16(c) == i16(42)\n    assert u16(c) == u16(42)\n\n    assert i32(e) == i32(42)\n    assert u32(e) == u32(42)\n    assert i8(e) == i8(42)\n    assert u8(e) == u8(42)\n    assert i16(e) == i16(42)\n    assert u16(e) == u16(42)\n    assert i128(e) == i128(42)\n    assert u128(e) == u128(42)\n\n    # ptr -> ptr\n    p1 = Ptr[int]()\n    p2 = Ptr[float]()\n    p3 = cobj()\n    assert Ptr[float](p1) == p2\n    assert Ptr[int](p2) == p1\n    assert Ptr[float](p3) == p2\n    assert Ptr[int](p3) == p1\n    assert cobj(p1) == p3\n    assert cobj(p2) == p3\ntest_conversions()\n\n@test\ndef test_int_pow():\n    @nonpure\n    def f(n):\n        return n\n\n    assert f(3) ** f(2) == 9\n    assert f(27) ** f(7) == 10460353203\n    assert f(-27) ** f(7) == -10460353203\n    assert f(-27) ** f(6) == 387420489\n    assert f(1) ** f(0) == 1\n    assert f(1) ** f(1000) == 1\n    assert f(0) ** f(3) == 0\n    assert f(0) ** f(0) == 1\n\n    T1 = Int[512]\n    assert f(T1(3)) ** f(T1(2)) == T1(9)\n    assert f(T1(27)) ** f(T1(7)) == T1(10460353203)\n    assert f(T1(-27)) ** f(T1(7)) == T1(-10460353203)\n    assert f(T1(-27)) ** f(T1(6)) == T1(387420489)\n    assert f(T1(1)) ** f(T1(0)) == T1(1)\n    assert f(T1(1)) ** f(T1(1000)) == T1(1)\n    assert f(T1(0)) ** f(T1(3)) == T1(0)\n    assert f(T1(0)) ** f(T1(0)) == T1(1)\n    assert str(f(T1(31)) ** f(T1(31))) == '17069174130723235958610643029059314756044734431'\n    assert str(f(T1(-31)) ** f(T1(31))) == '-17069174130723235958610643029059314756044734431'\n\n    T2 = UInt[200]\n    assert f(T2(3)) ** f(T2(2)) == T2(9)\n    assert f(T2(27)) ** f(T2(7)) == T2(10460353203)\n    assert f(T2(1)) ** f(T2(0)) == T2(1)\n    assert f(T2(1)) ** f(T2(1000)) == T2(1)\n    assert f(T2(0)) ** f(T2(3)) == T2(0)\n    assert f(T2(0)) ** f(T2(0)) == T2(1)\n    assert str(f(T2(31)) ** f(T2(31))) == '17069174130723235958610643029059314756044734431'\ntest_int_pow()\n\n@test\ndef test_float(F: type):\n    x = F(5.5)\n    assert str(x) == '5.5'\n    assert F(x) == x\n    assert F() == F(0.0)\n    assert x.__copy__() == x\n    assert x.__deepcopy__() == x\n    assert int(x) == 5\n    assert float(x) == 5.5\n    assert bool(x)\n    assert not bool(F())\n    assert +x == x\n    assert -x == F(-5.5)\n    assert x + x == F(11.0)\n    assert x - F(1.0) == F(4.5)\n    assert x * F(3.0) == F(16.5)\n    assert x / F(2.0) == F(2.75)\n    if F is not float128:  # LLVM ops give wrong results for fp128\n        assert x // F(2.0) == F(2.0)\n        assert x % F(0.75) == F(0.25)\n        assert divmod(x, F(0.75)) == (F(7.0), F(0.25))\n    assert x == x\n    assert x != F()\n    assert x < F(6.5)\n    assert x > F(4.5)\n    assert x <= F(6.5)\n    assert x >= F(4.5)\n    assert x >= x\n    assert x <= x\n    assert abs(x) == x\n    assert abs(-x) == x\n    assert x.__match__(x)\n    assert not x.__match__(F())\n    assert hash(x) == hash(5.5)\n\ntest_float(float)\ntest_float(float32)\n#test_float(float16)\n#test_float(bfloat16)\n#test_float(float128)\n\n@test\ndef test_float_out_of_range_parse():\n    x = 5e-324\n    assert Ptr[int](__ptr__(x).as_byte())[0] == 1\n    assert 1e-10000 == 0.0\n    assert 1e10000 == float('inf')\n\ntest_float_out_of_range_parse()\n\n@test\ndef test_int_float_ops(F: type):\n    def check(got, exp=True):\n        return (exp == got) and (type(exp) is type(got))\n\n    # standard\n    print(F(1.5) + 1, F(2.5), type(F(1.5)+1), type(F(2.5)))\n    assert check(F(1.5) + 1, F(2.5))\n    assert check(F(1.5) - 1, F(0.5))\n    assert check(F(1.5) * 2, F(3.0))\n    assert check(F(1.5) / 2, F(0.75))\n    assert check(F(3.5) // 2, F(1.0))\n    assert check(F(3.5) % 2, F(1.5))\n    assert check(F(3.5) ** 2, F(12.25))\n    assert check(divmod(F(3.5), 2), (F(1.0), F(1.5)))\n\n    # right-hand ops\n    assert check(1 + F(1.5), F(2.5))\n    assert check(1 - F(1.5), F(-0.5))\n    assert check(2 * F(1.5), F(3.0))\n    assert check(2 / F(2.5), F(0.8))\n    assert check(2 // F(1.5), F(1.0))\n    assert check(2 % F(1.5), F(0.5))\n    assert check(4 ** F(2.5), F(32.0))\n    assert check(divmod(4, F(2.5)), (F(1.0), F(1.5)))\n\n    # comparisons\n    assert check(F(1.0) == 1)\n    assert check(F(2.0) != 1)\n    assert check(F(0.0) < 1)\n    assert check(F(2.0) > 1)\n    assert check(F(0.0) <= 1)\n    assert check(F(2.0) >= 1)\n    assert check(1 == F(1.0))\n    assert check(1 != F(2.0))\n    assert check(1 < F(2.0))\n    assert check(1 > F(0.0))\n    assert check(1 <= F(2.0))\n    assert check(1 >= F(0.0))\n\n    # power\n    assert check(F(3.5) ** 1, F(3.5))\n    assert check(F(3.5) ** 2, F(12.25))\n    assert check(F(3.5) ** 3, F(42.875))\n    assert check(F(4.0) ** -1, F(0.25))\n    assert check(F(4.0) ** -2, F(0.0625))\n    assert check(F(4.0) ** -3, F(0.015625))\n    assert check(F(3.5) ** 0, F(1.0))\n\ntest_int_float_ops(float)\ntest_int_float_ops(float32)\ntest_int_float_ops(float16)\n\n@test\ndef test_bit_length():\n    assert (0).bit_length() == 0\n    assert (1).bit_length() == 1\n    assert (2).bit_length() == 2\n    assert (3).bit_length() == 2\n    assert (4).bit_length() == 3\n    assert (7).bit_length() == 3\n    assert (8).bit_length() == 4\n    assert (42).bit_length() == 6\n    assert (123).bit_length() == 7\n\n    assert int.bit_length(-1) == 1\n    assert int.bit_length(-2) == 2\n    assert int.bit_length(-3) == 2\n    assert int.bit_length(-4) == 3\n    assert int.bit_length(-5) == 3\n    assert int.bit_length(-6) == 3\n    assert int.bit_length(-7) == 3\n    assert int.bit_length(-8) == 4\n    assert int.bit_length(-9) == 4\n\n    assert i32(0).bit_length() == 0\n    assert i32(1).bit_length() == 1\n    assert i32(2).bit_length() == 2\n    assert i32(3).bit_length() == 2\n    assert i32(4).bit_length() == 3\n    assert i32(7).bit_length() == 3\n    assert i32(8).bit_length() == 4\n    assert i32(42).bit_length() == 6\n    assert i32(123).bit_length() == 7\n\n    assert i32(-1).bit_length() == 1\n    assert i32(-2).bit_length() == 2\n    assert i32(-3).bit_length() == 2\n    assert i32(-4).bit_length() == 3\n    assert i32(-5).bit_length() == 3\n    assert i32(-6).bit_length() == 3\n    assert i32(-7).bit_length() == 3\n    assert i32(-8).bit_length() == 4\n    assert i32(-9).bit_length() == 4\n\n    assert i8(0).bit_length() == 0\n    assert i8(1).bit_length() == 1\n    assert i8(2).bit_length() == 2\n    assert i8(3).bit_length() == 2\n    assert i8(127).bit_length() == 7\n\n    assert i8(-1).bit_length() == 1\n    assert i8(-2).bit_length() == 2\n    assert i8(-3).bit_length() == 2\n    assert i8(-4).bit_length() == 3\n    assert i8(-8).bit_length() == 4\n    assert i8(-128).bit_length() == 8\n\n    assert u8(0).bit_length() == 0\n    assert u8(1).bit_length() == 1\n    assert u8(2).bit_length() == 2\n    assert u8(3).bit_length() == 2\n    assert u8(255).bit_length() == 8\n\n    assert u8(-1).bit_length() == 8\n    assert u8(-2).bit_length() == 8\n\n    assert u32(0).bit_length() == 0\n    assert u32(1).bit_length() == 1\n    assert u32(0xffffffff).bit_length() == 32\n    assert u32(0x80000000).bit_length() == 32\n    assert u32(0x7fffffff).bit_length() == 31\n    assert u32(0x00010000).bit_length() == 17\n\n    assert u32(-1).bit_length() == 32\n\n    assert u64(0).bit_length() == 0\n    assert u64(1).bit_length() == 1\n    assert u64(-1).bit_length() == 64\n    assert u64(0x8000000000000000).bit_length() == 64\n    assert u64(0x7fffffffffffffff).bit_length() == 63\n\n    # For x > 0, bit_length(x) == k implies: (x >> (k-1)) == 1 and (x >> k) == 0\n    xs = [1, 2, 3, 4, 7, 8, 9, 42, 123, 255, 256, 1023, 1024]\n    for x in xs:\n        k = int.bit_length(x)\n        assert k == 0 or ((x >> (k - 1)) == 1)\n        assert (x >> k) == 0\n\ntest_bit_length()\n\n@test\ndef test_int_pow():\n    @noinline\n    def five(): return 5\n    @noinline\n    def two(): return 2\n    @noinline\n    def zero(): return 0\n\n    assert five() ** two() == 25\n    assert type(five() ** two()) is int\n\n    assert five() ** 3 == 125\n    assert type(five() ** 3) is int\n\n    assert 5 ** two() == 25\n    assert type(5 ** two()) is int\n\n    assert 5 ** 3 == 125\n    assert type(5 ** 3) is int\n\n    assert five() ** -1 == 0.2\n    assert type(five() ** -1) is float\n\n    assert 5 ** -1 == 0.2\n    assert type(5 ** -1) is float\n\n    assert (-five()) ** zero() == 1\n    assert type(five() ** zero()) is int\n\n    assert (-five()) ** 0 == 1\n    assert type(five() ** 0) is int\n\ntest_int_pow()\n"
  },
  {
    "path": "test/core/bltin.codon",
    "content": "# Python-specific\n\n@test\ndef test_min_max():\n    neg = lambda x: -x\n\n    assert min(42, 24) == 24\n    assert max(42, 24) == 42\n    assert min([42, 24]) == 24\n    assert max([42, 24]) == 42\n    assert min(1, 2, 3, 2, 1, 0, -1, 1, 2) == -1\n    assert max(1, 2, 3, 2, 1, 0, -1, 1, 2) == 3\n    assert min([1, 2, 3, 2, 1, 0, -1, 1, 2]) == -1\n    assert max([1, 2, 3, 2, 1, 0, -1, 1, 2]) == 3\n\n    assert min(42, 24, key=neg) == 42\n    assert max(42, 24, key=neg) == 24\n    assert min([42, 24], key=neg) == 42\n    assert max([42, 24], key=neg) == 24\n    assert min(1, 2, 3, 2, 1, 0, -1, 1, 2, key=neg) == 3\n    assert max(1, 2, 3, 2, 1, 0, -1, 1, 2, key=neg) == -1\n    assert min([1, 2, 3, 2, 1, 0, -1, 1, 2], key=neg) == 3\n    assert max([1, 2, 3, 2, 1, 0, -1, 1, 2], key=neg) == -1\n\n    assert min([42, 24], default=100) == 24\n    assert max([42, 24], default=100) == 42\n    assert min([1, 2, 3, 2, 1, 0, -1, 1, 2], default=100) == -1\n    assert max([1, 2, 3, 2, 1, 0, -1, 1, 2], default=100) == 3\n\n    assert min([42, 24], default=100, key=neg) == 42\n    assert max([42, 24], default=100, key=neg) == 24\n    assert min([1, 2, 3, 2, 1, 0, -1, 1, 2], default=100, key=neg) == 3\n    assert max([1, 2, 3, 2, 1, 0, -1, 1, 2], default=100, key=neg) == -1\n\n    assert max([2, 1, 1, 1, 1]) == 2\n    assert max([1, 2, 1, 1, 1]) == 2\n    assert max([1, 1, 2, 1, 1]) == 2\n    assert max([1, 1, 1, 2, 1]) == 2\n    assert max([1, 1, 1, 1, 2]) == 2\n    assert max([2, 1, 1, 1]) == 2\n    assert max([1, 2, 1, 1]) == 2\n    assert max([1, 1, 2, 1]) == 2\n    assert max([1, 1, 1, 2]) == 2\n    assert max([2, 1, 1]) == 2\n    assert max([1, 2, 1]) == 2\n    assert max([1, 1, 2]) == 2\n    assert max([2, 1]) == 2\n    assert max([1, 2]) == 2\n\n    assert max([2, 1, 1, 1, 1], key=neg) == 1\n    assert max([1, 2, 1, 1, 1], key=neg) == 1\n    assert max([1, 1, 2, 1, 1], key=neg) == 1\n    assert max([1, 1, 1, 2, 1], key=neg) == 1\n    assert max([1, 1, 1, 1, 2], key=neg) == 1\n    assert max([2, 1, 1, 1], key=neg) == 1\n    assert max([1, 2, 1, 1], key=neg) == 1\n    assert max([1, 1, 2, 1], key=neg) == 1\n    assert max([1, 1, 1, 2], key=neg) == 1\n    assert max([2, 1, 1], key=neg) == 1\n    assert max([1, 2, 1], key=neg) == 1\n    assert max([1, 1, 2], key=neg) == 1\n    assert max([2, 1], key=neg) == 1\n    assert max([1, 2], key=neg) == 1\n\n    assert min([2, 1, 1, 1, 1]) == 1\n    assert min([1, 2, 1, 1, 1]) == 1\n    assert min([1, 1, 2, 1, 1]) == 1\n    assert min([1, 1, 1, 2, 1]) == 1\n    assert min([1, 1, 1, 1, 2]) == 1\n    assert min([2, 1, 1, 1]) == 1\n    assert min([1, 2, 1, 1]) == 1\n    assert min([1, 1, 2, 1]) == 1\n    assert min([1, 1, 1, 2]) == 1\n    assert min([2, 1, 1]) == 1\n    assert min([1, 2, 1]) == 1\n    assert min([1, 1, 2]) == 1\n    assert min([2, 1]) == 1\n    assert min([1, 2]) == 1\n\n    assert min([2, 1, 1, 1, 1], key=neg) == 2\n    assert min([1, 2, 1, 1, 1], key=neg) == 2\n    assert min([1, 1, 2, 1, 1], key=neg) == 2\n    assert min([1, 1, 1, 2, 1], key=neg) == 2\n    assert min([1, 1, 1, 1, 2], key=neg) == 2\n    assert min([2, 1, 1, 1], key=neg) == 2\n    assert min([1, 2, 1, 1], key=neg) == 2\n    assert min([1, 1, 2, 1], key=neg) == 2\n    assert min([1, 1, 1, 2], key=neg) == 2\n    assert min([2, 1, 1], key=neg) == 2\n    assert min([1, 2, 1], key=neg) == 2\n    assert min([1, 1, 2], key=neg) == 2\n    assert min([2, 1], key=neg) == 2\n    assert min([1, 2], key=neg) == 2\n\n    assert max([0, 1, 1, 1, 1]) == 1\n    assert max([1, 0, 1, 1, 1]) == 1\n    assert max([1, 1, 0, 1, 1]) == 1\n    assert max([1, 1, 1, 0, 1]) == 1\n    assert max([1, 1, 1, 1, 0]) == 1\n    assert max([0, 1, 1, 1]) == 1\n    assert max([1, 0, 1, 1]) == 1\n    assert max([1, 1, 0, 1]) == 1\n    assert max([1, 1, 1, 0]) == 1\n    assert max([0, 1, 1]) == 1\n    assert max([1, 0, 1]) == 1\n    assert max([1, 1, 0]) == 1\n    assert max([0, 1]) == 1\n    assert max([1, 0]) == 1\n\n    assert max([0, 1, 1, 1, 1], key=neg) == 0\n    assert max([1, 0, 1, 1, 1], key=neg) == 0\n    assert max([1, 1, 0, 1, 1], key=neg) == 0\n    assert max([1, 1, 1, 0, 1], key=neg) == 0\n    assert max([1, 1, 1, 1, 0], key=neg) == 0\n    assert max([0, 1, 1, 1], key=neg) == 0\n    assert max([1, 0, 1, 1], key=neg) == 0\n    assert max([1, 1, 0, 1], key=neg) == 0\n    assert max([1, 1, 1, 0], key=neg) == 0\n    assert max([0, 1, 1], key=neg) == 0\n    assert max([1, 0, 1], key=neg) == 0\n    assert max([1, 1, 0], key=neg) == 0\n    assert max([0, 1], key=neg) == 0\n    assert max([1, 0], key=neg) == 0\n\n    assert min([0, 1, 1, 1, 1]) == 0\n    assert min([1, 0, 1, 1, 1]) == 0\n    assert min([1, 1, 0, 1, 1]) == 0\n    assert min([1, 1, 1, 0, 1]) == 0\n    assert min([1, 1, 1, 1, 0]) == 0\n    assert min([0, 1, 1, 1]) == 0\n    assert min([1, 0, 1, 1]) == 0\n    assert min([1, 1, 0, 1]) == 0\n    assert min([1, 1, 1, 0]) == 0\n    assert min([0, 1, 1]) == 0\n    assert min([1, 0, 1]) == 0\n    assert min([1, 1, 0]) == 0\n    assert min([0, 1]) == 0\n    assert min([1, 0]) == 0\n\n    assert min([0, 1, 1, 1, 1], key=neg) == 1\n    assert min([1, 0, 1, 1, 1], key=neg) == 1\n    assert min([1, 1, 0, 1, 1], key=neg) == 1\n    assert min([1, 1, 1, 0, 1], key=neg) == 1\n    assert min([1, 1, 1, 1, 0], key=neg) == 1\n    assert min([0, 1, 1, 1], key=neg) == 1\n    assert min([1, 0, 1, 1], key=neg) == 1\n    assert min([1, 1, 0, 1], key=neg) == 1\n    assert min([1, 1, 1, 0], key=neg) == 1\n    assert min([0, 1, 1], key=neg) == 1\n    assert min([1, 0, 1], key=neg) == 1\n    assert min([1, 1, 0], key=neg) == 1\n    assert min([0, 1], key=neg) == 1\n    assert min([1, 0], key=neg) == 1\n\n    assert min([0, 1, 1, 1, 1], default=99) == 0\n    assert min([1, 0, 1, 1, 1], default=99) == 0\n    assert min([1, 1, 0, 1, 1], default=99) == 0\n    assert min([1, 1, 1, 0, 1], default=99) == 0\n    assert min([1, 1, 1, 1, 0], default=99) == 0\n    assert min([0, 1, 1, 1], default=99) == 0\n    assert min([1, 0, 1, 1], default=99) == 0\n    assert min([1, 1, 0, 1], default=99) == 0\n    assert min([1, 1, 1, 0], default=99) == 0\n    assert min([0, 1, 1], default=99) == 0\n    assert min([1, 0, 1], default=99) == 0\n    assert min([1, 1, 0], default=99) == 0\n    assert min([0, 1], default=99) == 0\n    assert min([1, 0], default=99) == 0\n    assert min(List[int](), default=99) == 99\n\n    assert min([0, 1, 1, 1, 1], key=neg, default=99) == 1\n    assert min([1, 0, 1, 1, 1], key=neg, default=99) == 1\n    assert min([1, 1, 0, 1, 1], key=neg, default=99) == 1\n    assert min([1, 1, 1, 0, 1], key=neg, default=99) == 1\n    assert min([1, 1, 1, 1, 0], key=neg, default=99) == 1\n    assert min([0, 1, 1, 1], key=neg, default=99) == 1\n    assert min([1, 0, 1, 1], key=neg, default=99) == 1\n    assert min([1, 1, 0, 1], key=neg, default=99) == 1\n    assert min([1, 1, 1, 0], key=neg, default=99) == 1\n    assert min([0, 1, 1], key=neg, default=99) == 1\n    assert min([1, 0, 1], key=neg, default=99) == 1\n    assert min([1, 1, 0], key=neg, default=99) == 1\n    assert min([0, 1], key=neg, default=99) == 1\n    assert min([1, 0], key=neg, default=99) == 1\n    assert min(List[int](), key=neg, default=99) == 99\n\n    assert max([0, 1, 1, 1, 1], default=99) == 1\n    assert max([1, 0, 1, 1, 1], default=99) == 1\n    assert max([1, 1, 0, 1, 1], default=99) == 1\n    assert max([1, 1, 1, 0, 1], default=99) == 1\n    assert max([1, 1, 1, 1, 0], default=99) == 1\n    assert max([0, 1, 1, 1], default=99) == 1\n    assert max([1, 0, 1, 1], default=99) == 1\n    assert max([1, 1, 0, 1], default=99) == 1\n    assert max([1, 1, 1, 0], default=99) == 1\n    assert max([0, 1, 1], default=99) == 1\n    assert max([1, 0, 1], default=99) == 1\n    assert max([1, 1, 0], default=99) == 1\n    assert max([0, 1], default=99) == 1\n    assert max([1, 0], default=99) == 1\n    assert max(List[int](), default=99) == 99\n\n    assert max([0, 1, 1, 1, 1], key=neg, default=99) == 0\n    assert max([1, 0, 1, 1, 1], key=neg, default=99) == 0\n    assert max([1, 1, 0, 1, 1], key=neg, default=99) == 0\n    assert max([1, 1, 1, 0, 1], key=neg, default=99) == 0\n    assert max([1, 1, 1, 1, 0], key=neg, default=99) == 0\n    assert max([0, 1, 1, 1], key=neg, default=99) == 0\n    assert max([1, 0, 1, 1], key=neg, default=99) == 0\n    assert max([1, 1, 0, 1], key=neg, default=99) == 0\n    assert max([1, 1, 1, 0], key=neg, default=99) == 0\n    assert max([0, 1, 1], key=neg, default=99) == 0\n    assert max([1, 0, 1], key=neg, default=99) == 0\n    assert max([1, 1, 0], key=neg, default=99) == 0\n    assert max([0, 1], key=neg, default=99) == 0\n    assert max([1, 0], key=neg, default=99) == 0\n    assert max(List[int](), key=neg, default=99) == 99\n\n    assert max(2, 1, 1, 1, 1) == 2\n    assert max(1, 2, 1, 1, 1) == 2\n    assert max(1, 1, 2, 1, 1) == 2\n    assert max(1, 1, 1, 2, 1) == 2\n    assert max(1, 1, 1, 1, 2) == 2\n    assert max(2, 1, 1, 1) == 2\n    assert max(1, 2, 1, 1) == 2\n    assert max(1, 1, 2, 1) == 2\n    assert max(1, 1, 1, 2) == 2\n    assert max(2, 1, 1) == 2\n    assert max(1, 2, 1) == 2\n    assert max(1, 1, 2) == 2\n    assert max(2, 1) == 2\n    assert max(1, 2) == 2\n\n    assert max(2, 1, 1, 1, 1, key=neg) == 1\n    assert max(1, 2, 1, 1, 1, key=neg) == 1\n    assert max(1, 1, 2, 1, 1, key=neg) == 1\n    assert max(1, 1, 1, 2, 1, key=neg) == 1\n    assert max(1, 1, 1, 1, 2, key=neg) == 1\n    assert max(2, 1, 1, 1, key=neg) == 1\n    assert max(1, 2, 1, 1, key=neg) == 1\n    assert max(1, 1, 2, 1, key=neg) == 1\n    assert max(1, 1, 1, 2, key=neg) == 1\n    assert max(2, 1, 1, key=neg) == 1\n    assert max(1, 2, 1, key=neg) == 1\n    assert max(1, 1, 2, key=neg) == 1\n    assert max(2, 1, key=neg) == 1\n    assert max(1, 2, key=neg) == 1\n\n    assert min(2, 1, 1, 1, 1) == 1\n    assert min(1, 2, 1, 1, 1) == 1\n    assert min(1, 1, 2, 1, 1) == 1\n    assert min(1, 1, 1, 2, 1) == 1\n    assert min(1, 1, 1, 1, 2) == 1\n    assert min(2, 1, 1, 1) == 1\n    assert min(1, 2, 1, 1) == 1\n    assert min(1, 1, 2, 1) == 1\n    assert min(1, 1, 1, 2) == 1\n    assert min(2, 1, 1) == 1\n    assert min(1, 2, 1) == 1\n    assert min(1, 1, 2) == 1\n    assert min(2, 1) == 1\n    assert min(1, 2) == 1\n\n    assert min(2, 1, 1, 1, 1, key=neg) == 2\n    assert min(1, 2, 1, 1, 1, key=neg) == 2\n    assert min(1, 1, 2, 1, 1, key=neg) == 2\n    assert min(1, 1, 1, 2, 1, key=neg) == 2\n    assert min(1, 1, 1, 1, 2, key=neg) == 2\n    assert min(2, 1, 1, 1, key=neg) == 2\n    assert min(1, 2, 1, 1, key=neg) == 2\n    assert min(1, 1, 2, 1, key=neg) == 2\n    assert min(1, 1, 1, 2, key=neg) == 2\n    assert min(2, 1, 1, key=neg) == 2\n    assert min(1, 2, 1, key=neg) == 2\n    assert min(1, 1, 2, key=neg) == 2\n    assert min(2, 1, key=neg) == 2\n    assert min(1, 2, key=neg) == 2\n\n    assert max(0, 1, 1, 1, 1) == 1\n    assert max(1, 0, 1, 1, 1) == 1\n    assert max(1, 1, 0, 1, 1) == 1\n    assert max(1, 1, 1, 0, 1) == 1\n    assert max(1, 1, 1, 1, 0) == 1\n    assert max(0, 1, 1, 1) == 1\n    assert max(1, 0, 1, 1) == 1\n    assert max(1, 1, 0, 1) == 1\n    assert max(1, 1, 1, 0) == 1\n    assert max(0, 1, 1) == 1\n    assert max(1, 0, 1) == 1\n    assert max(1, 1, 0) == 1\n    assert max(0, 1) == 1\n    assert max(1, 0) == 1\n\n    assert max(0, 1, 1, 1, 1, key=neg) == 0\n    assert max(1, 0, 1, 1, 1, key=neg) == 0\n    assert max(1, 1, 0, 1, 1, key=neg) == 0\n    assert max(1, 1, 1, 0, 1, key=neg) == 0\n    assert max(1, 1, 1, 1, 0, key=neg) == 0\n    assert max(0, 1, 1, 1, key=neg) == 0\n    assert max(1, 0, 1, 1, key=neg) == 0\n    assert max(1, 1, 0, 1, key=neg) == 0\n    assert max(1, 1, 1, 0, key=neg) == 0\n    assert max(0, 1, 1, key=neg) == 0\n    assert max(1, 0, 1, key=neg) == 0\n    assert max(1, 1, 0, key=neg) == 0\n    assert max(0, 1, key=neg) == 0\n    assert max(1, 0, key=neg) == 0\n\n    assert min(0, 1, 1, 1, 1) == 0\n    assert min(1, 0, 1, 1, 1) == 0\n    assert min(1, 1, 0, 1, 1) == 0\n    assert min(1, 1, 1, 0, 1) == 0\n    assert min(1, 1, 1, 1, 0) == 0\n    assert min(0, 1, 1, 1) == 0\n    assert min(1, 0, 1, 1) == 0\n    assert min(1, 1, 0, 1) == 0\n    assert min(1, 1, 1, 0) == 0\n    assert min(0, 1, 1) == 0\n    assert min(1, 0, 1) == 0\n    assert min(1, 1, 0) == 0\n    assert min(0, 1) == 0\n    assert min(1, 0) == 0\n\n    assert min(0, 1, 1, 1, 1, key=neg) == 1\n    assert min(1, 0, 1, 1, 1, key=neg) == 1\n    assert min(1, 1, 0, 1, 1, key=neg) == 1\n    assert min(1, 1, 1, 0, 1, key=neg) == 1\n    assert min(1, 1, 1, 1, 0, key=neg) == 1\n    assert min(0, 1, 1, 1, key=neg) == 1\n    assert min(1, 0, 1, 1, key=neg) == 1\n    assert min(1, 1, 0, 1, key=neg) == 1\n    assert min(1, 1, 1, 0, key=neg) == 1\n    assert min(0, 1, 1, key=neg) == 1\n    assert min(1, 0, 1, key=neg) == 1\n    assert min(1, 1, 0, key=neg) == 1\n    assert min(0, 1, key=neg) == 1\n    assert min(1, 0, key=neg) == 1\n\n    assert min(a*a for a in range(3)) == 0\n    assert max(a*a for a in range(3)) == 4\n    assert min([0, 2, -1]) == -1\n    assert max([0, 2, -1]) == 2\n\n    assert min((a*a for a in range(3)), key=neg) == 4\n    assert max((a*a for a in range(3)), key=neg) == 0\n    assert min([0, 2, -1], key=neg) == 2\n    assert max([0, 2, -1], key=neg) == -1\n\n    assert min('abcx') == 'a'\n    assert max('abcx') == 'x'\n    assert min(['a', 'b', 'c', 'x']) == 'a'\n    assert max(['a', 'b', 'c', 'x']) == 'x'\n\n    d = {'a': 4, 'b': 1, 'c': -1, 'x': 9}\n    assert min('abcx', key=d.__getitem__) == 'c'\n    assert max('abcx', key=d.__getitem__) == 'x'\n    assert min(['a', 'b', 'c', 'x'], key=d.__getitem__) == 'c'\n    assert max(['a', 'b', 'c', 'x'], key=d.__getitem__) == 'x'\n\n    try:\n        max('')\n        assert False\n    except ValueError as e:\n        assert str(e) == 'max() arg is an empty sequence'\n\n    try:\n        min('')\n        assert False\n    except ValueError as e:\n        assert str(e) == 'min() arg is an empty sequence'\n\n    try:\n        max(List[float]())\n        assert False\n    except ValueError as e:\n        assert str(e) == 'max() arg is an empty sequence'\n\n    try:\n        min(List[float]())\n        assert False\n    except ValueError as e:\n        assert str(e) == 'min() arg is an empty sequence'\n\n    try:\n        max('', key=lambda x: x * 2)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'max() arg is an empty sequence'\n\n    try:\n        min('', key=lambda x: x * 2)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'min() arg is an empty sequence'\n\n    try:\n        max(List[float](), key=lambda x: x * 2)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'max() arg is an empty sequence'\n\n    try:\n        min(List[float](), key=lambda x: x * 2)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'min() arg is an empty sequence'\n\n@test\ndef test_map_filter():\n    assert list(map(lambda i: i+1, (i*2 for i in range(5)))) == [1, 3, 5, 7, 9]\n    assert list(map(lambda i: i+1, (i*2 for i in range(0)))) == []\n    assert list(map(lambda i: i//2, map(lambda i: i-1, map(lambda i: i+1, (i*2 for i in range(5)))))) == [0, 1, 2, 3, 4]\n    def f(x: int) -> int:\n        return x - 1\n    def g(x: int) -> int:\n        return x + 1\n    assert list(map(f, map(g, (i*2 for i in range(5))))) == [0, 2, 4, 6, 8]\n\n    def h(x: list[int]):\n        return x\n    assert h(list(map(lambda i: i-1, map(lambda i: i+1, range(5))))) == [0, 1, 2, 3, 4]\n\n    assert list(filter(lambda i: i % 2 == 0, range(5))) == [0, 2, 4]\n    assert list(filter(lambda i: i % 2 == 1, filter(lambda i: i % 2 == 0, range(5)))) == []\n\n    assert list(filter(lambda i: i%2 == 0, map(lambda i: i*i, range(10)))) == [0, 4, 16, 36, 64]\n\n@test\ndef test_gen_builtins():\n    assert sum([1, 2, 3]) == 6\n    assert sum([1, 2, 3], 0.5) == 6.5\n    assert sum([True, False, True, False, True], 0.5) == 3.5\n    assert sum(List[float]()) == 0\n    assert sum(i/2 for i in range(10)) == 22.5\n\n    def g1():\n        yield 1.5\n        yield 2.5\n        return\n        yield 3.5\n\n    assert sum(g1(), 10) == 14.0\n\n    def g2():\n        yield True\n        yield False\n        yield True\n\n    assert sum(g2()) == 2\n\n    class A:\n        iadd_count = 0\n        n: int\n\n        def __init__(self, n):\n            self.n = n\n\n        def __add__(self, other):\n            return A(self.n + other.n)\n\n        def __iadd__(self, other):\n            A.iadd_count += 1\n            self.n += other.n\n            return self\n\n    assert sum((A(i) for i in range(5)), A(100)).n == 110\n    assert A.iadd_count == 0\n\n    def g3(a, b):\n        for i in range(10):\n            yield a\n        yield b\n\n    assert all([True, True])\n    assert all(i for i in range(0))\n    assert not all([True, False])\n    assert all(List[str]())\n    assert all(g3(True, True))\n    assert not all(g3(True, False))\n    assert not all(g3(False, True))\n    assert not all(g3(False, False))\n\n    assert any([True, True])\n    assert not any(i for i in range(0))\n    assert not any([False, False])\n    assert not any(List[bool]())\n    assert any(g3(True, True))\n    assert any(g3(True, False))\n    assert any(g3(False, True))\n    assert not any(g3(False, False))\n\n@test\ndef test_int_format():\n    n = 0\n    assert (str(n), bin(n), oct(n), hex(n)) == ('0', '0b0', '0o0', '0x0')\n\n    n = -1\n    assert (str(n), bin(n), oct(n), hex(n)) == ('-1', '-0b1', '-0o1', '-0x1')\n\n    n = 12345\n    assert (str(n), bin(n), oct(n), hex(n)) == ('12345', '0b11000000111001', '0o30071', '0x3039')\n\n    n = -12345\n    assert (str(n), bin(n), oct(n), hex(n)) == ('-12345', '-0b11000000111001', '-0o30071', '-0x3039')\n\n    # this one is different than Python due to 64-bit ints\n    n = 0x8000000000000000\n    assert (str(n), bin(n), oct(n), hex(n)) == ('-9223372036854775808', '-0b1000000000000000000000000000000000000000000000000000000000000000', '-0o1000000000000000000000', '-0x8000000000000000')\n\n    n = 0x7fffffffffffffff\n    assert (str(n), bin(n), oct(n), hex(n)) == ('9223372036854775807', '0b111111111111111111111111111111111111111111111111111111111111111', '0o777777777777777777777', '0x7fffffffffffffff')\n\n    m = i32(0)\n    assert (str(m), bin(m), oct(m), hex(m)) == ('0', '0b0', '0o0', '0x0')\n\n    m = i32(-1)\n    assert (str(m), bin(m), oct(m), hex(m)) == ('-1', '-0b1', '-0o1', '-0x1')\n\n    m = i32(12345)\n    assert (str(m), bin(m), oct(m), hex(m)) == ('12345', '0b11000000111001', '0o30071', '0x3039')\n\n    m = i32(-12345)\n    assert (str(m), bin(m), oct(m), hex(m)) == ('-12345', '-0b11000000111001', '-0o30071', '-0x3039')\n\n    k = Int[128](0)\n    assert (str(k), bin(k), oct(k), hex(k)) == ('0', '0b0', '0o0', '0x0')\n\n    k = Int[128](-1)\n    assert (str(k), bin(k), oct(k), hex(k)) == ('-1', '-0b1', '-0o1', '-0x1')\n\n    k = Int[128](12345)\n    assert (str(k), bin(k), oct(k), hex(k)) == ('12345', '0b11000000111001', '0o30071', '0x3039')\n\n    k = Int[128](-12345)\n    assert (str(k), bin(k), oct(k), hex(k)) == ('-12345', '-0b11000000111001', '-0o30071', '-0x3039')\n\n    # this one is different than Python due to 64-bit ints\n    k = Int[128](0x8000000000000000)\n    assert (str(k), bin(k), oct(k), hex(k)) == ('-9223372036854775808', '-0b1000000000000000000000000000000000000000000000000000000000000000', '-0o1000000000000000000000', '-0x8000000000000000')\n\n    k = Int[128](0x7fffffffffffffff)\n    assert (str(k), bin(k), oct(k), hex(k)) == ('9223372036854775807', '0b111111111111111111111111111111111111111111111111111111111111111', '0o777777777777777777777', '0x7fffffffffffffff')\n\n@test\ndef test_complex_format():\n    import math\n\n    assert str(complex(1.1, 2.2)) == '(1.1+2.2j)'\n    assert str(complex(11.0, -22.0)) == '(11-22j)'\n    assert str(complex(-111.0, 222.0)) == '(-111+222j)'\n    assert str(complex(-1111.0, -2222.0)) == '(-1111-2222j)'\n    assert str(complex(1.0, 0.0)) == '(1+0j)'\n    assert str(complex(0.0, 1.0)) == '1j'\n    assert str(complex(-0.0, 1.0)) == '(-0+1j)'\n    assert str(complex(0.0, -1.0)) == '-1j'\n    assert str(complex(-0.0, -1.0)) == '(-0-1j)'\n    assert str(complex(0.0, 0.0)) == '0j'\n    assert str(complex(0.0, -0.0)) == '-0j'\n    assert str(complex(-0.0, 0.0)) == '(-0+0j)'\n    assert str(complex(-0.0, -0.0)) == '(-0-0j)'\n    assert str(complex(math.inf, math.inf)) == '(inf+infj)'\n    assert str(complex(-math.inf, math.nan)) == '(-inf+nanj)'\n    assert str(complex(math.nan, -math.inf)) == '(nan-infj)'\n    assert str(complex(math.nan, math.nan)) == '(nan+nanj)'\n\n    assert repr(complex(1.1, 2.2)) == '(1.1+2.2j)'\n    assert repr(complex(11.0, -22.0)) == '(11-22j)'\n    assert repr(complex(-111.0, 222.0)) == '(-111+222j)'\n    assert repr(complex(-1111.0, -2222.0)) == '(-1111-2222j)'\n    assert repr(complex(1.0, 0.0)) == '(1+0j)'\n    assert repr(complex(0.0, 1.0)) == '1j'\n    assert repr(complex(-0.0, 1.0)) == '(-0+1j)'\n    assert repr(complex(0.0, -1.0)) == '-1j'\n    assert repr(complex(-0.0, -1.0)) == '(-0-1j)'\n    assert repr(complex(0.0, 0.0)) == '0j'\n    assert repr(complex(0.0, -0.0)) == '-0j'\n    assert repr(complex(-0.0, 0.0)) == '(-0+0j)'\n    assert repr(complex(-0.0, -0.0)) == '(-0-0j)'\n    assert repr(complex(math.inf, math.inf)) == '(inf+infj)'\n    assert repr(complex(-math.inf, math.nan)) == '(-inf+nanj)'\n    assert repr(complex(math.nan, -math.inf)) == '(nan-infj)'\n    assert repr(complex(math.nan, math.nan)) == '(nan+nanj)'\n\n    assert str(complex64(1.1, 2.2)) == '(1.1+2.2j)'\n    assert str(complex64(11.0, -22.0)) == '(11-22j)'\n    assert str(complex64(-111.0, 222.0)) == '(-111+222j)'\n    assert str(complex64(-1111.0, -2222.0)) == '(-1111-2222j)'\n    assert str(complex64(1.0, 0.0)) == '(1+0j)'\n    assert str(complex64(0.0, 1.0)) == '1j'\n    assert str(complex64(-0.0, 1.0)) == '(-0+1j)'\n    assert str(complex64(0.0, -1.0)) == '-1j'\n    assert str(complex64(-0.0, -1.0)) == '(-0-1j)'\n    assert str(complex64(0.0, 0.0)) == '0j'\n    assert str(complex64(0.0, -0.0)) == '-0j'\n    assert str(complex64(-0.0, 0.0)) == '(-0+0j)'\n    assert str(complex64(-0.0, -0.0)) == '(-0-0j)'\n    assert str(complex64(math.inf, math.inf)) == '(inf+infj)'\n    assert str(complex64(-math.inf, math.nan)) == '(-inf+nanj)'\n    assert str(complex64(math.nan, -math.inf)) == '(nan-infj)'\n    assert str(complex64(math.nan, math.nan)) == '(nan+nanj)'\n\n    assert repr(complex64(1.1, 2.2)) == 'complex64(1.1+2.2j)'\n    assert repr(complex64(11.0, -22.0)) == 'complex64(11-22j)'\n    assert repr(complex64(-111.0, 222.0)) == 'complex64(-111+222j)'\n    assert repr(complex64(-1111.0, -2222.0)) == 'complex64(-1111-2222j)'\n    assert repr(complex64(1.0, 0.0)) == 'complex64(1+0j)'\n    assert repr(complex64(0.0, 1.0)) == 'complex64(1j)'\n    assert repr(complex64(-0.0, 1.0)) == 'complex64(-0+1j)'\n    assert repr(complex64(0.0, -1.0)) == 'complex64(-1j)'\n    assert repr(complex64(-0.0, -1.0)) == 'complex64(-0-1j)'\n    assert repr(complex64(0.0, 0.0)) == 'complex64(0j)'\n    assert repr(complex64(0.0, -0.0)) == 'complex64(-0j)'\n    assert repr(complex64(-0.0, 0.0)) == 'complex64(-0+0j)'\n    assert repr(complex64(-0.0, -0.0)) == 'complex64(-0-0j)'\n    assert repr(complex64(math.inf, math.inf)) == 'complex64(inf+infj)'\n    assert repr(complex64(-math.inf, math.nan)) == 'complex64(-inf+nanj)'\n    assert repr(complex64(math.nan, -math.inf)) == 'complex64(nan-infj)'\n    assert repr(complex64(math.nan, math.nan)) == 'complex64(nan+nanj)'\n\n@test\ndef test_alt_types_from_str():\n    assert float32('3.14') == float32(3.14)\n    # TODO: fails on manylinux w/ \"Symbols not found: [ __truncdfhf2, __extendhfsf2 ]\"\n    # assert float16('3.14') == float16(3.14)\n    assert complex64('1+2j') == complex64(1+2j)\n\nclass A:\n    def __len__(self):\n        return 42\n    def __getitem__(self, idx):\n        return idx\n\n@test\ndef test_reversed():\n    assert list(reversed([1,2,3])) == [3,2,1]\n    assert list(reversed('abc')) == ['c','b','a']\n    assert list(reversed('')) == []\n    assert list(reversed(A())) == list(reversed(range(42)))\n\n@test\ndef test_divmod():\n    import sys, math\n    assert divmod(12, 7) == (1, 5)\n    assert divmod(-12, 7) == (-2, 2)\n    assert divmod(12, -7) == (-2, -2)\n    assert divmod(-12, -7) == (1, -5)\n\n    assert divmod(i32(12), i32(7)) == (i32(1), i32(5))\n    assert divmod(i32(-12), i32(7)) == (i32(-2), i32(2))\n    assert divmod(i32(12), i32(-7)) == (i32(-2), i32(-2))\n    assert divmod(i32(-12), i32(-7)) == (i32(1), i32(-5))\n\n    assert divmod(u32(12), u32(7)) == (u32(1), u32(5))\n\n    i128 = Int[128]\n    assert divmod(i128(12), i128(7)) == (i128(1), i128(5))\n    assert divmod(i128(-12), i128(7)) == (i128(-2), i128(2))\n    assert divmod(i128(12), i128(-7)) == (i128(-2), i128(-2))\n    assert divmod(i128(-12), i128(-7)) == (i128(1), i128(-5))\n\n    class X:\n        n: int\n        def __floordiv__(self, other: X):\n            return X(self.n // other.n)\n        def __mod__(self, other: X):\n            return X(self.n % other.n)\n        def __eq__(self, other: X):\n            return self.n == other.n\n        def __nq__(self, other: X):\n            return self.n != other.n\n    assert divmod(X(12), X(7)) == (X(1), X(5))\n\n    # following is invalid in our case due to 64-bit ints\n    # print divmod(-sys.maxsize-1, -1), (sys.maxsize+1, 0)\n\n    for num, denom, exp_result in [ (3.25, 1.0, (3.0, 0.25)),\n                                    (-3.25, 1.0, (-4.0, 0.75)),\n                                    (3.25, -1.0, (-4.0, -0.75)),\n                                    (-3.25, -1.0, (3.0, -0.25))]:\n        result = divmod(num, denom)\n        assert math.isclose(result[0], exp_result[0])\n        assert math.isclose(result[1], exp_result[1])\n\n@test\ndef test_pow():\n    assert pow(3, 4) == 81\n    assert pow(-3, 3) == -27\n    assert pow(1, 0) == 1\n    assert pow(-1, 0) == 1\n    assert pow(0, 0) == 1\n    assert pow(12, 12, 42) == 36\n    assert pow(1234, 4321, 99) == 46\n    assert pow(9999, 9999, 2) == 1\n    assert pow(0, 0, 1) == 0\n\n    try:\n        pow(1, -1, 2)\n        assert False\n    except ValueError as e:\n        assert 'negative' in str(e)\n\n    try:\n        pow(1, 1, 0)\n        assert False\n    except ValueError as e:\n        assert 'cannot be 0' in str(e)\n\n    assert pow(1.5, 2) == 2.25\n    assert pow(9, 0.5) == 3.0\n    assert pow(2.0, -1.0) == 0.5\n\n@test\ndef test_num_from_str():\n    import math\n\n    assert int('0') == 0\n    assert int('010') == 10\n    assert int('3\\n') == 3\n    assert int('\\r\\t\\n 42\\r\\t\\n ') == 42\n    assert int('0101', 2) == 5\n    assert int('-0101', 2) == -5\n    assert int('0111', 8) == 73\n    assert int('-0111', 8) == -73\n    assert int('-0xabc', 16) == -2748\n    assert int('0xabc', 16) == 2748\n    assert int('-0xabc', 16) == -2748\n    assert int('111', 0) == 111\n    assert int('-111', 0) == -111\n    assert int('-0xabc', 0) == -2748\n    assert int('0xabc', 0) == 2748\n    assert int('-0xabc', 0) == -2748\n\n    try:\n        int('  10  a')\n        assert False\n    except ValueError as e:\n        assert str(e) == \"invalid literal for int() with base 10: '  10  a'\"\n\n    try:\n        int('')\n        assert False\n    except ValueError as e:\n        assert str(e) == \"invalid literal for int() with base 10: ''\"\n\n    # new tests for PR #561\n    assert int(\"123\") == 123\n    assert int(\"-123\") == -123\n    assert int(\"+123\") == 123\n    assert int(\"0\") == 0\n    assert int(\"-0\") == 0\n    assert int(\"0000123\") == 123\n    assert int(\"-0000123\") == -123\n    assert int(\"+0000123\") == 123\n    assert int(\"123 \") == 123\n    assert int(\" 123\") == 123\n    assert int(\" 123 \") == 123\n\n    assert int(\"1101\", 2) == 13\n    assert int(\"-1101\", 2) == -13\n    assert int(\"+1101\", 2) == 13\n    assert int(\"0b1101\", 2) == 13\n    assert int(\"-0b1101\", 2) == -13\n    assert int(\"+0b1101\", 2) == 13\n    assert int(\"00001101\", 2) == 13\n    assert int(\"-00001101\", 2) == -13\n    assert int(\"0\", 2) == 0\n\n    assert int(\"123\", 8) == 83\n    assert int(\"-123\", 8) == -83\n    assert int(\"+123\", 8) == 83\n    assert int(\"0o123\", 8) == 83\n    assert int(\"-0o123\", 8) == -83\n    assert int(\"+0o123\", 8) == 83\n    assert int(\"0000123\", 8) == 83\n    assert int(\"-0000123\", 8) == -83\n    assert int(\"0\", 8) == 0\n\n    assert int(\"1A\", 16) == 26\n    assert int(\"-1A\", 16) == -26\n    assert int(\"+1A\", 16) == 26\n    assert int(\"0x1A\", 16) == 26\n    assert int(\"-0x1A\", 16) == -26\n    assert int(\"+0x1A\", 16) == 26\n    assert int(\"00001A\", 16) == 26\n    assert int(\"-00001A\", 16) == -26\n    assert int(\"0\", 16) == 0\n\n    assert int(\"Z\", 36) == 35\n    assert int(\"-Z\", 36) == -35\n    assert int(\"+Z\", 36) == 35\n    assert int(\"0\", 36) == 0\n    assert int(\"10\", 36) == 36\n    assert int(\"000Z\", 36) == 35\n    assert int(\"-000Z\", 36) == -35\n    assert int(\"+000Z\", 36) == 35\n\n    assert int(\"123\", 0) == 123\n    assert int(\"-123\", 0) == -123\n    assert int(\"+123\", 0) == 123\n    assert int(\"0b1101\", 0) == 13\n    assert int(\"-0b1101\", 0) == -13\n    assert int(\"+0b1101\", 0) == 13\n    assert int(\"0o123\", 0) == 83\n    assert int(\"-0o123\", 0) == -83\n    assert int(\"+0o123\", 0) == 83\n    assert int(\"0x1A\", 0) == 26\n    assert int(\"-0x1A\", 0) == -26\n    assert int(\" +0x1a\\n\", 0) == 26\n\n    try:\n        int(\"1A\", 10)\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        int(\"0b1101\", 10)\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        int(\"0o123\", 10)\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        int(\"0x1A\", 10)\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        int(\"Z\", 10)\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        int(\"123\", 1)\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        int(\"123\", 37)\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        int(\"123\", -10)\n        assert False\n    except ValueError:\n        pass\n\n    assert int(\"\\n123\\n\") == 123\n    assert int(\"\\t123\\t\") == 123\n    assert int(\"   123   \") == 123\n    assert int(\"+0x7FFFFFFF\", 0) == 0x7FFFFFFF\n    assert int(\"-0x80000000\", 0) == -0x80000000\n    assert int(\"+0o37777777777\", 0) == 4294967295  # 0o37777777777\n    assert int(\"-0o40000000000\", 0) == -4294967296  # -0o40000000000\n    assert int(\"+0b1111111111111111111111111111111\", 0) == 0b1111111111111111111111111111111\n    assert int(\"-0b10000000000000000000000000000000\", 0) == -0b10000000000000000000000000000000\n\n    assert float('0') == 0\n    assert float('3.14') == 3.14\n    assert float('3\\n') == 3\n    assert float('\\r\\t\\n -4.2\\r\\t\\n ') == -4.2\n    assert math.isnan(float(' nan '))\n    assert math.isnan(float(' +nan '))\n    assert math.isnan(float(' -nan '))\n    assert math.isinf(float(' inf '))\n    assert math.isinf(float(' +inf '))\n    assert math.isinf(float(' -inf '))\n    assert math.isinf(float(' infinity '))\n    assert math.isinf(float(' +infinity '))\n    assert math.isinf(float(' -infinity '))\n    assert float(' inf ') > 0.0\n    assert float(' +inf ') > 0.0\n    assert float(' -inf ') < 0.0\n    assert float(' infinity ') > 0.0\n    assert float(' +infinity ') > 0.0\n    assert float(' -infinity ') < 0.0\n\n    try:\n        float('  3.14  a')\n        assert False\n    except ValueError as e:\n        assert str(e) == \"could not convert string to float: '  3.14  a'\"\n\n    try:\n        float('')\n        assert False\n    except ValueError as e:\n        assert str(e) == \"could not convert string to float: ''\"\n\n@test\ndef test_files(open_fn, append_allowed: bool = True):\n    path = 'build/testfile.txt'\n    f = open_fn(path, 'w')\n    f.write('hello\\nworld\\n')\n    f.close()\n\n    with open_fn(path) as f:\n        assert [line for line in f] == ['hello\\n', 'world\\n']\n\n    with open_fn(path) as f:\n        assert f.read(3) == 'hel'\n        assert f.read() == 'lo\\nworld\\n'\n        if hasattr(f, \"seek\"):\n            f.seek(0, 0)\n            assert f.tell() == 0\n            assert f.read() == 'hello\\nworld\\n'\n\n    if hasattr(f, \"tell\"):\n        try:\n            f.tell()\n            assert False\n        except IOError:\n            pass\n\n    if hasattr(f, \"seek\"):\n        try:\n            f.seek(0, 0)\n            assert False\n        except IOError:\n            pass\n\n    try:\n        f.flush()\n        assert False\n    except IOError:\n        pass\n\n    if append_allowed:\n        f = open_fn(path, 'a')\n        f.write('goodbye')\n        f.flush()\n        f.close()\n    else:\n        f = open_fn(path, 'w')\n        f.write('hello\\nworld\\ngoodbye')\n        f.flush()\n        f.close()\n\n    with open_fn(path) as f:\n        assert [line for line in f] == ['hello\\n', 'world\\n', 'goodbye']\n\n    with open_fn(path) as f:\n        assert f.read(3) == 'hel'\n        assert f.read() == 'lo\\nworld\\ngoodbye'\n\ntest_min_max()\ntest_map_filter()\ntest_gen_builtins()\ntest_int_format()\ntest_complex_format()\ntest_alt_types_from_str()\ntest_reversed()\ntest_divmod()\ntest_pow()\ntest_num_from_str()\ntest_files(open)\nimport gzip\ntest_files(gzip.open)\nimport bz2\ntest_files(bz2.open, append_allowed=False)\n\n\n# Codon-specific\n\n@pure\n@llvm\ndef zext(x: int, T: type) -> T:\n    %0 = zext i64 %x to {=T}\n    ret {=T} %0\n\n@test\ndef test_narrow_int_str(T: type):\n    z = T(0)\n    o = T(1)\n    a = T(42)\n    b = T(-9)\n\n    assert str(z) == '0'\n    assert str(-z) == '0'\n    assert str(o) == '1'\n    assert str(-o) == '-1'\n    assert str(o + o + o) == '3'\n    assert str((o + o + o + o) * (o + o + o)) == '12'\n    assert str(a) == '42'\n    assert str(b) == '-9'\n    assert repr(a) == f'Int[{T.N}](42)'\n\n@test\ndef test_narrow_uint_str(T: type):\n    z = T(0)\n    o = T(1)\n    a = T(42)\n\n    assert str(z) == '0'\n    assert str(-z) == '0'\n    assert str(o) == '1'\n    assert str(o + o + o) == '3'\n    assert str((o + o + o + o) * (o + o + o)) == '12'\n    assert str(a) == '42'\n\n    if T.N == 32:\n        assert str(T(0xffffffff)) == '4294967295'\n\n    if T.N == 64:\n        assert str(T(0xffffffffffffffff)) == '18446744073709551615'\n\n    assert repr(a) == f'UInt[{T.N}](42)'\n\n@test\ndef test_wide_int_str(T: type):\n    z = T(0)\n    o = T(1)\n    a = T(0xf23ff2341234)\n    b = T(-77777)\n\n    assert str(z) == '0'\n    assert str(-z) == '0'\n    assert str(o) == '1'\n    assert str(-o) == '-1'\n    assert str(o + o + o) == '3'\n    assert str((o + o + o + o) * (o + o + o)) == '12'\n    assert str(a) == '266356460360244'\n    assert str(b) == '-77777'\n    assert str(a * a) == '70945763975638233282255739536'\n    assert str(b * b) == '6049261729'\n    assert str(a * b) == '-20716406417438697588'\n\n    n = zext(0x7fffffffffffffff, T)\n    m = zext(0xffffffffffffffff, T)\n    s = T(64)\n    assert str((n << s) | m) == '170141183460469231731687303715884105727'\n    if T.N == 128:\n        assert str(T(1) << T(127)) == '-170141183460469231731687303715884105728'\n    if T.N > 500:\n        assert str(a * a * a * a * a * a * a * a) == '25334123245849102734940743817373556303530349383588924760280652082676453679304226528003335153202090430651964934127616'\n        assert str(a * a * a * a * a * a * a * a * b) == '-1970412103692405663415486231883863088619679984007395801080348277034326537815244826668515398210598987424817876681643589632'\n\n    assert repr(a) == f'Int[{T.N}](266356460360244)'\n    assert repr(a * b) == f'Int[{T.N}](-20716406417438697588)'\n\n@test\ndef test_wide_uint_str(T: type):\n    z = T(0)\n    o = T(1)\n    a = T(0xf23ff2341234)\n\n    assert str(z) == '0'\n    assert str(-z) == '0'\n    assert str(o) == '1'\n    assert str(o + o + o) == '3'\n    assert str((o + o + o + o) * (o + o + o)) == '12'\n    assert str(a) == '266356460360244'\n    assert str(a * a) == '70945763975638233282255739536'\n\n    n = zext(0xffffffffffffffff, T)\n    s = T(64)\n    assert str((n << s) | n) == '340282366920938463463374607431768211455'\n    assert str((n << s) | (n - T(1))) == '340282366920938463463374607431768211454'\n    if T.N > 500:\n        assert str(a * a * a * a * a * a * a * a) == '25334123245849102734940743817373556303530349383588924760280652082676453679304226528003335153202090430651964934127616'\n\n    assert repr(a) == f'UInt[{T.N}](266356460360244)'\n    assert repr(a * a) == f'UInt[{T.N}](70945763975638233282255739536)'\n\ntest_narrow_int_str(Int[7])\ntest_narrow_int_str(Int[8])\ntest_narrow_int_str(Int[10])\ntest_narrow_int_str(Int[16])\ntest_narrow_int_str(Int[32])\ntest_narrow_int_str(Int[60])\ntest_narrow_int_str(Int[63])\ntest_narrow_int_str(Int[64])\n\ntest_narrow_uint_str(UInt[7])\ntest_narrow_uint_str(UInt[8])\ntest_narrow_uint_str(UInt[10])\ntest_narrow_uint_str(UInt[16])\ntest_narrow_uint_str(UInt[32])\ntest_narrow_uint_str(UInt[60])\ntest_narrow_uint_str(UInt[63])\ntest_narrow_uint_str(UInt[64])\n\ntest_wide_int_str(Int[128])\ntest_wide_int_str(Int[200])\ntest_wide_int_str(Int[256])\ntest_wide_int_str(Int[512])\ntest_wide_int_str(Int[1024])\n\ntest_wide_uint_str(UInt[128])\ntest_wide_uint_str(UInt[200])\ntest_wide_uint_str(UInt[256])\ntest_wide_uint_str(UInt[512])\ntest_wide_uint_str(UInt[1024])\n\n# These take ages [80+ sec] on LLVM 17 to generate\n# test_wide_int_str(Int[2048])\n# test_wide_int_str(Int[4096])\n# test_wide_uint_str(UInt[2048])\n# test_wide_uint_str(UInt[4096])\n\n\n# __ptr__\n@test\ndef test_ptr_fields():\n    @tuple\n    class A:\n        n1: int\n        n2: int\n        n3: int\n\n    @tuple\n    class B:\n        a1: A\n        a2: A\n\n    @tuple\n    class C:\n        b1: B\n        b2: B\n\n    x1 = A(1, 2, 3)\n    __ptr__(x1.n1)[0] += 10\n    __ptr__(x1.n2)[0] += 20\n    __ptr__(x1.n3)[0] += 30\n\n    assert x1.n1 == 10 + 1\n    assert x1.n2 == 20 + 2\n    assert x1.n3 == 30 + 3\n\n    x2 = C(B(A(1, 2, 3), A(4, 5, 6)), B(A(7, 8, 9), A(10, 11, 12)))\n    __ptr__(x2.b1.a1.n1)[0] += 10\n    __ptr__(x2.b1.a1.n2)[0] += 20\n    __ptr__(x2.b1.a1.n3)[0] += 30\n    __ptr__(x2.b1.a2.n1)[0] += 40\n    __ptr__(x2.b1.a2.n2)[0] += 50\n    __ptr__(x2.b1.a2.n3)[0] += 60\n    __ptr__(x2.b2.a1.n1)[0] += 70\n    __ptr__(x2.b2.a1.n2)[0] += 80\n    __ptr__(x2.b2.a1.n3)[0] += 90\n    __ptr__(x2.b2.a2.n1)[0] += 100\n    __ptr__(x2.b2.a2.n2)[0] += 110\n    __ptr__(x2.b2.a2.n3)[0] += 120\n\n    assert x2.b1.a1.n1 == 10 + 1\n    assert x2.b1.a1.n2 == 20 + 2\n    assert x2.b1.a1.n3 == 30 + 3\n    assert x2.b1.a2.n1 == 40 + 4\n    assert x2.b1.a2.n2 == 50 + 5\n    assert x2.b1.a2.n3 == 60 + 6\n    assert x2.b2.a1.n1 == 70 + 7\n    assert x2.b2.a1.n2 == 80 + 8\n    assert x2.b2.a1.n3 == 90 + 9\n    assert x2.b2.a2.n1 == 100 + 10\n    assert x2.b2.a2.n2 == 110 + 11\n    assert x2.b2.a2.n3 == 120 + 12\n\n    __ptr__(x1)[0] = A(-1, -2, -3)\n    assert x1.n1 == -1\n    assert x1.n2 == -2\n    assert x1.n3 == -3\n\ntest_ptr_fields()\n\n@test\ndef test_sum_types():\n    class A:\n        n: int\n        def __init__(self, n: int):\n            self.n = n\n        def __add__(self, other):\n            return A(self.n + other.n)\n        def __eq__(self, other):\n            return self.n == other.n\n        def __ne__(self, other):\n            return self.n != other.n\n        def __hash__(self):\n            return self.n\n\n    # lists\n    s = sum([i/2 for i in range(6)])\n    assert type(s) is float\n    assert s == 7.5\n\n    s = sum([i*i for i in range(6)])\n    assert type(s) is int\n    assert s == 55\n\n    s = sum([i*i for i in range(6)], start=1.5)\n    assert type(s) is float\n    assert s == 56.5\n\n    s = sum([i%2 == 0 for i in range(6)])\n    assert type(s) is int\n    assert s == 3\n\n    s = sum([i%2 == 0 for i in range(6)], start=1.5)\n    assert type(s) is float\n    assert s == 4.5\n\n    s = sum([A(i) for i in range(6)], start=A(0))\n    assert type(s) is A\n    assert s.n == 15\n\n    # empty lists\n    s = sum([i/2 for i in range(0)])\n    assert type(s) is float\n    assert s == 0.0\n\n    s = sum([i*i for i in range(0)])\n    assert type(s) is int\n    assert s == 0\n\n    s = sum([i*i for i in range(0)], start=1.5)\n    assert type(s) is float\n    assert s == 1.5\n\n    s = sum([i%2 == 0 for i in range(0)])\n    assert type(s) is int\n    assert s == 0\n\n    s = sum([i%2 == 0 for i in range(0)], start=1.5)\n    assert type(s) is float\n    assert s == 1.5\n\n    s = sum([A(i) for i in range(0)], start=A(0))\n    assert type(s) is A\n    assert s.n == 0\n\n    # generators\n    s = sum(i/2 for i in range(6))\n    assert type(s) is float\n    assert s == 7.5\n\n    s = sum(i*i for i in range(6))\n    assert type(s) is int\n    assert s == 55\n\n    s = sum((i*i for i in range(6)), start=1.5)\n    assert type(s) is float\n    assert s == 56.5\n\n    s = sum(i%2 == 0 for i in range(6))\n    assert type(s) is int\n    assert s == 3\n\n    s = sum((i%2 == 0 for i in range(6)), start=1.5)\n    assert type(s) is float\n    assert s == 4.5\n\n    s = sum((A(i) for i in range(6)), start=A(0))\n    assert type(s) is A\n    assert s.n == 15\n\n    # empty generators\n    s = sum(i/2 for i in range(0))\n    assert type(s) is float\n    assert s == 0.0\n\n    s = sum(i*i for i in range(0))\n    assert type(s) is int\n    assert s == 0\n\n    s = sum((i*i for i in range(0)), start=1.5)\n    assert type(s) is float\n    assert s == 1.5\n\n    s = sum(i%2 == 0 for i in range(0))\n    assert type(s) is int\n    assert s == 0\n\n    s = sum((i%2 == 0 for i in range(0)), start=1.5)\n    assert type(s) is float\n    assert s == 1.5\n\n    s = sum((A(i) for i in range(0)), start=A(0))\n    assert type(s) is A\n    assert s.n == 0\n\n    # sets\n    s = sum({i/2 for i in range(6)})\n    assert type(s) is float\n    assert s == 7.5\n\n    s = sum({i*i for i in range(6)})\n    assert type(s) is int\n    assert s == 55\n\n    s = sum({i*i for i in range(6)}, start=1.5)\n    assert type(s) is float\n    assert s == 56.5\n\n    s = sum({i%2 == 0 for i in range(6)})\n    assert type(s) is int\n    assert s == 1\n\n    s = sum({i%2 == 0 for i in range(6)}, start=1.5)\n    assert type(s) is float\n    assert s == 2.5\n\n    s = sum({A(i) for i in range(6)}, start=A(0))\n    assert type(s) is A\n    assert s.n == 15\n\n    # empty sets\n    s = sum({i/2 for i in range(0)})\n    assert type(s) is float\n    assert s == 0.0\n\n    s = sum({i*i for i in range(0)})\n    assert type(s) is int\n    assert s == 0\n\n    s = sum({i*i for i in range(0)}, start=1.5)\n    assert type(s) is float\n    assert s == 1.5\n\n    s = sum({i%2 == 0 for i in range(0)})\n    assert type(s) is int\n    assert s == 0\n\n    s = sum({i%2 == 0 for i in range(0)}, start=1.5)\n    assert type(s) is float\n    assert s == 1.5\n\n    s = sum({A(i) for i in range(0)}, start=A(0))\n    assert type(s) is A\n    assert s.n == 0\n\ntest_sum_types()\n"
  },
  {
    "path": "test/core/containers.codon",
    "content": "from copy import copy, deepcopy\n\n@tuple\nclass A:\n    a: int\n    b: float\n\n    def __getitem__(self: A, n: int):\n        return 1\n\n    def __getitem__(self: A, x: Slice):\n        if x.start is None and x.stop is None:\n            return -1\n        if x.start is None:\n            return 2\n        elif x.stop is None:\n            return 3\n        else:\n            return self.a\n\n@dataclass(init=True, order=True, eq=True)\nclass A0:\n    pass\n\n@dataclass(init=True, order=True, eq=True)\nclass A1:\n    a: int\n\n@dataclass(init=True, order=True, eq=True)\nclass A2:\n    a: int\n    b: int\n\n@dataclass(init=True, order=True, eq=True)\nclass A3:\n    a: int\n    b: int\n    c: int\n\n@test\ndef test_tuple():\n    def test_in():\n        for i in range(10):\n            yield i, i in (4, 9, 10, -1, 3, 1), i in (7,)\n    assert list(test_in()) == [(0, False, False), (1, True, False), (2, False, False), (3, True, False), (4, True, False), (5, False, False), (6, False, False), (7, False, True), (8, False, False), (9, True, False)]\n\n    def test_cmp[T](a: T, b: T):\n        yield 'EQ', a == b\n        yield 'NE', a != b\n        yield 'LT', a < b\n        yield 'GT', a > b\n        yield 'LE', a <= b\n        yield 'GE', a >= b\n\n    assert list(test_cmp((1,), (2,))) == [('EQ', False), ('NE', True), ('LT', True), ('GT', False), ('LE', True), ('GE', False)]\n    assert list(test_cmp((1,), (1,))) == [('EQ', True), ('NE', False), ('LT', False), ('GT', False), ('LE', True), ('GE', True)]\n    assert list(test_cmp((2,), (1,))) == [('EQ', False), ('NE', True), ('LT', False), ('GT', True), ('LE', False), ('GE', True)]\n    assert list(test_cmp((1,2), (1,2))) == [('EQ', True), ('NE', False), ('LT', False), ('GT', False), ('LE', True), ('GE', True)]\n    assert list(test_cmp((1,2,2), (1,2,3))) == [('EQ', False), ('NE', True), ('LT', True), ('GT', False), ('LE', True), ('GE', False)]\n    assert list(test_cmp((1,2,-1), (1,0,1))) == [('EQ', False), ('NE', True), ('LT', False), ('GT', True), ('LE', False), ('GE', True)]\n    assert list(test_cmp((), ())) == [('EQ', True), ('NE', False), ('LT', False), ('GT', False), ('LE', True), ('GE', True)]\n    assert list(test_cmp(A0(), A0())) == [('EQ', True), ('NE', False), ('LT', False), ('GT', False), ('LE', True), ('GE', True)]\n    assert list(test_cmp(A1(1), A1(2))) == [('EQ', False), ('NE', True), ('LT', True), ('GT', False), ('LE', True), ('GE', False)]\n    assert list(test_cmp(A1(1), A1(1))) == [('EQ', True), ('NE', False), ('LT', False), ('GT', False), ('LE', True), ('GE', True)]\n    assert list(test_cmp(A1(2), A1(1))) == [('EQ', False), ('NE', True), ('LT', False), ('GT', True), ('LE', False), ('GE', True)]\n    assert list(test_cmp(A2(1,2), A2(1,2))) == [('EQ', True), ('NE', False), ('LT', False), ('GT', False), ('LE', True), ('GE', True)]\n    assert list(test_cmp(A3(1,2,2), A3(1,2,3))) == [('EQ', False), ('NE', True), ('LT', True), ('GT', False), ('LE', True), ('GE', False)]\n    assert list(test_cmp(A3(1,2,-1), A3(1,0,1))) == [('EQ', False), ('NE', True), ('LT', False), ('GT', True), ('LE', False), ('GE', True)]\n\n    t = (1,2,3)\n    assert (t[0], t[1], t[2]) == (1, 2, 3)\n    assert (t[-1], t[-2], t[-3]) == (3, 2, 1)\n    assert t[1:3] == (2, 3)\n    assert t[-3:1] == (1,)\n    assert t[-10:2] == (1, 2)\n    assert t[0:] == (1, 2, 3)\n    assert t[-2:] == (2, 3)\n    assert t[3:] == ()\n    assert t[:-1] == (1, 2)\n    assert t[:1] == (1,)\n    assert t[:] == (1, 2, 3)\n    assert t[::] == (1, 2, 3)\n    assert t[1::1] == (2, 3)\n    assert t[:2:1] == (1, 2)\n    assert t[::2] == (1, 3)\n    assert t[::-1] == (3, 2, 1)\n    assert t[0:3:-1] == ()\n    assert t[3:0:-1] == (3, 2)\n\n    a = A(42, 3.14)\n    assert a[0] == 1\n    assert a[:1] == 2\n    assert a[0:] == 3\n    assert a[0:1] == 42\n    assert a[:] == -1\n\n    assert str((11, 2, 333)) == '(11, 2, 333)'\n    assert str(()) == '()'\n    assert str((42,)) == '(42,)'\n    assert repr((11, 2, 333)) == '(11, 2, 333)'\n    assert repr(()) == '()'\n    assert repr((42,)) == '(42,)'\ntest_tuple()\n\n@test\ndef test_dyn_tuple():\n    def D(*args):\n        return DynamicTuple(args)\n\n    def test_in():\n        for i in range(10):\n            yield i, i in D(4, 9, 10, -1, 3, 1), i in D(7)\n    assert list(test_in()) == [(0, False, False), (1, True, False), (2, False, False), (3, True, False), (4, True, False), (5, False, False), (6, False, False), (7, False, True), (8, False, False), (9, True, False)]\n\n    def test_cmp(a, b):\n        yield 'EQ', a == b\n        yield 'NE', a != b\n        yield 'LT', a < b\n        yield 'GT', a > b\n        yield 'LE', a <= b\n        yield 'GE', a >= b\n\n    assert list(test_cmp(D(1,2), D(1,2))) == [('EQ', True), ('NE', False), ('LT', False), ('GT', False), ('LE', True), ('GE', True)]\n    assert list(test_cmp(D(1,2,2), D(1,2,3))) == [('EQ', False), ('NE', True), ('LT', True), ('GT', False), ('LE', True), ('GE', False)]\n    assert list(test_cmp(D(1,2,-1), D(1,0,1))) == [('EQ', False), ('NE', True), ('LT', False), ('GT', True), ('LE', False), ('GE', True)]\n    assert list(test_cmp(DynamicTuple[int](), DynamicTuple[int]())) == [('EQ', True), ('NE', False), ('LT', False), ('GT', False), ('LE', True), ('GE', True)]\n\n    assert list(test_cmp((1,2), D(1,2))) == [('EQ', True), ('NE', False), ('LT', False), ('GT', False), ('LE', True), ('GE', True)]\n    assert list(test_cmp((1,2,2), D(1,2,3))) == [('EQ', False), ('NE', True), ('LT', True), ('GT', False), ('LE', True), ('GE', False)]\n    assert list(test_cmp((1,2,-1), D(1,0,1))) == [('EQ', False), ('NE', True), ('LT', False), ('GT', True), ('LE', False), ('GE', True)]\n    assert list(test_cmp((), DynamicTuple[int]())) == [('EQ', True), ('NE', False), ('LT', False), ('GT', False), ('LE', True), ('GE', True)]\n\n    assert list(test_cmp(D(1,2), (1,2))) == [('EQ', True), ('NE', False), ('LT', False), ('GT', False), ('LE', True), ('GE', True)]\n    assert list(test_cmp(D(1,2,2), (1,2,3))) == [('EQ', False), ('NE', True), ('LT', True), ('GT', False), ('LE', True), ('GE', False)]\n    assert list(test_cmp(D(1,2,-1), (1,0,1))) == [('EQ', False), ('NE', True), ('LT', False), ('GT', True), ('LE', False), ('GE', True)]\n    assert list(test_cmp(DynamicTuple[int](), ())) == [('EQ', True), ('NE', False), ('LT', False), ('GT', False), ('LE', True), ('GE', True)]\n\n    t = D(1,2,3)\n    assert D(t[0], t[1], t[2]) == D(1, 2, 3)\n    assert D(t[-1], t[-2], t[-3]) == D(3, 2, 1)\n    assert t[1:3] == D(2, 3)\n    assert t[-3:1] == D(1,)\n    assert t[-10:2] == D(1, 2)\n    assert t[0:] == D(1, 2, 3)\n    assert t[-2:] == D(2, 3)\n    assert t[3:] == DynamicTuple[int]()\n    assert t[:-1] == D(1, 2)\n    assert t[:1] == D(1)\n    assert t[:] == D(1, 2, 3)\n    assert t[::] == D(1, 2, 3)\n    assert t[1::1] == D(2, 3)\n    assert t[:2:1] == D(1, 2)\n    assert t[::2] == D(1, 3)\n    assert t[::-1] == D(3, 2, 1)\n    assert t[0:3:-1] == DynamicTuple[int]()\n    assert t[3:0:-1] == D(3, 2)\n\n    assert D(t[0], t[1], t[2]) == (1, 2, 3)\n    assert D(t[-1], t[-2], t[-3]) == (3, 2, 1)\n    assert t[1:3] == (2, 3)\n    assert t[-3:1] == (1,)\n    assert t[-10:2] == (1, 2)\n    assert t[0:] == (1, 2, 3)\n    assert t[-2:] == (2, 3)\n    assert t[3:] == ()\n    assert t[:-1] == (1, 2)\n    assert t[:1] == (1,)\n    assert t[:] == (1, 2, 3)\n    assert t[::] == (1, 2, 3)\n    assert t[1::1] == (2, 3)\n    assert t[:2:1] == (1, 2)\n    assert t[::2] == (1, 3)\n    assert t[::-1] == (3, 2, 1)\n    assert t[0:3:-1] == DynamicTuple[int]()\n    assert t[3:0:-1] == (3, 2)\n\n    t = (1,2,3)\n    assert (t[0], t[1], t[2]) == D(1, 2, 3)\n    assert (t[-1], t[-2], t[-3]) == D(3, 2, 1)\n    assert t[1:3] == D(2, 3)\n    assert t[-3:1] == D(1,)\n    assert t[-10:2] == D(1, 2)\n    assert t[0:] == D(1, 2, 3)\n    assert t[-2:] == D(2, 3)\n    assert t[3:] == DynamicTuple[int]()\n    assert t[:-1] == D(1, 2)\n    assert t[:1] == D(1)\n    assert t[:] == D(1, 2, 3)\n    assert t[::] == D(1, 2, 3)\n    assert t[1::1] == D(2, 3)\n    assert t[:2:1] == D(1, 2)\n    assert t[::2] == D(1, 3)\n    assert t[::-1] == D(3, 2, 1)\n    assert t[0:3:-1] == DynamicTuple[int]()\n    assert t[3:0:-1] == D(3, 2)\n\n    assert hash(D(1,2,3,4,5)) == hash((1,2,3,4,5))\n\n    assert (1, 2) + (3,) == (1, 2, 3)\n    assert (1,) + (2, 3) == (1, 2, 3)\n    assert (1, 2) + () == (1, 2)\n    assert () + () == ()\n    assert () + (1, 2) == (1, 2)\n    assert (1,) + (2,) == (1, 2)\n    assert (1, 2) * 3 == (1, 2, 1, 2, 1, 2)\n    assert () * 99 == ()\n    assert (1, 2, 3, 4) * 1 == (1, 2, 3, 4)\n    assert (1, 2) * 0 == ()\n    assert (1, 2) * (-1) == ()\n    assert () * -1 == ()\ntest_dyn_tuple()\n\n@test\ndef test_list():\n    l1 = [i+1 for i in range(100)]\n    assert len(l1) == 100\n    l1 = l1[98:]\n    l2 = [1, 2] * 2\n\n    assert [a for a in l1] == [99, 100]\n    assert [a for a in l2] == [1, 2, 1, 2]\n    assert 2 * [1, 2] == l2\n\n    l1 = [i*2 for i in range(3)]\n    l1.insert(0, 99)\n    l1[0] += 1\n    del l1[1]\n    assert [a for a in l1[0:3]] == [100, 2, 4]\n\n    l3 = [1, 2, 3]\n    assert l3.remove(2) == True\n    assert l3.remove(2) == False\n    assert l3 == [1, 3]\n    assert list[int]().remove(0) == False\n\n    # l4 = [5, 1, 4, 2, 1, -10, 10, 100, -100]\n    # assert sorted(l4) == [-100, -10, 1, 1, 2, 4, 5, 10, 100]\n    #l4.sort()\n    #assert l4 == [-100, -10, 1, 1, 2, 4, 5, 10, 100]\n    #assert str(sorted(list[int]())) == \"[]\"\n\n    l5 = [11, 22, 33, 44]\n    del l5[-1]\n    assert l5 == [11, 22, 33]\n    l5.insert(-1, 55)\n    l5.insert(1000, 66)\n    l5.insert(-100, 77)\n    assert l5 == [77, 11, 22, 55, 33, 66]\n    l5 = [11, 22, 55, 33]\n    assert l5 + [1,2,3] == [11, 22, 55, 33, 1, 2, 3]\n    l5 += [1,2,3]\n    assert l5 == [11, 22, 55, 33, 1, 2, 3]\n    assert l5.pop() == 3\n    assert l5 * 2 == [11, 22, 55, 33, 1, 2, 11, 22, 55, 33, 1, 2]\n    l5 *= 2\n    assert l5 == [11, 22, 55, 33, 1, 2, 11, 22, 55, 33, 1, 2]\n    assert l5.index(33) == 3\n    try:\n        l5.index(0)\n        assert False\n    except ValueError as e:\n        assert str(e) == '0 is not in list'\n    l5 *= 0\n    assert len(l5) == 0\n\n    l6 = []\n    l6.extend('abc')\n    l6.extend(['xyz'])\n    l6.extend('')\n    assert l6 == ['a', 'b', 'c', 'xyz']\n\n    assert List[int]().copy() == List[int]()\n    assert [1,2,3].copy() == [1,2,3]\n\n    def test_cmp[T](a: T, b: T):\n        yield 'EQ', a == b\n        yield 'NE', a != b\n        yield 'LT', a < b\n        yield 'GT', a > b\n        yield 'LE', a <= b\n        yield 'GE', a >= b\n\n    assert list(test_cmp([1,2], [1,2])) == [('EQ', True), ('NE', False), ('LT', False), ('GT', False), ('LE', True), ('GE', True)]\n    assert list(test_cmp([1,2,2], [1,2,3])) == [('EQ', False), ('NE', True), ('LT', True), ('GT', False), ('LE', True), ('GE', False)]\n    assert list(test_cmp([1,2,-1], [1,0,1])) == [('EQ', False), ('NE', True), ('LT', False), ('GT', True), ('LE', False), ('GE', True)]\n    assert list(test_cmp(List[int](), List[int]())) == [('EQ', True), ('NE', False), ('LT', False), ('GT', False), ('LE', True), ('GE', True)]\n    assert list(test_cmp([1], List[int]())) == [('EQ', False), ('NE', True), ('LT', False), ('GT', True), ('LE', False), ('GE', True)]\n    assert list(test_cmp(List[int](), [1])) == [('EQ', False), ('NE', True), ('LT', True), ('GT', False), ('LE', True), ('GE', False)]\n    assert list(test_cmp([1,2,-1], [2])) == [('EQ', False), ('NE', True), ('LT', True), ('GT', False), ('LE', True), ('GE', False)]\n    assert list(test_cmp([1,2,-1], [1,2,-1,3])) == [('EQ', False), ('NE', True), ('LT', True), ('GT', False), ('LE', True), ('GE', False)]\n    assert list(test_cmp([1,2,-1,3], [1,2,-1])) == [('EQ', False), ('NE', True), ('LT', False), ('GT', True), ('LE', False), ('GE', True)]\n\n    assert str([1] + [2] + [] + [3]) == '[1, 2, 3]'\n    assert ['a', 'b'] + ['x', 'y', 'z'] == ['a', 'b'] + ['x', 'y', 'z']\n    assert [(1,1), (2,2)] + [] == [(1,1), (2,2)]\n    assert [] + [(1,1), (2,2)] == [(1,1), (2,2)]\n    assert List[int]() + List[int]() == List[int]()\n    l7 = [3.14, 2.5]\n    l7 += [9.99, -1.0]\n    assert l7 == [3.14, 2.5, 9.99, -1.0]\n    l8 = []\n    l8 += [11, 22, 33]\n    assert l8 == [11, 22, 33]\n    l8 = [11, 22, 33]\n    l8 += []\n    assert l8 == [11, 22, 33]\n    l8 = List[int]()\n    l8 += List[int]()\n    assert l8 == List[int]()\n\n    assert list(reversed(list('abc'))) == ['c', 'b', 'a']\n    assert list(list('abc')[::-1]) == ['c', 'b', 'a']\n    assert list(reversed(List[str]())) == List[str]()\n\n    # https://github.com/exaloop/codon/issues/402\n    b1 = [1 for _ in range(0)]\n    b2 = [str(x) for x in b1]\n    b3 = List[float](capacity=0)\n    b4 = List[float](capacity=-1)\n    assert len(b1) == 0\n    assert len(b2) == 0\n    assert len(b3) == 0\n    assert len(b4) == 0\n    b1.append(42)\n    b2.append('a')\n    b3.append(4.2)\n    b4.append(2.4)\n    assert b1 == [42]\n    assert b2 == ['a']\n    assert b3 == [4.2]\n    assert b4 == [2.4]\ntest_list()\n\n@test\ndef test_setslice():\n    l = [0, 1]\n    a = l\n\n    for i in range(-3, 4):\n        a[:i] = l[:i]\n        assert a == l\n        a2 = a[:]\n        a2[:i] = a[:i]\n        assert a2 == a\n        a[i:] = l[i:]\n        assert a == l\n        a2 = a[:]\n        a2[i:] = a[i:]\n        assert a2 == a\n        for j in range(-3, 4):\n            a[i:j] = l[i:j]\n            assert a == l\n            a2 = a[:]\n            a2[i:j] = a[i:j]\n            assert a2 == a\n\n    a2 = a[:]\n    aa2 = a2[:]\n    aa2[:0] = [-2, -1]\n    assert aa2 == [-2, -1, 0, 1]\n    aa2[0:] = list[int]()\n    assert aa2 == list[int]()\n\n    a = [1, 2, 3, 4, 5]\n    a[:-1] = a\n    assert a == [1, 2, 3, 4, 5, 5]\n    a = [1, 2, 3, 4, 5]\n    a[1:] = a\n    assert a == [1, 1, 2, 3, 4, 5]\n    a = [1, 2, 3, 4, 5]\n    a[1:-1] = a\n    assert a == [1, 1, 2, 3, 4, 5, 5]\n\n    a = list[int]()\n    a[:] = list(range(10))\n    assert a == list(range(10))\n\n    a = list(range(20))\n    try:\n        a[0:10:0] = [1,2,3]\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        a[0:10:2] = [1,2]\n        assert False\n    except ValueError:\n        pass\n\n    a[2:10:3] = [1,2,3]\n    assert a == [0, 1, 1, 3, 4, 2, 6, 7, 3, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19]\ntest_setslice()\n\n@test\ndef test_delslice():\n    a = [0, 1]\n    del a[1:2]\n    del a[0:1]\n    assert a == list[int]()\n\n    a = [0, 1]\n    del a[1:2]\n    del a[0:1]\n    assert a == list[int]()\n\n    a = [0, 1]\n    del a[-2:-1]\n    assert a == [1]\n\n    a = [0, 1]\n    del a[-2:-1]\n    assert a == [1]\n\n    a = [0, 1]\n    del a[1:]\n    del a[:1]\n    assert a == list[int]()\n\n    a = [0, 1]\n    del a[1:]\n    del a[:1]\n    assert a == list[int]()\n\n    a = [0, 1]\n    del a[-1:]\n    assert a == [0]\n\n    a = [0, 1]\n    del a[-1:]\n    assert a == [0]\n\n    a = [0,1]\n    del a[:]\n    assert a == list[int]()\ntest_delslice()\n\n@test\ndef test_extendedslicing():\n    a = [0,1,2,3,4]\n    del a[::2]\n    assert a == [1,3]\n\n    a = list(range(5))\n    del a[1::2]\n    assert a == [0,2,4]\n\n    a = list(range(5))\n    del a[1::-2]\n    assert a == [0,2,3,4]\n\n    a = list(range(10))\n    del a[::1000]\n    assert a == [1, 2, 3, 4, 5, 6, 7, 8, 9]\n\n    a = list(range(10))\n    a[::2] = [-1]*5\n    assert a == [-1, 1, -1, 3, -1, 5, -1, 7, -1, 9]\n\n    a = list(range(10))\n    a[::-4] = [10]*3\n    assert a == [0, 10, 2, 3, 4, 10, 6, 7, 8 ,10]\n\n    a = list(range(4))\n    a[::-1] = a\n    assert a == [3, 2, 1, 0]\n\n    a = list(range(10))\n    b = a[:]\n    c = a[:]\n    a[2:3] = [222, 333]\n    b[2:3] = [222, 333]\n    c[2:3:] = [222, 333]\n    assert a == b\n    assert a == c\n\n    a = list(range(10))\n    a[::2] = (0, 1, 2, 3, 4)\n    assert a == [0, 1, 1, 3, 2, 5, 3, 7, 4, 9]\n\n    assert repr(['x', 'y', 'z']) == \"['x', 'y', 'z']\"\n    assert repr(List[int]()) == '[]'\ntest_extendedslicing()\n\n@test\ndef test_set():\n    s1 = {a for a in range(100)}\n    assert len(s1) == 100\n    s1 = {a%8 for a in range(100)}\n    for a in range(8):\n        assert a in s1\n    for a in range(8,100):\n        assert a not in s1\n\n    assert 5 in s1\n    s1.remove(5)\n    assert 5 not in s1\n    assert len(s1) == 7\n\n    s1 = {1,2,3,4}\n    s2 = {2,3,4,5}\n    s3 = set[int]()\n\n    assert (s1 | s2) == {1, 2, 3, 4, 5}\n    assert (s1 & s2) == {4, 2, 3}\n    assert (s1 ^ s2) == {1, 5}\n    assert (s1 | s3) == {1, 2, 3, 4}\n    assert (s1 & s3) == set[int]()\n    assert (s1 ^ s3) == {1, 2, 3, 4}\n    assert (s1 - s2) == {1}\n    assert (s2 - s1) == {5}\n    assert (s3 - s1 - s2) == set[int]()\n    assert (s1 > s2) == False\n    assert (s1 < s2) == False\n    assert (s3 <= s1) == True\n    assert (s2 >= s1) == False\n    assert ((s1 | s2) > s1) == True\n\n    s1c = copy(s1)\n    s2c = copy(s2)\n    s3c = copy(s3)\n\n    assert s1c == {1, 2, 3, 4}\n    s1c &= s2c\n    assert s1c == {2, 3, 4}\n    s1c -= s3c\n    assert s1c == {2, 3, 4}\n    s1c ^= s1c\n    assert s1c == set[int]()\n    s1c |= s2c\n    assert s1c == {2, 3, 4, 5}\n    assert s1 == {1, 2, 3, 4}\n\n    s1 = {1, 2, 3, 999999}\n    s2 = {1, 2, 3, 999999}\n    v = s1.pop()\n    assert v in s2\n    s2.remove(v)\n\n    v = s1.pop()\n    assert v in s2\n    s2.remove(v)\n\n    v = s1.pop()\n    assert v in s2\n    s2.remove(v)\n\n    v = s1.pop()\n    assert v in s2\n    s2.remove(v)\n\n    try:\n        s1.pop()\n        assert False\n    except ValueError:\n        pass\n\n    assert repr({(1,2)}) == '{(1, 2)}'\n    assert repr(Set[int]()) == 'set()'\ntest_set()\n\n@test\ndef test_dict():\n    d1 = copy({a: a*a for a in range(100)})\n    assert len(d1) == 100\n    d1 = {a: a*a for a in range(5)}\n    assert len(copy(dict[int,int]())) == 0\n\n    assert [d1.get(a, -1) for a in range(6)] == [0, 1, 4, 9, 16, -1]\n\n    assert 2 in d1\n    del d1[2]\n    assert 2 not in d1\n    d1[2] = 44\n    assert 2 in d1\n    assert d1.get(2, -1) == 44\n    assert d1[3] == 9\n\n    del d1[3]\n    del d1[4]\n\n    assert [k for k in d1] == [0, 1, 2]\n    assert [t for t in d1.items()] == [(0, 0), (1, 1), (2, 44)]\n\n    d2 = {'x': 10, 'y': 0}\n    d2.increment('x')\n    d2.increment('y', by=-1)\n    d2.increment('z', by=2)\n    assert d2['x'] == 11\n    assert d2['y'] == -1\n    assert d2['z'] == 2\n    assert d2 == {'x': 11, 'y': -1, 'z': 2}\n\n    d3 = {1: 2, 42: 42}\n    d4 = {1: 5, 2: 9}\n    assert d3 | d4 == {1: 5, 42: 42, 2: 9}\n    d3 |= d4\n    assert d3 == {1: 5, 42: 42, 2: 9}\n\n    assert repr({1: ['x']}) == \"{1: ['x']}\"\n    assert repr(Dict[int,int]()) == '{}'\ntest_dict()\n\ndef slice_indices(slice, length):\n    \"\"\"\n    Reference implementation for the slice.indices method.\n\n    \"\"\"\n    # Compute step and length as integers.\n    #length = operator.index(length)\n    step: int = 1 if slice.step is None else slice.step\n\n    # Raise ValueError for negative length or zero step.\n    if length < 0:\n        raise ValueError(\"length should not be negative\")\n    if step == 0:\n        raise ValueError(\"slice step cannot be zero\")\n\n    # Find lower and upper bounds for start and stop.\n    lower = -1 if step < 0 else 0\n    upper = length - 1 if step < 0 else length\n\n    # Compute start.\n    if slice.start is None:\n        start = upper if step < 0 else lower\n    else:\n        start = slice.start\n        start = max(start + length, lower) if start < 0 else min(start, upper)\n\n    # Compute stop.\n    if slice.stop is None:\n        stop = lower if step < 0 else upper\n    else:\n        stop = slice.stop\n        stop = max(stop + length, lower) if stop < 0 else min(stop, upper)\n\n    return start, stop, step\n\ndef check_indices(slice, length):\n    err1 = False\n    err2 = False\n\n    try:\n        actual = slice.indices(length)\n    except ValueError:\n        err1 = True\n\n    try:\n        expected = slice_indices(slice, length)\n    except ValueError:\n        err2 = True\n\n    if err1 or err2:\n        return err1 and err2\n\n    if actual != expected:\n        return False\n\n    if length >= 0 and slice.step != 0:\n        actual = range(*slice.indices(length))\n        expected = range(length)[slice]\n        if actual != expected:\n            return False\n\n    return True\n\n@test\ndef test_slice():\n    assert repr(slice(1, 2, 3)) == 'slice(1, 2, 3)'\n\n    s1 = slice(1, 2, 3)\n    s2 = slice(1, 2, 3)\n    s3 = slice(1, 2, 4)\n\n    assert s1 == s2\n    assert s1 != s3\n\n    s = slice(1)\n    assert s.start == None\n    assert s.stop == 1\n    assert s.step == None\n\n    s = slice(1, 2)\n    assert s.start == 1\n    assert s.stop == 2\n    assert s.step == None\n\n    s = slice(1, 2, 3)\n    assert s.start == 1\n    assert s.stop == 2\n    assert s.step == 3\n\n    # TODO\n    assert slice(None           ).indices(10) == (0, 10,  1)\n    assert slice(None,  None,  2).indices(10) == (0, 10,  2)\n    assert slice(1,     None,  2).indices(10) == (1, 10,  2)\n    assert slice(None,  None, -1).indices(10) == (9, -1, -1)\n    assert slice(None,  None, -2).indices(10) == (9, -1, -2)\n    assert slice(3,     None, -2).indices(10) == (3, -1, -2)\n    # issue 3004 tests\n    assert slice(None, -9).indices(10) == (0, 1, 1)\n    assert slice(None, -10).indices(10) == (0, 0, 1)\n    assert slice(None, -11).indices(10) == (0, 0, 1)\n    assert slice(None, -10, -1).indices(10) == (9, 0, -1)\n    assert slice(None, -11, -1).indices(10) == (9, -1, -1)\n    assert slice(None, -12, -1).indices(10) == (9, -1, -1)\n    assert slice(None, 9).indices(10) == (0, 9, 1)\n    assert slice(None, 10).indices(10) == (0, 10, 1)\n    assert slice(None, 11).indices(10) == (0, 10, 1)\n    assert slice(None, 8, -1).indices(10) == (9, 8, -1)\n    assert slice(None, 9, -1).indices(10) == (9, 9, -1)\n    assert slice(None, 10, -1).indices(10) == (9, 9, -1)\n\n    assert slice(-100,  100     ).indices(10) == slice(None).indices(10)\n\n    assert slice(100,  -100,  -1).indices(10) == slice(None, None, -1).indices(10)\n\n    assert slice(-100, 100, 2).indices(10) == (0, 10,  2)\n\n    import sys\n    assert list(range(10))[::sys.maxsize - 1] == [0]\n\n    # Check a variety of start, stop, step and length values, including\n    # values exceeding sys.maxsize (see issue #14794).\n    vals = [None, -2**100, -2**30, -53, -7, -1, 0, 1, 7, 53, 2**30, 2**100]\n    lengths = [0, 1, 7, 53, 2**30, 2**100]\n    #for slice_args in itertools.product(vals, repeat=3):\n    for a in vals:\n        for b in vals:\n            for c in vals:\n                slice_args = (a, b, c)\n                s = slice(*slice_args)\n                for length in lengths:\n                    assert check_indices(s, length)\n    assert check_indices(slice(0, 10, 1), -3)\n\n    # Negative length should raise ValueError\n    try:\n        slice(None).indices(-1)\n        assert False\n    except ValueError:\n        pass\n\n    # Zero step should raise ValueError\n    try:\n        slice(0, 10, 0).indices(5)\n    except ValueError:\n        pass\n\n    # ... but it should be fine to use a custom class that provides index.\n    assert slice(0, 10, 1).indices(5) == (0, 5, 1)\n    '''  # not yet supported in Codon\n    assert slice(MyIndexable(0), 10, 1).indices(5) == (0, 5, 1)\n    assert slice(0, MyIndexable(10), 1).indices(5) == (0, 5, 1)\n    assert slice(0, 10, MyIndexable(1)).indices(5) == (0, 5, 1)\n    assert slice(0, 10, 1).indices(MyIndexable(5)) == (0, 5, 1)\n    '''\n    tmp = []\n    class X[T](object):\n        tmp: T\n        def __setitem__(self, i, k):\n            self.tmp.append((i, k))\n\n    x = X(tmp)\n    x[1:2] = 42\n    assert tmp == [(slice(1, 2), 42)]\n\n    # Non-int elements\n    def check_types(s, T: type, U: type, V: type):\n        return (type(s.start) is Optional[T] and\n                type(s.stop) is Optional[U] and\n                type(s.step) is Optional[V])\n    assert check_types(slice(1j, 'x', 3.14), complex, str, float)\n    assert check_types(slice(None, 'x', 3.14), int, str, float)\n    assert check_types(slice(1j, None, 3.14), complex, int, float)\n    assert check_types(slice(1j, 'x', None), complex, str, int)\n    assert check_types(slice(1j, None, None), complex, int, int)\n    assert check_types(slice(None, 'x', None), int, str, int)\n    assert check_types(slice(None, None, 3.14), int, int, float)\n    assert check_types(slice(None, None, None), int, int, int)\n    assert check_types(slice(1j, 'x'), complex, str, int)\n    assert check_types(slice(None, 'x'), int, str, int)\n    assert check_types(slice(1j, None), complex, int, int)\n    assert check_types(slice(None, None), int, int, int)\n    assert check_types(slice(1j), int, complex, int)\n    assert check_types(slice(None), int, int, int)\n\n    # eq / ne\n    assert slice(1, 2, 3) == slice(1, 2, 3)\n    assert slice(0, 2, 3) != slice(1, 2, 3)\n    assert slice(1, 0, 3) != slice(1, 2, 3)\n    assert slice(1, 2, 0) != slice(1, 2, 3)\n    assert slice(None, None, None) == slice(None, None, None)\n    assert slice(None, 42, None) == slice(None, 42, None)\n    assert slice(None, 42, None) != slice(None, 43, None)\n    assert slice(1, None, 3) == slice(1, None, 3)\n    assert slice(1, None, 3) != slice(1, None, 0)\n    assert slice(1, None, 3) != slice(0, None, 3)\n    assert slice(1) == slice(1)\n    assert slice(1) != slice(2)\ntest_slice()\n\n@test\ndef test_deque():\n    from collections import deque\n\n    dq = deque[int]()\n    dq.append(1)\n    dq.append(2)\n    dq.append(3)\n    dq.appendleft(11)\n    dq.appendleft(22)\n    assert str(dq) == 'deque([22, 11, 1, 2, 3])'\n    assert bool(dq) == True\n\n    # test cap increase:\n    dq.clear()\n    assert bool(dq) == False\n    for i in range(20):\n        dq.append(i)\n        dq.appendleft(i)\n    assert str(dq) == 'deque([19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19])'\n    assert len(dq) == 40\n\n    for i in range(19):\n        dq.pop()\n        dq.popleft()\n    assert str(dq) == 'deque([0, 0])'\n    for a in dq:\n        assert a == 0\n\n    assert (0 in dq) == True\n    assert (1 in dq) == False\n    assert str(copy(dq)) == 'deque([0, 0])'\n\n    # test maxlen:\n    dq = deque[int](5)\n    for i in range(100):\n        dq.append(i)\n    assert str(dq) == 'deque([95, 96, 97, 98, 99])'\n\n    for i in range(5):\n        dq.append(i)\n    assert str(dq) == 'deque([0, 1, 2, 3, 4])'\ntest_deque()\n\n@test\ndef test_counter():\n    from collections import Counter\n\n    # main\n    c = Counter('abcaba')\n    assert c == Counter({'a':3 , 'b': 2, 'c': 1})\n    assert len(c) == 3\n    assert sum(c.values()) == 6\n    assert set(c.values()) == {1, 2, 3}\n    assert set(c.keys()) == {'a','b','c'}\n    assert set(c.items()) == {('a',3), ('b',2), ('c',1)}\n    assert c['b'] == 2\n    assert c['z'] == 0\n    assert c.__contains__('b')\n    assert not c.__contains__('z')\n    assert c.get('b', 10) == 2\n    assert c.get('z', 10) == 10\n    assert c == {'a':3 , 'b': 2, 'c': 1}\n    assert c.most_common() == [('a', 3), ('b', 2), ('c', 1)]\n    for i in range(5):\n        assert c.most_common(i) == [('a', 3), ('b', 2), ('c', 1)][:i]\n\n    c['a'] += 1         # increment an existing value\n    c['b'] -= 2         # sub existing value to zero\n    del c['c']          # remove an entry\n    del c['c']          # make sure that del doesn't raise KeyError\n    c['d'] -= 2         # sub from a missing value\n    c['e'] = -5         # directly assign a missing value\n    c['f'] += 4         # add to a missing value\n\n    assert dict(c) == {'a':4, 'b':0, 'd':-2, 'e':-5, 'f':4}\n    assert c.pop('f') == 4\n    assert 'f' not in c\n    for i in range(3):\n        elem, cnt = c.popitem()\n        assert elem not in c\n    c.clear()\n    assert c == {}\n    c.update({'a':5, 'b':3})\n    c.update({'c': 1})\n    c.update(Counter('a' * 50 + 'b' * 30))\n    c.update()\n    c.update('a' * 500 + 'b' * 300)\n    c.update('cdc')\n    assert c == {'a':555, 'b':333, 'c':3, 'd':1}\n\n    assert c.setdefault('d',5) == 1\n    assert c['d'] == 1\n    assert c.setdefault('e', 5) == 5\n    assert c['e'] == 5\n\n\n    # total\n    c = Counter({'a':10, 'b':5, 'c':0})\n    assert c.total() == 15\n\n\n    # conversions\n    s = 'she sells sea shells by the sea shore'\n    assert sorted(Counter(s).elements()) == sorted(s)\n    assert sorted(Counter(s)) == sorted(set(s))\n    assert dict(Counter(s)) == dict(Counter(s).items())\n    assert set(Counter(s)) == set(s)\n\n\n    # in invariant\n    c = Counter({'a':10, 'b':-2, 'c':0})\n    for elem in c:\n        assert elem in c\n\n\n    # multiset\n    c = Counter({'a':10, 'b':-2, 'c':0}) + Counter()\n    assert dict(c) == {'a':10}\n\n    from random import randrange, randint\n    elements = 'abcd'\n    for i in range(1000):\n        # test random pairs of multisets\n        p = Counter(dict((elem, randrange(-2,4)) for elem in elements))\n        p.update({'e':1, 'f':-1, 'g':0})\n        q = Counter(dict((elem, randrange(-2,4)) for elem in elements))\n        q.update({'h':1, 'i':-1, 'j':0})\n\n        result = p + q\n        for x in elements:\n            assert max(0, p[x] + q[x]) == result[x]\n        assert all(x>0 for x in result.values())\n\n        result = p - q\n        for x in elements:\n            assert max(0, p[x] - q[x]) == result[x]\n        assert all(x>0 for x in result.values())\n\n        result = p | q\n        for x in elements:\n            assert max(0, p[x], q[x]) == result[x]\n        assert all(x>0 for x in result.values())\n\n        result = p & q\n        for x in elements:\n            assert max(0, min(p[x], q[x])) == result[x]\n        assert all(x>0 for x in result.values())\n\n    elements = 'abcdef'\n    for i in range(100):\n        # verify that random multisets with no repeats are exactly like sets\n        p = Counter(dict((elem, randrange(0, 2)) for elem in elements))\n        q = Counter(dict((elem, randrange(0, 2)) for elem in elements))\n\n        counter_result = p - q\n        set_result = set(p.elements()) - set(q.elements())\n        assert counter_result == dict.fromkeys(set_result, 1)\n\n        counter_result = p | q\n        set_result = set(p.elements()) | set(q.elements())\n        assert counter_result == dict.fromkeys(set_result, 1)\n\n        counter_result = p & q\n        set_result = set(p.elements()) & set(q.elements())\n        assert counter_result == dict.fromkeys(set_result, 1)\n\n\n    # in-place\n    elements = 'abcd'\n    for i in range(1000):\n        # test random pairs of multisets\n        p = Counter(dict((elem, randrange(-2,4)) for elem in elements))\n        p.update({'e':1, 'f':-1, 'g':0})\n        q = Counter(dict((elem, randrange(-2,4)) for elem in elements))\n        q.update({'h':1, 'i':-1, 'j':0})\n\n        c = p.copy()\n        c_id = id(c)\n        regular_result = c + q\n        inplace_result = c.__iadd__(q)\n        assert inplace_result == regular_result\n        assert id(inplace_result) == c_id\n\n        c = p.copy()\n        c_id = id(c)\n        regular_result = c - q\n        inplace_result = c.__isub__(q)\n        assert inplace_result == regular_result\n        assert id(inplace_result) == c_id\n\n        c = p.copy()\n        c_id = id(c)\n        regular_result = c | q\n        inplace_result = c.__ior__(q)\n        assert inplace_result == regular_result\n        assert id(inplace_result) == c_id\n\n        c = p.copy()\n        c_id = id(c)\n        regular_result = c & q\n        inplace_result = c.__iand__(q)\n        assert inplace_result == regular_result\n        assert id(inplace_result) == c_id\n\n\n    # subtract\n    c = Counter({'a':-5, 'b':0, 'c':5, 'd':10, 'e':15,'g':40})\n    c.subtract({'a':1, 'b':2, 'c':-3, 'd':10, 'e':20, 'f':30, 'h':-50})\n    assert c == Counter({'a':-6, 'b':-2, 'c':8, 'd':0, 'e':-5, 'f':-30, 'g':40, 'h':50})\n    c = Counter({'a':-5, 'b':0, 'c':5, 'd':10, 'e':15,'g':40})\n    c.subtract(Counter({'a':1, 'b':2, 'c':-3, 'd':10, 'e':20, 'f':30, 'h':-50}))\n    assert c == Counter({'a':-6, 'b':-2, 'c':8, 'd':0, 'e':-5, 'f':-30, 'g':40, 'h':50})\n\n    c = Counter('aaabbcd')\n    c.subtract('aaaabbcce')\n    assert c == Counter({'a':-1, 'b':0, 'c':-1, 'd':1, 'e':-1})\n\n    c = Counter()\n    c.subtract({'self':42})\n    assert list(c.items()) == [('self', -42)]\n    c = Counter()\n    c.subtract({'iterable':42})\n    assert list(c.items()) == [('iterable', -42)]\n\n\n    # unary\n    c = Counter({'a':-5, 'b':0, 'c':5, 'd':10, 'e':15, 'g':40})\n    assert dict(+c) == {'c':5, 'd':10, 'e':15, 'g':40}\n    assert dict(-c) == {'a':5}\n\n\n    # equality\n    assert Counter({'a':3, 'b':2}) == Counter('ababa')\n    assert Counter({'a':3, 'b':2}) != Counter('babab')\n\n\n    # most common\n    c = Counter({v:k for k,v in enumerate('hgfedcba')})\n    q = [(v,k) for k,v in enumerate('hgfedcba')][::-1]\n    assert c.most_common() == q\n    for i in range(10):\n        assert c.most_common(i) == q[:i]\n\n    for limit in range(100):\n        for samples in range(100):\n            for most_common in range(100):\n                data = [randint(0, limit) for _ in range(samples)]\n                d = Counter(data)\n                exp = sorted(d.values(), reverse=True)[:most_common]\n                got = [v for k,v in d.most_common(most_common)]\n                assert exp == got\n\n    for limit in range(100):\n        for samples in range(100):\n            data = [randint(0, limit) for _ in range(samples)]\n            d = Counter(data)\n            exp = sorted(d.values(), reverse=True)\n            got = [v for k,v in d.most_common()]\n            assert exp == got\n\n    assert repr(Counter('abcabc')) == \"Counter({'a': 2, 'b': 2, 'c': 2})\"\ntest_counter()  # this call doubles compile time!\n\n@test\ndef test_defaultdict():\n    from collections import defaultdict\n\n    # basic\n    #d1 = defaultdict()\n    #self.assertEqual(d1.default_factory, None)\n    #d1.default_factory = list\n    d1 = defaultdict(list)\n    d1[12].append(42)\n    assert d1 == {12: [42]}\n    d1[12].append(24)\n    assert d1 == {12: [42, 24]}\n    d1[13]\n    d1[14]\n    assert d1 == {12: [42, 24], 13: [], 14: []}\n    assert d1[12] is not d1[13] is not d1[14]\n    #d2 = defaultdict(list, foo=1, bar=2)\n    #self.assertEqual(d2.default_factory, list)\n    #self.assertEqual(d2, {\"foo\": 1, \"bar\": 2})\n    #self.assertEqual(d2[\"foo\"], 1)\n    #self.assertEqual(d2[\"bar\"], 2)\n    #self.assertEqual(d2[42], [])\n    #self.assertIn(\"foo\", d2)\n    #self.assertIn(\"foo\", d2.keys())\n    #self.assertIn(\"bar\", d2)\n    #self.assertIn(\"bar\", d2.keys())\n    #self.assertIn(42, d2)\n    #self.assertIn(42, d2.keys())\n    #self.assertNotIn(12, d2)\n    #self.assertNotIn(12, d2.keys())\n    #d2.default_factory = None\n    #self.assertEqual(d2.default_factory, None)\n    #try:\n    #    d2[15]\n    #except KeyError as err:\n    #    self.assertEqual(err.args, (15,))\n    #else:\n    #    self.fail(\"d2[15] didn't raise KeyError\")\n    #self.assertRaises(TypeError, defaultdict, 1)\n\n    # missing\n    #d1 = defaultdict()\n    #self.assertRaises(KeyError, d1.__missing__, 42)\n    #d1.default_factory = list\n    #d1 = defaultdict(list)\n    assert d1.__missing__(42) == []\n\n    # repr\n    d1 = defaultdict(lambda: 0)\n    #self.assertEqual(d1.default_factory, None)\n    #self.assertEqual(repr(d1), \"defaultdict(None, {})\")\n    #self.assertEqual(eval(repr(d1)), d1)\n    d1[11] = 41\n    assert repr(d1) == \"defaultdict(<default factory of 'int'>, {11: 41})\"\n    d2 = defaultdict(lambda: 0)  # TODO: use 'int' when it's fixed...\n    #self.assertEqual(d2.default_factory, int)\n    d2[12] = 42\n    assert repr(d2) == \"defaultdict(<default factory of 'int'>, {12: 42})\"\n    def foo(): return 43\n    d3 = defaultdict(foo)\n    #self.assertTrue(d3.default_factory is foo)\n    d3[13]\n    assert repr(d3) == \"defaultdict(<default factory of 'int'>, {13: 43})\"\n\n\n    # copy\n    d1 = defaultdict(list)\n    d2 = d1.copy()\n    #self.assertEqual(type(d2), defaultdict)\n    #self.assertEqual(d2.default_factory, None)\n    assert d2 == {}\n    #d1.default_factory = list\n    #d3 = d1.copy()\n    #self.assertEqual(type(d3), defaultdict)\n    #self.assertEqual(d3.default_factory, list)\n    #self.assertEqual(d3, {})\n    d1[42].append(0)\n    #d4 = d1.copy()\n    #assert d4 == {42: [0]}\n    #d4[12]\n    #assert d4 == {42: [], 12: []}\n\n    # Issue 6637: Copy fails for empty default dict\n    #d = defaultdict()\n    #d['a'] = 42\n    #e = d.copy()\n    #assert e['a'] == 42\n\n\n    # shallow copy\n    foobar = list\n    d1 = defaultdict(foobar)\n    d1[1] += [1]\n    d2 = copy(d1)\n    #self.assertEqual(d2.default_factory, foobar)\n    assert d2 == d1\n    #d1.default_factory = list\n    d2 = copy(d1)\n    #self.assertEqual(d2.default_factory, list)\n    assert d2 == d1\n\n    # deep copy\n    d1 = defaultdict(foobar)\n    d1[1].append(1)\n    d2 = deepcopy(d1)\n    #self.assertEqual(d2.default_factory, foobar)\n    assert d2 == d1\n    assert d1[1] is not d2[1]\n    #d1.default_factory = list\n    d2 = deepcopy(d1)\n    #self.assertEqual(d2.default_factory, list)\n    assert d2 == d1\n\n    # KeyError without factory\n    #d1 = defaultdict()\n    #try:\n    #    d1[(1,)]\n    #except KeyError as err:\n    #    self.assertEqual(err.args[0], (1,))\n    #else:\n    #    self.fail(\"expected KeyError\")\n\n    # pickling\n    #d = defaultdict(int)\n    #d[1]\n    #for proto in range(pickle.HIGHEST_PROTOCOL + 1):\n    #    s = pickle.dumps(d, proto)\n    #    o = pickle.loads(s)\n    #    self.assertEqual(d, o)\n\n    # union\n    i = defaultdict(int, {1: 1, 2: 2})\n    s = defaultdict(int, {0: 0, 1: 111})\n\n    i_s = i | s\n    #self.assertIs(i_s.default_factory, int)\n    assert i_s == {1: 111, 2: 2, 0: 0}\n    assert sorted(i_s) == [0, 1, 2]\n\n    s_i = s | i\n    #self.assertIs(s_i.default_factory, str)\n    assert s_i == {0: 0, 1: 1, 2: 2}\n    assert sorted(s_i) == [0, 1, 2]\n\n    i_ds = i | dict(s)\n    #self.assertIs(i_ds.default_factory, int)\n    assert i_ds == {1: 111, 2: 2, 0: 0}\n    assert sorted(i_ds) == [0, 1, 2]\n\n    ds_i = dict(s) | i\n    #self.assertIs(ds_i.default_factory, int)\n    assert ds_i == {0: 0, 1: 1, 2: 2}\n    assert sorted(ds_i) == [0, 1, 2]\n\n    # We inherit a fine |= from dict, so just a few sanity checks here:\n    i |= list(s.items())\n    #self.assertIs(i.default_factory, int)\n    assert i == {1: 111, 2: 2, 0: 0}\n    assert sorted(i), [1, 2, 0]\n\n    # general\n    s = 'mississippi'\n    d = defaultdict(int)\n    for k in s:\n        d[k] += 1\n    assert sorted(d.items()) == [('i', 4), ('m', 1), ('p', 2), ('s', 4)]\n\n    s = 'mississippi'\n    d = defaultdict(int)\n    for k in s:\n        d[k] = d.get(k, 0) + 1\n    assert sorted(d.items()) == [('i', 4), ('m', 1), ('p', 2), ('s', 4)]\n\n    s = [('yellow', 1), ('blue', 2), ('yellow', 3), ('blue', 4), ('red', 1)]\n    d = defaultdict(list)\n    for k, v in s:\n        d[k].append(v)\n    assert sorted(d.items()) == [('blue', [2, 4]), ('red', [1]), ('yellow', [1, 3])]\n\n    def constant_factory(value):\n        return lambda: value\n\n    d = defaultdict(constant_factory('<missing>'))\n    assert d[10] == '<missing>'\ntest_defaultdict()\n"
  },
  {
    "path": "test/core/empty.codon",
    "content": ""
  },
  {
    "path": "test/core/exceptions.codon",
    "content": "class Exc1(Exception):\n    def __init__(self, msg: str):\n        super().__init__(msg)\n\n    def show(self):\n        print self.message\n\nclass Exc2(Exception):\n    def __init__(self, msg: str):\n        super().__init__(msg)\n\n    def show(self):\n        print self.message\n\nclass A(Exception):\n    def __init__(self, msg: str):\n        super().__init__(msg)\n\nclass B(Exception):\n    def __init__(self, msg: str):\n        super().__init__(msg)\n\nclass C(Exception):\n    def __init__(self, msg: str):\n        super().__init__(msg)\n\nclass D(Exception):\n    def __init__(self, msg: str):\n        super().__init__(msg)\n\nclass E(Exception):\n    def __init__(self, msg: str):\n        super().__init__(msg)\n\ndef foo1(x):\n    if x:\n        raise Exc1('e1')\n    else:\n        raise Exc2('e2')\n\ndef foo2(x):\n    foo1(x)\n\ndef foo(x):\n    foo2(x)\n\ndef square(x):\n    return x * x\n\ndef bar(x):\n    try:\n        print 'try'\n        foo(x)\n        print 'error'\n    except Exc1 as e:\n        print 'catch Exc1 1'\n        print e.message\n        print 'catch Exc1 2'\n    except Exc2 as e:\n        print 'catch Exc2 1'\n        print e.message\n        print 'catch Exc2 2'\n    finally:\n        print 'finally'\n    print 'done'\n\ndef baz(x):\n    try:\n        print 'try 1'\n        square(x)\n        print 'try 2'\n    except Exc1 as e:\n        print 'catch Exc1 1'\n        print e.message\n        print 'catch Exc1 2'\n    except Exc2 as e:\n        print 'catch Exc2 1'\n        print e.message\n        print 'catch Exc2 2'\n    finally:\n        print 'finally'\n    print 'done'\n\ndef baz1(x):\n    try:\n        print 'try 1.1'\n        foo(x)\n        print 'try 1.2'\n    except Exc1 as e:\n        print 'catch Exc1 1.1'\n        print e.message\n        print 'catch Exc1 1.2'\n    finally:\n        print 'finally 1'\n    print 'done 1'\n\ndef baz2(x):\n    try:\n        print 'try 2.1'\n        baz1(x)\n        print 'try 2.2'\n    except:\n        print 'catch Exc2'\n    finally:\n        print 'finally 2'\n    print 'done 2'\n\ndef nest1(b):\n    if b:\n        raise C('C')\n    else:\n        raise E('E')\n\ndef nest2(b):\n    try:\n        try:\n            try:\n                try:\n                    nest1(b)\n                except A:\n                    print 'A'\n                finally:\n                    print 'f A'\n            except B:\n                print 'B'\n            finally:\n                print 'f B'\n        except C as c:\n            print c.message\n        finally:\n            print 'f C'\n    except D:\n        print 'D'\n    finally:\n        print 'f D'\n\ndef nest3(b):\n    try:\n        nest2(b)\n    except:\n        print 'except'\n    finally:\n        print 'done'\n\ndef finally_return(x):\n    try:\n        try:\n            return 'A'\n        finally:\n            if x < 5:\n                return 'B'\n    finally:\n        if x > 10:\n            return 'C'\n\ndef finally_return_void():\n    try:\n        try:\n            print 'A'\n            return\n        finally:\n            print 'B'\n            return\n    finally:\n        print 'C'\n\ndef finally_break_continue1():\n    try:\n        for i in range(3):\n            try:\n                continue\n            finally:\n                print i\n                continue\n            print 'X'\n    finally:\n        print 'f'\n\ndef finally_break_continue2():\n    try:\n        for i in range(5):\n            try:\n                if i == 4:\n                    continue\n\n                for j in range(i):\n                    try:\n                        try:\n                            if j == 3:\n                                break\n                            elif j == 1:\n                                continue\n                            print j\n                        finally:\n                            print 'f1'\n                    finally:\n                        print 'f2'\n                        if j == 4:\n                            break\n            finally:\n                print 'f3'\n    finally:\n        print 'f4'\n\ndef finally_break_continue3(n):\n    while n != 0:\n        print 'A'\n        try:\n            while n != 0:\n                print 'B'\n                if n == 42:\n                    print 'C'\n                    n -= 1\n                    continue\n                try:\n                    print 'D'\n                    if n > 0:\n                        print 'E'\n                        continue\n                    else:\n                        print 'F'\n                        break\n                finally:\n                    print('G')\n                    return -1\n            print 'H'\n            return -2\n        finally:\n            print 'I'\n            return n + 1\n\ndef test_try_with_loop1(_):\n    try:\n        print 'A'\n        while True:\n            print 'B'\n            try:\n                print 'C'\n                raise ValueError()\n                assert False\n            except ValueError:\n                print 'D'\n                break\n    finally:\n        print 'E'\n\ndef test_try_with_loop2(_):\n    try:\n        print 'A'\n        while True:\n            print 'B'\n            try:\n                print 'C'\n                raise ValueError()\n                assert False\n            except:\n                print 'D'\n                break\n    finally:\n        print 'E'\n\ndef test_try_with_loop3(_):\n    try:\n        try:\n            print 'A'\n            while True:\n                print 'B'\n                try:\n                    print 'C'\n                    raise ValueError()\n                    assert False\n                except IOError:\n                    print 'D'\n                    break\n        finally:\n            print 'E'\n    except:\n        print 'F'\n    finally:\n        print 'G'\n\ndef test_try_with_loop4(_):\n    try:\n        try:\n            print 'A'\n            while True:\n                print 'B'\n                try:\n                    print 'C'\n                    raise ValueError()\n                    assert False\n                except:\n                    print 'D'\n                    raise IOError()\n                    break\n        finally:\n            print 'E'\n    except:\n        print 'F'\n    finally:\n        print 'G'\n\n# EXPECT: try\n# EXPECT: catch Exc1 1\n# EXPECT: e1\n# EXPECT: catch Exc1 2\n# EXPECT: finally\n# EXPECT: done\nbar(True)\n\n# EXPECT: try\n# EXPECT: catch Exc2 1\n# EXPECT: e2\n# EXPECT: catch Exc2 2\n# EXPECT: finally\n# EXPECT: done\nbar(0)\n\n# EXPECT: try 1\n# EXPECT: try 2\n# EXPECT: finally\n# EXPECT: done\nbaz(3.14)\n\n# EXPECT: try 2.1\n# EXPECT: try 1.1\n# EXPECT: catch Exc1 1.1\n# EXPECT: e1\n# EXPECT: catch Exc1 1.2\n# EXPECT: finally 1\n# EXPECT: done 1\n# EXPECT: try 2.2\n# EXPECT: finally 2\n# EXPECT: done 2\nbaz2(1)\n\n# EXPECT: try 2.1\n# EXPECT: try 1.1\n# EXPECT: finally 1\n# EXPECT: catch Exc2\n# EXPECT: finally 2\n# EXPECT: done 2\nbaz2(0)\n\n# EXPECT: f A\n# EXPECT: f B\n# EXPECT: C\n# EXPECT: f C\n# EXPECT: f D\n# EXPECT: done\nnest3(True)\n\n# EXPECT: f A\n# EXPECT: f B\n# EXPECT: f C\n# EXPECT: f D\n# EXPECT: except\n# EXPECT: done\nnest3(0)\n\nprint finally_return(3.14)  # EXPECT: B\nprint finally_return(7)     # EXPECT: A\nprint finally_return(11)    # EXPECT: C\n\n# EXPECT: A\n# EXPECT: B\n# EXPECT: C\nfinally_return_void()\n\n# EXPECT: 0\n# EXPECT: 1\n# EXPECT: 2\n# EXPECT: f\nfinally_break_continue1()\n\n# EXPECT: f3\n# EXPECT: 0\n# EXPECT: f1\n# EXPECT: f2\n# EXPECT: f3\n# EXPECT: 0\n# EXPECT: f1\n# EXPECT: f2\n# EXPECT: f1\n# EXPECT: f2\n# EXPECT: f3\n# EXPECT: 0\n# EXPECT: f1\n# EXPECT: f2\n# EXPECT: f1\n# EXPECT: f2\n# EXPECT: 2\n# EXPECT: f1\n# EXPECT: f2\n# EXPECT: f3\n# EXPECT: f3\n# EXPECT: f4\nfinally_break_continue2()\n\n# EXPECT: A\n# EXPECT: B\n# EXPECT: C\n# EXPECT: B\n# EXPECT: D\n# EXPECT: E\n# EXPECT: G\n# EXPECT: I\n# EXPECT: 42\nprint finally_break_continue3(42)\n\n# EXPECT: A\n# EXPECT: B\n# EXPECT: C\n# EXPECT: D\n# EXPECT: E\ntest_try_with_loop1(0)\n\n# EXPECT: A\n# EXPECT: B\n# EXPECT: C\n# EXPECT: D\n# EXPECT: E\ntest_try_with_loop2(0.0)\n\n# EXPECT: A\n# EXPECT: B\n# EXPECT: C\n# EXPECT: E\n# EXPECT: F\n# EXPECT: G\ntest_try_with_loop3('')\n\n# EXPECT: A\n# EXPECT: B\n# EXPECT: C\n# EXPECT: D\n# EXPECT: E\n# EXPECT: F\n# EXPECT: G\ntest_try_with_loop4(False)\n\nclass Foo:\n    i: int\n    def __enter__(self: Foo):\n        print '> foo! ' + str(self.i)\n    def __exit__(self: Foo):\n        print '< foo! ' + str(self.i)\n    def foo(self: Foo):\n        print 'woof'\n\nclass Bar:\n    s: str\n    def __enter__(self: Bar):\n        print '> bar! ' + self.s\n    def __exit__(self: Bar):\n        print '< bar! ' + self.s\n    def bar(self: Bar):\n        print 'meow'\n\ndef test_with():\n    with Foo(0) as f:\n        f.foo()\n    with Foo(1) as f, Bar('s') as b:\n        f.foo()\n        b.bar()\n    with Foo(2), Bar('t') as q:\n        print 'eeh'\n        q.bar()\n\n# EXPECT: > foo! 0\n# EXPECT: woof\n# EXPECT: < foo! 0\n# EXPECT: > foo! 1\n# EXPECT: > bar! s\n# EXPECT: woof\n# EXPECT: meow\n# EXPECT: < bar! s\n# EXPECT: < foo! 1\n# EXPECT: > foo! 2\n# EXPECT: > bar! t\n# EXPECT: eeh\n# EXPECT: meow\n# EXPECT: < bar! t\n# EXPECT: < foo! 2\ntest_with()\n\n\nclass PropClass:\n    x: int\n\n    @property\n    def foo(self: PropClass):\n        raise IOError('foo')\n\ndef test_property_exceptions():\n    try:\n        PropClass(42).foo\n        print 'X'\n    except IOError as e:\n        print e.message\n\n# EXPECT: foo\ntest_property_exceptions()\n\ndef test_empty_raise():\n    def foo(b):\n        if b:\n            raise ValueError('A')\n        else:\n            raise IOError('B')\n\n    def bar(b):\n        try:\n            foo(b)\n            print('X')\n        except IOError as e:\n            print(e)\n            raise\n        except:\n            raise\n\n    def baz(b):\n        try:\n            bar(b)\n        except ValueError as e:\n            print(e)\n            raise\n\n    for b in (False, True):\n        try:\n            baz(b)\n        except:\n            print('C')\n\n# EXPECT: B\n# EXPECT: C\n# EXPECT: A\n# EXPECT: C\ntest_empty_raise()\n\ndef test_raise_from_except_or_else():\n    def div(x, y):\n        if y == 0: raise ZeroDivisionError(\"oops!\")\n        return x // y\n\n    try:\n        try:\n            div(5, 0)\n        except ZeroDivisionError:\n            print('Zero')\n            div(5, 0)\n        finally:\n            print(\"finally\")\n    except:\n        print('caught')\n\n    try:\n        try:\n            div(5, 1)\n        except ZeroDivisionError:\n            print('Zero')\n        else:\n            print('else before')\n            div(5, 0)\n            print('else after')\n        finally:\n            print(\"finally\")\n    except:\n        print('caught')\n\n# EXPECT: Zero\n# EXPECT: finally\n# EXPECT: caught\n# EXPECT: else before\n# EXPECT: finally\n# EXPECT: caught\ntest_raise_from_except_or_else()\n\ndef test_abrupt_break_continue():\n    for i in range(6):\n        print('->', i)\n        try:\n            if i == 0:\n                raise ValueError(\"v1\")\n            if i == 5:\n                raise IOError(\"io1\")\n        except ValueError as e:\n            print(e)\n            break\n        except IOError as e:\n            print(e)\n            break\n        else:\n            if i == 3:\n                raise ZeroDivisionError(\"z1\")\n        finally:\n            if i == 0 or i == 3:\n                continue\n            print(i)\n\n# EXPECT: -> 0\n# EXPECT: v1\n# EXPECT: -> 1\n# EXPECT: 1\n# EXPECT: -> 2\n# EXPECT: 2\n# EXPECT: -> 3\n# EXPECT: -> 4\n# EXPECT: 4\n# EXPECT: -> 5\n# EXPECT: io1\n# EXPECT: 5\ntest_abrupt_break_continue()\n"
  },
  {
    "path": "test/core/generators.codon",
    "content": "v = range(5)\nprint len(v)  # EXPECT: 5\nprint 2 in v  # EXPECT: True\nprint 5 in v  # EXPECT: False\nprint [a for a in v]  # EXPECT: [0, 1, 2, 3, 4]\nprint list(reversed(v))  # EXPECT: [4, 3, 2, 1, 0]\nprint bool(v)  # EXPECT: True\n\nv = range(10, 2, -3)\nprint len(v)  # EXPECT: 3\nprint 13 in v  # EXPECT: False\nprint 10 in v  # EXPECT: True\nprint 7 in v  # EXPECT: True\nprint 4 in v  # EXPECT: True\nprint 1 in v  # EXPECT: False\nprint [a for a in v]  # EXPECT: [10, 7, 4]\nprint list(reversed(v))  # EXPECT: [4, 7, 10]\nprint bool(v)  # EXPECT: True\n\nv = range(10, 2, 3)\nprint len(v)  # EXPECT: 0\nprint 13 in v  # EXPECT: False\nprint 10 in v  # EXPECT: False\nprint 7 in v  # EXPECT: False\nprint 4 in v  # EXPECT: False\nprint 1 in v  # EXPECT: False\nprint [a for a in v]  # EXPECT: []\nprint list(reversed(v))  # EXPECT: []\nprint bool(v)  # EXPECT: False\n\ndef perms[T](elements: list[T]):\n    if len(elements) <=1:\n        yield elements\n    else:\n        for perm in perms(elements[1:]):\n            for i in range(len(elements)):\n                yield perm[:i] + elements[0:1] + perm[i:]\n\nfor a in perms([1,2,3,4]):\n    print a\n\n# EXPECT: [1, 2, 3, 4]\n# EXPECT: [2, 1, 3, 4]\n# EXPECT: [2, 3, 1, 4]\n# EXPECT: [2, 3, 4, 1]\n# EXPECT: [1, 3, 2, 4]\n# EXPECT: [3, 1, 2, 4]\n# EXPECT: [3, 2, 1, 4]\n# EXPECT: [3, 2, 4, 1]\n# EXPECT: [1, 3, 4, 2]\n# EXPECT: [3, 1, 4, 2]\n# EXPECT: [3, 4, 1, 2]\n# EXPECT: [3, 4, 2, 1]\n# EXPECT: [1, 2, 4, 3]\n# EXPECT: [2, 1, 4, 3]\n# EXPECT: [2, 4, 1, 3]\n# EXPECT: [2, 4, 3, 1]\n# EXPECT: [1, 4, 2, 3]\n# EXPECT: [4, 1, 2, 3]\n# EXPECT: [4, 2, 1, 3]\n# EXPECT: [4, 2, 3, 1]\n# EXPECT: [1, 4, 3, 2]\n# EXPECT: [4, 1, 3, 2]\n# EXPECT: [4, 3, 1, 2]\n# EXPECT: [4, 3, 2, 1]\n\n\ndef mysum[T](start: T):\n    m = start\n    while True:\n        a = (yield)\n        if a == -1:\n            break\n        m += a\n    yield m\n\niadder = mysum(0)\nnext(iadder)\nfor i in range(10):\n    iadder.send(i)\nprint(iadder.send(-1))  # EXPECT: 45\n\nfadder = mysum(0.0)\nnext(fadder)\nfor i in range(10):\n    fadder.send(float(i))\nprint(fadder.send(-1.0))  # EXPECT: 45\n\n\n@test\ndef test_generator_in_finally():\n    def foo(n):\n        if not n:\n            raise ValueError('not n')\n        return n\n\n    b = False\n    try:\n        try:\n            a = 1\n            x = 2\n            z = 0\n            for a in (foo(i) * x for i in [a,2,3,4,z,5] * x):\n                pass\n        finally:\n            b = True\n    except ValueError as e:\n        assert e.message == 'not n'\n    assert b\ntest_generator_in_finally()\n"
  },
  {
    "path": "test/core/generics.codon",
    "content": "class A[TA,TB,TC]:\n    a: TA\n    b: TB\n    c: TC\n\n    def dump(a, b, c):\n        print a, b, c\n\n    # non-generic method:\n    def m0(self: A[TA,TB,TC], a: int):\n        print a\n\n    # basic generics:\n    def m1[X](self: A[TA,TB,TC], other: A[X,X,X]):\n        print other.a, other.b, other.c\n\n    # non-generic method referencing outer generics:\n    def m2(a: TA, b: TB, c: TC):\n        A.dump(a, b, c)\n\n    # generic args:\n    def m3(self, other):\n        return self.a\n\n    # lots of nesting:\n    def m4[TD](self: A[TA,TB,TC], d: TD):\n        def m5[TA,TB,TC,TD,TE](a: TA, b: TB, c: TC, d: TD, e: TE):\n            print a, b, c, d, e\n        m5(self.a, self.b, self.c, d, d)\n\n    # instantiating the type:\n    def m5(self):\n        x = A(self.a, self.b, self.c)\n        A.dump(x.a, x.b, x.c)\n\n    # deeply nested generic type:\n    def m6[T](v: array[array[array[T]]]):\n        return v[0][0][0]\n\n# explicit realization:\ndef m7(T: type, S: type):\n    print \"works\"\n\nclass B1[T]:\n    a: T\n    def foo[S](self: S) -> B1[int]:\n        return B1[int](111)\n\nclass B2[T]:\n    a: T\n    def foo[S](self: B2[S]):\n        return B2[int](222)\n\na1 = A(42, 3.14, \"hello\")\na2 = A(1, 2, 3)\nb1 = B1[bool](True).foo()\nb2 = B2[str](\"x\").foo()\n\nv1 = array[array[array[str]]](1)\nv2 = array[array[str]](1)\nv3 = array[str](1)\nv1[0] = v2\nv2[0] = v3\nv3[0] = \"world\"\nf = a2.m0\n\na1.m1(a2)                           # EXPECT: 1 2 3\nA[int,float,str].m2(1, 1.0, \"one\")  # EXPECT: 1 1 one\nA[int,int,int].m2(11, 22, 33)       # EXPECT: 11 22 33\nprint a1.m3(a2)                     # EXPECT: 42\nprint a1.m3(a2)                     # EXPECT: 42\nprint a2.m3(a1)                     # EXPECT: 1\na1.m4(True)                         # EXPECT: 42 3.14 hello True True\na1.m4([1])                          # EXPECT: 42 3.14 hello [1] [1]\na2.m4(\"x\")                          # EXPECT: 1 2 3 x x\na1.m5()                             # EXPECT: 42 3.14 hello\na2.m5()                             # EXPECT: 1 2 3\nprint A.m6(v1)                      # EXPECT: world\nm7(str,float)                       # EXPECT: works\nm7(str,float)                       # EXPECT: works\nm7(float,str)                       # EXPECT: works\nf(99)                               # EXPECT: 99\nprint b1.foo().a                    # EXPECT: 111\nprint b2.foo().a                    # EXPECT: 222\n\n\n# recursive generics with different inner type parameter\ndef foo(a):\n    if not a:\n        foo(True)\n    print a\n# EXPECT: True\n# EXPECT: 0\nfoo(0)\n\ndef bar(a):\n    def baz(x):\n        if not x:\n            bar(True)\n        print(x)\n    baz(a)\n# EXPECT: True\n# EXPECT: 0\nbar(0)\n"
  },
  {
    "path": "test/core/helloworld.codon",
    "content": "print \"hello world\"  # EXPECT: hello world\n"
  },
  {
    "path": "test/core/match.codon",
    "content": "@test\ndef test_bool_match():\n    T, F = True, False\n\n    b = False\n    match T:\n        case True:\n            b = True\n        case False:\n            assert False\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match F:\n        case True:\n            assert False\n        case False:\n            b = True\n        case _:\n            assert False\n    assert b\ntest_bool_match()\n\n@test\ndef test_str_match():\n    s = 'abc'\n    t = 'xyz'\n    e = ''\n\n    b = False\n    match s:\n        case '':\n            assert False\n        case 'abc':\n            b = True\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match e:\n        case '':\n            b = True\n        case 'abc':\n            assert False\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match t:\n        case '':\n            assert False\n        case 'abc':\n            assert False\n        case _:\n            b = True\n    assert b\n\n    b = False\n    match t:\n        case '':\n            assert False\n        case x if len(x) >= 3:\n            b = True\n        case _:\n            assert False\n    assert b\ntest_str_match()\n\n@test\ndef test_tuple_match():\n    t = (42, 99)\n    r = (12, 12)\n\n    b = False\n    match t:\n        case (0, 0):\n            assert False\n        case (42, 99):\n            b = True\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match t:\n        case (0, 0):\n            assert False\n        case (42, 0):\n            assert False\n        case _:\n            b = True\n    assert b\n\n    b = False\n    match t:\n        case (0, 0):\n            assert False\n        case (0, 99):\n            assert False\n        case _:\n            b = True\n    assert b\n\n    b = False\n    match t:\n        case (0, 0):\n            assert False\n        case (a, _) if a == 42:\n            b = True\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match t:\n        case (0, 0):\n            assert False\n        case (a, bb) if 40 < a < bb < 100:\n            b = True\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match t:\n        case (0, 0):\n            assert False\n        case (a, bb) if a == bb:\n            assert False\n        case _:\n            b = True\n    assert b\n\n    b = False\n    match r:\n        case (0, 0):\n            assert False\n        case (a, bb) if a == bb:\n            b = True\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match t:\n        case (0, 0):\n            assert False\n        case (41 ... 43, 98 ... 100):\n            b = True\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match r:\n        case (0, 0):\n            assert False\n        case (41 ... 43, 98 ... 100):\n            assert False\n        case _:\n            b = True\n    assert b\n\n    b = False\n    match t:\n        case (0, 0):\n            assert False\n        case (41 ... 43, 99 | 10) | (11 ... 13, 11 ... 13) | (-1, -1):\n            b = True\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match r:\n        case (0, 0):\n            assert False\n        case (-1, -1) | (41 ... 43, 10 | 99) | (12 | 11, 9 | 11 ... 13):\n            b = True\n        case _:\n            assert False\n    assert b\ntest_tuple_match()\n\n@test\ndef test_int_match():\n    n = 42\n    m = -99\n\n    b = False\n    match n:\n        case 0:\n            assert False\n        case 1:\n            assert False\n        case _:\n            b = True\n    assert b\n\n    b = False\n    match n:\n        case 0:\n            assert False\n        case 42:\n            b = True\n        case 99:\n            assert False\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match n:\n        case 0:\n            assert False\n        case 1:\n            assert False\n        case _:\n            b = True\n    assert b\n\n    b = False\n    match m:\n        case 0:\n            assert False\n        case 42:\n            assert False\n        case -99:\n            b = True\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match m:\n        case 0 ... 10:\n            assert False\n        case 12 ... 42:\n            assert False\n        case 42:\n            assert False\n        case _:\n            b = True\n    assert b\n\n    b = False\n    match n:\n        case 0 ... 10:\n            assert False\n        case 42 ... 100:\n            b = True\n        case 42:\n            assert False\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match n:\n        case t if t < 10:\n            assert False\n        case t if 41 < t < 43:\n            b = True\n        case _:\n            assert False\n    assert b\ntest_int_match()\n\n@test\ndef test_list_match():\n    v = [1, 2, 3, 4, 5]\n    e = list[int]()\n\n    b = False\n    match v:\n        case []:\n            assert False\n        case [1, 2, 3, 4]:\n            assert False\n        case [1, 2, 3, 4, 5]:\n            b = True\n        case [1, 2, 3, 4, 5, 6]:\n            assert False\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match e:\n        case []:\n            b = True\n        case [1, 2, 3, 4]:\n            assert False\n        case [1, 2, 3, 4, 5]:\n            assert False\n        case [1, 2, 3, 4, 5, 6]:\n            assert False\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match e:\n        case [...]:\n            b = True\n        case [1, 2, 3, 4]:\n            assert False\n        case [1, 2, 3, 4, 5]:\n            assert False\n        case [1, 2, 3, 4, 5, 6]:\n            assert False\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match e:\n        case [_]:\n            assert False\n        case [1, 2, 3, 4]:\n            assert False\n        case [1, 2, 3, 4, 5]:\n            assert False\n        case [1, 2, 3, 4, 5, 6]:\n            assert False\n        case _:\n            b = True\n    assert b\n\n    b = False\n    match v:\n        case []:\n            assert False\n        case [1, ..., 4]:\n            assert False\n        case [1, ..., 5]:\n            b = True\n        case [1, ..., 6]:\n            assert False\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match v:\n        case []:\n            assert False\n        case [_, ..., 4]:\n            assert False\n        case [_, ..., 5]:\n            b = True\n        case [_, ..., 6]:\n            assert False\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match [5]:\n        case []:\n            assert False\n        case [_, ..., 4]:\n            assert False\n        case [_, ..., 5]:\n            assert False\n        case [_, ..., 6]:\n            assert False\n        case _:\n            b = True\n    assert b\n\n    b = False\n    match v:\n        case []:\n            assert False\n        case [..., 4]:\n            assert False\n        case [..., 5]:\n            b = True\n        case [..., 6]:\n            assert False\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match v:\n        case []:\n            assert False\n        case [1, ...]:\n            b = True\n        case [2, ...]:\n            assert False\n        case [3, ...]:\n            assert False\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match v:\n        case []:\n            assert False\n        case [2, ..., a, bb] if (a,bb) == (4,5):\n            assert False\n        case [1, ..., a, bb] if (a,bb) == (4,5):\n            b = True\n        case [3, ..., a, bb] if (a,bb) == (4,5):\n            assert False\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match v:\n        case []:\n            assert False\n        case [2, ..., a, _, bb] if (a,bb) == (3,5):\n            assert False\n        case [1, ..., a, _, bb] if (a,bb) == (3,5):\n            b = True\n        case [3, ..., a, _, bb] if (a,bb) == (3,5):\n            assert False\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match v:\n        case []:\n            assert False\n        case [1, _, 3, _, 5]:\n            b = True\n        case [_, _]:\n            assert False\n        case [_]:\n            assert False\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match [[v]]:\n        case [[[]]]:\n            assert False\n        case [[[..., 4]]]:\n            assert False\n        case [[[..., 5]]]:\n            b = True\n        case [[[..., 6]]]:\n            assert False\n        case _:\n            assert False\n    assert b\n\n    b = False\n    match [[v]]:\n        case [[[]]]:\n            assert False\n        case [[[1, _, 3, _, 5]]]:\n            b = True\n        case [[[_, _]]]:\n            assert False\n        case [[[_]]]:\n            assert False\n        case _:\n            assert False\n    assert b\ntest_list_match()\n"
  },
  {
    "path": "test/core/numerics.codon",
    "content": "import operator as op\nimport math\n\nNAN = math.nan\nINF = math.inf\nNINF = -math.inf\n\n@test\ndef test_py_numerics_int():\n    one = 1\n    iz = 0\n    fz = 0.0\n    n = 0\n\n    # through function (not optimized / pre-evaluated)\n    assert op.floordiv(-5, 2) == -3\n    assert op.floordiv(-5, 2.0) == -3.0\n    assert op.truediv(-5, 2) == -2.5\n    assert op.truediv(-5, 2.0) == -2.5\n    assert op.mod(-10, 3) == 2\n    assert op.mod(-1, 0.3) == 0.19999999999999996\n    assert divmod(-10, 3) == (-4, 2)\n    assert divmod(-1, 0.3) == (-4.0, 0.19999999999999996)\n\n    # with vars (evaluated in IR)\n    a = -5\n    b = 2\n    c = 2.0\n    d = -10\n    e = 3\n    f = -1\n    g = 0.3\n    assert a // b == -3\n    assert a // c == -3.0\n    assert a / b == -2.5\n    assert a / c == -2.5\n    assert d % e == 2\n    assert f % g == 0.19999999999999996\n\n    # constant (evaluated statically by parser)\n    assert -5 // 2 == -3\n    assert -10 % 3 == 2\n\n    # errors\n    try:\n        print(one // fz)\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'float floor division by zero'\n        n += 1\n\n    try:\n        print(one // iz)\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'integer division or modulo by zero'\n        n += 1\n\n    try:\n        print(one / fz)\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'float division by zero'\n        n += 1\n\n    try:\n        print(one / iz)\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'division by zero'\n        n += 1\n\n    try:\n        print(one % fz)\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'float modulo'\n        n += 1\n\n    try:\n        print(one % iz)\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'integer division or modulo by zero'\n        n += 1\n\n    try:\n        print(divmod(one, iz))\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'integer division or modulo by zero'\n        n += 1\n\n    try:\n        print(divmod(one, fz))\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'float divmod()'\n        n += 1\n\n    assert n == 8\n\n@test\ndef test_py_numerics_float():\n    one = 1.0\n    iz = 0\n    fz = 0.0\n    n = 0\n\n    # through function (not optimized / pre-evaluated)\n    assert op.floordiv(-5.6, 2) == -3.0\n    assert op.floordiv(-5.6, 2.0) == -3.0\n    assert op.truediv(-5.6, 2) == -2.8\n    assert op.truediv(-5.6, 2.0) == -2.8\n    assert op.mod(-10.0, 3) == 2.0\n    assert op.mod(-1.0, 0.3) == 0.19999999999999996\n    assert divmod(-10.0, 3) == (-4.0, 2.0)\n    assert divmod(-1.0, 0.3) == (-4.0, 0.19999999999999996)\n\n    # with vars (evaluated in IR)\n    a = -5.6\n    b = 2\n    c = 2.0\n    d = -10.0\n    e = 3\n    f = -1.0\n    g = 0.3\n    assert a // b == -3\n    assert a // c == -3.0\n    assert a / b == -2.8\n    assert a / c == -2.8\n    assert d % e == 2\n    assert f % g == 0.19999999999999996\n\n    try:\n        print(one // fz)\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'float floor division by zero'\n        n += 1\n\n    try:\n        print(one // iz)\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'float floor division by zero'\n        n += 1\n\n    try:\n        print(one / fz)\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'float division by zero'\n        n += 1\n\n    try:\n        print(one / iz)\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'float division by zero'\n        n += 1\n\n    try:\n        print(one % fz)\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'float modulo'\n        n += 1\n\n    try:\n        print(one % iz)\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'float modulo'\n        n += 1\n\n    try:\n        print(divmod(one, iz))\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'float divmod()'\n        n += 1\n\n    try:\n        print(divmod(one, fz))\n        assert False\n    except ZeroDivisionError as e:\n        assert str(e) == 'float divmod()'\n        n += 1\n\n    assert n == 8\n\n\ndef close(a: float, b: float, epsilon: float = 1e-7):\n    return abs(a - b) <= epsilon\n\n\n@test\ndef test_isnan():\n    assert math.isnan(float(\"nan\")) == True\n    assert math.isnan(4.0) == False\n\n\n@test\ndef test_isinf():\n    assert math.isinf(float(\"inf\")) == True\n    assert math.isinf(7.0) == False\n\n\n@test\ndef test_isfinite():\n    assert math.isfinite(1.4) == True\n    assert math.isfinite(0.0) == True\n    assert math.isfinite(NAN) == False\n    assert math.isfinite(INF) == False\n    assert math.isfinite(NINF) == False\n\n\n@test\ndef test_ceil():\n    assert math.ceil(3.3) == 4\n    assert math.ceil(0.5) == 1\n    assert math.ceil(1.0) == 1\n    assert math.ceil(1.5) == 2\n    assert math.ceil(-0.5) == 0\n    assert math.ceil(-1.0) == -1\n    assert math.ceil(-1.5) == -1\n\n\n@test\ndef test_floor():\n    assert math.floor(3.3) == 3\n    assert math.floor(0.5) == 0\n    assert math.floor(1.0) == 1\n    assert math.floor(1.5) == 1\n    assert math.floor(-0.5) == -1\n    assert math.floor(-1.0) == -1\n    assert math.floor(-1.5) == -2\n\n\n@test\ndef test_fabs():\n    assert math.fabs(-1.0) == 1\n    assert math.fabs(0.0) == 0\n    assert math.fabs(1.0) == 1\n\n\n@test\ndef test_fmod():\n    assert math.fmod(10.0, 1.0) == 0.0\n    assert math.fmod(10.0, 0.5) == 0.0\n    assert math.fmod(10.0, 1.5) == 1.0\n    assert math.fmod(-10.0, 1.0) == -0.0\n    assert math.fmod(-10.0, 0.5) == -0.0\n    assert math.fmod(-10.0, 1.5) == -1.0\n\n\n@test\ndef test_exp():\n    assert math.exp(0.0) == 1\n    assert math.exp(-1.0) == 1 / math.e\n    assert math.exp(1.0) == math.e\n\n\n@test\ndef test_expm1():\n    assert math.expm1(0.0) == 0\n    assert close(math.expm1(1.0), 1.7182818284590453)\n    assert close(math.expm1(3.0), 19.085536923187668)\n    assert close(math.expm1(5.0), 147.4131591025766)\n    assert math.expm1(INF) == INF\n    assert math.expm1(NINF) == -1\n    assert math.isnan(math.expm1(NAN)) == True\n\n\n@test\ndef test_ldexp():\n    assert math.ldexp(0.0, 1) == 0.0\n    assert math.ldexp(1.0, 1) == 2.0\n    assert math.ldexp(1.0, -1) == 0.5\n    assert math.ldexp(-1.0, 1) == -2.0\n    assert math.ldexp(0.0, 1) == 0.0\n    assert math.ldexp(1.0, -1000000) == 0.0\n    assert math.ldexp(-1.0, -1000000) == -0.0\n    assert math.ldexp(INF, 30) == INF\n    assert math.ldexp(NINF, -213) == NINF\n    assert math.isnan(math.ldexp(NAN, 0)) == True\n\n\n@test\ndef test_log():\n    assert math.log(1.0 / math.e) == -1\n    assert math.log(1.0) == 0\n    assert math.log(math.e) == 1\n\n\n@test\ndef test_log2():\n    assert math.log2(1.0) == 0.0\n    assert math.log2(2.0) == 1.0\n    assert math.log2(4.0) == 2.0\n    assert math.log2(2.0 ** 1023) == 1023.0\n    try:\n        math.log2(-1.5)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n    try:\n        math.log2(NINF)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n    assert math.isnan(math.log2(NAN)) == True\n\n\n@test\ndef test_log10():\n    assert math.log10(0.1) == -1\n    assert math.log10(1.0) == 0\n    assert math.log10(10.0) == 1\n    assert math.log10(10000.0) == 4\n\n\n@test\ndef test_degrees():\n    assert math.degrees(math.pi) == 180.0\n    assert math.degrees(math.pi / 2) == 90.0\n    assert math.degrees(-math.pi / 4) == -45.0\n    assert math.degrees(0.0) == 0.0\n\n\n@test\ndef test_radians():\n    assert math.radians(180.0) == math.pi\n    assert math.radians(90.0) == math.pi / 2\n    assert math.radians(-45.0) == -math.pi / 4\n    assert math.radians(0.0) == 0.0\n\n\n@test\ndef test_sqrt():\n    assert math.sqrt(4.0) == 2\n    assert math.sqrt(0.0) == 0\n    assert math.sqrt(1.0) == 1\n    try:\n        math.sqrt(-1.0)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n\n\n@test\ndef test_pow():\n    assert math.pow(0.0, 1.0) == 0\n    assert math.pow(1.0, 0.0) == 1\n    assert math.pow(2.0, 1.0) == 2\n    assert math.pow(2.0, -1.0) == 0.5\n    assert math.pow(-0.0, 3.0) == -0.0\n    assert math.pow(-0.0, 2.3) == 0.0\n    assert math.pow(-0.0, 0.0) == 1\n    assert math.pow(-0.0, -0.0) == 1\n    assert math.pow(-2.0, 2.0) == 4.0\n    assert math.pow(-2.0, 3.0) == -8.0\n    assert math.pow(-2.0, -3.0) == -0.125\n    assert math.pow(INF, 1.0) == INF\n    assert math.pow(NINF, 1.0) == NINF\n    assert math.pow(1.0, INF) == 1\n    assert math.pow(1.0, NINF) == 1\n    assert math.isnan(math.pow(NAN, 1.0)) == True\n    assert math.isnan(math.pow(2.0, NAN)) == True\n    assert math.isnan(math.pow(0.0, NAN)) == True\n    assert math.pow(1.0, NAN) == 1\n    try:\n        math.pow(10.0, 400.0)\n        assert False\n    except OverflowError as e:\n        assert str(e) == 'math range error'\n\n\n@test\ndef test_acos():\n    assert math.acos(-1.0) == math.pi\n    assert math.acos(0.0) == math.pi / 2\n    assert math.acos(1.0) == 0\n    assert math.isnan(math.acos(NAN)) == True\n\n\n@test\ndef test_asin():\n    assert math.asin(-1.0) == -math.pi / 2\n    assert math.asin(0.0) == 0\n    assert math.asin(1.0) == math.pi / 2\n    assert math.isnan(math.asin(NAN)) == True\n\n\n@test\ndef test_atan():\n    assert math.atan(-1.0) == -math.pi / 4\n    assert math.atan(0.0) == 0\n    assert math.atan(1.0) == math.pi / 4\n    assert math.atan(INF) == math.pi / 2\n    assert math.atan(NINF) == -math.pi / 2\n    assert math.isnan(math.atan(NAN)) == True\n\n\n@test\ndef test_atan2():\n    assert math.atan2(-1.0, 0.0) == -math.pi / 2\n    assert math.atan2(-1.0, 1.0) == -math.pi / 4\n    assert math.atan2(0.0, 1.0) == 0\n    assert math.atan2(1.0, 1.0) == math.pi / 4\n    assert math.atan2(1.0, 0.0) == math.pi / 2\n    assert math.atan2(-0.0, 0.0) == -0\n    assert math.atan2(-0.0, 2.3) == -0\n    assert math.atan2(0.0, -2.3) == math.pi\n    assert math.atan2(INF, NINF) == math.pi * 3 / 4\n    assert math.atan2(INF, 2.3) == math.pi / 2\n    assert math.isnan(math.atan2(NAN, 0.0)) == True\n\n\n@test\ndef test_cos():\n    assert math.cos(0.0) == 1\n    assert close(math.cos(math.pi / 2), 6.123233995736766e-17)\n    assert close(math.cos(-math.pi / 2), 6.123233995736766e-17)\n    assert math.cos(math.pi) == -1\n\n    try:\n        math.cos(INF)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n\n    try:\n        math.cos(NINF)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n\n    assert math.isnan(math.cos(NAN)) == True\n\n\n@test\ndef test_sin():\n    assert math.sin(0.0) == 0\n    assert math.sin(math.pi / 2) == 1\n    assert math.sin(-math.pi / 2) == -1\n\n    try:\n        math.sin(INF)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n\n    try:\n        math.sin(NINF)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n\n    assert math.isnan(math.sin(NAN)) == True\n\n\n@test\ndef test_hypot():\n    assert math.hypot(12.0, 5.0) == 13\n    assert math.hypot(12.0 / 32.0, 5.0 / 32) == 13 / 32\n    assert math.hypot(0.0, 0.0) == 0\n    assert math.hypot(-3.0, 4.0) == 5\n    assert math.hypot(3.0, 4.0) == 5\n\n\n@test\ndef test_tan():\n    assert math.tan(0.0) == 0\n    assert close(math.tan(math.pi / 4), 0.9999999999999999)\n    assert close(math.tan(-math.pi / 4), -0.9999999999999999)\n\n    try:\n        math.tan(INF)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n\n    try:\n        math.tan(NINF)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n\n    assert math.isnan(math.tan(NAN)) == True\n\n\n@test\ndef test_cosh():\n    assert math.cosh(0.0) == 1\n    assert math.cosh(2.0) - 2 * math.cosh(1.0) ** 2 == -1\n    assert math.cosh(INF) == INF\n    assert math.cosh(NINF) == INF\n    assert math.isnan(math.cosh(NAN)) == True\n\n\n@test\ndef test_sinh():\n    assert math.sinh(0.0) == 0\n    assert math.sinh(1.0) + math.sinh(-1.0) == 0\n    assert math.sinh(INF) == INF\n    assert math.sinh(NINF) == NINF\n    assert math.isnan(math.sinh(NAN)) == True\n\n\n@test\ndef test_tanh():\n    assert math.tanh(0.0) == 0\n    assert math.tanh(1.0) + math.tanh(-1.0) == 0\n    assert math.tanh(INF) == 1\n    assert math.tanh(NINF) == -1\n    assert math.isnan(math.tanh(NAN)) == True\n\n\n@test\ndef test_acosh():\n    assert math.acosh(1.0) == 0\n    assert close(math.acosh(2.0), 1.3169578969248166)\n    assert math.acosh(INF) == INF\n    assert math.isnan(math.acosh(NAN)) == True\n    try:\n        math.acosh(-1.0)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n\n\n@test\ndef test_asinh():\n    assert math.asinh(0.0) == 0\n    assert close(math.asinh(1.0), 0.881373587019543)\n    assert close(math.asinh(-1.0), -0.881373587019543)\n    assert math.asinh(INF) == INF\n    assert math.isnan(math.asinh(NAN)) == True\n    assert math.asinh(NINF) == NINF\n\n\n@test\ndef test_atanh():\n    assert math.atanh(0.0) == 0\n    assert close(math.atanh(0.5), 0.5493061443340549)\n    assert close(math.atanh(-0.5), -0.5493061443340549)\n\n    try:\n        math.atanh(INF)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n\n    try:\n        math.atanh(NINF)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n\n    assert math.isnan(math.atanh(NAN)) == True\n\n\n@test\ndef test_copysign():\n    assert math.copysign(1.0, -0.0) == -1\n    assert math.copysign(1.0, 42.0) == 1\n    assert math.copysign(1.0, -42.0) == -1\n    assert math.copysign(3.0, 0.0) == 3\n    assert math.copysign(INF, 0.0) == INF\n    assert math.copysign(INF, -0.0) == NINF\n    assert math.copysign(NINF, 0.0) == INF\n    assert math.copysign(NINF, -0.0) == NINF\n    assert math.copysign(1.0, INF) == 1\n    assert math.copysign(1.0, NINF) == -1\n    assert math.copysign(INF, INF) == INF\n    assert math.copysign(INF, NINF) == NINF\n    assert math.copysign(NINF, INF) == INF\n    assert math.copysign(NINF, NINF) == NINF\n    assert math.isnan(math.copysign(NAN, 1.0)) == True\n    assert math.isnan(math.copysign(NAN, INF)) == True\n    assert math.isnan(math.copysign(NAN, NINF)) == True\n    assert math.isnan(math.copysign(NAN, NAN)) == True\n\n\n@test\ndef test_log1p():\n    assert close(math.log1p(2.0), 1.0986122886681098)\n    assert close(math.log1p(2.0 ** 90), 62.383246250395075)\n    assert close(math.log1p(2.0 ** 300), 207.94415416798358)\n    assert math.log1p(INF) == INF\n    try:\n        math.log1p(-1.0)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n\n\n@test\ndef test_trunc():\n    assert math.trunc(1.0) == 1\n    assert math.trunc(-1.0) == -1\n    assert math.trunc(1.5) == 1\n    assert math.trunc(-1.5) == -1\n    assert math.trunc(1.99999999) == 1\n    assert math.trunc(-1.99999999) == -1\n    assert math.trunc(0.99999999) == 0\n    assert math.trunc(-100.999) == -100\n\n\n@test\ndef test_erf():\n    assert close(math.erf(1.0), 0.8427007929497148)\n    assert math.erf(0.0) == 0\n    assert close(math.erf(3.0), 0.9999779095030015)\n    assert math.erf(256.0) == 1.0\n    assert math.erf(INF) == 1.0\n    assert math.erf(NINF) == -1.0\n    assert math.isnan(math.erf(NAN)) == True\n\n\n@test\ndef test_erfc():\n    assert math.erfc(0.0) == 1.0\n    assert close(math.erfc(1.0), 0.15729920705028516)\n    assert close(math.erfc(2.0), 0.0046777349810472645)\n    assert close(math.erfc(-1.0), 1.8427007929497148)\n    assert math.erfc(INF) == 0.0\n    assert math.erfc(NINF) == 2.0\n    assert math.isnan(math.erfc(NAN)) == True\n\n\n@test\ndef test_gamma():\n    assert close(math.gamma(6.0), 120.0)\n    assert close(math.gamma(1.0), 1.0)\n    assert close(math.gamma(2.0), 1.0)\n    assert close(math.gamma(3.0), 2.0)\n    try:\n        math.gamma(-1.0)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n    assert math.gamma(INF) == INF\n    try:\n        math.gamma(NINF)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n    assert math.isnan(math.gamma(NAN)) == True\n\n\n@test\ndef test_lgamma():\n    assert math.lgamma(1.0) == 0.0\n    assert math.lgamma(2.0) == 0.0\n    #assert math.lgamma(-1.0) == INF  # Python's custom lgamma gives math domain error\n    assert math.lgamma(INF) == INF\n    assert math.lgamma(NINF) == INF\n    assert math.isnan(math.lgamma(NAN)) == True\n\n\n@test\ndef test_remainder():\n    assert math.remainder(2.0, 2.0) == 0.0\n    assert math.remainder(-4.0, 1.0) == -0.0\n    assert close(math.remainder(-3.8, 1.0), 0.20000000000000018)\n    assert close(math.remainder(3.8, 1.0), -0.20000000000000018)\n    try:\n        math.remainder(INF, 1.0)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n    try:\n        math.remainder(NINF, 1.0)\n        assert False\n    except ValueError as e:\n        assert str(e) == 'math domain error'\n    assert math.isnan(math.remainder(NAN, 1.0)) == True\n\n\n@test\ndef test_gcd():\n    assert math.gcd(0.0, 0.0) == 0\n    assert math.gcd(1.0, 0.0) == 1\n    assert math.gcd(-1.0, 0.0) == 1\n    assert math.gcd(0.0, -1.0) == 1\n    assert math.gcd(0.0, 1.0) == 1\n    assert math.gcd(7.0, 1.0) == 1\n    assert math.gcd(7.0, -1.0) == 1\n    assert math.gcd(-23.0, 15.0) == 1\n    assert math.gcd(120.0, 84.0) == 12\n    assert math.gcd(84.0, -120.0) == 12\n\n\n@test\ndef test_frexp():\n    assert math.frexp(-2.0) == (-0.5, 2)\n    assert math.frexp(-1.0) == (-0.5, 1)\n    assert math.frexp(0.0) == (0.0, 0)\n    assert math.frexp(1.0) == (0.5, 1)\n    assert math.frexp(2.0) == (0.5, 2)\n    assert math.frexp(INF)[0] == INF\n    assert math.frexp(NINF)[0] == NINF\n    assert math.isnan(math.frexp(NAN)[0]) == True\n\n\n@test\ndef test_modf():\n    assert math.modf(1.5) == (0.5, 1.0)\n    assert math.modf(-1.5) == (-0.5, -1.0)\n    assert math.modf(math.inf) == (0.0, INF)\n    assert math.modf(-math.inf) == (-0.0, NINF)\n    modf_nan = math.modf(NAN)\n    assert math.isnan(modf_nan[0]) == True\n    assert math.isnan(modf_nan[1]) == True\n\n\n@test\ndef test_isclose():\n    assert math.isclose(1.0 + 1.0, 1.000000000001 + 1.0) == True\n    assert math.isclose(2.90909324093284, 2.909093240932844234234234234) == True\n    assert math.isclose(2.90909324093284, 2.9) == False\n    assert math.isclose(2.90909324093284, 2.90909324) == True\n    assert math.isclose(2.90909324, 2.90909325) == False\n    assert math.isclose(NAN, 2.9) == False\n    assert math.isclose(2.9, NAN) == False\n    assert math.isclose(INF, INF) == True\n    assert math.isclose(NINF, NINF) == True\n    assert math.isclose(NINF, INF) == False\n    assert math.isclose(INF, NINF) == False\n\ntest_py_numerics_int()\ntest_py_numerics_float()\n\ntest_isnan()\ntest_isinf()\ntest_isfinite()\ntest_ceil()\ntest_floor()\ntest_fabs()\ntest_fmod()\ntest_exp()\ntest_expm1()\ntest_ldexp()\ntest_log()\ntest_log2()\ntest_log10()\ntest_degrees()\ntest_radians()\ntest_sqrt()\ntest_pow()\ntest_acos()\ntest_asin()\ntest_atan()\ntest_atan2()\ntest_cos()\ntest_sin()\ntest_hypot()\ntest_tan()\ntest_cosh()\ntest_sinh()\ntest_tanh()\ntest_acosh()\ntest_asinh()\ntest_atanh()\ntest_copysign()\ntest_log1p()\ntest_trunc()\ntest_erf()\ntest_erfc()\ntest_gamma()\ntest_lgamma()\ntest_remainder()\ntest_gcd()\ntest_frexp()\ntest_modf()\ntest_isclose()\n"
  },
  {
    "path": "test/core/parser.codon",
    "content": "x, y = 1, 2\nprint x, y # EXPECT: 1 2\n(x, y) = (3, 4)\nprint x, y # EXPECT: 3 4\nx, y = (1, 2)\nprint x, y # EXPECT: 1 2\n(x, y) = 3, 4\nprint x, y # EXPECT: 3 4\n(x, y) = [3, 4]\nprint x, y # EXPECT: 3 4\n[x, y] = [1, 2]\nprint x, y # EXPECT: 1 2\n\n# TODO generator/range slices?\n# a, b, *tx, c, d = range(10)\n# print a, b, tx, c, d # 0 1 [2, 3, 4, 5, 6, 7] 8 9\n\nl = list(iter(range(10)))\n[a, b, *lx, c, d] = l\nprint a, b, lx, c, d # EXPECT: 0 1 [2, 3, 4, 5, 6, 7] 8 9\na, b, *lx = l\nprint a, b, lx # EXPECT: 0 1 [2, 3, 4, 5, 6, 7, 8, 9]\n*lx, a, b = l\nprint lx, a, b # EXPECT: [0, 1, 2, 3, 4, 5, 6, 7] 8 9\n\n*xz, a, b = (1, 2, 3, 4, 5)\nprint xz, a, b # EXPECT: (1, 2, 3) 4 5\n\n\n# *x = range(5) # ERR\n(*ex,) = [1, 2, 3]\nprint ex # EXPECT: [1, 2, 3]\n\n# https://stackoverflow.com/questions/6967632/unpacking-extended-unpacking-and-nested-extended-unpacking\n\nsa, sb = 'XY'\nprint sa, sb # EXPECT: X Y\n(sa, sb), sc = 'XY', 'Z'\nprint sa, sb, sc # EXPECT: X Y Z\n# (sa, sb), sc = 'XYZ' # ERROR:\n\nsa, *la = 'X'\nprint sa, la, 1 # EXPECT: X [] 1\n\nsa, *la = 'XYZ'\nprint sa, la # EXPECT: X ['Y', 'Z']\n\n# a, *b, c, *d = 1,2,3,4,5             # ERROR -- two starred expressions in assignment\n\n(xa,xb), *xc, xd = [1,2],'this'\nprint xa, xb, xc, xd # EXPECT: 1 2 () this\n\n(a, b), (sc, *sl) = [1,2], 'this'\nprint a, b, sc, sl # EXPECT: 1 2 t ['h', 'i', 's']\n\n# Issue #116\ndef foo():\n    a = 42\n    def bar():\n        print a\n    bar()\nfoo() # EXPECT: 42\n"
  },
  {
    "path": "test/core/pipeline.codon",
    "content": "from threading import Lock, RLock\n\nn = 0\nf = 0.0\ng = f32(0.0)\n\n@atomic\ndef inc(_):\n    global n, f, g\n    n += 1\n    f += 1.0\n    g += f32(1.0)\n    return 0\n\n@atomic\ndef dec(_):\n    global n, f, g\n    n -= 1\n    f -= 1.0\n    g -= f32(1.0)\n    return 0\n\nlock = Lock()\ndef inc_lock(_):\n    global n\n    with lock:\n        n += 1\n    return 0\n\ndef dec_lock(_):\n    global n\n    with lock:\n        n -= 1\n    return 0\n\nrlock = RLock()\ndef inc_rlock(_):\n    global n\n    with rlock:\n        n += 1\n    return 0\n\ndef dec_rlock(_):\n    global n\n    with rlock:\n        n -= 1\n    return 0\n\ndef foo(_):\n    yield 0\n\n@test\ndef test_parallel_pipe(m: int):\n    global n, f\n    n, f = 0, 0.0\n    range(m) |> iter ||> inc\n    assert n == m\n    assert f == float(m)\n    assert g == f32(m)\n    range(m) |> iter ||> dec\n    assert n == 0\n    assert f == 0.0\n    assert g == f32(0.0)\n    range(m) |> iter ||> inc |> dec\n    assert n == 0\n    assert f == 0.0\n    assert g == f32(0.0)\n\n    n = 0\n    range(m) |> iter ||> inc_lock\n    assert n == m\n    range(m) |> iter ||> dec_lock\n    assert n == 0\n    range(m) |> iter ||> inc_lock |> dec_lock\n    assert n == 0\n\n    n = 0\n    range(m) |> iter ||> inc_rlock\n    assert n == m\n    range(m) |> iter ||> dec_rlock\n    assert n == 0\n    range(m) |> iter ||> inc_rlock |> dec_rlock\n    assert n == 0\n\n@test\ndef test_nested_parallel_pipe(m: int):\n    global n\n    n = 0\n    range(m) |> iter ||> inc |> foo ||> dec\n    assert n == 0\n\ntest_parallel_pipe(0)\ntest_parallel_pipe(1)\ntest_parallel_pipe(10)\ntest_parallel_pipe(10000)\n\ntest_nested_parallel_pipe(0)\ntest_nested_parallel_pipe(1)\ntest_nested_parallel_pipe(10)\ntest_nested_parallel_pipe(10000)\n"
  },
  {
    "path": "test/core/range.codon",
    "content": "# test adapted from https://github.com/python/cpython/blob/master/Lib/test/test_range.py\n\n@test\ndef test_range():\n    assert list(range(3)) == [0, 1, 2]\n    assert list(range(1, 5)) == [1, 2, 3, 4]\n    assert list(range(0)) == list[int]()\n    assert list(range(-3)) == list[int]()\n    assert list(range(1, 10, 3)) == [1, 4, 7]\n    assert list(range(5, -5, -3)) == [5, 2, -1, -4]\n\n    a = 10\n    b = 100\n    c = 50\n\n    assert list(range(a, a+2)) == [a, a+1]\n    assert list(range(a+2, a, -1)) == [a+2, a+1]\n    assert list(range(a+4, a, -2)) == [a+4, a+2]\n\n    s = list(range(a, b, c))\n    assert a in s\n    assert b not in s\n    assert len(s) == 2\n\n    s = list(range(b, a, -c))\n    assert b in s\n    assert a not in s\n    assert len(s) == 2\n\n    s = list(range(-a, -b, -c))\n    assert -a in s\n    assert -b not in s\n    assert len(s) == 2\n\n    val_err = False\n    try:\n        range(1, 2, 0)\n    except ValueError:\n        val_err = True\n    assert val_err\n\n@test\ndef test_index():\n    u = range(2)\n    assert u.index(0) == 0\n    assert u.index(1) == 1\n    val_err = False\n    try:\n        u.index(2)\n    except ValueError:\n        val_err = True\n    assert val_err\n\n    u = range(-2, 3)\n    assert u.count(0) == 1\n    assert u.index(0) == 2\n\n    a = range(-2, 3)\n    assert a.index(0) == 2\n\n    assert range(1, 10, 3).index(4) == 1\n    assert range(1, -10, -3).index(-5) == 2\n\n@test\ndef test_count():\n    assert range(3).count(-1) == 0\n    assert range(3).count(0) == 1\n    assert range(3).count(1) == 1\n    assert range(3).count(2) == 1\n    assert range(3).count(3) == 0\n    assert range(3).index(1) == 1\n    assert range(7, 0, -2).count(5) == 1\n    assert range(7, 0, -2).count(-2) == 0\n    assert range(7, 0, -1).count(0) == 0\n    assert range(7, 0, -1).count(1) == 1\n\n@test\ndef test_repr():\n    assert str(range(1)) == 'range(0, 1)'\n    assert str(range(1, 2)) == 'range(1, 2)'\n    assert str(range(1, 2, 3)) == 'range(1, 2, 3)'\n\n@test\ndef test_strided_limits():\n    r = range(0, 101, 2)\n    assert 0 in r\n    assert 1 not in r\n    assert 2 in r\n    assert 99 not in r\n    assert 100 in r\n    assert 101 not in r\n\n    r = range(0, -20, -1)\n    assert 0 in r\n    assert -1 in r\n    assert -19 in r\n    assert -20 not in r\n\n    r = range(0, -20, -2)\n    assert -18 in r\n    assert -19 not in r\n    assert -20 not in r\n\n@test\ndef test_empty():\n    r = range(0)\n    assert 0 not in r\n    assert 1 not in r\n\n    r = range(0, -10)\n    assert 0 not in r\n    assert -1 not in r\n    assert 1 not in r\n\n@test\ndef test_slice():\n    r = range(10)\n    assert list(r[:2]) == list(r)[:2]\n    assert list(r[0:2]) == list(r)[0:2]\n    assert list(r[0:20]) == list(r)[0:20]\n    assert list(r[:20]) == list(r)[:20]\n    assert list(r[1:2]) == list(r)[1:2]\n    assert list(r[20:30]) == list(r)[20:30]\n    assert list(r[-30:-20]) == list(r)[-30:-20]\n    assert list(r[-1:100:2]) == list(r)[-1:100:2]\n    assert list(r[0:-1]) == list(r)[0:-1]\n    assert list(r[-1:-3:-1]) == list(r)[-1:-3:-1]\n\n    r = range(0)\n    assert list(r[:2]) == list(r)[:2]\n    assert list(r[0:2]) == list(r)[0:2]\n    assert list(r[0:20]) == list(r)[0:20]\n    assert list(r[:20]) == list(r)[:20]\n    assert list(r[1:2]) == list(r)[1:2]\n    assert list(r[20:30]) == list(r)[20:30]\n    assert list(r[-30:-20]) == list(r)[-30:-20]\n    assert list(r[-1:100:2]) == list(r)[-1:100:2]\n    assert list(r[0:-1]) == list(r)[0:-1]\n    assert list(r[-1:-3:-1]) == list(r)[-1:-3:-1]\n\n    r = range(1, 9, 3)\n    assert list(r[:2]) == list(r)[:2]\n    assert list(r[0:2]) == list(r)[0:2]\n    assert list(r[0:20]) == list(r)[0:20]\n    assert list(r[:20]) == list(r)[:20]\n    assert list(r[1:2]) == list(r)[1:2]\n    assert list(r[20:30]) == list(r)[20:30]\n    assert list(r[-30:-20]) == list(r)[-30:-20]\n    assert list(r[-1:100:2]) == list(r)[-1:100:2]\n    assert list(r[0:-1]) == list(r)[0:-1]\n    assert list(r[-1:-3:-1]) == list(r)[-1:-3:-1]\n\n    r = range(8, 0, -3)\n    assert list(r[:2]) == list(r)[:2]\n    assert list(r[0:2]) == list(r)[0:2]\n    assert list(r[0:20]) == list(r)[0:20]\n    assert list(r[:20]) == list(r)[:20]\n    assert list(r[1:2]) == list(r)[1:2]\n    assert list(r[20:30]) == list(r)[20:30]\n    assert list(r[-30:-20]) == list(r)[-30:-20]\n    assert list(r[-1:100:2]) == list(r)[-1:100:2]\n    assert list(r[0:-1]) == list(r)[0:-1]\n    assert list(r[-1:-3:-1]) == list(r)[-1:-3:-1]\n\n@test\ndef test_contains():\n    r = range(10)\n    assert 0 in r\n    assert 1 in r\n    assert 5 in r\n    assert -1 not in r\n    assert 10 not in r\n\n    r = range(9, -1, -1)\n    assert 0 in r\n    assert 1 in r\n    assert 5 in r\n    assert -1 not in r\n    assert 10 not in r\n\n    r = range(0, 10, 2)\n    assert 0 in r\n    assert 1 not in r\n    assert 5 not in r\n    assert -1 not in r\n    assert 10 not in r\n\n    r = range(9, -1, -2)\n    assert 0 not in r\n    assert 1 in r\n    assert 5 in r\n    assert -1 not in r\n    assert 10 not in r\n\n@test\ndef test_reverse_iteration():\n    for r in [range(10),\n              range(0),\n              range(1, 9, 3),\n              range(8, 0, -3),\n              ]:\n        assert list(reversed(r)) == list(r)[::-1]\n\ntest_range()\ntest_index()\ntest_count()\ntest_repr()\ntest_strided_limits()\ntest_empty()\ntest_slice()\ntest_contains()\ntest_reverse_iteration()\n"
  },
  {
    "path": "test/core/serialization.codon",
    "content": "import pickle\nfrom copy import copy\n\n@tuple\nclass MyType:\n    a: i32\n    b: str\n    c: float\n\nclass A:\n    a: int\n    v: list[str]\n\n    def __eq__(self: A, other: A):\n        return self.a == other.a and self.v == other.v\n\n    def __ne__(self: A, other: A):\n        return not (self == other)\n\n    def __hash__(self: A):\n        return self.a\n\n    def __copy__(self: A):\n        return A(self.a, copy(self.v))\n\n@test\ndef test_pickle[T](x: T):\n    import gzip\n    path = 'build/testjar.bin'\n    jar = gzip.open(path, 'wb')\n    pickle.dump(x, jar)\n    jar.close()\n\n    jar = gzopen(path, 'rb')\n    y = pickle.load(jar, T)\n    jar.close()\n\n    assert x == y\n\n@test\ndef test_non_atomic_list_pickle[T](x: list[list[T]]):\n    import gzip\n    ncopy = [copy(a) for a in x]\n    path = 'build/testjar.bin'\n    jar = gzip.open(path, 'wb')\n    pickle.dump(x, jar)\n    jar.close()\n\n    for v in x:\n        v.clear()\n\n    jar = gzopen(path, 'rb')\n    y = pickle.load(jar, list[list[T]])\n    jar.close()\n\n    assert y == ncopy\n\n@test\ndef test_non_atomic_dict_pickle[T](x: dict[str, list[T]]):\n    import gzip\n    ncopy = {k: copy(v) for k,v in x.items()}\n    path = 'build/testjar.bin'\n    jar = gzip.open(path, 'wb')\n    pickle.dump(x, jar)\n    jar.close()\n\n    for v in x.values():\n        v.clear()\n\n    jar = gzopen(path, 'rb')\n    y = pickle.load(jar, dict[str, list[T]])\n    jar.close()\n\n    assert y == ncopy\n\n@test\ndef test_non_atomic_set_pickle(x: set[A]):\n    import gzip\n    ncopy = {copy(a) for a in x}\n    path = 'build/testjar.bin'\n    jar = gzip.open(path, 'wb')\n    pickle.dump(x, jar)\n    jar.close()\n\n    for a in x:\n        a.v.clear()\n\n    jar = gzopen(path, 'rb')\n    y = pickle.load(jar, set[A])\n    jar.close()\n\n    assert y == ncopy\n\ntest_pickle(42)\ntest_pickle(3.14)\ntest_pickle(True)\ntest_pickle(byte(90))\ntest_pickle(UInt[123](123123123))\ntest_pickle(Int[123](-123123123))\ntest_pickle([11, 22, 33, 44])\ntest_pickle({11, 22, 33, 44})\ntest_pickle({11: 1.1, 22: 2.2, 33: 3.3, 44: 4.4})\ntest_pickle('hello world')\ntest_pickle('')\ntest_pickle(MyType(i32(-1001), 'xyz', 5.55))\ntest_pickle((A(1, ['x', 'abc', '1.1.1.1']), 42, A(1000, ['foo'])))\ntest_pickle(['ACGTAAGG', 'TATCTGTT'])\ntest_pickle(list[Int[8]]())\ntest_pickle({'ACGTAAGG', 'CATTTTTA'})\ntest_pickle({'ACGTAAGG'})\ntest_pickle({'ACGTAAGG', 'TTTTGGTT'})\ntest_pickle(set[Int[8]]())\ntest_pickle({'ACGTAAGG': 99, 'TTATTCTT': 42})\ntest_pickle(dict[Int[8],Int[8]]())\ntest_pickle({'ACGTAAGG': 'ACGTAAGG'})\ntest_pickle((42, 3.14, True, byte(90), 'ACGTAAGG', 'ACGTAAGG'))\ntest_pickle(DynamicTuple((111, 222, 333, 444)))\ntest_pickle(DynamicTuple('hello world'))\ntest_pickle(DynamicTuple[int]())\ntest_pickle(DynamicTuple[str]())\ntest_pickle({i32(42): [[{'ACG', 'ACGTAGCG', 'ACGTAGCG'}, {'ACG', 'ACGTAGCG', 'ACGTAGCG'}], list[set[str]](), [set[str]()], [{''}, {'', 'GCGC'}]]})\n\ntest_non_atomic_list_pickle([[3,2,1], [-1,-2,-3], [111,999,888,777], list[int]()])\ntest_non_atomic_dict_pickle({'first': [3,2,1], 'second': [-1,-2,-3], 'third': [111,999,888,777], 'fourth:': list[int]()})\ntest_non_atomic_set_pickle({A(42, ['fourty', 'two']), A(0, list[str]()), A(-99, ['negative', 'ninety', 'nine'])})\n"
  },
  {
    "path": "test/core/sort.codon",
    "content": "from algorithms.pdqsort import pdq_sort\nfrom algorithms.insertionsort import insertion_sort\nfrom algorithms.heapsort import heap_sort\nfrom algorithms.timsort import tim_sort\n\n\nMANUAL_TEST = False\n\n\ndef print_test[T](a: list[T], b: list[T]):\n    if not MANUAL_TEST:\n        print(a)\n        return\n\n    if len(a) != len(b):\n        print(f\"FAILED: length of a ({len(a)}) is not length of b ({len(b)})\")\n        return\n\n    if len(a) == 0:\n        print(\"PASSED\")\n        return\n\n    for i in range(len(a)):\n        if a[i] != b[i]:\n            print(f\"FAILED: element {i} of a is {a[i]} and is not equal to the element in b {b[i]}\")\n            return\n\n    print(\"PASSED\")\n\n### Comparison Functions ###\n\ndef compare_less(x: int):\n    return x\n\ndef compare_greater(x: int):\n    return -x\n\ndef compare_string(x):\n    return x\n\ndef compare_dict(x: dict[str,int]):\n    return x[\"key\"]\n\n### Basic Sort Tests ###\nprint_test(insertion_sort(list[int](), compare_less), list[int]()) # EXPECT: []\nprint_test(insertion_sort([1, 2, 3], compare_less), [1, 2, 3]) # EXPECT: [1, 2, 3]\nprint_test(insertion_sort([3, 2, 1], compare_less), [1, 2, 3]) # EXPECT: [1, 2, 3]\nprint_test(insertion_sort([3, 2, 1], compare_greater), [3, 2, 1]) # EXPECT: [3, 2, 1]\nprint_test(insertion_sort([1, 2, 3], compare_greater), [3, 2, 1]) # EXPECT: [3, 2, 1]\n\nprint_test(heap_sort(list[int](), compare_less), list[int]()) # EXPECT: []\nprint_test(heap_sort([1, 2, 3], compare_less), [1, 2, 3]) # EXPECT: [1, 2, 3]\nprint_test(heap_sort([3, 2, 1], compare_less), [1, 2, 3]) # EXPECT: [1, 2, 3]\nprint_test(heap_sort([3, 2, 1], compare_greater), [3, 2, 1]) # EXPECT: [3, 2, 1]\nprint_test(heap_sort([1, 2, 3], compare_greater), [3, 2, 1]) # EXPECT: [3, 2, 1]\n\nprint_test(pdq_sort(list[int](), compare_less), list[int]()) # EXPECT: []\nprint_test(pdq_sort([1, 2, 3], compare_less), [1, 2, 3]) # EXPECT: [1, 2, 3]\nprint_test(pdq_sort([3, 2, 1], compare_less), [1, 2, 3]) # EXPECT: [1, 2, 3]\nprint_test(pdq_sort([3, 2, 1], compare_greater), [3, 2, 1]) # EXPECT: [3, 2, 1]\nprint_test(pdq_sort([1, 2, 3], compare_greater), [3, 2, 1]) # EXPECT: [3, 2, 1]\n\nprint_test(tim_sort(list[int](), compare_less), list[int]()) # EXPECT: []\nprint_test(tim_sort([1, 2, 3], compare_less), [1, 2, 3]) # EXPECT: [1, 2, 3]\nprint_test(tim_sort([3, 2, 1], compare_less), [1, 2, 3]) # EXPECT: [1, 2, 3]\nprint_test(tim_sort([3, 2, 1], compare_greater), [3, 2, 1]) # EXPECT: [3, 2, 1]\nprint_test(tim_sort([1, 2, 3], compare_greater), [3, 2, 1]) # EXPECT: [3, 2, 1]\n\n\n### Sort Different Types ###\nwords = ['The', 'quick', 'Brown', 'fox', 'Jumped', 'over', 'The', 'lazy', 'Dog']\ndicts = [{\"key\": 5}, {\"key\": 9}, {\"key\": 1}]\n\nprint_test(insertion_sort(list[str](), compare_string), list[str]()) # EXPECT: []\nprint_test(insertion_sort(words, compare_string), ['Brown', 'Dog', 'Jumped', 'The', 'The', 'fox', 'lazy', 'over', 'quick']) # EXPECT: ['Brown', 'Dog', 'Jumped', 'The', 'The', 'fox', 'lazy', 'over', 'quick']\nprint_test(insertion_sort(dicts, compare_dict), [{\"key\": 1}, {\"key\": 5}, {\"key\": 9}]) # EXPECT: [{\"key\": 1}, {\"key\": 5}, {\"key\": 9}]\n\nprint_test(heap_sort(list[str](), compare_string), list[str]()) # EXPECT: []\nprint_test(heap_sort(words, compare_string), ['Brown', 'Dog', 'Jumped', 'The', 'The', 'fox', 'lazy', 'over', 'quick']) # EXPECT: ['Brown', 'Dog', 'Jumped', 'The', 'The', 'fox', 'lazy', 'over', 'quick']\nprint_test(heap_sort(dicts, compare_dict), [{\"key\": 1}, {\"key\": 5}, {\"key\": 9}]) # EXPECT: [{\"key\": 1}, {\"key\": 5}, {\"key\": 9}]\n\nprint_test(pdq_sort(list[str](), compare_string), list[str]()) # EXPECT: []\nprint_test(pdq_sort(words, compare_string), ['Brown', 'Dog', 'Jumped', 'The', 'The', 'fox', 'lazy', 'over', 'quick']) # EXPECT: ['Brown', 'Dog', 'Jumped', 'The', 'The', 'fox', 'lazy', 'over', 'quick']\nprint_test(pdq_sort(dicts, compare_dict), [{\"key\": 1}, {\"key\": 5}, {\"key\": 9}]) # EXPECT: [{\"key\": 1}, {\"key\": 5}, {\"key\": 9}]\n\nprint_test(tim_sort(list[str](), compare_string), list[str]()) # EXPECT: []\nprint_test(tim_sort(words, compare_string), ['Brown', 'Dog', 'Jumped', 'The', 'The', 'fox', 'lazy', 'over', 'quick']) # EXPECT: ['Brown', 'Dog', 'Jumped', 'The', 'The', 'fox', 'lazy', 'over', 'quick']\nprint_test(tim_sort(dicts, compare_dict), [{\"key\": 1}, {\"key\": 5}, {\"key\": 9}]) # EXPECT: [{\"key\": 1}, {\"key\": 5}, {\"key\": 9}]\n\n### Sort longer lists ###\nordered = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]\nordered_r = [100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]\nunordered = [98, 57, 39, 5, 70, 80, 93, 65, 96, 43, 99, 18, 26, 1, 46, 94, 55, 37, 95, 32, 49, 2, 24, 14, 9, 53, 64, 62, 16, 33, 77, 35, 69, 19, 44, 51, 76, 66, 52, 83, 38, 3, 20, 54, 100, 7, 6, 17, 84, 73, 58, 10, 11, 40, 15, 12, 68, 45, 81, 78, 30, 97, 13, 82, 79, 27, 22, 90, 74, 41, 29, 56, 50, 92, 71, 89, 72, 8, 91, 88, 23, 4, 59, 63, 36, 47, 86, 61, 25, 67, 34, 75, 85, 87, 31, 60, 42, 21, 28, 48]\n\nprint_test(insertion_sort(ordered, compare_greater), [100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]) # EXPECT: [100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]\nprint_test(insertion_sort(ordered_r, compare_less), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]) # EXPECT: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]\nprint_test(insertion_sort(unordered, compare_less), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]) # EXPECT: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]\n\nprint_test(heap_sort(ordered, compare_greater), [100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]) # EXPECT: [100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]\nprint_test(heap_sort(ordered_r, compare_less), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]) # EXPECT: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]\nprint_test(heap_sort(unordered, compare_less), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]) # EXPECT: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]\n\nprint_test(pdq_sort(ordered, compare_greater), [100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]) # EXPECT: [100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]\nprint_test(pdq_sort(ordered_r, compare_less), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]) # EXPECT: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]\nprint_test(pdq_sort(unordered, compare_less), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]) # EXPECT: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]\n\nprint_test(tim_sort(ordered, compare_greater), [100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]) # EXPECT: [100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]\nprint_test(tim_sort(ordered_r, compare_less), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]) # EXPECT: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]\nprint_test(tim_sort(unordered, compare_less), [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]) # EXPECT: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]\n\n### Testing Stability ###\n\n@test\ndef test_stable():\n    data_list = [{'key': 58, 'key2': 1}, {'key': 37, 'key2': 1}, {'key': 41, 'key2': 1}, {'key': 91, 'key2': 1}, {'key': 8, 'key2': 1}, {'key': 15, 'key2': 1}, {'key': 28, 'key2': 1}, {'key': 45, 'key2': 1}, {'key': 84, 'key2': 1}, {'key': 94, 'key2': 1}, {'key': 75, 'key2': 1}, {'key': 70, 'key2': 1}, {'key': 64, 'key2': 1}, {'key': 17, 'key2': 1}, {'key': 47, 'key2': 1}, {'key': 11, 'key2': 1}, {'key': 97, 'key2': 1}, {'key': 19, 'key2': 1}, {'key': 92, 'key2': 1}, {'key': 82, 'key2': 1}, {'key': 7, 'key2': 1}, {'key': 77, 'key2': 1}, {'key': 69, 'key2': 1}, {'key': 87, 'key2': 1}, {'key': 72, 'key2': 1}, {'key': 48, 'key2': 1}, {'key': 35, 'key2': 1}, {'key': 26, 'key2': 1}, {'key': 60, 'key2': 1}, {'key': 78, 'key2': 1}, {'key': 67, 'key2': 1}, {'key': 12, 'key2': 1}, {'key': 9, 'key2': 1}, {'key': 79, 'key2': 1}, {'key': 38, 'key2': 1}, {'key': 88, 'key2': 1}, {'key': 86, 'key2': 1}, {'key': 3, 'key2': 1}, {'key': 30, 'key2': 1}, {'key': 89, 'key2': 1}, {'key': 2, 'key2': 1}, {'key': 40, 'key2': 1}, {'key': 81, 'key2': 1}, {'key': 39, 'key2': 1}, {'key': 99, 'key2': 1}, {'key': 5, 'key2': 1}, {'key': 90, 'key2': 1}, {'key': 18, 'key2': 1}, {'key': 1, 'key2': 1}, {'key': 68, 'key2': 1}, {'key': 98, 'key2': 1}, {'key': 27, 'key2': 1}, {'key': 33, 'key2': 1}, {'key': 49, 'key2': 1}, {'key': 62, 'key2': 1}, {'key': 63, 'key2': 1}, {'key': 55, 'key2': 1}, {'key': 71, 'key2': 1}, {'key': 73, 'key2': 1}, {'key': 93, 'key2': 1}, {'key': 57, 'key2': 1}, {'key': 32, 'key2': 1}, {'key': 10, 'key2': 1}, {'key': 23, 'key2': 1}, {'key': 0, 'key2': 1}, {'key': 61, 'key2': 1}, {'key': 95, 'key2': 1}, {'key': 66, 'key2': 1}, {'key': 80, 'key2': 1}, {'key': 4, 'key2': 1}, {'key': 53, 'key2': 1}, {'key': 83, 'key2': 1}, {'key': 21, 'key2': 1}, {'key': 96, 'key2': 1}, {'key': 34, 'key2': 1}, {'key': 25, 'key2': 1}, {'key': 51, 'key2': 1}, {'key': 31, 'key2': 1}, {'key': 44, 'key2': 1}, {'key': 50, 'key2': 1}, {'key': 22, 'key2': 1}, {'key': 36, 'key2': 1}, {'key': 76, 'key2': 1}, {'key': 59, 'key2': 1}, {'key': 14, 'key2': 1}, {'key': 20, 'key2': 1}, {'key': 74, 'key2': 1}, {'key': 13, 'key2': 1}, {'key': 85, 'key2': 1}, {'key': 24, 'key2': 1}, {'key': 56, 'key2': 1}, {'key': 42, 'key2': 1}, {'key': 6, 'key2': 1}, {'key': 16, 'key2': 1}, {'key': 29, 'key2': 1}, {'key': 52, 'key2': 1}, {'key': 54, 'key2': 1}, {'key': 43, 'key2': 1}, {'key': 65, 'key2': 1}, {'key': 46, 'key2': 1}, {'key': 59, 'key2': 2}, {'key': 52, 'key2': 2}, {'key': 8, 'key2': 2}, {'key': 82, 'key2': 2}, {'key': 98, 'key2': 2}, {'key': 67, 'key2': 2}, {'key': 78, 'key2': 2}, {'key': 12, 'key2': 2}, {'key': 60, 'key2': 2}, {'key': 18, 'key2': 2}, {'key': 72, 'key2': 2}, {'key': 80, 'key2': 2}, {'key': 9, 'key2': 2}, {'key': 35, 'key2': 2}, {'key': 90, 'key2': 2}, {'key': 85, 'key2': 2}, {'key': 38, 'key2': 2}, {'key': 64, 'key2': 2}, {'key': 6, 'key2': 2}, {'key': 41, 'key2': 2}, {'key': 10, 'key2': 2}, {'key': 94, 'key2': 2}, {'key': 7, 'key2': 2}, {'key': 21, 'key2': 2}, {'key': 29, 'key2': 2}, {'key': 20, 'key2': 2}, {'key': 44, 'key2': 2}, {'key': 55, 'key2': 2}, {'key': 69, 'key2': 2}, {'key': 2, 'key2': 2}, {'key': 26, 'key2': 2}, {'key': 62, 'key2': 2}, {'key': 45, 'key2': 2}, {'key': 14, 'key2': 2}, {'key': 63, 'key2': 2}, {'key': 47, 'key2': 2}, {'key': 22, 'key2': 2}, {'key': 74, 'key2': 2}, {'key': 56, 'key2': 2}, {'key': 70, 'key2': 2}, {'key': 79, 'key2': 2}, {'key': 68, 'key2': 2}, {'key': 13, 'key2': 2}, {'key': 76, 'key2': 2}, {'key': 58, 'key2': 2}, {'key': 99, 'key2': 2}, {'key': 61, 'key2': 2}, {'key': 5, 'key2': 2}, {'key': 96, 'key2': 2}, {'key': 0, 'key2': 2}, {'key': 81, 'key2': 2}, {'key': 87, 'key2': 2}, {'key': 57, 'key2': 2}, {'key': 40, 'key2': 2}, {'key': 3, 'key2': 2}, {'key': 33, 'key2': 2}, {'key': 15, 'key2': 2}, {'key': 77, 'key2': 2}, {'key': 1, 'key2': 2}, {'key': 24, 'key2': 2}, {'key': 27, 'key2': 2}, {'key': 86, 'key2': 2}, {'key': 25, 'key2': 2}, {'key': 88, 'key2': 2}, {'key': 51, 'key2': 2}, {'key': 16, 'key2': 2}, {'key': 32, 'key2': 2}, {'key': 97, 'key2': 2}, {'key': 28, 'key2': 2}, {'key': 23, 'key2': 2}, {'key': 54, 'key2': 2}, {'key': 31, 'key2': 2}, {'key': 65, 'key2': 2}, {'key': 46, 'key2': 2}, {'key': 50, 'key2': 2}, {'key': 17, 'key2': 2}, {'key': 89, 'key2': 2}, {'key': 39, 'key2': 2}, {'key': 95, 'key2': 2}, {'key': 84, 'key2': 2}, {'key': 92, 'key2': 2}, {'key': 93, 'key2': 2}, {'key': 75, 'key2': 2}, {'key': 34, 'key2': 2}, {'key': 36, 'key2': 2}, {'key': 43, 'key2': 2}, {'key': 30, 'key2': 2}, {'key': 71, 'key2': 2}, {'key': 66, 'key2': 2}, {'key': 4, 'key2': 2}, {'key': 42, 'key2': 2}, {'key': 19, 'key2': 2}, {'key': 37, 'key2': 2}, {'key': 53, 'key2': 2}, {'key': 49, 'key2': 2}, {'key': 73, 'key2': 2}, {'key': 91, 'key2': 2}, {'key': 11, 'key2': 2}, {'key': 48, 'key2': 2}, {'key': 83, 'key2': 2}]\n    sorted_list = [{'key': 0, 'key2': 1}, {'key': 0, 'key2': 2}, {'key': 1, 'key2': 1}, {'key': 1, 'key2': 2}, {'key': 2, 'key2': 1}, {'key': 2, 'key2': 2}, {'key': 3, 'key2': 1}, {'key': 3, 'key2': 2}, {'key': 4, 'key2': 1}, {'key': 4, 'key2': 2}, {'key': 5, 'key2': 1}, {'key': 5, 'key2': 2}, {'key': 6, 'key2': 1}, {'key': 6, 'key2': 2}, {'key': 7, 'key2': 1}, {'key': 7, 'key2': 2}, {'key': 8, 'key2': 1}, {'key': 8, 'key2': 2}, {'key': 9, 'key2': 1}, {'key': 9, 'key2': 2}, {'key': 10, 'key2': 1}, {'key': 10, 'key2': 2}, {'key': 11, 'key2': 1}, {'key': 11, 'key2': 2}, {'key': 12, 'key2': 1}, {'key': 12, 'key2': 2}, {'key': 13, 'key2': 1}, {'key': 13, 'key2': 2}, {'key': 14, 'key2': 1}, {'key': 14, 'key2': 2}, {'key': 15, 'key2': 1}, {'key': 15, 'key2': 2}, {'key': 16, 'key2': 1}, {'key': 16, 'key2': 2}, {'key': 17, 'key2': 1}, {'key': 17, 'key2': 2}, {'key': 18, 'key2': 1}, {'key': 18, 'key2': 2}, {'key': 19, 'key2': 1}, {'key': 19, 'key2': 2}, {'key': 20, 'key2': 1}, {'key': 20, 'key2': 2}, {'key': 21, 'key2': 1}, {'key': 21, 'key2': 2}, {'key': 22, 'key2': 1}, {'key': 22, 'key2': 2}, {'key': 23, 'key2': 1}, {'key': 23, 'key2': 2}, {'key': 24, 'key2': 1}, {'key': 24, 'key2': 2}, {'key': 25, 'key2': 1}, {'key': 25, 'key2': 2}, {'key': 26, 'key2': 1}, {'key': 26, 'key2': 2}, {'key': 27, 'key2': 1}, {'key': 27, 'key2': 2}, {'key': 28, 'key2': 1}, {'key': 28, 'key2': 2}, {'key': 29, 'key2': 1}, {'key': 29, 'key2': 2}, {'key': 30, 'key2': 1}, {'key': 30, 'key2': 2}, {'key': 31, 'key2': 1}, {'key': 31, 'key2': 2}, {'key': 32, 'key2': 1}, {'key': 32, 'key2': 2}, {'key': 33, 'key2': 1}, {'key': 33, 'key2': 2}, {'key': 34, 'key2': 1}, {'key': 34, 'key2': 2}, {'key': 35, 'key2': 1}, {'key': 35, 'key2': 2}, {'key': 36, 'key2': 1}, {'key': 36, 'key2': 2}, {'key': 37, 'key2': 1}, {'key': 37, 'key2': 2}, {'key': 38, 'key2': 1}, {'key': 38, 'key2': 2}, {'key': 39, 'key2': 1}, {'key': 39, 'key2': 2}, {'key': 40, 'key2': 1}, {'key': 40, 'key2': 2}, {'key': 41, 'key2': 1}, {'key': 41, 'key2': 2}, {'key': 42, 'key2': 1}, {'key': 42, 'key2': 2}, {'key': 43, 'key2': 1}, {'key': 43, 'key2': 2}, {'key': 44, 'key2': 1}, {'key': 44, 'key2': 2}, {'key': 45, 'key2': 1}, {'key': 45, 'key2': 2}, {'key': 46, 'key2': 1}, {'key': 46, 'key2': 2}, {'key': 47, 'key2': 1}, {'key': 47, 'key2': 2}, {'key': 48, 'key2': 1}, {'key': 48, 'key2': 2}, {'key': 49, 'key2': 1}, {'key': 49, 'key2': 2}, {'key': 50, 'key2': 1}, {'key': 50, 'key2': 2}, {'key': 51, 'key2': 1}, {'key': 51, 'key2': 2}, {'key': 52, 'key2': 1}, {'key': 52, 'key2': 2}, {'key': 53, 'key2': 1}, {'key': 53, 'key2': 2}, {'key': 54, 'key2': 1}, {'key': 54, 'key2': 2}, {'key': 55, 'key2': 1}, {'key': 55, 'key2': 2}, {'key': 56, 'key2': 1}, {'key': 56, 'key2': 2}, {'key': 57, 'key2': 1}, {'key': 57, 'key2': 2}, {'key': 58, 'key2': 1}, {'key': 58, 'key2': 2}, {'key': 59, 'key2': 1}, {'key': 59, 'key2': 2}, {'key': 60, 'key2': 1}, {'key': 60, 'key2': 2}, {'key': 61, 'key2': 1}, {'key': 61, 'key2': 2}, {'key': 62, 'key2': 1}, {'key': 62, 'key2': 2}, {'key': 63, 'key2': 1}, {'key': 63, 'key2': 2}, {'key': 64, 'key2': 1}, {'key': 64, 'key2': 2}, {'key': 65, 'key2': 1}, {'key': 65, 'key2': 2}, {'key': 66, 'key2': 1}, {'key': 66, 'key2': 2}, {'key': 67, 'key2': 1}, {'key': 67, 'key2': 2}, {'key': 68, 'key2': 1}, {'key': 68, 'key2': 2}, {'key': 69, 'key2': 1}, {'key': 69, 'key2': 2}, {'key': 70, 'key2': 1}, {'key': 70, 'key2': 2}, {'key': 71, 'key2': 1}, {'key': 71, 'key2': 2}, {'key': 72, 'key2': 1}, {'key': 72, 'key2': 2}, {'key': 73, 'key2': 1}, {'key': 73, 'key2': 2}, {'key': 74, 'key2': 1}, {'key': 74, 'key2': 2}, {'key': 75, 'key2': 1}, {'key': 75, 'key2': 2}, {'key': 76, 'key2': 1}, {'key': 76, 'key2': 2}, {'key': 77, 'key2': 1}, {'key': 77, 'key2': 2}, {'key': 78, 'key2': 1}, {'key': 78, 'key2': 2}, {'key': 79, 'key2': 1}, {'key': 79, 'key2': 2}, {'key': 80, 'key2': 1}, {'key': 80, 'key2': 2}, {'key': 81, 'key2': 1}, {'key': 81, 'key2': 2}, {'key': 82, 'key2': 1}, {'key': 82, 'key2': 2}, {'key': 83, 'key2': 1}, {'key': 83, 'key2': 2}, {'key': 84, 'key2': 1}, {'key': 84, 'key2': 2}, {'key': 85, 'key2': 1}, {'key': 85, 'key2': 2}, {'key': 86, 'key2': 1}, {'key': 86, 'key2': 2}, {'key': 87, 'key2': 1}, {'key': 87, 'key2': 2}, {'key': 88, 'key2': 1}, {'key': 88, 'key2': 2}, {'key': 89, 'key2': 1}, {'key': 89, 'key2': 2}, {'key': 90, 'key2': 1}, {'key': 90, 'key2': 2}, {'key': 91, 'key2': 1}, {'key': 91, 'key2': 2}, {'key': 92, 'key2': 1}, {'key': 92, 'key2': 2}, {'key': 93, 'key2': 1}, {'key': 93, 'key2': 2}, {'key': 94, 'key2': 1}, {'key': 94, 'key2': 2}, {'key': 95, 'key2': 1}, {'key': 95, 'key2': 2}, {'key': 96, 'key2': 1}, {'key': 96, 'key2': 2}, {'key': 97, 'key2': 1}, {'key': 97, 'key2': 2}, {'key': 98, 'key2': 1}, {'key': 98, 'key2': 2}, {'key': 99, 'key2': 1}, {'key': 99, 'key2': 2}]\n    assert tim_sort(data_list, compare_dict) == sorted_list\n\n### Stdlib sort tests ###\nprint_test(sorted(list[int](), compare_less, reverse=True), list[int]()) # EXPECT: []\nprint_test(sorted([3, 2, 1], reverse=True), [3, 2, 1]) # EXPECT: [3, 2, 1]\nprint_test(sorted([1, 2, 3], compare_greater), [3, 2, 1]) # EXPECT: [3, 2, 1]\nprint_test(sorted([1, 2, 3], compare_greater, reverse=True), [1, 2, 3]) # EXPECT: [1, 2, 3]\n\n"
  },
  {
    "path": "test/core/trees.codon",
    "content": "import sys\n\ndef max(a, b):\n    return a if a>=b else b\n\nclass AVLNode[K,V]:\n    key: K\n    value: V\n    parent: Optional[AVLNode[K,V]]\n    left: Optional[AVLNode[K,V]]\n    right: Optional[AVLNode[K,V]]\n    height: int\n\n    def __init__(self: AVLNode[K,V], parent: Optional[AVLNode[K,V]], k: K, v: V):\n        self.key = k\n        self.value = v\n        self.parent = parent\n        self.left = None\n        self.right = None\n        self.height = -1\n\n    def find(self, k):\n        if k < self.key:\n            if self.left is None:\n                return None\n            else:\n                return self.left.find(k)\n        elif k == self.key:\n            return self\n        else:\n            if self.right is None:\n                return None\n            else:\n                return self.right.find(k)\n\n    def find_min(self):\n        current: Optional[AVLNode[K, V]] = self\n        while current.left is not None:\n            current = current.left\n        return current\n\n    def next_larger(self):\n        if self.right is not None:\n            return self.right.find_min()\n        current: Optional[AVLNode[K, V]] = self\n        while current.parent is not None and current is current.parent.right:\n            current = current.parent\n        return current.parent\n\n    def insert(self, node):\n        if node is None:\n            return\n        if node.key < self.key:\n            if self.left is None:\n                node.parent = self\n                self.left = node\n            else:\n                self.left.insert(node)\n        else:\n            if self.right is None:\n                node.parent = self\n                self.right = node\n            else:\n                self.right.insert(node)\n\n    def delete(self):\n        if self.left is None or self.right is None:\n            if self is self.parent.left:\n                self.parent.left = self.left if self.left else self.right\n                if self.parent.left is not None:\n                    self.parent.left.parent = self.parent\n            else:\n                self.parent.right = self.left if self.left else self.right\n                if self.parent.right is not None:\n                    self.parent.right.parent = self.parent\n            return self\n        else:\n            s = self.next_larger()\n            self.key, s.key = s.key, self.key\n            return s.delete()\n\n    def __iter__(self: AVLNode[K,V]):\n        if self.left:\n            for i in self.left:\n                yield i\n        yield self\n        if self.right:\n            for i in self.right:\n                yield i\n\ndef height(node):\n    if node is None:\n        return -1\n    else:\n        return node.height\n\ndef update_height(node):\n    node.height = max(height(node.left), height(node.right)) + 1\n\nclass AVL[K,V]:\n    root: Optional[AVLNode[K,V]]\n\n    def __init__(self: AVL[K,V]):\n        self.root = None\n\n    def find(self, k):\n        if not self.root:\n            return None\n        return self.root.find(k)\n\n    def find_min(self):\n        if not self.root:\n            return None\n        return self.root.find_min()\n\n    def next_larger(self, k):\n        node = self.find(k)\n        return node.next_larger() if node else None\n\n    def left_rotate(self, x):\n        y = x.right\n        y.parent = x.parent\n        if y.parent is None:\n            self.root = y\n        else:\n            if y.parent.left is x:\n                y.parent.left = y\n            elif y.parent.right is x:\n                y.parent.right = y\n        x.right = y.left\n        if x.right is not None:\n            x.right.parent = x\n        y.left = x\n        x.parent = y\n        update_height(x)\n        update_height(y)\n\n    def right_rotate(self, x):\n        y = x.left\n        y.parent = x.parent\n        if y.parent is None:\n            self.root = y\n        else:\n            if y.parent.left is x:\n                y.parent.left = y\n            elif y.parent.right is x:\n                y.parent.right = y\n        x.left = y.right\n        if x.left is not None:\n            x.left.parent = x\n        y.right = x\n        x.parent = y\n        update_height(x)\n        update_height(y)\n\n    def rebalance(self, node: Optional[AVLNode[K, V]]):\n        while node is not None:\n            update_height(node)\n            if height(node.left) >= 2 + height(node.right):\n                if height(node.left.left) >= height(node.left.right):\n                    self.right_rotate(node)\n                else:\n                    self.left_rotate(node.left)\n                    self.right_rotate(node)\n            elif height(node.right) >= 2 + height(node.left):\n                if height(node.right.right) >= height(node.right.left):\n                    self.left_rotate(node)\n                else:\n                    self.right_rotate(node.right)\n                    self.left_rotate(node)\n            node = node.parent\n\n    def insert(self, k, v):\n        node = AVLNode[K,V](None, k, v)\n        if self.root is None:\n            # The root's parent is None.\n            self.root = node\n        else:\n            self.root.insert(node)\n        self.rebalance(node)\n\n    def delete(self, k):\n        node = self.find(k)\n        if node is None:\n            return\n        deleted = None\n        if node is self.root:\n            pseudoroot = AVLNode[K,V](None, 0, 0)\n            pseudoroot.left = self.root\n            self.root.parent = pseudoroot\n            deleted = self.root.delete()\n            self.root = pseudoroot.left\n            if self.root is not None:\n                self.root.parent = None\n        else:\n            deleted = node.delete()\n        self.rebalance(deleted.parent)\n\n    def __setitem__(self: AVL[K,V], k: K, v: V):\n        self.insert(k, v)\n\n    def __getitem__(self: AVL[K,V], k: K):\n        nd = self.find(k)\n        if not nd:\n            print 'whoops', k, 'not found'\n            sys.exit(1)\n        return nd.value\n\n    def __delitem__(self: AVL[K,V], k: K):\n        self.delete(k)\n\n    def __contains__(self: AVL[K,V], k: K):\n        return self.find(k) is not None\n\n    def __iter__(self: AVL[K,V]):\n        if self.root:\n            for i in self.root:\n                yield i.key, i.value\n\nd1 = AVL[int,int]()\nfor a in range(5):\n    d1[a] = a*a\n\n# EXPECT: 0\n# EXPECT: 1\n# EXPECT: 4\n# EXPECT: 9\n# EXPECT: 16\nfor a in range(5):\n    print d1[a]\n\nprint 2 in d1  # EXPECT: True\ndel d1[2]\nprint 2 in d1  # EXPECT: False\nd1[2] = 44\nprint 2 in d1  # EXPECT: True\nprint d1[2]    # EXPECT: 44\n\ndel d1[3]\ndel d1[4]\n\n# EXPECT: 0 0\n# EXPECT: 1 1\n# EXPECT: 2 44\nfor t in d1:\n    print t[0], t[1]\n"
  },
  {
    "path": "test/core/vec_simd.codon",
    "content": "from simd import Vec\nimport math\n\ndef close(x, y):\n    if isinstance(x, List):\n        return all(abs(i - j) < float32(1e-4) for i, j in zip(x, y))\n    else:\n        return abs(x - y) < float32(1e-4)\n\n@test\ndef test_new():\n    assert Vec[u32, 4](1u32).scatter() == [1u32] * 4\n    assert Vec[i32, 4](-1i32).scatter() == [-1i32] * 4\n    assert Vec[float32, 4](float32(1.0)).scatter() == [float32(1.0)] * 4\n\n    data = [u8(i) for i in range(16)]\n    assert Vec[u8, 8](data.arr.ptr).scatter() == data[:8]\n    assert Vec[u8, 8](data).scatter() == data[:8]\n    assert Vec[u8, 8](\"abcdefgh\").scatter() == [u8(i) for i in range(ord('a'), ord('a') + 8)]\n\n    u = Vec[u8, 8](data)\n    assert Vec[i16, 8](u).scatter() == [i16(i) for i in range(8)], (u, Vec[i16, 8](u).scatter())\n\n    assert Vec[u8, 8](\"abcdefghijkl\", 4).scatter() == [u8(i) for i in range(ord('e'), ord('e') + 8)]\n    assert Vec[u8, 8](data, 4).scatter() == data[4:4 + 8]\n\n@test\ndef test_cast():\n    assert Vec[u32, 4](1u32).cast(u32).scatter() == [1u32] * 4\n\n    assert Vec[u8, 8](1u8).cast(u16).scatter() == [1u16] * 8\n    assert Vec[u16, 8](257u16).cast(u8).scatter() == [1u8] * 8\n    assert Vec[i8, 8](1i8).cast(i16).scatter() == [1i16] * 8\n    assert Vec[u16, 8](257u16).cast(i8).scatter() == [1i8] * 8\n\n    assert Vec[u16, 8](129u16).cast(i16).scatter() == [129i16] * 8\n    assert Vec[u16, 8](129u16).cast(i8).scatter() == [-127i8] * 8\n    assert Vec[i16, 8](-1i16).cast(u16).scatter() == [65535u16] * 8\n    assert Vec[i16, 8](-1i16).cast(u8).scatter() == [255u8] * 8\n\n    assert Vec[float32, 4](float32(1.0)).cast(float).scatter() == [1.0] * 4\n    assert Vec[float32, 4](float32(1.0)).cast(float16).scatter() == [float16(1.0)] * 4\n    assert Vec[float32, 4](float32(1.0)).cast(i32).scatter() == [1i32] * 4\n    assert Vec[i32, 4](1i32).cast(float32).scatter() == [float32(1.0)] * 4\n\n@test\ndef test_op():\n    T = Vec[u8, 8]\n    a = T([u8(100 + i) for i in range(T.N)])\n    b = T([u8(i) for i in range(T.N)])\n    b1 = T([u8(i + 1) for i in range(T.N)])\n    af = Vec[float32, 4]([float32(100 + i) for i in range(4)])\n    bf = Vec[float32, 4]([float32(i + 1) for i in range(4)])\n    c = T(1u8)\n\n    assert a[2] == 102u8\n    assert af[2] == float32(102)\n\n    assert (-a).scatter() == [u8(-(100 + i)) for i in range(T.N)]\n    assert (-af).scatter() == [float32(-(100 + i)) for i in range(4)]\n\n    assert (a == a).scatter() == [1u1 for _ in range(T.N)]\n    assert (a == 100u8).scatter() == [1u1] + [0u1 for _ in range(T.N - 1)]\n    assert (a == b).scatter() == [0u1 for _ in range(T.N)]\n    assert (a != a).scatter() == [0u1 for _ in range(T.N)]\n    assert (a != 100u8).scatter() == [0u1] + [1u1 for _ in range(T.N - 1)]\n    assert (a != b).scatter() == [1u1 for _ in range(T.N)]\n\n    assert (a & b).scatter() == [u8(100 + i) & u8(i) for i in range(T.N)]\n    assert (a | b).scatter() == [u8(100 + i) | u8(i) for i in range(T.N)]\n    assert (a ^ b).scatter() == [u8(100 + i) ^ u8(i) for i in range(T.N)]\n    assert (a << c).scatter() == [u8(100 + i) << 1u8 for i in range(T.N)]\n    assert (a >> c).scatter() == [u8(100 + i) >> 1u8 for i in range(T.N)]\n    assert a.lsh(2).scatter() == [u8(100 + i) for i in range(T.N)][2:] + [0u8] * 2\n    assert a.rsh(2).scatter() == [0u8] * 2 + [u8(100 + i) for i in range(T.N)][:-2]\n    assert a.lsh(5).scatter() == [u8(100 + i) for i in range(T.N)][5:] + [0u8] * 5\n    assert a.rsh(5).scatter() == [0u8] * 5 + [u8(100 + i) for i in range(T.N)][:-5]\n\n    assert (a + b).scatter() == [u8(100 + i) + u8(i) for i in range(T.N)]\n    assert (a - b).scatter() == [u8(100 + i) - u8(i) for i in range(T.N)]\n    assert (b - a).scatter() == [u8(i) - u8(100 + i) for i in range(T.N)]\n    assert (a * b).scatter() == [u8(100 + i) * u8(i) for i in range(T.N)]\n    assert close((af / bf).scatter(), [float32(100 + i) / float32(i + 1) for i in range(4)])\n    assert (a // b1).scatter() == [u8((100 + i) // (i + 1)) for i in range(T.N)]\n    assert close((af / float32(2.0)).scatter(), [float32(100 + i) / float32(2) for i in range(4)])\n    assert (a // 2u8).scatter() == [u8((100 + i) // 2) for i in range(T.N)]\n    assert (a % b1).scatter() == [u8(100 + i) % u8(i + 1) for i in range(T.N)]\n\n    assert (a < b).scatter() == [u1(int(100 + i < i)) for i in range(T.N)]\n    assert (a <= b).scatter() == [u1(int(100 + i <= i)) for i in range(T.N)]\n    assert (a > b).scatter() == [u1(int(100 + i > i)) for i in range(T.N)]\n    assert (a >= b).scatter() == [u1(int(100 + i >= i)) for i in range(T.N)]\n\n    assert close(af.sqrt().scatter(), [float32(math.sqrt(100+i)) for i in range(4)])\n    assert close(af.log().scatter(), [float32(math.log(100+i)) for i in range(4)])\n    assert close(af.sin().scatter(), [float32(math.sin(100+i)) for i in range(4)])\n    assert close(af.cos().scatter(), [float32(math.cos(100+i)) for i in range(4)])\n\n    f = Vec[float32, 4]([float32((-1 if i % 2 == 0 else 1) * (10.0+i)) for i in range(4)])\n    assert close(abs(f).scatter(), [float32(10+i) for i in range(4)])\n    k = Vec[i8, 8]([i8((-1 if i % 2 == 0 else 1) * (10+i)) for i in range(8)])\n    assert abs(k).scatter() == [i8(10+i) for i in range(8)]\n\n    assert a.min(b).scatter() == b.scatter()\n    assert a.max(b).scatter() == a.scatter()\n\ndef test_utils():\n    # TODO: add.overflow / sub.overflow\n\n    a = Vec[u8, 8]([u8(100 + i) for i in range(8)])\n    b = Vec[u8, 8]([u8(i) for i in range(8)])\n    af = Vec[float32, 4]([float32(100 + i) for i in range(4)])\n\n    assert a.bitflip().scatter() == [~u8(100 + i) for i in range(8)]\n    mask = Vec[u8,8]([0u8 if i % 2 == 0 else 1u8 for i in range(8)])\n    assert a.mask(b, mask).scatter() == [u8(i) if i % 2 == 0 else u8(100 + i) for i in range(8)]\n\n    assert a.sum() == u8(sum(100 + i for i in range(8)))\n    assert close(af.sum(), float32(sum(100 + i for i in range(4))))\n\ntest_new()\ntest_cast()\ntest_op()\ntest_utils()\n\n"
  },
  {
    "path": "test/main.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <algorithm>\n#include <cstdio>\n#include <dirent.h>\n#include <fcntl.h>\n#include <fstream>\n#include <gc.h>\n#include <iostream>\n#include <sstream>\n#include <string>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <tuple>\n#include <unistd.h>\n#include <vector>\n\n#include \"codon/cir/analyze/dataflow/capture.h\"\n#include \"codon/cir/analyze/dataflow/reaching.h\"\n#include \"codon/cir/util/inlining.h\"\n#include \"codon/cir/util/irtools.h\"\n#include \"codon/cir/util/operator.h\"\n#include \"codon/cir/util/outlining.h\"\n#include \"codon/compiler/compiler.h\"\n#include \"codon/compiler/error.h\"\n#include \"codon/parser/common.h\"\n#include \"codon/util/common.h\"\n\n#include \"gtest/gtest.h\"\n\nusing namespace codon;\nusing namespace std;\n\nclass TestOutliner : public ir::transform::OperatorPass {\n  int successes = 0;\n  int failures = 0;\n  ir::ReturnInstr *successesReturn = nullptr;\n  ir::ReturnInstr *failuresReturn = nullptr;\n\n  const std::string KEY = \"test-outliner-pass\";\n  std::string getKey() const override { return KEY; }\n\n  void handle(ir::SeriesFlow *v) override {\n    auto *M = v->getModule();\n    auto begin = v->begin(), end = v->end();\n    bool sawBegin = false, sawEnd = false;\n    for (auto it = v->begin(); it != v->end(); ++it) {\n      if (ir::util::isCallOf(*it, \"__outline_begin__\") && !sawBegin) {\n        begin = it;\n        sawBegin = true;\n      } else if (ir::util::isCallOf(*it, \"__outline_end__\") && !sawEnd) {\n        end = it;\n        sawEnd = true;\n      }\n    }\n    if (sawBegin && sawEnd) {\n      auto result = ir::util::outlineRegion(ir::cast<ir::BodiedFunc>(getParentFunc()),\n                                            v, begin, end);\n      ++(result ? successes : failures);\n      if (successesReturn)\n        successesReturn->setValue(M->getInt(successes));\n      if (failuresReturn)\n        failuresReturn->setValue(M->getInt(failures));\n    }\n  }\n\n  void handle(ir::ReturnInstr *v) override {\n    auto *M = v->getModule();\n    if (getParentFunc()->getUnmangledName() == \"__outline_successes__\") {\n      v->setValue(M->getInt(successes));\n      successesReturn = v;\n    }\n    if (getParentFunc()->getUnmangledName() == \"__outline_failures__\") {\n      v->setValue(M->getInt(failures));\n      failuresReturn = v;\n    }\n  }\n};\n\nclass TestInliner : public ir::transform::OperatorPass {\n  const std::string KEY = \"test-inliner-pass\";\n  std::string getKey() const override { return KEY; }\n\n  void handle(ir::CallInstr *v) override {\n    auto *M = v->getModule();\n    auto *f = ir::cast<ir::BodiedFunc>(ir::util::getFunc(v->getCallee()));\n    auto *neg = M->getOrRealizeMethod(M->getIntType(), ir::Module::NEG_MAGIC_NAME,\n                                      {M->getIntType()});\n    if (!f)\n      return;\n    auto name = f->getUnmangledName();\n    if (name.find(\"inline_me\") != std::string::npos) {\n      auto aggressive = name.find(\"aggressive\") != std::string::npos;\n      auto res = ir::util::inlineCall(v, aggressive);\n      if (!res)\n        return;\n      for (auto *var : res.newVars)\n        ir::cast<ir::BodiedFunc>(getParentFunc())->push_back(var);\n      v->replaceAll(ir::util::call(neg, {res.result}));\n    }\n  }\n};\n\nstruct PartitionArgsByEscape : public ir::util::Operator {\n  std::vector<ir::analyze::dataflow::CaptureInfo> expected;\n  std::vector<ir::Value *> calls;\n\n  void handle(ir::CallInstr *v) override {\n    using namespace codon::ir;\n    if (auto *f = cast<Func>(util::getFunc(v->getCallee()))) {\n      if (f->getUnmangledName() == \"expect_capture\") {\n        // Format is:\n        //   - Return captures (bool)\n        //   - Extern captures (bool)\n        //   - Captured arg indices (int tuple)\n        std::vector<Value *> args(v->begin(), v->end());\n        seqassertn(args.size() == 3, \"bad escape-test call (size)\");\n        seqassertn(isA<BoolConst>(args[0]) && isA<BoolConst>(args[1]),\n                   \"bad escape-test call (arg types)\");\n\n        ir::analyze::dataflow::CaptureInfo info;\n        info.returnCaptures = cast<BoolConst>(args[0])->getVal();\n        info.externCaptures = cast<BoolConst>(args[1])->getVal();\n        auto *tuple = cast<CallInstr>(args[2]);\n        seqassertn(tuple,\n                   \"last escape-test call argument should be a const tuple literal\");\n\n        for (auto *arg : *tuple) {\n          seqassertn(isA<IntConst>(arg), \"final args should be int\");\n          info.argCaptures.push_back(cast<IntConst>(arg)->getVal());\n        }\n\n        expected.push_back(info);\n        calls.push_back(v);\n      }\n    }\n  }\n};\n\nstruct EscapeValidator : public ir::transform::Pass {\n  const std::string KEY = \"test-escape-validator-pass\";\n  std::string getKey() const override { return KEY; }\n\n  std::string capAnalysisKey;\n\n  explicit EscapeValidator(const std::string &capAnalysisKey)\n      : ir::transform::Pass(), capAnalysisKey(capAnalysisKey) {}\n\n  void run(ir::Module *m) override {\n    using namespace codon::ir;\n    auto *capResult =\n        getAnalysisResult<ir::analyze::dataflow::CaptureResult>(capAnalysisKey);\n    for (auto *var : *m) {\n      if (auto *f = cast<Func>(var)) {\n        PartitionArgsByEscape pabe;\n        f->accept(pabe);\n        auto expected = pabe.expected;\n        if (expected.empty())\n          continue;\n\n        auto it = capResult->results.find(f->getId());\n        seqassertn(it != capResult->results.end(),\n                   \"function not found in capture results\");\n        auto received = it->second;\n        seqassertn(expected.size() == received.size(),\n                   \"size mismatch in capture results\");\n\n        for (unsigned i = 0; i < expected.size(); i++) {\n          auto exp = expected[i];\n          auto got = received[i];\n          std::sort(exp.argCaptures.begin(), exp.argCaptures.end());\n          std::sort(got.argCaptures.begin(), got.argCaptures.end());\n\n          bool good = (exp.returnCaptures == got.returnCaptures) &&\n                      (exp.externCaptures == got.externCaptures) &&\n                      (exp.argCaptures == got.argCaptures);\n          pabe.calls[i]->replaceAll(m->getBool(good));\n        }\n      }\n    }\n  }\n};\n\nvector<string> splitLines(const string &output) {\n  vector<string> result;\n  string line;\n  istringstream stream(output);\n  const char delim = '\\n';\n\n  while (getline(stream, line, delim))\n    result.push_back(line);\n\n  return result;\n}\n\nstatic pair<bool, string> findExpectOnLine(const string &line) {\n  for (auto EXPECT_STR : vector<pair<bool, string>>{\n           {false, \"# EXPECT: \"}, {false, \"#: \"}, {true, \"#! \"}}) {\n    size_t pos = line.find(EXPECT_STR.second);\n    if (pos != string::npos)\n      return {EXPECT_STR.first, line.substr(pos + EXPECT_STR.second.length())};\n  }\n  return {false, \"\"};\n}\n\nstatic pair<vector<string>, bool> findExpects(const string &filename, bool isCode) {\n  vector<string> result;\n  bool isError = false;\n  string line;\n  if (!isCode) {\n    ifstream file(filename);\n    if (!file.good()) {\n      cerr << \"error: could not open \" << filename << endl;\n      exit(EXIT_FAILURE);\n    }\n\n    while (getline(file, line)) {\n      auto expect = findExpectOnLine(line);\n      if (!expect.second.empty()) {\n        result.push_back(expect.second);\n        isError |= expect.first;\n      }\n    }\n    file.close();\n  } else {\n    istringstream file(filename);\n    while (getline(file, line)) {\n      auto expect = findExpectOnLine(line);\n      if (!expect.second.empty()) {\n        result.push_back(expect.second);\n        isError |= expect.first;\n      }\n    }\n  }\n  return {result, isError};\n}\n\nstring argv0;\nvoid seq_exc_init(int flags);\n\nclass SeqTest\n    : public testing::TestWithParam<tuple<\n          string /*filename*/, bool /*debug*/, string /* case name */,\n          string /* case code */, int /* case line */, bool /* barebones stdlib */,\n          bool /* Python numerics */, bool /* run */>> {\n  vector<char> buf;\n  int out_pipe[2];\n  pid_t pid;\n\npublic:\n  SeqTest() : buf(65536), out_pipe(), pid() {}\n  string getFilename(const string &basename) {\n    return string(TEST_DIR) + \"/\" + basename;\n  }\n  int runInChildProcess(bool avoidFork = false) {\n    auto fn = [this]() {\n      auto file = getFilename(get<0>(GetParam()));\n      bool debug = get<1>(GetParam());\n      auto code = get<3>(GetParam());\n      auto startLine = get<4>(GetParam());\n      int testFlags = 1 + get<5>(GetParam());\n      bool pyNumerics = get<6>(GetParam());\n      bool run = get<7>(GetParam());\n\n      auto compiler = std::make_unique<Compiler>(\n          argv0, debug, /*disabledPasses=*/std::vector<std::string>{}, /*isTest=*/true,\n          pyNumerics);\n      // make sure we abort() on runtime error\n      compiler->getLLVMVisitor()->setStandalone(true);\n      llvm::handleAllErrors(code.empty()\n                                ? compiler->parseFile(file, testFlags)\n                                : compiler->parseCode(file, code, startLine, testFlags),\n                            [](const error::ParserErrorInfo &e) {\n                              for (auto &group : e.getErrors()) {\n                                for (auto &msg : group) {\n                                  getLogger().level = 0;\n                                  printf(\"%s\\n\", msg.getMessage().c_str());\n                                }\n                              }\n                              fflush(stdout);\n                              exit(EXIT_FAILURE);\n                            });\n      auto *pm = compiler->getPassManager();\n      pm->registerPass(std::make_unique<TestOutliner>());\n      pm->registerPass(std::make_unique<TestInliner>());\n      auto capKey =\n          pm->registerAnalysis(std::make_unique<ir::analyze::dataflow::CaptureAnalysis>(\n                                   ir::analyze::dataflow::RDAnalysis::KEY,\n                                   ir::analyze::dataflow::DominatorAnalysis::KEY),\n                               {ir::analyze::dataflow::RDAnalysis::KEY,\n                                ir::analyze::dataflow::DominatorAnalysis::KEY});\n      pm->registerPass(std::make_unique<EscapeValidator>(capKey), /*insertBefore=*/\"\",\n                       {capKey});\n      llvm::cantFail(compiler->compile());\n\n      if (run)\n        compiler->getLLVMVisitor()->run({file});\n      fflush(stdout);\n    };\n\n    assert(pipe(out_pipe) != -1);\n    pid = fork();\n    GC_atfork_prepare();\n    assert(pid != -1);\n\n    if (pid == 0) {\n      GC_atfork_child();\n      dup2(out_pipe[1], STDOUT_FILENO);\n      close(out_pipe[0]);\n      close(out_pipe[1]);\n      fn();\n      exit(EXIT_SUCCESS);\n    } else {\n      GC_atfork_parent();\n      int status = -1;\n      close(out_pipe[1]);\n      assert(waitpid(pid, &status, 0) == pid);\n      read(out_pipe[0], buf.data(), buf.size() - 1);\n      close(out_pipe[0]);\n      return status;\n    }\n    return -1;\n  }\n  string result() { return string(buf.data()); }\n};\nstatic string\ngetTestNameFromParam(const testing::TestParamInfo<SeqTest::ParamType> &info) {\n  const string basename = get<0>(info.param);\n  const bool debug = get<1>(info.param);\n\n  // normalize basename\n  // size_t found1 = basename.find('/');\n  // size_t found2 = basename.find('.');\n  // assert(found1 != string::npos);\n  // assert(found2 != string::npos);\n  // assert(found2 > found1);\n  // string normname = basename.substr(found1 + 1, found2 - found1 - 1);\n  string normname = basename;\n  replace(normname.begin(), normname.end(), '/', '_');\n  replace(normname.begin(), normname.end(), '.', '_');\n  return normname + (debug ? \"_debug\" : \"\");\n}\nstatic string\ngetTypeTestNameFromParam(const testing::TestParamInfo<SeqTest::ParamType> &info) {\n  return getTestNameFromParam(info) + \"_\" + get<2>(info.param);\n}\nTEST_P(SeqTest, Run) {\n  const string file = get<0>(GetParam());\n  int status;\n  bool isCase = !get<2>(GetParam()).empty();\n  if (!isCase)\n    status = runInChildProcess();\n  else\n    status = runInChildProcess();\n  if (!WIFEXITED(status))\n    std::cerr << result() << std::endl;\n  ASSERT_TRUE(WIFEXITED(status));\n\n  string output = result();\n\n  auto expects = findExpects(!isCase ? getFilename(file) : get<3>(GetParam()), isCase);\n  if (WEXITSTATUS(status) != int(expects.second))\n    fprintf(stderr, \"%s\\n\", output.c_str());\n  ASSERT_EQ(WEXITSTATUS(status), int(expects.second));\n  const bool assertsFailed = output.find(\"TEST FAILED\") != string::npos;\n  EXPECT_FALSE(assertsFailed);\n  if (assertsFailed)\n    std::cerr << output << std::endl;\n\n  if (!expects.first.empty()) {\n    vector<string> results = splitLines(output);\n    for (unsigned i = 0; i < min(results.size(), expects.first.size()); i++)\n      if (expects.second)\n        EXPECT_EQ(results[i].substr(0, expects.first[i].size()), expects.first[i]);\n      else\n        EXPECT_EQ(results[i], expects.first[i]);\n    EXPECT_EQ(results.size(), expects.first.size());\n  }\n}\nauto getTypeTests(const vector<string> &files) {\n  vector<tuple<string, bool, string, string, int, bool, bool, bool>> cases;\n  for (auto &f : files) {\n    bool barebones = false;\n    string l;\n    ifstream fin(string(TEST_DIR) + \"/\" + f);\n    string code, testName;\n    int test = 0;\n    int codeLine = 0;\n    int line = 0;\n    while (getline(fin, l)) {\n      if (l.substr(0, 3) == \"#%%\") {\n        if (line && testName != \"__ignore__\") {\n          cases.emplace_back(make_tuple(f, true, to_string(line) + \"_\" + testName, code,\n                                        codeLine, barebones, false, true));\n        }\n        auto t = ast::split(l.substr(4), ',');\n        barebones = (t.size() > 1 && t[1] == \"barebones\");\n        testName = t[0];\n        code = l + \"\\n\";\n        codeLine = line;\n        test++;\n      } else {\n        code += l + \"\\n\";\n      }\n      line++;\n    }\n    if (line && testName != \"__ignore__\") {\n      cases.emplace_back(make_tuple(f, true, to_string(line) + \"_\" + testName, code,\n                                    codeLine, barebones, false, true));\n    }\n  }\n  return cases;\n}\n\n// clang-format off\nINSTANTIATE_TEST_SUITE_P(\n    TypeTests, SeqTest,\n    testing::ValuesIn(getTypeTests({\n      \"parser/typecheck/test_access.codon\",\n      \"parser/typecheck/test_assign.codon\",\n      \"parser/typecheck/test_basic.codon\",\n      \"parser/typecheck/test_call.codon\",\n      \"parser/typecheck/test_class.codon\",\n      \"parser/typecheck/test_collections.codon\",\n      \"parser/typecheck/test_cond.codon\",\n      \"parser/typecheck/test_ctx.codon\",\n      \"parser/typecheck/test_error.codon\",\n      \"parser/typecheck/test_function.codon\",\n      \"parser/typecheck/test_import.codon\",\n      \"parser/typecheck/test_infer.codon\",\n      \"parser/typecheck/test_loops.codon\",\n      \"parser/typecheck/test_op.codon\",\n      \"parser/typecheck/test_parser.codon\",\n      \"parser/typecheck/test_python.codon\",\n      \"parser/typecheck/test_typecheck.codon\"\n    })),\n    getTypeTestNameFromParam);\n\nINSTANTIATE_TEST_SUITE_P(\n    CoreTests, SeqTest,\n    testing::Combine(\n      testing::Values(\n        \"core/helloworld.codon\",\n        \"core/arithmetic.codon\",\n        \"core/parser.codon\",\n        \"core/generics.codon\",\n        \"core/generators.codon\",\n        \"core/exceptions.codon\",\n        \"core/containers.codon\",\n        \"core/trees.codon\",\n        \"core/range.codon\",\n        \"core/bltin.codon\",\n        \"core/arguments.codon\",\n        \"core/match.codon\",\n        \"core/serialization.codon\",\n        \"core/pipeline.codon\",\n        \"core/empty.codon\",\n        \"core/vec_simd.codon\"\n      ),\n      testing::Values(true, false),\n      testing::Values(\"\"),\n      testing::Values(\"\"),\n      testing::Values(0),\n      testing::Values(false),\n      testing::Values(false),\n      testing::Values(true)\n    ),\n    getTestNameFromParam);\n\nINSTANTIATE_TEST_SUITE_P(\n    NumericsTests, SeqTest,\n    testing::Combine(\n      testing::Values(\n        \"core/numerics.codon\"\n      ),\n      testing::Values(true, false),\n      testing::Values(\"\"),\n      testing::Values(\"\"),\n      testing::Values(0),\n      testing::Values(false),\n      testing::Values(true),\n      testing::Values(true)\n    ),\n    getTestNameFromParam);\n\nINSTANTIATE_TEST_SUITE_P(\n    StdlibTests, SeqTest,\n    testing::Combine(\n      testing::Values(\n        \"stdlib/llvm_test.codon\",\n        \"stdlib/str_test.codon\",\n        \"stdlib/re_test.codon\",\n        \"stdlib/math_test.codon\",\n        \"stdlib/cmath_test.codon\",\n        \"stdlib/datetime_test.codon\",\n        \"stdlib/itertools_test.codon\",\n        \"stdlib/bisect_test.codon\",\n        \"stdlib/random_test.codon\",\n        \"stdlib/statistics_test.codon\",\n        \"stdlib/sort_test.codon\",\n        \"stdlib/heapq_test.codon\",\n        \"stdlib/operator_test.codon\",\n        \"stdlib/asyncio_test.codon\",\n        \"python/pybridge.codon\"\n      ),\n      testing::Values(true, false),\n      testing::Values(\"\"),\n      testing::Values(\"\"),\n      testing::Values(0),\n      testing::Values(false),\n      testing::Values(false),\n      testing::Values(true)\n    ),\n    getTestNameFromParam);\n\nINSTANTIATE_TEST_SUITE_P(\n    OptTests, SeqTest,\n    testing::Combine(\n        testing::Values(\n            \"transform/canonical.codon\",\n            \"transform/dict_opt.codon\",\n            \"transform/escapes.codon\",\n            \"transform/folding.codon\",\n            \"transform/for_lowering.codon\",\n            \"transform/io_opt.codon\",\n            \"transform/inlining.codon\",\n            \"transform/list_opt.codon\",\n            \"transform/omp.codon\",\n            \"transform/outlining.codon\",\n            \"transform/str_opt.codon\"\n        ),\n        testing::Values(true, false),\n        testing::Values(\"\"),\n        testing::Values(\"\"),\n        testing::Values(0),\n        testing::Values(false),\n        testing::Values(false),\n        testing::Values(true)\n    ),\n    getTestNameFromParam);\n\n  INSTANTIATE_TEST_SUITE_P(\n    GpuTests, SeqTest,\n    testing::Combine(\n        testing::Values(\n            \"transform/kernels.codon\"\n        ),\n        testing::Values(true, false),\n        testing::Values(\"\"),\n        testing::Values(\"\"),\n        testing::Values(0),\n        testing::Values(false),\n        testing::Values(false),\n        testing::Values(false)  // do not run by default, just compile\n    ),\n    getTestNameFromParam);\n\nINSTANTIATE_TEST_SUITE_P(\n    NumPyTests, SeqTest,\n    testing::Combine(\n        testing::Values(\n            \"numpy/random_tests/test_mt19937.codon\",\n            \"numpy/random_tests/test_pcg64.codon\",\n            \"numpy/random_tests/test_philox.codon\",\n            \"numpy/random_tests/test_sfc64.codon\",\n            \"numpy/test_dtype.codon\",\n            \"numpy/test_elision.codon\",\n            \"numpy/test_fft.codon\",\n            \"numpy/test_functional.codon\",\n            // \"numpy/test_fusion.codon\", // TODO: uses a lot of RAM\n            \"numpy/test_indexing.codon\",\n            \"numpy/test_io.codon\",\n            \"numpy/test_lib.codon\",\n            \"numpy/test_linalg.codon\",\n            \"numpy/test_loops.codon\",\n            // \"numpy/test_misc.codon\", // TODO: takes forever in debug mode\n            \"numpy/test_ndmath.codon\",\n            \"numpy/test_npdatetime.codon\",\n            \"numpy/test_pybridge.codon\",\n            \"numpy/test_reductions.codon\",\n            \"numpy/test_routines.codon\",\n            \"numpy/test_sorting.codon\",\n            \"numpy/test_statistics.codon\",\n            \"numpy/test_ufunc.codon\",\n            \"numpy/test_window.codon\"\n        ),\n        testing::Values(true, false),\n        testing::Values(\"\"),\n        testing::Values(\"\"),\n        testing::Values(0),\n        testing::Values(false),\n        testing::Values(false),\n        testing::Values(true)\n    ),\n    getTestNameFromParam);\n\n// clang-format on\n\nint main(int argc, char *argv[]) {\n  argv0 = ast::Filesystem::executable_path(argv[0]);\n  testing::InitGoogleTest(&argc, argv);\n  return RUN_ALL_TESTS();\n}\n"
  },
  {
    "path": "test/numpy/data/.gitignore",
    "content": "*\n*/\n!.gitignore\n"
  },
  {
    "path": "test/numpy/random_tests/test_mt19937.codon",
    "content": "import numpy.random as rnd\nfrom numpy import *\nimport numpy as np\nimport internal.static as static\n\n@test\ndef test_MT19937_integers(seed, low, expected, high=None, size=None, e=False):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(low, int)\n                or isinstance(low, float)) and (high is None or isinstance(\n                    high, int) or isinstance(high, float)):\n            assert gen.integers(low, high, endpoint=e) == expected\n        else:\n            assert (gen.integers(low, high, endpoint=e) == expected).all()\n    else:\n        assert (gen.integers(low, high, size, endpoint=e) == expected).all()\n\ntest_MT19937_integers(74, 5, 3)\ntest_MT19937_integers(74, 987.4, 706)\ntest_MT19937_integers(74, 98, 734, high=987)\ntest_MT19937_integers(74, 89, 64, e=True)\ntest_MT19937_integers(74, -1218, -277, high=98)\ntest_MT19937_integers(74, -5, np.array([37, 38, 38]), 55, size=3)\ntest_MT19937_integers(74, -5, np.array([38, 39, 39]), 55, size=3, e=True)\ntest_MT19937_integers(74, np.array([7, 49, 17]), np.array([57, 70, 61]), 78)\ntest_MT19937_integers(74, -99, np.array([26, 38, 45]), np.array([77, 89, 101]))\ntest_MT19937_integers(74, np.array([7, 49, 17]), np.array([57, 78, 77]),\n                      np.array([77, 89, 101]))\n\n@test\ndef test_MT19937_random(seed, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        assert gen.random() == expected\n    else:\n        assert (round(gen.random(size), 8) == round(expected, 8)).all()\n\ntest_MT19937_random(0, 0.47932306384132817)\ntest_MT19937_random(1, 0.24067553804036945)\ntest_MT19937_random(1234, 0.12038356302504949)\ntest_MT19937_random(\n    1234,\n    np.array([0.12038356, 0.40370142, 0.87770263, 0.95657880, 0.42646002]),\n    size=5)\ntest_MT19937_random(74,\n                    np.array([[0.71552153, 0.72399061, 0.98011015],\n                              [0.92823638, 0.81411241, 0.85174267]]),\n                    size=(2, 3))\n\n@test\ndef test_MT19937_choice(seed,\n                        a,\n                        expected,\n                        size=None,\n                        r=True,\n                        p=None,\n                        axis=0,\n                        shuffle=True):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if isinstance(a, int):\n            assert gen.choice(a, size, r, p, axis, shuffle) == expected\n        elif static.len(a.shape) == 1:\n            assert gen.choice(a, size, r, p, axis, shuffle) == expected\n        else:\n            assert (round(gen.choice(a, size, r, p, axis, shuffle),\n                          8) == expected).all()\n    else:\n        assert (round(gen.choice(a, size, r, p, axis, shuffle),\n                      8) == expected).all()\n\ntest_MT19937_choice(\n    621,\n    np.array([\n        1.34, 2.15, 3.21, 4.78, 5.55, 6.42, 7.99, 8.31, 9.61, 10.75, 11.33,\n        12.93\n    ]), np.array([9.61, 8.31, 1.34]), 3)\ntest_MT19937_choice(\n    318, np.array([5, 4, 2, 6, 7, 8, 2, 12, 1, 55, 33, 7, 78, 15, 43]), 5)\ntest_MT19937_choice(318, np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),\n                    np.array([12, 1, 11]), 3, False)\ntest_MT19937_choice(\n    4589,\n    np.array([\n        -1.34, -2.15, -3.21, -4.78, -5.55, -6.42, -7.99, -8.31, -9.61, -10.75,\n        -11.33, -12.93\n    ]), -1.34)\ntest_MT19937_choice(318,\n                    np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]),\n                    np.array([[1, 2, 3], [10, 11, 12]]), 2)\ntest_MT19937_choice(74,\n                    np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]),\n                    np.array([7, 8, 9]))\ntest_MT19937_choice(74, 1, 0)\ntest_MT19937_choice(74, np.array([13, -4]), -4, p=np.array([0.3, 0.7]))\ntest_MT19937_choice(74,\n                    np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]),\n                    np.array([[3], [6], [9], [12]]),\n                    axis=1)\ntest_MT19937_choice(74,\n                    np.array([13, -4, 3, 18]),\n                    -4,\n                    p=np.array([0.5, 0.3, 0.1, 0.1]),\n                    shuffle=True)\n\n@test\ndef test_MT19937_bytes(seed, length, expected):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    check = gen.bytes(length) == expected\n    assert check\n\ntest_MT19937_bytes(555, 10, \"\\x93\\x94\\x84Ym\\xca5\\xa7\\x0bi\")\n\n@test\ndef test_MT19937_shuffle(seed, x, expected, axis=0):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    gen.shuffle(x, axis)\n    assert (x == expected).all()\n\ntest_MT19937_shuffle(876, np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),\n                     np.array([9, 0, 5, 7, 4, 3, 1, 8, 6, 2]))\ntest_MT19937_shuffle(111, np.array([5.43, 6.56, 1.22, 4.3221, 7.88, 0.9352]),\n                     np.array([1.22, 4.3221, 6.56, 0.9352, 5.43, 7.88]))\ntest_MT19937_shuffle(1234, np.array([3.14]), np.array([3.14]))\ntest_MT19937_shuffle(74,\n                     np.arange(9).reshape((3, 3)),\n                     np.array([[6, 7, 8], [3, 4, 5], [0, 1, 2]]))\ntest_MT19937_shuffle(74,\n                     np.arange(9).reshape((3, 3)),\n                     np.array([[2, 1, 0], [5, 4, 3], [8, 7, 6]]),\n                     axis=1)\n\n@test\ndef test_MT19937_permutation(seed, x, expected, axis=0):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    assert (gen.permutation(x, axis) == expected).all()\n\ntest_MT19937_permutation(12345, 10, np.array([6, 8, 0, 7, 9, 2, 5, 4, 1, 3]))\ntest_MT19937_permutation(1234,\n                         np.array([1.124, 4.7532, 9.1246, 12.53243, 15.64324]),\n                         np.array([1.124, 12.53243, 4.7532, 15.64324, 9.1246]))\ntest_MT19937_permutation(\n    4321,\n    np.arange(24).reshape(3, 8),\n    np.array([[0, 1, 2, 3, 4, 5, 6, 7], [8, 9, 10, 11, 12, 13, 14, 15],\n              [16, 17, 18, 19, 20, 21, 22, 23]]))\ntest_MT19937_permutation(74,\n                         np.arange(9).reshape((3, 3)),\n                         np.array([[2, 1, 0], [5, 4, 3], [8, 7, 6]]),\n                         axis=1)\n\n@test\ndef test_MT19937_permuted(seed, x, expected, axis=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    arr = gen.permuted(x, axis)\n    assert (arr == expected).all()\n\ntest_MT19937_permuted(4321,\n                      np.array([1.124, 4.7532, 9.1246, 12.53243, 15.64324]),\n                      np.array([15.64324, 9.1246, 1.124, 12.53243, 4.7532]))\ntest_MT19937_permuted(\n    4321,\n    np.arange(24).reshape(3, 8),\n    np.array([[7, 8, 10, 2, 9, 15, 23, 20], [19, 17, 13, 21, 18, 16, 4, 5],\n              [11, 3, 1, 22, 0, 14, 12, 6]]))\n\n@test\ndef test_MT19937_beta(seed, a, b, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(a, int) or isinstance(a, float)) and (isinstance(\n                b, int) or isinstance(b, float)):\n            assert gen.beta(a, b) == expected\n        else:\n            assert (round(gen.beta(a, b), 8) == expected).all()\n    else:\n        assert (round(gen.beta(a, b, size), 8) == expected).all()\n\n#test_MT19937_beta(4321, -7, 0, error)\ntest_MT19937_beta(4321, 0.2, 0.1, 0.001794143054409161)\ntest_MT19937_beta(4321, 10, 14, 0.44288329572745766)\ntest_MT19937_beta(4321, 167, 2041,\n                  np.array([0.08210844, 0.07407015, 0.07356348, 0.07749650]),\n                  4)\ntest_MT19937_beta(139, 14.32, 4, 0.498515830795026)\ntest_MT19937_beta(457, 12.87, 9.21, 0.5178223999023288)\ntest_MT19937_beta(\n    457, np.array([12.87, 1.32, 4.532, 1.34, 8.432]), 9.21,\n    np.array([0.51782240, 0.10097888, 0.38175823, 0.36792669, 0.48282513]))\ntest_MT19937_beta(\n    457, 32.14, np.array([9.21, 0.21, 14.32, 41.21, 5.55]),\n    np.array([[0.73639876, 1.00000000, 0.68338397, 0.53857496, 0.83560158]]))\ntest_MT19937_beta(\n    457, np.array([12.87, 1.32, 4.532, 1.34, 8.432]),\n    np.array([9.21, 0.21, 14.32, 41.21, 5.55]),\n    np.array([0.51782240, 0.99999999, 0.29636436, 0.10105227, 0.59638962]))\n\n@test\ndef test_MT19937_binomial(seed, n, p, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(n, int) or isinstance(n, float)) and (isinstance(\n                p, int) or isinstance(p, float)):\n            assert gen.binomial(n, p) == expected\n        else:\n            assert (gen.binomial(n, p) == expected).all()\n    else:\n        assert (gen.binomial(n, p, size) == expected).all()\n\n#test_MT19937_binomial(139, -2, .33, error)\ntest_MT19937_binomial(139, 28400, .66, 18590)\ntest_MT19937_binomial(139, 14.76, .33, 6)\ntest_MT19937_binomial(139, 0, .33, 0)\ntest_MT19937_binomial(139, 14, 0, 0)\ntest_MT19937_binomial(147, 10, .5, np.array([6, 6, 3, 5, 7, 5, 5, 6, 5, 3]),\n                      10)\ntest_MT19937_binomial(457, np.array([12, 1, 4, 7, 8]), 0.11,\n                      np.array([2, 0, 0, 0, 0]))\ntest_MT19937_binomial(457, 12, np.array([0.11, 0.21, 0.32, 0.43]),\n                      np.array([2, 3, 1, 4]))\ntest_MT19937_binomial(457, np.array([12, 1, 4, 7, 8]),\n                      np.array([0.11, 0.21, 0.32, 0.43, 0.55]),\n                      np.array([2, 0, 0, 2, 5]))\n\n@test\ndef test_MT19937_chisquare(seed, df, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(df, float) or isinstance(df, int)):\n            assert gen.chisquare(df) == expected\n        else:\n            assert (round(gen.chisquare(df), 8) == expected).all()\n    else:\n        assert (round(gen.chisquare(df, size), 8) == expected).all()\n\ntest_MT19937_chisquare(457, 12.12, 10.371553390964028)\ntest_MT19937_chisquare(457, 24.12, np.array([21.88993555, 28.10914107]), 2)\ntest_MT19937_chisquare(\n    457, np.array([23, 42, 34, 17]),\n    np.array([20.80858708, 47.42164781, 32.29862709, 14.08026439]))\ntest_MT19937_chisquare(\n    457, np.array([274.23, 325.42, 352.34, 825.17]),\n    np.array([268.13692669, 341.31749251, 348.28803050, 807.81116202]))\n\n@test\ndef test_MT19937_dirichlet(seed, alpha, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    assert (round(gen.dirichlet(alpha, size), 8) == round(expected, 8)).all()\n\ntest_MT19937_dirichlet(\n    457, np.array([278.23, 325.42, 352.34, 825.17]),\n    np.array([0.15451881, 0.18988989, 0.19714225, 0.45844905]))\ntest_MT19937_dirichlet(\n    874, np.array([10, 5, 3]),\n    np.array([[0.63510742, 0.22941463, 0.13547795],\n              [0.74168572, 0.20100343, 0.05731085]]), 2)\n#test_MT19937_dirichlet(1234, np.array([]), np.array([]))\ntest_MT19937_dirichlet(74, np.array([0.01, 0.05]),\n                       np.array([1.85187737e-12, 1.00000000]))\n\n@test\ndef test_MT19937_exponential(seed, scale, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(scale, float) or isinstance(scale, int)):\n            assert gen.exponential(scale) == expected\n        else:\n            assert (round(gen.exponential(scale), 8) == expected).all()\n    else:\n        assert (round(gen.exponential(scale, size), 8) == expected).all()\n\ntest_MT19937_exponential(74, 1, 0.29730933531689413)\ntest_MT19937_exponential(874, 3, 1.3077665346601641)\ntest_MT19937_exponential(874, 724.952, 316.0226549449851)\ntest_MT19937_exponential(\n    874, np.array([278.23, 325.42, 352.34, 825.17]),\n    np.array([121.28662765, 1308.69597268, 1407.647810890, 156.18934242]))\ntest_MT19937_exponential(\n    874, 724.952,\n    np.array([\n        316.02265494, 2915.43778128, 2896.28511041, 137.21993791, 449.65873658,\n        462.39004499, 1267.56770807\n    ]), 7)\n\n@test\ndef test_MT19937_f(seed, dfnum, dfden, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(dfnum, float)\n                or isinstance(dfnum, int)) and (isinstance(dfden, float)\n                                                or isinstance(dfden, int)):\n            assert gen.f(dfnum, dfden) == expected\n        else:\n            assert (round(gen.f(dfnum, dfden), 8) == expected).all()\n    else:\n        assert (round(gen.f(dfnum, dfden, size), 8) == expected).all()\n\ntest_MT19937_f(874, 12, 4.21, 1.6343383299954102)\ntest_MT19937_f(\n    874, np.array([14, 15, 16, 17, 18]), 4.21,\n    np.array([1.60433134, 0.33113465, 2.70993818, 1.30846860, 0.31543733]))\ntest_MT19937_f(\n    874, 2557, 0.92,\n    np.array([0.35240241, 0.35598113, 1.55704194, 1.06506043, 0.51375425]), 5)\ntest_MT19937_f(874, 7343, np.array([12.52, 0.92, 231.52]),\n               np.array([1.07913456, 0.35586940, 1.10156925]))\n\n@test\ndef test_MT19937_gamma(seed, shape, expected, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(shape, float)\n                or isinstance(shape, int)) and (isinstance(scale, float)\n                                                or isinstance(scale, int)):\n            assert gen.gamma(shape, scale) == expected\n        else:\n            assert (round(gen.gamma(shape, scale), 8) == expected).all()\n    else:\n        assert (round(gen.gamma(shape, scale, size), 8) == expected).all()\n\ntest_MT19937_gamma(318, 564, 579.9843506186213)\ntest_MT19937_gamma(318, 564, 3189.9139284024172, 5.5)\ntest_MT19937_gamma(318, 564, np.array([3189.91392840, 1295.02679157]),\n                   np.array([5.5, 2.2]))\ntest_MT19937_gamma(\n    318, 564,\n    np.array([2391.52777079, 2427.25399922, 2330.71722926, 2310.71793516]),\n    4.123435, 4)\ntest_MT19937_gamma(\n    318, np.array([19, 17, 45, 52, 21]),\n    np.array(\n        [185.59130933, 181.36147656, 384.65049865, 432.13182218,\n         150.84772053]), 8.527)\ntest_MT19937_gamma(\n    318, np.array([19, 17, 45, 52, 21]),\n    np.array(\n        [23.94164891, 93.58396820, 90.21942035, 385.15326007, 174.60619229]),\n    np.array([1.1, 4.4, 2, 7.6, 9.87]))\n\n@test\ndef test_MT19937_geometric(seed, p, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if isinstance(p, float) or isinstance(p, int):\n            assert gen.geometric(p) == expected\n        else:\n            assert (gen.geometric(p) == expected).all()\n    else:\n        assert (gen.geometric(p, size) == expected).all()\n\ntest_MT19937_geometric(457, 0.35, 3)\ntest_MT19937_geometric(457, 0.2, 7)\ntest_MT19937_geometric(2000, 1, 1)\ntest_MT19937_geometric(12654, 0.5, np.array([1., 5., 3., 1.]), 4)\ntest_MT19937_geometric(12654, np.array([0.01, 0.6, 0.45, 0.33]),\n                       np.array([53, 4, 4, 1]))\n\n@test\ndef test_MT19937_gumbel(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.gumbel(loc, scale) == expected\n        else:\n            assert (round(gen.gumbel(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.gumbel(loc, scale, size), 8) == expected).all()\n\ntest_MT19937_gumbel(1234, 2.0536230569636045)\ntest_MT19937_gumbel(1234, 14.053623056963605, 12)\ntest_MT19937_gumbel(1234, 250.54201294955973, scale=122)\ntest_MT19937_gumbel(1234,\n                    np.array([2.05362306, 0.65968578, -0.74255606]),\n                    size=3)\ntest_MT19937_gumbel(1234,\n                    np.array(\n                        [2.85362306, 1.82968578, -0.61255606, 264.30679443]),\n                    loc=np.array([0.8, 1.17, 0.13, 265.45]))\ntest_MT19937_gumbel(1234,\n                    np.array(\n                        [24.23275207, 33.75612153, -44.64989592, -7.37367590]),\n                    scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_MT19937_hypergeometric(seed,\n                                ngood,\n                                nbad,\n                                nsample,\n                                expected,\n                                size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(ngood, int) or isinstance(ngood, float)) and (\n                isinstance(nbad, int) or isinstance(nbad, float)) and (\n                    isinstance(nsample, int) or isinstance(nsample, float)):\n            assert gen.hypergeometric(ngood, nbad, nsample) == expected\n        else:\n            assert (gen.hypergeometric(ngood, nbad, nsample) == expected).all()\n    else:\n        assert (gen.hypergeometric(ngood, nbad, nsample,\n                                   size) == expected).all()\n\ntest_MT19937_hypergeometric(1234, 11, 12, 9, 6)\ntest_MT19937_hypergeometric(1234, 100, 300, 200, 43)\n#test_MT19937_hypergeometric(1234, 100.31, 300.55, 200.2, 43)\ntest_MT19937_hypergeometric(1234, 100, 300, np.array([200, 201, 222, 221]),\n                            np.array([43, 55, 57, 60]))\ntest_MT19937_hypergeometric(1234, 100, np.array([301, 303, 304, 344, 355]),\n                            200, np.array([43, 45, 48, 41, 44]))\ntest_MT19937_hypergeometric(1234, np.array([100, 111, 112, 122]), 300, 200,\n                            np.array([43, 50, 53, 53]))\ntest_MT19937_hypergeometric(1234, 100, 700, 450, np.array([63, 61, 58, 61,\n                                                           56]), 5)\n\n@test\ndef test_MT19937_laplace(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.laplace(loc, scale) == expected\n        else:\n            assert (round(gen.laplace(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.laplace(loc, scale, size), 8) == expected).all()\n\ntest_MT19937_laplace(1234, -1.4239250945926396)\ntest_MT19937_laplace(1234, 10.57607490540736, 12)\ntest_MT19937_laplace(1234, -173.71886154030204, scale=122)\ntest_MT19937_laplace(1234,\n                     np.array([-1.42392509, -0.21393255, 1.40815252]),\n                     size=3)\ntest_MT19937_laplace(1234,\n                     np.array(\n                         [-0.62392509, 0.95606745, 1.53815252, 267.89366033]),\n                     loc=np.array([0.8, 1.17, 0.13, 265.45]))\ntest_MT19937_laplace(\n    1234,\n    np.array([-16.80231612, -10.94692881, 84.67221132, 15.76160913]),\n    scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_MT19937_logistic(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.logistic(loc, scale) == expected\n        else:\n            assert (round(gen.logistic(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.logistic(loc, scale, size), 8) == expected).all()\n\ntest_MT19937_logistic(1234, -1.9888029415511161)\ntest_MT19937_logistic(1234, 10.011197058448884, 12)\ntest_MT19937_logistic(1234, -242.63395886923618, scale=122)\ntest_MT19937_logistic(1234,\n                      np.array([-1.98880294, -0.39006597, 1.97085227]),\n                      size=3)\ntest_MT19937_logistic(1234,\n                      np.array(\n                          [-1.18880294, 0.77993403, 2.10085227, 268.54241540]),\n                      loc=np.array([0.8, 1.17, 0.13, 265.45]))\ntest_MT19937_logistic(\n    1234,\n    np.array([-23.46787471, -19.95967576, 118.50734684, 19.94607935]),\n    scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_MT19937_lognormal(seed, expected, mean=0.0, sigma=1.0, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(mean, float) or isinstance(mean, int)) and (isinstance(\n                sigma, float) or isinstance(sigma, int)):\n            assert gen.lognormal(mean, sigma) == expected\n        else:\n            assert (round(gen.lognormal(mean, sigma), 8) == expected).all()\n    else:\n        assert (round(gen.lognormal(mean, sigma, size), 8) == expected).all()\n\ntest_MT19937_lognormal(1234, 7.733643068788345)\ntest_MT19937_lognormal(1234, 1258687.4645696718, 12)\ntest_MT19937_lognormal(1234, 59.809235115418005, sigma=2)\ntest_MT19937_lognormal(1234,\n                       np.array([7.73364307, 0.81831089, 1.03314855]),\n                       size=3)\ntest_MT19937_lognormal(\n    1234,\n    np.array([17.21153918, 2.63659168, 1.17657889, 756.94196432]),\n    mean=np.array([0.8, 1.17, 0.13, 5.45]))\ntest_MT19937_lognormal(1234,\n                       np.array(\n                           [39.72751712, 0.35464005, 1.00424843, 58.47145689]),\n                       sigma=np.array([1.8, 5.17, 0.13, 3.45]))\n\n@test\ndef test_MT19937_logseries(seed, p, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if isinstance(p, float) or isinstance(p, int):\n            assert gen.logseries(p) == expected\n        else:\n            assert (gen.logseries(p) == expected).all()\n    else:\n        assert (gen.logseries(p, size) == expected).all()\n\ntest_MT19937_logseries(457, 0.35, 1)\ntest_MT19937_logseries(2000, 0, 1)\ntest_MT19937_logseries(457, 0.5, np.array([1., 1., 2., 1.]), 4)\ntest_MT19937_logseries(457, np.array([0.01, 0.6, 0.45, 0.33]),\n                       np.array([1, 1, 2, 1]))\n\n@test\ndef test_MT19937_multinomial(seed, n, pvals, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    assert (round(gen.multinomial(n, pvals, size), 8) == expected).all()\n\ntest_MT19937_multinomial(457, 20, [1 / 6.], np.array([20]))\ntest_MT19937_multinomial(457, 20.6, [1 / 6.], np.array([20]))\ntest_MT19937_multinomial(457, 20, [1 / 6.] * 6, np.array([4, 4, 1, 3, 4, 4]))\ntest_MT19937_multinomial(\n    457, 20, [1 / 6] * 6,\n    np.array([[4, 4, 1, 3, 4, 4], [4, 0, 4, 3, 6, 3], [4, 4, 5, 1, 1, 5]]), 3)\n#test_MT19937_multinomial(457, 1, np.array([[.1, .5, .4 ], [.3, .7, .0]]), np.array([[0, 0, 1], [0, 1, 0]]))\n#test_MT19937_multinomial(457, -1, [1/6.], error)\n\n@test\ndef test_MT19937_multivariate_hypergeometric(seed,\n                                             colors,\n                                             nsample,\n                                             expected,\n                                             size=None,\n                                             method='marginals'):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    assert (gen.multivariate_hypergeometric(colors, nsample, size,\n                                            method) == expected).all()\n\ntest_MT19937_multivariate_hypergeometric(457, np.array([16, 8, 4]), 15,\n                                         np.array([10, 4, 1]))\ntest_MT19937_multivariate_hypergeometric(457,\n                                         np.array([16, 8, 4]),\n                                         15,\n                                         np.array([[10, 4, 1], [8, 5, 2],\n                                                   [9, 4, 2]]),\n                                         size=3)\ntest_MT19937_multivariate_hypergeometric(457, np.array([16, 8, 4]), 0,\n                                         np.array([0, 0, 0]))\ntest_MT19937_multivariate_hypergeometric(457,\n                                         np.array([16, 8, 4]),\n                                         8,\n                                         np.array([4, 3, 1]),\n                                         method='count')\n\n@test\ndef test_MT19937_multivariate_normal(seed,\n                                     mean,\n                                     cov,\n                                     expected,\n                                     size=None,\n                                     check_valid: Literal[str] = 'warn',\n                                     tol: float = 1e-8,\n                                     m: Literal[str] = 'svd'):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    assert (round(\n        gen.multivariate_normal(mean,\n                                cov,\n                                size,\n                                check_valid=check_valid,\n                                tol=tol,\n                                method=m), 8) == expected).all()\n\ntest_MT19937_multivariate_normal(1234, (1, 2), np.array([[1, 0], [0, 1]]),\n                                 np.array([3.04558004, 1.79948705]))\ntest_MT19937_multivariate_normal(5432, (1, 2),\n                                 np.array([[1, 0], [0, 1]]),\n                                 np.array([0.16390254, 2.42265006]),\n                                 m='cholesky')\n\n@test\ndef test_MT19937_negative_binomial(seed, n, p, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(n, int) or isinstance(n, float)) and (isinstance(\n                p, int) or isinstance(p, float)):\n            assert gen.negative_binomial(n, p) == expected\n        else:\n            assert (gen.negative_binomial(n, p) == expected).all()\n    else:\n        assert (gen.negative_binomial(n, p, size) == expected).all()\n\ntest_MT19937_negative_binomial(139, 28400, .66, 14541)\ntest_MT19937_negative_binomial(139, 14.76, .33, 21)\n#test_MT19937_negative_binomial(139, 14, 0, error)\ntest_MT19937_negative_binomial(147, 10, .5,\n                               np.array([6, 6, 11, 9, 8, 7, 10, 22, 4, 10]),\n                               10)\ntest_MT19937_negative_binomial(457, np.array([12, 1, 4, 7, 8]), 0.11,\n                               np.array([66, 1, 14, 70, 68]))\ntest_MT19937_negative_binomial(457, 12, np.array([0.11, 0.21, 0.32, 0.43]),\n                               np.array([[66, 39, 41, 15]]))\n\n@test\ndef test_MT19937_noncentral_chisquare(seed, df, nonc, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(df, int) or isinstance(df, float)) and (isinstance(\n                nonc, int) or isinstance(nonc, float)):\n            assert gen.noncentral_chisquare(df, nonc) == expected\n        else:\n            assert (round(gen.noncentral_chisquare(df, nonc),\n                          8) == expected).all()\n    else:\n        assert (round(gen.noncentral_chisquare(df, nonc, size),\n                      8) == expected).all()\n\ntest_MT19937_noncentral_chisquare(457, 0.1, 0.2, 0.004731999292935856)\ntest_MT19937_noncentral_chisquare(457, 5, 0, 3.681528279878184)\ntest_MT19937_noncentral_chisquare(457, 99, 7, 104.90202975815625)\ntest_MT19937_noncentral_chisquare(\n    457, 14.2, 0.5, np.array([13.21259789, 7.10349904, 15.26757211]), 3)\ntest_MT19937_noncentral_chisquare(\n    457, np.array([8, 7.1, 45.3]), 0.6,\n    np.array([7.53680531, 2.86873129, 44.87145655]))\ntest_MT19937_noncentral_chisquare(\n    457, 0.6, np.array([8, 7.1, 45.3]),\n    np.array([5.50303631, 0.12292794, 48.45712182]))\n\n@test\ndef test_MT19937_noncentral_f(seed, dfnum, dfden, nonc, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(dfnum, float) or isinstance(dfnum, int)) and (\n                isinstance(dfden, int)\n                or isinstance(dfden, float)) and (isinstance(nonc, int)\n                                                  or isinstance(nonc, float)):\n            assert gen.noncentral_f(dfnum, dfden, nonc) == expected\n        else:\n            assert (round(gen.noncentral_f(dfnum, dfden, nonc),\n                          8) == expected).all()\n    else:\n        assert (round(gen.noncentral_f(dfnum, dfden, nonc, size),\n                      8) == expected).all()\n\ntest_MT19937_noncentral_f(874, 3, 20, 3, 0.34619084015853513)\ntest_MT19937_noncentral_f(874, 3, 20, 0, 1.714196752567725)\ntest_MT19937_noncentral_f(\n    874, np.array([14, 15, 16, 17, 18]), 4.21, 6.7,\n    np.array([2.22094675, 1.17172906, 7.58260278, 0.82149275, 2.28171278]))\ntest_MT19937_noncentral_f(\n    874, 27, 5.92, 3.1,\n    np.array([1.58502318, 0.92155582, 3.77043692, 0.68717422, 1.64072948]), 5)\ntest_MT19937_noncentral_f(\n    874, 27, np.array([5.92, 8.13, 9.53, 33.14]), 3.1,\n    np.array([1.58502318, 0.89487268, 2.43982719, 0.92196298]))\ntest_MT19937_noncentral_f(\n    874, 743, 8.24, np.array([0.11, 0.21, 0.32, 0.43]),\n    np.array([1.20344619, 1.02049827, 3.17379438, 0.61622202]))\n\n@test\ndef test_MT19937_normal(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.normal(loc, scale) == expected\n        else:\n            assert (round(gen.normal(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.normal(loc, scale, size), 8) == expected).all()\n\ntest_MT19937_normal(1234, 2.0455800412005876)\ntest_MT19937_normal(1234, 14.045580041200587, 12)\ntest_MT19937_normal(1234, 249.56076502647167, scale=122)\ntest_MT19937_normal(1234,\n                    np.array([2.04558004, -0.20051295, 0.03261098]),\n                    size=3)\ntest_MT19937_normal(1234,\n                    np.array([2.84558004, 0.96948705, 0.16261098,\n                              26.62928659]),\n                    loc=np.array([0.8, 1.17, 0.13, 25.45]))\ntest_MT19937_normal(1234,\n                    np.array(\n                        [24.13784449, -10.26024755, 1.96089830, 7.60639847]),\n                    scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_MT19937_pareto(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if isinstance(a, float) or isinstance(a, int):\n            assert gen.pareto(a) == expected\n        else:\n            assert (round(gen.pareto(a), 8) == expected).all()\n    else:\n        assert (round(gen.pareto(a, size), 8) == expected).all()\n\ntest_MT19937_pareto(1234, 3, 0.08386598990600276)\ntest_MT19937_pareto(\n    1234, 5,\n    np.array([0.04950703, 0.11427519, 0.21520507, 0.44809618, 0.16192707]), 5)\ntest_MT19937_pareto(\n    1234, np.array([3, 13, 4, 23.2, 5.6]),\n    np.array([0.08386599, 0.04249510, 0.27588628, 0.08306523, 0.14339271]))\n\n@test\ndef test_MT19937_poisson(seed, expected, lam=1.0, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if isinstance(lam, float) or isinstance(lam, int):\n            assert gen.poisson(lam) == expected\n        else:\n            assert (round(gen.poisson(lam), 8) == expected).all()\n    else:\n        assert (round(gen.poisson(lam, size), 8) == expected).all()\n\ntest_MT19937_poisson(1234, 0, 0)\ntest_MT19937_poisson(1234, 0)\ntest_MT19937_poisson(1234, 1, 3)\ntest_MT19937_poisson(1234, 9, 14.65)\ntest_MT19937_poisson(1234, np.array([27., 14., 17., 82., 30.]),\n                     np.array([35.62, 9.23, 9.57, 76.79, 21.74]))\ntest_MT19937_poisson(1234, np.array([785., 818., 868., 839., 803.]), 824.14, 5)\n\n@test\ndef test_MT19937_power(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if isinstance(a, float) or isinstance(a, int):\n            assert gen.power(a) == expected\n        else:\n            assert (round(gen.power(a), 8) == expected).all()\n    else:\n        assert (round(gen.power(a, size), 8) == expected).all()\n\ntest_MT19937_power(1234, 1, 0.21463194364452776)\ntest_MT19937_power(1234, 45.11, 0.966462434390213)\ntest_MT19937_power(1234, 6, np.array([0.77377684, 0.86464152, 0.92407356]), 3)\ntest_MT19937_power(\n    1234, np.array([5.4, 1.1, 78, 19.34, 99999999999999]),\n    np.array(\n        array([0.75203784, 0.45234475, 0.99394429, 0.99120555, 1.00000000])))\n\n@test\ndef test_MT19937_rayleigh(seed, expected, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if isinstance(scale, float) or isinstance(scale, int):\n            assert gen.rayleigh(scale) == expected\n        else:\n            assert (round(gen.rayleigh(scale), 8) == expected).all()\n    else:\n        assert (round(gen.rayleigh(scale, size), 8) == expected).all()\n\n#test_MT19937_rayleigh(1234, error, -1)\ntest_MT19937_rayleigh(1234, 0.6951299295551241)\ntest_MT19937_rayleigh(1234, 2.0853897886653723, 3)\ntest_MT19937_rayleigh(1234, 10.183653467982568, 14.65)\ntest_MT19937_rayleigh(1234,\n                      np.array(\n                          [0.69512993, 1.04021218, 1.39611191, 1.92418740]),\n                      size=4)\ntest_MT19937_rayleigh(\n    1234,\n    np.array([3.75370162, 1.14423340, 108.89672861, 37.21378433, 68.84898931]),\n    np.array([5.4, 1.1, 78, 19.34, 56.2]))\n\n@test\ndef test_MT19937_standard_cauchy(seed, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        assert gen.standard_cauchy() == expected\n    else:\n        assert (round(gen.standard_cauchy(size), 8) == round(expected,\n                                                             8)).all()\n\ntest_MT19937_standard_cauchy(1234, -10.201735410135576)\ntest_MT19937_standard_cauchy(\n    1234, np.array([-10.20173541, 0.02765314, 1.39310640, 0.50990013]), 4)\n\n@test\ndef test_MT19937_standard_exponential(seed, expected, size=None, m='zig'):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        assert gen.standard_exponential(method=m) == expected\n    else:\n        assert (round(gen.standard_exponential(size, method=m),\n                      8) == expected).all()\n\ntest_MT19937_standard_exponential(1234, 0.24160280948165586)\ntest_MT19937_standard_exponential(\n    1234, np.array([0.24160281, 0.54102069, 0.97456423, 1.85124858]), 4)\ntest_MT19937_standard_exponential(1234, 0.1282693336014689, m='inv')\n\n@test\ndef test_MT19937_standard_gamma(seed, shape, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if isinstance(shape, float) or isinstance(shape, int):\n            assert gen.standard_gamma(shape) == expected\n        else:\n            assert (round(gen.standard_gamma(shape), 8) == expected).all()\n    else:\n        assert (round(gen.standard_gamma(shape, size), 8) == expected).all()\n\ntest_MT19937_standard_gamma(318, 1, 0.0589214166795813)\ntest_MT19937_standard_gamma(1234, 0, 0)\ntest_MT19937_standard_gamma(1234, 0.5, 0.014492202246606063)\ntest_MT19937_standard_gamma(1234, 3, 7.59601798666233)\ntest_MT19937_standard_gamma(\n    1234, 0.5, np.array([0.01449220, 1.44979965, 0.18186815, 1.71465035]), 4)\ntest_MT19937_standard_gamma(\n    1234, np.array([4.5, 2, 7.7, 81.15]),\n    np.array([9.89229577, 1.70912275, 8.61357207, 77.06196796]))\n\n@test\ndef test_MT19937_standard_normal(seed, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        assert gen.standard_normal(size) == expected\n    else:\n        assert (round(gen.standard_normal(size), 8) == expected).all()\n\ntest_MT19937_standard_normal(1234, 2.0455800412005876)\ntest_MT19937_standard_normal(1234,\n                             np.array([2.04558004, -0.20051295, 0.03261098]),\n                             3)\n\n@test\ndef test_MT19937_standard_t(seed, df, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if isinstance(df, float) or isinstance(df, int):\n            assert gen.standard_t(df) == expected\n        else:\n            assert (round(gen.standard_t(df), 8) == expected).all()\n    else:\n        assert (round(gen.standard_t(df, size), 8) == expected).all()\n\ntest_MT19937_standard_t(1234, 3, 2.5527064129505828)\ntest_MT19937_standard_t(1234, 1, 3.582953759233768)\ntest_MT19937_standard_t(1234, 0.5, 6.275754251724512)\ntest_MT19937_standard_t(1234, 0.5,\n                        np.array([6.27575425, 3.24214707, -0.29526934]), 3)\ntest_MT19937_standard_t(1234, np.array([99, 5.6, 11.43]),\n                        np.array([2.08220290, 1.10034819, -0.52927360]))\n\n@test\ndef test_MT19937_triangular(seed, left, mode, right, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(left, int) or isinstance(left, float)) and (isinstance(\n                mode, int) or isinstance(mode, float)) and (isinstance(\n                    right, int) or isinstance(right, float)):\n            assert gen.triangular(left, mode, right) == expected\n        else:\n            assert (round(gen.triangular(left, mode, right),\n                          8) == expected).all()\n    else:\n        assert (round(gen.triangular(left, mode, right, size),\n                      8) == expected).all()\n\ntest_MT19937_triangular(1234, -3, 0, 8, -1.006847326513437)\ntest_MT19937_triangular(1234, np.array([-1, -4.2, -3.2]), -0.2, 77,\n                        np.array([4.22158056, 15.86096882, 49.48278983]))\ntest_MT19937_triangular(\n    1234, -4, np.array([0.1, 0.5, 3, 6, 7.9]), 8,\n    np.array([-1.56630501, 0.67423231, 5.29115477, 6.97916271, 3.80374854]))\ntest_MT19937_triangular(1234, -77, 0, np.array([0.1, 11, 789]),\n                        np.array([-50.26647987, -24.69817576, 499.92800867]))\ntest_MT19937_triangular(\n    1234,\n    2,\n    10,\n    53,\n    np.array([9.00831604, 16.83810311, 36.62324385, 43.24178867]),\n    size=4)\n\n@test\ndef test_MT19937_uniform(seed, expected, low=0.0, high=1.0, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    result = round(gen.uniform(low, high, size), 8)\n    if size is None:\n        if (isinstance(low, int) or isinstance(low, float)) and (isinstance(\n                high, int) or isinstance(high, float)):\n            assert result == round(expected, 8)\n        else:\n            assert (result == expected).all()\n    else:\n        assert (result == expected).all()\n\ntest_MT19937_uniform(1234, 6.203835630250495, 5, 15)\ntest_MT19937_uniform(1234, 0.20834520672254453, 0.1)\ntest_MT19937_uniform(1234, 14.686794689056038, high=122)\ntest_MT19937_uniform(1234,\n                     np.array([0.12038356, 0.40370142, 0.87770263]),\n                     size=3)\ntest_MT19937_uniform(1234,\n                     np.array(\n                         [5.76910691, 19.95545962, 40.23040240, 43.34999446]),\n                     low=np.array([0.4, 3., 6., 7.]),\n                     high=45.)\ntest_MT19937_uniform(\n    1234,\n    np.array([6.74147953, 6.45922271, 38.61891553, 2.96539428, 0.08529200]),\n    high=np.array([56., 16., 44., 3.1, 0.2]))\n\n@test\ndef test_MT19937_vonmises(seed, mu, kappa, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(mu, float) or isinstance(mu, int)) and (isinstance(\n                kappa, float) or isinstance(kappa, int)):\n            assert gen.vonmises(mu, kappa) == expected\n        else:\n            assert (round(gen.vonmises(mu, kappa), 8) == expected).all()\n    else:\n        assert (round(gen.vonmises(mu, kappa, size), 8) == expected).all()\n\ntest_MT19937_vonmises(1234, 0, 4, 0.09543421777776429)\ntest_MT19937_vonmises(1234, 0.1, 0.2, 0.412385001376804)\ntest_MT19937_vonmises(1234, -1, 0, -2.3852004191648746)\ntest_MT19937_vonmises(1234,\n                      0,\n                      5,\n                      np.array([0.08543132, 0.21199714, 0.75964701]),\n                      size=3)\ntest_MT19937_vonmises(\n    1234, np.array([43.09, 18.62, 42, 16.94]), 5,\n    np.array([-0.80686583, -0.01755878, -1.22265014, -2.50501149]))\ntest_MT19937_vonmises(\n    1234, 4, np.array([0, 0.1, 7.67, 99]),\n    np.array([-2.38520042, -1.10822682, -1.99926278, -2.42105600]))\n\n@test\ndef test_MT19937_wald(seed, mean, scale, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(mean, float) or isinstance(mean, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.wald(mean, scale) == expected\n        else:\n            assert (round(gen.wald(mean, scale), 8) == expected).all()\n    else:\n        assert (round(gen.wald(mean, scale, size), 8) == expected).all()\n\ntest_MT19937_wald(1234, 0.1, 0.1, 0.01661616443641617)\ntest_MT19937_wald(1234, 3, 2, 0.36791953972524594)\ntest_MT19937_wald(1234, 3, 2, np.array([0.36791954, 3.12223711, 1.77030450]),\n                  3)\ntest_MT19937_wald(1234, np.array([0.1, 0.5, 1, 4]), 0.5,\n                  np.array([0.04124719, 0.51657353, 0.54509668, 12.47886442]))\ntest_MT19937_wald(1234, 0.5, np.array([0.1, 0.5, 1, 4]),\n                  np.array([0.02185479, 0.51657353, 0.36787436, 0.58084343]))\ntest_MT19937_wald(1234, np.array([0.1, 0.5, 1, 4]), np.array([3, 2, 1, 8]),\n                  np.array([0.06898142, 0.50821948, 0.64901109, 5.39356830]))\n\n@test\ndef test_MT19937_weibull(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(a, float) or isinstance(a, int)):\n            assert gen.weibull(a) == expected\n        else:\n            assert (round(gen.weibull(a), 8) == expected).all()\n    else:\n        assert (round(gen.weibull(a, size), 8) == expected).all()\n\ntest_MT19937_weibull(1234, 0, 0)\ntest_MT19937_weibull(1234, 0.1, 6.776724055705368e-07)\ntest_MT19937_weibull(1234, 1, 0.24160280948165586)\ntest_MT19937_weibull(1234, 1, np.array([0.24160281, 0.54102069, 0.97456423]),\n                     3)\ntest_MT19937_weibull(1234, np.array([17.8, 4.32]),\n                     np.array([0.92329994, 0.86744900]))\n\n@test\ndef test_MT19937_zipf(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.MT19937(seed))\n    if size is None:\n        if (isinstance(a, float) or isinstance(a, int)):\n            assert gen.zipf(a) == expected\n        else:\n            assert (gen.zipf(a) == expected).all()\n    else:\n        assert (gen.zipf(a, size) == expected).all()\n\ntest_MT19937_zipf(1234, 1.1, 3)\ntest_MT19937_zipf(1234, 3, 1)\ntest_MT19937_zipf(1234, 45, np.array([1, 1, 1]), 3)\ntest_MT19937_zipf(1234, np.array([17.8, 4.32]), np.array([1, 1]))\n"
  },
  {
    "path": "test/numpy/random_tests/test_pcg64.codon",
    "content": "import numpy.random as rnd\nfrom numpy import *\nimport numpy as np\nimport internal.static as static\n\n@test\ndef test_PCG64_integers(seed, low, expected, high=None, size=None, e=False):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(low, int)\n                or isinstance(low, float)) and (high is None or isinstance(\n                    high, int) or isinstance(high, float)):\n            assert gen.integers(low, high, endpoint=e) == expected\n        else:\n            assert (gen.integers(low, high, endpoint=e) == expected).all()\n    else:\n        assert (gen.integers(low, high, size, endpoint=e) == expected).all()\n\ntest_PCG64_integers(1234, 5, 4)\ntest_PCG64_integers(5678, 987.4, 43)\ntest_PCG64_integers(5678, 98, 137, high=987)\ntest_PCG64_integers(74, 89, 17, e=True)\ntest_PCG64_integers(74, -1218, -958, high=98)\ntest_PCG64_integers(74, -5, np.array([6, 48, 17]), 55, size=3)\ntest_PCG64_integers(74, -5, np.array([7, 49, 17]), 55, size=3, e=True)\ntest_PCG64_integers(5678, np.array([7, 49, 17]), np.array([10, 75, 43]), 78)\ntest_PCG64_integers(5678, -99, np.array([-92, 74, -13]),\n                    np.array([77, 89, 101]))\ntest_PCG64_integers(5678, np.array([7, 49, 17]), np.array([10, 85, 53]),\n                    np.array([77, 89, 101]))\n\n@test\ndef test_PCG64_random(seed, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        assert gen.random() == expected\n    else:\n        assert (round(gen.random(size), 8) == round(expected, 8)).all()\n\ntest_PCG64_random(0, 0.6369616873214543)\ntest_PCG64_random(1, 0.5118216247002567)\ntest_PCG64_random(1234, 0.9766997666981422)\ntest_PCG64_random(\n    1234567,\n    np.array([0.99365208, 0.00776825, 0.23769419, 0.77242113, 0.30128085]),\n    size=5)\ntest_PCG64_random(74,\n                  np.array([[0.89437827, 0.52072095, 0.67640414],\n                            [0.66919894, 0.56483809, 0.5245347]]),\n                  size=(2, 3))\n\n@test\ndef test_PCG64_choice(seed,\n                      a,\n                      expected,\n                      size=None,\n                      r=True,\n                      p=None,\n                      axis=0,\n                      shuffle=True):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if isinstance(a, int):\n            assert gen.choice(a, size, r, p, axis, shuffle) == expected\n        elif static.len(a.shape) == 1:\n            assert gen.choice(a, size, r, p, axis, shuffle) == expected\n        else:\n            assert (round(gen.choice(a, size, r, p, axis, shuffle),\n                          8) == expected).all()\n    else:\n        assert (round(gen.choice(a, size, r, p, axis, shuffle),\n                      8) == expected).all()\n\ntest_PCG64_choice(\n    621,\n    np.array([\n        1.34, 2.15, 3.21, 4.78, 5.55, 6.42, 7.99, 8.31, 9.61, 10.75, 11.33,\n        12.93\n    ]), np.array([4.78, 7.99, 8.31]), 3)\ntest_PCG64_choice(\n    318, np.array([5, 4, 2, 6, 7, 8, 2, 12, 1, 55, 33, 7, 78, 15, 43]), 4)\ntest_PCG64_choice(318, np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),\n                  np.array([11, 2, 12]), 3, False)\ntest_PCG64_choice(\n    4589,\n    np.array([\n        -1.34, -2.15, -3.21, -4.78, -5.55, -6.42, -7.99, -8.31, -9.61, -10.75,\n        -11.33, -12.93\n    ]), -7.99)\ntest_PCG64_choice(318, np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11,\n                                                                   12]]),\n                  np.array([[1, 2, 3], [10, 11, 12]]), 2)\ntest_PCG64_choice(74, np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11,\n                                                                  12]]),\n                  np.array([1, 2, 3]))\ntest_PCG64_choice(74, 1, 0)\ntest_PCG64_choice(74, np.array([13, -4]), -4, p=np.array([0.3, 0.7]))\ntest_PCG64_choice(74,\n                  np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]),\n                  np.array([[1], [4], [7], [10]]),\n                  axis=1)\ntest_PCG64_choice(74,\n                  np.array([13, -4, 3, 18]),\n                  3,\n                  p=np.array([0.5, 0.3, 0.1, 0.1]),\n                  shuffle=True)\n\n@test\ndef test_PCG64_bytes(seed, length, expected):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    check = gen.bytes(length) == expected\n    assert check\n\ntest_PCG64_bytes(555, 10, \"\\xbe^\\x97g\\x10\\xa4;7z\\x95\")\n\n@test\ndef test_PCG64_shuffle(seed, x, expected, axis=0):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    gen.shuffle(x, axis)\n    assert (x == expected).all()\n\ntest_PCG64_shuffle(876, np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),\n                   np.array([6, 3, 5, 9, 7, 8, 1, 2, 4, 0]))\ntest_PCG64_shuffle(111, np.array([5.43, 6.56, 1.22, 4.3221, 7.88, 0.9352]),\n                   np.array([0.9352, 6.56, 5.43, 7.88, 1.22, 4.3221]))\ntest_PCG64_shuffle(1234, np.array([3.14]), np.array([3.14]))\ntest_PCG64_shuffle(74,\n                   np.arange(9).reshape((3, 3)),\n                   np.array([[6, 7, 8], [3, 4, 5], [0, 1, 2]]))\ntest_PCG64_shuffle(74,\n                   np.arange(9).reshape((3, 3)),\n                   np.array([[2, 1, 0], [5, 4, 3], [8, 7, 6]]),\n                   axis=1)\n\n@test\ndef test_PCG64_permutation(seed, x, expected, axis=0):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    assert (gen.permutation(x, axis) == expected).all()\n\ntest_PCG64_permutation(12345, 10, np.array([4, 8, 1, 3, 7, 9, 6, 0, 2, 5]))\ntest_PCG64_permutation(1234,\n                       np.array([1.124, 4.7532, 9.1246, 12.53243, 15.64324]),\n                       np.array([1.124, 15.64324, 4.7532, 12.53243, 9.1246]))\ntest_PCG64_permutation(\n    4321,\n    np.arange(24).reshape(3, 8),\n    np.array([[16, 17, 18, 19, 20, 21, 22, 23], [8, 9, 10, 11, 12, 13, 14, 15],\n              [0, 1, 2, 3, 4, 5, 6, 7]]))\ntest_PCG64_permutation(74,\n                       np.arange(9).reshape((3, 3)),\n                       np.array([[2, 1, 0], [5, 4, 3], [8, 7, 6]]),\n                       axis=1)\n\n@test\ndef test_PCG64_permuted(seed, x, expected, axis=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    arr = gen.permuted(x, axis)\n    assert (arr == expected).all()\n\ntest_PCG64_permuted(4321, np.array([1.124, 4.7532, 9.1246, 12.53243,\n                                    15.64324]),\n                    np.array([1.124, 4.7532, 9.1246, 12.53243, 15.64324]))\ntest_PCG64_permuted(\n    4321,\n    np.arange(24).reshape(3, 8),\n    np.array([[17, 6, 2, 12, 8, 10, 23, 7], [11, 13, 4, 19, 22, 5, 18, 21],\n              [14, 1, 9, 0, 16, 3, 15, 20]]))\n\n@test\ndef test_PCG64_beta(seed, a, b, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(a, int) or isinstance(a, float)) and (isinstance(\n                b, int) or isinstance(b, float)):\n            assert gen.beta(a, b) == expected\n        else:\n            assert (round(gen.beta(a, b), 8) == expected).all()\n    else:\n        assert (round(gen.beta(a, b, size), 8) == expected).all()\n\n#test_PCG64_beta(4321, -7, 0, error)\ntest_PCG64_beta(4321, 0.2, 0.1, 2.2810297427691828e-12)\ntest_PCG64_beta(4321, 10, 14, 0.4269214782557806)\ntest_PCG64_beta(4321, 167, 2041,\n                np.array([0.07569048, 0.08132162, 0.07432054, 0.07364077]), 4)\ntest_PCG64_beta(139, 14.32, 4, 0.7753363939635708)\ntest_PCG64_beta(457, 12.87, 9.21, 0.5055315480345436)\ntest_PCG64_beta(\n    457, np.array([12.87, 1.32, 4.532, 1.34, 8.432]), 9.21,\n    np.array([0.50553155, 0.01362657, 0.34461015, 0.17758410, 0.47101973]))\ntest_PCG64_beta(\n    457, 32.14, np.array([9.21, 0.21, 14.32, 41.21, 5.55]),\n    np.array([0.72323361, 0.99185115, 0.76192197, 0.44454895, 0.86813848]))\ntest_PCG64_beta(\n    457, np.array([12.87, 1.32, 4.532, 1.34, 8.432]),\n    np.array([9.21, 0.21, 14.32, 41.21, 5.55]),\n    np.array([0.50553155, 0.36824135, 0.22202680, 0.05385470, 0.61055151]))\n\n@test\ndef test_PCG64_binomial(seed, n, p, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(n, int) or isinstance(n, float)) and (isinstance(\n                p, int) or isinstance(p, float)):\n            assert gen.binomial(n, p) == expected\n        else:\n            assert (gen.binomial(n, p) == expected).all()\n    else:\n        assert (gen.binomial(n, p, size) == expected).all()\n\n#test_PCG64_binomial(139, -2, .33, 0)\ntest_PCG64_binomial(139, 28400, .66, 18748)\ntest_PCG64_binomial(139, 14.76, .33, 4)\ntest_PCG64_binomial(139, 0, .33, 0)\ntest_PCG64_binomial(139, 14, 0, 0)\ntest_PCG64_binomial(147, 10, .5, np.array([9, 6, 6, 5, 6, 4, 3, 6, 5, 3]), 10)\ntest_PCG64_binomial(457, np.array([12, 1, 4, 7, 8]), 0.11,\n                    np.array([1, 0, 0, 1, 0]))\ntest_PCG64_binomial(457, 12, np.array([0.11, 0.21, 0.32, 0.43]),\n                    np.array([1, 4, 2, 6]))\ntest_PCG64_binomial(457, np.array([12, 1, 4, 7, 8]),\n                    np.array([0.11, 0.21, 0.32, 0.43, 0.55]),\n                    np.array([1, 1, 0, 3, 5]))\n\n@test\ndef test_PCG64_chisquare(seed, df, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(df, float) or isinstance(df, int)):\n            assert gen.chisquare(df) == expected\n        else:\n            assert (round(gen.chisquare(df), 8) == expected).all()\n    else:\n        assert (round(gen.chisquare(df, size), 8) == expected).all()\n\ntest_PCG64_chisquare(457, 12.12, 11.12088340114576)\ntest_PCG64_chisquare(457, 24.12, np.array([22.97619518, 30.58538001]), 2)\ntest_PCG64_chisquare(\n    457, np.array([23, 42, 34, 17]),\n    np.array([21.86780618, 50.59339837, 22.36475303, 15.23606778]))\ntest_PCG64_chisquare(\n    457, np.array([274.23, 325.42, 352.34, 825.17]),\n    np.array([271.92587239, 349.58892133, 312.74784926, 816.55165763]))\n\n@test\ndef test_PCG64_dirichlet(seed, alpha, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    assert (round(gen.dirichlet(alpha, size), 8) == round(expected, 8)).all()\n\ntest_PCG64_dirichlet(\n    457, np.array([278.23, 325.42, 352.34, 825.17]),\n    np.array([0.15699856, 0.19433008, 0.18390472, 0.46476664]))\ntest_PCG64_dirichlet(\n    874, np.array([10, 5, 3]),\n    np.array([[0.70264987, 0.16185327, 0.13549686],\n              [0.35554736, 0.40690167, 0.23755097]]), 2)\n#test_PCG64_dirichlet(1234, np.array([]), np.array([]))\ntest_PCG64_dirichlet(74, np.array([0.01, 0.05]),\n                     np.array([0.86854699, 0.13145301]))\n\n@test\ndef test_PCG64_exponential(seed, scale, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(scale, float) or isinstance(scale, int)):\n            assert gen.exponential(scale) == expected\n        else:\n            assert (round(gen.exponential(scale), 8) == expected).all()\n    else:\n        assert (round(gen.exponential(scale, size), 8) == expected).all()\n\ntest_PCG64_exponential(74, 1, 1.693462457917779)\ntest_PCG64_exponential(874, 3, 0.7277035588087964)\ntest_PCG64_exponential(874, 724.952, 175.85005012185152)\ntest_PCG64_exponential(\n    874, np.array([278.23, 325.42, 352.34, 825.17]),\n    np.array([67.48965372, 681.9977866, 102.20548868, 71.49681695]))\ntest_PCG64_exponential(\n    874, 724.952,\n    np.array([\n        175.85005012, 1519.31552882, 210.29140441, 62.81343292, 679.83990463,\n        351.19474068, 1802.19789426\n    ]), 7)\n\n@test\ndef test_PCG64_f(seed, dfnum, dfden, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(dfnum, float)\n                or isinstance(dfnum, int)) and (isinstance(dfden, float)\n                                                or isinstance(dfden, int)):\n            assert gen.f(dfnum, dfden) == expected\n        else:\n            assert (round(gen.f(dfnum, dfden), 8) == expected).all()\n    else:\n        assert (round(gen.f(dfnum, dfden, size), 8) == expected).all()\n\ntest_PCG64_f(874, 12, 4.21, 3.428479552488479)\ntest_PCG64_f(\n    874, np.array([14, 15, 16, 17, 18]), 4.21,\n    np.array([3.34933794, 4.77314128, 0.96677047, 2.93878710, 0.50349151]))\ntest_PCG64_f(\n    874, 2557, 0.92,\n    np.array([14.97080143, 0.35262364, 1.69187013, 0.88598733, 41.64594068]),\n    5)\ntest_PCG64_f(874, 7343, np.array([12.52, 0.92, 231.52]),\n             np.array([1.53233839, 0.3528156, 0.94834774]))\n\n@test\ndef test_PCG64_gamma(seed, shape, expected, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(shape, float)\n                or isinstance(shape, int)) and (isinstance(scale, float)\n                                                or isinstance(scale, int)):\n            assert gen.gamma(shape, scale) == expected\n        else:\n            assert (round(gen.gamma(shape, scale), 8) == expected).all()\n    else:\n        assert (round(gen.gamma(shape, scale, size), 8) == expected).all()\n\ntest_PCG64_gamma(318, 564, 575.499699177782)\ntest_PCG64_gamma(318, 564, 3165.2483454778007, 5.5)\ntest_PCG64_gamma(318, 564, np.array([3165.24834548, 1172.04010906]),\n                 np.array([5.5, 2.2]))\ntest_PCG64_gamma(\n    318, 564,\n    np.array([2373.03560208, 2196.74145777, 2459.45823200, 2409.61200879]),\n    4.123435, 4)\ntest_PCG64_gamma(\n    318, np.array([19, 17, 45, 52, 21]),\n    np.array(\n        [178.11061929, 100.74725160, 463.44439870, 495.50634389,\n         194.23053272]), 8.527)\ntest_PCG64_gamma(\n    318, np.array([19, 17, 45, 52, 21]),\n    np.array(\n        [22.97662498, 51.98638525, 108.70045707, 441.63811581, 224.82178468]),\n    np.array([1.1, 4.4, 2, 7.6, 9.87]))\n\n@test\ndef test_PCG64_geometric(seed, p, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if isinstance(p, float) or isinstance(p, int):\n            assert gen.geometric(p) == expected\n        else:\n            assert (gen.geometric(p) == expected).all()\n    else:\n        assert (gen.geometric(p, size) == expected).all()\n\ntest_PCG64_geometric(457, 0.35, 2)\ntest_PCG64_geometric(457, 0.2, 8)\ntest_PCG64_geometric(2000, 1, 1)\ntest_PCG64_geometric(12654, 0.5, np.array([2., 1., 2., 1.]), 4)\ntest_PCG64_geometric(12654, np.array([0.01, 0.6, 0.45, 0.33]),\n                     np.array([93, 1, 4, 2]))\n\n@test\ndef test_PCG64_gumbel(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.gumbel(loc, scale) == expected\n        else:\n            assert (round(gen.gumbel(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.gumbel(loc, scale, size), 8) == expected).all()\n\ntest_PCG64_gumbel(1234, -1.3242306166629008)\ntest_PCG64_gumbel(1234, 10.6757693833371, 12)\ntest_PCG64_gumbel(1234, -161.55613523287388, scale=122)\ntest_PCG64_gumbel(1234,\n                  np.array([-1.32423062, 0.73740935, -0.94279743]),\n                  size=3)\ntest_PCG64_gumbel(1234,\n                  np.array(\n                      [-0.52423062, 1.90740935, -0.81279743, 266.64272045]),\n                  loc=np.array([0.8, 1.17, 0.13, 265.45]))\ntest_PCG64_gumbel(1234,\n                  np.array(\n                      [-15.62592128, 37.73323651, -56.69040968, 7.69304687]),\n                  scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_PCG64_hypergeometric(seed, ngood, nbad, nsample, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(ngood, int) or isinstance(ngood, float)) and (\n                isinstance(nbad, int) or isinstance(nbad, float)) and (\n                    isinstance(nsample, int) or isinstance(nsample, float)):\n            assert gen.hypergeometric(ngood, nbad, nsample) == expected\n        else:\n            assert (gen.hypergeometric(ngood, nbad, nsample) == expected).all()\n    else:\n        assert (gen.hypergeometric(ngood, nbad, nsample,\n                                   size) == expected).all()\n\ntest_PCG64_hypergeometric(1234, 11, 12, 9, 4)\ntest_PCG64_hypergeometric(1234, 100, 300, 200, 49)\n#test_PCG64_hypergeometric(1234, 100.31, 300.55, 200.2, 49)\ntest_PCG64_hypergeometric(1234, 100, 300, np.array([200, 201, 222, 221]),\n                          np.array([49, 52, 62, 53]))\ntest_PCG64_hypergeometric(1234, 100, np.array([301, 303, 304, 344, 355]), 200,\n                          np.array([49, 43, 52, 47, 41]))\ntest_PCG64_hypergeometric(1234, np.array([100, 111, 112, 122]), 300, 200,\n                          np.array([49, 52, 48, 60]))\ntest_PCG64_hypergeometric(1234, 100, 700, 450, np.array([57, 63, 58, 54, 54]),\n                          5)\n\n@test\ndef test_PCG64_laplace(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.laplace(loc, scale) == expected\n        else:\n            assert (round(gen.laplace(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.laplace(loc, scale, size), 8) == expected).all()\n\ntest_PCG64_laplace(1234, 3.0661447249453975)\ntest_PCG64_laplace(1234, 15.066144724945397, 12)\ntest_PCG64_laplace(1234, 374.0696564433385, scale=122)\ntest_PCG64_laplace(1234,\n                   np.array([3.06614472, -0.27392189, 1.87400564]),\n                   size=3)\ntest_PCG64_laplace(1234,\n                   np.array([3.86614472, 0.89607811, 2.00400564,\n                             264.80256176]),\n                   loc=np.array([0.8, 1.17, 0.13, 265.45]))\ntest_PCG64_laplace(1234,\n                   np.array(\n                       [36.18050775, -14.01658291, 112.68395923, -4.17597664]),\n                   scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_PCG64_logistic(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.logistic(loc, scale) == expected\n        else:\n            assert (round(gen.logistic(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.logistic(loc, scale, size), 8) == expected).all()\n\ntest_PCG64_logistic(1234, 3.7357159301091363)\ntest_PCG64_logistic(1234, 15.735715930109137, 12)\ntest_PCG64_logistic(1234, 455.7573434733146, scale=122)\ntest_PCG64_logistic(1234,\n                    np.array([3.73571593, -0.48871751, 2.48729352]),\n                    size=3)\ntest_PCG64_logistic(1234,\n                    np.array(\n                        [4.53571593, 0.68128249, 2.61729352, 264.41280935]),\n                    loc=np.array([0.8, 1.17, 0.13, 265.45]))\ntest_PCG64_logistic(\n    1234,\n    np.array([44.08144798, -25.00767521, 149.56095922, -6.68987968]),\n    scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_PCG64_lognormal(seed, expected, mean=0.0, sigma=1.0, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(mean, float) or isinstance(mean, int)) and (isinstance(\n                sigma, float) or isinstance(sigma, int)):\n            assert gen.lognormal(mean, sigma) == expected\n        else:\n            assert (round(gen.lognormal(mean, sigma), 8) == expected).all()\n    else:\n        assert (round(gen.lognormal(mean, sigma, size), 8) == expected).all()\n\ntest_PCG64_lognormal(1234, 0.20112336451311477)\ntest_PCG64_lognormal(1234, 32733.791240820276, 12)\ntest_PCG64_lognormal(1234, 0.04045060775307524, sigma=2)\ntest_PCG64_lognormal(1234,\n                     np.array([0.20112336, 1.06619892, 2.09780445]),\n                     size=3)\ntest_PCG64_lognormal(1234,\n                     np.array(\n                         [0.44760828, 3.43528508, 2.38903925, 271.13563493]),\n                     mean=np.array([0.8, 1.17, 0.13, 5.45]))\ntest_PCG64_lognormal(1234,\n                     np.array(\n                         [0.05574842, 26.57557603, 1.10110681, 2.67618908]),\n                     sigma=np.array([1.8, 51.17, 0.13, 6.45]))\n\n@test\ndef test_PCG64_logseries(seed, p, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if isinstance(p, float) or isinstance(p, int):\n            assert gen.logseries(p) == expected\n        else:\n            assert (gen.logseries(p) == expected).all()\n    else:\n        assert (gen.logseries(p, size) == expected).all()\n\ntest_PCG64_logseries(457, 0.35, 1)\ntest_PCG64_logseries(2000, 0, 1)\ntest_PCG64_logseries(457, 0.5, np.array([2., 3., 2., 1.]), 4)\ntest_PCG64_logseries(457, np.array([0.01, 0.6, 0.45, 0.33]),\n                     np.array([1, 1, 3, 2]))\n\n@test\ndef test_PCG64_multinomial(seed, n, pvals, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    assert (round(gen.multinomial(n, pvals, size), 8) == expected).all()\n\ntest_PCG64_multinomial(457, 20, [1 / 6.], np.array([20]))\ntest_PCG64_multinomial(457, 20.6, [1 / 6.], np.array([20]))\ntest_PCG64_multinomial(457, 20, [1 / 6.] * 6, np.array([3, 5, 1, 4, 2, 5]))\ntest_PCG64_multinomial(\n    457, 20, [1 / 6] * 6,\n    np.array([[3, 5, 1, 4, 2, 5], [5, 4, 3, 6, 0, 2], [4, 2, 2, 3, 6, 3]]), 3)\n#test_PCG64_multinomial(457, 1, np.array([[.1, .5, .4 ], [.3, .7, .0]]), np.array([[0, 0, 1], [0, 1, 0]]))\n#test_PCG64_multinomial(457, -1, [1/6.], 0)\n\n@test\ndef test_PCG64_multivariate_hypergeometric(seed,\n                                           colors,\n                                           nsample,\n                                           expected,\n                                           size=None,\n                                           method='marginals'):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    assert (gen.multivariate_hypergeometric(colors, nsample, size,\n                                            method) == expected).all()\n\ntest_PCG64_multivariate_hypergeometric(457, np.array([16, 8, 4]), 15,\n                                       np.array([8, 5, 2]))\ntest_PCG64_multivariate_hypergeometric(457,\n                                       np.array([16, 8, 4]),\n                                       15,\n                                       np.array([[8, 5, 2], [9, 4, 2],\n                                                 [8, 5, 2]]),\n                                       size=3)\ntest_PCG64_multivariate_hypergeometric(457, np.array([16, 8, 4]), 0,\n                                       np.array([0, 0, 0]))\ntest_PCG64_multivariate_hypergeometric(457,\n                                       np.array([16, 8, 4]),\n                                       8,\n                                       np.array([4, 3, 1]),\n                                       method='count')\n\n@test\ndef test_PCG64_multivariate_normal(seed,\n                                   mean,\n                                   cov,\n                                   expected,\n                                   size=None,\n                                   check_valid: Literal[str] = 'warn',\n                                   tol: float = 1e-8,\n                                   m: Literal[str] = 'svd'):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    assert (round(\n        gen.multivariate_normal(mean,\n                                cov,\n                                size,\n                                check_valid=check_valid,\n                                tol=tol,\n                                method=m), 8) == expected).all()\n\ntest_PCG64_multivariate_normal(1234, (1, 2), np.array([[1, 0], [0, 1]]),\n                               np.array([-0.60383681, 2.06409991]))\ntest_PCG64_multivariate_normal(5432, (1, 2),\n                               np.array([[1, 0], [0, 1]]),\n                               np.array([1.46547576, 1.68314218]),\n                               m='cholesky')\n\n@test\ndef test_PCG64_negative_binomial(seed, n, p, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(n, int) or isinstance(n, float)) and (isinstance(\n                p, int) or isinstance(p, float)):\n            assert gen.negative_binomial(n, p) == expected\n        else:\n            assert (gen.negative_binomial(n, p) == expected).all()\n    else:\n        assert (gen.negative_binomial(n, p, size) == expected).all()\n\ntest_PCG64_negative_binomial(139, 28400, .66, 14407)\ntest_PCG64_negative_binomial(139, 14.76, .33, 17)\n#test_PCG64_negative_binomial(139, 14, 0, error)\ntest_PCG64_negative_binomial(147, 10, .5,\n                             np.array([6, 1, 7, 14, 6, 8, 10, 5, 8, 12]), 10)\ntest_PCG64_negative_binomial(457, np.array([12, 1, 4, 7, 8]), 0.11,\n                             np.array([75, 4, 8, 40, 64]))\ntest_PCG64_negative_binomial(457, 12, np.array([0.11, 0.21, 0.32, 0.43]),\n                             np.array([[75, 30, 19, 28]]))\n\n@test\ndef test_PCG64_noncentral_chisquare(seed, df, nonc, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(df, int) or isinstance(df, float)) and (isinstance(\n                nonc, int) or isinstance(nonc, float)):\n            assert gen.noncentral_chisquare(df, nonc) == expected\n        else:\n            assert (round(gen.noncentral_chisquare(df, nonc),\n                          8) == expected).all()\n    else:\n        assert (round(gen.noncentral_chisquare(df, nonc, size),\n                      8) == expected).all()\n\ntest_PCG64_noncentral_chisquare(457, 0.1, 0.2, 0.061552060427800606)\ntest_PCG64_noncentral_chisquare(457, 5, 0, 4.130095429396157)\ntest_PCG64_noncentral_chisquare(457, 99, 7, 109.29213518232945)\ntest_PCG64_noncentral_chisquare(\n    457, 14.2, 0.5, np.array([14.93361759, 16.34617163, 11.73437051]), 3)\ntest_PCG64_noncentral_chisquare(457, np.array([8, 7.1, 45.3]), 0.6,\n                                np.array([9.06347808, 8.17895956, 41.9335875]))\ntest_PCG64_noncentral_chisquare(\n    457, 0.6, np.array([8, 7.1, 45.3]),\n    np.array([2.07706000, 16.39779665, 54.01862096]))\n\n@test\ndef test_PCG64_noncentral_f(seed, dfnum, dfden, nonc, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(dfnum, float) or isinstance(dfnum, int)) and (\n                isinstance(dfden, int)\n                or isinstance(dfden, float)) and (isinstance(nonc, int)\n                                                  or isinstance(nonc, float)):\n            assert gen.noncentral_f(dfnum, dfden, nonc) == expected\n        else:\n            assert (round(gen.noncentral_f(dfnum, dfden, nonc),\n                          8) == expected).all()\n    else:\n        assert (round(gen.noncentral_f(dfnum, dfden, nonc, size),\n                      8) == expected).all()\n\ntest_PCG64_noncentral_f(874, 3, 20, 3, 0.9479155644253658)\ntest_PCG64_noncentral_f(874, 3, 20, 0, 2.563595388305448)\ntest_PCG64_noncentral_f(\n    874, np.array([14, 15, 16, 17, 18]), 4.21, 6.7,\n    np.array([1.41231957, 1.18112963, 2.53826028, 1.35162988, 4.92394815]))\ntest_PCG64_noncentral_f(\n    874, 27, 5.92, 3.1,\n    np.array([1.16488560, 0.84874875, 1.91535224, 1.22666518, 2.94041668]), 5)\ntest_PCG64_noncentral_f(\n    874, 27, np.array([5.92, 8.13, 9.53, 33.14]), 3.1,\n    np.array([1.16488560, 0.86567470, 1.67032973, 1.38249851]))\ntest_PCG64_noncentral_f(\n    874, 743, 8.24, np.array([0.11, 0.21, 0.32, 0.43]),\n    np.array([0.95109146, 0.79144400, 1.53122749, 0.87951980]))\n\n@test\ndef test_PCG64_normal(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.normal(loc, scale) == expected\n        else:\n            assert (round(gen.normal(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.normal(loc, scale, size), 8) == expected).all()\n\ntest_PCG64_normal(1234, -1.6038368053963015)\ntest_PCG64_normal(1234, 10.396163194603698, 12)\ntest_PCG64_normal(1234, -195.66809025834877, scale=122)\ntest_PCG64_normal(1234,\n                  np.array([-1.60383681, 0.06409991, 0.74089130]),\n                  size=3)\ntest_PCG64_normal(1234,\n                  np.array([-0.80383681, 1.23409991, 0.87089130,\n                            265.60261919]),\n                  loc=np.array([0.8, 1.17, 0.13, 265.45]))\ntest_PCG64_normal(1234,\n                  np.array([-18.92527430, 3.27999260, 44.54979362,\n                            0.98439380]),\n                  scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_PCG64_pareto(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if isinstance(a, float) or isinstance(a, int):\n            assert gen.pareto(a) == expected\n        else:\n            assert (round(gen.pareto(a), 8) == expected).all()\n    else:\n        assert (round(gen.pareto(a, size), 8) == expected).all()\n\ntest_PCG64_pareto(1234, 3, 0.66181450621784)\ntest_PCG64_pareto(\n    1234, 5,\n    np.array([0.35628053, 0.15364923, 0.43703957, 0.17654679, 0.21708714]), 5)\ntest_PCG64_pareto(\n    1234, np.array([3, 13, 4, 23.2, 5.6]),\n    np.array([0.66181451, 0.05651224, 0.57338827, 0.03566071, 0.19173602]))\n\n@test\ndef test_PCG64_poisson(seed, expected, lam=1.0, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if isinstance(lam, float) or isinstance(lam, int):\n            assert gen.poisson(lam) == expected\n        else:\n            assert (round(gen.poisson(lam), 8) == expected).all()\n    else:\n        assert (round(gen.poisson(lam, size), 8) == expected).all()\n\ntest_PCG64_poisson(1234, 0, 0)\ntest_PCG64_poisson(1234, 2)\ntest_PCG64_poisson(1234, 4, 3)\ntest_PCG64_poisson(1234, 21, 14.65)\ntest_PCG64_poisson(1234, np.array([46., 11., 8., 78., 18.]),\n                   np.array([35.62, 9.23, 9.57, 76.79, 21.74]))\ntest_PCG64_poisson(1234, np.array([875., 809., 801., 904., 819.]), 824.14, 5)\n\n@test\ndef test_PCG64_power(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if isinstance(a, float) or isinstance(a, int):\n            assert np.isclose(gen.power(a), expected)\n        else:\n            assert (round(gen.power(a), 8) == expected).all()\n    else:\n        assert (round(gen.power(a, size), 8) == expected).all()\n\ntest_PCG64_power(1234, 1, 0.7821024420483678)\ntest_PCG64_power(1234, 45.11, 0.9945665870769975)\ntest_PCG64_power(1234, 6, np.array([0.95986600, 0.89402992, 0.97074617]), 3)\ntest_PCG64_power(\n    1234, np.array([5.4, 1.1, 78, 19.34, 99999999999999]),\n    np.array(\n        array([0.95550730, 0.54280964, 0.99771874, 0.97014457, 1.00000000])))\n\n@test\ndef test_PCG64_rayleigh(seed, expected, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if isinstance(scale, float) or isinstance(scale, int):\n            assert gen.rayleigh(scale) == expected\n        else:\n            assert (round(gen.rayleigh(scale), 8) == expected).all()\n    else:\n        assert (round(gen.rayleigh(scale, size), 8) == expected).all()\n\n#test_PCG64_rayleigh(1234, error, -1)\ntest_PCG64_rayleigh(1234, 1.7456977082484888)\ntest_PCG64_rayleigh(1234, 5.237093124745466, 3)\ntest_PCG64_rayleigh(1234, 25.57447142584036, 14.65)\ntest_PCG64_rayleigh(1234,\n                    np.array([1.74569771, 1.19553403, 1.90416686, 1.27508312]),\n                    size=4)\ntest_PCG64_rayleigh(\n    1234,\n    np.array([9.42676762, 1.31508743, 148.52501510, 24.66010757, 78.77235752]),\n    np.array([5.4, 1.1, 78, 19.34, 56.2]))\n\n@test\ndef test_PCG64_standard_cauchy(seed, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        assert gen.standard_cauchy() == expected\n    else:\n        assert (round(gen.standard_cauchy(size), 8) == round(expected,\n                                                             8)).all()\n\ntest_PCG64_standard_cauchy(1234, -25.020888566279826)\ntest_PCG64_standard_cauchy(\n    1234, np.array([-25.02088857, 4.85450931, 0.29650342, -1.56410960]), 4)\n\n@test\ndef test_PCG64_standard_exponential(seed, expected, size=None, m='zig'):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        assert gen.standard_exponential(method=m) == expected\n    else:\n        assert (round(gen.standard_exponential(size, method=m),\n                      8) == expected).all()\n\ntest_PCG64_standard_exponential(1234, 1.5237302442920129)\ntest_PCG64_standard_exponential(\n    1234, np.array([1.52373024, 0.71465080, 1.81292572, 0.81291848]), 4)\ntest_PCG64_standard_exponential(1234, 3.75929190550534, m='inv')\n\n@test\ndef test_PCG64_standard_gamma(seed, shape, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if isinstance(shape, float) or isinstance(shape, int):\n            assert gen.standard_gamma(shape) == expected\n        else:\n            assert (round(gen.standard_gamma(shape), 8) == expected).all()\n    else:\n        assert (round(gen.standard_gamma(shape, size), 8) == expected).all()\n\ntest_PCG64_standard_gamma(318, 1, 1.5074447843195276)\ntest_PCG64_standard_gamma(1234, 0, 0)\ntest_PCG64_standard_gamma(1234, 0.5, 2.0649771071013165)\ntest_PCG64_standard_gamma(1234, 3, 0.8114738755097568)\ntest_PCG64_standard_gamma(\n    1234, 0.5, np.array([2.06497711, 0.10182293, 0.05845094, 3.30020422]), 4)\ntest_PCG64_standard_gamma(\n    1234, np.array([4.5, 2, 7.7, 81.15]),\n    np.array([1.67542406, 2.81779395, 9.96848401, 68.23797589]))\n\n@test\ndef test_PCG64_standard_normal(seed, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        assert gen.standard_normal(size) == expected\n    else:\n        assert (round(gen.standard_normal(size), 8) == expected).all()\n\ntest_PCG64_standard_normal(1234, -1.6038368053963015)\ntest_PCG64_standard_normal(1234,\n                           np.array([-1.60383681, 0.06409991, 0.74089130]), 3)\n\n@test\ndef test_PCG64_standard_t(seed, df, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if isinstance(df, float) or isinstance(df, int):\n            assert gen.standard_t(df) == expected\n        else:\n            assert (round(gen.standard_t(df), 8) == expected).all()\n    else:\n        assert (round(gen.standard_t(df, size), 8) == expected).all()\n\ntest_PCG64_standard_t(1234, 3, -1.7659223883243176)\ntest_PCG64_standard_t(1234, 1, -2.982894800105467)\ntest_PCG64_standard_t(1234, 0.5, -5.547734880855075)\ntest_PCG64_standard_t(1234, 0.5,\n                      np.array([-5.54773488, 0.74943429, -7.28743863]), 3)\ntest_PCG64_standard_t(1234, np.array([99, 5.6, 11.43]),\n                      np.array([-1.60193665, 0.12632228, -1.25887551]))\n\n@test\ndef test_PCG64_triangular(seed, left, mode, right, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(left, int) or isinstance(left, float)) and (isinstance(\n                mode, int) or isinstance(mode, float)) and (isinstance(\n                    right, int) or isinstance(right, float)):\n            assert gen.triangular(left, mode, right) == expected\n        else:\n            assert (round(gen.triangular(left, mode, right),\n                          8) == expected).all()\n    else:\n        assert (round(gen.triangular(left, mode, right, size),\n                      8) == expected).all()\n\ntest_PCG64_triangular(1234, -3, 0, 8, 6.56807104556005)\ntest_PCG64_triangular(1234, np.array([-1, -4.2, -3.2]), -0.2, 77,\n                      np.array([65.15497215, 14.66758622, 55.20054270]))\ntest_PCG64_triangular(\n    1234, -4, np.array([0.1, 0.5, 3, 6, 7.9]), 8,\n    np.array([6.51377589, 0.53123947, 5.85402098, 1.60384608, 2.75033776]))\ntest_PCG64_triangular(1234, -77, 0, np.array([0.1, 11, 789]),\n                      np.array([-0.85294815, -26.24365753, 559.99377111]))\ntest_PCG64_triangular(\n    1234,\n    2,\n    10,\n    53,\n    np.array([45.85175465, 16.13225321, 40.02614131, 12.76185250]),\n    size=4)\n\n@test\ndef test_PCG64_uniform(seed, expected, low=0.0, high=1.0, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    result = round(gen.uniform(low, high, size), 8)\n    if size is None:\n        if (isinstance(low, int) or isinstance(low, float)) and (isinstance(\n                high, int) or isinstance(high, float)):\n            assert result == round(expected, 8)\n        else:\n            assert (result == expected).all()\n    else:\n        assert (result == expected).all()\n\ntest_PCG64_uniform(1234, 14.766997666981421, 5, 15)\ntest_PCG64_uniform(1234, 0.9790297900283279, 0.1)\ntest_PCG64_uniform(1234, 119.15737153717335, high=122)\ntest_PCG64_uniform(1234,\n                   np.array([0.97669977, 0.38019574, 0.92324623]),\n                   size=3)\ntest_PCG64_uniform(1234,\n                   np.array(\n                       [43.96080959, 18.96822087, 42.00660312, 16.94431211]),\n                   low=np.array([0.4, 3., 6., 7.]),\n                   high=45.)\ntest_PCG64_uniform(\n    1234,\n    np.array([54.69518694, 6.08313176, 40.62283429, 0.81124651, 0.06381941]),\n    high=np.array([56., 16., 44., 3.1, 0.2]))\n\n@test\ndef test_PCG64_vonmises(seed, mu, kappa, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(mu, float) or isinstance(mu, int)) and (isinstance(\n                kappa, float) or isinstance(kappa, int)):\n            assert np.isclose(gen.vonmises(mu, kappa), expected)\n        else:\n            assert (round(gen.vonmises(mu, kappa), 8) == expected).all()\n    else:\n        assert (round(gen.vonmises(mu, kappa, size), 8) == expected).all()\n\ntest_PCG64_vonmises(1234, 0, 4, -0.27173852171597135)\ntest_PCG64_vonmises(1234, 0.1, 0.2, -3.130535859602788)\ntest_PCG64_vonmises(1234, -1, 0, 2.9951929700537034)\ntest_PCG64_vonmises(1234,\n                    0,\n                    5,\n                    np.array([-0.24351687, 0.19570655, 0.75861058]),\n                    size=3)\ntest_PCG64_vonmises(\n    1234, np.array([43.09, 18.62, 42, 16.94]), 5,\n    np.array([-1.13581402, -0.03384937, -1.22368657, -1.74692310]))\ntest_PCG64_vonmises(\n    1234, 4, np.array([0, 0.1, 7.67, 99]),\n    np.array([2.99519297, 2.89617923, -2.48027841, -2.23901495]))\n\n@test\ndef test_PCG64_wald(seed, mean, scale, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(mean, float) or isinstance(mean, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.wald(mean, scale) == expected\n        else:\n            assert (round(gen.wald(mean, scale), 8) == expected).all()\n    else:\n        assert (round(gen.wald(mean, scale, size), 8) == expected).all()\n\ntest_PCG64_wald(1234, 0.1, 0.1, 0.023030951620822665)\ntest_PCG64_wald(1234, 3, 2, 0.527940495568358)\ntest_PCG64_wald(1234, 3, 2, np.array([0.52794050, 1.24578624, 1.08842697]), 3)\ntest_PCG64_wald(1234, np.array([0.1, 0.5, 1, 4]), 0.5,\n                np.array([0.04952415, 0.24218309, 0.31472446, 0.20571985]))\ntest_PCG64_wald(1234, 0.5, np.array([0.1, 0.5, 1, 4]),\n                np.array([0.03379779, 0.24218309, 0.27395511, 0.29813439]))\ntest_PCG64_wald(1234, np.array([0.1, 0.5, 1, 4]), np.array([3, 2, 1, 8]),\n                np.array([0.07469306, 0.34593420, 0.43217476, 1.46696504]))\n\n@test\ndef test_PCG64_weibull(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(a, float) or isinstance(a, int)):\n            assert gen.weibull(a) == expected\n        else:\n            assert (round(gen.weibull(a), 8) == expected).all()\n    else:\n        assert (round(gen.weibull(a, size), 8) == expected).all()\n\ntest_PCG64_weibull(1234, 0, 0)\ntest_PCG64_weibull(1234, 0.1, 67.46536569583394)\ntest_PCG64_weibull(1234, 1, 1.5237302442920129)\ntest_PCG64_weibull(1234, 1, np.array([1.52373024, 0.71465080, 1.81292572]), 3)\ntest_PCG64_weibull(1234, np.array([17.8, 4.32]),\n                   np.array([1.02394289, 0.92517830]))\n\n@test\ndef test_PCG64_zipf(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.PCG64(seed))\n    if size is None:\n        if (isinstance(a, float) or isinstance(a, int)):\n            assert gen.zipf(a) == expected\n        else:\n            assert (gen.zipf(a) == expected).all()\n    else:\n        assert (gen.zipf(a, size) == expected).all()\n\ntest_PCG64_zipf(1234, 1.1, 21202999555633256)\ntest_PCG64_zipf(1234, 3, 6)\ntest_PCG64_zipf(1234, 45, np.array([1, 1, 1]), 3)\ntest_PCG64_zipf(1234, np.array([17.8, 4.32]), np.array([1, 2]))\n"
  },
  {
    "path": "test/numpy/random_tests/test_philox.codon",
    "content": "import numpy.random as rnd\nfrom numpy import *\nimport numpy as np\nimport internal.static as static\n\n@test\ndef test_Philox_integers(seed, low, expected, high=None, size=None, e=False):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(low, int)\n                or isinstance(low, float)) and (high is None or isinstance(\n                    high, int) or isinstance(high, float)):\n            assert gen.integers(low, high, endpoint=e) == expected\n        else:\n            assert (gen.integers(low, high, endpoint=e) == expected).all()\n    else:\n        assert (gen.integers(low, high, size, endpoint=e) == expected).all()\n\ntest_Philox_integers(74, 5, 0)\ntest_Philox_integers(74, 987.4, 105)\ntest_Philox_integers(74, 98, 193, high=987)\ntest_Philox_integers(74, 89, 9, e=True)\ntest_Philox_integers(74, -1218, -1077, high=98)\ntest_Philox_integers(74, -5, np.array([1, 19, 13]), 55, size=3)\ntest_Philox_integers(74, -5, np.array([1, 19, 14]), 55, size=3, e=True)\ntest_Philox_integers(74, np.array([7, 49, 17]), np.array([14, 60, 36]), 78)\ntest_Philox_integers(74, -99, np.array([-81, -23, -36]),\n                     np.array([77, 89, 101]))\ntest_Philox_integers(74, np.array([7, 49, 17]), np.array([14, 65, 43]),\n                     np.array([77, 89, 101]))\n\n@test\ndef test_Philox_random(seed, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        assert gen.random() == expected\n    else:\n        assert (round(gen.random(size), 8) == round(expected, 8)).all()\n\ntest_Philox_random(0, 0.014067035665647709)\ntest_Philox_random(1, 0.0668020093396563)\ntest_Philox_random(1234, 0.5572569371365311)\ntest_Philox_random(\n    1234,\n    np.array([0.55725694, 0.22373624, 0.29333458, 0.57975576, 0.81181516]),\n    size=5)\ntest_Philox_random(74,\n                   np.array([[0.40856838, 0.47485863, 0.99389396],\n                             [0.15559785, 0.87641347, 0.49119767]]),\n                   size=(2, 3))\n\n@test\ndef test_Philox_choice(seed,\n                       a,\n                       expected,\n                       size=None,\n                       r=True,\n                       p=None,\n                       axis=0,\n                       shuffle=True):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if isinstance(a, int):\n            assert gen.choice(a, size, r, p, axis, shuffle) == expected\n        elif static.len(a.shape) == 1:\n            assert gen.choice(a, size, r, p, axis, shuffle) == expected\n        else:\n            assert (round(gen.choice(a, size, r, p, axis, shuffle),\n                          8) == expected).all()\n    else:\n        assert (round(gen.choice(a, size, r, p, axis, shuffle),\n                      8) == expected).all()\n\ntest_Philox_choice(\n    621,\n    np.array([\n        1.34, 2.15, 3.21, 4.78, 5.55, 6.42, 7.99, 8.31, 9.61, 10.75, 11.33,\n        12.93\n    ]), np.array([7.99, 5.55, 11.33]), 3)\ntest_Philox_choice(\n    318, np.array([5, 4, 2, 6, 7, 8, 2, 12, 1, 55, 33, 7, 78, 15, 43]), 43)\ntest_Philox_choice(318, np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),\n                   np.array([6, 10, 12]), 3, False)\ntest_Philox_choice(\n    4589,\n    np.array([\n        -1.34, -2.15, -3.21, -4.78, -5.55, -6.42, -7.99, -8.31, -9.61, -10.75,\n        -11.33, -12.93\n    ]), -8.31)\ntest_Philox_choice(318,\n                   np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]),\n                   np.array([[10, 11, 12], [7, 8, 9]]), 2)\ntest_Philox_choice(74, np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11,\n                                                                   12]]),\n                   np.array([1, 2, 3]))\ntest_Philox_choice(74, 1, 0)\ntest_Philox_choice(74, np.array([13, -4]), -4, p=np.array([0.3, 0.7]))\ntest_Philox_choice(74,\n                   np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]),\n                   np.array([[1], [4], [7], [10]]),\n                   axis=1)\ntest_Philox_choice(74,\n                   np.array([13, -4, 3, 18]),\n                   13,\n                   p=np.array([0.5, 0.3, 0.1, 0.1]),\n                   shuffle=True)\n\n@test\ndef test_Philox_bytes(seed, length, expected):\n    gen = rnd.Generator(rnd.Philox(seed))\n    check = gen.bytes(length) == expected\n    assert check\n\ntest_Philox_bytes(555, 10, \"\\x86\\xb5\\xa3\\xf8\\xa9d\\xdb\\xa0,\\xd2\")\n\n@test\ndef test_Philox_shuffle(seed, x, expected, axis=0):\n    gen = rnd.Generator(rnd.Philox(seed))\n    gen.shuffle(x, axis)\n    assert (x == expected).all()\n\ntest_Philox_shuffle(876, np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),\n                    np.array([2, 8, 9, 0, 3, 4, 7, 1, 6, 5]))\ntest_Philox_shuffle(111, np.array([5.43, 6.56, 1.22, 4.3221, 7.88, 0.9352]),\n                    np.array([7.88, 1.22, 6.56, 0.9352, 4.3221, 5.43]))\ntest_Philox_shuffle(1234, np.array([3.14]), np.array([3.14]))\ntest_Philox_shuffle(74,\n                    np.arange(9).reshape((3, 3)),\n                    np.array([[3, 4, 5], [6, 7, 8], [0, 1, 2]]))\ntest_Philox_shuffle(74,\n                    np.arange(9).reshape((3, 3)),\n                    np.array([[1, 2, 0], [4, 5, 3], [7, 8, 6]]),\n                    axis=1)\n\n@test\ndef test_Philox_permutation(seed, x, expected, axis=0):\n    gen = rnd.Generator(rnd.Philox(seed))\n    assert (gen.permutation(x, axis) == expected).all()\n\ntest_Philox_permutation(12345, 10, np.array([8, 9, 6, 4, 3, 2, 7, 1, 5, 0]))\ntest_Philox_permutation(1234,\n                        np.array([1.124, 4.7532, 9.1246, 12.53243, 15.64324]),\n                        np.array([12.53243, 15.64324, 9.1246, 1.124, 4.7532]))\ntest_Philox_permutation(\n    4321,\n    np.arange(24).reshape(3, 8),\n    np.array([[8, 9, 10, 11, 12, 13, 14, 15], [16, 17, 18, 19, 20, 21, 22, 23],\n              [0, 1, 2, 3, 4, 5, 6, 7]]))\ntest_Philox_permutation(74,\n                        np.arange(9).reshape((3, 3)),\n                        np.array([[1, 2, 0], [4, 5, 3], [7, 8, 6]]),\n                        axis=1)\n\n@test\ndef test_Philox_permuted(seed, x, expected, axis=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    arr = gen.permuted(x, axis)\n    assert (arr == expected).all()\n\ntest_Philox_permuted(4321,\n                     np.array([1.124, 4.7532, 9.1246, 12.53243, 15.64324]),\n                     np.array([15.64324, 4.7532, 9.1246, 1.124, 12.53243]))\ntest_Philox_permuted(\n    4321,\n    np.arange(24).reshape(3, 8),\n    np.array([[14, 21, 10, 17, 16, 12, 19, 5], [13, 20, 6, 0, 7, 15, 4, 9],\n              [18, 22, 1, 23, 3, 2, 8, 11]]))\n\n@test\ndef test_Philox_beta(seed, a, b, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(a, int) or isinstance(a, float)) and (isinstance(\n                b, int) or isinstance(b, float)):\n            assert gen.beta(a, b) == expected\n        else:\n            assert (round(gen.beta(a, b), 8) == expected).all()\n    else:\n        assert (round(gen.beta(a, b, size), 8) == expected).all()\n\n#test_Philox_beta(4321, -7, 0, error)\ntest_Philox_beta(4321, 0.2, 0.1, 0.0001674971842261576)\ntest_Philox_beta(4321, 10, 14, 0.3991628907366655)\ntest_Philox_beta(4321, 167, 2041,\n                 np.array([0.07344472, 0.08315303, 0.08115929, 0.07195913]), 4)\ntest_Philox_beta(139, 14.32, 4, 0.828761259380857)\ntest_Philox_beta(457, 12.87, 9.21, 0.53629054859836)\ntest_Philox_beta(\n    457, np.array([12.87, 1.32, 4.532, 1.34, 8.432]), 9.21,\n    np.array([0.53629055, 0.31970199, 0.61013405, 0.06910402, 0.58929108]))\ntest_Philox_beta(\n    457, 32.14, np.array([9.21, 0.21, 14.32, 41.21, 5.55]),\n    np.array([0.78367830, 0.99976162, 0.83007856, 0.44624419, 0.89121140]))\ntest_Philox_beta(\n    457, np.array([12.87, 1.32, 4.532, 1.34, 8.432]),\n    np.array([9.21, 0.21, 14.32, 41.21, 5.55]),\n    np.array([0.53629055, 0.99558555, 0.45404014, 0.01310910, 0.71423699]))\n\n@test\ndef test_Philox_binomial(seed, n, p, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(n, int) or isinstance(n, float)) and (isinstance(\n                p, int) or isinstance(p, float)):\n            assert gen.binomial(n, p) == expected\n        else:\n            assert (gen.binomial(n, p) == expected).all()\n    else:\n        assert (gen.binomial(n, p, size) == expected).all()\n\n#test_Philox_binomial(139, -2, .33, error)\ntest_Philox_binomial(139, 28400, .66, 18735)\ntest_Philox_binomial(139, 14.76, .33, 8)\ntest_Philox_binomial(139, 0, .33, 0)\ntest_Philox_binomial(139, 14, 0, 0)\ntest_Philox_binomial(147, 10, .5, np.array([5, 4, 5, 4, 3, 7, 7, 4, 2, 4]), 10)\ntest_Philox_binomial(457, np.array([12, 1, 4, 7, 8]), 0.11,\n                     np.array([2, 0, 0, 0, 1]))\ntest_Philox_binomial(457, 12, np.array([0.11, 0.21, 0.32, 0.43]),\n                     np.array([2, 1, 3, 4]))\ntest_Philox_binomial(457, np.array([12, 1, 4, 7, 8]),\n                     np.array([0.11, 0.21, 0.32, 0.43, 0.55]),\n                     np.array([2, 0, 1, 2, 4]))\n\n@test\ndef test_Philox_chisquare(seed, df, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(df, float) or isinstance(df, int)):\n            assert gen.chisquare(df) == expected\n        else:\n            assert (round(gen.chisquare(df), 8) == expected).all()\n    else:\n        assert (round(gen.chisquare(df, size), 8) == expected).all()\n\ntest_Philox_chisquare(457, 12.12, 4.987326814746572)\ntest_Philox_chisquare(457, 24.12, np.array([13.45189670, 17.54542500]), 2)\ntest_Philox_chisquare(\n    457, np.array([23, 42, 34, 17]),\n    np.array([12.61672185, 33.30496035, 39.938353210, 7.36364561]))\ntest_Philox_chisquare(\n    457, np.array([274.23, 325.42, 352.34, 825.17]),\n    np.array([234.89812766, 301.20489918, 372.23635244, 745.94706704]))\n\n@test\ndef test_Philox_dirichlet(seed, alpha, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    assert (round(gen.dirichlet(alpha, size), 8) == round(expected, 8)).all()\n\ntest_Philox_dirichlet(\n    457, np.array([278.23, 325.42, 352.34, 825.17]),\n    np.array([0.14758409, 0.18205838, 0.21640768, 0.45394985]))\ntest_Philox_dirichlet(\n    874, np.array([10, 5, 3]),\n    np.array([[0.63748511, 0.22927703, 0.13323785],\n              [0.38843058, 0.41643801, 0.19513141]]), 2)\n#test_Philox_dirichlet(1234, np.array([]), np.array([]))\ntest_Philox_dirichlet(74, np.array([0.01, 0.05]),\n                      np.array([3.93720652e-33, 1.00000000]))\n\n@test\ndef test_Philox_exponential(seed, scale, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(scale, float) or isinstance(scale, int)):\n            assert gen.exponential(scale) == expected\n        else:\n            assert (round(gen.exponential(scale), 8) == expected).all()\n    else:\n        assert (round(gen.exponential(scale, size), 8) == expected).all()\n\ntest_Philox_exponential(74, 1, 0.677350334467786)\ntest_Philox_exponential(874, 3, 2.0459069098380795)\ntest_Philox_exponential(874, 724.952, 494.39476870031183)\ntest_Philox_exponential(\n    874, np.array([278.23, 325.42, 352.34, 825.17]),\n    np.array([189.74422651, 10.21851605, 301.34973289, 110.59639221]))\ntest_Philox_exponential(\n    874, 724.952,\n    np.array([\n        494.39476870, 22.76422361, 620.03772368, 97.16431247, 1148.46168532,\n        1852.86020050, 2203.97929230\n    ]), 7)\n\n@test\ndef test_Philox_f(seed, dfnum, dfden, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(dfnum, float)\n                or isinstance(dfnum, int)) and (isinstance(dfden, float)\n                                                or isinstance(dfden, int)):\n            assert gen.f(dfnum, dfden) == expected\n        else:\n            assert (round(gen.f(dfnum, dfden), 8) == expected).all()\n    else:\n        assert (round(gen.f(dfnum, dfden, size), 8) == expected).all()\n\ntest_Philox_f(874, 12, 4.21, 1.5606440016521452)\ntest_Philox_f(\n    874, np.array([14, 15, 16, 17, 18]), 4.21,\n    np.array([1.51159613, 2.12468474, 1.13710348, 2.75465007, 2.48772155]))\ntest_Philox_f(\n    874, 2557, 0.92,\n    np.array([0.640902440, 1.79388925, 18.24367928, 20.97090336, 14.06117768]),\n    5)\ntest_Philox_f(874, 7343, np.array([12.52, 0.92, 231.52]),\n              np.array([0.95066689, 1.78861180, 0.96449548]))\n\n@test\ndef test_Philox_gamma(seed, shape, expected, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(shape, float)\n                or isinstance(shape, int)) and (isinstance(scale, float)\n                                                or isinstance(scale, int)):\n            assert gen.gamma(shape, scale) == expected\n        else:\n            assert (round(gen.gamma(shape, scale), 8) == expected).all()\n    else:\n        assert (round(gen.gamma(shape, scale, size), 8) == expected).all()\n\ntest_Philox_gamma(318, 564, 569.4970306954932)\ntest_Philox_gamma(318, 564, 3132.2336688252126, 5.5)\ntest_Philox_gamma(318, 564, np.array([3132.23366883, 1304.85485375]),\n                  np.array([5.5, 2.2]))\ntest_Philox_gamma(\n    318, 564,\n    np.array([2348.28398877, 2445.67462449, 2219.17838471, 2465.85102355]),\n    4.123435, 4)\ntest_Philox_gamma(\n    318, np.array([19, 17, 45, 52, 21]),\n    np.array(\n        [168.35816137, 188.93243525, 322.08095866, 533.31906573,\n         175.15726538]), 8.527)\ntest_Philox_gamma(\n    318, np.array([19, 17, 45, 52, 21]),\n    np.array(\n        [21.71853847, 97.49064326, 75.54379234, 475.34008438, 202.74448332]),\n    np.array([1.1, 4.4, 2, 7.6, 9.87]))\n\n@test\ndef test_Philox_geometric(seed, p, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if isinstance(p, float) or isinstance(p, int):\n            assert gen.geometric(p) == expected\n        else:\n            assert (gen.geometric(p) == expected).all()\n    else:\n        assert (gen.geometric(p, size) == expected).all()\n\ntest_Philox_geometric(457, 0.35, 3)\ntest_Philox_geometric(457, 0.2, 6)\ntest_Philox_geometric(2000, 1, 1)\ntest_Philox_geometric(12654, 0.5, np.array([1., 1., 1., 1.]), 4)\ntest_Philox_geometric(12654, np.array([0.01, 0.6, 0.45, 0.33]),\n                      np.array([102, 1, 2, 2]))\n\n@test\ndef test_Philox_gumbel(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.gumbel(loc, scale) == expected\n        else:\n            assert (round(gen.gumbel(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.gumbel(loc, scale, size), 8) == expected).all()\n\ntest_Philox_gumbel(1234, 0.20485472765439966)\ntest_Philox_gumbel(1234, 12.2048547276544, 12)\ntest_Philox_gumbel(1234, 24.992276773836757, scale=122)\ntest_Philox_gumbel(1234,\n                   np.array([0.20485473, 1.37332713, 1.05786017]),\n                   size=3)\ntest_Philox_gumbel(1234,\n                   np.array([1.00485473, 2.54332713, 1.18786017,\n                             265.59280948]),\n                   loc=np.array([0.8, 1.17, 0.13, 265.45]))\ntest_Philox_gumbel(1234,\n                   np.array([2.41728579, 70.27314942, 63.60913187,\n                             0.92112117]),\n                   scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_Philox_hypergeometric(seed,\n                               ngood,\n                               nbad,\n                               nsample,\n                               expected,\n                               size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(ngood, int) or isinstance(ngood, float)) and (\n                isinstance(nbad, int) or isinstance(nbad, float)) and (\n                    isinstance(nsample, int) or isinstance(nsample, float)):\n            assert gen.hypergeometric(ngood, nbad, nsample) == expected\n        else:\n            assert (gen.hypergeometric(ngood, nbad, nsample) == expected).all()\n    else:\n        assert (gen.hypergeometric(ngood, nbad, nsample,\n                                   size) == expected).all()\n\ntest_Philox_hypergeometric(1234, 11, 12, 9, 4)\ntest_Philox_hypergeometric(1234, 100, 300, 200, 46)\n#test_Philox_hypergeometric(1234, 100.31, 300.55, 200.2, 46)\ntest_Philox_hypergeometric(1234, 100, 300, np.array([200, 201, 222, 221]),\n                           np.array([46, 48, 54, 46]))\ntest_Philox_hypergeometric(1234, 100, np.array([301, 303, 304, 344, 355]), 200,\n                           np.array([46, 52, 51, 55, 47]))\ntest_Philox_hypergeometric(1234, np.array([100, 111, 112, 122]), 300, 200,\n                           np.array([46, 56, 56, 68]))\ntest_Philox_hypergeometric(1234, 100, 700, 450, np.array([61, 54, 54, 46, 64]),\n                           5)\n\n@test\ndef test_Philox_laplace(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.laplace(loc, scale) == expected\n        else:\n            assert (round(gen.laplace(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.laplace(loc, scale, size), 8) == expected).all()\n\ntest_Philox_laplace(1234, 0.12161849017455835)\ntest_Philox_laplace(1234, 12.121618490174559, 12)\ntest_Philox_laplace(1234, 14.83745580129612, scale=122)\ntest_Philox_laplace(1234,\n                    np.array([0.12161849, -0.80414025, -0.53329424]),\n                    size=3)\ntest_Philox_laplace(1234,\n                    np.array(\n                        [0.92161849, 0.36585975, -0.40329424, 265.62377203]),\n                    loc=np.array([0.8, 1.17, 0.13, 265.45]))\ntest_Philox_laplace(1234,\n                    np.array(\n                        [1.43509818, -41.14785663, -32.06698241, 1.12082962]),\n                    scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_Philox_logistic(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.logistic(loc, scale) == expected\n        else:\n            assert (round(gen.logistic(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.logistic(loc, scale, size), 8) == expected).all()\n\ntest_Philox_logistic(1234, 0.23003681281754018)\ntest_Philox_logistic(1234, 12.23003681281754, 12)\ntest_Philox_logistic(1234, 28.064491163739902, scale=122)\ntest_Philox_logistic(1234,\n                     np.array([0.23003681, -1.24402451, -0.87924346]),\n                     size=3)\ntest_Philox_logistic(1234,\n                     np.array(\n                         [1.03003681, -0.07402451, -0.74924346, 25.77177085]),\n                     loc=np.array([0.8, 1.17, 0.13, 25.45]))\ntest_Philox_logistic(1234,\n                     np.array(\n                         [2.71443439, -63.65673441, -52.86890896, 2.07542198]),\n                     scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_Philox_lognormal(seed, expected, mean=0.0, sigma=1.0, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(mean, float) or isinstance(mean, int)) and (isinstance(\n                sigma, float) or isinstance(sigma, int)):\n            assert gen.lognormal(mean, sigma) == expected\n        else:\n            assert (round(gen.lognormal(mean, sigma), 8) == expected).all()\n    else:\n        assert (round(gen.lognormal(mean, sigma, size), 8) == expected).all()\n\ntest_Philox_lognormal(1234, 0.46906380361126687)\ntest_Philox_lognormal(1234, 76342.3815189564, 12)\ntest_Philox_lognormal(1234, 0.22002085185826914, sigma=2)\ntest_Philox_lognormal(1234,\n                      np.array([0.4690638, 5.02772598, 1.96860722]),\n                      size=3)\ntest_Philox_lognormal(1234,\n                      np.array(\n                          [1.04392069, 16.19929610, 2.24190578, 668.12967098]),\n                      mean=np.array([0.8, 1.17, 0.13, 5.45]))\ntest_Philox_lognormal(1234,\n                      np.array(\n                          [0.25598673, 33.26414553, 1.69607119, 4.61355770]),\n                      sigma=np.array([1.8, 2.17, 0.78, 1.45]))\n\n@test\ndef test_Philox_logseries(seed, p, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if isinstance(p, float) or isinstance(p, int):\n            assert gen.logseries(p) == expected\n        else:\n            assert (gen.logseries(p) == expected).all()\n    else:\n        assert (gen.logseries(p, size) == expected).all()\n\ntest_Philox_logseries(457, 0.35, 1)\ntest_Philox_logseries(2000, 0, 1)\ntest_Philox_logseries(457, 0.5, np.array([1., 2., 2., 2.]), 4)\ntest_Philox_logseries(457, np.array([0.01, 0.6, 0.45, 0.33]),\n                      np.array([1, 2, 1, 1]))\n\n@test\ndef test_Philox_multinomial(seed, n, pvals, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    assert (round(gen.multinomial(n, pvals, size), 8) == expected).all()\n\ntest_Philox_multinomial(457, 20, [1 / 6.], np.array([20]))\ntest_Philox_multinomial(457, 20.6, [1 / 6.], np.array([20]))\ntest_Philox_multinomial(457, 20, [1 / 6.] * 6, np.array([4, 1, 3, 3, 5, 4]))\ntest_Philox_multinomial(\n    457, 20, [1 / 6] * 6,\n    np.array([[4, 1, 3, 3, 5, 4], [2, 3, 2, 8, 2, 3], [2, 5, 1, 5, 2, 5]]), 3)\n#test_Philox_multinomial(457, 1, np.array([[.1, .5, .4 ], [.3, .7, .0]]), np.array([[0, 1, 0], [0, 1, 0]]))\n#test_Philox_multinomial(457, -1, [1/6.], error)\n\n@test\ndef test_Philox_multivariate_hypergeometric(seed,\n                                            colors,\n                                            nsample,\n                                            expected,\n                                            size=None,\n                                            method='marginals'):\n    gen = rnd.Generator(rnd.Philox(seed))\n    assert (gen.multivariate_hypergeometric(colors, nsample, size,\n                                            method) == expected).all()\n\ntest_Philox_multivariate_hypergeometric(457, np.array([16, 8, 4]), 15,\n                                        np.array([7, 4, 4]))\ntest_Philox_multivariate_hypergeometric(457,\n                                        np.array([16, 8, 4]),\n                                        15,\n                                        np.array([[7, 4, 4], [6, 7, 2],\n                                                  [6, 6, 3]]),\n                                        size=3)\ntest_Philox_multivariate_hypergeometric(457, np.array([16, 8, 4]), 0,\n                                        np.array([0, 0, 0]))\ntest_Philox_multivariate_hypergeometric(457,\n                                        np.array([16, 8, 4]),\n                                        8,\n                                        np.array([6, 1, 1]),\n                                        method='count')\n\n@test\ndef test_Philox_multivariate_normal(seed,\n                                    mean,\n                                    cov,\n                                    expected,\n                                    size=None,\n                                    check_valid: Literal[str] = 'warn',\n                                    tol: float = 1e-8,\n                                    m: Literal[str] = 'svd'):\n    gen = rnd.Generator(rnd.Philox(seed))\n    assert (round(\n        gen.multivariate_normal(mean,\n                                cov,\n                                size,\n                                check_valid=check_valid,\n                                tol=tol,\n                                method=m), 8) == expected).all()\n\ntest_Philox_multivariate_normal(1234, (1, 2), np.array([[1, 0], [0, 1]]),\n                                np.array([0.24298352, 3.61496779]))\ntest_Philox_multivariate_normal(5432, (1, 2),\n                                np.array([[1, 0], [0, 1]]),\n                                np.array([1.31546928, 1.83957609]),\n                                m='cholesky')\n\n@test\ndef test_Philox_negative_binomial(seed, n, p, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(n, int) or isinstance(n, float)) and (isinstance(\n                p, int) or isinstance(p, float)):\n            assert gen.negative_binomial(n, p) == expected\n        else:\n            assert (gen.negative_binomial(n, p) == expected).all()\n    else:\n        assert (gen.negative_binomial(n, p, size) == expected).all()\n\ntest_Philox_negative_binomial(139, 28400, .66, 14627)\ntest_Philox_negative_binomial(139, 14.76, .33, 27)\n#test_Philox_negative_binomial(139, 14, 0, error)\ntest_Philox_negative_binomial(147, 10, .5, np.array([6, 10, 21]), 3)\ntest_Philox_negative_binomial(457, np.array([12, 1, 4, 7, 8]), 0.11,\n                              np.array([51, 10, 23, 42, 65]))\ntest_Philox_negative_binomial(457, 12, np.array([0.11, 0.21, 0.32, 0.43]),\n                              np.array([[51, 51, 27, 9]]))\n\n@test\ndef test_Philox_noncentral_chisquare(seed, df, nonc, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(df, int) or isinstance(df, float)) and (isinstance(\n                nonc, int) or isinstance(nonc, float)):\n            assert gen.noncentral_chisquare(df, nonc) == expected\n        else:\n            assert (round(gen.noncentral_chisquare(df, nonc),\n                          8) == expected).all()\n    else:\n        assert (round(gen.noncentral_chisquare(df, nonc, size),\n                      8) == expected).all()\n\ntest_Philox_noncentral_chisquare(457, 0.1, 0.2, 1.3412155895288272e-19)\ntest_Philox_noncentral_chisquare(457, 5, 0, 0.9667491310888645)\ntest_Philox_noncentral_chisquare(457, 99, 7, 77.93117603431178)\ntest_Philox_noncentral_chisquare(\n    457, 14.2, 0.5, np.array([5.74888694, 17.17099047, 7.53364203]), 3)\ntest_Philox_noncentral_chisquare(\n    457, np.array([8, 7.1, 45.3]), 0.6,\n    np.array([1.97336093, 9.18958927, 30.29737961]))\ntest_Philox_noncentral_chisquare(\n    457, 0.6, np.array([8, 7.1, 45.3]),\n    np.array([8.95858437, 1.28791705, 46.52231840]))\n\n@test\ndef test_Philox_noncentral_f(seed, dfnum, dfden, nonc, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(dfnum, float) or isinstance(dfnum, int)) and (\n                isinstance(dfden, int)\n                or isinstance(dfden, float)) and (isinstance(nonc, int)\n                                                  or isinstance(nonc, float)):\n            assert gen.noncentral_f(dfnum, dfden, nonc) == expected\n        else:\n            assert (round(gen.noncentral_f(dfnum, dfden, nonc),\n                          8) == expected).all()\n    else:\n        assert (round(gen.noncentral_f(dfnum, dfden, nonc, size),\n                      8) == expected).all()\n\ntest_Philox_noncentral_f(874, 3, 20, 3, 2.792477829917526)\ntest_Philox_noncentral_f(874, 3, 20, 0, 2.2362415189878746)\ntest_Philox_noncentral_f(\n    874, np.array([14, 15, 16, 17, 18]), 4.21, 6.7,\n    np.array([1.04484935, 1.21683455, 1.13983240, 3.66570463, 0.57133496]))\ntest_Philox_noncentral_f(\n    874, 27, 5.92, 3.1,\n    np.array([0.82984208, 1.01215833, 0.94357663, 2.19740092, 0.48883215]), 5)\ntest_Philox_noncentral_f(\n    874, 27, np.array([5.92, 8.13, 9.53, 33.14]), 3.1,\n    np.array([0.82984208, 1.09159907, 0.97830503, 1.43588432]))\ntest_Philox_noncentral_f(\n    874, 743, 8.24, np.array([0.11, 0.21, 0.32, 0.43]),\n    np.array([0.62498462, 0.65443537, 0.81711437, 1.64557267]))\n\n@test\ndef test_Philox_normal(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.normal(loc, scale) == expected\n        else:\n            assert (round(gen.normal(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.normal(loc, scale, size), 8) == expected).all()\n\ntest_Philox_normal(1234, -0.7570164779736382)\ntest_Philox_normal(1234, 11.242983522026362, 12)\ntest_Philox_normal(1234, -92.35601031278387, scale=122)\ntest_Philox_normal(1234,\n                   np.array([-0.75701648, 1.61496779, 0.67732630]),\n                   size=3)\ntest_Philox_normal(1234,\n                   np.array([0.04298352, 2.78496779, 0.80732630, 26.50448227]),\n                   loc=np.array([0.8, 1.17, 0.13, 25.45]))\ntest_Philox_normal(1234,\n                   np.array(\n                       [-8.93279444, 82.63790185, 40.72763043, 6.80141066]),\n                   scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_Philox_pareto(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if isinstance(a, float) or isinstance(a, int):\n            assert gen.pareto(a) == expected\n        else:\n            assert (round(gen.pareto(a), 8) == expected).all()\n    else:\n        assert (round(gen.pareto(a, size), 8) == expected).all()\n\ntest_Philox_pareto(1234, 3, 0.5653481922908028)\ntest_Philox_pareto(\n    1234, 5,\n    np.array([0.30847845, 0.09324970, 0.02876564, 0.04935884, 0.94079746]), 5)\ntest_Philox_pareto(\n    1234, np.array([3, 13, 4, 23.2, 5.6]),\n    np.array([0.56534819, 0.03488493, 0.03608542, 0.01043758, 0.80769515]))\n\n@test\ndef test_Philox_poisson(seed, expected, lam=1.0, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if isinstance(lam, float) or isinstance(lam, int):\n            assert gen.poisson(lam) == expected\n        else:\n            assert (round(gen.poisson(lam), 8) == expected).all()\n    else:\n        assert (round(gen.poisson(lam, size), 8) == expected).all()\n\ntest_Philox_poisson(1234, 0, 0)\ntest_Philox_poisson(1234, 1)\ntest_Philox_poisson(1234, 2, 3)\ntest_Philox_poisson(1234, 15, 14.65)\ntest_Philox_poisson(1234, np.array([37., 9., 11., 67., 27.]),\n                    np.array([35.62, 9.23, 9.57, 76.79, 21.74]))\ntest_Philox_poisson(1234, np.array([829., 806., 853., 800., 821.]), 824.14, 5)\n\n@test\ndef test_Philox_power(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if isinstance(a, float) or isinstance(a, int):\n            assert gen.power(a) == expected\n        else:\n            assert (round(gen.power(a), 8) == expected).all()\n    else:\n        assert (round(gen.power(a, size), 8) == expected).all()\n\ntest_Philox_power(1234, 1, 0.7392843317079527)\ntest_Philox_power(1234, 45.11, 0.9933260132890823)\ntest_Philox_power(1234, 6, np.array([0.95090088, 0.84330415, 0.71374101]), 3)\ntest_Philox_power(\n    1234, np.array([5.4, 1.1, 78, 19.34, 99999999999999]),\n    np.array(\n        array([0.94559645, 0.39470958, 0.97439242, 0.92339234, 1.00000000])))\n\n@test\ndef test_Philox_rayleigh(seed, expected, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if isinstance(scale, float) or isinstance(scale, int):\n            assert gen.rayleigh(scale) == expected\n        else:\n            assert (round(gen.rayleigh(scale), 8) == expected).all()\n    else:\n        assert (round(gen.rayleigh(scale, size), 8) == expected).all()\n\n#test_Philox_rayleigh(1234, error, -1)\ntest_Philox_rayleigh(1234, 1.6397102542808777)\ntest_Philox_rayleigh(1234, 4.919130762842633, 3)\ntest_Philox_rayleigh(1234, 24.021755225214857, 14.65)\ntest_Philox_rayleigh(1234,\n                     np.array([1.63971025, 0.94421734, 0.53253802,\n                               0.69411344]),\n                     size=4)\ntest_Philox_rayleigh(\n    1234,\n    np.array([8.85443537, 1.03863908, 41.53796574, 13.42415392, 144.71897760]),\n    np.array([5.4, 1.1, 78, 19.34, 56.2]))\n\n@test\ndef test_Philox_standard_cauchy(seed, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        assert gen.standard_cauchy() == expected\n    else:\n        assert (round(gen.standard_cauchy(size), 8) == round(expected,\n                                                             8)).all()\n\ntest_Philox_standard_cauchy(1234, -0.46875020188678784)\ntest_Philox_standard_cauchy(\n    1234, np.array([-0.46875020, 0.64233067, -0.70952735, -18.92416824]), 4)\n\n@test\ndef test_Philox_standard_exponential(seed, expected, size=None, m='zig'):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        assert gen.standard_exponential(method=m) == expected\n    else:\n        assert (round(gen.standard_exponential(size, method=m),\n                      8) == expected).all()\n\ntest_Philox_standard_exponential(1234, 1.34432485899693)\ntest_Philox_standard_exponential(\n    1234, np.array([1.34432486, 0.44577319, 0.14179837, 0.24089673]), 4)\ntest_Philox_standard_exponential(1234, 0.8147656707345036, m='inv')\n\n@test\ndef test_Philox_standard_gamma(seed, shape, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if isinstance(shape, float) or isinstance(shape, int):\n            assert gen.standard_gamma(shape) == expected\n        else:\n            assert (round(gen.standard_gamma(shape), 8) == expected).all()\n    else:\n        assert (round(gen.standard_gamma(shape, size), 8) == expected).all()\n\ntest_Philox_standard_gamma(318, 1, 0.15235906502561086)\ntest_Philox_standard_gamma(1234, 0, 0)\ntest_Philox_standard_gamma(1234, 0.5, 0.3145070093753639)\ntest_Philox_standard_gamma(1234, 3, 1.6116492009757368)\ntest_Philox_standard_gamma(\n    1234, 0.5, np.array([0.31450701, 0.08604517, 0.97731355, 0.05278714]), 4)\ntest_Philox_standard_gamma(\n    1234, np.array([4.5, 2, 7.7, 81.15]),\n    np.array([2.80456639, 2.70292948, 5.81000297, 62.55816392]))\n\n@test\ndef test_Philox_standard_normal(seed, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        assert gen.standard_normal(size) == expected\n    else:\n        assert (round(gen.standard_normal(size), 8) == expected).all()\n\ntest_Philox_standard_normal(1234, -0.7570164779736382)\ntest_Philox_standard_normal(1234,\n                            np.array([-0.75701648, 1.61496779, 0.67732630]), 3)\n\n@test\ndef test_Philox_standard_t(seed, df, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if isinstance(df, float) or isinstance(df, int):\n            assert gen.standard_t(df) == expected\n        else:\n            assert (round(gen.standard_t(df), 8) == expected).all()\n    else:\n        assert (round(gen.standard_t(df, size), 8) == expected).all()\n\ntest_Philox_standard_t(1234, 3, -0.4679940382376999)\ntest_Philox_standard_t(1234, 1, -2.3925113365078903)\ntest_Philox_standard_t(1234, 0.5, -7.561408056322522)\ntest_Philox_standard_t(1234, 0.5,\n                       np.array([-7.56140806, 0.78219204, -1.00719952]), 3)\ntest_Philox_standard_t(1234, np.array([99, 5.6, 11.43]),\n                       np.array([-0.67980828, 1.38799933, -2.21798311]))\n\n@test\ndef test_Philox_triangular(seed, left, mode, right, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(left, int) or isinstance(left, float)) and (isinstance(\n                mode, int) or isinstance(mode, float)) and (isinstance(\n                    right, int) or isinstance(right, float)):\n            assert gen.triangular(left, mode, right) == expected\n        else:\n            assert (round(gen.triangular(left, mode, right),\n                          8) == expected).all()\n    else:\n        assert (round(gen.triangular(left, mode, right, size),\n                      8) == expected).all()\n\ntest_Philox_triangular(1234, -3, 0, 8, 1.758094078569811)\ntest_Philox_triangular(1234, np.array([-1, -4.2, -3.2]), -0.2, 77,\n                       np.array([25.36646799, 7.24243359, 10.85412666]))\ntest_Philox_triangular(\n    1234, -4, np.array([0.1, 0.5, 3, 6, 7.9]), 8,\n    np.array([1.52141664, -0.52411783, 0.96387999, 4.34090471, 6.76694962]))\ntest_Philox_triangular(1234, -77, 0, np.array([0.1, 11, 789]),\n                       np.array([-19.48246129, -38.06368345, 94.12871014]))\ntest_Philox_triangular(\n    1234,\n    2,\n    10,\n    53,\n    np.array([21.84016148, 11.74049890, 13.63355147, 22.64220666]),\n    size=4)\n\n@test\ndef test_Philox_uniform(seed, expected, low=0.0, high=1.0, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    result = round(gen.uniform(low, high, size), 8)\n    if size is None:\n        if (isinstance(low, int) or isinstance(low, float)) and (isinstance(\n                high, int) or isinstance(high, float)):\n            assert result == round(expected, 8)\n        else:\n            assert (result == expected).all()\n    else:\n        assert (result == expected).all()\n\ntest_Philox_uniform(1234, 10.572569371365311, 5, 15)\ntest_Philox_uniform(1234, 0.601531243422878, 0.1)\ntest_Philox_uniform(1234, 67.9853463306568, high=122)\ntest_Philox_uniform(1234,\n                    np.array([0.55725694, 0.22373624, 0.29333458]),\n                    size=3)\ntest_Philox_uniform(1234,\n                    np.array(\n                        [25.25365940, 12.39692198, 17.44004855, 29.03071891]),\n                    low=np.array([0.4, 3., 6., 7.]),\n                    high=45.)\ntest_Philox_uniform(\n    1234,\n    np.array([31.20638848, 3.57977980, 12.90672144, 1.79724286, 0.16236303]),\n    high=np.array([56., 16., 44., 3.1, 0.2]))\n\n@test\ndef test_Philox_vonmises(seed, mu, kappa, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(mu, float) or isinstance(mu, int)) and (isinstance(\n                kappa, float) or isinstance(kappa, int)):\n            assert gen.vonmises(mu, kappa) == expected\n        else:\n            assert (round(gen.vonmises(mu, kappa), 8) == expected).all()\n    else:\n        assert (round(gen.vonmises(mu, kappa, size), 8) == expected).all()\n\ntest_Philox_vonmises(1234, 0, 4, -0.581040612761238)\ntest_Philox_vonmises(1234, 0.1, 0.2, -1.4566350588891002)\ntest_Philox_vonmises(1234, -1, 0, 0.3597559461503576)\ntest_Philox_vonmises(1234,\n                     0,\n                     5,\n                     np.array([-0.52295031, 0.56024213, -0.16818327]),\n                     size=3)\ntest_Philox_vonmises(\n    1234, np.array([43.09, 18.62, 42, 16.94]), 5,\n    np.array([-1.41524746, 0.33068621, -2.15048042, -1.76019478]))\ntest_Philox_vonmises(\n    1234, 4, np.array([0, 0.1, 7.67, 99]),\n    np.array([0.35975595, -1.64210258, -2.41918436, -2.24951773]))\n\n@test\ndef test_Philox_wald(seed, mean, scale, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(mean, float) or isinstance(mean, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.wald(mean, scale) == expected\n        else:\n            assert (round(gen.wald(mean, scale), 8) == expected).all()\n    else:\n        assert (round(gen.wald(mean, scale, size), 8) == expected).all()\n\ntest_Philox_wald(1234, 0.1, 0.1, 0.04771066895204586)\ntest_Philox_wald(1234, 3, 2, 1.2236223738845495)\ntest_Philox_wald(1234, 3, 2, np.array([1.22362237, 1.33799709, 1.42908304]), 3)\ntest_Philox_wald(1234, np.array([0.1, 0.5, 1, 4]), 0.5,\n                 np.array([0.07139433, 0.25713553, 0.42732171, 0.09772811]))\ntest_Philox_wald(1234, 0.5, np.array([0.1, 0.5, 1, 4]),\n                 np.array([0.10751922, 0.25713553, 0.77217339, 1.07087709]))\ntest_Philox_wald(1234, np.array([0.1, 0.5, 1, 4]), np.array([3, 2, 1, 8]),\n                 np.array([0.08710099, 0.35693113, 1.84037068, 0.95321118]))\n\n@test\ndef test_Philox_weibull(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(a, float) or isinstance(a, int)):\n            assert gen.weibull(a) == expected\n        else:\n            assert (round(gen.weibull(a), 8) == expected).all()\n    else:\n        assert (round(gen.weibull(a, size), 8) == expected).all()\n\ntest_Philox_weibull(1234, 0, 0)\ntest_Philox_weibull(1234, 0.1, 19.277126441209486)\ntest_Philox_weibull(1234, 1, 1.34432485899693)\ntest_Philox_weibull(1234, 1, np.array([1.34432486, 0.44577319, 0.14179837]), 3)\ntest_Philox_weibull(1234, np.array([17.8, 4.32]),\n                    np.array([1.01676207, 0.82942358]))\n\n@test\ndef test_Philox_zipf(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.Philox(seed))\n    if size is None:\n        if (isinstance(a, float) or isinstance(a, int)):\n            assert gen.zipf(a) == expected\n        else:\n            assert (gen.zipf(a) == expected).all()\n    else:\n        assert (gen.zipf(a, size) == expected).all()\n\ntest_Philox_zipf(1234, 1.1, 3455)\ntest_Philox_zipf(1234, 3, 1)\ntest_Philox_zipf(1234, 45, np.array([1, 1, 1]), 3)\ntest_Philox_zipf(1234, np.array([17.8, 4.32]), np.array([1, 1]))\n"
  },
  {
    "path": "test/numpy/random_tests/test_sfc64.codon",
    "content": "import numpy.random as rnd\nfrom numpy import *\nimport numpy as np\nimport internal.static as static\n\n@test\ndef test_SFC64_integers(seed, low, expected, high=None, size=None, e=False):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(low, int)\n                or isinstance(low, float)) and (high is None or isinstance(\n                    high, int) or isinstance(high, float)):\n            assert gen.integers(low, high, endpoint=e) == expected\n        else:\n            assert (gen.integers(low, high, endpoint=e) == expected).all()\n    else:\n        assert (gen.integers(low, high, size, endpoint=e) == expected).all()\n\ntest_SFC64_integers(74, 5, 4)\ntest_SFC64_integers(74, 987.4, 897)\ntest_SFC64_integers(74, 98, 906, high=987)\ntest_SFC64_integers(74, 89, 81, e=True)\ntest_SFC64_integers(74, -1218, -22, high=98)\ntest_SFC64_integers(74, -5, np.array([49, 17, 46]), 55, size=3)\ntest_SFC64_integers(74, -5, np.array([50, 18, 47]), 55, size=3, e=True)\ntest_SFC64_integers(74, np.array([7, 49, 17]), np.array([71, 60, 69]), 78)\ntest_SFC64_integers(74, -99, np.array([61, -28, 72]), np.array([77, 89, 101]))\ntest_SFC64_integers(74, np.array([7, 49, 17]), np.array([70, 64, 88]),\n                    np.array([77, 89, 101]))\n\n@test\ndef test_SFC64_random(seed, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        assert gen.random() == expected\n    else:\n        assert (round(gen.random(size), 8) == round(expected, 8)).all()\n\ntest_SFC64_random(0, 0.5686892493917326)\ntest_SFC64_random(1, 0.9956200510286735)\ntest_SFC64_random(1234, 0.845119081462231)\ntest_SFC64_random(\n    1234,\n    np.array([0.84511908, 0.55138007, 0.31781911, 0.05565777, 0.96435368]),\n    size=5)\ntest_SFC64_random(74,\n                  np.array([[0.38219269, 0.67730401, 0.68207767],\n                            [0.40336162, 0.58776338, 0.35552843]]),\n                  size=(2, 3))\n\n@test\ndef test_SFC64_choice(seed,\n                      a,\n                      expected,\n                      size=None,\n                      r=True,\n                      p=None,\n                      axis=0,\n                      shuffle=True):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if isinstance(a, int):\n            assert gen.choice(a, size, r, p, axis, shuffle) == expected\n        elif static.len(a.shape) == 1:\n            assert gen.choice(a, size, r, p, axis, shuffle) == expected\n        else:\n            assert (round(gen.choice(a, size, r, p, axis, shuffle),\n                          8) == expected).all()\n    else:\n        assert (round(gen.choice(a, size, r, p, axis, shuffle),\n                      8) == expected).all()\n\ntest_SFC64_choice(\n    621,\n    np.array([\n        1.34, 2.15, 3.21, 4.78, 5.55, 6.42, 7.99, 8.31, 9.61, 10.75, 11.33,\n        12.93\n    ]), np.array([1.34, 10.75, 7.99]), 3)\ntest_SFC64_choice(\n    318, np.array([5, 4, 2, 6, 7, 8, 2, 12, 1, 55, 33, 7, 78, 15, 43]), 7)\ntest_SFC64_choice(318, np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),\n                  np.array([11, 3, 5]), 3, False)\ntest_SFC64_choice(\n    4589,\n    np.array([\n        -1.34, -2.15, -3.21, -4.78, -5.55, -6.42, -7.99, -8.31, -9.61, -10.75,\n        -11.33, -12.93\n    ]), -9.61)\ntest_SFC64_choice(318, np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11,\n                                                                   12]]),\n                  np.array([[4, 5, 6], [10, 11, 12]]), 2)\ntest_SFC64_choice(74, np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11,\n                                                                  12]]),\n                  np.array([10, 11, 12]))\ntest_SFC64_choice(74, 1, 0)\ntest_SFC64_choice(74, np.array([13, -4]), -4, p=np.array([0.3, 0.7]))\ntest_SFC64_choice(74,\n                  np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]]),\n                  np.array([[3], [6], [9], [12]]),\n                  axis=1)\ntest_SFC64_choice(74,\n                  np.array([13, -4, 3, 18]),\n                  13,\n                  p=np.array([0.5, 0.3, 0.1, 0.1]),\n                  shuffle=True)\n\n@test\ndef test_SFC64_bytes(seed, length, expected):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    check = gen.bytes(length) == expected\n    assert check\n\ntest_SFC64_bytes(555, 10, \"O\\x9e\\x1b<\\x9cR\\x1c.H\\x19\")\n\n@test\ndef test_SFC64_shuffle(seed, x, expected, axis=0):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    gen.shuffle(x, axis)\n    assert (x == expected).all()\n\ntest_SFC64_shuffle(876, np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),\n                   np.array([9, 0, 8, 2, 7, 3, 4, 6, 1, 5]))\ntest_SFC64_shuffle(111, np.array([5.43, 6.56, 1.22, 4.3221, 7.88, 0.9352]),\n                   np.array([7.88, 0.9352, 6.56, 1.22, 5.43, 4.3221]))\ntest_SFC64_shuffle(1234, np.array([3.14]), np.array([3.14]))\ntest_SFC64_shuffle(74,\n                   np.arange(9).reshape((3, 3)),\n                   np.array([[6, 7, 8], [0, 1, 2], [3, 4, 5]]))\ntest_SFC64_shuffle(74,\n                   np.arange(9).reshape((3, 3)),\n                   np.array([[2, 0, 1], [5, 3, 4], [8, 6, 7]]),\n                   axis=1)\n\n@test\ndef test_SFC64_permutation(seed, x, expected, axis=0):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    assert (gen.permutation(x, axis) == expected).all()\n\ntest_SFC64_permutation(12345, 10, np.array([4, 3, 1, 5, 9, 8, 6, 0, 7, 2]))\ntest_SFC64_permutation(1234,\n                       np.array([1.124, 4.7532, 9.1246, 12.53243, 15.64324]),\n                       np.array([15.64324, 4.7532, 9.1246, 12.53243, 1.124]))\ntest_SFC64_permutation(\n    4321,\n    np.arange(24).reshape(3, 8),\n    np.array([[16, 17, 18, 19, 20, 21, 22, 23], [8, 9, 10, 11, 12, 13, 14, 15],\n              [0, 1, 2, 3, 4, 5, 6, 7]]))\ntest_SFC64_permutation(74,\n                       np.arange(9).reshape((3, 3)),\n                       np.array([[2, 0, 1], [5, 3, 4], [8, 6, 7]]),\n                       axis=1)\n\n@test\ndef test_SFC64_permuted(seed, x, expected, axis=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    arr = gen.permuted(x, axis)\n    assert (arr == expected).all()\n\ntest_SFC64_permuted(4321, np.array([1.124, 4.7532, 9.1246, 12.53243,\n                                    15.64324]),\n                    np.array([9.1246, 15.64324, 4.7532, 1.124, 12.53243]))\ntest_SFC64_permuted(\n    4321,\n    np.arange(24).reshape(3, 8),\n    np.array([[21, 7, 22, 5, 9, 1, 6, 19], [18, 16, 14, 0, 20, 17, 12, 3],\n              [8, 11, 10, 23, 2, 13, 4, 15]]))\n\n@test\ndef test_SFC64_beta(seed, a, b, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(a, int) or isinstance(a, float)) and (isinstance(\n                b, int) or isinstance(b, float)):\n            assert gen.beta(a, b) == expected\n        else:\n            assert (round(gen.beta(a, b), 8) == expected).all()\n    else:\n        assert (round(gen.beta(a, b, size), 8) == expected).all()\n\n#test_SFC64_beta(4321, -7, 0, error)\ntest_SFC64_beta(4321, 0.2, 0.1, 0.997423991996233)\ntest_SFC64_beta(4321, 10, 14, 0.28271738921432865)\ntest_SFC64_beta(4321, 167, 2041,\n                np.array([0.06958452, 0.07693825, 0.07529241, 0.08165749]), 4)\ntest_SFC64_beta(139, 14.32, 4, 0.8247260405458904)\ntest_SFC64_beta(457, 12.87, 9.21, 0.5617457872800475)\ntest_SFC64_beta(\n    457, np.array([12.87, 1.32, 4.532, 1.34, 8.432]), 9.21,\n    np.array([0.56174579, 0.00369936, 0.25818220, 0.13144450, 0.36499717]))\ntest_SFC64_beta(\n    457, 32.14, np.array([9.21, 0.21, 14.32, 41.21, 5.55]),\n    np.array([0.76283046, 0.99080339, 0.69766178, 0.38339742, 0.89963656]))\ntest_SFC64_beta(\n    457, np.array([12.87, 1.32, 4.532, 1.34, 8.432]),\n    np.array([9.21, 0.21, 14.32, 41.21, 5.55]),\n    np.array([0.56174579, 0.11208923, 0.25413326, 0.00258646, 0.77501163]))\n\n@test\ndef test_SFC64_binomial(seed, n, p, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(n, int) or isinstance(n, float)) and (isinstance(\n                p, int) or isinstance(p, float)):\n            assert gen.binomial(n, p) == expected\n        else:\n            assert (gen.binomial(n, p) == expected).all()\n    else:\n        assert (gen.binomial(n, p, size) == expected).all()\n\n#test_SFC64_binomial(139, -2, .33, 0)\ntest_SFC64_binomial(139, 28400, .66, 18691)\ntest_SFC64_binomial(139, 14.76, .33, 6)\ntest_SFC64_binomial(139, 0, .33, 0)\ntest_SFC64_binomial(139, 14, 0, 0)\ntest_SFC64_binomial(147, 10, .5, np.array([6, 4, 7, 6, 5, 5, 4, 5, 6, 3]), 10)\ntest_SFC64_binomial(457, np.array([12, 1, 4, 7, 8]), 0.11,\n                    np.array([1, 0, 0, 1, 1]))\ntest_SFC64_binomial(457, 12, np.array([0.11, 0.21, 0.32, 0.43]),\n                    np.array([1, 2, 3, 5]))\ntest_SFC64_binomial(457, np.array([12, 1, 4, 7, 8]),\n                    np.array([0.11, 0.21, 0.32, 0.43, 0.55]),\n                    np.array([1, 0, 1, 3, 4]))\n\n@test\ndef test_SFC64_chisquare(seed, df, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(df, float) or isinstance(df, int)):\n            assert gen.chisquare(df) == expected\n        else:\n            assert (round(gen.chisquare(df), 8) == expected).all()\n    else:\n        assert (round(gen.chisquare(df, size), 8) == expected).all()\n\ntest_SFC64_chisquare(457, 12.12, 11.966063817767916)\ntest_SFC64_chisquare(457, 24.12, np.array([24.18381621, 26.19325346]), 2)\ntest_SFC64_chisquare(\n    457, np.array([23, 42, 34, 17]),\n    np.array([23.04634179, 44.93754189, 18.93394980, 12.24727194]))\ntest_SFC64_chisquare(\n    457, np.array([274.23, 325.42, 352.34, 825.17]),\n    np.array([276.04011404, 334.67517564, 298.76233776, 793.05450604]))\n\n@test\ndef test_SFC64_dirichlet(seed, alpha, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    assert (round(gen.dirichlet(alpha, size), 8) == round(expected, 8)).all()\n\ntest_SFC64_dirichlet(\n    457, np.array([278.23, 325.42, 352.34, 825.17]),\n    np.array([0.16181824, 0.19215271, 0.18167716, 0.46435189]))\ntest_SFC64_dirichlet(\n    874, np.array([10, 5, 3]),\n    np.array([[0.51743412, 0.34158578, 0.14098010],\n              [0.83079306, 0.06186507, 0.10734187]]), 2)\n#test_SFC64_dirichlet(1234, np.array([]), np.array([]))\ntest_SFC64_dirichlet(74, np.array([0.01, 0.05]),\n                     np.array([4.09793014e-39, 1.00000000]))\n\n@test\ndef test_SFC64_exponential(seed, scale, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(scale, float) or isinstance(scale, int)):\n            assert gen.exponential(scale) == expected\n        else:\n            assert (round(gen.exponential(scale), 8) == expected).all()\n    else:\n        assert (round(gen.exponential(scale, size), 8) == expected).all()\n\ntest_SFC64_exponential(74, 1, 0.614763996552136)\ntest_SFC64_exponential(874, 3, 0.13467829696559896)\ntest_SFC64_exponential(874, 724.952, 32.5451002472683)\ntest_SFC64_exponential(\n    874, np.array([278.23, 325.42, 352.34, 825.17]),\n    np.array([12.49051419, 58.84353559, 22.45415942, 593.22299347]))\ntest_SFC64_exponential(\n    874, 724.952,\n    np.array([\n        32.54510025, 131.08825152, 46.20022644, 521.17526760, 1494.82126609,\n        45.90285356, 239.67612418\n    ]), 7)\n\n@test\ndef test_SFC64_f(seed, dfnum, dfden, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(dfnum, float)\n                or isinstance(dfnum, int)) and (isinstance(dfden, float)\n                                                or isinstance(dfden, int)):\n            assert gen.f(dfnum, dfden) == expected\n        else:\n            assert (round(gen.f(dfnum, dfden), 8) == expected).all()\n    else:\n        assert (round(gen.f(dfnum, dfden, size), 8) == expected).all()\n\ntest_SFC64_f(874, 12, 4.21, 0.6713378390508739)\ntest_SFC64_f(\n    874, np.array([14, 15, 16, 17, 18]), 4.21,\n    np.array([0.67043074, 0.36520957, 0.55067818, 1.16853235, 14.44185656]))\ntest_SFC64_f(\n    874, 2557, 0.92,\n    np.array([12.55103903, 4.24970982, 4.78102326, 4.87903268, 4.01745429]), 5)\ntest_SFC64_f(874, 7343, np.array([12.52, 0.92, 231.52]),\n             np.array([0.73535605, 4.24355837, 0.91151352]))\n\n@test\ndef test_SFC64_gamma(seed, shape, expected, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(shape, float)\n                or isinstance(shape, int)) and (isinstance(scale, float)\n                                                or isinstance(scale, int)):\n            assert gen.gamma(shape, scale) == expected\n        else:\n            assert (round(gen.gamma(shape, scale), 8) == expected).all()\n    else:\n        assert (round(gen.gamma(shape, scale, size), 8) == expected).all()\n\ntest_SFC64_gamma(318, 564, 598.378275104569)\ntest_SFC64_gamma(318, 564, 3291.080513075129, 5.5)\ntest_SFC64_gamma(318, 564, np.array([3291.08051308, 1306.08172100]),\n                 np.array([5.5, 2.2]))\ntest_SFC64_gamma(\n    318, 564,\n    np.array([2467.37392281, 2447.97412784, 2399.22002265, 2336.40987953]),\n    4.123435, 4)\ntest_SFC64_gamma(\n    318, np.array([19, 17, 45, 52, 21]),\n    np.array(\n        [218.01672004, 189.88935671, 425.71054348, 448.20979789,\n         155.14718090]), 8.527)\ntest_SFC64_gamma(\n    318, np.array([19, 17, 45, 52, 21]),\n    np.array(\n        [28.12459154, 97.98442237, 99.85001606, 399.48334279, 179.58281641]),\n    np.array([1.1, 4.4, 2, 7.6, 9.87]))\n\n@test\ndef test_SFC64_geometric(seed, p, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if isinstance(p, float) or isinstance(p, int):\n            assert gen.geometric(p) == expected\n        else:\n            assert (gen.geometric(p) == expected).all()\n    else:\n        assert (gen.geometric(p, size) == expected).all()\n\ntest_SFC64_geometric(457, 0.35, 2)\ntest_SFC64_geometric(457, 0.2, 2)\ntest_SFC64_geometric(2000, 1, 1)\ntest_SFC64_geometric(12654, 0.5, np.array([2., 4., 3., 1.]), 4)\ntest_SFC64_geometric(12654, np.array([0.01, 0.6, 0.45, 0.33]),\n                     np.array([67, 3, 3, 1]))\n\n@test\ndef test_SFC64_gumbel(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.gumbel(loc, scale) == expected\n        else:\n            assert (round(gen.gumbel(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.gumbel(loc, scale, size), 8) == expected).all()\n\ntest_SFC64_gumbel(1234, -0.6233139872603548)\ntest_SFC64_gumbel(1234, 11.376686012739645, 12)\ntest_SFC64_gumbel(1234, -76.04430644576328, scale=122)\ntest_SFC64_gumbel(1234,\n                  np.array([-0.62331399, 0.22117146, 0.96113010]),\n                  size=3)\ntest_SFC64_gumbel(1234,\n                  np.array(\n                      [-7.35510505, 11.31734384, 57.79275280, 18.44723753]),\n                  scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_SFC64_hypergeometric(seed, ngood, nbad, nsample, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(ngood, int) or isinstance(ngood, float)) and (\n                isinstance(nbad, int) or isinstance(nbad, float)) and (\n                    isinstance(nsample, int) or isinstance(nsample, float)):\n            assert gen.hypergeometric(ngood, nbad, nsample) == expected\n        else:\n            assert (gen.hypergeometric(ngood, nbad, nsample) == expected).all()\n    else:\n        assert (gen.hypergeometric(ngood, nbad, nsample,\n                                   size) == expected).all()\n\ntest_SFC64_hypergeometric(1234, 11, 12, 9, 6)\ntest_SFC64_hypergeometric(1234, 100, 300, 200, 51)\n#test_SFC64_hypergeometric(1234, 100.31, 300.55, 200.2, 51)\ntest_SFC64_hypergeometric(1234, 100, 300, np.array([200, 201, 222, 221]),\n                          np.array([51, 51, 54, 62]))\ntest_SFC64_hypergeometric(1234, 100, np.array([301, 303, 304, 344, 355]), 200,\n                          np.array([50, 49, 51, 38, 38]))\ntest_SFC64_hypergeometric(1234, np.array([100, 111, 112, 122]), 300, 200,\n                          np.array([51, 53, 55, 50]))\ntest_SFC64_hypergeometric(1234, 100, 700, 450, np.array([56, 57, 55, 64, 62]),\n                          5)\n\n@test\ndef test_SFC64_laplace(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.laplace(loc, scale) == expected\n        else:\n            assert (round(gen.laplace(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.laplace(loc, scale, size), 8) == expected).all()\n\ntest_SFC64_laplace(1234, 1.1719515442699244)\ntest_SFC64_laplace(1234, 13.171951544269925, 12)\ntest_SFC64_laplace(1234, 142.97808840093077, scale=122)\ntest_SFC64_laplace(1234,\n                   np.array([1.17195154, 0.10843205, -0.45312571]),\n                   size=3)\ntest_SFC64_laplace(1234,\n                   np.array(\n                       [1.97195154, 1.27843205, -0.32312571, 263.25461367]),\n                   loc=np.array([0.8, 1.17, 0.13, 265.45]))\ntest_SFC64_laplace(1234,\n                   np.array(\n                       [13.82902822, 5.54846777, -27.24644891, -14.16024183]),\n                   scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_SFC64_logistic(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.logistic(loc, scale) == expected\n        else:\n            assert (round(gen.logistic(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.logistic(loc, scale, size), 8) == expected).all()\n\ntest_SFC64_logistic(1234, 1.6968209880833125)\ntest_SFC64_logistic(1234, 13.696820988083312, 12)\ntest_SFC64_logistic(1234, 207.01216054616413, scale=122)\ntest_SFC64_logistic(1234,\n                    np.array([1.69682099, 0.20624830, -0.76381247]),\n                    size=3)\ntest_SFC64_logistic(1234,\n                    np.array(\n                        [2.49682099, 1.37624830, -0.63381247, 262.61873314]),\n                    loc=np.array([0.8, 1.17, 0.13, 265.45]))\ntest_SFC64_logistic(\n    1234,\n    np.array([20.02248766, 10.55372536, -45.92804359, -18.26167125]),\n    scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_SFC64_lognormal(seed, expected, mean=0.0, sigma=1.0, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(mean, float) or isinstance(mean, int)) and (isinstance(\n                sigma, float) or isinstance(sigma, int)):\n            assert gen.lognormal(mean, sigma) == expected\n        else:\n            assert (round(gen.lognormal(mean, sigma), 8) == expected).all()\n    else:\n        assert (round(gen.lognormal(mean, sigma, size), 8) == expected).all()\n\ntest_SFC64_lognormal(1234, 0.23949890805477525)\ntest_SFC64_lognormal(1234, 38979.59482553418, 12)\ntest_SFC64_lognormal(1234, 0.05735972695942969, sigma=2)\ntest_SFC64_lognormal(1234,\n                     np.array([0.23949891, 2.19364846, 1.33309599]),\n                     size=3)\ntest_SFC64_lognormal(1234,\n                     np.array(\n                         [0.53301462, 7.06791919, 1.51816755, 117.49197082]),\n                     mean=np.array([0.8, 1.17, 0.13, 5.45]))\ntest_SFC64_lognormal(1234,\n                     np.array([0.07633898, 5.49961716, 1.25138778,\n                               0.37110750]),\n                     sigma=np.array([1.8, 2.17, 0.78, 1.45]))\n\n@test\ndef test_SFC64_logseries(seed, p, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if isinstance(p, float) or isinstance(p, int):\n            assert gen.logseries(p) == expected\n        else:\n            assert (gen.logseries(p) == expected).all()\n    else:\n        assert (gen.logseries(p, size) == expected).all()\n\ntest_SFC64_logseries(457, 0.35, 1)\ntest_SFC64_logseries(2000, 0, 1)\ntest_SFC64_logseries(457, 0.5, np.array([1., 2., 1., 2.]), 4)\ntest_SFC64_logseries(457, np.array([0.01, 0.6, 0.45, 0.33]),\n                     np.array([1, 1, 1, 1]))\n\n@test\ndef test_SFC64_multinomial(seed, n, pvals, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    assert (round(gen.multinomial(n, pvals, size), 8) == expected).all()\n\ntest_SFC64_multinomial(457, 20, [1 / 6.], np.array([20]))\ntest_SFC64_multinomial(457, 20.6, [1 / 6.], np.array([20]))\ntest_SFC64_multinomial(457, 20, [1 / 6.] * 6, np.array([3, 3, 2, 4, 4, 4]))\ntest_SFC64_multinomial(\n    457, 20, [1 / 6] * 6,\n    np.array([[3, 3, 2, 4, 4, 4], [2, 4, 1, 9, 2, 2], [4, 3, 4, 3, 5, 1]]), 3)\n#test_SFC64_multinomial(457, 1, np.array([[.1, .5, .4 ], [.3, .7, .0]]), np.array([[0, 1, 0], [0, 1, 0]]))\n#test_SFC64_multinomial(457, -1, [1/6.], error)\n\n@test\ndef test_SFC64_multivariate_hypergeometric(seed,\n                                           colors,\n                                           nsample,\n                                           expected,\n                                           size=None,\n                                           method='marginals'):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    assert (gen.multivariate_hypergeometric(colors, nsample, size,\n                                            method) == expected).all()\n\ntest_SFC64_multivariate_hypergeometric(457, np.array([16, 8, 4]), 15,\n                                       np.array([7, 7, 1]))\ntest_SFC64_multivariate_hypergeometric(457,\n                                       np.array([16, 8, 4]),\n                                       15,\n                                       np.array([[7, 7, 1], [10, 4, 1],\n                                                 [9, 4, 2]]),\n                                       size=3)\ntest_SFC64_multivariate_hypergeometric(457, np.array([16, 8, 4]), 0,\n                                       np.array([0, 0, 0]))\ntest_SFC64_multivariate_hypergeometric(457,\n                                       np.array([16, 8, 4]),\n                                       8,\n                                       np.array([5, 3, 0]),\n                                       method='count')\n\n@test\ndef test_SFC64_multivariate_normal(seed,\n                                   mean,\n                                   cov,\n                                   expected,\n                                   size=None,\n                                   check_valid: Literal[str] = 'warn',\n                                   tol: float = 1e-8,\n                                   m: Literal[str] = 'svd'):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    assert (round(\n        gen.multivariate_normal(mean,\n                                cov,\n                                size,\n                                check_valid=check_valid,\n                                tol=tol,\n                                method=m), 8) == expected).all()\n\ntest_SFC64_multivariate_normal(1234, (1, 2), np.array([[1, 0], [0, 1]]),\n                               np.array([-0.42920642, 2.78556612]))\ntest_SFC64_multivariate_normal(5432, (1, 2),\n                               np.array([[1, 0], [0, 1]]),\n                               np.array([-0.56243526, 4.58304033]),\n                               m='cholesky')\n\n@test\ndef test_SFC64_negative_binomial(seed, n, p, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(n, int) or isinstance(n, float)) and (isinstance(\n                p, int) or isinstance(p, float)):\n            assert gen.negative_binomial(n, p) == expected\n        else:\n            assert (gen.negative_binomial(n, p) == expected).all()\n    else:\n        assert (gen.negative_binomial(n, p, size) == expected).all()\n\ntest_SFC64_negative_binomial(139, 28400, .66, 14582)\ntest_SFC64_negative_binomial(139, 14.76, .33, 28)\n#test_SFC64_negative_binomial(139, 14, 0, error)\ntest_SFC64_negative_binomial(147, 10, .5,\n                             np.array([10, 6, 6, 13, 12, 5, 16, 13, 10, 13]),\n                             10)\ntest_SFC64_negative_binomial(457, np.array([12, 1, 4, 7, 8]), 0.11,\n                             np.array([89, 26, 50, 33, 48]))\ntest_SFC64_negative_binomial(457, 12, np.array([0.11, 0.21, 0.32, 0.43]),\n                             np.array([[89, 23, 18, 24]]))\n\n@test\ndef test_SFC64_noncentral_chisquare(seed, df, nonc, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(df, int) or isinstance(df, float)) and (isinstance(\n                nonc, int) or isinstance(nonc, float)):\n            assert gen.noncentral_chisquare(df, nonc) == expected\n        else:\n            assert (round(gen.noncentral_chisquare(df, nonc),\n                          8) == expected).all()\n    else:\n        assert (round(gen.noncentral_chisquare(df, nonc, size),\n                      8) == expected).all()\n\ntest_SFC64_noncentral_chisquare(457, 0.1, 0.2, 8.383813499573139e-10)\ntest_SFC64_noncentral_chisquare(457, 5, 0, 4.651609985678877)\ntest_SFC64_noncentral_chisquare(457, 99, 7, 108.00168289970618)\ntest_SFC64_noncentral_chisquare(\n    457, 14.2, 0.5, np.array([14.26295297, 7.90698982, 9.56194238]), 3)\ntest_SFC64_noncentral_chisquare(\n    457, np.array([8, 7.1, 45.3]), 0.6,\n    np.array([8.06216455, 2.67439755, 37.17123094]))\ntest_SFC64_noncentral_chisquare(\n    457, 0.6, np.array([8, 7.1, 45.3]),\n    np.array([1.23557513, 3.84927719, 43.61419849]))\n\n@test\ndef test_SFC64_noncentral_f(seed, dfnum, dfden, nonc, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(dfnum, float) or isinstance(dfnum, int)) and (\n                isinstance(dfden, int)\n                or isinstance(dfden, float)) and (isinstance(nonc, int)\n                                                  or isinstance(nonc, float)):\n            assert gen.noncentral_f(dfnum, dfden, nonc) == expected\n        else:\n            assert (round(gen.noncentral_f(dfnum, dfden, nonc),\n                          8) == expected).all()\n    else:\n        assert (round(gen.noncentral_f(dfnum, dfden, nonc, size),\n                      8) == expected).all()\n\ntest_SFC64_noncentral_f(874, 3, 20, 3, 1.2543069110903733)\ntest_SFC64_noncentral_f(874, 3, 20, 0, 0.790159454240572)\ntest_SFC64_noncentral_f(\n    874, np.array([14, 15, 16, 17, 18]), 4.21, 6.7,\n    np.array([10.25330200, 20.13749033, 12.78219243, 15.78411365, 3.22543898]))\ntest_SFC64_noncentral_f(\n    874, 27, 5.92, 3.1,\n    np.array([4.76909802, 8.23160216, 5.56157716, 6.50176072, 2.50623523]), 5)\ntest_SFC64_noncentral_f(\n    874, 27, np.array([5.92, 8.13, 9.53, 33.14]), 3.1,\n    np.array([4.76909802, 5.33473529, 3.53537993, 1.94776216]))\ntest_SFC64_noncentral_f(\n    874, 743, 8.24, np.array([0.11, 0.21, 0.32, 0.43]),\n    np.array([2.86196164, 4.87847860, 3.43786139, 3.98570386]))\n\n@test\ndef test_SFC64_normal(seed, expected, loc=0.0, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(loc, float) or isinstance(loc, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.normal(loc, scale) == expected\n        else:\n            assert (round(gen.normal(loc, scale), 8) == expected).all()\n    else:\n        assert (round(gen.normal(loc, scale, size), 8) == expected).all()\n\ntest_SFC64_normal(1234, -1.42920642141181)\ntest_SFC64_normal(1234, 10.57079357858819, 12)\ntest_SFC64_normal(1234, -174.36318341224083, scale=122)\ntest_SFC64_normal(1234,\n                  np.array([-1.42920642, 0.78556612, 0.28750405]),\n                  size=3)\ntest_SFC64_normal(1234,\n                  np.array([-0.62920642, 1.95556612, 0.41750405,\n                            264.76637000]),\n                  loc=np.array([0.8, 1.17, 0.13, 265.45]))\ntest_SFC64_normal(1234,\n                  np.array(\n                      [-16.86463577, 40.19741841, 17.28761860, -4.40941351]),\n                  scale=np.array([11.8, 51.17, 60.13, 6.45]))\n\n@test\ndef test_SFC64_pareto(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if isinstance(a, float) or isinstance(a, int):\n            assert gen.pareto(a) == expected\n        else:\n            assert (round(gen.pareto(a), 8) == expected).all()\n    else:\n        assert (round(gen.pareto(a, size), 8) == expected).all()\n\ntest_SFC64_pareto(1234, 3, 0.5414922795373657)\ntest_SFC64_pareto(\n    1234, 5,\n    np.array([0.29647699, 0.13971592, 0.00406694, 0.30058308, 0.31654861]), 5)\ntest_SFC64_pareto(\n    1234, np.array([3, 13, 4, 23.2, 5.6]),\n    np.array([0.54149228, 0.05158614, 0.00508625, 0.05827547, 0.27832148]))\n\n@test\ndef test_SFC64_poisson(seed, expected, lam=1.0, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if isinstance(lam, float) or isinstance(lam, int):\n            assert gen.poisson(lam) == expected\n        else:\n            assert (round(gen.poisson(lam), 8) == expected).all()\n    else:\n        assert (round(gen.poisson(lam, size), 8) == expected).all()\n\ntest_SFC64_poisson(1234, 0, 0)\ntest_SFC64_poisson(1234, 2)\ntest_SFC64_poisson(1234, 3, 3)\ntest_SFC64_poisson(1234, 19, 14.65)\ntest_SFC64_poisson(1234, np.array([43., 7., 10., 65., 23.]),\n                   np.array([35.62, 9.23, 9.57, 76.79, 21.74]))\ntest_SFC64_poisson(1234, np.array([857., 809., 837., 886., 788.]), 824.14, 5)\n\n@test\ndef test_SFC64_power(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if isinstance(a, float) or isinstance(a, int):\n            assert np.isclose(gen.power(a), expected)\n        else:\n            assert (round(gen.power(a), 8) == expected).all()\n    else:\n        assert (round(gen.power(a, size), 8) == expected).all()\n\ntest_SFC64_power(1234, 1, 0.7269916449719858)\ntest_SFC64_power(1234, 45.11, 0.992956857625712)\ntest_SFC64_power(1234, 6, np.array([0.94824720, 0.88485292, 0.52138604]), 3)\ntest_SFC64_power(\n    1234, np.array([5.4, 1.1, 78, 19.34, 99999999999999]),\n    np.array(\n        array([0.94266482, 0.51310462, 0.95113690, 0.98394787, 1.00000000])))\n\n@test\ndef test_SFC64_rayleigh(seed, expected, scale=1.0, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if isinstance(scale, float) or isinstance(scale, int):\n            assert gen.rayleigh(scale) == expected\n        else:\n            assert (round(gen.rayleigh(scale), 8) == expected).all()\n    else:\n        assert (round(gen.rayleigh(scale, size), 8) == expected).all()\n\n#test_SFC64_rayleigh(1234, error, -1)\ntest_SFC64_rayleigh(1234, 1.6113676673976456)\ntest_SFC64_rayleigh(1234, 4.834103002192936, 3)\ntest_SFC64_rayleigh(1234, 23.606536327375508, 14.65)\ntest_SFC64_rayleigh(1234,\n                    np.array([1.61136767, 1.14358662, 0.20146185, 1.62114986]),\n                    size=4)\ntest_SFC64_rayleigh(\n    1234,\n    np.array([8.70138540, 1.25794529, 15.71402409, 31.35303831, 93.19946494]),\n    np.array([5.4, 1.1, 78, 19.34, 56.2]))\n\n@test\ndef test_SFC64_standard_cauchy(seed, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        assert gen.standard_cauchy() == expected\n    else:\n        assert (round(gen.standard_cauchy(size), 8) == round(expected,\n                                                             8)).all()\n\ntest_SFC64_standard_cauchy(1234, -1.8193330687397915)\ntest_SFC64_standard_cauchy(\n    1234, np.array([-1.81933307, -0.42055505, -0.93484213, -3.48517456]), 4)\n\n@test\ndef test_SFC64_standard_exponential(seed, expected, size=None, m='zig'):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        assert np.isclose(gen.standard_exponential(method=m), expected)\n    else:\n        assert (round(gen.standard_exponential(size, method=m),\n                      8) == expected).all()\n\ntest_SFC64_standard_exponential(1234, 1.2982528797672646)\ntest_SFC64_standard_exponential(\n    1234, np.array([1.29825288, 0.65389518, 0.02029344, 1.31406344]), 4)\ntest_SFC64_standard_exponential(1234, 1.8650987248298696, m='inv')\n\n@test\ndef test_SFC64_standard_gamma(seed, shape, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if isinstance(shape, float) or isinstance(shape, int):\n            assert gen.standard_gamma(shape) == expected\n        else:\n            assert (round(gen.standard_gamma(shape), 8) == expected).all()\n    else:\n        assert (round(gen.standard_gamma(shape, size), 8) == expected).all()\n\ntest_SFC64_standard_gamma(318, 1, 0.44353979202019395)\ntest_SFC64_standard_gamma(1234, 0, 0)\ntest_SFC64_standard_gamma(1234, 0.5, 1.1793433776641271)\ntest_SFC64_standard_gamma(1234, 3, 0.9474473458925592)\ntest_SFC64_standard_gamma(\n    1234, 0.5, np.array([1.17934338, 3.31415165, 2.61458827, 0.01842475]), 4)\ntest_SFC64_standard_gamma(\n    1234, np.array([4.5, 2, 7.7, 81.15]),\n    np.array([1.87721867, 2.06606744, 5.58659139, 87.42655316]))\n\n@test\ndef test_SFC64_standard_normal(seed, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        assert gen.standard_normal(size) == expected\n    else:\n        assert (round(gen.standard_normal(size), 8) == expected).all()\n\ntest_SFC64_standard_normal(1234, -1.42920642141181)\ntest_SFC64_standard_normal(1234,\n                           np.array([-1.42920642, 0.78556612, 0.28750405]), 3)\n\n@test\ndef test_SFC64_standard_t(seed, df, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if isinstance(df, float) or isinstance(df, int):\n            assert gen.standard_t(df) == expected\n        else:\n            assert (round(gen.standard_t(df), 8) == expected).all()\n    else:\n        assert (round(gen.standard_t(df, size), 8) == expected).all()\n\ntest_SFC64_standard_t(1234, 3, -1.1701964917825112)\ntest_SFC64_standard_t(1234, 1, -0.5551288173526309)\ntest_SFC64_standard_t(1234, 0.5, -0.46704423557734326)\ntest_SFC64_standard_t(1234, 0.6,\n                      np.array([-0.48120758, 204.42340747, -16.42414839]), 3)\ntest_SFC64_standard_t(1234, np.array([99, 5.6, 11.43]),\n                      np.array([-1.35730576, -0.93306323, 0.77185391]))\n\n@test\ndef test_SFC64_triangular(seed, left, mode, right, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(left, int) or isinstance(left, float)) and (isinstance(\n                mode, int) or isinstance(mode, float)) and (isinstance(\n                    right, int) or isinstance(right, float)):\n            assert gen.triangular(left, mode, right) == expected\n        else:\n            assert (round(gen.triangular(left, mode, right),\n                          8) == expected).all()\n    else:\n        assert (round(gen.triangular(left, mode, right, size),\n                      8) == expected).all()\n\ntest_SFC64_triangular(1234, -3, 0, 8, 4.308181907064803)\ntest_SFC64_triangular(1234, np.array([-1, -4.2, -3.2]), -0.2, 77,\n                      np.array([46.46099316, 23.96947246, 12.01013894]))\ntest_SFC64_triangular(\n    1234, -4, np.array([0.1, 0.5, 3, 6, 7.9]), 8,\n    np.array([4.16819741, 1.64580502, 1.16689514, -1.41563685, 7.73497789]))\ntest_SFC64_triangular(1234, -77, 0, np.array([0.1, 11, 789]),\n                      np.array([-6.16767369, -15.87593485, 106.27277449]))\ntest_SFC64_triangular(1234,\n                      2,\n                      10,\n                      53,\n                      np.array(\n                          [34.57029967, 21.63403897, 14.32154749, 6.76533020]),\n                      size=4)\n\n@test\ndef test_SFC64_uniform(seed, expected, low=0.0, high=1.0, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    result = round(gen.uniform(low, high, size), 8)\n    if size is None:\n        if (isinstance(low, int) or isinstance(low, float)) and (isinstance(\n                high, int) or isinstance(high, float)):\n            assert result == round(expected, 8)\n        else:\n            assert (result == expected).all()\n    else:\n        assert (result == expected).all()\n\ntest_SFC64_uniform(1234, 13.45119081462231, 5, 15)\ntest_SFC64_uniform(1234, 0.8606071733160079, 0.1)\ntest_SFC64_uniform(1234, 103.10452793839218, high=122)\ntest_SFC64_uniform(1234,\n                   np.array([0.84511908, 0.55138007, 0.31781911]),\n                   size=3)\ntest_SFC64_uniform(1234,\n                   np.array(\n                       [38.09231103, 26.15796285, 18.39494535, 9.11499542]),\n                   low=np.array([0.4, 3., 6., 7.]),\n                   high=45.)\ntest_SFC64_uniform(\n    1234,\n    np.array([47.32666856, 8.82208109, 13.98404090, 0.17253910, 0.19287074]),\n    high=np.array([56., 16., 44., 3.1, 0.2]))\n\n@test\ndef test_SFC64_vonmises(seed, mu, kappa, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(mu, float) or isinstance(mu, int)) and (isinstance(\n                kappa, float) or isinstance(kappa, int)):\n            assert gen.vonmises(mu, kappa) == expected\n        else:\n            assert (round(gen.vonmises(mu, kappa), 8) == expected).all()\n    else:\n        assert (round(gen.vonmises(mu, kappa, size), 8) == expected).all()\n\ntest_SFC64_vonmises(1234, 0, 4, 0.2704612302733711)\ntest_SFC64_vonmises(1234, 0.1, 0.2, -2.4556551142789758)\ntest_SFC64_vonmises(1234, -1, 0, 2.168447141870805)\ntest_SFC64_vonmises(1234,\n                    0,\n                    5,\n                    np.array([0.24236946, -0.35020145, -0.09662924]),\n                    size=3)\ntest_SFC64_vonmises(\n    1234, np.array([43.09, 18.62, 42, 16.94]), 5,\n    np.array([-0.64992769, -0.57975737, -2.07892639, -2.04431317]))\ntest_SFC64_vonmises(\n    1234, 4, np.array([0, 0.1, 7.67, 99]),\n    np.array([2.16844714, 2.36646913, -1.69291029, -2.42902277]))\n\n@test\ndef test_SFC64_wald(seed, mean, scale, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(mean, float) or isinstance(mean, int)) and (isinstance(\n                scale, float) or isinstance(scale, int)):\n            assert gen.wald(mean, scale) == expected\n        else:\n            assert (round(gen.wald(mean, scale), 8) == expected).all()\n    else:\n        assert (round(gen.wald(mean, scale, size), 8) == expected).all()\n\ntest_SFC64_wald(1234, 0.1, 0.1, 0.02646947813421463)\ntest_SFC64_wald(1234, 3, 2, 0.6175247458060582)\ntest_SFC64_wald(1234, 3, 2, np.array([0.61752475, 2.11337747, 1.27929571]), 3)\ntest_SFC64_wald(1234, np.array([0.1, 0.5, 1, 4]), 0.5,\n                np.array([0.05332567, 0.37543492, 0.37714757, 0.67411925]))\ntest_SFC64_wald(1234, 0.5, np.array([0.1, 0.5, 1, 4]),\n                np.array([0.04121767, 0.37543492, 0.30271246, 0.38842724]))\ntest_SFC64_wald(1234, np.array([0.1, 0.5, 1, 4]), np.array([3, 2, 1, 8]),\n                np.array([0.07708962, 0.43310473, 0.49528816, 2.42348396]))\n\n@test\ndef test_SFC64_weibull(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(a, float) or isinstance(a, int)):\n            assert gen.weibull(a) == expected\n        else:\n            assert (round(gen.weibull(a), 8) == expected).all()\n    else:\n        assert (round(gen.weibull(a, size), 8) == expected).all()\n\ntest_SFC64_weibull(1234, 0, 0)\ntest_SFC64_weibull(1234, 0.1, 13.601692306667198)\ntest_SFC64_weibull(1234, 1, 1.2982528797672646)\ntest_SFC64_weibull(1234, 1, np.array([1.29825288, 0.65389518, 0.02029344]), 3)\ntest_SFC64_weibull(1234, np.array([17.8, 4.32]),\n                   np.array([1.01477206, 0.90634502]))\n\n@test\ndef test_SFC64_zipf(seed, a, expected, size=None):\n    gen = rnd.Generator(rnd.SFC64(seed))\n    if size is None:\n        if (isinstance(a, float) or isinstance(a, int)):\n            assert gen.zipf(a) == expected\n        else:\n            assert (gen.zipf(a) == expected).all()\n    else:\n        assert (gen.zipf(a, size) == expected).all()\n\ntest_SFC64_zipf(1234, 1.1, 125898583)\ntest_SFC64_zipf(1234, 3, 2)\ntest_SFC64_zipf(1234, 45, np.array([1, 1, 1]), 3)\ntest_SFC64_zipf(1234, np.array([17.8, 4.32]), np.array([1, 1]))\n"
  },
  {
    "path": "test/numpy/test_dtype.codon",
    "content": "import numpy as np\n\n@test\ndef test_finfo():\n    def fi(s: Literal[str]):\n        fi16 = getattr(np.finfo(np.float16), s)\n        fi32 = getattr(np.finfo(np.float32), s)\n        fi64 = getattr(np.finfo(np.float64), s)\n        return (fi16, fi32, fi64)\n\n    def fic(s: Literal[str]):\n        fic64 = getattr(np.finfo(np.complex64), s)\n        fic128 = getattr(np.finfo(np.complex128), s)\n        return (fic64, fic128)\n\n    assert fi('bits') == (16, 32, 64)\n    assert fi('iexp') == (5, 8, 11)\n    assert fi('machep') == (-10, -23, -52)\n    assert fi('maxexp') == (16, 128, 1024)\n    assert fi('minexp') == (-14, -126, -1022)\n    assert fi('negep') == (-11, -24, -53)\n    assert fi('nexp') == (5, 8, 11)\n    assert fi('nmant') == (10, 23, 52)\n    assert fi('precision') == (3, 6, 15)\n    assert fi('eps') == (np.float16(0.000977), np.float32(1.1920929e-07), 2.220446049250313e-16)\n    assert fi('epsneg') == (np.float16(0.0004883), np.float32(5.9604645e-08), 1.1102230246251565e-16)\n    assert fi('max') == (np.float16(65500.0), np.float32(3.4028235e+38), 1.7976931348623157e+308)\n    assert fi('min') == (np.float16(-65500.0), np.float32(-3.4028235e+38), -1.7976931348623157e+308)\n    assert fi('resolution') == (np.float16(0.001), np.float32(1e-06), 1e-15)\n    assert fi('smallest_normal') == (np.float16(6.104e-05), np.float32(1.1754944e-38), 2.2250738585072014e-308)\n    assert fi('smallest_subnormal') == (np.float16(6e-08), np.float32(1e-45), np.array(1).view(float).item())\n    assert fi('tiny') == (np.float16(6.104e-05), np.float32(1.1754944e-38), 2.2250738585072014e-308)\n\n    assert fic('bits') == (32, 64)\n    assert fic('iexp') == (8, 11)\n    assert fic('machep') == (-23, -52)\n    assert fic('maxexp') == (128, 1024)\n    assert fic('minexp') == (-126, -1022)\n    assert fic('negep') == (-24, -53)\n    assert fic('nexp') == (8, 11)\n    assert fic('nmant') == (23, 52)\n    assert fic('precision') == (6, 15)\n    assert fic('eps') == (np.float32(1.1920929e-07), 2.220446049250313e-16)\n    assert fic('epsneg') == (np.float32(5.9604645e-08), 1.1102230246251565e-16)\n    assert fic('max') == (np.float32(3.4028235e+38), 1.7976931348623157e+308)\n    assert fic('min') == (np.float32(-3.4028235e+38), -1.7976931348623157e+308)\n    assert fic('resolution') == (np.float32(1e-06), 1e-15)\n    assert fic('smallest_normal') == (np.float32(1.1754944e-38), 2.2250738585072014e-308)\n    assert fic('smallest_subnormal') == (np.float32(1e-45), np.array(1).view(float).item())\n    assert fic('tiny') == (np.float32(1.1754944e-38), 2.2250738585072014e-308)\n\n    assert str(np.finfo(np.float64)) == 'Machine parameters for float64\\n---------------------------------------------------------------\\nprecision =  15   resolution = 1.0000000000000001e-15\\nmachep =    -52   eps =        2.2204460492503131e-16\\nnegep =     -53   epsneg =     1.1102230246251565e-16\\nminexp =  -1022   tiny =       2.2250738585072014e-308\\nmaxexp =   1024   max =        1.7976931348623157e+308\\nnexp =       11   min =        -max\\nsmallest_normal = 2.2250738585072014e-308   smallest_subnormal = 4.9406564584124654e-324\\n---------------------------------------------------------------\\n'\n    assert str(np.finfo(np.float32)) == 'Machine parameters for float32\\n---------------------------------------------------------------\\nprecision =   6   resolution = 1.0000000e-06\\nmachep =    -23   eps =        1.1920929e-07\\nnegep =     -24   epsneg =     5.9604645e-08\\nminexp =   -126   tiny =       1.1754944e-38\\nmaxexp =    128   max =        3.4028235e+38\\nnexp =        8   min =        -max\\nsmallest_normal = 1.1754944e-38   smallest_subnormal = 1.4012985e-45\\n---------------------------------------------------------------\\n'\n    assert str(np.finfo(np.float16)) == 'Machine parameters for float16\\n---------------------------------------------------------------\\nprecision =   3   resolution = 1.00040e-03\\nmachep =    -10   eps =        9.76562e-04\\nnegep =     -11   epsneg =     4.88281e-04\\nminexp =    -14   tiny =       6.10352e-05\\nmaxexp =     16   max =        6.55040e+04\\nnexp =        5   min =        -max\\nsmallest_normal = 6.10352e-05   smallest_subnormal = 5.96046e-08\\n---------------------------------------------------------------\\n'\n\ntest_finfo()\n\n@test\ndef test_iinfo():\n    def ii(dtype: type):\n        info = np.iinfo(dtype)\n        return (info.bits, info.min, info.max)\n\n    assert ii(np.int8) == (8, -128, 127)\n    assert ii(np.uint8) == (8, 0, 255)\n    assert ii(np.int16) == (16, -32768, 32767)\n    assert ii(np.uint16) == (16, 0, 65535)\n    assert ii(np.int32) == (32, -2147483648, 2147483647)\n    assert ii(np.uint32) == (32, 0, 4294967295)\n    assert ii(np.int64) == (64, -9223372036854775808, 9223372036854775807)\n    assert ii(np.uint64) == (64, 0, 18446744073709551615)\n    assert ii(int) == (64, -9223372036854775808, 9223372036854775807)\n\n    assert repr(np.iinfo(np.int8)) == 'iinfo(min=-128, max=127, dtype=int8)'\n    assert str(np.iinfo(np.int8)) == 'Machine parameters for int8\\n---------------------------------------------------------------\\nmin = -128\\nmax = 127\\n---------------------------------------------------------------\\n'\n    assert repr(np.iinfo(np.uint8)) == 'iinfo(min=0, max=255, dtype=uint8)'\n    assert str(np.iinfo(np.uint8)) == 'Machine parameters for uint8\\n---------------------------------------------------------------\\nmin = 0\\nmax = 255\\n---------------------------------------------------------------\\n'\n    assert repr(np.iinfo(np.int16)) == 'iinfo(min=-32768, max=32767, dtype=int16)'\n    assert str(np.iinfo(np.int16)) == 'Machine parameters for int16\\n---------------------------------------------------------------\\nmin = -32768\\nmax = 32767\\n---------------------------------------------------------------\\n'\n    assert repr(np.iinfo(np.uint16)) == 'iinfo(min=0, max=65535, dtype=uint16)'\n    assert str(np.iinfo(np.uint16)) == 'Machine parameters for uint16\\n---------------------------------------------------------------\\nmin = 0\\nmax = 65535\\n---------------------------------------------------------------\\n'\n    assert repr(np.iinfo(np.int32)) == 'iinfo(min=-2147483648, max=2147483647, dtype=int32)'\n    assert str(np.iinfo(np.int32)) == 'Machine parameters for int32\\n---------------------------------------------------------------\\nmin = -2147483648\\nmax = 2147483647\\n---------------------------------------------------------------\\n'\n    assert repr(np.iinfo(np.uint32)) == 'iinfo(min=0, max=4294967295, dtype=uint32)'\n    assert str(np.iinfo(np.uint32)) == 'Machine parameters for uint32\\n---------------------------------------------------------------\\nmin = 0\\nmax = 4294967295\\n---------------------------------------------------------------\\n'\n    assert repr(np.iinfo(np.int64)) == 'iinfo(min=-9223372036854775808, max=9223372036854775807, dtype=int64)'\n    assert str(np.iinfo(np.int64)) == 'Machine parameters for int64\\n---------------------------------------------------------------\\nmin = -9223372036854775808\\nmax = 9223372036854775807\\n---------------------------------------------------------------\\n'\n    assert repr(np.iinfo(np.uint64)) == 'iinfo(min=0, max=18446744073709551615, dtype=uint64)'\n    assert str(np.iinfo(np.uint64)) == 'Machine parameters for uint64\\n---------------------------------------------------------------\\nmin = 0\\nmax = 18446744073709551615\\n---------------------------------------------------------------\\n'\n    assert repr(np.iinfo(int)) == 'iinfo(min=-9223372036854775808, max=9223372036854775807, dtype=int64)'\n    assert str(np.iinfo(int)) == 'Machine parameters for int64\\n---------------------------------------------------------------\\nmin = -9223372036854775808\\nmax = 9223372036854775807\\n---------------------------------------------------------------\\n'\n\ntest_iinfo()\n"
  },
  {
    "path": "test/numpy/test_elision.codon",
    "content": "from numpy import ndarray, array, zeros, sqrt\n\ngetitem_count = 0\nsetitem_count = 0\n\n@extend\nclass ndarray:\n    def __getitem__(self, idx):\n        global getitem_count\n        getitem_count += 1\n        return superf(self, idx)\n\n    def __setitem__(self, idx, item):\n        global setitem_count\n        setitem_count += 1\n        return superf(self, idx, item)\n\ndef reset():\n    global getitem_count\n    global setitem_count\n    getitem_count = 0\n    setitem_count = 0\n\n@test\ndef test_simple_for_loop():\n    reset()\n    x = array([1, 2, 3, 4])\n    s = 0\n\n    for i in range(len(x)):\n        s += x[i]\n\n    assert s == 10\n    assert getitem_count == 0\n\n    s = 0\n\n    for i in range(x.size):\n        s += x[i]\n\n    assert s == 10\n    assert getitem_count == 0\n\n@test\ndef test_simple_for_loop_setitem():\n    reset()\n    x = zeros(4, dtype=int)\n\n    for i in range(len(x)):\n        x[i] = (i + 1)**2\n\n    assert (x == [1, 4, 9, 16]).all()\n    assert setitem_count == 0\n\n@test\ndef test_simple_for_loop_getset():\n    reset()\n    x = zeros(4, dtype=int)\n    s = 0\n\n    for i in range(len(x)):\n        x[i] = (i + 1)**2\n        s += x[i]\n\n    assert s == 1 + 4 + 9 + 16\n    assert (x == [1, 4, 9, 16]).all()\n    assert getitem_count == 0\n    assert setitem_count == 0\n\n@test\ndef test_simple_for_loop_getset_invalid():\n    reset()\n    x = zeros(4, dtype=int)\n    s = 0\n\n    try:\n        for i in range(len(x)):\n            x[i] = (i + 1)**2\n            i = len(x) + 1\n            s += x[i]\n        assert False\n    except IndexError:\n        pass\n\n    assert s == 0\n    assert (x == [1, 0, 0, 0]).all()\n    assert getitem_count == 1\n    assert setitem_count == 0\n\n@test\ndef test_for_loop_with_offset():\n    reset()\n    x = array([1, 2, 3, 4, 5])\n    w = 2\n    s = 0\n\n    for i in range(len(x) - w):\n        s += x[i + w]\n\n    assert s == 12\n    assert getitem_count == 0\n\n@test\ndef test_for_loop_with_offset_invalid():\n    @nonpure\n    @noinline\n    def zero():\n        return 0\n\n    @nonpure\n    @noinline\n    def two():\n        return 2\n\n    reset()\n    x = array([1, 2, 3, 4, 5])\n    w = two()\n    s = 0\n\n    for i in range(len(x) - w):\n        w = zero()  # reaching-def analysis should catch this\n        s += x[i + w]\n\n    assert s == 6\n    assert getitem_count == 3\n\n@test\ndef test_varied_stride_access():\n    reset()\n    x = array([0, 1, 2, 3, 4, 5])\n    s = 0\n\n    for i in range(0, len(x), 2):\n        s += x[i]\n\n    assert s == 0 + 2 + 4\n    assert getitem_count == 0\n\n@test\ndef test_out_of_bounds_in_loop():\n    reset()\n    x = array([1, 2, 3])\n\n    try:\n        for i in range(len(x)):\n            i = 3\n            x[i] = 42\n        assert False\n    except IndexError:\n        assert True\n\n@test\ndef test_variable_bounds_failure():\n    reset()\n    x = array([10, 20, 30])\n    w = 3\n\n    try:\n        for i in range(len(x) - w + 1):\n            _ = x[i + w]  # This will fail if w == len(x)\n        assert False\n    except IndexError:\n        assert True\n\n@test\ndef test_empty_array():\n    reset()\n    x = zeros((0,), dtype=int)\n    s = 0\n\n    for i in range(len(x)):\n        s += x[i]\n\n    assert s == 0\n    assert getitem_count == 0\n\n@test\ndef test_single_element_array():\n    reset()\n    x = array([42])\n    s = 0\n\n    for i in range(len(x)):\n        s += x[i]\n\n    assert s == 42\n    assert getitem_count == 0\n\n@test\ndef test_inplace_update():\n    reset()\n    x = array([1, 2, 3])\n\n    for i in range(len(x)):\n        x[i] += i + 1\n        x[i] *= 2\n\n    assert (x == [4, 8, 12]).all()\n    assert getitem_count == 0\n    assert setitem_count == 0\n\n@test\ndef test_mixed_types():\n    reset()\n    x = array([1, 2, 3, 4])\n\n    for i in range(len(x)):\n        x[i] = x[i] + sqrt(x[i])\n\n    assert (x == [2, 3, 4, 6]).all()\n    assert getitem_count == 0\n    assert setitem_count == 0\n\n@test\ndef test_strided():\n    x = array([1, 2, 3, 4])[::2]\n    reset()\n\n    for i in range(len(x)):\n        x[i] = 42\n\n    assert (x == [42, 42]).all()\n    assert getitem_count == 0\n    assert setitem_count == 0\n\nif not __debug__:\n    test_simple_for_loop()\n    test_simple_for_loop_setitem()\n    test_simple_for_loop_getset()\n    test_simple_for_loop_getset_invalid()\n    test_for_loop_with_offset()\n    test_for_loop_with_offset_invalid()\n    test_varied_stride_access()\n    test_out_of_bounds_in_loop()\n    test_variable_bounds_failure()\n    test_empty_array()\n    test_single_element_array()\n    test_inplace_update()\n    test_mixed_types()\n    test_strided()\n"
  },
  {
    "path": "test/numpy/test_fft.codon",
    "content": "import numpy as np\nimport numpy.fft as fft\nimport numpy.random as rnd\n\ndef fft1(x):\n    L = len(x)\n    phase = np.pi * (np.arange(L) / L) * -2j\n    phase = np.arange(L).reshape(-1, 1) * phase\n    return np.sum(x*np.exp(phase), axis=1)\n\n@test\ndef test_fft_n():\n    try:\n        fft.fft([1, 2, 3], 0)\n        assert False\n    except ValueError:\n        pass\n\n@test\ndef test_fft1d_identity():\n    gen = rnd.default_rng(seed=0)\n    maxlen = 512\n    x = gen.random(maxlen) + gen.random(maxlen)*1j\n    xr = gen.random(maxlen)\n    for i in range(1, maxlen):\n        assert np.allclose(np.fft.ifft(np.fft.fft(x[0:i])), x[0:i],\n                           atol=1e-12)\n        assert np.allclose(np.fft.irfft(np.fft.rfft(xr[0:i]), i),\n                           xr[0:i], atol=1e-12)\n\n@test\ndef test_fft1d_identity_long_short(dtype: type):\n    # Test with explicitly given number of points, both for n\n    # smaller and for n larger than the input size.\n    gen = rnd.default_rng(seed=0)\n    maxlen = 16\n    atol = 4 * float(np.spacing(np.array(1., dtype=dtype)))\n    x = gen.random(maxlen).astype(dtype) + gen.random(maxlen).astype(dtype)*1j\n    xx = np.concatenate([x, np.zeros_like(x)])\n    xr = gen.random(maxlen).astype(dtype)\n    xxr = np.concatenate([xr, np.zeros_like(xr)])\n    for i in range(1, maxlen*2):\n        check_c = np.fft.ifft(np.fft.fft(x, n=i), n=i)\n        assert check_c.real.dtype is dtype\n        assert np.allclose(check_c, xx[0:i], atol=atol, rtol=0)\n        check_r = np.fft.irfft(np.fft.rfft(xr, n=i), n=i)\n        assert check_r.dtype is dtype\n        assert np.allclose(check_r, xxr[0:i], atol=atol, rtol=0)\n\n@test\ndef test_fft1d_identity_long_short_reversed(dtype: type):\n    # Also test explicitly given number of points in reversed order.\n    gen = rnd.default_rng(seed=1)\n    maxlen = 16\n    atol = 5 * float(np.spacing(np.array(1., dtype=dtype)))\n    x = gen.random(maxlen).astype(dtype) + gen.random(maxlen).astype(dtype)*1j\n    xx = np.concatenate([x, np.zeros_like(x)])\n    for i in range(1, maxlen*2):\n        check_via_c = np.fft.fft(np.fft.ifft(x, n=i), n=i)\n        assert check_via_c.dtype is x.dtype\n        assert np.allclose(check_via_c, xx[0:i], atol=atol, rtol=0)\n        # For irfft, we can neither recover the imaginary part of\n        # the first element, nor the imaginary part of the last\n        # element if npts is even.  So, set to 0 for the comparison.\n        y = x.copy()\n        n = i // 2 + 1\n        y.imag[0] = 0\n        if i % 2 == 0:\n            y.imag[n-1:] = 0\n        yy = np.concatenate([y, np.zeros_like(y)])\n        check_via_r = np.fft.rfft(np.fft.irfft(x, n=i), n=i)\n        assert check_via_r.dtype is x.dtype\n        assert np.allclose(check_via_r, yy[0:n], atol=atol, rtol=0)\n\n@test\ndef test_fft():\n    gen = rnd.default_rng(seed=2)\n    x = gen.random(30) + gen.random(30)*1j\n    assert np.allclose(fft1(x), np.fft.fft(x), atol=1e-6)\n    assert np.allclose(fft1(x), np.fft.fft(x, norm=\"backward\"), atol=1e-6)\n    assert np.allclose(fft1(x) / np.sqrt(30),\n                       np.fft.fft(x, norm=\"ortho\"), atol=1e-6)\n    assert np.allclose(fft1(x) / 30.,\n                       np.fft.fft(x, norm=\"forward\"), atol=1e-6)\n\n@test\ndef test_fft_out_argument(dtype: type, transpose: bool, axis: int):\n    def zeros_like(x):\n        if transpose:\n            return np.zeros_like(x.T).T\n        else:\n            return np.zeros_like(x)\n\n    gen = rnd.default_rng(seed=2)\n\n    # tests below only test the out parameter\n    if dtype is complex:\n        y = gen.random((10, 20)) + gen.random((10, 20))*1j\n        fft, ifft = np.fft.fft, np.fft.ifft\n    else:\n        y = gen.random((10, 20))\n        fft, ifft = np.fft.rfft, np.fft.irfft\n\n    expected = fft(y, axis=axis)\n    out = zeros_like(expected)\n    result = fft(y, out=out, axis=axis)\n    assert result.data == out.data\n    assert np.array_equal(result, expected)\n\n    expected2 = ifft(expected, axis=axis)\n    out2 = out if dtype is complex else zeros_like(expected2)\n    result2 = ifft(out, out=out2, axis=axis)\n    assert result2.data == out2.data\n    assert np.array_equal(result2, expected2)\n\n@test\ndef test_fft_inplace_out(axis: int):\n    gen = rnd.default_rng(seed=3)\n    # Test some weirder in-place combinations\n    y = gen.random((20, 20)) + gen.random((20, 20))*1j\n    # Fully in-place.\n    y1 = y.copy()\n    expected1 = np.fft.fft(y1, axis=axis)\n    result1 = np.fft.fft(y1, axis=axis, out=y1)\n    assert result1.data == y1.data\n    assert np.array_equal(result1, expected1)\n    # In-place of part of the array; rest should be unchanged.\n    y2 = y.copy()\n    out2 = y2[:10] if axis == 0 else y2[:, :10]\n    expected2 = np.fft.fft(y2, n=10, axis=axis)\n    result2 = np.fft.fft(y2, n=10, axis=axis, out=out2)\n    assert result2.data == out2.data\n    assert np.array_equal(result2, expected2)\n    if axis == 0:\n        assert np.array_equal(y2[10:], y[10:])\n    else:\n        assert np.array_equal(y2[:, 10:], y[:, 10:])\n    # In-place of another part of the array.\n    y3 = y.copy()\n    y3_sel = y3[5:] if axis == 0 else y3[:, 5:]\n    out3 = y3[5:15] if axis == 0 else y3[:, 5:15]\n    expected3 = np.fft.fft(y3_sel, n=10, axis=axis)\n    result3 = np.fft.fft(y3_sel, n=10, axis=axis, out=out3)\n    assert result3.data == out3.data\n    assert np.array_equal(result3, expected3)\n    if axis == 0:\n        assert np.array_equal(y3[:5], y[:5])\n        assert np.array_equal(y3[15:], y[15:])\n    else:\n        assert np.array_equal(y3[:, :5], y[:, :5])\n        assert np.array_equal(y3[:, 15:], y[:, 15:])\n    # In-place with n > nin; rest should be unchanged.\n    y4 = y.copy()\n    y4_sel = y4[:10] if axis == 0 else y4[:, :10]\n    out4 = y4[:15] if axis == 0 else y4[:, :15]\n    expected4 = np.fft.fft(y4_sel, n=15, axis=axis)\n    result4 = np.fft.fft(y4_sel, n=15, axis=axis, out=out4)\n    assert result4.data == out4.data\n    assert np.array_equal(result4, expected4)\n    if axis == 0:\n        assert np.array_equal(y4[15:], y[15:])\n    else:\n        assert np.array_equal(y4[:, 15:], y[:, 15:])\n    # Overwrite in a transpose.\n    y5 = y.copy()\n    out5 = y5.T\n    result5 = np.fft.fft(y5, axis=axis, out=out5)\n    assert result5.data == out5.data\n    # assert np.array_equal(result5, expected1)  # TODO: we don't check for this\n    # Reverse strides.\n    y6 = y.copy()\n    out6 = y6[::-1] if axis == 0 else y6[:, ::-1]\n    result6 = np.fft.fft(y6, axis=axis, out=out6)\n    assert result6.data == out6.data\n    assert np.array_equal(result6, expected1)\n\n@test\ndef test_fft_bad_out():\n    x = np.arange(30.)\n    try:\n        np.fft.fft(x, out=np.zeros(5, complex))\n        assert False\n    except ValueError:\n        pass\n\n@test\ndef test_ifft(norm: Optional[str]):\n    gen = rnd.default_rng(seed=4)\n    x = gen.random(30) + gen.random(30)*1j\n    assert np.allclose(\n        x, np.fft.ifft(np.fft.fft(x, norm=norm), norm=norm),\n        atol=1e-6)\n    try:\n        np.fft.ifft(x[0:0], norm=norm)\n        assert False\n    except ValueError:\n        pass\n\n@test\ndef test_fft2():\n    gen = rnd.default_rng(seed=5)\n    x = gen.random((30, 20)) + gen.random((30, 20))*1j\n    assert np.allclose(np.fft.fft(np.fft.fft(x, axis=1), axis=0),\n                       np.fft.fft2(x), atol=1e-6)\n    assert np.allclose(np.fft.fft2(x),\n                       np.fft.fft2(x, norm=\"backward\"), atol=1e-6)\n    assert np.allclose(np.fft.fft2(x) / np.sqrt(30 * 20),\n                       np.fft.fft2(x, norm=\"ortho\"), atol=1e-6)\n    assert np.allclose(np.fft.fft2(x) / (30. * 20.),\n                       np.fft.fft2(x, norm=\"forward\"), atol=1e-6)\n\n@test\ndef test_ifft2():\n    gen = rnd.default_rng(seed=6)\n    x = gen.random((30, 20)) + gen.random((30, 20))*1j\n    assert np.allclose(np.fft.ifft(np.fft.ifft(x, axis=1), axis=0),\n                       np.fft.ifft2(x), atol=1e-6)\n    assert np.allclose(np.fft.ifft2(x),\n                       np.fft.ifft2(x, norm=\"backward\"), atol=1e-6)\n    assert np.allclose(np.fft.ifft2(x) * np.sqrt(30 * 20),\n                    np.fft.ifft2(x, norm=\"ortho\"), atol=1e-6)\n    assert np.allclose(np.fft.ifft2(x) * (30. * 20.),\n                       np.fft.ifft2(x, norm=\"forward\"), atol=1e-6)\n\n@test\ndef test_fftn():\n    gen = rnd.default_rng(seed=7)\n    x = gen.random((30, 20, 10)) + gen.random((30, 20, 10))*1j\n    assert np.allclose(\n        np.fft.fft(np.fft.fft(np.fft.fft(x, axis=2), axis=1), axis=0),\n        np.fft.fftn(x), atol=1e-6)\n    assert np.allclose(np.fft.fftn(x),\n                       np.fft.fftn(x, norm=\"backward\"), atol=1e-6)\n    assert np.allclose(np.fft.fftn(x) / np.sqrt(30 * 20 * 10),\n                       np.fft.fftn(x, norm=\"ortho\"), atol=1e-6)\n    assert np.allclose(np.fft.fftn(x) / (30. * 20. * 10.),\n                       np.fft.fftn(x, norm=\"forward\"), atol=1e-6)\n\n@test\ndef test_ifftn():\n    gen = rnd.default_rng(seed=8)\n    x = gen.random((30, 20, 10)) + gen.random((30, 20, 10))*1j\n    assert np.allclose(\n        np.fft.ifft(np.fft.ifft(np.fft.ifft(x, axis=2), axis=1), axis=0),\n        np.fft.ifftn(x), atol=1e-6)\n    assert np.allclose(np.fft.ifftn(x),\n                       np.fft.ifftn(x, norm=\"backward\"), atol=1e-6)\n    assert np.allclose(np.fft.ifftn(x) * np.sqrt(30 * 20 * 10),\n                       np.fft.ifftn(x, norm=\"ortho\"), atol=1e-6)\n    assert np.allclose(np.fft.ifftn(x) * (30. * 20. * 10.),\n                       np.fft.ifftn(x, norm=\"forward\"), atol=1e-6)\n\n@test\ndef test_rfft():\n    gen = rnd.default_rng(seed=9)\n    x = gen.random(30)\n    for n in [x.size, 2*x.size]:\n        for norm in [None, 'backward', 'ortho', 'forward']:\n            assert np.allclose(\n                np.fft.fft(x, n=n, norm=norm)[:(n//2 + 1)],\n                np.fft.rfft(x, n=n, norm=norm), atol=1e-6)\n        assert np.allclose(\n            np.fft.rfft(x, n=n),\n            np.fft.rfft(x, n=n, norm=\"backward\"), atol=1e-6)\n        assert np.allclose(\n            np.fft.rfft(x, n=n) / np.sqrt(n),\n            np.fft.rfft(x, n=n, norm=\"ortho\"), atol=1e-6)\n        assert np.allclose(\n            np.fft.rfft(x, n=n) / n,\n            np.fft.rfft(x, n=n, norm=\"forward\"), atol=1e-6)\n\n@test\ndef test_rfft_even():\n    x = np.arange(8)\n    n = 4\n    y = np.fft.rfft(x, n)\n    assert np.allclose(y, np.fft.fft(x[:n])[:n//2 + 1], rtol=1e-14)\n\n@test\ndef test_rfft_odd():\n    x = np.array([1, 0, 2, 3, -3])\n    y = np.fft.rfft(x)\n    assert np.allclose(y, np.fft.fft(x)[:3], rtol=1e-14)\n\n@test\ndef test_irfft():\n    gen = rnd.default_rng(seed=10)\n    x = gen.random(30)\n    assert np.allclose(x, np.fft.irfft(np.fft.rfft(x)), atol=1e-6)\n    assert np.allclose(x, np.fft.irfft(np.fft.rfft(x, norm=\"backward\"),\n                       norm=\"backward\"), atol=1e-6)\n    assert np.allclose(x, np.fft.irfft(np.fft.rfft(x, norm=\"ortho\"),\n                       norm=\"ortho\"), atol=1e-6)\n    assert np.allclose(x, np.fft.irfft(np.fft.rfft(x, norm=\"forward\"),\n                       norm=\"forward\"), atol=1e-6)\n\n@test\ndef test_rfft2():\n    gen = rnd.default_rng(seed=11)\n    x = gen.random((30, 20))\n    assert np.allclose(np.fft.fft2(x)[:, :11], np.fft.rfft2(x), atol=1e-6)\n    assert np.allclose(np.fft.rfft2(x),\n                       np.fft.rfft2(x, norm=\"backward\"), atol=1e-6)\n    assert np.allclose(np.fft.rfft2(x) / np.sqrt(30 * 20),\n                       np.fft.rfft2(x, norm=\"ortho\"), atol=1e-6)\n    assert np.allclose(np.fft.rfft2(x) / (30. * 20.),\n                       np.fft.rfft2(x, norm=\"forward\"), atol=1e-6)\n\n@test\ndef test_irfft2():\n    gen = rnd.default_rng(seed=12)\n    x = gen.random((30, 20))\n    assert np.allclose(x, np.fft.irfft2(np.fft.rfft2(x)), atol=1e-6)\n    assert np.allclose(x, np.fft.irfft2(np.fft.rfft2(x, norm=\"backward\"),\n                       norm=\"backward\"), atol=1e-6)\n    assert np.allclose(x, np.fft.irfft2(np.fft.rfft2(x, norm=\"ortho\"),\n                       norm=\"ortho\"), atol=1e-6)\n    assert np.allclose(x, np.fft.irfft2(np.fft.rfft2(x, norm=\"forward\"),\n                       norm=\"forward\"), atol=1e-6)\n\n@test\ndef test_rfftn():\n    gen = rnd.default_rng(seed=13)\n    x = gen.random((30, 20, 10))\n    assert np.allclose(np.fft.fftn(x)[:, :, :6], np.fft.rfftn(x), atol=1e-6)\n    assert np.allclose(np.fft.rfftn(x),\n                       np.fft.rfftn(x, norm=\"backward\"), atol=1e-6)\n    assert np.allclose(np.fft.rfftn(x) / np.sqrt(30 * 20 * 10),\n                       np.fft.rfftn(x, norm=\"ortho\"), atol=1e-6)\n    assert np.allclose(np.fft.rfftn(x) / (30. * 20. * 10.),\n                       np.fft.rfftn(x, norm=\"forward\"), atol=1e-6)\n\n@test\ndef test_irfftn():\n    gen = rnd.default_rng(seed=14)\n    x = gen.random((30, 20, 10))\n    assert np.allclose(x, np.fft.irfftn(np.fft.rfftn(x)), atol=1e-6)\n    assert np.allclose(x, np.fft.irfftn(np.fft.rfftn(x, norm=\"backward\"),\n                       norm=\"backward\"), atol=1e-6)\n    assert np.allclose(x, np.fft.irfftn(np.fft.rfftn(x, norm=\"ortho\"),\n                       norm=\"ortho\"), atol=1e-6)\n    assert np.allclose(x, np.fft.irfftn(np.fft.rfftn(x, norm=\"forward\"),\n                       norm=\"forward\"), atol=1e-6)\n\n@test\ndef test_hfft():\n    gen = rnd.default_rng(seed=15)\n    x = gen.random(14) + gen.random(14)*1j\n    x_herm = np.concatenate((gen.random(1), x, gen.random(1)))\n    x = np.concatenate((x_herm, x[::-1].conj()))\n    assert np.allclose(np.fft.fft(x), np.fft.hfft(x_herm), atol=1e-6)\n    assert np.allclose(np.fft.hfft(x_herm),\n                       np.fft.hfft(x_herm, norm=\"backward\"), atol=1e-6)\n    assert np.allclose(np.fft.hfft(x_herm) / np.sqrt(30),\n                       np.fft.hfft(x_herm, norm=\"ortho\"), atol=1e-6)\n    assert np.allclose(np.fft.hfft(x_herm) / 30.,\n                       np.fft.hfft(x_herm, norm=\"forward\"), atol=1e-6)\n\n@test\ndef test_ihfft():\n    gen = rnd.default_rng(seed=16)\n    x = gen.random(14) + gen.random(14)*1j\n    x_herm = np.concatenate((gen.random(1), x, gen.random(1)))\n    x = np.concatenate((x_herm, x[::-1].conj()))\n    assert np.allclose(x_herm, np.fft.ihfft(np.fft.hfft(x_herm)), atol=1e-6)\n    assert np.allclose(x_herm, np.fft.ihfft(np.fft.hfft(x_herm,\n                       norm=\"backward\"), norm=\"backward\"), atol=1e-6)\n    assert np.allclose(x_herm, np.fft.ihfft(np.fft.hfft(x_herm,\n                       norm=\"ortho\"), norm=\"ortho\"), atol=1e-6)\n    assert np.allclose(x_herm, np.fft.ihfft(np.fft.hfft(x_herm,\n                       norm=\"forward\"), norm=\"forward\"), atol=1e-6)\n\n@test\ndef test_axes(op):\n    gen = rnd.default_rng(seed=17)\n    x = gen.random((30, 20, 10))\n    axes = [(0, 1, 2), (0, 2, 1), (1, 0, 2), (1, 2, 0), (2, 0, 1), (2, 1, 0)]\n    for a in axes:\n        op_tr = op(np.transpose(x, a))\n        tr_op = np.transpose(op(x, axes=a), a)\n        assert np.allclose(op_tr, tr_op, atol=1e-6)\n\n@test\ndef test_s_negative_1(op):\n    x = np.arange(100).reshape(10, 10)\n    # should use the whole input array along the first axis\n    assert op(x, s=(-1, 5), axes=(0, 1)).shape == (10, 5)\n\n@test\ndef test_s_axes_none(op):\n    x = np.arange(100).reshape(10, 10)\n    # Should test warning here:\n    op(x, s=(-1, 5))\n\n@test\ndef test_s_axes_none_2D(op):\n    x = np.arange(100).reshape(10, 10)\n    # Should test warning here:\n    op(x, s=(-1, 5), axes=None)\n\n@test\ndef test_all_1d_norm_preserving():\n    gen = rnd.default_rng(seed=18)\n    # verify that round-trip transforms are norm-preserving\n    x = gen.random(30)\n    x_norm = np.linalg.norm(x)\n    n = x.size * 2\n    func_pairs = ((np.fft.fft, np.fft.ifft),\n                  (np.fft.rfft, np.fft.irfft),\n                  # hfft: order so the first function takes x.size samples\n                  #       (necessary for comparison to x_norm above)\n                  (np.fft.ihfft, np.fft.hfft),\n                  )\n    for forw, back in func_pairs:\n        for n in [x.size, 2*x.size]:\n            for norm in [None, 'backward', 'ortho', 'forward']:\n                tmp = forw(x, n=n, norm=norm)\n                tmp = back(tmp, n=n, norm=norm)\n                assert np.allclose(x_norm,\n                                   np.linalg.norm(tmp), atol=1e-6)\n\n@test\ndef test_fftn_out_argument(dtype: type, transpose: bool, axes):\n    def zeros_like(x):\n        if transpose:\n            return np.zeros_like(x.T).T\n        else:\n            return np.zeros_like(x)\n\n    gen = rnd.default_rng(seed=19)\n    # tests below only test the out parameter\n    if dtype is complex:\n        x = gen.random((10, 5, 6)) + gen.random((10, 5, 6))*1j\n        fft, ifft = np.fft.fftn, np.fft.ifftn\n    else:\n        x = gen.random((10, 5, 6))\n        fft, ifft = np.fft.rfftn, np.fft.irfftn\n\n    expected = fft(x, axes=axes)\n    out = zeros_like(expected)\n    result = fft(x, out=out, axes=axes)\n    assert result.data == out.data\n    assert np.array_equal(result, expected)\n\n    expected2 = ifft(expected, axes=axes)\n    out2 = out if dtype is complex else zeros_like(expected2)\n    result2 = ifft(out, out=out2, axes=axes)\n    assert result2.data == out2.data\n    assert np.array_equal(result2, expected2)\n\n@test\ndef test_fftn_out_and_s_interaction(fft, rfftn: Literal[bool]):\n    # With s, shape varies, so generally one cannot pass in out.\n    gen = rnd.default_rng(seed=20)\n    if rfftn:\n        x = gen.random((10, 5, 6))\n    else:\n        x = gen.random((10, 5, 6)) + gen.random((10, 5, 6))*1j\n    try:\n        fft(x, out=np.zeros_like(x, dtype=complex), s=(3, 3, 3), axes=(0, 1, 2))\n        assert False\n    except ValueError:\n        pass\n    # Except on the first axis done (which is the last of axes).\n    s = (10, 5, 5)\n    expected = fft(x, s=s, axes=(0, 1, 2))\n    out = np.zeros_like(expected)\n    result = fft(x, s=s, axes=(0, 1, 2), out=out)\n    assert result.data == out.data\n    assert np.array_equal(result, expected)\n\n@test\ndef test_irfftn_out_and_s_interaction(s):\n    gen = rnd.default_rng(seed=21)\n    # Since for irfftn, the output is real and thus cannot be used for\n    # intermediate steps, it should always work.\n    x = gen.random((9, 5, 6, 2)) + gen.random((9, 5, 6, 2))*1j\n    expected = np.fft.irfftn(x, s=s, axes=(0, 1, 2))\n    out = np.zeros_like(expected)\n    result = np.fft.irfftn(x, s=s, axes=(0, 1, 2), out=out)\n    assert result.data == out.data\n    assert np.array_equal(result, expected)\n\n@test\ndef test_fft_with_order(dtype: type, order: str, fft, fftname: Literal[str]):\n    def eps(dtype: type):\n        if dtype is complex or dtype is float:\n            return 2.220446049250313e-16\n        elif dtype is complex64 or dtype is float32:\n            return 1.1920929e-07\n        else:\n            compile_error(\"unknown type for eps\")\n\n    # Check that FFT/IFFT produces identical results for C, Fortran and\n    # non contiguous arrays\n    gen = rnd.default_rng(seed=22)\n    X = gen.random((8, 7, 13)).astype(dtype, copy=False)\n    # See discussion in pull/14178\n    _tol = 8.0 * np.sqrt(np.log2(X.size)) * eps(X.dtype)\n    if order == 'F':\n        Y = np.asfortranarray(X)\n    else:\n        # Make a non contiguous array\n        Y = X[::-1]\n        X = np.ascontiguousarray(X[::-1])\n\n    if fftname[-3:] == 'fft':\n        for axis in range(3):\n            X_res = fft(X, axis=axis)\n            Y_res = fft(Y, axis=axis)\n            assert np.allclose(X_res, Y_res, atol=_tol, rtol=_tol)\n    elif fftname[-4:] == 'fft2' or fftname[-4:] == 'fftn':\n        for ax in ((0, 1), (1, 2), (0, 2)):\n            X_res = fft(X, axes=ax)\n            Y_res = fft(Y, axes=ax)\n            assert np.allclose(X_res, Y_res, atol=_tol, rtol=_tol)\n        if fftname[-4:] == 'fftn':\n            for ax in ((0,), (1,), (2,), None):\n                X_res = fft(X, axes=ax)\n                Y_res = fft(Y, axes=ax)\n                assert np.allclose(X_res, Y_res, atol=_tol, rtol=_tol)\n    else:\n        raise ValueError()\n\n@test\ndef test_fft_output_order(order: str, n):\n    gen = rnd.default_rng(seed=22)\n    x = gen.random(10)\n    x = np.asarray(x, dtype=np.complex64, order=order)\n    res = np.fft.fft(x, n=n)\n    assert res.flags.c_contiguous == x.flags.c_contiguous\n    assert res.flags.f_contiguous == x.flags.f_contiguous\n\n@test\ndef test_irfft_with_n_1_regression():\n    # Regression test for gh-25661\n    x = np.arange(10)\n    np.fft.irfft(x, n=1)\n    np.fft.hfft(x, n=1)\n    np.fft.irfft(np.array([0], complex), n=10)\n\n@test\ndef test_irfft_with_n_large_regression():\n    # Regression test for gh-25679\n    x = np.arange(5) * (1 + 1j)\n    result = np.fft.hfft(x, n=10)\n    expected = np.array([20., 9.91628173, -11.8819096, 7.1048486,\n                         -6.62459848, 4., -3.37540152, -0.16057669,\n                         1.8819096, -20.86055364])\n    assert np.allclose(result, expected)\n\n@test\ndef test_fft_with_integer_or_bool_input(data, fft):\n    # Regression test for gh-25819\n    result = fft(data)\n    float_data = data.astype(float)\n    expected = fft(float_data)\n    assert np.array_equal(result, expected)\n\n\ntest_fft_n()\ntest_fft1d_identity()\ntest_fft1d_identity_long_short(np.float32)\ntest_fft1d_identity_long_short(np.float64)\ntest_fft1d_identity_long_short_reversed(np.float32)\ntest_fft1d_identity_long_short_reversed(np.float64)\ntest_fft()\n\nfor axis in (0, 1):\n    for transpose in (False, True):\n        test_fft_out_argument(complex, transpose, axis)\n        test_fft_out_argument(float, transpose, axis)\n\ntest_fft_inplace_out(0)\ntest_fft_inplace_out(1)\ntest_fft_bad_out()\ntest_ifft(None)\ntest_ifft('backward')\ntest_ifft('ortho')\ntest_ifft('forward')\ntest_fft2()\ntest_ifft2()\ntest_fftn()\ntest_ifftn()\ntest_rfft()\ntest_rfft_even()\ntest_rfft_odd()\ntest_irfft()\ntest_rfft2()\ntest_irfft2()\ntest_rfftn()\ntest_irfftn()\ntest_hfft()\ntest_ihfft()\n\nfor op in (np.fft.fftn, np.fft.ifftn,\n           np.fft.rfftn, np.fft.irfftn):\n    test_axes(op)\n\nfor op in (np.fft.fftn, np.fft.ifftn,\n           np.fft.fft2, np.fft.ifft2):\n    test_s_negative_1(op)\n\nfor op in (np.fft.fftn, np.fft.ifftn,\n           np.fft.rfftn, np.fft.irfftn):\n    test_s_axes_none(op)\n\nfor op in (np.fft.fft2, np.fft.ifft2):\n    test_s_axes_none_2D(op)\n\ntest_all_1d_norm_preserving()\n\nfor axes in ((0, 1), (0, 2), None):\n    for transpose in (False, True):\n        test_fftn_out_argument(complex, transpose, axes)\n        test_fftn_out_argument(float, transpose, axes)\n\ntest_fftn_out_and_s_interaction(np.fft.fftn, rfftn=False)\ntest_fftn_out_and_s_interaction(np.fft.ifftn, rfftn=False)\ntest_fftn_out_and_s_interaction(np.fft.rfftn, rfftn=True)\ntest_irfftn_out_and_s_interaction((9, 5, 5))\ntest_irfftn_out_and_s_interaction((3, 3, 3))\n\nfor order in ('F', 'non-contiguous'):\n    test_fft_with_order(np.float32, order, np.fft.fft, 'fft')\n    test_fft_with_order(np.float64, order, np.fft.fft, 'fft')\n    test_fft_with_order(np.complex64, order, np.fft.fft, 'fft')\n    test_fft_with_order(np.complex128, order, np.fft.fft, 'fft')\n\n    test_fft_with_order(np.float32, order, np.fft.fft2, 'fft2')\n    test_fft_with_order(np.float64, order, np.fft.fft2, 'fft2')\n    test_fft_with_order(np.complex64, order, np.fft.fft2, 'fft2')\n    test_fft_with_order(np.complex128, order, np.fft.fft2, 'fft2')\n\n    test_fft_with_order(np.float32, order, np.fft.fftn, 'fftn')\n    test_fft_with_order(np.float64, order, np.fft.fftn, 'fftn')\n    test_fft_with_order(np.complex64, order, np.fft.fftn, 'fftn')\n    test_fft_with_order(np.complex128, order, np.fft.fftn, 'fftn')\n\n    test_fft_with_order(np.float32, order, np.fft.ifft, 'ifft')\n    test_fft_with_order(np.float64, order, np.fft.ifft, 'ifft')\n    test_fft_with_order(np.complex64, order, np.fft.ifft, 'ifft')\n    test_fft_with_order(np.complex128, order, np.fft.ifft, 'ifft')\n\n    test_fft_with_order(np.float32, order, np.fft.ifft2, 'ifft2')\n    test_fft_with_order(np.float64, order, np.fft.ifft2, 'ifft2')\n    test_fft_with_order(np.complex64, order, np.fft.ifft2, 'ifft2')\n    test_fft_with_order(np.complex128, order, np.fft.ifft2, 'ifft2')\n\n    test_fft_with_order(np.float32, order, np.fft.ifftn, 'ifftn')\n    test_fft_with_order(np.float64, order, np.fft.ifftn, 'ifftn')\n    test_fft_with_order(np.complex64, order, np.fft.ifftn, 'ifftn')\n    test_fft_with_order(np.complex128, order, np.fft.ifftn, 'ifftn')\n\nfor order in ('F', 'C'):\n    for n in [None, 7, 12]:\n        test_fft_output_order(order, n)\n\ntest_irfft_with_n_1_regression()\ntest_irfft_with_n_large_regression()\n\nfor ffti in (np.fft.fft, np.fft.ifft, np.fft.rfft, np.fft.irfft):\n    for data in (np.array([False, True, False]),\n                 np.arange(10, dtype=np.uint8),\n                 np.arange(5, dtype=np.int16)):\n        test_fft_with_integer_or_bool_input(data, ffti)\n\n\n# Helper Tests\n\n@test\ndef TestFFTShift_test_definition():\n    x = [0, 1, 2, 3, 4, -4, -3, -2, -1]\n    y = [-4, -3, -2, -1, 0, 1, 2, 3, 4]\n    assert np.allclose(fft.fftshift(x), y)\n    assert np.allclose(fft.ifftshift(y), x)\n    x = [0, 1, 2, 3, 4, -5, -4, -3, -2, -1]\n    y = [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4]\n    assert np.allclose(fft.fftshift(x), y)\n    assert np.allclose(fft.ifftshift(y), x)\n\n@test\ndef TestFFTShift_test_inverse():\n    for n in [1, 4, 9, 100, 211]:\n        x = np.random.random((n,))\n        assert np.allclose(fft.ifftshift(fft.fftshift(x)), x)\n\n@test\ndef TestFFTShift_test_axes_keyword():\n    freqs = [[0, 1, 2], [3, 4, -4], [-3, -2, -1]]\n    shifted = [[-1, -3, -2], [2, 0, 1], [-4, 3, 4]]\n    assert np.allclose(fft.fftshift(freqs, axes=(0, 1)), shifted)\n    assert np.allclose(fft.fftshift(freqs, axes=0),\n                              fft.fftshift(freqs, axes=(0,)))\n    assert np.allclose(fft.ifftshift(shifted, axes=(0, 1)), freqs)\n    assert np.allclose(fft.ifftshift(shifted, axes=0),\n                              fft.ifftshift(shifted, axes=(0,)))\n\n    assert np.allclose(fft.fftshift(freqs), shifted)\n    assert np.allclose(fft.ifftshift(shifted), freqs)\n\n@test\ndef TestFFTShift_test_uneven_dims():\n    \"\"\" Test 2D input, which has uneven dimension sizes \"\"\"\n    freqs = [\n        [0, 1],\n        [2, 3],\n        [4, 5]\n    ]\n\n    # shift in dimension 0\n    shift_dim0 = [\n        [4, 5],\n        [0, 1],\n        [2, 3]\n    ]\n    assert np.allclose(fft.fftshift(freqs, axes=0), shift_dim0)\n    assert np.allclose(fft.ifftshift(shift_dim0, axes=0), freqs)\n    assert np.allclose(fft.fftshift(freqs, axes=(0,)), shift_dim0)\n    assert np.allclose(fft.ifftshift(shift_dim0, axes=(0,)), freqs)\n\n    # shift in dimension 1\n    shift_dim1 = [\n        [1, 0],\n        [3, 2],\n        [5, 4]\n    ]\n    assert np.allclose(fft.fftshift(freqs, axes=1), shift_dim1)\n    assert np.allclose(fft.ifftshift(shift_dim1, axes=1), freqs)\n\n    # shift in both dimensions\n    shift_dim_both = [\n        [5, 4],\n        [1, 0],\n        [3, 2]\n    ]\n    assert np.allclose(fft.fftshift(freqs, axes=(0, 1)), shift_dim_both)\n    assert np.allclose(fft.ifftshift(shift_dim_both, axes=(0, 1)), freqs)\n    assert np.allclose(fft.fftshift(freqs, axes=(0, 1)), shift_dim_both)\n    assert np.allclose(fft.ifftshift(shift_dim_both, axes=(0, 1)), freqs)\n\n    # axes=None (default) shift in all dimensions\n    assert np.allclose(fft.fftshift(freqs, axes=None), shift_dim_both)\n    assert np.allclose(fft.ifftshift(shift_dim_both, axes=None), freqs)\n    assert np.allclose(fft.fftshift(freqs), shift_dim_both)\n    assert np.allclose(fft.ifftshift(shift_dim_both), freqs)\n\n@test\ndef TestFFTFreq_test_definition():\n    x = [0, 1, 2, 3, 4, -4, -3, -2, -1]\n    assert np.allclose(9*fft.fftfreq(9), x)\n    assert np.allclose(9*np.pi*fft.fftfreq(9, np.pi), x)\n    x = [0, 1, 2, 3, 4, -5, -4, -3, -2, -1]\n    assert np.allclose(10*fft.fftfreq(10), x)\n    assert np.allclose(10*np.pi*fft.fftfreq(10, np.pi), x)\n\n@test\ndef TestRFFTFreq_test_definition():\n    x = [0, 1, 2, 3, 4]\n    assert np.allclose(9*fft.rfftfreq(9), x)\n    assert np.allclose(9*np.pi*fft.rfftfreq(9, np.pi), x)\n    x = [0, 1, 2, 3, 4, 5]\n    assert np.allclose(10*fft.rfftfreq(10), x)\n    assert np.allclose(10*np.pi*fft.rfftfreq(10, np.pi), x)\n\n@test\ndef TestIRFFTN_test_not_last_axis_success():\n    ar, ai = np.random.random((2, 16, 8, 32))\n    a = ar + ai*1j\n\n    axes = (-2,)\n\n    # Should not raise error\n    fft.irfftn(a, axes=axes)\n\n\nTestFFTShift_test_definition()\nTestFFTShift_test_inverse()\nTestFFTShift_test_axes_keyword()\nTestFFTShift_test_uneven_dims()\nTestFFTFreq_test_definition()\nTestRFFTFreq_test_definition()\nTestIRFFTN_test_not_last_axis_success()\n"
  },
  {
    "path": "test/numpy/test_functional.codon",
    "content": "import numpy as np\n\n@test\ndef test_apply_along_axis():\n\n    def my_func(a):\n        return (a[0] + a[-1]) * 0.5\n\n    b = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n    assert np.array_equal(np.apply_along_axis(my_func, 0, b), [4., 5., 6.])\n    assert np.array_equal(np.apply_along_axis(my_func, 1, b), [2., 5., 8.])\n\n    b = np.array([[8, 1, 7], [4, 3, 9], [5, 2, 6]])\n    assert np.array_equal(np.apply_along_axis(sorted, 1, b),\n                          [[1, 7, 8], [3, 4, 9], [2, 5, 6]])\n\n    b = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n    assert np.array_equal(\n        np.apply_along_axis(np.diag, -1, b),\n        [[[1, 0, 0], [0, 2, 0], [0, 0, 3]], [[4, 0, 0], [0, 5, 0], [0, 0, 6]],\n         [[7, 0, 0], [0, 8, 0], [0, 0, 9]]])\n\ntest_apply_along_axis()\n\n@test\ndef test_apply_over_axes():\n    a = np.arange(24).reshape(2, 3, 4)\n    assert np.array_equal(np.apply_over_axes(np.sum, a, [0, 2]),\n                          [[[60], [92], [124]]])\n    assert np.array_equal(np.apply_over_axes(np.mean, a, [0, 2]),\n                          [[[7.5], [11.5], [15.5]]])\n    assert np.array_equal(np.apply_over_axes(np.sum, a, [0, 1, 2]), [[[276]]])\n    assert np.array_equal(np.apply_over_axes(np.mean, a, [0, 1, 2]),\n                          [[[11.5]]])\n\ntest_apply_over_axes()\n\n@test\ndef test_vectorize():\n\n    def f1(a):\n        return 2 * a + 1\n\n    def f3(a, b):\n        if a > b:\n            return a - b\n        else:\n            return a + b\n\n    v1 = np.vectorize(f1)\n    v3 = np.vectorize(f3)\n    a = np.arange(24).reshape(2, 3, 4)\n\n    assert np.array_equal(v1([1, 2, 3, 4]), [3, 5, 7, 9])\n\n    assert np.array_equal(v3([1, 2, 3, 4], 2), [3, 4, 1, 2])\n    a = np.arange(24).reshape(2, 3, 4)\n    assert np.array_equal(v3(a, a), 2 * a)\n\ntest_vectorize()\n\n@test\ndef test_frompyfunc():\n\n    def f1(a):\n        return 2 * a + 1\n\n    def f2(a):\n        return (a / 2, 3 * a + 1)\n\n    def f3(a, b):\n        if a > b:\n            return a - b\n        else:\n            return a + b\n\n    v1 = np.frompyfunc(f1, nin=1, nout=1, identity=None)\n    v2 = np.frompyfunc(f2, nin=1, nout=2, identity=None)\n    v3 = np.frompyfunc(f3, nin=2, nout=1, identity=None)\n    a = np.arange(24).reshape(2, 3, 4)\n\n    assert np.array_equal(v1([1, 2, 3, 4]), [3, 5, 7, 9])\n\n    b1, b2 = v2(a)\n    assert np.array_equal(b1, a / 2)\n    assert np.array_equal(b2, 3 * a + 1)\n\n    assert np.array_equal(v3([1, 2, 3, 4], 2), [3, 4, 1, 2])\n    a = np.arange(24).reshape(2, 3, 4)\n    assert np.array_equal(v3(a, a), 2 * a)\n\ntest_frompyfunc()\n"
  },
  {
    "path": "test/numpy/test_fusion.codon",
    "content": "import numpy as np\n\nL: List[str] = []\n\n@pure\ndef f(x, label):\n    L.append(label)\n    return x\n\ndef eq(got, exp, dtype: type):\n    if got.dtype is not dtype:\n        print('-> dtype mismatch: got', got.dtype.__name__, 'but expected', dtype.__name__)\n        return False\n    return np.allclose(got, np.asarray(exp, dtype), equal_nan=True, atol=1e-10)\n\n@test\ndef test_basic():\n    # A few trivial cases\n    a = np.array([1, 2, 3, 4])\n    b = np.array([10, 11, 12, 13])\n\n    assert eq((1 + a)/2, [1., 1.5, 2., 2.5], float)\n    assert eq(a + b**2, [101, 123, 147, 173], int)\n    assert eq(np.multiply(a, b) + np.subtract(a, b) + np.square(a) + (b**2), [102, 138, 180, 228], int)\n\n@test\ndef test_order():\n    # Make sure things are executed in the correct order after fusion\n    L.clear()\n    a = np.array([1, 2, 3, 4])\n    b = np.array([10, 11, 12, 13])\n\n    z = (f(1, '1') + f(a, 'a')) * (f(b, 'b') + f(2, '2'))\n    assert eq(z, [24, 39, 56, 75], int)\n    assert L == ['1', 'a', 'b', '2']\n\n@test\ndef test_ops_int64():\n    a = np.array([1, 2, 3, 4])\n    assert eq(1 + (+a), [2, 3, 4, 5], int)\n    assert eq(1 + (-a), [0, -1, -2, -3], int)\n    assert eq(1 + (~a), [-1, -2, -3, -4], int)\n    assert eq(1 + np.abs(a), [2, 3, 4, 5], int)\n    assert eq(1 + abs(a), [2, 3, 4, 5], int)\n    assert eq(2 * (1 + a), [4, 6, 8, 10], int)\n    assert eq(2 * (a + 1), [4, 6, 8, 10], int)\n    assert eq(2 * (a - 1), [0, 2, 4, 6], int)\n    assert eq(2 * (1 - a), [0, -2, -4, -6], int)\n    assert eq(1 + (a * 2), [3, 5, 7, 9], int)\n    assert eq(1 + (2 * a), [3, 5, 7, 9], int)\n    assert eq(1 + (a.reshape(1, -1) @ a.reshape(-1, 1)), [[31]], int)\n    assert eq(1 + (a / 2), [1.5, 2., 2.5, 3.], float)\n    assert eq(1 + (2 / a), [3., 2., 1 + 2/3, 1.5], float)\n    assert eq(1 + (a // 2), [1, 2, 2, 3], int)\n    assert eq(1 + (10 // a), [11, 6, 4, 3], int)\n    assert eq(1 + (a % 3), [2, 3, 1, 2], int)\n    assert eq(1 + (10 % a), [1, 1, 2, 3], int)\n    assert eq(1 + np.fmod(a, 3), [2, 3, 1, 2], int)\n    assert eq(1 + np.fmod(10, a), [1, 1, 2, 3], int)\n    assert eq(1 + (a ** 3), [2, 9, 28, 65], int)\n    assert eq(1 + (3 ** a), [4, 10, 28, 82], int)\n    assert eq(1 + (a << 3), [9, 17, 25, 33], int)\n    assert eq(1 + (3 << a), [7, 13, 25, 49], int)\n    assert eq(1 + (a >> 1), [1, 2, 2, 3], int)\n    assert eq(1 + (1000 >> a), [501, 251, 126, 63], int)\n    assert eq(1 + (a & 2), [1, 3, 3, 1], int)\n    assert eq(1 + (2 & a), [1, 3, 3, 1], int)\n    assert eq(1 + (a | 2), [4, 3, 4, 7], int)\n    assert eq(1 + (2 | a), [4, 3, 4, 7], int)\n    assert eq(1 + (a ^ 2), [4, 1, 2, 7], int)\n    assert eq(1 + (2 ^ a), [4, 1, 2, 7], int)\n    assert eq(np.logical_and(a, a & 1), [True, False, True, False], bool)\n    assert eq(np.logical_or(a & 2, a & 1), [True, True, True, False], bool)\n    assert eq(np.logical_xor(a, a & 1), [False, True, False, True], bool)\n    assert eq((a + 1) == 2, [True, False, False, False], bool)\n    assert eq(2 == (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) != 2, [False, True, True, True], bool)\n    assert eq(2 != (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) < 3, [True, False, False, False], bool)\n    assert eq(3 < (a + 1), [False, False, True, True], bool)\n    assert eq((a + 1) > 3, [False, False, True, True], bool)\n    assert eq(3 > (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) <= 3, [True, True, False, False], bool)\n    assert eq(3 <= (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) >= 3, [False, True, True, True], bool)\n    assert eq(3 >= (a + 1), [True, True, False, False], bool)\n    assert eq(1 + np.minimum(a, 3), [2, 3, 4, 4], int)\n    assert eq(1 + np.minimum(3, a), [2, 3, 4, 4], int)\n    assert eq(1 + np.maximum(a, 3), [4, 4, 4, 5], int)\n    assert eq(1 + np.maximum(3, a), [4, 4, 4, 5], int)\n    assert eq(1 + np.fmin(a, 3), [2, 3, 4, 4], int)\n    assert eq(1 + np.fmin(3, a), [2, 3, 4, 4], int)\n    assert eq(1 + np.fmax(a, 3), [4, 4, 4, 5], int)\n    assert eq(1 + np.fmax(3, a), [4, 4, 4, 5], int)\n    assert eq(1 + np.sin(a), [1 + np.sin(1), 1 + np.sin(2), 1 + np.sin(3), 1 + np.sin(4)], float)\n    assert eq(1 + np.cos(a), [1 + np.cos(1), 1 + np.cos(2), 1 + np.cos(3), 1 + np.cos(4)], float)\n    assert eq(1 + np.tan(a), [1 + np.tan(1), 1 + np.tan(2), 1 + np.tan(3), 1 + np.tan(4)], float)\n    assert eq(1 + np.arcsin(a), [1 + np.arcsin(1), 1 + np.arcsin(2), 1 + np.arcsin(3), 1 + np.arcsin(4)], float)\n    assert eq(1 + np.arccos(a), [1 + np.arccos(1), 1 + np.arccos(2), 1 + np.arccos(3), 1 + np.arccos(4)], float)\n    assert eq(1 + np.arctan(a), [1 + np.arctan(1), 1 + np.arctan(2), 1 + np.arctan(3), 1 + np.arctan(4)], float)\n    assert eq(1 + np.arctan2(a, 3), [1 + np.arctan2(1, 3), 1 + np.arctan2(2, 3), 1 + np.arctan2(3, 3), 1 + np.arctan2(4, 3)], float)\n    assert eq(1 + np.arctan2(3, a), [1 + np.arctan2(3, 1), 1 + np.arctan2(3, 2), 1 + np.arctan2(3, 3), 1 + np.arctan2(3, 4)], float)\n    assert eq(1 + np.hypot(a, 3), [1 + np.hypot(1, 3), 1 + np.hypot(2, 3), 1 + np.hypot(3, 3), 1 + np.hypot(4, 3)], float)\n    assert eq(1 + np.hypot(3, a), [1 + np.hypot(3, 1), 1 + np.hypot(3, 2), 1 + np.hypot(3, 3), 1 + np.hypot(3, 4)], float)\n    assert eq(1 + np.sinh(a), [1 + np.sinh(1), 1 + np.sinh(2), 1 + np.sinh(3), 1 + np.sinh(4)], float)\n    assert eq(1 + np.cosh(a), [1 + np.cosh(1), 1 + np.cosh(2), 1 + np.cosh(3), 1 + np.cosh(4)], float)\n    assert eq(1 + np.tanh(a), [1 + np.tanh(1), 1 + np.tanh(2), 1 + np.tanh(3), 1 + np.tanh(4)], float)\n    assert eq(1 + np.arcsinh(a), [1 + np.arcsinh(1), 1 + np.arcsinh(2), 1 + np.arcsinh(3), 1 + np.arcsinh(4)], float)\n    assert eq(1 + np.arccosh(a), [1 + np.arccosh(1), 1 + np.arccosh(2), 1 + np.arccosh(3), 1 + np.arccosh(4)], float)\n    assert eq(1 + np.arctanh(a), [1 + np.arctanh(1), 1 + np.arctanh(2), 1 + np.arctanh(3), 1 + np.arctanh(4)], float)\n    assert eq(1 + np.conj(a), [1 + np.conj(1), 1 + np.conj(2), 1 + np.conj(3), 1 + np.conj(4)], int)\n    assert eq(1 + np.exp(a), [1 + np.exp(1), 1 + np.exp(2), 1 + np.exp(3), 1 + np.exp(4)], float)\n    assert eq(1 + np.exp2(a), [1 + np.exp2(1), 1 + np.exp2(2), 1 + np.exp2(3), 1 + np.exp2(4)], float)\n    assert eq(1 + np.log(a), [1 + np.log(1), 1 + np.log(2), 1 + np.log(3), 1 + np.log(4)], float)\n    assert eq(1 + np.log2(a), [1 + np.log2(1), 1 + np.log2(2), 1 + np.log2(3), 1 + np.log2(4)], float)\n    assert eq(1 + np.log10(a), [1 + np.log10(1), 1 + np.log10(2), 1 + np.log10(3), 1 + np.log10(4)], float)\n    assert eq(1 + np.expm1(a), [1 + np.expm1(1), 1 + np.expm1(2), 1 + np.expm1(3), 1 + np.expm1(4)], float)\n    assert eq(1 + np.log1p(a), [1 + np.log1p(1), 1 + np.log1p(2), 1 + np.log1p(3), 1 + np.log1p(4)], float)\n    assert eq(1 + np.sqrt(a), [1 + np.sqrt(1), 1 + np.sqrt(2), 1 + np.sqrt(3), 1 + np.sqrt(4)], float)\n    assert eq(1 + np.square(a), [1 + np.square(1), 1 + np.square(2), 1 + np.square(3), 1 + np.square(4)], int)\n    assert eq(1 + np.cbrt(a), [1 + np.cbrt(1), 1 + np.cbrt(2), 1 + np.cbrt(3), 1 + np.cbrt(4)], float)\n    assert eq(1 + np.logaddexp(a, 3), [1 + np.logaddexp(1, 3), 1 + np.logaddexp(2, 3), 1 + np.logaddexp(3, 3), 1 + np.logaddexp(4, 3)], float)\n    assert eq(1 + np.logaddexp(3, a), [1 + np.logaddexp(3, 1), 1 + np.logaddexp(3, 2), 1 + np.logaddexp(3, 3), 1 + np.logaddexp(3, 4)], float)\n    assert eq(1 + np.logaddexp2(a, 3), [1 + np.logaddexp2(1, 3), 1 + np.logaddexp2(2, 3), 1 + np.logaddexp2(3, 3), 1 + np.logaddexp2(4, 3)], float)\n    assert eq(1 + np.logaddexp2(3, a), [1 + np.logaddexp2(3, 1), 1 + np.logaddexp2(3, 2), 1 + np.logaddexp2(3, 3), 1 + np.logaddexp2(3, 4)], float)\n    assert eq(1 + np.reciprocal(a), [2, 1, 1, 1], int)\n    assert eq(1 + np.rint(a), [1 + np.rint(1), 1 + np.rint(2), 1 + np.rint(3), 1 + np.rint(4)], float)\n    assert eq(1 + np.floor(a), [1 + np.floor(1), 1 + np.floor(2), 1 + np.floor(3), 1 + np.floor(4)], float)\n    assert eq(1 + np.ceil(a), [1 + np.ceil(1), 1 + np.ceil(2), 1 + np.ceil(3), 1 + np.ceil(4)], float)\n    assert eq(1 + np.trunc(a), [1 + np.trunc(1), 1 + np.trunc(2), 1 + np.trunc(3), 1 + np.trunc(4)], float)\n    assert eq(1 + np.isnan(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isinf(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isfinite(a), [2, 2, 2, 2], int)\n    assert eq(1 + np.ldexp(a, 1), [1 + np.ldexp(1, 1), 1 + np.ldexp(2, 1), 1 + np.ldexp(3, 1), 1 + np.ldexp(4, 1)], float)\n    assert eq(1 + np.ldexp(1, a), [1 + np.ldexp(1, 1), 1 + np.ldexp(1, 2), 1 + np.ldexp(1, 3), 1 + np.ldexp(1, 4)], float)\n    assert eq(1 + np.sign(a), [2, 2, 2, 2], int)\n    assert eq(1 + np.signbit(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.copysign(a, -1), [1 + np.copysign(1, -1), 1 + np.copysign(2, -1), 1 + np.copysign(3, -1), 1 + np.copysign(4, -1)], float)\n    assert eq(1 + np.copysign(-1, a), [1 + np.copysign(-1, 1), 1 + np.copysign(-1, 2), 1 + np.copysign(-1, 3), 1 + np.copysign(-1, 4)], float)\n    assert eq(1 + np.spacing(a), [1 + np.spacing(1), 1 + np.spacing(2), 1 + np.spacing(3), 1 + np.spacing(4)], float)\n    assert eq(1 + np.nextafter(a, 1), [1 + np.nextafter(1, 1), 1 + np.nextafter(2, 1), 1 + np.nextafter(3, 1), 1 + np.nextafter(4, 1)], float)\n    assert eq(1 + np.nextafter(1, a), [1 + np.nextafter(1, 1), 1 + np.nextafter(1, 2), 1 + np.nextafter(1, 3), 1 + np.nextafter(1, 4)], float)\n    assert eq(1 + np.deg2rad(a), [1 + np.deg2rad(1), 1 + np.deg2rad(2), 1 + np.deg2rad(3), 1 + np.deg2rad(4)], float)\n    assert eq(1 + np.rad2deg(a), [1 + np.rad2deg(1), 1 + np.rad2deg(2), 1 + np.rad2deg(3), 1 + np.rad2deg(4)], float)\n    assert eq(1 + np.heaviside(a, 1), [1 + np.heaviside(1, 1), 1 + np.heaviside(2, 1), 1 + np.heaviside(3, 1), 1 + np.heaviside(4, 1)], float)\n    assert eq(1 + np.heaviside(1, a), [1 + np.heaviside(1, 1), 1 + np.heaviside(1, 2), 1 + np.heaviside(1, 3), 1 + np.heaviside(1, 4)], float)\n\n@test\ndef test_ops_int32():\n    a = np.array([1, 2, 3, 4], np.int32)\n    assert eq(1 + (+a), [2, 3, 4, 5], np.int32)\n    assert eq(1 + (-a), [0, -1, -2, -3], np.int32)\n    assert eq(1 + (~a), [-1, -2, -3, -4], np.int32)\n    assert eq(1 + np.abs(a), [2, 3, 4, 5], np.int32)\n    assert eq(1 + abs(a), [2, 3, 4, 5], np.int32)\n    assert eq(2 * (1 + a), [4, 6, 8, 10], np.int32)\n    assert eq(2 * (a + 1), [4, 6, 8, 10], np.int32)\n    assert eq(2 * (a - 1), [0, 2, 4, 6], np.int32)\n    assert eq(2 * (1 - a), [0, -2, -4, -6], np.int32)\n    assert eq(1 + (a * 2), [3, 5, 7, 9], np.int32)\n    assert eq(1 + (2 * a), [3, 5, 7, 9], np.int32)\n    assert eq(1 + (a.reshape(1, -1) @ a.reshape(-1, 1)), [[31]], np.int32)\n    assert eq(1 + (a / 2), [1.5, 2., 2.5, 3.], float)\n    assert eq(1 + (2 / a), [3., 2., 1 + 2/3, 1.5], float)\n    assert eq(1 + (a // 2), [1, 2, 2, 3], np.int32)\n    assert eq(1 + (10 // a), [11, 6, 4, 3], np.int32)\n    assert eq(1 + (a % 3), [2, 3, 1, 2], np.int32)\n    assert eq(1 + (10 % a), [1, 1, 2, 3], np.int32)\n    assert eq(1 + np.fmod(a, 3), [2, 3, 1, 2], np.int32)\n    assert eq(1 + np.fmod(10, a), [1, 1, 2, 3], np.int32)\n    assert eq(1 + (a ** 3), [2, 9, 28, 65], np.int32)\n    assert eq(1 + (3 ** a), [4, 10, 28, 82], np.int32)\n    assert eq(1 + (a << 3), [9, 17, 25, 33], np.int32)\n    assert eq(1 + (3 << a), [7, 13, 25, 49], np.int32)\n    assert eq(1 + (a >> 1), [1, 2, 2, 3], np.int32)\n    assert eq(1 + (1000 >> a), [501, 251, 126, 63], np.int32)\n    assert eq(1 + (a & 2), [1, 3, 3, 1], np.int32)\n    assert eq(1 + (2 & a), [1, 3, 3, 1], np.int32)\n    assert eq(1 + (a | 2), [4, 3, 4, 7], np.int32)\n    assert eq(1 + (2 | a), [4, 3, 4, 7], np.int32)\n    assert eq(1 + (a ^ 2), [4, 1, 2, 7], np.int32)\n    assert eq(1 + (2 ^ a), [4, 1, 2, 7], np.int32)\n    assert eq(np.logical_and(a, a & 1), [True, False, True, False], bool)\n    assert eq(np.logical_or(a & 2, a & 1), [True, True, True, False], bool)\n    assert eq(np.logical_xor(a, a & 1), [False, True, False, True], bool)\n    assert eq((a + 1) == 2, [True, False, False, False], bool)\n    assert eq(2 == (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) != 2, [False, True, True, True], bool)\n    assert eq(2 != (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) < 3, [True, False, False, False], bool)\n    assert eq(3 < (a + 1), [False, False, True, True], bool)\n    assert eq((a + 1) > 3, [False, False, True, True], bool)\n    assert eq(3 > (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) <= 3, [True, True, False, False], bool)\n    assert eq(3 <= (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) >= 3, [False, True, True, True], bool)\n    assert eq(3 >= (a + 1), [True, True, False, False], bool)\n    assert eq(1 + np.minimum(a, 3), [2, 3, 4, 4], np.int32)\n    assert eq(1 + np.minimum(3, a), [2, 3, 4, 4], np.int32)\n    assert eq(1 + np.maximum(a, 3), [4, 4, 4, 5], np.int32)\n    assert eq(1 + np.maximum(3, a), [4, 4, 4, 5], np.int32)\n    assert eq(1 + np.fmin(a, 3), [2, 3, 4, 4], np.int32)\n    assert eq(1 + np.fmin(3, a), [2, 3, 4, 4], np.int32)\n    assert eq(1 + np.fmax(a, 3), [4, 4, 4, 5], np.int32)\n    assert eq(1 + np.fmax(3, a), [4, 4, 4, 5], np.int32)\n    assert eq(1 + np.sin(a), [1 + np.sin(1), 1 + np.sin(2), 1 + np.sin(3), 1 + np.sin(4)], float)\n    assert eq(1 + np.cos(a), [1 + np.cos(1), 1 + np.cos(2), 1 + np.cos(3), 1 + np.cos(4)], float)\n    assert eq(1 + np.tan(a), [1 + np.tan(1), 1 + np.tan(2), 1 + np.tan(3), 1 + np.tan(4)], float)\n    assert eq(1 + np.arcsin(a), [1 + np.arcsin(1), 1 + np.arcsin(2), 1 + np.arcsin(3), 1 + np.arcsin(4)], float)\n    assert eq(1 + np.arccos(a), [1 + np.arccos(1), 1 + np.arccos(2), 1 + np.arccos(3), 1 + np.arccos(4)], float)\n    assert eq(1 + np.arctan(a), [1 + np.arctan(1), 1 + np.arctan(2), 1 + np.arctan(3), 1 + np.arctan(4)], float)\n    assert eq(1 + np.arctan2(a, 3), [1 + np.arctan2(1, 3), 1 + np.arctan2(2, 3), 1 + np.arctan2(3, 3), 1 + np.arctan2(4, 3)], float)\n    assert eq(1 + np.arctan2(3, a), [1 + np.arctan2(3, 1), 1 + np.arctan2(3, 2), 1 + np.arctan2(3, 3), 1 + np.arctan2(3, 4)], float)\n    assert eq(1 + np.hypot(a, 3), [1 + np.hypot(1, 3), 1 + np.hypot(2, 3), 1 + np.hypot(3, 3), 1 + np.hypot(4, 3)], float)\n    assert eq(1 + np.hypot(3, a), [1 + np.hypot(3, 1), 1 + np.hypot(3, 2), 1 + np.hypot(3, 3), 1 + np.hypot(3, 4)], float)\n    assert eq(1 + np.sinh(a), [1 + np.sinh(1), 1 + np.sinh(2), 1 + np.sinh(3), 1 + np.sinh(4)], float)\n    assert eq(1 + np.cosh(a), [1 + np.cosh(1), 1 + np.cosh(2), 1 + np.cosh(3), 1 + np.cosh(4)], float)\n    assert eq(1 + np.tanh(a), [1 + np.tanh(1), 1 + np.tanh(2), 1 + np.tanh(3), 1 + np.tanh(4)], float)\n    assert eq(1 + np.arcsinh(a), [1 + np.arcsinh(1), 1 + np.arcsinh(2), 1 + np.arcsinh(3), 1 + np.arcsinh(4)], float)\n    assert eq(1 + np.arccosh(a), [1 + np.arccosh(1), 1 + np.arccosh(2), 1 + np.arccosh(3), 1 + np.arccosh(4)], float)\n    assert eq(1 + np.arctanh(a), [1 + np.arctanh(1), 1 + np.arctanh(2), 1 + np.arctanh(3), 1 + np.arctanh(4)], float)\n    assert eq(1 + np.conj(a), [1 + np.conj(1), 1 + np.conj(2), 1 + np.conj(3), 1 + np.conj(4)], np.int32)\n    assert eq(1 + np.exp(a), [1 + np.exp(1), 1 + np.exp(2), 1 + np.exp(3), 1 + np.exp(4)], float)\n    assert eq(1 + np.exp2(a), [1 + np.exp2(1), 1 + np.exp2(2), 1 + np.exp2(3), 1 + np.exp2(4)], float)\n    assert eq(1 + np.log(a), [1 + np.log(1), 1 + np.log(2), 1 + np.log(3), 1 + np.log(4)], float)\n    assert eq(1 + np.log2(a), [1 + np.log2(1), 1 + np.log2(2), 1 + np.log2(3), 1 + np.log2(4)], float)\n    assert eq(1 + np.log10(a), [1 + np.log10(1), 1 + np.log10(2), 1 + np.log10(3), 1 + np.log10(4)], float)\n    assert eq(1 + np.expm1(a), [1 + np.expm1(1), 1 + np.expm1(2), 1 + np.expm1(3), 1 + np.expm1(4)], float)\n    assert eq(1 + np.log1p(a), [1 + np.log1p(1), 1 + np.log1p(2), 1 + np.log1p(3), 1 + np.log1p(4)], float)\n    assert eq(1 + np.sqrt(a), [1 + np.sqrt(1), 1 + np.sqrt(2), 1 + np.sqrt(3), 1 + np.sqrt(4)], float)\n    assert eq(1 + np.square(a), [1 + np.square(1), 1 + np.square(2), 1 + np.square(3), 1 + np.square(4)], np.int32)\n    assert eq(1 + np.cbrt(a), [1 + np.cbrt(1), 1 + np.cbrt(2), 1 + np.cbrt(3), 1 + np.cbrt(4)], float)\n    assert eq(1 + np.logaddexp(a, 3), [1 + np.logaddexp(1, 3), 1 + np.logaddexp(2, 3), 1 + np.logaddexp(3, 3), 1 + np.logaddexp(4, 3)], float)\n    assert eq(1 + np.logaddexp(3, a), [1 + np.logaddexp(3, 1), 1 + np.logaddexp(3, 2), 1 + np.logaddexp(3, 3), 1 + np.logaddexp(3, 4)], float)\n    assert eq(1 + np.logaddexp2(a, 3), [1 + np.logaddexp2(1, 3), 1 + np.logaddexp2(2, 3), 1 + np.logaddexp2(3, 3), 1 + np.logaddexp2(4, 3)], float)\n    assert eq(1 + np.logaddexp2(3, a), [1 + np.logaddexp2(3, 1), 1 + np.logaddexp2(3, 2), 1 + np.logaddexp2(3, 3), 1 + np.logaddexp2(3, 4)], float)\n    assert eq(1 + np.reciprocal(a), [2, 1, 1, 1], np.int32)\n    assert eq(1 + np.rint(a), [1 + np.rint(1), 1 + np.rint(2), 1 + np.rint(3), 1 + np.rint(4)], float)\n    assert eq(1 + np.floor(a), [1 + np.floor(1), 1 + np.floor(2), 1 + np.floor(3), 1 + np.floor(4)], float)\n    assert eq(1 + np.ceil(a), [1 + np.ceil(1), 1 + np.ceil(2), 1 + np.ceil(3), 1 + np.ceil(4)], float)\n    assert eq(1 + np.trunc(a), [1 + np.trunc(1), 1 + np.trunc(2), 1 + np.trunc(3), 1 + np.trunc(4)], float)\n    assert eq(1 + np.isnan(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isinf(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isfinite(a), [2, 2, 2, 2], int)\n    assert eq(1 + np.ldexp(a, 1), [1 + np.ldexp(1, 1), 1 + np.ldexp(2, 1), 1 + np.ldexp(3, 1), 1 + np.ldexp(4, 1)], float)\n    assert eq(1 + np.ldexp(1, a), [1 + np.ldexp(1, 1), 1 + np.ldexp(1, 2), 1 + np.ldexp(1, 3), 1 + np.ldexp(1, 4)], float)\n    assert eq(1 + np.sign(a), [2, 2, 2, 2], np.int32)\n    assert eq(1 + np.signbit(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.copysign(a, -1), [1 + np.copysign(1, -1), 1 + np.copysign(2, -1), 1 + np.copysign(3, -1), 1 + np.copysign(4, -1)], float)\n    assert eq(1 + np.copysign(-1, a), [1 + np.copysign(-1, 1), 1 + np.copysign(-1, 2), 1 + np.copysign(-1, 3), 1 + np.copysign(-1, 4)], float)\n    assert eq(1 + np.spacing(a), [1 + np.spacing(1), 1 + np.spacing(2), 1 + np.spacing(3), 1 + np.spacing(4)], float)\n    assert eq(1 + np.nextafter(a, 1), [1 + np.nextafter(1, 1), 1 + np.nextafter(2, 1), 1 + np.nextafter(3, 1), 1 + np.nextafter(4, 1)], float)\n    assert eq(1 + np.nextafter(1, a), [1 + np.nextafter(1, 1), 1 + np.nextafter(1, 2), 1 + np.nextafter(1, 3), 1 + np.nextafter(1, 4)], float)\n    assert eq(1 + np.deg2rad(a), [1 + np.deg2rad(1), 1 + np.deg2rad(2), 1 + np.deg2rad(3), 1 + np.deg2rad(4)], float)\n    assert eq(1 + np.rad2deg(a), [1 + np.rad2deg(1), 1 + np.rad2deg(2), 1 + np.rad2deg(3), 1 + np.rad2deg(4)], float)\n    assert eq(1 + np.heaviside(a, 1), [1 + np.heaviside(1, 1), 1 + np.heaviside(2, 1), 1 + np.heaviside(3, 1), 1 + np.heaviside(4, 1)], float)\n    assert eq(1 + np.heaviside(1, a), [1 + np.heaviside(1, 1), 1 + np.heaviside(1, 2), 1 + np.heaviside(1, 3), 1 + np.heaviside(1, 4)], float)\n\n@test\ndef test_ops_int16():\n    a = np.array([1, 2, 3, 4], np.int16)\n    assert eq(1 + (+a), [2, 3, 4, 5], np.int16)\n    assert eq(1 + (-a), [0, -1, -2, -3], np.int16)\n    assert eq(1 + (~a), [-1, -2, -3, -4], np.int16)\n    assert eq(1 + np.abs(a), [2, 3, 4, 5], np.int16)\n    assert eq(1 + abs(a), [2, 3, 4, 5], np.int16)\n    assert eq(2 * (1 + a), [4, 6, 8, 10], np.int16)\n    assert eq(2 * (a + 1), [4, 6, 8, 10], np.int16)\n    assert eq(2 * (a - 1), [0, 2, 4, 6], np.int16)\n    assert eq(2 * (1 - a), [0, -2, -4, -6], np.int16)\n    assert eq(1 + (a * 2), [3, 5, 7, 9], np.int16)\n    assert eq(1 + (2 * a), [3, 5, 7, 9], np.int16)\n    assert eq(1 + (a.reshape(1, -1) @ a.reshape(-1, 1)), [[31]], np.int16)\n    assert eq(1 + (a / 2), [1.5, 2., 2.5, 3.], float)\n    assert eq(1 + (2 / a), [3., 2., 1 + 2/3, 1.5], float)\n    assert eq(1 + (a // 2), [1, 2, 2, 3], np.int16)\n    assert eq(1 + (10 // a), [11, 6, 4, 3], np.int16)\n    assert eq(1 + (a % 3), [2, 3, 1, 2], np.int16)\n    assert eq(1 + (10 % a), [1, 1, 2, 3], np.int16)\n    assert eq(1 + np.fmod(a, 3), [2, 3, 1, 2], np.int16)\n    assert eq(1 + np.fmod(10, a), [1, 1, 2, 3], np.int16)\n    assert eq(1 + (a ** 3), [2, 9, 28, 65], np.int16)\n    assert eq(1 + (3 ** a), [4, 10, 28, 82], np.int16)\n    assert eq(1 + (a << 3), [9, 17, 25, 33], np.int16)\n    assert eq(1 + (3 << a), [7, 13, 25, 49], np.int16)\n    assert eq(1 + (a >> 1), [1, 2, 2, 3], np.int16)\n    assert eq(1 + (1000 >> a), [501, 251, 126, 63], np.int16)\n    assert eq(1 + (a & 2), [1, 3, 3, 1], np.int16)\n    assert eq(1 + (2 & a), [1, 3, 3, 1], np.int16)\n    assert eq(1 + (a | 2), [4, 3, 4, 7], np.int16)\n    assert eq(1 + (2 | a), [4, 3, 4, 7], np.int16)\n    assert eq(1 + (a ^ 2), [4, 1, 2, 7], np.int16)\n    assert eq(1 + (2 ^ a), [4, 1, 2, 7], np.int16)\n    assert eq(np.logical_and(a, a & 1), [True, False, True, False], bool)\n    assert eq(np.logical_or(a & 2, a & 1), [True, True, True, False], bool)\n    assert eq(np.logical_xor(a, a & 1), [False, True, False, True], bool)\n    assert eq((a + 1) == 2, [True, False, False, False], bool)\n    assert eq(2 == (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) != 2, [False, True, True, True], bool)\n    assert eq(2 != (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) < 3, [True, False, False, False], bool)\n    assert eq(3 < (a + 1), [False, False, True, True], bool)\n    assert eq((a + 1) > 3, [False, False, True, True], bool)\n    assert eq(3 > (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) <= 3, [True, True, False, False], bool)\n    assert eq(3 <= (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) >= 3, [False, True, True, True], bool)\n    assert eq(3 >= (a + 1), [True, True, False, False], bool)\n    assert eq(1 + np.minimum(a, 3), [2, 3, 4, 4], np.int16)\n    assert eq(1 + np.minimum(3, a), [2, 3, 4, 4], np.int16)\n    assert eq(1 + np.maximum(a, 3), [4, 4, 4, 5], np.int16)\n    assert eq(1 + np.maximum(3, a), [4, 4, 4, 5], np.int16)\n    assert eq(1 + np.fmin(a, 3), [2, 3, 4, 4], np.int16)\n    assert eq(1 + np.fmin(3, a), [2, 3, 4, 4], np.int16)\n    assert eq(1 + np.fmax(a, 3), [4, 4, 4, 5], np.int16)\n    assert eq(1 + np.fmax(3, a), [4, 4, 4, 5], np.int16)\n    assert eq(1 + np.sin(a), [1.f32 + np.sin(1.f32), 1.f32 + np.sin(2.f32), 1.f32 + np.sin(3.f32), 1.f32 + np.sin(4.f32)], np.float32)\n    assert eq(1 + np.cos(a), [1.f32 + np.cos(1.f32), 1.f32 + np.cos(2.f32), 1.f32 + np.cos(3.f32), 1.f32 + np.cos(4.f32)], np.float32)\n    assert eq(1 + np.tan(a), [1.f32 + np.tan(1.f32), 1.f32 + np.tan(2.f32), 1.f32 + np.tan(3.f32), 1.f32 + np.tan(4.f32)], np.float32)\n    assert eq(1 + np.arcsin(a), [1.f32 + np.arcsin(1.f32), 1.f32 + np.arcsin(2.f32), 1.f32 + np.arcsin(3.f32), 1.f32 + np.arcsin(4.f32)], np.float32)\n    assert eq(1 + np.arccos(a), [1.f32 + np.arccos(1.f32), 1.f32 + np.arccos(2.f32), 1.f32 + np.arccos(3.f32), 1.f32 + np.arccos(4.f32)], np.float32)\n    assert eq(1 + np.arctan(a), [1.f32 + np.arctan(1.f32), 1.f32 + np.arctan(2.f32), 1.f32 + np.arctan(3.f32), 1.f32 + np.arctan(4.f32)], np.float32)\n    assert eq(1 + np.arctan2(a, 3.f32), [1.f32 + np.arctan2(1.f32, 3.f32), 1.f32 + np.arctan2(2.f32, 3.f32), 1.f32 + np.arctan2(3.f32, 3.f32), 1.f32 + np.arctan2(4.f32, 3.f32)], np.float32)\n    assert eq(1 + np.arctan2(3.f32, a), [1.f32 + np.arctan2(3.f32, 1.f32), 1.f32 + np.arctan2(3.f32, 2.f32), 1.f32 + np.arctan2(3.f32, 3.f32), 1.f32 + np.arctan2(3.f32, 4.f32)], np.float32)\n    assert eq(1 + np.hypot(a, 3), [1.f32 + np.hypot(1.f32, 3.f32), 1.f32 + np.hypot(2.f32, 3.f32), 1.f32 + np.hypot(3.f32, 3.f32), 1.f32 + np.hypot(4.f32, 3.f32)], np.float32)\n    assert eq(1 + np.hypot(3, a), [1.f32 + np.hypot(3.f32, 1.f32), 1.f32 + np.hypot(3.f32, 2.f32), 1.f32 + np.hypot(3.f32, 3.f32), 1.f32 + np.hypot(3.f32, 4.f32)], np.float32)\n    assert eq(1 + np.sinh(a), [1.f32 + np.sinh(1.f32), 1.f32 + np.sinh(2.f32), 1.f32 + np.sinh(3.f32), 1.f32 + np.sinh(4.f32)], np.float32)\n    assert eq(1 + np.cosh(a), [1.f32 + np.cosh(1.f32), 1.f32 + np.cosh(2.f32), 1.f32 + np.cosh(3.f32), 1.f32 + np.cosh(4.f32)], np.float32)\n    assert eq(1 + np.tanh(a), [1.f32 + np.tanh(1.f32), 1.f32 + np.tanh(2.f32), 1.f32 + np.tanh(3.f32), 1.f32 + np.tanh(4.f32)], np.float32)\n    assert eq(1 + np.arcsinh(a), [1.f32 + np.arcsinh(1.f32), 1.f32 + np.arcsinh(2.f32), 1.f32 + np.arcsinh(3.f32), 1.f32 + np.arcsinh(4.f32)], np.float32)\n    assert eq(1 + np.arccosh(a), [1.f32 + np.arccosh(1.f32), 1.f32 + np.arccosh(2.f32), 1.f32 + np.arccosh(3.f32), 1.f32 + np.arccosh(4.f32)], np.float32)\n    assert eq(1 + np.arctanh(a), [1.f32 + np.arctanh(1.f32), 1.f32 + np.arctanh(2.f32), 1.f32 + np.arctanh(3.f32), 1.f32 + np.arctanh(4.f32)], np.float32)\n    assert eq(1 + np.conj(a), [1 + np.conj(1), 1 + np.conj(2), 1 + np.conj(3), 1 + np.conj(4)], np.int16)\n    assert eq(1 + np.exp(a), [1.f32 + np.exp(1.f32), 1.f32 + np.exp(2.f32), 1.f32 + np.exp(3.f32), 1.f32 + np.exp(4.f32)], np.float32)\n    assert eq(1 + np.exp2(a), [1.f32 + np.exp2(1.f32), 1.f32 + np.exp2(2.f32), 1.f32 + np.exp2(3.f32), 1.f32 + np.exp2(4.f32)], np.float32)\n    assert eq(1 + np.log(a), [1.f32 + np.log(1.f32), 1.f32 + np.log(2.f32), 1.f32 + np.log(3.f32), 1.f32 + np.log(4.f32)], np.float32)\n    assert eq(1 + np.log2(a), [1.f32 + np.log2(1.f32), 1.f32 + np.log2(2.f32), 1.f32 + np.log2(3.f32), 1.f32 + np.log2(4.f32)], np.float32)\n    assert eq(1 + np.log10(a), [1.f32 + np.log10(1.f32), 1.f32 + np.log10(2.f32), 1.f32 + np.log10(3.f32), 1.f32 + np.log10(4.f32)], np.float32)\n    assert eq(1 + np.expm1(a), [1.f32 + np.expm1(1.f32), 1.f32 + np.expm1(2.f32), 1.f32 + np.expm1(3.f32), 1.f32 + np.expm1(4.f32)], np.float32)\n    assert eq(1 + np.log1p(a), [1.f32 + np.log1p(1.f32), 1.f32 + np.log1p(2.f32), 1.f32 + np.log1p(3.f32), 1.f32 + np.log1p(4.f32)], np.float32)\n    assert eq(1 + np.sqrt(a), [1.f32 + np.sqrt(1.f32), 1.f32 + np.sqrt(2.f32), 1.f32 + np.sqrt(3.f32), 1.f32 + np.sqrt(4.f32)], np.float32)\n    assert eq(1 + np.square(a), [1 + np.square(1), 1 + np.square(2), 1 + np.square(3), 1 + np.square(4)], np.int16)\n    assert eq(1 + np.cbrt(a), [1.f32 + np.cbrt(1.f32), 1.f32 + np.cbrt(2.f32), 1.f32 + np.cbrt(3.f32), 1.f32 + np.cbrt(4.f32)], np.float32)\n    assert eq(1 + np.logaddexp(a, 3), [1.f32 + np.logaddexp(1.f32, 3.f32), 1.f32 + np.logaddexp(2.f32, 3.f32), 1.f32 + np.logaddexp(3.f32, 3.f32), 1.f32 + np.logaddexp(4.f32, 3.f32)], np.float32)\n    assert eq(1 + np.logaddexp(3, a), [1.f32 + np.logaddexp(3.f32, 1.f32), 1.f32 + np.logaddexp(3.f32, 2.f32), 1.f32 + np.logaddexp(3.f32, 3.f32), 1.f32 + np.logaddexp(3.f32, 4.f32)], np.float32)\n    assert eq(1 + np.logaddexp2(a, 3), [1.f32 + np.logaddexp2(1.f32, 3.f32), 1.f32 + np.logaddexp2(2.f32, 3.f32), 1.f32 + np.logaddexp2(3.f32, 3.f32), 1.f32 + np.logaddexp2(4.f32, 3.f32)], np.float32)\n    assert eq(1 + np.logaddexp2(3, a), [1.f32 + np.logaddexp2(3.f32, 1.f32), 1.f32 + np.logaddexp2(3.f32, 2.f32), 1.f32 + np.logaddexp2(3.f32, 3.f32), 1.f32 + np.logaddexp2(3.f32, 4.f32)], np.float32)\n    assert eq(1 + np.reciprocal(a), [2, 1, 1, 1], np.int16)\n    assert eq(1 + np.rint(a), [1.f32 + np.rint(1.f32), 1.f32 + np.rint(2.f32), 1.f32 + np.rint(3.f32), 1.f32 + np.rint(4.f32)], np.float32)\n    assert eq(1 + np.floor(a), [1.f32 + np.floor(1.f32), 1.f32 + np.floor(2.f32), 1.f32 + np.floor(3.f32), 1.f32 + np.floor(4.f32)], np.float32)\n    assert eq(1 + np.ceil(a), [1.f32 + np.ceil(1.f32), 1.f32 + np.ceil(2.f32), 1.f32 + np.ceil(3.f32), 1.f32 + np.ceil(4.f32)], np.float32)\n    assert eq(1 + np.trunc(a), [1.f32 + np.trunc(1.f32), 1.f32 + np.trunc(2.f32), 1.f32 + np.trunc(3.f32), 1.f32 + np.trunc(4.f32)], np.float32)\n    assert eq(1 + np.isnan(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isinf(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isfinite(a), [2, 2, 2, 2], int)\n    assert eq(1 + np.ldexp(a, 1), [1.f32 + np.ldexp(1.f32, 1), 1.f32 + np.ldexp(2.f32, 1), 1.f32 + np.ldexp(3.f32, 1), 1.f32 + np.ldexp(4.f32, 1)], np.float32)\n    assert eq(1 + np.ldexp(1, a), [1.f32 + np.ldexp(1.f32, 1), 1.f32 + np.ldexp(1.f32, 2), 1.f32 + np.ldexp(1.f32, 3), 1.f32 + np.ldexp(1.f32, 4)], np.float32)\n    assert eq(1 + np.sign(a), [2, 2, 2, 2], np.int16)\n    assert eq(1 + np.signbit(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.copysign(a, -1), [1.f32 + np.copysign(1.f32, -1.f32), 1.f32 + np.copysign(2.f32, -1.f32), 1.f32 + np.copysign(3.f32, -1.f32), 1.f32 + np.copysign(4.f32, -1.f32)], np.float32)\n    assert eq(1 + np.copysign(-1, a), [1.f32 + np.copysign(-1.f32, 1.f32), 1.f32 + np.copysign(-1.f32, 2.f32), 1.f32 + np.copysign(-1.f32, 3.f32), 1.f32 + np.copysign(-1.f32, 4.f32)], np.float32)\n    assert eq(1 + np.spacing(a), [1.f32 + np.spacing(1.f32), 1.f32 + np.spacing(2.f32), 1.f32 + np.spacing(3.f32), 1.f32 + np.spacing(4.f32)], np.float32)\n    assert eq(1 + np.nextafter(a, 1), [1.f32 + np.nextafter(1.f32, 1.f32), 1.f32 + np.nextafter(2.f32, 1.f32), 1.f32 + np.nextafter(3.f32, 1.f32), 1.f32 + np.nextafter(4.f32, 1.f32)], np.float32)\n    assert eq(1 + np.nextafter(1, a), [1.f32 + np.nextafter(1.f32, 1.f32), 1.f32 + np.nextafter(1.f32, 2.f32), 1.f32 + np.nextafter(1.f32, 3.f32), 1.f32 + np.nextafter(1.f32, 4.f32)], np.float32)\n    assert eq(1 + np.deg2rad(a), [1.f32 + np.deg2rad(1.f32), 1.f32 + np.deg2rad(2.f32), 1.f32 + np.deg2rad(3.f32), 1.f32 + np.deg2rad(4.f32)], np.float32)\n    assert eq(1 + np.rad2deg(a), [1.f32 + np.rad2deg(1.f32), 1.f32 + np.rad2deg(2.f32), 1.f32 + np.rad2deg(3.f32), 1.f32 + np.rad2deg(4.f32)], np.float32)\n    assert eq(1 + np.heaviside(a, 1), [1.f32 + np.heaviside(1.f32, 1.f32), 1.f32 + np.heaviside(2.f32, 1.f32), 1.f32 + np.heaviside(3.f32, 1.f32), 1.f32 + np.heaviside(4.f32, 1.f32)], np.float32)\n    assert eq(1 + np.heaviside(1, a), [1.f32 + np.heaviside(1.f32, 1.f32), 1.f32 + np.heaviside(1.f32, 2.f32), 1.f32 + np.heaviside(1.f32, 3.f32), 1.f32 + np.heaviside(1.f32, 4.f32)], np.float32)\n\n@test\ndef test_ops_int8():\n    a = np.array([1, 2, 3, 4], np.int8)\n    assert eq(1 + (+a), [2, 3, 4, 5], np.int8)\n    assert eq(1 + (-a), [0, -1, -2, -3], np.int8)\n    assert eq(1 + (~a), [-1, -2, -3, -4], np.int8)\n    assert eq(1 + np.abs(a), [2, 3, 4, 5], np.int8)\n    assert eq(1 + abs(a), [2, 3, 4, 5], np.int8)\n    assert eq(2 * (1 + a), [4, 6, 8, 10], np.int8)\n    assert eq(2 * (a + 1), [4, 6, 8, 10], np.int8)\n    assert eq(2 * (a - 1), [0, 2, 4, 6], np.int8)\n    assert eq(2 * (1 - a), [0, -2, -4, -6], np.int8)\n    assert eq(1 + (a * 2), [3, 5, 7, 9], np.int8)\n    assert eq(1 + (2 * a), [3, 5, 7, 9], np.int8)\n    assert eq(1 + (a.reshape(1, -1) @ a.reshape(-1, 1)), [[31]], np.int8)\n    assert eq(1 + (a / 2), [1.5, 2., 2.5, 3.], float)\n    assert eq(1 + (2 / a), [3., 2., 1 + 2/3, 1.5], float)\n    assert eq(1 + (a // 2), [1, 2, 2, 3], np.int8)\n    assert eq(1 + (10 // a), [11, 6, 4, 3], np.int8)\n    assert eq(1 + (a % 3), [2, 3, 1, 2], np.int8)\n    assert eq(1 + (10 % a), [1, 1, 2, 3], np.int8)\n    assert eq(1 + np.fmod(a, 3), [2, 3, 1, 2], np.int8)\n    assert eq(1 + np.fmod(10, a), [1, 1, 2, 3], np.int8)\n    assert eq(1 + (a ** 3), [2, 9, 28, 65], np.int8)\n    assert eq(1 + (3 ** a), [4, 10, 28, 82], np.int8)\n    assert eq(1 + (a << 3), [9, 17, 25, 33], np.int8)\n    assert eq(1 + (3 << a), [7, 13, 25, 49], np.int8)\n    assert eq(1 + (a >> 1), [1, 2, 2, 3], np.int8)\n    assert eq(1 + (100 >> a), [51, 26, 13, 7], np.int8)\n    assert eq(1 + (a & 2), [1, 3, 3, 1], np.int8)\n    assert eq(1 + (2 & a), [1, 3, 3, 1], np.int8)\n    assert eq(1 + (a | 2), [4, 3, 4, 7], np.int8)\n    assert eq(1 + (2 | a), [4, 3, 4, 7], np.int8)\n    assert eq(1 + (a ^ 2), [4, 1, 2, 7], np.int8)\n    assert eq(1 + (2 ^ a), [4, 1, 2, 7], np.int8)\n    assert eq(np.logical_and(a, a & 1), [True, False, True, False], bool)\n    assert eq(np.logical_or(a & 2, a & 1), [True, True, True, False], bool)\n    assert eq(np.logical_xor(a, a & 1), [False, True, False, True], bool)\n    assert eq((a + 1) == 2, [True, False, False, False], bool)\n    assert eq(2 == (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) != 2, [False, True, True, True], bool)\n    assert eq(2 != (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) < 3, [True, False, False, False], bool)\n    assert eq(3 < (a + 1), [False, False, True, True], bool)\n    assert eq((a + 1) > 3, [False, False, True, True], bool)\n    assert eq(3 > (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) <= 3, [True, True, False, False], bool)\n    assert eq(3 <= (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) >= 3, [False, True, True, True], bool)\n    assert eq(3 >= (a + 1), [True, True, False, False], bool)\n    assert eq(1 + np.minimum(a, 3), [2, 3, 4, 4], np.int8)\n    assert eq(1 + np.minimum(3, a), [2, 3, 4, 4], np.int8)\n    assert eq(1 + np.maximum(a, 3), [4, 4, 4, 5], np.int8)\n    assert eq(1 + np.maximum(3, a), [4, 4, 4, 5], np.int8)\n    assert eq(1 + np.fmin(a, 3), [2, 3, 4, 4], np.int8)\n    assert eq(1 + np.fmin(3, a), [2, 3, 4, 4], np.int8)\n    assert eq(1 + np.fmax(a, 3), [4, 4, 4, 5], np.int8)\n    assert eq(1 + np.fmax(3, a), [4, 4, 4, 5], np.int8)\n    assert eq(1 + np.sin(a), [1.f16 + np.sin(1.f16), 1.f16 + np.sin(2.f16), 1.f16 + np.sin(3.f16), 1.f16 + np.sin(4.f16)], np.float16)\n    assert eq(1 + np.cos(a), [1.f16 + np.cos(1.f16), 1.f16 + np.cos(2.f16), 1.f16 + np.cos(3.f16), 1.f16 + np.cos(4.f16)], np.float16)\n    assert eq(1 + np.tan(a), [1.f16 + np.tan(1.f16), 1.f16 + np.tan(2.f16), 1.f16 + np.tan(3.f16), 1.f16 + np.tan(4.f16)], np.float16)\n    assert eq(1 + np.arcsin(a), [1.f16 + np.arcsin(1.f16), 1.f16 + np.arcsin(2.f16), 1.f16 + np.arcsin(3.f16), 1.f16 + np.arcsin(4.f16)], np.float16)\n    assert eq(1 + np.arccos(a), [1.f16 + np.arccos(1.f16), 1.f16 + np.arccos(2.f16), 1.f16 + np.arccos(3.f16), 1.f16 + np.arccos(4.f16)], np.float16)\n    assert eq(1 + np.arctan(a), [1.f16 + np.arctan(1.f16), 1.f16 + np.arctan(2.f16), 1.f16 + np.arctan(3.f16), 1.f16 + np.arctan(4.f16)], np.float16)\n    assert eq(1 + np.arctan2(a, 3.f16), [1.f16 + np.arctan2(1.f16, 3.f16), 1.f16 + np.arctan2(2.f16, 3.f16), 1.f16 + np.arctan2(3.f16, 3.f16), 1.f16 + np.arctan2(4.f16, 3.f16)], np.float16)\n    assert eq(1 + np.arctan2(3.f16, a), [1.f16 + np.arctan2(3.f16, 1.f16), 1.f16 + np.arctan2(3.f16, 2.f16), 1.f16 + np.arctan2(3.f16, 3.f16), 1.f16 + np.arctan2(3.f16, 4.f16)], np.float16)\n    assert eq(1 + np.hypot(a, 3), [1.f16 + np.hypot(1.f16, 3.f16), 1.f16 + np.hypot(2.f16, 3.f16), 1.f16 + np.hypot(3.f16, 3.f16), 1.f16 + np.hypot(4.f16, 3.f16)], np.float16)\n    assert eq(1 + np.hypot(3, a), [1.f16 + np.hypot(3.f16, 1.f16), 1.f16 + np.hypot(3.f16, 2.f16), 1.f16 + np.hypot(3.f16, 3.f16), 1.f16 + np.hypot(3.f16, 4.f16)], np.float16)\n    assert eq(1 + np.sinh(a), [1.f16 + np.sinh(1.f16), 1.f16 + np.sinh(2.f16), 1.f16 + np.sinh(3.f16), 1.f16 + np.sinh(4.f16)], np.float16)\n    assert eq(1 + np.cosh(a), [1.f16 + np.cosh(1.f16), 1.f16 + np.cosh(2.f16), 1.f16 + np.cosh(3.f16), 1.f16 + np.cosh(4.f16)], np.float16)\n    assert eq(1 + np.tanh(a), [1.f16 + np.tanh(1.f16), 1.f16 + np.tanh(2.f16), 1.f16 + np.tanh(3.f16), 1.f16 + np.tanh(4.f16)], np.float16)\n    assert eq(1 + np.arcsinh(a), [1.f16 + np.arcsinh(1.f16), 1.f16 + np.arcsinh(2.f16), 1.f16 + np.arcsinh(3.f16), 1.f16 + np.arcsinh(4.f16)], np.float16)\n    assert eq(1 + np.arccosh(a), [1.f16 + np.arccosh(1.f16), 1.f16 + np.arccosh(2.f16), 1.f16 + np.arccosh(3.f16), 1.f16 + np.arccosh(4.f16)], np.float16)\n    assert eq(1 + np.arctanh(a), [1.f16 + np.arctanh(1.f16), 1.f16 + np.arctanh(2.f16), 1.f16 + np.arctanh(3.f16), 1.f16 + np.arctanh(4.f16)], np.float16)\n    assert eq(1 + np.conj(a), [1 + np.conj(1), 1 + np.conj(2), 1 + np.conj(3), 1 + np.conj(4)], np.int8)\n    assert eq(1 + np.exp(a), [1.f16 + np.exp(1.f16), 1.f16 + np.exp(2.f16), 1.f16 + np.exp(3.f16), 1.f16 + np.exp(4.f16)], np.float16)\n    assert eq(1 + np.exp2(a), [1.f16 + np.exp2(1.f16), 1.f16 + np.exp2(2.f16), 1.f16 + np.exp2(3.f16), 1.f16 + np.exp2(4.f16)], np.float16)\n    assert eq(1 + np.log(a), [1.f16 + np.log(1.f16), 1.f16 + np.log(2.f16), 1.f16 + np.log(3.f16), 1.f16 + np.log(4.f16)], np.float16)\n    assert eq(1 + np.log2(a), [1.f16 + np.log2(1.f16), 1.f16 + np.log2(2.f16), 1.f16 + np.log2(3.f16), 1.f16 + np.log2(4.f16)], np.float16)\n    assert eq(1 + np.log10(a), [1.f16 + np.log10(1.f16), 1.f16 + np.log10(2.f16), 1.f16 + np.log10(3.f16), 1.f16 + np.log10(4.f16)], np.float16)\n    assert eq(1 + np.expm1(a), [1.f16 + np.expm1(1.f16), 1.f16 + np.expm1(2.f16), 1.f16 + np.expm1(3.f16), 1.f16 + np.expm1(4.f16)], np.float16)\n    assert eq(1 + np.log1p(a), [1.f16 + np.log1p(1.f16), 1.f16 + np.log1p(2.f16), 1.f16 + np.log1p(3.f16), 1.f16 + np.log1p(4.f16)], np.float16)\n    assert eq(1 + np.sqrt(a), [1.f16 + np.sqrt(1.f16), 1.f16 + np.sqrt(2.f16), 1.f16 + np.sqrt(3.f16), 1.f16 + np.sqrt(4.f16)], np.float16)\n    assert eq(1 + np.square(a), [1 + np.square(1), 1 + np.square(2), 1 + np.square(3), 1 + np.square(4)], np.int8)\n    assert eq(1 + np.cbrt(a), [1.f16 + np.cbrt(1.f16), 1.f16 + np.cbrt(2.f16), 1.f16 + np.cbrt(3.f16), 1.f16 + np.cbrt(4.f16)], np.float16)\n    assert eq(1 + np.logaddexp(a, 3), [1.f16 + np.logaddexp(1.f16, 3.f16), 1.f16 + np.logaddexp(2.f16, 3.f16), 1.f16 + np.logaddexp(3.f16, 3.f16), 1.f16 + np.logaddexp(4.f16, 3.f16)], np.float16)\n    assert eq(1 + np.logaddexp(3, a), [1.f16 + np.logaddexp(3.f16, 1.f16), 1.f16 + np.logaddexp(3.f16, 2.f16), 1.f16 + np.logaddexp(3.f16, 3.f16), 1.f16 + np.logaddexp(3.f16, 4.f16)], np.float16)\n    assert eq(1 + np.logaddexp2(a, 3), [1.f16 + np.logaddexp2(1.f16, 3.f16), 1.f16 + np.logaddexp2(2.f16, 3.f16), 1.f16 + np.logaddexp2(3.f16, 3.f16), 1.f16 + np.logaddexp2(4.f16, 3.f16)], np.float16)\n    assert eq(1 + np.logaddexp2(3, a), [1.f16 + np.logaddexp2(3.f16, 1.f16), 1.f16 + np.logaddexp2(3.f16, 2.f16), 1.f16 + np.logaddexp2(3.f16, 3.f16), 1.f16 + np.logaddexp2(3.f16, 4.f16)], np.float16)\n    assert eq(1 + np.reciprocal(a), [2, 1, 1, 1], np.int8)\n    assert eq(1 + np.rint(a), [1.f16 + np.rint(1.f16), 1.f16 + np.rint(2.f16), 1.f16 + np.rint(3.f16), 1.f16 + np.rint(4.f16)], np.float16)\n    assert eq(1 + np.floor(a), [1.f16 + np.floor(1.f16), 1.f16 + np.floor(2.f16), 1.f16 + np.floor(3.f16), 1.f16 + np.floor(4.f16)], np.float16)\n    assert eq(1 + np.ceil(a), [1.f16 + np.ceil(1.f16), 1.f16 + np.ceil(2.f16), 1.f16 + np.ceil(3.f16), 1.f16 + np.ceil(4.f16)], np.float16)\n    assert eq(1 + np.trunc(a), [1.f16 + np.trunc(1.f16), 1.f16 + np.trunc(2.f16), 1.f16 + np.trunc(3.f16), 1.f16 + np.trunc(4.f16)], np.float16)\n    assert eq(1 + np.isnan(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isinf(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isfinite(a), [2, 2, 2, 2], int)\n    assert eq(1 + np.ldexp(a, 1), [1.f16 + np.ldexp(1.f16, 1), 1.f16 + np.ldexp(2.f16, 1), 1.f16 + np.ldexp(3.f16, 1), 1.f16 + np.ldexp(4.f16, 1)], np.float16)\n    assert eq(1 + np.ldexp(1, a), [1.f16 + np.ldexp(1.f16, 1), 1.f16 + np.ldexp(1.f16, 2), 1.f16 + np.ldexp(1.f16, 3), 1.f16 + np.ldexp(1.f16, 4)], np.float16)\n    assert eq(1 + np.sign(a), [2, 2, 2, 2], np.int8)\n    assert eq(1 + np.signbit(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.copysign(a, -1), [1.f16 + np.copysign(1.f16, -1.f16), 1.f16 + np.copysign(2.f16, -1.f16), 1.f16 + np.copysign(3.f16, -1.f16), 1.f16 + np.copysign(4.f16, -1.f16)], np.float16)\n    assert eq(1 + np.copysign(-1, a), [1.f16 + np.copysign(-1.f16, 1.f16), 1.f16 + np.copysign(-1.f16, 2.f16), 1.f16 + np.copysign(-1.f16, 3.f16), 1.f16 + np.copysign(-1.f16, 4.f16)], np.float16)\n    assert eq(1 + np.spacing(a), [1.f16 + np.spacing(1.f16), 1.f16 + np.spacing(2.f16), 1.f16 + np.spacing(3.f16), 1.f16 + np.spacing(4.f16)], np.float16)\n    assert eq(1 + np.nextafter(a, 1), [1.f16 + np.nextafter(1.f16, 1.f16), 1.f16 + np.nextafter(2.f16, 1.f16), 1.f16 + np.nextafter(3.f16, 1.f16), 1.f16 + np.nextafter(4.f16, 1.f16)], np.float16)\n    assert eq(1 + np.nextafter(1, a), [1.f16 + np.nextafter(1.f16, 1.f16), 1.f16 + np.nextafter(1.f16, 2.f16), 1.f16 + np.nextafter(1.f16, 3.f16), 1.f16 + np.nextafter(1.f16, 4.f16)], np.float16)\n    assert eq(1 + np.deg2rad(a), [1.f16 + np.deg2rad(1.f16), 1.f16 + np.deg2rad(2.f16), 1.f16 + np.deg2rad(3.f16), 1.f16 + np.deg2rad(4.f16)], np.float16)\n    assert eq(1 + np.rad2deg(a), [1.f16 + np.rad2deg(1.f16), 1.f16 + np.rad2deg(2.f16), 1.f16 + np.rad2deg(3.f16), 1.f16 + np.rad2deg(4.f16)], np.float16)\n    assert eq(1 + np.heaviside(a, 1), [1.f16 + np.heaviside(1.f16, 1.f16), 1.f16 + np.heaviside(2.f16, 1.f16), 1.f16 + np.heaviside(3.f16, 1.f16), 1.f16 + np.heaviside(4.f16, 1.f16)], np.float16)\n    assert eq(1 + np.heaviside(1, a), [1.f16 + np.heaviside(1.f16, 1.f16), 1.f16 + np.heaviside(1.f16, 2.f16), 1.f16 + np.heaviside(1.f16, 3.f16), 1.f16 + np.heaviside(1.f16, 4.f16)], np.float16)\n\n@test\ndef test_ops_uint64():\n    a = np.array([1, 2, 3, 4], dtype=np.uint64)\n    assert eq(1 + (+a), [2, 3, 4, 5], np.uint64)\n    assert eq(1 + (-a), [0, -1, -2, -3], np.uint64)\n    assert eq(1 + (~a), [-1, -2, -3, -4], np.uint64)\n    assert eq(1 + np.abs(a), [2, 3, 4, 5], np.uint64)\n    assert eq(1 + abs(a), [2, 3, 4, 5], np.uint64)\n    assert eq(2 * (1 + a), [4, 6, 8, 10], np.uint64)\n    assert eq(2 * (a + 1), [4, 6, 8, 10], np.uint64)\n    assert eq(2 * (a - 1), [0, 2, 4, 6], np.uint64)\n    assert eq(2 * (1 - a), [0, -2, -4, -6], np.uint64)\n    assert eq(1 + (a * 2), [3, 5, 7, 9], np.uint64)\n    assert eq(1 + (2 * a), [3, 5, 7, 9], np.uint64)\n    assert eq(1 + (a.reshape(1, -1) @ a.reshape(-1, 1)), [[31]], np.uint64)\n    assert eq(1 + (a / 2), [1.5, 2., 2.5, 3.], float)\n    assert eq(1 + (2 / a), [3., 2., 1 + 2/3, 1.5], float)\n    assert eq(1 + (a // 2), [1, 2, 2, 3], np.uint64)\n    assert eq(1 + (10 // a), [11, 6, 4, 3], np.uint64)\n    assert eq(1 + (a % 3), [2, 3, 1, 2], np.uint64)\n    assert eq(1 + (10 % a), [1, 1, 2, 3], np.uint64)\n    assert eq(1 + np.fmod(a, 3), [2, 3, 1, 2], np.uint64)\n    assert eq(1 + np.fmod(10, a), [1, 1, 2, 3], np.uint64)\n    assert eq(1 + (a ** 3), [2, 9, 28, 65], np.uint64)\n    assert eq(1 + (3 ** a), [4, 10, 28, 82], np.uint64)\n    assert eq(1 + (a << 3), [9, 17, 25, 33], np.uint64)\n    assert eq(1 + (3 << a), [7, 13, 25, 49], np.uint64)\n    assert eq(1 + (a >> 1), [1, 2, 2, 3], np.uint64)\n    assert eq(1 + (1000 >> a), [501, 251, 126, 63], np.uint64)\n    assert eq(1 + (a & 2), [1, 3, 3, 1], np.uint64)\n    assert eq(1 + (2 & a), [1, 3, 3, 1], np.uint64)\n    assert eq(1 + (a | 2), [4, 3, 4, 7], np.uint64)\n    assert eq(1 + (2 | a), [4, 3, 4, 7], np.uint64)\n    assert eq(1 + (a ^ 2), [4, 1, 2, 7], np.uint64)\n    assert eq(1 + (2 ^ a), [4, 1, 2, 7], np.uint64)\n    assert eq(np.logical_and(a, a & 1), [True, False, True, False], bool)\n    assert eq(np.logical_or(a & 2, a & 1), [True, True, True, False], bool)\n    assert eq(np.logical_xor(a, a & 1), [False, True, False, True], bool)\n    assert eq((a + 1) == 2, [True, False, False, False], bool)\n    assert eq(2 == (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) != 2, [False, True, True, True], bool)\n    assert eq(2 != (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) < 3, [True, False, False, False], bool)\n    assert eq(3 < (a + 1), [False, False, True, True], bool)\n    assert eq((a + 1) > 3, [False, False, True, True], bool)\n    assert eq(3 > (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) <= 3, [True, True, False, False], bool)\n    assert eq(3 <= (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) >= 3, [False, True, True, True], bool)\n    assert eq(3 >= (a + 1), [True, True, False, False], bool)\n    assert eq(1 + np.minimum(a, 3), [2, 3, 4, 4], np.uint64)\n    assert eq(1 + np.minimum(3, a), [2, 3, 4, 4], np.uint64)\n    assert eq(1 + np.maximum(a, 3), [4, 4, 4, 5], np.uint64)\n    assert eq(1 + np.maximum(3, a), [4, 4, 4, 5], np.uint64)\n    assert eq(1 + np.fmin(a, 3), [2, 3, 4, 4], np.uint64)\n    assert eq(1 + np.fmin(3, a), [2, 3, 4, 4], np.uint64)\n    assert eq(1 + np.fmax(a, 3), [4, 4, 4, 5], np.uint64)\n    assert eq(1 + np.fmax(3, a), [4, 4, 4, 5], np.uint64)\n    assert eq(1 + np.sin(a), [1 + np.sin(1), 1 + np.sin(2), 1 + np.sin(3), 1 + np.sin(4)], float)\n    assert eq(1 + np.cos(a), [1 + np.cos(1), 1 + np.cos(2), 1 + np.cos(3), 1 + np.cos(4)], float)\n    assert eq(1 + np.tan(a), [1 + np.tan(1), 1 + np.tan(2), 1 + np.tan(3), 1 + np.tan(4)], float)\n    assert eq(1 + np.arcsin(a), [1 + np.arcsin(1), 1 + np.arcsin(2), 1 + np.arcsin(3), 1 + np.arcsin(4)], float)\n    assert eq(1 + np.arccos(a), [1 + np.arccos(1), 1 + np.arccos(2), 1 + np.arccos(3), 1 + np.arccos(4)], float)\n    assert eq(1 + np.arctan(a), [1 + np.arctan(1), 1 + np.arctan(2), 1 + np.arctan(3), 1 + np.arctan(4)], float)\n    assert eq(1 + np.arctan2(a, 3), [1 + np.arctan2(1, 3), 1 + np.arctan2(2, 3), 1 + np.arctan2(3, 3), 1 + np.arctan2(4, 3)], float)\n    assert eq(1 + np.arctan2(3, a), [1 + np.arctan2(3, 1), 1 + np.arctan2(3, 2), 1 + np.arctan2(3, 3), 1 + np.arctan2(3, 4)], float)\n    assert eq(1 + np.hypot(a, 3), [1 + np.hypot(1, 3), 1 + np.hypot(2, 3), 1 + np.hypot(3, 3), 1 + np.hypot(4, 3)], float)\n    assert eq(1 + np.hypot(3, a), [1 + np.hypot(3, 1), 1 + np.hypot(3, 2), 1 + np.hypot(3, 3), 1 + np.hypot(3, 4)], float)\n    assert eq(1 + np.sinh(a), [1 + np.sinh(1), 1 + np.sinh(2), 1 + np.sinh(3), 1 + np.sinh(4)], float)\n    assert eq(1 + np.cosh(a), [1 + np.cosh(1), 1 + np.cosh(2), 1 + np.cosh(3), 1 + np.cosh(4)], float)\n    assert eq(1 + np.tanh(a), [1 + np.tanh(1), 1 + np.tanh(2), 1 + np.tanh(3), 1 + np.tanh(4)], float)\n    assert eq(1 + np.arcsinh(a), [1 + np.arcsinh(1), 1 + np.arcsinh(2), 1 + np.arcsinh(3), 1 + np.arcsinh(4)], float)\n    assert eq(1 + np.arccosh(a), [1 + np.arccosh(1), 1 + np.arccosh(2), 1 + np.arccosh(3), 1 + np.arccosh(4)], float)\n    assert eq(1 + np.arctanh(a), [1 + np.arctanh(1), 1 + np.arctanh(2), 1 + np.arctanh(3), 1 + np.arctanh(4)], float)\n    assert eq(1 + np.conj(a), [1 + np.conj(1), 1 + np.conj(2), 1 + np.conj(3), 1 + np.conj(4)], np.uint64)\n    assert eq(1 + np.exp(a), [1 + np.exp(1), 1 + np.exp(2), 1 + np.exp(3), 1 + np.exp(4)], float)\n    assert eq(1 + np.exp2(a), [1 + np.exp2(1), 1 + np.exp2(2), 1 + np.exp2(3), 1 + np.exp2(4)], float)\n    assert eq(1 + np.log(a), [1 + np.log(1), 1 + np.log(2), 1 + np.log(3), 1 + np.log(4)], float)\n    assert eq(1 + np.log2(a), [1 + np.log2(1), 1 + np.log2(2), 1 + np.log2(3), 1 + np.log2(4)], float)\n    assert eq(1 + np.log10(a), [1 + np.log10(1), 1 + np.log10(2), 1 + np.log10(3), 1 + np.log10(4)], float)\n    assert eq(1 + np.expm1(a), [1 + np.expm1(1), 1 + np.expm1(2), 1 + np.expm1(3), 1 + np.expm1(4)], float)\n    assert eq(1 + np.log1p(a), [1 + np.log1p(1), 1 + np.log1p(2), 1 + np.log1p(3), 1 + np.log1p(4)], float)\n    assert eq(1 + np.sqrt(a), [1 + np.sqrt(1), 1 + np.sqrt(2), 1 + np.sqrt(3), 1 + np.sqrt(4)], float)\n    assert eq(1 + np.square(a), [1 + np.square(1), 1 + np.square(2), 1 + np.square(3), 1 + np.square(4)], np.uint64)\n    assert eq(1 + np.cbrt(a), [1 + np.cbrt(1), 1 + np.cbrt(2), 1 + np.cbrt(3), 1 + np.cbrt(4)], float)\n    assert eq(1 + np.logaddexp(a, 3), [1 + np.logaddexp(1, 3), 1 + np.logaddexp(2, 3), 1 + np.logaddexp(3, 3), 1 + np.logaddexp(4, 3)], float)\n    assert eq(1 + np.logaddexp(3, a), [1 + np.logaddexp(3, 1), 1 + np.logaddexp(3, 2), 1 + np.logaddexp(3, 3), 1 + np.logaddexp(3, 4)], float)\n    assert eq(1 + np.logaddexp2(a, 3), [1 + np.logaddexp2(1, 3), 1 + np.logaddexp2(2, 3), 1 + np.logaddexp2(3, 3), 1 + np.logaddexp2(4, 3)], float)\n    assert eq(1 + np.logaddexp2(3, a), [1 + np.logaddexp2(3, 1), 1 + np.logaddexp2(3, 2), 1 + np.logaddexp2(3, 3), 1 + np.logaddexp2(3, 4)], float)\n    assert eq(1 + np.reciprocal(a), [2, 1, 1, 1], np.uint64)\n    assert eq(1 + np.rint(a), [1 + np.rint(1), 1 + np.rint(2), 1 + np.rint(3), 1 + np.rint(4)], float)\n    assert eq(1 + np.floor(a), [1 + np.floor(1), 1 + np.floor(2), 1 + np.floor(3), 1 + np.floor(4)], float)\n    assert eq(1 + np.ceil(a), [1 + np.ceil(1), 1 + np.ceil(2), 1 + np.ceil(3), 1 + np.ceil(4)], float)\n    assert eq(1 + np.trunc(a), [1 + np.trunc(1), 1 + np.trunc(2), 1 + np.trunc(3), 1 + np.trunc(4)], float)\n    assert eq(1 + np.isnan(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isinf(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isfinite(a), [2, 2, 2, 2], int)\n    assert eq(1 + np.ldexp(a, 1), [1 + np.ldexp(1, 1), 1 + np.ldexp(2, 1), 1 + np.ldexp(3, 1), 1 + np.ldexp(4, 1)], float)\n    assert eq(1 + np.ldexp(1, a), [1 + np.ldexp(1, 1), 1 + np.ldexp(1, 2), 1 + np.ldexp(1, 3), 1 + np.ldexp(1, 4)], float)\n    assert eq(1 + np.sign(a), [2, 2, 2, 2], np.uint64)\n    assert eq(1 + np.signbit(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.copysign(a, -1), [1 + np.copysign(1, -1), 1 + np.copysign(2, -1), 1 + np.copysign(3, -1), 1 + np.copysign(4, -1)], float)\n    assert eq(1 + np.copysign(-1, a), [1 + np.copysign(-1, 1), 1 + np.copysign(-1, 2), 1 + np.copysign(-1, 3), 1 + np.copysign(-1, 4)], float)\n    assert eq(1 + np.spacing(a), [1 + np.spacing(1), 1 + np.spacing(2), 1 + np.spacing(3), 1 + np.spacing(4)], float)\n    assert eq(1 + np.nextafter(a, 1), [1 + np.nextafter(1, 1), 1 + np.nextafter(2, 1), 1 + np.nextafter(3, 1), 1 + np.nextafter(4, 1)], float)\n    assert eq(1 + np.nextafter(1, a), [1 + np.nextafter(1, 1), 1 + np.nextafter(1, 2), 1 + np.nextafter(1, 3), 1 + np.nextafter(1, 4)], float)\n    assert eq(1 + np.deg2rad(a), [1 + np.deg2rad(1), 1 + np.deg2rad(2), 1 + np.deg2rad(3), 1 + np.deg2rad(4)], float)\n    assert eq(1 + np.rad2deg(a), [1 + np.rad2deg(1), 1 + np.rad2deg(2), 1 + np.rad2deg(3), 1 + np.rad2deg(4)], float)\n    assert eq(1 + np.heaviside(a, 1), [1 + np.heaviside(1, 1), 1 + np.heaviside(2, 1), 1 + np.heaviside(3, 1), 1 + np.heaviside(4, 1)], float)\n    assert eq(1 + np.heaviside(1, a), [1 + np.heaviside(1, 1), 1 + np.heaviside(1, 2), 1 + np.heaviside(1, 3), 1 + np.heaviside(1, 4)], float)\n\n@test\ndef test_ops_uint32():\n    a = np.array([1, 2, 3, 4], np.uint32)\n    assert eq(1 + (+a), [2, 3, 4, 5], np.uint32)\n    assert eq(1 + (-a), [0, -1, -2, -3], np.uint32)\n    assert eq(1 + (~a), [-1, -2, -3, -4], np.uint32)\n    assert eq(1 + np.abs(a), [2, 3, 4, 5], np.uint32)\n    assert eq(1 + abs(a), [2, 3, 4, 5], np.uint32)\n    assert eq(2 * (1 + a), [4, 6, 8, 10], np.uint32)\n    assert eq(2 * (a + 1), [4, 6, 8, 10], np.uint32)\n    assert eq(2 * (a - 1), [0, 2, 4, 6], np.uint32)\n    assert eq(2 * (1 - a), [0, -2, -4, -6], np.uint32)\n    assert eq(1 + (a * 2), [3, 5, 7, 9], np.uint32)\n    assert eq(1 + (2 * a), [3, 5, 7, 9], np.uint32)\n    assert eq(1 + (a.reshape(1, -1) @ a.reshape(-1, 1)), [[31]], np.uint32)\n    assert eq(1 + (a / 2), [1.5, 2., 2.5, 3.], float)\n    assert eq(1 + (2 / a), [3., 2., 1 + 2/3, 1.5], float)\n    assert eq(1 + (a // 2), [1, 2, 2, 3], np.uint32)\n    assert eq(1 + (10 // a), [11, 6, 4, 3], np.uint32)\n    assert eq(1 + (a % 3), [2, 3, 1, 2], np.uint32)\n    assert eq(1 + (10 % a), [1, 1, 2, 3], np.uint32)\n    assert eq(1 + np.fmod(a, 3), [2, 3, 1, 2], np.uint32)\n    assert eq(1 + np.fmod(10, a), [1, 1, 2, 3], np.uint32)\n    assert eq(1 + (a ** 3), [2, 9, 28, 65], np.uint32)\n    assert eq(1 + (3 ** a), [4, 10, 28, 82], np.uint32)\n    assert eq(1 + (a << 3), [9, 17, 25, 33], np.uint32)\n    assert eq(1 + (3 << a), [7, 13, 25, 49], np.uint32)\n    assert eq(1 + (a >> 1), [1, 2, 2, 3], np.uint32)\n    assert eq(1 + (1000 >> a), [501, 251, 126, 63], np.uint32)\n    assert eq(1 + (a & 2), [1, 3, 3, 1], np.uint32)\n    assert eq(1 + (2 & a), [1, 3, 3, 1], np.uint32)\n    assert eq(1 + (a | 2), [4, 3, 4, 7], np.uint32)\n    assert eq(1 + (2 | a), [4, 3, 4, 7], np.uint32)\n    assert eq(1 + (a ^ 2), [4, 1, 2, 7], np.uint32)\n    assert eq(1 + (2 ^ a), [4, 1, 2, 7], np.uint32)\n    assert eq(np.logical_and(a, a & 1), [True, False, True, False], bool)\n    assert eq(np.logical_or(a & 2, a & 1), [True, True, True, False], bool)\n    assert eq(np.logical_xor(a, a & 1), [False, True, False, True], bool)\n    assert eq((a + 1) == 2, [True, False, False, False], bool)\n    assert eq(2 == (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) != 2, [False, True, True, True], bool)\n    assert eq(2 != (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) < 3, [True, False, False, False], bool)\n    assert eq(3 < (a + 1), [False, False, True, True], bool)\n    assert eq((a + 1) > 3, [False, False, True, True], bool)\n    assert eq(3 > (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) <= 3, [True, True, False, False], bool)\n    assert eq(3 <= (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) >= 3, [False, True, True, True], bool)\n    assert eq(3 >= (a + 1), [True, True, False, False], bool)\n    assert eq(1 + np.minimum(a, 3), [2, 3, 4, 4], np.uint32)\n    assert eq(1 + np.minimum(3, a), [2, 3, 4, 4], np.uint32)\n    assert eq(1 + np.maximum(a, 3), [4, 4, 4, 5], np.uint32)\n    assert eq(1 + np.maximum(3, a), [4, 4, 4, 5], np.uint32)\n    assert eq(1 + np.fmin(a, 3), [2, 3, 4, 4], np.uint32)\n    assert eq(1 + np.fmin(3, a), [2, 3, 4, 4], np.uint32)\n    assert eq(1 + np.fmax(a, 3), [4, 4, 4, 5], np.uint32)\n    assert eq(1 + np.fmax(3, a), [4, 4, 4, 5], np.uint32)\n    assert eq(1 + np.sin(a), [1 + np.sin(1), 1 + np.sin(2), 1 + np.sin(3), 1 + np.sin(4)], float)\n    assert eq(1 + np.cos(a), [1 + np.cos(1), 1 + np.cos(2), 1 + np.cos(3), 1 + np.cos(4)], float)\n    assert eq(1 + np.tan(a), [1 + np.tan(1), 1 + np.tan(2), 1 + np.tan(3), 1 + np.tan(4)], float)\n    assert eq(1 + np.arcsin(a), [1 + np.arcsin(1), 1 + np.arcsin(2), 1 + np.arcsin(3), 1 + np.arcsin(4)], float)\n    assert eq(1 + np.arccos(a), [1 + np.arccos(1), 1 + np.arccos(2), 1 + np.arccos(3), 1 + np.arccos(4)], float)\n    assert eq(1 + np.arctan(a), [1 + np.arctan(1), 1 + np.arctan(2), 1 + np.arctan(3), 1 + np.arctan(4)], float)\n    assert eq(1 + np.arctan2(a, 3), [1 + np.arctan2(1, 3), 1 + np.arctan2(2, 3), 1 + np.arctan2(3, 3), 1 + np.arctan2(4, 3)], float)\n    assert eq(1 + np.arctan2(3, a), [1 + np.arctan2(3, 1), 1 + np.arctan2(3, 2), 1 + np.arctan2(3, 3), 1 + np.arctan2(3, 4)], float)\n    assert eq(1 + np.hypot(a, 3), [1 + np.hypot(1, 3), 1 + np.hypot(2, 3), 1 + np.hypot(3, 3), 1 + np.hypot(4, 3)], float)\n    assert eq(1 + np.hypot(3, a), [1 + np.hypot(3, 1), 1 + np.hypot(3, 2), 1 + np.hypot(3, 3), 1 + np.hypot(3, 4)], float)\n    assert eq(1 + np.sinh(a), [1 + np.sinh(1), 1 + np.sinh(2), 1 + np.sinh(3), 1 + np.sinh(4)], float)\n    assert eq(1 + np.cosh(a), [1 + np.cosh(1), 1 + np.cosh(2), 1 + np.cosh(3), 1 + np.cosh(4)], float)\n    assert eq(1 + np.tanh(a), [1 + np.tanh(1), 1 + np.tanh(2), 1 + np.tanh(3), 1 + np.tanh(4)], float)\n    assert eq(1 + np.arcsinh(a), [1 + np.arcsinh(1), 1 + np.arcsinh(2), 1 + np.arcsinh(3), 1 + np.arcsinh(4)], float)\n    assert eq(1 + np.arccosh(a), [1 + np.arccosh(1), 1 + np.arccosh(2), 1 + np.arccosh(3), 1 + np.arccosh(4)], float)\n    assert eq(1 + np.arctanh(a), [1 + np.arctanh(1), 1 + np.arctanh(2), 1 + np.arctanh(3), 1 + np.arctanh(4)], float)\n    assert eq(1 + np.conj(a), [1 + np.conj(1), 1 + np.conj(2), 1 + np.conj(3), 1 + np.conj(4)], np.uint32)\n    assert eq(1 + np.exp(a), [1 + np.exp(1), 1 + np.exp(2), 1 + np.exp(3), 1 + np.exp(4)], float)\n    assert eq(1 + np.exp2(a), [1 + np.exp2(1), 1 + np.exp2(2), 1 + np.exp2(3), 1 + np.exp2(4)], float)\n    assert eq(1 + np.log(a), [1 + np.log(1), 1 + np.log(2), 1 + np.log(3), 1 + np.log(4)], float)\n    assert eq(1 + np.log2(a), [1 + np.log2(1), 1 + np.log2(2), 1 + np.log2(3), 1 + np.log2(4)], float)\n    assert eq(1 + np.log10(a), [1 + np.log10(1), 1 + np.log10(2), 1 + np.log10(3), 1 + np.log10(4)], float)\n    assert eq(1 + np.expm1(a), [1 + np.expm1(1), 1 + np.expm1(2), 1 + np.expm1(3), 1 + np.expm1(4)], float)\n    assert eq(1 + np.log1p(a), [1 + np.log1p(1), 1 + np.log1p(2), 1 + np.log1p(3), 1 + np.log1p(4)], float)\n    assert eq(1 + np.sqrt(a), [1 + np.sqrt(1), 1 + np.sqrt(2), 1 + np.sqrt(3), 1 + np.sqrt(4)], float)\n    assert eq(1 + np.square(a), [1 + np.square(1), 1 + np.square(2), 1 + np.square(3), 1 + np.square(4)], np.uint32)\n    assert eq(1 + np.cbrt(a), [1 + np.cbrt(1), 1 + np.cbrt(2), 1 + np.cbrt(3), 1 + np.cbrt(4)], float)\n    assert eq(1 + np.logaddexp(a, 3), [1 + np.logaddexp(1, 3), 1 + np.logaddexp(2, 3), 1 + np.logaddexp(3, 3), 1 + np.logaddexp(4, 3)], float)\n    assert eq(1 + np.logaddexp(3, a), [1 + np.logaddexp(3, 1), 1 + np.logaddexp(3, 2), 1 + np.logaddexp(3, 3), 1 + np.logaddexp(3, 4)], float)\n    assert eq(1 + np.logaddexp2(a, 3), [1 + np.logaddexp2(1, 3), 1 + np.logaddexp2(2, 3), 1 + np.logaddexp2(3, 3), 1 + np.logaddexp2(4, 3)], float)\n    assert eq(1 + np.logaddexp2(3, a), [1 + np.logaddexp2(3, 1), 1 + np.logaddexp2(3, 2), 1 + np.logaddexp2(3, 3), 1 + np.logaddexp2(3, 4)], float)\n    assert eq(1 + np.reciprocal(a), [2, 1, 1, 1], np.uint32)\n    assert eq(1 + np.rint(a), [1 + np.rint(1), 1 + np.rint(2), 1 + np.rint(3), 1 + np.rint(4)], float)\n    assert eq(1 + np.floor(a), [1 + np.floor(1), 1 + np.floor(2), 1 + np.floor(3), 1 + np.floor(4)], float)\n    assert eq(1 + np.ceil(a), [1 + np.ceil(1), 1 + np.ceil(2), 1 + np.ceil(3), 1 + np.ceil(4)], float)\n    assert eq(1 + np.trunc(a), [1 + np.trunc(1), 1 + np.trunc(2), 1 + np.trunc(3), 1 + np.trunc(4)], float)\n    assert eq(1 + np.isnan(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isinf(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isfinite(a), [2, 2, 2, 2], int)\n    assert eq(1 + np.ldexp(a, 1), [1 + np.ldexp(1, 1), 1 + np.ldexp(2, 1), 1 + np.ldexp(3, 1), 1 + np.ldexp(4, 1)], float)\n    assert eq(1 + np.ldexp(1, a), [1 + np.ldexp(1, 1), 1 + np.ldexp(1, 2), 1 + np.ldexp(1, 3), 1 + np.ldexp(1, 4)], float)\n    assert eq(1 + np.sign(a), [2, 2, 2, 2], np.uint32)\n    assert eq(1 + np.signbit(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.copysign(a, -1), [1 + np.copysign(1, -1), 1 + np.copysign(2, -1), 1 + np.copysign(3, -1), 1 + np.copysign(4, -1)], float)\n    assert eq(1 + np.copysign(-1, a), [1 + np.copysign(-1, 1), 1 + np.copysign(-1, 2), 1 + np.copysign(-1, 3), 1 + np.copysign(-1, 4)], float)\n    assert eq(1 + np.spacing(a), [1 + np.spacing(1), 1 + np.spacing(2), 1 + np.spacing(3), 1 + np.spacing(4)], float)\n    assert eq(1 + np.nextafter(a, 1), [1 + np.nextafter(1, 1), 1 + np.nextafter(2, 1), 1 + np.nextafter(3, 1), 1 + np.nextafter(4, 1)], float)\n    assert eq(1 + np.nextafter(1, a), [1 + np.nextafter(1, 1), 1 + np.nextafter(1, 2), 1 + np.nextafter(1, 3), 1 + np.nextafter(1, 4)], float)\n    assert eq(1 + np.deg2rad(a), [1 + np.deg2rad(1), 1 + np.deg2rad(2), 1 + np.deg2rad(3), 1 + np.deg2rad(4)], float)\n    assert eq(1 + np.rad2deg(a), [1 + np.rad2deg(1), 1 + np.rad2deg(2), 1 + np.rad2deg(3), 1 + np.rad2deg(4)], float)\n    assert eq(1 + np.heaviside(a, 1), [1 + np.heaviside(1, 1), 1 + np.heaviside(2, 1), 1 + np.heaviside(3, 1), 1 + np.heaviside(4, 1)], float)\n    assert eq(1 + np.heaviside(1, a), [1 + np.heaviside(1, 1), 1 + np.heaviside(1, 2), 1 + np.heaviside(1, 3), 1 + np.heaviside(1, 4)], float)\n\n@test\ndef test_ops_uint16():\n    a = np.array([1, 2, 3, 4], np.uint16)\n    assert eq(1 + (+a), [2, 3, 4, 5], np.uint16)\n    assert eq(1 + (-a), [0, -1, -2, -3], np.uint16)\n    assert eq(1 + (~a), [-1, -2, -3, -4], np.uint16)\n    assert eq(1 + np.abs(a), [2, 3, 4, 5], np.uint16)\n    assert eq(1 + abs(a), [2, 3, 4, 5], np.uint16)\n    assert eq(2 * (1 + a), [4, 6, 8, 10], np.uint16)\n    assert eq(2 * (a + 1), [4, 6, 8, 10], np.uint16)\n    assert eq(2 * (a - 1), [0, 2, 4, 6], np.uint16)\n    assert eq(2 * (1 - a), [0, -2, -4, -6], np.uint16)\n    assert eq(1 + (a * 2), [3, 5, 7, 9], np.uint16)\n    assert eq(1 + (2 * a), [3, 5, 7, 9], np.uint16)\n    assert eq(1 + (a.reshape(1, -1) @ a.reshape(-1, 1)), [[31]], np.uint16)\n    assert eq(1 + (a / 2), [1.5, 2., 2.5, 3.], float)\n    assert eq(1 + (2 / a), [3., 2., 1 + 2/3, 1.5], float)\n    assert eq(1 + (a // 2), [1, 2, 2, 3], np.uint16)\n    assert eq(1 + (10 // a), [11, 6, 4, 3], np.uint16)\n    assert eq(1 + (a % 3), [2, 3, 1, 2], np.uint16)\n    assert eq(1 + (10 % a), [1, 1, 2, 3], np.uint16)\n    assert eq(1 + np.fmod(a, 3), [2, 3, 1, 2], np.uint16)\n    assert eq(1 + np.fmod(10, a), [1, 1, 2, 3], np.uint16)\n    assert eq(1 + (a ** 3), [2, 9, 28, 65], np.uint16)\n    assert eq(1 + (3 ** a), [4, 10, 28, 82], np.uint16)\n    assert eq(1 + (a << 3), [9, 17, 25, 33], np.uint16)\n    assert eq(1 + (3 << a), [7, 13, 25, 49], np.uint16)\n    assert eq(1 + (a >> 1), [1, 2, 2, 3], np.uint16)\n    assert eq(1 + (1000 >> a), [501, 251, 126, 63], np.uint16)\n    assert eq(1 + (a & 2), [1, 3, 3, 1], np.uint16)\n    assert eq(1 + (2 & a), [1, 3, 3, 1], np.uint16)\n    assert eq(1 + (a | 2), [4, 3, 4, 7], np.uint16)\n    assert eq(1 + (2 | a), [4, 3, 4, 7], np.uint16)\n    assert eq(1 + (a ^ 2), [4, 1, 2, 7], np.uint16)\n    assert eq(1 + (2 ^ a), [4, 1, 2, 7], np.uint16)\n    assert eq(np.logical_and(a, a & 1), [True, False, True, False], bool)\n    assert eq(np.logical_or(a & 2, a & 1), [True, True, True, False], bool)\n    assert eq(np.logical_xor(a, a & 1), [False, True, False, True], bool)\n    assert eq((a + 1) == 2, [True, False, False, False], bool)\n    assert eq(2 == (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) != 2, [False, True, True, True], bool)\n    assert eq(2 != (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) < 3, [True, False, False, False], bool)\n    assert eq(3 < (a + 1), [False, False, True, True], bool)\n    assert eq((a + 1) > 3, [False, False, True, True], bool)\n    assert eq(3 > (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) <= 3, [True, True, False, False], bool)\n    assert eq(3 <= (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) >= 3, [False, True, True, True], bool)\n    assert eq(3 >= (a + 1), [True, True, False, False], bool)\n    assert eq(1 + np.minimum(a, 3), [2, 3, 4, 4], np.uint16)\n    assert eq(1 + np.minimum(3, a), [2, 3, 4, 4], np.uint16)\n    assert eq(1 + np.maximum(a, 3), [4, 4, 4, 5], np.uint16)\n    assert eq(1 + np.maximum(3, a), [4, 4, 4, 5], np.uint16)\n    assert eq(1 + np.fmin(a, 3), [2, 3, 4, 4], np.uint16)\n    assert eq(1 + np.fmin(3, a), [2, 3, 4, 4], np.uint16)\n    assert eq(1 + np.fmax(a, 3), [4, 4, 4, 5], np.uint16)\n    assert eq(1 + np.fmax(3, a), [4, 4, 4, 5], np.uint16)\n    assert eq(1 + np.sin(a), [1.f32 + np.sin(1.f32), 1.f32 + np.sin(2.f32), 1.f32 + np.sin(3.f32), 1.f32 + np.sin(4.f32)], np.float32)\n    assert eq(1 + np.cos(a), [1.f32 + np.cos(1.f32), 1.f32 + np.cos(2.f32), 1.f32 + np.cos(3.f32), 1.f32 + np.cos(4.f32)], np.float32)\n    assert eq(1 + np.tan(a), [1.f32 + np.tan(1.f32), 1.f32 + np.tan(2.f32), 1.f32 + np.tan(3.f32), 1.f32 + np.tan(4.f32)], np.float32)\n    assert eq(1 + np.arcsin(a), [1.f32 + np.arcsin(1.f32), 1.f32 + np.arcsin(2.f32), 1.f32 + np.arcsin(3.f32), 1.f32 + np.arcsin(4.f32)], np.float32)\n    assert eq(1 + np.arccos(a), [1.f32 + np.arccos(1.f32), 1.f32 + np.arccos(2.f32), 1.f32 + np.arccos(3.f32), 1.f32 + np.arccos(4.f32)], np.float32)\n    assert eq(1 + np.arctan(a), [1.f32 + np.arctan(1.f32), 1.f32 + np.arctan(2.f32), 1.f32 + np.arctan(3.f32), 1.f32 + np.arctan(4.f32)], np.float32)\n    assert eq(1 + np.arctan2(a, 3.f32), [1.f32 + np.arctan2(1.f32, 3.f32), 1.f32 + np.arctan2(2.f32, 3.f32), 1.f32 + np.arctan2(3.f32, 3.f32), 1.f32 + np.arctan2(4.f32, 3.f32)], np.float32)\n    assert eq(1 + np.arctan2(3.f32, a), [1.f32 + np.arctan2(3.f32, 1.f32), 1.f32 + np.arctan2(3.f32, 2.f32), 1.f32 + np.arctan2(3.f32, 3.f32), 1.f32 + np.arctan2(3.f32, 4.f32)], np.float32)\n    assert eq(1 + np.hypot(a, 3), [1.f32 + np.hypot(1.f32, 3.f32), 1.f32 + np.hypot(2.f32, 3.f32), 1.f32 + np.hypot(3.f32, 3.f32), 1.f32 + np.hypot(4.f32, 3.f32)], np.float32)\n    assert eq(1 + np.hypot(3, a), [1.f32 + np.hypot(3.f32, 1.f32), 1.f32 + np.hypot(3.f32, 2.f32), 1.f32 + np.hypot(3.f32, 3.f32), 1.f32 + np.hypot(3.f32, 4.f32)], np.float32)\n    assert eq(1 + np.sinh(a), [1.f32 + np.sinh(1.f32), 1.f32 + np.sinh(2.f32), 1.f32 + np.sinh(3.f32), 1.f32 + np.sinh(4.f32)], np.float32)\n    assert eq(1 + np.cosh(a), [1.f32 + np.cosh(1.f32), 1.f32 + np.cosh(2.f32), 1.f32 + np.cosh(3.f32), 1.f32 + np.cosh(4.f32)], np.float32)\n    assert eq(1 + np.tanh(a), [1.f32 + np.tanh(1.f32), 1.f32 + np.tanh(2.f32), 1.f32 + np.tanh(3.f32), 1.f32 + np.tanh(4.f32)], np.float32)\n    assert eq(1 + np.arcsinh(a), [1.f32 + np.arcsinh(1.f32), 1.f32 + np.arcsinh(2.f32), 1.f32 + np.arcsinh(3.f32), 1.f32 + np.arcsinh(4.f32)], np.float32)\n    assert eq(1 + np.arccosh(a), [1.f32 + np.arccosh(1.f32), 1.f32 + np.arccosh(2.f32), 1.f32 + np.arccosh(3.f32), 1.f32 + np.arccosh(4.f32)], np.float32)\n    assert eq(1 + np.arctanh(a), [1.f32 + np.arctanh(1.f32), 1.f32 + np.arctanh(2.f32), 1.f32 + np.arctanh(3.f32), 1.f32 + np.arctanh(4.f32)], np.float32)\n    assert eq(1 + np.conj(a), [1 + np.conj(1), 1 + np.conj(2), 1 + np.conj(3), 1 + np.conj(4)], np.uint16)\n    assert eq(1 + np.exp(a), [1.f32 + np.exp(1.f32), 1.f32 + np.exp(2.f32), 1.f32 + np.exp(3.f32), 1.f32 + np.exp(4.f32)], np.float32)\n    assert eq(1 + np.exp2(a), [1.f32 + np.exp2(1.f32), 1.f32 + np.exp2(2.f32), 1.f32 + np.exp2(3.f32), 1.f32 + np.exp2(4.f32)], np.float32)\n    assert eq(1 + np.log(a), [1.f32 + np.log(1.f32), 1.f32 + np.log(2.f32), 1.f32 + np.log(3.f32), 1.f32 + np.log(4.f32)], np.float32)\n    assert eq(1 + np.log2(a), [1.f32 + np.log2(1.f32), 1.f32 + np.log2(2.f32), 1.f32 + np.log2(3.f32), 1.f32 + np.log2(4.f32)], np.float32)\n    assert eq(1 + np.log10(a), [1.f32 + np.log10(1.f32), 1.f32 + np.log10(2.f32), 1.f32 + np.log10(3.f32), 1.f32 + np.log10(4.f32)], np.float32)\n    assert eq(1 + np.expm1(a), [1.f32 + np.expm1(1.f32), 1.f32 + np.expm1(2.f32), 1.f32 + np.expm1(3.f32), 1.f32 + np.expm1(4.f32)], np.float32)\n    assert eq(1 + np.log1p(a), [1.f32 + np.log1p(1.f32), 1.f32 + np.log1p(2.f32), 1.f32 + np.log1p(3.f32), 1.f32 + np.log1p(4.f32)], np.float32)\n    assert eq(1 + np.sqrt(a), [1.f32 + np.sqrt(1.f32), 1.f32 + np.sqrt(2.f32), 1.f32 + np.sqrt(3.f32), 1.f32 + np.sqrt(4.f32)], np.float32)\n    assert eq(1 + np.square(a), [1 + np.square(1), 1 + np.square(2), 1 + np.square(3), 1 + np.square(4)], np.uint16)\n    assert eq(1 + np.cbrt(a), [1.f32 + np.cbrt(1.f32), 1.f32 + np.cbrt(2.f32), 1.f32 + np.cbrt(3.f32), 1.f32 + np.cbrt(4.f32)], np.float32)\n    assert eq(1 + np.logaddexp(a, 3), [1.f32 + np.logaddexp(1.f32, 3.f32), 1.f32 + np.logaddexp(2.f32, 3.f32), 1.f32 + np.logaddexp(3.f32, 3.f32), 1.f32 + np.logaddexp(4.f32, 3.f32)], np.float32)\n    assert eq(1 + np.logaddexp(3, a), [1.f32 + np.logaddexp(3.f32, 1.f32), 1.f32 + np.logaddexp(3.f32, 2.f32), 1.f32 + np.logaddexp(3.f32, 3.f32), 1.f32 + np.logaddexp(3.f32, 4.f32)], np.float32)\n    assert eq(1 + np.logaddexp2(a, 3), [1.f32 + np.logaddexp2(1.f32, 3.f32), 1.f32 + np.logaddexp2(2.f32, 3.f32), 1.f32 + np.logaddexp2(3.f32, 3.f32), 1.f32 + np.logaddexp2(4.f32, 3.f32)], np.float32)\n    assert eq(1 + np.logaddexp2(3, a), [1.f32 + np.logaddexp2(3.f32, 1.f32), 1.f32 + np.logaddexp2(3.f32, 2.f32), 1.f32 + np.logaddexp2(3.f32, 3.f32), 1.f32 + np.logaddexp2(3.f32, 4.f32)], np.float32)\n    assert eq(1 + np.reciprocal(a), [2, 1, 1, 1], np.uint16)\n    assert eq(1 + np.rint(a), [1.f32 + np.rint(1.f32), 1.f32 + np.rint(2.f32), 1.f32 + np.rint(3.f32), 1.f32 + np.rint(4.f32)], np.float32)\n    assert eq(1 + np.floor(a), [1.f32 + np.floor(1.f32), 1.f32 + np.floor(2.f32), 1.f32 + np.floor(3.f32), 1.f32 + np.floor(4.f32)], np.float32)\n    assert eq(1 + np.ceil(a), [1.f32 + np.ceil(1.f32), 1.f32 + np.ceil(2.f32), 1.f32 + np.ceil(3.f32), 1.f32 + np.ceil(4.f32)], np.float32)\n    assert eq(1 + np.trunc(a), [1.f32 + np.trunc(1.f32), 1.f32 + np.trunc(2.f32), 1.f32 + np.trunc(3.f32), 1.f32 + np.trunc(4.f32)], np.float32)\n    assert eq(1 + np.isnan(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isinf(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isfinite(a), [2, 2, 2, 2], int)\n    assert eq(1 + np.ldexp(a, 1), [1.f32 + np.ldexp(1.f32, 1), 1.f32 + np.ldexp(2.f32, 1), 1.f32 + np.ldexp(3.f32, 1), 1.f32 + np.ldexp(4.f32, 1)], np.float32)\n    assert eq(1 + np.ldexp(1, a), [1.f32 + np.ldexp(1.f32, 1), 1.f32 + np.ldexp(1.f32, 2), 1.f32 + np.ldexp(1.f32, 3), 1.f32 + np.ldexp(1.f32, 4)], np.float32)\n    assert eq(1 + np.sign(a), [2, 2, 2, 2], np.uint16)\n    assert eq(1 + np.signbit(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.copysign(a, -1), [1.f32 + np.copysign(1.f32, -1.f32), 1.f32 + np.copysign(2.f32, -1.f32), 1.f32 + np.copysign(3.f32, -1.f32), 1.f32 + np.copysign(4.f32, -1.f32)], np.float32)\n    assert eq(1 + np.copysign(-1, a), [1.f32 + np.copysign(-1.f32, 1.f32), 1.f32 + np.copysign(-1.f32, 2.f32), 1.f32 + np.copysign(-1.f32, 3.f32), 1.f32 + np.copysign(-1.f32, 4.f32)], np.float32)\n    assert eq(1 + np.spacing(a), [1.f32 + np.spacing(1.f32), 1.f32 + np.spacing(2.f32), 1.f32 + np.spacing(3.f32), 1.f32 + np.spacing(4.f32)], np.float32)\n    assert eq(1 + np.nextafter(a, 1), [1.f32 + np.nextafter(1.f32, 1.f32), 1.f32 + np.nextafter(2.f32, 1.f32), 1.f32 + np.nextafter(3.f32, 1.f32), 1.f32 + np.nextafter(4.f32, 1.f32)], np.float32)\n    assert eq(1 + np.nextafter(1, a), [1.f32 + np.nextafter(1.f32, 1.f32), 1.f32 + np.nextafter(1.f32, 2.f32), 1.f32 + np.nextafter(1.f32, 3.f32), 1.f32 + np.nextafter(1.f32, 4.f32)], np.float32)\n    assert eq(1 + np.deg2rad(a), [1.f32 + np.deg2rad(1.f32), 1.f32 + np.deg2rad(2.f32), 1.f32 + np.deg2rad(3.f32), 1.f32 + np.deg2rad(4.f32)], np.float32)\n    assert eq(1 + np.rad2deg(a), [1.f32 + np.rad2deg(1.f32), 1.f32 + np.rad2deg(2.f32), 1.f32 + np.rad2deg(3.f32), 1.f32 + np.rad2deg(4.f32)], np.float32)\n    assert eq(1 + np.heaviside(a, 1), [1.f32 + np.heaviside(1.f32, 1.f32), 1.f32 + np.heaviside(2.f32, 1.f32), 1.f32 + np.heaviside(3.f32, 1.f32), 1.f32 + np.heaviside(4.f32, 1.f32)], np.float32)\n    assert eq(1 + np.heaviside(1, a), [1.f32 + np.heaviside(1.f32, 1.f32), 1.f32 + np.heaviside(1.f32, 2.f32), 1.f32 + np.heaviside(1.f32, 3.f32), 1.f32 + np.heaviside(1.f32, 4.f32)], np.float32)\n\n@test\ndef test_ops_uint8():\n    a = np.array([1, 2, 3, 4], np.uint8)\n    assert eq(1 + (+a), [2, 3, 4, 5], np.uint8)\n    assert eq(1 + (-a), [0, -1, -2, -3], np.uint8)\n    assert eq(1 + (~a), [-1, -2, -3, -4], np.uint8)\n    assert eq(1 + np.abs(a), [2, 3, 4, 5], np.uint8)\n    assert eq(1 + abs(a), [2, 3, 4, 5], np.uint8)\n    assert eq(2 * (1 + a), [4, 6, 8, 10], np.uint8)\n    assert eq(2 * (a + 1), [4, 6, 8, 10], np.uint8)\n    assert eq(2 * (a - 1), [0, 2, 4, 6], np.uint8)\n    assert eq(2 * (1 - a), [0, -2, -4, -6], np.uint8)\n    assert eq(1 + (a * 2), [3, 5, 7, 9], np.uint8)\n    assert eq(1 + (2 * a), [3, 5, 7, 9], np.uint8)\n    assert eq(1 + (a.reshape(1, -1) @ a.reshape(-1, 1)), [[31]], np.uint8)\n    assert eq(1 + (a / 2), [1.5, 2., 2.5, 3.], float)\n    assert eq(1 + (2 / a), [3., 2., 1 + 2/3, 1.5], float)\n    assert eq(1 + (a // 2), [1, 2, 2, 3], np.uint8)\n    assert eq(1 + (10 // a), [11, 6, 4, 3], np.uint8)\n    assert eq(1 + (a % 3), [2, 3, 1, 2], np.uint8)\n    assert eq(1 + (10 % a), [1, 1, 2, 3], np.uint8)\n    assert eq(1 + np.fmod(a, 3), [2, 3, 1, 2], np.uint8)\n    assert eq(1 + np.fmod(10, a), [1, 1, 2, 3], np.uint8)\n    assert eq(1 + (a ** 3), [2, 9, 28, 65], np.uint8)\n    assert eq(1 + (3 ** a), [4, 10, 28, 82], np.uint8)\n    assert eq(1 + (a << 3), [9, 17, 25, 33], np.uint8)\n    assert eq(1 + (3 << a), [7, 13, 25, 49], np.uint8)\n    assert eq(1 + (a >> 1), [1, 2, 2, 3], np.uint8)\n    assert eq(1 + (100 >> a), [51, 26, 13, 7], np.uint8)\n    assert eq(1 + (a & 2), [1, 3, 3, 1], np.uint8)\n    assert eq(1 + (2 & a), [1, 3, 3, 1], np.uint8)\n    assert eq(1 + (a | 2), [4, 3, 4, 7], np.uint8)\n    assert eq(1 + (2 | a), [4, 3, 4, 7], np.uint8)\n    assert eq(1 + (a ^ 2), [4, 1, 2, 7], np.uint8)\n    assert eq(1 + (2 ^ a), [4, 1, 2, 7], np.uint8)\n    assert eq(np.logical_and(a, a & 1), [True, False, True, False], bool)\n    assert eq(np.logical_or(a & 2, a & 1), [True, True, True, False], bool)\n    assert eq(np.logical_xor(a, a & 1), [False, True, False, True], bool)\n    assert eq((a + 1) == 2, [True, False, False, False], bool)\n    assert eq(2 == (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) != 2, [False, True, True, True], bool)\n    assert eq(2 != (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) < 3, [True, False, False, False], bool)\n    assert eq(3 < (a + 1), [False, False, True, True], bool)\n    assert eq((a + 1) > 3, [False, False, True, True], bool)\n    assert eq(3 > (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) <= 3, [True, True, False, False], bool)\n    assert eq(3 <= (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) >= 3, [False, True, True, True], bool)\n    assert eq(3 >= (a + 1), [True, True, False, False], bool)\n    assert eq(1 + np.minimum(a, 3), [2, 3, 4, 4], np.uint8)\n    assert eq(1 + np.minimum(3, a), [2, 3, 4, 4], np.uint8)\n    assert eq(1 + np.maximum(a, 3), [4, 4, 4, 5], np.uint8)\n    assert eq(1 + np.maximum(3, a), [4, 4, 4, 5], np.uint8)\n    assert eq(1 + np.fmin(a, 3), [2, 3, 4, 4], np.uint8)\n    assert eq(1 + np.fmin(3, a), [2, 3, 4, 4], np.uint8)\n    assert eq(1 + np.fmax(a, 3), [4, 4, 4, 5], np.uint8)\n    assert eq(1 + np.fmax(3, a), [4, 4, 4, 5], np.uint8)\n    assert eq(1 + np.sin(a), [1.f16 + np.sin(1.f16), 1.f16 + np.sin(2.f16), 1.f16 + np.sin(3.f16), 1.f16 + np.sin(4.f16)], np.float16)\n    assert eq(1 + np.cos(a), [1.f16 + np.cos(1.f16), 1.f16 + np.cos(2.f16), 1.f16 + np.cos(3.f16), 1.f16 + np.cos(4.f16)], np.float16)\n    assert eq(1 + np.tan(a), [1.f16 + np.tan(1.f16), 1.f16 + np.tan(2.f16), 1.f16 + np.tan(3.f16), 1.f16 + np.tan(4.f16)], np.float16)\n    assert eq(1 + np.arcsin(a), [1.f16 + np.arcsin(1.f16), 1.f16 + np.arcsin(2.f16), 1.f16 + np.arcsin(3.f16), 1.f16 + np.arcsin(4.f16)], np.float16)\n    assert eq(1 + np.arccos(a), [1.f16 + np.arccos(1.f16), 1.f16 + np.arccos(2.f16), 1.f16 + np.arccos(3.f16), 1.f16 + np.arccos(4.f16)], np.float16)\n    assert eq(1 + np.arctan(a), [1.f16 + np.arctan(1.f16), 1.f16 + np.arctan(2.f16), 1.f16 + np.arctan(3.f16), 1.f16 + np.arctan(4.f16)], np.float16)\n    assert eq(1 + np.arctan2(a, 3.f16), [1.f16 + np.arctan2(1.f16, 3.f16), 1.f16 + np.arctan2(2.f16, 3.f16), 1.f16 + np.arctan2(3.f16, 3.f16), 1.f16 + np.arctan2(4.f16, 3.f16)], np.float16)\n    assert eq(1 + np.arctan2(3.f16, a), [1.f16 + np.arctan2(3.f16, 1.f16), 1.f16 + np.arctan2(3.f16, 2.f16), 1.f16 + np.arctan2(3.f16, 3.f16), 1.f16 + np.arctan2(3.f16, 4.f16)], np.float16)\n    assert eq(1 + np.hypot(a, 3), [1.f16 + np.hypot(1.f16, 3.f16), 1.f16 + np.hypot(2.f16, 3.f16), 1.f16 + np.hypot(3.f16, 3.f16), 1.f16 + np.hypot(4.f16, 3.f16)], np.float16)\n    assert eq(1 + np.hypot(3, a), [1.f16 + np.hypot(3.f16, 1.f16), 1.f16 + np.hypot(3.f16, 2.f16), 1.f16 + np.hypot(3.f16, 3.f16), 1.f16 + np.hypot(3.f16, 4.f16)], np.float16)\n    assert eq(1 + np.sinh(a), [1.f16 + np.sinh(1.f16), 1.f16 + np.sinh(2.f16), 1.f16 + np.sinh(3.f16), 1.f16 + np.sinh(4.f16)], np.float16)\n    assert eq(1 + np.cosh(a), [1.f16 + np.cosh(1.f16), 1.f16 + np.cosh(2.f16), 1.f16 + np.cosh(3.f16), 1.f16 + np.cosh(4.f16)], np.float16)\n    assert eq(1 + np.tanh(a), [1.f16 + np.tanh(1.f16), 1.f16 + np.tanh(2.f16), 1.f16 + np.tanh(3.f16), 1.f16 + np.tanh(4.f16)], np.float16)\n    assert eq(1 + np.arcsinh(a), [1.f16 + np.arcsinh(1.f16), 1.f16 + np.arcsinh(2.f16), 1.f16 + np.arcsinh(3.f16), 1.f16 + np.arcsinh(4.f16)], np.float16)\n    assert eq(1 + np.arccosh(a), [1.f16 + np.arccosh(1.f16), 1.f16 + np.arccosh(2.f16), 1.f16 + np.arccosh(3.f16), 1.f16 + np.arccosh(4.f16)], np.float16)\n    assert eq(1 + np.arctanh(a), [1.f16 + np.arctanh(1.f16), 1.f16 + np.arctanh(2.f16), 1.f16 + np.arctanh(3.f16), 1.f16 + np.arctanh(4.f16)], np.float16)\n    assert eq(1 + np.conj(a), [1 + np.conj(1), 1 + np.conj(2), 1 + np.conj(3), 1 + np.conj(4)], np.uint8)\n    assert eq(1 + np.exp(a), [1.f16 + np.exp(1.f16), 1.f16 + np.exp(2.f16), 1.f16 + np.exp(3.f16), 1.f16 + np.exp(4.f16)], np.float16)\n    assert eq(1 + np.exp2(a), [1.f16 + np.exp2(1.f16), 1.f16 + np.exp2(2.f16), 1.f16 + np.exp2(3.f16), 1.f16 + np.exp2(4.f16)], np.float16)\n    assert eq(1 + np.log(a), [1.f16 + np.log(1.f16), 1.f16 + np.log(2.f16), 1.f16 + np.log(3.f16), 1.f16 + np.log(4.f16)], np.float16)\n    assert eq(1 + np.log2(a), [1.f16 + np.log2(1.f16), 1.f16 + np.log2(2.f16), 1.f16 + np.log2(3.f16), 1.f16 + np.log2(4.f16)], np.float16)\n    assert eq(1 + np.log10(a), [1.f16 + np.log10(1.f16), 1.f16 + np.log10(2.f16), 1.f16 + np.log10(3.f16), 1.f16 + np.log10(4.f16)], np.float16)\n    assert eq(1 + np.expm1(a), [1.f16 + np.expm1(1.f16), 1.f16 + np.expm1(2.f16), 1.f16 + np.expm1(3.f16), 1.f16 + np.expm1(4.f16)], np.float16)\n    assert eq(1 + np.log1p(a), [1.f16 + np.log1p(1.f16), 1.f16 + np.log1p(2.f16), 1.f16 + np.log1p(3.f16), 1.f16 + np.log1p(4.f16)], np.float16)\n    assert eq(1 + np.sqrt(a), [1.f16 + np.sqrt(1.f16), 1.f16 + np.sqrt(2.f16), 1.f16 + np.sqrt(3.f16), 1.f16 + np.sqrt(4.f16)], np.float16)\n    assert eq(1 + np.square(a), [1 + np.square(1), 1 + np.square(2), 1 + np.square(3), 1 + np.square(4)], np.uint8)\n    assert eq(1 + np.cbrt(a), [1.f16 + np.cbrt(1.f16), 1.f16 + np.cbrt(2.f16), 1.f16 + np.cbrt(3.f16), 1.f16 + np.cbrt(4.f16)], np.float16)\n    assert eq(1 + np.logaddexp(a, 3), [1.f16 + np.logaddexp(1.f16, 3.f16), 1.f16 + np.logaddexp(2.f16, 3.f16), 1.f16 + np.logaddexp(3.f16, 3.f16), 1.f16 + np.logaddexp(4.f16, 3.f16)], np.float16)\n    assert eq(1 + np.logaddexp(3, a), [1.f16 + np.logaddexp(3.f16, 1.f16), 1.f16 + np.logaddexp(3.f16, 2.f16), 1.f16 + np.logaddexp(3.f16, 3.f16), 1.f16 + np.logaddexp(3.f16, 4.f16)], np.float16)\n    assert eq(1 + np.logaddexp2(a, 3), [1.f16 + np.logaddexp2(1.f16, 3.f16), 1.f16 + np.logaddexp2(2.f16, 3.f16), 1.f16 + np.logaddexp2(3.f16, 3.f16), 1.f16 + np.logaddexp2(4.f16, 3.f16)], np.float16)\n    assert eq(1 + np.logaddexp2(3, a), [1.f16 + np.logaddexp2(3.f16, 1.f16), 1.f16 + np.logaddexp2(3.f16, 2.f16), 1.f16 + np.logaddexp2(3.f16, 3.f16), 1.f16 + np.logaddexp2(3.f16, 4.f16)], np.float16)\n    assert eq(1 + np.reciprocal(a), [2, 1, 1, 1], np.uint8)\n    assert eq(1 + np.rint(a), [1.f16 + np.rint(1.f16), 1.f16 + np.rint(2.f16), 1.f16 + np.rint(3.f16), 1.f16 + np.rint(4.f16)], np.float16)\n    assert eq(1 + np.floor(a), [1.f16 + np.floor(1.f16), 1.f16 + np.floor(2.f16), 1.f16 + np.floor(3.f16), 1.f16 + np.floor(4.f16)], np.float16)\n    assert eq(1 + np.ceil(a), [1.f16 + np.ceil(1.f16), 1.f16 + np.ceil(2.f16), 1.f16 + np.ceil(3.f16), 1.f16 + np.ceil(4.f16)], np.float16)\n    assert eq(1 + np.trunc(a), [1.f16 + np.trunc(1.f16), 1.f16 + np.trunc(2.f16), 1.f16 + np.trunc(3.f16), 1.f16 + np.trunc(4.f16)], np.float16)\n    assert eq(1 + np.isnan(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isinf(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isfinite(a), [2, 2, 2, 2], int)\n    assert eq(1 + np.ldexp(a, 1), [1.f16 + np.ldexp(1.f16, 1), 1.f16 + np.ldexp(2.f16, 1), 1.f16 + np.ldexp(3.f16, 1), 1.f16 + np.ldexp(4.f16, 1)], np.float16)\n    assert eq(1 + np.ldexp(1, a), [1.f16 + np.ldexp(1.f16, 1), 1.f16 + np.ldexp(1.f16, 2), 1.f16 + np.ldexp(1.f16, 3), 1.f16 + np.ldexp(1.f16, 4)], np.float16)\n    assert eq(1 + np.sign(a), [2, 2, 2, 2], np.uint8)\n    assert eq(1 + np.signbit(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.copysign(a, -1), [1.f16 + np.copysign(1.f16, -1.f16), 1.f16 + np.copysign(2.f16, -1.f16), 1.f16 + np.copysign(3.f16, -1.f16), 1.f16 + np.copysign(4.f16, -1.f16)], np.float16)\n    assert eq(1 + np.copysign(-1, a), [1.f16 + np.copysign(-1.f16, 1.f16), 1.f16 + np.copysign(-1.f16, 2.f16), 1.f16 + np.copysign(-1.f16, 3.f16), 1.f16 + np.copysign(-1.f16, 4.f16)], np.float16)\n    assert eq(1 + np.spacing(a), [1.f16 + np.spacing(1.f16), 1.f16 + np.spacing(2.f16), 1.f16 + np.spacing(3.f16), 1.f16 + np.spacing(4.f16)], np.float16)\n    assert eq(1 + np.nextafter(a, 1), [1.f16 + np.nextafter(1.f16, 1.f16), 1.f16 + np.nextafter(2.f16, 1.f16), 1.f16 + np.nextafter(3.f16, 1.f16), 1.f16 + np.nextafter(4.f16, 1.f16)], np.float16)\n    assert eq(1 + np.nextafter(1, a), [1.f16 + np.nextafter(1.f16, 1.f16), 1.f16 + np.nextafter(1.f16, 2.f16), 1.f16 + np.nextafter(1.f16, 3.f16), 1.f16 + np.nextafter(1.f16, 4.f16)], np.float16)\n    assert eq(1 + np.deg2rad(a), [1.f16 + np.deg2rad(1.f16), 1.f16 + np.deg2rad(2.f16), 1.f16 + np.deg2rad(3.f16), 1.f16 + np.deg2rad(4.f16)], np.float16)\n    assert eq(1 + np.rad2deg(a), [1.f16 + np.rad2deg(1.f16), 1.f16 + np.rad2deg(2.f16), 1.f16 + np.rad2deg(3.f16), 1.f16 + np.rad2deg(4.f16)], np.float16)\n    assert eq(1 + np.heaviside(a, 1), [1.f16 + np.heaviside(1.f16, 1.f16), 1.f16 + np.heaviside(2.f16, 1.f16), 1.f16 + np.heaviside(3.f16, 1.f16), 1.f16 + np.heaviside(4.f16, 1.f16)], np.float16)\n    assert eq(1 + np.heaviside(1, a), [1.f16 + np.heaviside(1.f16, 1.f16), 1.f16 + np.heaviside(1.f16, 2.f16), 1.f16 + np.heaviside(1.f16, 3.f16), 1.f16 + np.heaviside(1.f16, 4.f16)], np.float16)\n\n@test\ndef test_ops_float64():\n    a = np.array([1., 2., 3., 4.])\n    assert eq(1 + (+a), [2, 3, 4, 5], float)\n    assert eq(1 + (-a), [0, -1, -2, -3], float)\n    assert eq(1 + np.abs(a), [2, 3, 4, 5], float)\n    assert eq(1 + abs(a), [2, 3, 4, 5], float)\n    assert eq(2 * (1 + a), [4, 6, 8, 10], float)\n    assert eq(2 * (a + 1), [4, 6, 8, 10], float)\n    assert eq(2 * (a - 1), [0, 2, 4, 6], float)\n    assert eq(2 * (1 - a), [0, -2, -4, -6], float)\n    assert eq(1 + (a * 2), [3, 5, 7, 9], float)\n    assert eq(1 + (2 * a), [3, 5, 7, 9], float)\n    assert eq(1 + (a.reshape(1, -1) @ a.reshape(-1, 1)), [[31]], float)\n    assert eq(1 + (a / 2), [1.5, 2., 2.5, 3.], float)\n    assert eq(1 + (2 / a), [3., 2., 1 + 2/3, 1.5], float)\n    assert eq(1 + (a // 2), [1, 2, 2, 3], float)\n    assert eq(1 + (10 // a), [11, 6, 4, 3], float)\n    assert eq(1 + (a % 3), [2, 3, 1, 2], float)\n    assert eq(1 + (10 % a), [1, 1, 2, 3], float)\n    assert eq(1 + np.fmod(a, 3), [2, 3, 1, 2], float)\n    assert eq(1 + np.fmod(10, a), [1, 1, 2, 3], float)\n    assert eq(1 + (a ** 3), [2, 9, 28, 65], float)\n    assert eq(1 + (3 ** a), [4, 10, 28, 82], float)\n    assert eq(np.logical_and(a, a - 1), [False, True, True, True], bool)\n    assert eq(np.logical_or(a, a - 1), [True, True, True, True], bool)\n    assert eq(np.logical_xor(a, a % 2), [False, True, False, True], bool)\n    assert eq((a + 1) == 2, [True, False, False, False], bool)\n    assert eq(2 == (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) != 2, [False, True, True, True], bool)\n    assert eq(2 != (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) < 3, [True, False, False, False], bool)\n    assert eq(3 < (a + 1), [False, False, True, True], bool)\n    assert eq((a + 1) > 3, [False, False, True, True], bool)\n    assert eq(3 > (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) <= 3, [True, True, False, False], bool)\n    assert eq(3 <= (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) >= 3, [False, True, True, True], bool)\n    assert eq(3 >= (a + 1), [True, True, False, False], bool)\n    assert eq(1 + np.minimum(a, 3), [2, 3, 4, 4], float)\n    assert eq(1 + np.minimum(3, a), [2, 3, 4, 4], float)\n    assert eq(1 + np.maximum(a, 3), [4, 4, 4, 5], float)\n    assert eq(1 + np.maximum(3, a), [4, 4, 4, 5], float)\n    assert eq(1 + np.fmin(a, 3), [2, 3, 4, 4], float)\n    assert eq(1 + np.fmin(3, a), [2, 3, 4, 4], float)\n    assert eq(1 + np.fmax(a, 3), [4, 4, 4, 5], float)\n    assert eq(1 + np.fmax(3, a), [4, 4, 4, 5], float)\n    assert eq(1 + np.minimum(a, np.nan), [np.nan, np.nan, np.nan, np.nan], float)\n    assert eq(1 + np.minimum(np.nan, a), [np.nan, np.nan, np.nan, np.nan], float)\n    assert eq(1 + np.maximum(a, np.nan), [np.nan, np.nan, np.nan, np.nan], float)\n    assert eq(1 + np.maximum(np.nan, a), [np.nan, np.nan, np.nan, np.nan], float)\n    assert eq(1 + np.fmin(a, np.nan), [2, 3, 4, 5], float)\n    assert eq(1 + np.fmin(np.nan, a), [2, 3, 4, 5], float)\n    assert eq(1 + np.fmax(a, np.nan), [2, 3, 4, 5], float)\n    assert eq(1 + np.fmax(np.nan, a), [2, 3, 4, 5], float)\n    assert eq(1 + np.sin(a), [1 + np.sin(1), 1 + np.sin(2), 1 + np.sin(3), 1 + np.sin(4)], float)\n    assert eq(1 + np.cos(a), [1 + np.cos(1), 1 + np.cos(2), 1 + np.cos(3), 1 + np.cos(4)], float)\n    assert eq(1 + np.tan(a), [1 + np.tan(1), 1 + np.tan(2), 1 + np.tan(3), 1 + np.tan(4)], float)\n    assert eq(1 + np.arcsin(a), [1 + np.arcsin(1), 1 + np.arcsin(2), 1 + np.arcsin(3), 1 + np.arcsin(4)], float)\n    assert eq(1 + np.arccos(a), [1 + np.arccos(1), 1 + np.arccos(2), 1 + np.arccos(3), 1 + np.arccos(4)], float)\n    assert eq(1 + np.arctan(a), [1 + np.arctan(1), 1 + np.arctan(2), 1 + np.arctan(3), 1 + np.arctan(4)], float)\n    assert eq(1 + np.arctan2(a, 3), [1 + np.arctan2(1, 3), 1 + np.arctan2(2, 3), 1 + np.arctan2(3, 3), 1 + np.arctan2(4, 3)], float)\n    assert eq(1 + np.arctan2(3, a), [1 + np.arctan2(3, 1), 1 + np.arctan2(3, 2), 1 + np.arctan2(3, 3), 1 + np.arctan2(3, 4)], float)\n    assert eq(1 + np.hypot(a, 3), [1 + np.hypot(1, 3), 1 + np.hypot(2, 3), 1 + np.hypot(3, 3), 1 + np.hypot(4, 3)], float)\n    assert eq(1 + np.hypot(3, a), [1 + np.hypot(3, 1), 1 + np.hypot(3, 2), 1 + np.hypot(3, 3), 1 + np.hypot(3, 4)], float)\n    assert eq(1 + np.sinh(a), [1 + np.sinh(1), 1 + np.sinh(2), 1 + np.sinh(3), 1 + np.sinh(4)], float)\n    assert eq(1 + np.cosh(a), [1 + np.cosh(1), 1 + np.cosh(2), 1 + np.cosh(3), 1 + np.cosh(4)], float)\n    assert eq(1 + np.tanh(a), [1 + np.tanh(1), 1 + np.tanh(2), 1 + np.tanh(3), 1 + np.tanh(4)], float)\n    assert eq(1 + np.arcsinh(a), [1 + np.arcsinh(1), 1 + np.arcsinh(2), 1 + np.arcsinh(3), 1 + np.arcsinh(4)], float)\n    assert eq(1 + np.arccosh(a), [1 + np.arccosh(1), 1 + np.arccosh(2), 1 + np.arccosh(3), 1 + np.arccosh(4)], float)\n    assert eq(1 + np.arctanh(a), [1 + np.arctanh(1), 1 + np.arctanh(2), 1 + np.arctanh(3), 1 + np.arctanh(4)], float)\n    assert eq(1 + np.conj(a), [1 + np.conj(1), 1 + np.conj(2), 1 + np.conj(3), 1 + np.conj(4)], float)\n    assert eq(1 + np.exp(a), [1 + np.exp(1), 1 + np.exp(2), 1 + np.exp(3), 1 + np.exp(4)], float)\n    assert eq(1 + np.exp2(a), [1 + np.exp2(1), 1 + np.exp2(2), 1 + np.exp2(3), 1 + np.exp2(4)], float)\n    assert eq(1 + np.log(a), [1 + np.log(1), 1 + np.log(2), 1 + np.log(3), 1 + np.log(4)], float)\n    assert eq(1 + np.log2(a), [1 + np.log2(1), 1 + np.log2(2), 1 + np.log2(3), 1 + np.log2(4)], float)\n    assert eq(1 + np.log10(a), [1 + np.log10(1), 1 + np.log10(2), 1 + np.log10(3), 1 + np.log10(4)], float)\n    assert eq(1 + np.expm1(a), [1 + np.expm1(1), 1 + np.expm1(2), 1 + np.expm1(3), 1 + np.expm1(4)], float)\n    assert eq(1 + np.log1p(a), [1 + np.log1p(1), 1 + np.log1p(2), 1 + np.log1p(3), 1 + np.log1p(4)], float)\n    assert eq(1 + np.sqrt(a), [1 + np.sqrt(1), 1 + np.sqrt(2), 1 + np.sqrt(3), 1 + np.sqrt(4)], float)\n    assert eq(1 + np.square(a), [1 + np.square(1), 1 + np.square(2), 1 + np.square(3), 1 + np.square(4)], float)\n    assert eq(1 + np.cbrt(a), [1 + np.cbrt(1), 1 + np.cbrt(2), 1 + np.cbrt(3), 1 + np.cbrt(4)], float)\n    assert eq(1 + np.logaddexp(a, 3), [1 + np.logaddexp(1, 3), 1 + np.logaddexp(2, 3), 1 + np.logaddexp(3, 3), 1 + np.logaddexp(4, 3)], float)\n    assert eq(1 + np.logaddexp(3, a), [1 + np.logaddexp(3, 1), 1 + np.logaddexp(3, 2), 1 + np.logaddexp(3, 3), 1 + np.logaddexp(3, 4)], float)\n    assert eq(1 + np.logaddexp2(a, 3), [1 + np.logaddexp2(1, 3), 1 + np.logaddexp2(2, 3), 1 + np.logaddexp2(3, 3), 1 + np.logaddexp2(4, 3)], float)\n    assert eq(1 + np.logaddexp2(3, a), [1 + np.logaddexp2(3, 1), 1 + np.logaddexp2(3, 2), 1 + np.logaddexp2(3, 3), 1 + np.logaddexp2(3, 4)], float)\n    assert eq(1 + np.reciprocal(a), [2., 1.5, 1 + 1/3, 1.25], float)\n    assert eq(1 + np.rint(a + .1), [1 + np.rint(1.1), 1 + np.rint(2.1), 1 + np.rint(3.1), 1 + np.rint(4.1)], float)\n    assert eq(1 + np.floor(a + .1), [1 + np.floor(1.1), 1 + np.floor(2.1), 1 + np.floor(3.1), 1 + np.floor(4.1)], float)\n    assert eq(1 + np.ceil(a + .1), [1 + np.ceil(1.1), 1 + np.ceil(2.1), 1 + np.ceil(3.1), 1 + np.ceil(4.1)], float)\n    assert eq(1 + np.trunc(a + .1), [1 + np.trunc(1.1), 1 + np.trunc(2.1), 1 + np.trunc(3.1), 1 + np.trunc(4.1)], float)\n    assert eq(1 + np.isnan(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isinf(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isfinite(a), [2, 2, 2, 2], int)\n    assert eq(1 + np.ldexp(a, 1), [1 + np.ldexp(1, 1), 1 + np.ldexp(2, 1), 1 + np.ldexp(3, 1), 1 + np.ldexp(4, 1)], float)\n    assert eq(1 + np.sign(a), [2, 2, 2, 2], float)\n    assert eq(1 + np.signbit(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.copysign(a, -1), [1 + np.copysign(1, -1), 1 + np.copysign(2, -1), 1 + np.copysign(3, -1), 1 + np.copysign(4, -1)], float)\n    assert eq(1 + np.copysign(-1, a), [1 + np.copysign(-1, 1), 1 + np.copysign(-1, 2), 1 + np.copysign(-1, 3), 1 + np.copysign(-1, 4)], float)\n    assert eq(1 + np.spacing(a), [1 + np.spacing(1), 1 + np.spacing(2), 1 + np.spacing(3), 1 + np.spacing(4)], float)\n    assert eq(1 + np.nextafter(a, 1), [1 + np.nextafter(1, 1), 1 + np.nextafter(2, 1), 1 + np.nextafter(3, 1), 1 + np.nextafter(4, 1)], float)\n    assert eq(1 + np.nextafter(1, a), [1 + np.nextafter(1, 1), 1 + np.nextafter(1, 2), 1 + np.nextafter(1, 3), 1 + np.nextafter(1, 4)], float)\n    assert eq(1 + np.deg2rad(a), [1 + np.deg2rad(1), 1 + np.deg2rad(2), 1 + np.deg2rad(3), 1 + np.deg2rad(4)], float)\n    assert eq(1 + np.rad2deg(a), [1 + np.rad2deg(1), 1 + np.rad2deg(2), 1 + np.rad2deg(3), 1 + np.rad2deg(4)], float)\n    assert eq(1 + np.heaviside(a, 1), [1 + np.heaviside(1, 1), 1 + np.heaviside(2, 1), 1 + np.heaviside(3, 1), 1 + np.heaviside(4, 1)], float)\n    assert eq(1 + np.heaviside(1, a), [1 + np.heaviside(1, 1), 1 + np.heaviside(1, 2), 1 + np.heaviside(1, 3), 1 + np.heaviside(1, 4)], float)\n\n@test\ndef test_ops_float32():\n    a = np.array([1., 2., 3., 4.], np.float32)\n    assert eq(1 + (+a), [2, 3, 4, 5], np.float32)\n    assert eq(1 + (-a), [0, -1, -2, -3], np.float32)\n    assert eq(1 + np.abs(a), [2, 3, 4, 5], np.float32)\n    assert eq(1 + abs(a), [2, 3, 4, 5], np.float32)\n    assert eq(2 * (1 + a), [4, 6, 8, 10], np.float32)\n    assert eq(2 * (a + 1), [4, 6, 8, 10], np.float32)\n    assert eq(2 * (a - 1), [0, 2, 4, 6], np.float32)\n    assert eq(2 * (1 - a), [0, -2, -4, -6], np.float32)\n    assert eq(1 + (a * 2), [3, 5, 7, 9], np.float32)\n    assert eq(1 + (2 * a), [3, 5, 7, 9], np.float32)\n    assert eq(1 + (a.reshape(1, -1) @ a.reshape(-1, 1)), [[31]], np.float32)\n    assert eq(1 + (a / 2), [1.5, 2., 2.5, 3.], np.float32)\n    assert eq(1 + (2 / a), [3., 2., 1 + 2/3, 1.5], np.float32)\n    assert eq(1 + (a // 2), [1, 2, 2, 3], np.float32)\n    assert eq(1 + (10 // a), [11, 6, 4, 3], np.float32)\n    assert eq(1 + (a % 3), [2, 3, 1, 2], np.float32)\n    assert eq(1 + (10 % a), [1, 1, 2, 3], np.float32)\n    assert eq(1 + np.fmod(a, 3), [2, 3, 1, 2], np.float32)\n    assert eq(1 + np.fmod(10, a), [1, 1, 2, 3], np.float32)\n    assert eq(1 + (a ** 3), [2, 9, 28, 65], np.float32)\n    assert eq(1 + (3 ** a), [4, 10, 28, 82], np.float32)\n    assert eq(np.logical_and(a, a - 1), [False, True, True, True], bool)\n    assert eq(np.logical_or(a, a - 1), [True, True, True, True], bool)\n    assert eq(np.logical_xor(a, a % 2), [False, True, False, True], bool)\n    assert eq((a + 1) == 2, [True, False, False, False], bool)\n    assert eq(2 == (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) != 2, [False, True, True, True], bool)\n    assert eq(2 != (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) < 3, [True, False, False, False], bool)\n    assert eq(3 < (a + 1), [False, False, True, True], bool)\n    assert eq((a + 1) > 3, [False, False, True, True], bool)\n    assert eq(3 > (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) <= 3, [True, True, False, False], bool)\n    assert eq(3 <= (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) >= 3, [False, True, True, True], bool)\n    assert eq(3 >= (a + 1), [True, True, False, False], bool)\n    assert eq(1 + np.minimum(a, 3), [2, 3, 4, 4], np.float32)\n    assert eq(1 + np.minimum(3, a), [2, 3, 4, 4], np.float32)\n    assert eq(1 + np.maximum(a, 3), [4, 4, 4, 5], np.float32)\n    assert eq(1 + np.maximum(3, a), [4, 4, 4, 5], np.float32)\n    assert eq(1 + np.fmin(a, 3), [2, 3, 4, 4], np.float32)\n    assert eq(1 + np.fmin(3, a), [2, 3, 4, 4], np.float32)\n    assert eq(1 + np.fmax(a, 3), [4, 4, 4, 5], np.float32)\n    assert eq(1 + np.fmax(3, a), [4, 4, 4, 5], np.float32)\n    assert eq(1 + np.minimum(a, np.nan), [np.nan, np.nan, np.nan, np.nan], np.float32)\n    assert eq(1 + np.minimum(np.nan, a), [np.nan, np.nan, np.nan, np.nan], np.float32)\n    assert eq(1 + np.maximum(a, np.nan), [np.nan, np.nan, np.nan, np.nan], np.float32)\n    assert eq(1 + np.maximum(np.nan, a), [np.nan, np.nan, np.nan, np.nan], np.float32)\n    assert eq(1 + np.fmin(a, np.nan), [2, 3, 4, 5], np.float32)\n    assert eq(1 + np.fmin(np.nan, a), [2, 3, 4, 5], np.float32)\n    assert eq(1 + np.fmax(a, np.nan), [2, 3, 4, 5], np.float32)\n    assert eq(1 + np.fmax(np.nan, a), [2, 3, 4, 5], np.float32)\n    assert eq(1 + np.sin(a), [1.f32 + np.sin(1.f32), 1.f32 + np.sin(2.f32), 1.f32 + np.sin(3.f32), 1.f32 + np.sin(4.f32)], np.float32)\n    assert eq(1 + np.cos(a), [1.f32 + np.cos(1.f32), 1.f32 + np.cos(2.f32), 1.f32 + np.cos(3.f32), 1.f32 + np.cos(4.f32)], np.float32)\n    assert eq(1 + np.tan(a), [1.f32 + np.tan(1.f32), 1.f32 + np.tan(2.f32), 1.f32 + np.tan(3.f32), 1.f32 + np.tan(4.f32)], np.float32)\n    assert eq(1 + np.arcsin(a), [1.f32 + np.arcsin(1.f32), 1.f32 + np.arcsin(2.f32), 1.f32 + np.arcsin(3.f32), 1.f32 + np.arcsin(4.f32)], np.float32)\n    assert eq(1 + np.arccos(a), [1.f32 + np.arccos(1.f32), 1.f32 + np.arccos(2.f32), 1.f32 + np.arccos(3.f32), 1.f32 + np.arccos(4.f32)], np.float32)\n    assert eq(1 + np.arctan(a), [1.f32 + np.arctan(1.f32), 1.f32 + np.arctan(2.f32), 1.f32 + np.arctan(3.f32), 1.f32 + np.arctan(4.f32)], np.float32)\n    assert eq(1 + np.arctan2(a, 3.f32), [1.f32 + np.arctan2(1.f32, 3.f32), 1.f32 + np.arctan2(2.f32, 3.f32), 1.f32 + np.arctan2(3.f32, 3.f32), 1.f32 + np.arctan2(4.f32, 3.f32)], np.float32)\n    assert eq(1 + np.arctan2(3.f32, a), [1.f32 + np.arctan2(3.f32, 1.f32), 1.f32 + np.arctan2(3.f32, 2.f32), 1.f32 + np.arctan2(3.f32, 3.f32), 1.f32 + np.arctan2(3.f32, 4.f32)], np.float32)\n    assert eq(1 + np.hypot(a, 3), [1.f32 + np.hypot(1.f32, 3.f32), 1.f32 + np.hypot(2.f32, 3.f32), 1.f32 + np.hypot(3.f32, 3.f32), 1.f32 + np.hypot(4.f32, 3.f32)], np.float32)\n    assert eq(1 + np.hypot(3, a), [1.f32 + np.hypot(3.f32, 1.f32), 1.f32 + np.hypot(3.f32, 2.f32), 1.f32 + np.hypot(3.f32, 3.f32), 1.f32 + np.hypot(3.f32, 4.f32)], np.float32)\n    assert eq(1 + np.sinh(a), [1.f32 + np.sinh(1.f32), 1.f32 + np.sinh(2.f32), 1.f32 + np.sinh(3.f32), 1.f32 + np.sinh(4.f32)], np.float32)\n    assert eq(1 + np.cosh(a), [1.f32 + np.cosh(1.f32), 1.f32 + np.cosh(2.f32), 1.f32 + np.cosh(3.f32), 1.f32 + np.cosh(4.f32)], np.float32)\n    assert eq(1 + np.tanh(a), [1.f32 + np.tanh(1.f32), 1.f32 + np.tanh(2.f32), 1.f32 + np.tanh(3.f32), 1.f32 + np.tanh(4.f32)], np.float32)\n    assert eq(1 + np.arcsinh(a), [1.f32 + np.arcsinh(1.f32), 1.f32 + np.arcsinh(2.f32), 1.f32 + np.arcsinh(3.f32), 1.f32 + np.arcsinh(4.f32)], np.float32)\n    assert eq(1 + np.arccosh(a), [1.f32 + np.arccosh(1.f32), 1.f32 + np.arccosh(2.f32), 1.f32 + np.arccosh(3.f32), 1.f32 + np.arccosh(4.f32)], np.float32)\n    assert eq(1 + np.arctanh(a), [1.f32 + np.arctanh(1.f32), 1.f32 + np.arctanh(2.f32), 1.f32 + np.arctanh(3.f32), 1.f32 + np.arctanh(4.f32)], np.float32)\n    assert eq(1 + np.conj(a), [1 + np.conj(1), 1 + np.conj(2), 1 + np.conj(3), 1 + np.conj(4)], np.float32)\n    assert eq(1 + np.exp(a), [1.f32 + np.exp(1.f32), 1.f32 + np.exp(2.f32), 1.f32 + np.exp(3.f32), 1.f32 + np.exp(4.f32)], np.float32)\n    assert eq(1 + np.exp2(a), [1.f32 + np.exp2(1.f32), 1.f32 + np.exp2(2.f32), 1.f32 + np.exp2(3.f32), 1.f32 + np.exp2(4.f32)], np.float32)\n    assert eq(1 + np.log(a), [1.f32 + np.log(1.f32), 1.f32 + np.log(2.f32), 1.f32 + np.log(3.f32), 1.f32 + np.log(4.f32)], np.float32)\n    assert eq(1 + np.log2(a), [1.f32 + np.log2(1.f32), 1.f32 + np.log2(2.f32), 1.f32 + np.log2(3.f32), 1.f32 + np.log2(4.f32)], np.float32)\n    assert eq(1 + np.log10(a), [1.f32 + np.log10(1.f32), 1.f32 + np.log10(2.f32), 1.f32 + np.log10(3.f32), 1.f32 + np.log10(4.f32)], np.float32)\n    assert eq(1 + np.expm1(a), [1.f32 + np.expm1(1.f32), 1.f32 + np.expm1(2.f32), 1.f32 + np.expm1(3.f32), 1.f32 + np.expm1(4.f32)], np.float32)\n    assert eq(1 + np.log1p(a), [1.f32 + np.log1p(1.f32), 1.f32 + np.log1p(2.f32), 1.f32 + np.log1p(3.f32), 1.f32 + np.log1p(4.f32)], np.float32)\n    assert eq(1 + np.sqrt(a), [1.f32 + np.sqrt(1.f32), 1.f32 + np.sqrt(2.f32), 1.f32 + np.sqrt(3.f32), 1.f32 + np.sqrt(4.f32)], np.float32)\n    assert eq(1 + np.square(a), [1 + np.square(1), 1 + np.square(2), 1 + np.square(3), 1 + np.square(4)], np.float32)\n    assert eq(1 + np.cbrt(a), [1.f32 + np.cbrt(1.f32), 1.f32 + np.cbrt(2.f32), 1.f32 + np.cbrt(3.f32), 1.f32 + np.cbrt(4.f32)], np.float32)\n    assert eq(1 + np.logaddexp(a, 3), [1.f32 + np.logaddexp(1.f32, 3.f32), 1.f32 + np.logaddexp(2.f32, 3.f32), 1.f32 + np.logaddexp(3.f32, 3.f32), 1.f32 + np.logaddexp(4.f32, 3.f32)], np.float32)\n    assert eq(1 + np.logaddexp(3, a), [1.f32 + np.logaddexp(3.f32, 1.f32), 1.f32 + np.logaddexp(3.f32, 2.f32), 1.f32 + np.logaddexp(3.f32, 3.f32), 1.f32 + np.logaddexp(3.f32, 4.f32)], np.float32)\n    assert eq(1 + np.logaddexp2(a, 3), [1.f32 + np.logaddexp2(1.f32, 3.f32), 1.f32 + np.logaddexp2(2.f32, 3.f32), 1.f32 + np.logaddexp2(3.f32, 3.f32), 1.f32 + np.logaddexp2(4.f32, 3.f32)], np.float32)\n    assert eq(1 + np.logaddexp2(3, a), [1.f32 + np.logaddexp2(3.f32, 1.f32), 1.f32 + np.logaddexp2(3.f32, 2.f32), 1.f32 + np.logaddexp2(3.f32, 3.f32), 1.f32 + np.logaddexp2(3.f32, 4.f32)], np.float32)\n    assert eq(1 + np.reciprocal(a), [2., 1.5, 1 + 1/3, 1.25], np.float32)\n    assert eq(1 + np.rint(a), [1.f32 + np.rint(1.f32), 1.f32 + np.rint(2.f32), 1.f32 + np.rint(3.f32), 1.f32 + np.rint(4.f32)], np.float32)\n    assert eq(1 + np.floor(a), [1.f32 + np.floor(1.f32), 1.f32 + np.floor(2.f32), 1.f32 + np.floor(3.f32), 1.f32 + np.floor(4.f32)], np.float32)\n    assert eq(1 + np.ceil(a), [1.f32 + np.ceil(1.f32), 1.f32 + np.ceil(2.f32), 1.f32 + np.ceil(3.f32), 1.f32 + np.ceil(4.f32)], np.float32)\n    assert eq(1 + np.trunc(a), [1.f32 + np.trunc(1.f32), 1.f32 + np.trunc(2.f32), 1.f32 + np.trunc(3.f32), 1.f32 + np.trunc(4.f32)], np.float32)\n    assert eq(1 + np.isnan(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isinf(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isfinite(a), [2, 2, 2, 2], int)\n    assert eq(1 + np.ldexp(a, 1), [1.f32 + np.ldexp(1.f32, 1), 1.f32 + np.ldexp(2.f32, 1), 1.f32 + np.ldexp(3.f32, 1), 1.f32 + np.ldexp(4.f32, 1)], np.float32)\n    assert eq(1 + np.sign(a), [2, 2, 2, 2], np.float32)\n    assert eq(1 + np.signbit(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.copysign(a, -1), [1.f32 + np.copysign(1.f32, -1.f32), 1.f32 + np.copysign(2.f32, -1.f32), 1.f32 + np.copysign(3.f32, -1.f32), 1.f32 + np.copysign(4.f32, -1.f32)], np.float32)\n    assert eq(1 + np.copysign(-1, a), [1.f32 + np.copysign(-1.f32, 1.f32), 1.f32 + np.copysign(-1.f32, 2.f32), 1.f32 + np.copysign(-1.f32, 3.f32), 1.f32 + np.copysign(-1.f32, 4.f32)], np.float32)\n    assert eq(1 + np.spacing(a), [1.f32 + np.spacing(1.f32), 1.f32 + np.spacing(2.f32), 1.f32 + np.spacing(3.f32), 1.f32 + np.spacing(4.f32)], np.float32)\n    assert eq(1 + np.nextafter(a, 1), [1.f32 + np.nextafter(1.f32, 1.f32), 1.f32 + np.nextafter(2.f32, 1.f32), 1.f32 + np.nextafter(3.f32, 1.f32), 1.f32 + np.nextafter(4.f32, 1.f32)], np.float32)\n    assert eq(1 + np.nextafter(1, a), [1.f32 + np.nextafter(1.f32, 1.f32), 1.f32 + np.nextafter(1.f32, 2.f32), 1.f32 + np.nextafter(1.f32, 3.f32), 1.f32 + np.nextafter(1.f32, 4.f32)], np.float32)\n    assert eq(1 + np.deg2rad(a), [1.f32 + np.deg2rad(1.f32), 1.f32 + np.deg2rad(2.f32), 1.f32 + np.deg2rad(3.f32), 1.f32 + np.deg2rad(4.f32)], np.float32)\n    assert eq(1 + np.rad2deg(a), [1.f32 + np.rad2deg(1.f32), 1.f32 + np.rad2deg(2.f32), 1.f32 + np.rad2deg(3.f32), 1.f32 + np.rad2deg(4.f32)], np.float32)\n    assert eq(1 + np.heaviside(a, 1), [1.f32 + np.heaviside(1.f32, 1.f32), 1.f32 + np.heaviside(2.f32, 1.f32), 1.f32 + np.heaviside(3.f32, 1.f32), 1.f32 + np.heaviside(4.f32, 1.f32)], np.float32)\n    assert eq(1 + np.heaviside(1, a), [1.f32 + np.heaviside(1.f32, 1.f32), 1.f32 + np.heaviside(1.f32, 2.f32), 1.f32 + np.heaviside(1.f32, 3.f32), 1.f32 + np.heaviside(1.f32, 4.f32)], np.float32)\n\n@test\ndef test_ops_float16():\n    a = np.array([1., 2., 3., 4.], np.float16)\n    assert eq(1 + (+a), [2, 3, 4, 5], np.float16)\n    assert eq(1 + (-a), [0, -1, -2, -3], np.float16)\n    assert eq(1 + np.abs(a), [2, 3, 4, 5], np.float16)\n    assert eq(1 + abs(a), [2, 3, 4, 5], np.float16)\n    assert eq(2 * (1 + a), [4, 6, 8, 10], np.float16)\n    assert eq(2 * (a + 1), [4, 6, 8, 10], np.float16)\n    assert eq(2 * (a - 1), [0, 2, 4, 6], np.float16)\n    assert eq(2 * (1 - a), [0, -2, -4, -6], np.float16)\n    assert eq(1 + (a * 2), [3, 5, 7, 9], np.float16)\n    assert eq(1 + (2 * a), [3, 5, 7, 9], np.float16)\n    assert eq(1 + (a.reshape(1, -1) @ a.reshape(-1, 1)), [[31]], np.float16)\n    assert eq(1 + (a / 2), [1.5, 2., 2.5, 3.], np.float16)\n    assert eq(1 + (2 / a), [3.f16, 2.f16, 1.f16 + 2.f16/3.f16, 1.5f16], np.float16)\n    assert eq(1 + (a // 2), [1, 2, 2, 3], np.float16)\n    assert eq(1 + (10 // a), [11, 6, 4, 3], np.float16)\n    assert eq(1 + (a % 3), [2, 3, 1, 2], np.float16)\n    assert eq(1 + (10 % a), [1, 1, 2, 3], np.float16)\n    assert eq(1 + np.fmod(a, 3), [2, 3, 1, 2], np.float16)\n    assert eq(1 + np.fmod(10, a), [1, 1, 2, 3], np.float16)\n    assert eq(1 + (a ** 3), [2, 9, 28, 65], np.float16)\n    assert eq(1 + (3 ** a), [4, 10, 28, 82], np.float16)\n    assert eq(np.logical_and(a, a - 1), [False, True, True, True], bool)\n    assert eq(np.logical_or(a, a - 1), [True, True, True, True], bool)\n    assert eq(np.logical_xor(a, a % 2), [False, True, False, True], bool)\n    assert eq((a + 1) == 2, [True, False, False, False], bool)\n    assert eq(2 == (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) != 2, [False, True, True, True], bool)\n    assert eq(2 != (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) < 3, [True, False, False, False], bool)\n    assert eq(3 < (a + 1), [False, False, True, True], bool)\n    assert eq((a + 1) > 3, [False, False, True, True], bool)\n    assert eq(3 > (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) <= 3, [True, True, False, False], bool)\n    assert eq(3 <= (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) >= 3, [False, True, True, True], bool)\n    assert eq(3 >= (a + 1), [True, True, False, False], bool)\n    assert eq(1 + np.minimum(a, 3), [2, 3, 4, 4], np.float16)\n    assert eq(1 + np.minimum(3, a), [2, 3, 4, 4], np.float16)\n    assert eq(1 + np.maximum(a, 3), [4, 4, 4, 5], np.float16)\n    assert eq(1 + np.maximum(3, a), [4, 4, 4, 5], np.float16)\n    assert eq(1 + np.fmin(a, 3), [2, 3, 4, 4], np.float16)\n    assert eq(1 + np.fmin(3, a), [2, 3, 4, 4], np.float16)\n    assert eq(1 + np.fmax(a, 3), [4, 4, 4, 5], np.float16)\n    assert eq(1 + np.fmax(3, a), [4, 4, 4, 5], np.float16)\n    assert eq(1 + np.minimum(a, np.nan), [np.nan, np.nan, np.nan, np.nan], np.float16)\n    assert eq(1 + np.minimum(np.nan, a), [np.nan, np.nan, np.nan, np.nan], np.float16)\n    assert eq(1 + np.maximum(a, np.nan), [np.nan, np.nan, np.nan, np.nan], np.float16)\n    assert eq(1 + np.maximum(np.nan, a), [np.nan, np.nan, np.nan, np.nan], np.float16)\n    assert eq(1 + np.fmin(a, np.nan), [2, 3, 4, 5], np.float16)\n    assert eq(1 + np.fmin(np.nan, a), [2, 3, 4, 5], np.float16)\n    assert eq(1 + np.fmax(a, np.nan), [2, 3, 4, 5], np.float16)\n    assert eq(1 + np.fmax(np.nan, a), [2, 3, 4, 5], np.float16)\n    assert eq(1 + np.sin(a), [1.f16 + np.sin(1.f16), 1.f16 + np.sin(2.f16), 1.f16 + np.sin(3.f16), 1.f16 + np.sin(4.f16)], np.float16)\n    assert eq(1 + np.cos(a), [1.f16 + np.cos(1.f16), 1.f16 + np.cos(2.f16), 1.f16 + np.cos(3.f16), 1.f16 + np.cos(4.f16)], np.float16)\n    assert eq(1 + np.tan(a), [1.f16 + np.tan(1.f16), 1.f16 + np.tan(2.f16), 1.f16 + np.tan(3.f16), 1.f16 + np.tan(4.f16)], np.float16)\n    assert eq(1 + np.arcsin(a), [1.f16 + np.arcsin(1.f16), 1.f16 + np.arcsin(2.f16), 1.f16 + np.arcsin(3.f16), 1.f16 + np.arcsin(4.f16)], np.float16)\n    assert eq(1 + np.arccos(a), [1.f16 + np.arccos(1.f16), 1.f16 + np.arccos(2.f16), 1.f16 + np.arccos(3.f16), 1.f16 + np.arccos(4.f16)], np.float16)\n    assert eq(1 + np.arctan(a), [1.f16 + np.arctan(1.f16), 1.f16 + np.arctan(2.f16), 1.f16 + np.arctan(3.f16), 1.f16 + np.arctan(4.f16)], np.float16)\n    assert eq(1 + np.arctan2(a, 3.f16), [1.f16 + np.arctan2(1.f16, 3.f16), 1.f16 + np.arctan2(2.f16, 3.f16), 1.f16 + np.arctan2(3.f16, 3.f16), 1.f16 + np.arctan2(4.f16, 3.f16)], np.float16)\n    assert eq(1 + np.arctan2(3.f16, a), [1.f16 + np.arctan2(3.f16, 1.f16), 1.f16 + np.arctan2(3.f16, 2.f16), 1.f16 + np.arctan2(3.f16, 3.f16), 1.f16 + np.arctan2(3.f16, 4.f16)], np.float16)\n    assert eq(1 + np.hypot(a, 3), [1.f16 + np.hypot(1.f16, 3.f16), 1.f16 + np.hypot(2.f16, 3.f16), 1.f16 + np.hypot(3.f16, 3.f16), 1.f16 + np.hypot(4.f16, 3.f16)], np.float16)\n    assert eq(1 + np.hypot(3, a), [1.f16 + np.hypot(3.f16, 1.f16), 1.f16 + np.hypot(3.f16, 2.f16), 1.f16 + np.hypot(3.f16, 3.f16), 1.f16 + np.hypot(3.f16, 4.f16)], np.float16)\n    assert eq(1 + np.sinh(a), [1.f16 + np.sinh(1.f16), 1.f16 + np.sinh(2.f16), 1.f16 + np.sinh(3.f16), 1.f16 + np.sinh(4.f16)], np.float16)\n    assert eq(1 + np.cosh(a), [1.f16 + np.cosh(1.f16), 1.f16 + np.cosh(2.f16), 1.f16 + np.cosh(3.f16), 1.f16 + np.cosh(4.f16)], np.float16)\n    assert eq(1 + np.tanh(a), [1.f16 + np.tanh(1.f16), 1.f16 + np.tanh(2.f16), 1.f16 + np.tanh(3.f16), 1.f16 + np.tanh(4.f16)], np.float16)\n    assert eq(1 + np.arcsinh(a), [1.f16 + np.arcsinh(1.f16), 1.f16 + np.arcsinh(2.f16), 1.f16 + np.arcsinh(3.f16), 1.f16 + np.arcsinh(4.f16)], np.float16)\n    assert eq(1 + np.arccosh(a), [1.f16 + np.arccosh(1.f16), 1.f16 + np.arccosh(2.f16), 1.f16 + np.arccosh(3.f16), 1.f16 + np.arccosh(4.f16)], np.float16)\n    assert eq(1 + np.arctanh(a), [1.f16 + np.arctanh(1.f16), 1.f16 + np.arctanh(2.f16), 1.f16 + np.arctanh(3.f16), 1.f16 + np.arctanh(4.f16)], np.float16)\n    assert eq(1 + np.conj(a), [1 + np.conj(1), 1 + np.conj(2), 1 + np.conj(3), 1 + np.conj(4)], np.float16)\n    assert eq(1 + np.exp(a), [1.f16 + np.exp(1.f16), 1.f16 + np.exp(2.f16), 1.f16 + np.exp(3.f16), 1.f16 + np.exp(4.f16)], np.float16)\n    assert eq(1 + np.exp2(a), [1.f16 + np.exp2(1.f16), 1.f16 + np.exp2(2.f16), 1.f16 + np.exp2(3.f16), 1.f16 + np.exp2(4.f16)], np.float16)\n    assert eq(1 + np.log(a), [1.f16 + np.log(1.f16), 1.f16 + np.log(2.f16), 1.f16 + np.log(3.f16), 1.f16 + np.log(4.f16)], np.float16)\n    assert eq(1 + np.log2(a), [1.f16 + np.log2(1.f16), 1.f16 + np.log2(2.f16), 1.f16 + np.log2(3.f16), 1.f16 + np.log2(4.f16)], np.float16)\n    assert eq(1 + np.log10(a), [1.f16 + np.log10(1.f16), 1.f16 + np.log10(2.f16), 1.f16 + np.log10(3.f16), 1.f16 + np.log10(4.f16)], np.float16)\n    assert eq(1 + np.expm1(a), [1.f16 + np.expm1(1.f16), 1.f16 + np.expm1(2.f16), 1.f16 + np.expm1(3.f16), 1.f16 + np.expm1(4.f16)], np.float16)\n    assert eq(1 + np.log1p(a), [1.f16 + np.log1p(1.f16), 1.f16 + np.log1p(2.f16), 1.f16 + np.log1p(3.f16), 1.f16 + np.log1p(4.f16)], np.float16)\n    assert eq(1 + np.sqrt(a), [1.f16 + np.sqrt(1.f16), 1.f16 + np.sqrt(2.f16), 1.f16 + np.sqrt(3.f16), 1.f16 + np.sqrt(4.f16)], np.float16)\n    assert eq(1 + np.square(a), [1 + np.square(1), 1 + np.square(2), 1 + np.square(3), 1 + np.square(4)], np.float16)\n    assert eq(1 + np.cbrt(a), [1.f16 + np.cbrt(1.f16), 1.f16 + np.cbrt(2.f16), 1.f16 + np.cbrt(3.f16), 1.f16 + np.cbrt(4.f16)], np.float16)\n    assert eq(1 + np.logaddexp(a, 3), [1.f16 + np.logaddexp(1.f16, 3.f16), 1.f16 + np.logaddexp(2.f16, 3.f16), 1.f16 + np.logaddexp(3.f16, 3.f16), 1.f16 + np.logaddexp(4.f16, 3.f16)], np.float16)\n    assert eq(1 + np.logaddexp(3, a), [1.f16 + np.logaddexp(3.f16, 1.f16), 1.f16 + np.logaddexp(3.f16, 2.f16), 1.f16 + np.logaddexp(3.f16, 3.f16), 1.f16 + np.logaddexp(3.f16, 4.f16)], np.float16)\n    assert eq(1 + np.logaddexp2(a, 3), [1.f16 + np.logaddexp2(1.f16, 3.f16), 1.f16 + np.logaddexp2(2.f16, 3.f16), 1.f16 + np.logaddexp2(3.f16, 3.f16), 1.f16 + np.logaddexp2(4.f16, 3.f16)], np.float16)\n    assert eq(1 + np.logaddexp2(3, a), [1.f16 + np.logaddexp2(3.f16, 1.f16), 1.f16 + np.logaddexp2(3.f16, 2.f16), 1.f16 + np.logaddexp2(3.f16, 3.f16), 1.f16 + np.logaddexp2(3.f16, 4.f16)], np.float16)\n    assert eq(1 + np.reciprocal(a), [2., 1.5, 1 + 1/3, 1.25], np.float16)\n    assert eq(1 + np.rint(a), [1.f16 + np.rint(1.f16), 1.f16 + np.rint(2.f16), 1.f16 + np.rint(3.f16), 1.f16 + np.rint(4.f16)], np.float16)\n    assert eq(1 + np.floor(a), [1.f16 + np.floor(1.f16), 1.f16 + np.floor(2.f16), 1.f16 + np.floor(3.f16), 1.f16 + np.floor(4.f16)], np.float16)\n    assert eq(1 + np.ceil(a), [1.f16 + np.ceil(1.f16), 1.f16 + np.ceil(2.f16), 1.f16 + np.ceil(3.f16), 1.f16 + np.ceil(4.f16)], np.float16)\n    assert eq(1 + np.trunc(a), [1.f16 + np.trunc(1.f16), 1.f16 + np.trunc(2.f16), 1.f16 + np.trunc(3.f16), 1.f16 + np.trunc(4.f16)], np.float16)\n    assert eq(1 + np.isnan(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isinf(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isfinite(a), [2, 2, 2, 2], int)\n    assert eq(1 + np.ldexp(a, 1), [1.f16 + np.ldexp(1.f16, 1), 1.f16 + np.ldexp(2.f16, 1), 1.f16 + np.ldexp(3.f16, 1), 1.f16 + np.ldexp(4.f16, 1)], np.float16)\n    assert eq(1 + np.sign(a), [2, 2, 2, 2], np.float16)\n    assert eq(1 + np.signbit(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.copysign(a, -1), [1.f16 + np.copysign(1.f16, -1.f16), 1.f16 + np.copysign(2.f16, -1.f16), 1.f16 + np.copysign(3.f16, -1.f16), 1.f16 + np.copysign(4.f16, -1.f16)], np.float16)\n    assert eq(1 + np.copysign(-1, a), [1.f16 + np.copysign(-1.f16, 1.f16), 1.f16 + np.copysign(-1.f16, 2.f16), 1.f16 + np.copysign(-1.f16, 3.f16), 1.f16 + np.copysign(-1.f16, 4.f16)], np.float16)\n    assert eq(1 + np.spacing(a), [1.f16 + np.spacing(1.f16), 1.f16 + np.spacing(2.f16), 1.f16 + np.spacing(3.f16), 1.f16 + np.spacing(4.f16)], np.float16)\n    assert eq(1 + np.nextafter(a, 1), [1.f16 + np.nextafter(1.f16, 1.f16), 1.f16 + np.nextafter(2.f16, 1.f16), 1.f16 + np.nextafter(3.f16, 1.f16), 1.f16 + np.nextafter(4.f16, 1.f16)], np.float16)\n    assert eq(1 + np.nextafter(1, a), [1.f16 + np.nextafter(1.f16, 1.f16), 1.f16 + np.nextafter(1.f16, 2.f16), 1.f16 + np.nextafter(1.f16, 3.f16), 1.f16 + np.nextafter(1.f16, 4.f16)], np.float16)\n    assert eq(1 + np.deg2rad(a), [1.f16 + np.deg2rad(1.f16), 1.f16 + np.deg2rad(2.f16), 1.f16 + np.deg2rad(3.f16), 1.f16 + np.deg2rad(4.f16)], np.float16)\n    assert eq(1 + np.rad2deg(a), [1.f16 + np.rad2deg(1.f16), 1.f16 + np.rad2deg(2.f16), 1.f16 + np.rad2deg(3.f16), 1.f16 + np.rad2deg(4.f16)], np.float16)\n    assert eq(1 + np.heaviside(a, 1), [1.f16 + np.heaviside(1.f16, 1.f16), 1.f16 + np.heaviside(2.f16, 1.f16), 1.f16 + np.heaviside(3.f16, 1.f16), 1.f16 + np.heaviside(4.f16, 1.f16)], np.float16)\n    assert eq(1 + np.heaviside(1, a), [1.f16 + np.heaviside(1.f16, 1.f16), 1.f16 + np.heaviside(1.f16, 2.f16), 1.f16 + np.heaviside(1.f16, 3.f16), 1.f16 + np.heaviside(1.f16, 4.f16)], np.float16)\n\n@test\ndef test_ops_complex128():\n    a = np.array([1., 2., 3., 4.], complex)\n    assert eq(1 + (+a), [2, 3, 4, 5], complex)\n    assert eq(1 + (-a), [0, -1, -2, -3], complex)\n    assert eq(1 + np.abs(a), [2, 3, 4, 5], float)\n    assert eq(1 + abs(a), [2, 3, 4, 5], float)\n    assert eq(2 * (1 + a), [4, 6, 8, 10], complex)\n    assert eq(2 * (a + 1), [4, 6, 8, 10], complex)\n    assert eq(2 * (a - 1), [0, 2, 4, 6], complex)\n    assert eq(2 * (1 - a), [0, -2, -4, -6], complex)\n    assert eq(1 + (a * 2), [3, 5, 7, 9], complex)\n    assert eq(1 + (2 * a), [3, 5, 7, 9], complex)\n    assert eq(1 + (a.reshape(1, -1) @ a.reshape(-1, 1)), [[31]], complex)\n    assert eq(1 + (a / 2), [1.5, 2., 2.5, 3.], complex)\n    assert eq(1 + (2 / a), [3., 2., 1 + 2/3, 1.5], complex)\n    assert eq(1 + (a ** 3), [2, 9, 28, 65], complex)\n    assert eq(1 + (3 ** a), [4, 10, 28, 82], complex)\n    assert eq(np.logical_and(a, a - 1), [False, True, True, True], bool)\n    assert eq(np.logical_or(a, a - 1), [True, True, True, True], bool)\n    assert eq(np.logical_xor(a, a - 1), [True, False, False, False], bool)\n    assert eq((a + 1) == 2, [True, False, False, False], bool)\n    assert eq(2 == (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) != 2, [False, True, True, True], bool)\n    assert eq(2 != (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) < 3, [True, False, False, False], bool)\n    assert eq(3 < (a + 1), [False, False, True, True], bool)\n    assert eq((a + 1) > 3, [False, False, True, True], bool)\n    assert eq(3 > (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) <= 3, [True, True, False, False], bool)\n    assert eq(3 <= (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) >= 3, [False, True, True, True], bool)\n    assert eq(3 >= (a + 1), [True, True, False, False], bool)\n    assert eq(1 + np.minimum(a, 3), [2, 3, 4, 4], complex)\n    assert eq(1 + np.minimum(3, a), [2, 3, 4, 4], complex)\n    assert eq(1 + np.maximum(a, 3), [4, 4, 4, 5], complex)\n    assert eq(1 + np.maximum(3, a), [4, 4, 4, 5], complex)\n    assert eq(1 + np.fmin(a, 3), [2, 3, 4, 4], complex)\n    assert eq(1 + np.fmin(3, a), [2, 3, 4, 4], complex)\n    assert eq(1 + np.fmax(a, 3), [4, 4, 4, 5], complex)\n    assert eq(1 + np.fmax(3, a), [4, 4, 4, 5], complex)\n    assert eq(1 + np.minimum(a, np.nan), [np.nan, np.nan, np.nan, np.nan], complex)\n    assert eq(1 + np.minimum(np.nan, a), [np.nan, np.nan, np.nan, np.nan], complex)\n    assert eq(1 + np.maximum(a, np.nan), [np.nan, np.nan, np.nan, np.nan], complex)\n    assert eq(1 + np.maximum(np.nan, a), [np.nan, np.nan, np.nan, np.nan], complex)\n    assert eq(1 + np.fmin(a, np.nan), [2, 3, 4, 5], complex)\n    assert eq(1 + np.fmin(np.nan, a), [2, 3, 4, 5], complex)\n    assert eq(1 + np.fmax(a, np.nan), [2, 3, 4, 5], complex)\n    assert eq(1 + np.fmax(np.nan, a), [2, 3, 4, 5], complex)\n    assert eq(1 + np.sin(a), [1 + np.sin(1+0j), 1 + np.sin(2+0j), 1 + np.sin(3+0j), 1 + np.sin(4+0j)], complex)\n    assert eq(1 + np.cos(a), [1 + np.cos(1+0j), 1 + np.cos(2+0j), 1 + np.cos(3+0j), 1 + np.cos(4+0j)], complex)\n    assert eq(1 + np.tan(a), [1 + np.tan(1+0j), 1 + np.tan(2+0j), 1 + np.tan(3+0j), 1 + np.tan(4+0j)], complex)\n    assert eq(1 + np.arcsin(a), [1 + np.arcsin(1+0j), 1 + np.arcsin(2+0j), 1 + np.arcsin(3+0j), 1 + np.arcsin(4+0j)], complex)\n    assert eq(1 + np.arccos(a), [1 + np.arccos(1+0j), 1 + np.arccos(2+0j), 1 + np.arccos(3+0j), 1 + np.arccos(4+0j)], complex)\n    assert eq(1 + np.arctan(a), [1 + np.arctan(1+0j), 1 + np.arctan(2+0j), 1 + np.arctan(3+0j), 1 + np.arctan(4+0j)], complex)\n    assert eq(1 + np.sinh(a), [1 + np.sinh(1+0j), 1 + np.sinh(2+0j), 1 + np.sinh(3+0j), 1 + np.sinh(4+0j)], complex)\n    assert eq(1 + np.cosh(a), [1 + np.cosh(1+0j), 1 + np.cosh(2+0j), 1 + np.cosh(3+0j), 1 + np.cosh(4+0j)], complex)\n    assert eq(1 + np.tanh(a), [1 + np.tanh(1+0j), 1 + np.tanh(2+0j), 1 + np.tanh(3+0j), 1 + np.tanh(4+0j)], complex)\n    assert eq(1 + np.arcsinh(a), [1 + np.arcsinh(1+0j), 1 + np.arcsinh(2+0j), 1 + np.arcsinh(3+0j), 1 + np.arcsinh(4+0j)], complex)\n    assert eq(1 + np.arccosh(a), [1 + np.arccosh(1+0j), 1 + np.arccosh(2+0j), 1 + np.arccosh(3+0j), 1 + np.arccosh(4+0j)], complex)\n    assert eq(1 + np.arctanh(a), [1 + np.arctanh(1+0j), 1 + np.arctanh(2+0j), 1 + np.arctanh(3+0j), 1 + np.arctanh(4+0j)], complex)\n    assert eq(1 + np.conj(a), [1 + np.conj(1), 1 + np.conj(2), 1 + np.conj(3), 1 + np.conj(4)], complex)\n    assert eq(1 + np.exp(a), [1 + np.exp(1), 1 + np.exp(2), 1 + np.exp(3), 1 + np.exp(4)], complex)\n    assert eq(1 + np.exp2(a), [1 + np.exp2(1), 1 + np.exp2(2), 1 + np.exp2(3), 1 + np.exp2(4)], complex)\n    assert eq(1 + np.log(a), [1 + np.log(1), 1 + np.log(2), 1 + np.log(3), 1 + np.log(4)], complex)\n    assert eq(1 + np.log2(a), [1 + np.log2(1), 1 + np.log2(2), 1 + np.log2(3), 1 + np.log2(4)], complex)\n    assert eq(1 + np.log10(a), [1 + np.log10(1), 1 + np.log10(2), 1 + np.log10(3), 1 + np.log10(4)], complex)\n    assert eq(1 + np.expm1(a), [1 + np.expm1(1), 1 + np.expm1(2), 1 + np.expm1(3), 1 + np.expm1(4)], complex)\n    assert eq(1 + np.log1p(a), [1 + np.log1p(1), 1 + np.log1p(2), 1 + np.log1p(3), 1 + np.log1p(4)], complex)\n    assert eq(1 + np.sqrt(a), [1 + np.sqrt(1), 1 + np.sqrt(2), 1 + np.sqrt(3), 1 + np.sqrt(4)], complex)\n    assert eq(1 + np.square(a), [1 + np.square(1), 1 + np.square(2), 1 + np.square(3), 1 + np.square(4)], complex)\n    assert eq(1 + np.reciprocal(a), [2., 1.5, 1 + 1/3, 1.25], complex)\n    assert eq(1 + np.isnan(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isinf(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isfinite(a), [2, 2, 2, 2], int)\n    assert eq(1 + np.sign(a), [2, 2, 2, 2], complex)\n\n@test\ndef test_ops_complex64():\n    a = np.array([1., 2., 3., 4.], np.complex64)\n    assert eq(1 + (+a), [2, 3, 4, 5], np.complex64)\n    assert eq(1 + (-a), [0, -1, -2, -3], np.complex64)\n    assert eq(1 + np.abs(a), [2, 3, 4, 5], np.float32)\n    assert eq(1 + abs(a), [2, 3, 4, 5], np.float32)\n    assert eq(2 * (1 + a), [4, 6, 8, 10], np.complex64)\n    assert eq(2 * (a + 1), [4, 6, 8, 10], np.complex64)\n    assert eq(2 * (a - 1), [0, 2, 4, 6], np.complex64)\n    assert eq(2 * (1 - a), [0, -2, -4, -6], np.complex64)\n    assert eq(1 + (a * 2), [3, 5, 7, 9], np.complex64)\n    assert eq(1 + (2 * a), [3, 5, 7, 9], np.complex64)\n    assert eq(1 + (a.reshape(1, -1) @ a.reshape(-1, 1)), [[31]], np.complex64)\n    assert eq(1 + (a / 2), [1.5, 2., 2.5, 3.], np.complex64)\n    assert eq(1 + (2 / a), [3., 2., 1 + 2/3, 1.5], np.complex64)\n    assert eq(1 + (a ** 3), [2, 9, 28, 65], np.complex64)\n    assert eq(1 + (3 ** a), [4, 10, 28, 82], np.complex64)\n    assert eq(np.logical_and(a, a - 1), [False, True, True, True], bool)\n    assert eq(np.logical_or(a, a - 1), [True, True, True, True], bool)\n    assert eq(np.logical_xor(a, a - 1), [True, False, False, False], bool)\n    assert eq((a + 1) == 2, [True, False, False, False], bool)\n    assert eq(2 == (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) != 2, [False, True, True, True], bool)\n    assert eq(2 != (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) < 3, [True, False, False, False], bool)\n    assert eq(3 < (a + 1), [False, False, True, True], bool)\n    assert eq((a + 1) > 3, [False, False, True, True], bool)\n    assert eq(3 > (a + 1), [True, False, False, False], bool)\n    assert eq((a + 1) <= 3, [True, True, False, False], bool)\n    assert eq(3 <= (a + 1), [False, True, True, True], bool)\n    assert eq((a + 1) >= 3, [False, True, True, True], bool)\n    assert eq(3 >= (a + 1), [True, True, False, False], bool)\n    assert eq(1 + np.minimum(a, 3), [2, 3, 4, 4], np.complex64)\n    assert eq(1 + np.minimum(3, a), [2, 3, 4, 4], np.complex64)\n    assert eq(1 + np.maximum(a, 3), [4, 4, 4, 5], np.complex64)\n    assert eq(1 + np.maximum(3, a), [4, 4, 4, 5], np.complex64)\n    assert eq(1 + np.fmin(a, 3), [2, 3, 4, 4], np.complex64)\n    assert eq(1 + np.fmin(3, a), [2, 3, 4, 4], np.complex64)\n    assert eq(1 + np.fmax(a, 3), [4, 4, 4, 5], np.complex64)\n    assert eq(1 + np.fmax(3, a), [4, 4, 4, 5], np.complex64)\n    assert eq(1 + np.minimum(a, np.nan), [np.nan, np.nan, np.nan, np.nan], np.complex64)\n    assert eq(1 + np.minimum(np.nan, a), [np.nan, np.nan, np.nan, np.nan], np.complex64)\n    assert eq(1 + np.maximum(a, np.nan), [np.nan, np.nan, np.nan, np.nan], np.complex64)\n    assert eq(1 + np.maximum(np.nan, a), [np.nan, np.nan, np.nan, np.nan], np.complex64)\n    assert eq(1 + np.fmin(a, np.nan), [2, 3, 4, 5], np.complex64)\n    assert eq(1 + np.fmin(np.nan, a), [2, 3, 4, 5], np.complex64)\n    assert eq(1 + np.fmax(a, np.nan), [2, 3, 4, 5], np.complex64)\n    assert eq(1 + np.fmax(np.nan, a), [2, 3, 4, 5], np.complex64)\n    assert eq(1 + np.sin(a), [1 + np.sin(1+0j), 1 + np.sin(2+0j), 1 + np.sin(3+0j), 1 + np.sin(4+0j)], np.complex64)\n    assert eq(1 + np.cos(a), [1 + np.cos(1+0j), 1 + np.cos(2+0j), 1 + np.cos(3+0j), 1 + np.cos(4+0j)], np.complex64)\n    assert eq(1 + np.tan(a), [1 + np.tan(1+0j), 1 + np.tan(2+0j), 1 + np.tan(3+0j), 1 + np.tan(4+0j)], np.complex64)\n    assert eq(1 + np.arcsin(a), [1 + np.arcsin(1+0j), 1 + np.arcsin(2+0j), 1 + np.arcsin(3+0j), 1 + np.arcsin(4+0j)], np.complex64)\n    assert eq(1 + np.arccos(a), [1 + np.arccos(1+0j), 1 + np.arccos(2+0j), 1 + np.arccos(3+0j), 1 + np.arccos(4+0j)], np.complex64)\n    assert eq(1 + np.arctan(a), [1 + np.arctan(1+0j), 1 + np.arctan(2+0j), 1 + np.arctan(3+0j), 1 + np.arctan(4+0j)], np.complex64)\n    assert eq(1 + np.sinh(a), [1 + np.sinh(1+0j), 1 + np.sinh(2+0j), 1 + np.sinh(3+0j), 1 + np.sinh(4+0j)], np.complex64)\n    assert eq(1 + np.cosh(a), [1 + np.cosh(1+0j), 1 + np.cosh(2+0j), 1 + np.cosh(3+0j), 1 + np.cosh(4+0j)], np.complex64)\n    assert eq(1 + np.tanh(a), [1 + np.tanh(1+0j), 1 + np.tanh(2+0j), 1 + np.tanh(3+0j), 1 + np.tanh(4+0j)], np.complex64)\n    assert eq(1 + np.arcsinh(a), [1 + np.arcsinh(1+0j), 1 + np.arcsinh(2+0j), 1 + np.arcsinh(3+0j), 1 + np.arcsinh(4+0j)], np.complex64)\n    assert eq(1 + np.arccosh(a), [1 + np.arccosh(1+0j), 1 + np.arccosh(2+0j), 1 + np.arccosh(3+0j), 1 + np.arccosh(4+0j)], np.complex64)\n    # Note: it seems im(arctanh(complex64(1, 0))) == 0 on macOS\n    # assert eq(1 + np.arctanh(a), [1 + np.arctanh(1+0j), 1 + np.arctanh(2+0j), 1 + np.arctanh(3+0j), 1 + np.arctanh(4+0j)], np.complex64)\n    assert eq(1 + np.conj(a), [1 + np.conj(1), 1 + np.conj(2), 1 + np.conj(3), 1 + np.conj(4)], np.complex64)\n    assert eq(1 + np.exp(a), [1 + np.exp(1), 1 + np.exp(2), 1 + np.exp(3), 1 + np.exp(4)], np.complex64)\n    assert eq(1 + np.exp2(a), [1 + np.exp2(1), 1 + np.exp2(2), 1 + np.exp2(3), 1 + np.exp2(4)], np.complex64)\n    assert eq(1 + np.log(a), [1 + np.log(1), 1 + np.log(2), 1 + np.log(3), 1 + np.log(4)], np.complex64)\n    assert eq(1 + np.log2(a), [1 + np.log2(1), 1 + np.log2(2), 1 + np.log2(3), 1 + np.log2(4)], np.complex64)\n    assert eq(1 + np.log10(a), [1 + np.log10(1), 1 + np.log10(2), 1 + np.log10(3), 1 + np.log10(4)], np.complex64)\n    assert eq(1 + np.expm1(a), [1 + np.expm1(1), 1 + np.expm1(2), 1 + np.expm1(3), 1 + np.expm1(4)], np.complex64)\n    assert eq(1 + np.log1p(a), [1 + np.log1p(1), 1 + np.log1p(2), 1 + np.log1p(3), 1 + np.log1p(4)], np.complex64)\n    assert eq(1 + np.sqrt(a), [1 + np.sqrt(1), 1 + np.sqrt(2), 1 + np.sqrt(3), 1 + np.sqrt(4)], np.complex64)\n    assert eq(1 + np.square(a), [1 + np.square(1), 1 + np.square(2), 1 + np.square(3), 1 + np.square(4)], np.complex64)\n    assert eq(1 + np.reciprocal(a), [2., 1.5, 1 + 1/3, 1.25], np.complex64)\n    assert eq(1 + np.isnan(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isinf(a), [1, 1, 1, 1], int)\n    assert eq(1 + np.isfinite(a), [2, 2, 2, 2], int)\n    assert eq(1 + np.sign(a), [2, 2, 2, 2], np.complex64)\n\n@test\ndef test_mixed_types():\n    i8 = np.array([1, 2, 3, 4], np.int8)\n    i16 = np.array([1, 2, 3, 4], np.int16)\n    i32 = np.array([1, 2, 3, 4], np.int32)\n    i64 = np.array([1, 2, 3, 4], np.int64)\n    u8 = np.array([1, 2, 3, 4], np.uint8)\n    u16 = np.array([1, 2, 3, 4], np.uint16)\n    u32 = np.array([1, 2, 3, 4], np.uint32)\n    u64 = np.array([1, 2, 3, 4], np.uint64)\n    f16 = np.array([1., 2., 3., 4.], np.float16)\n    f32 = np.array([1., 2., 3., 4.], np.float32)\n    f64 = np.array([1., 2., 3., 4.], np.float64)\n    c64 = np.array([1., 2., 3., 4.], np.complex64)\n    c128 = np.array([1., 2., 3., 4.], np.complex128)\n\n    assert eq(i8 + i16 + i32, [3, 6, 9, 12], np.int32)\n    assert eq(i16 + i32 + i64, [3, 6, 9, 12], np.int64)\n    assert eq(u8 + u16 + u32, [3, 6, 9, 12], np.uint32)\n    assert eq(i8 + u16 + i32, [3, 6, 9, 12], np.int32)\n    assert eq(i16 + u32 + i64, [3, 6, 9, 12], np.int64)\n    assert eq(u8 + i32 + u64, [3, 6, 9, 12], np.float64)\n    assert eq(i8 + u16 + u64, [3, 6, 9, 12], np.float64)\n    assert eq(i16 + i64 + u64, [3, 6, 9, 12], np.float64)\n    assert eq(i32 + u32 + i64, [3, 6, 9, 12], np.int64)\n    assert eq(u16 + i64 + u32, [3, 6, 9, 12], np.int64)\n\n    assert eq(f16 + f32 + f64, [3., 6., 9., 12.], np.float64)\n    assert eq(f16 + f32 + f16, [3., 6., 9., 12.], np.float32)\n    assert eq(f32 + f64 + f32, [3., 6., 9., 12.], np.float64)\n    assert eq(f64 + f16 + f32, [3., 6., 9., 12.], np.float64)\n    assert eq(f16 + f16 + f64, [3., 6., 9., 12.], np.float64)\n    assert eq(f32 + f32 + f16, [3., 6., 9., 12.], np.float32)\n    assert eq(f64 + f64 + f32, [3., 6., 9., 12.], np.float64)\n    assert eq(f16 + f64 + f64, [3., 6., 9., 12.], np.float64)\n    assert eq(f32 + f16 + f32, [3., 6., 9., 12.], np.float32)\n    assert eq(f64 + f32 + f64, [3., 6., 9., 12.], np.float64)\n\n    assert eq(i8 + i16 + f32, [3., 6., 9., 12.], np.float32)\n    assert eq(i32 + f32 + c64, [3.+0.j, 6.+0.j, 9.+0.j, 12.+0.j], np.complex128)\n    assert eq(f16 + u8 + u16, [3., 6., 9., 12.], np.float32)\n    assert eq(i64 + f64 + c128, [3.+0.j, 6.+0.j, 9+0.j, 12.+0.j], np.complex128)\n    assert eq(u16 + c64 + f32, [3.+0.j, 6.+0.j, 9.+0.j, 12.+0.j], np.complex64)\n    assert eq(f32 + c128 + i32, [3.+0.j, 6.+0.j, 9.+0.j, 12.+0.j], np.complex128)\n    assert eq(u32 + i16 + f64, [3., 6., 9., 12.], np.float64)\n    assert eq(i8 + u64 + f64, [3., 6., 9., 12.], np.float64)\n    assert eq(f64 + c64 + u16, [3.+0.j, 6.+0.j, 9.+0.j, 12.+0.j], np.complex128)\n    assert eq(c128 + i32 + u32, [3.+0.j, 6.+0.j, 9.+0.j, 12.+0.j], np.complex128)\n    assert eq(i8 + f16 + c64, [3.+0.j, 6.+0.j, 9.+0.j, 12.+0.j], np.complex64)\n    assert eq(u8 + i64 + c128, [3.+0.j, 6.+0.j, 9.+0.j, 12.+0.j], np.complex128)\n    assert eq(i16 + f32 + c64, [3.+0.j, 6.+0.j, 9.+0.j, 12.+0.j], np.complex64)\n\n    assert eq((i8 == i16) + c64, [2.+0j, 3.+0j, 4.+0j, 5.+0j], np.complex64)\n    assert eq(c64 + (i8 == i16), [2.+0j, 3.+0j, 4.+0j, 5.+0j], np.complex64)\n\n    assert eq((i8 + u8) + (i16 + u16) + (u32 + u32) + (i64 + u64) + f16 + f32 + f64 + c64 + c128,\n              [13.+0.j, 26.+0.j, 39.+0.j, 52.+0.j], np.complex128)\n\n    # Mixing scalars and arrays\n    assert eq((i8 + 100) << 1, [-54, -52, -50, -48], np.int8)\n    assert eq((i8 + np.array(100)) << 1, [202, 204, 206, 208], np.int64)\n    assert eq((100 + i8) << 1, [-54, -52, -50, -48], np.int8)\n    assert eq((np.array(100) + i8) << 1, [202, 204, 206, 208], np.int64)\n    assert eq((i32 + 1.5) * 2, [5., 7., 9., 11.], np.float64)\n    assert eq((i32 + np.array(1.5)) * 2, [5., 7., 9., 11.], np.float64)\n    assert eq((1.5 + i32) * 2, [5., 7., 9., 11.], np.float64)\n    assert eq((np.array(1.5) + i32) * 2, [5., 7., 9., 11.], np.float64)\n    assert eq((i32 + (1.5 + 0j)) * 2, [5., 7., 9., 11.], np.complex128)\n    assert eq((i32 + np.array(1.5 + 0j)) * 2, [5., 7., 9., 11.], np.complex128)\n    assert eq(((1.5 + 0j) + i32) * 2, [5., 7., 9., 11.], np.complex128)\n    assert eq((np.array(1.5 + 0j) + i32) * 2, [5., 7., 9., 11.], np.complex128)\n\n    # Complex -> Float conversions\n    z = np.array([1+1j, 2+2j, 3+3j])\n    assert eq(1 + abs(z), [2.41421356, 3.82842712, 5.24264069], np.float64)\n    assert eq(1 + np.abs(z), [2.41421356, 3.82842712, 5.24264069], np.float64)\n    z = np.array([1+1j, 2+2j, 3+3j], np.complex64)\n    assert eq(1 + abs(z), [2.41421356, 3.82842712, 5.24264069], np.float32)\n    assert eq(1 + np.abs(z), [2.41421356, 3.82842712, 5.24264069], np.float32)\n\n@test\ndef test_div_mod():\n    xf64 = np.array([-4., -3., -2., -1., 0., 1., 2., 3., 4.], np.float64)\n    xf32 = np.array([-4., -3., -2., -1., 0., 1., 2., 3., 4.], np.float32)\n    xf16 = np.array([-4., -3., -2., -1., 0., 1., 2., 3., 4.], np.float16)\n\n    xi64 = np.array([-4, -3, -2, -1, 0, 1, 2, 3, 4], np.int64)\n    xi32 = np.array([-4, -3, -2, -1, 0, 1, 2, 3, 4], np.int32)\n    xi16 = np.array([-4, -3, -2, -1, 0, 1, 2, 3, 4], np.int16)\n    xi8 = np.array([-4, -3, -2, -1, 0, 1, 2, 3, 4], np.int8)\n\n    xu64 = np.array([0, 1, 2, 3, 4], np.uint64)\n    xu32 = np.array([0, 1, 2, 3, 4], np.uint32)\n    xu16 = np.array([0, 1, 2, 3, 4], np.uint16)\n    xu8 = np.array([0, 1, 2, 3, 4], np.uint8)\n\n    assert eq(xf64 % 1, [0., 0., 0., 0., 0., 0., 0., 0., 0.], np.float64)\n    assert eq(np.fmod(xf64, 1), [-0., -0., -0., -0., 0., 0., 0., 0., 0.],\n              np.float64)\n    assert eq(np.remainder(xf64, 1), [0., 0., 0., 0., 0., 0., 0., 0., 0.],\n              np.float64)\n    assert eq(xf64 % -1, [-0., -0., -0., -0., -0., -0., -0., -0., -0.], np.float64)\n    assert eq(np.fmod(xf64, -1), [-0., -0., -0., -0., 0., 0., 0., 0., 0.],\n              np.float64)\n    assert eq(np.remainder(xf64, -1),\n              [-0., -0., -0., -0., -0., -0., -0., -0., -0.], np.float64)\n\n    assert eq(xf64 % 2, [0., 1., 0., 1., 0., 1., 0., 1., 0.], np.float64)\n    assert eq(np.fmod(xf64, 2), [-0., -1., -0., -1., 0., 1., 0., 1., 0.],\n              np.float64)\n    assert eq(np.remainder(xf64, 2), [0., 1., 0., 1., 0., 1., 0., 1., 0.],\n              np.float64)\n    assert eq(xf64 % -2, [-0., -1., -0., -1., -0., -1., -0., -1., -0.], np.float64)\n    assert eq(np.fmod(xf64, -2), [-0., -1., -0., -1., 0., 1., 0., 1., 0.],\n              np.float64)\n    assert eq(np.remainder(xf64, -2),\n              [-0., -1., -0., -1., -0., -1., -0., -1., -0.], np.float64)\n\n    assert eq(xf64 % 3, [2., 0., 1., 2., 0., 1., 2., 0., 1.], np.float64)\n    assert eq(np.fmod(xf64, 3), [-1., -0., -2., -1., 0., 1., 2., 0., 1.],\n              np.float64)\n    assert eq(np.remainder(xf64, 3), [2., 0., 1., 2., 0., 1., 2., 0., 1.],\n              np.float64)\n    assert eq(xf64 % -3, [-1., -0., -2., -1., -0., -2., -1., -0., -2.], np.float64)\n    assert eq(np.fmod(xf64, -3), [-1., -0., -2., -1., 0., 1., 2., 0., 1.],\n              np.float64)\n    assert eq(np.remainder(xf64, -3),\n              [-1., -0., -2., -1., -0., -2., -1., -0., -2.], np.float64)\n\n    assert eq(xf32 % 1, [0., 0., 0., 0., 0., 0., 0., 0., 0.], np.float32)\n    assert eq(np.fmod(xf32, 1), [-0., -0., -0., -0., 0., 0., 0., 0., 0.],\n              np.float32)\n    assert eq(np.remainder(xf32, 1), [0., 0., 0., 0., 0., 0., 0., 0., 0.],\n              np.float32)\n    assert eq(xf32 % -1, [-0., -0., -0., -0., -0., -0., -0., -0., -0.], np.float32)\n    assert eq(np.fmod(xf32, -1), [-0., -0., -0., -0., 0., 0., 0., 0., 0.],\n              np.float32)\n    assert eq(np.remainder(xf32, -1),\n              [-0., -0., -0., -0., -0., -0., -0., -0., -0.], np.float32)\n\n    assert eq(xf32 % 2, [0., 1., 0., 1., 0., 1., 0., 1., 0.], np.float32)\n    assert eq(np.fmod(xf32, 2), [-0., -1., -0., -1., 0., 1., 0., 1., 0.],\n              np.float32)\n    assert eq(np.remainder(xf32, 2), [0., 1., 0., 1., 0., 1., 0., 1., 0.],\n              np.float32)\n    assert eq(xf32 % -2, [-0., -1., -0., -1., -0., -1., -0., -1., -0.], np.float32)\n    assert eq(np.fmod(xf32, -2), [-0., -1., -0., -1., 0., 1., 0., 1., 0.],\n              np.float32)\n    assert eq(np.remainder(xf32, -2),\n              [-0., -1., -0., -1., -0., -1., -0., -1., -0.], np.float32)\n\n    assert eq(xf32 % 3, [2., 0., 1., 2., 0., 1., 2., 0., 1.], np.float32)\n    assert eq(np.fmod(xf32, 3), [-1., -0., -2., -1., 0., 1., 2., 0., 1.],\n              np.float32)\n    assert eq(np.remainder(xf32, 3), [2., 0., 1., 2., 0., 1., 2., 0., 1.],\n              np.float32)\n    assert eq(xf32 % -3, [-1., -0., -2., -1., -0., -2., -1., -0., -2.], np.float32)\n    assert eq(np.fmod(xf32, -3), [-1., -0., -2., -1., 0., 1., 2., 0., 1.],\n              np.float32)\n    assert eq(np.remainder(xf32, -3),\n              [-1., -0., -2., -1., -0., -2., -1., -0., -2.], np.float32)\n\n    assert eq(xf16 % 1, [0., 0., 0., 0., 0., 0., 0., 0., 0.], np.float16)\n    assert eq(np.fmod(xf16, 1), [-0., -0., -0., -0., 0., 0., 0., 0., 0.],\n              np.float16)\n    assert eq(np.remainder(xf16, 1), [0., 0., 0., 0., 0., 0., 0., 0., 0.],\n              np.float16)\n    assert eq(xf16 % -1, [-0., -0., -0., -0., -0., -0., -0., -0., -0.], np.float16)\n    assert eq(np.fmod(xf16, -1), [-0., -0., -0., -0., 0., 0., 0., 0., 0.],\n              np.float16)\n    assert eq(np.remainder(xf16, -1),\n              [-0., -0., -0., -0., -0., -0., -0., -0., -0.], np.float16)\n\n    assert eq(xf16 % 2, [0., 1., 0., 1., 0., 1., 0., 1., 0.], np.float16)\n    assert eq(np.fmod(xf16, 2), [-0., -1., -0., -1., 0., 1., 0., 1., 0.],\n              np.float16)\n    assert eq(np.remainder(xf16, 2), [0., 1., 0., 1., 0., 1., 0., 1., 0.],\n              np.float16)\n    assert eq(xf16 % -2, [-0., -1., -0., -1., -0., -1., -0., -1., -0.], np.float16)\n    assert eq(np.fmod(xf16, -2), [-0., -1., -0., -1., 0., 1., 0., 1., 0.],\n              np.float16)\n    assert eq(np.remainder(xf16, -2),\n              [-0., -1., -0., -1., -0., -1., -0., -1., -0.], np.float16)\n\n    assert eq(xf16 % 3, [2., 0., 1., 2., 0., 1., 2., 0., 1.], np.float16)\n    assert eq(np.fmod(xf16, 3), [-1., -0., -2., -1., 0., 1., 2., 0., 1.],\n              np.float16)\n    assert eq(np.remainder(xf16, 3), [2., 0., 1., 2., 0., 1., 2., 0., 1.],\n              np.float16)\n    assert eq(xf16 % -3, [-1., -0., -2., -1., -0., -2., -1., -0., -2.], np.float16)\n    assert eq(np.fmod(xf16, -3), [-1., -0., -2., -1., 0., 1., 2., 0., 1.],\n              np.float16)\n    assert eq(np.remainder(xf16, -3),\n              [-1., -0., -2., -1., -0., -2., -1., -0., -2.], np.float16)\n\n    assert eq(xi64 % 1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int64)\n    assert eq(np.fmod(xi64, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int64)\n    assert eq(np.remainder(xi64, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int64)\n    assert eq(xi64 % -1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int64)\n    assert eq(np.fmod(xi64, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int64)\n    assert eq(np.remainder(xi64, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int64)\n\n    assert eq(xi64 % 2, [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int64)\n    assert eq(np.fmod(xi64, 2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int64)\n    assert eq(np.remainder(xi64, 2), [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int64)\n    assert eq(xi64 % -2, [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int64)\n    assert eq(np.fmod(xi64, -2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int64)\n    assert eq(np.remainder(xi64, -2), [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int64)\n\n    assert eq(xi64 % 3, [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int64)\n    assert eq(np.fmod(xi64, 3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int64)\n    assert eq(np.remainder(xi64, 3), [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int64)\n    assert eq(xi64 % -3, [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int64)\n    assert eq(np.fmod(xi64, -3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int64)\n    assert eq(np.remainder(xi64, -3), [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int64)\n\n    assert eq(xi32 % 1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int32)\n    assert eq(np.fmod(xi32, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int32)\n    assert eq(np.remainder(xi32, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int32)\n    assert eq(xi32 % -1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int32)\n    assert eq(np.fmod(xi32, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int32)\n    assert eq(np.remainder(xi32, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int32)\n\n    assert eq(xi32 % 2, [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int32)\n    assert eq(np.fmod(xi32, 2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int32)\n    assert eq(np.remainder(xi32, 2), [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int32)\n    assert eq(xi32 % -2, [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int32)\n    assert eq(np.fmod(xi32, -2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int32)\n    assert eq(np.remainder(xi32, -2), [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int32)\n\n    assert eq(xi32 % 3, [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int32)\n    assert eq(np.fmod(xi32, 3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int32)\n    assert eq(np.remainder(xi32, 3), [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int32)\n    assert eq(xi32 % -3, [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int32)\n    assert eq(np.fmod(xi32, -3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int32)\n    assert eq(np.remainder(xi32, -3), [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int32)\n\n    assert eq(xi16 % 1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int16)\n    assert eq(np.fmod(xi16, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int16)\n    assert eq(np.remainder(xi16, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int16)\n    assert eq(xi16 % -1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int16)\n    assert eq(np.fmod(xi16, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int16)\n    assert eq(np.remainder(xi16, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int16)\n\n    assert eq(xi16 % 2, [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int16)\n    assert eq(np.fmod(xi16, 2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int16)\n    assert eq(np.remainder(xi16, 2), [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int16)\n    assert eq(xi16 % -2, [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int16)\n    assert eq(np.fmod(xi16, -2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int16)\n    assert eq(np.remainder(xi16, -2), [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int16)\n\n    assert eq(xi16 % 3, [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int16)\n    assert eq(np.fmod(xi16, 3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int16)\n    assert eq(np.remainder(xi16, 3), [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int16)\n    assert eq(xi16 % -3, [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int16)\n    assert eq(np.fmod(xi16, -3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int16)\n    assert eq(np.remainder(xi16, -3), [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int16)\n\n    assert eq(xi8 % 1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int8)\n    assert eq(np.fmod(xi8, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int8)\n    assert eq(np.remainder(xi8, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int8)\n    assert eq(xi8 % -1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int8)\n    assert eq(np.fmod(xi8, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int8)\n    assert eq(np.remainder(xi8, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int8)\n\n    assert eq(xi8 % 2, [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int8)\n    assert eq(np.fmod(xi8, 2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int8)\n    assert eq(np.remainder(xi8, 2), [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int8)\n    assert eq(xi8 % -2, [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int8)\n    assert eq(np.fmod(xi8, -2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int8)\n    assert eq(np.remainder(xi8, -2), [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int8)\n\n    assert eq(xi8 % 3, [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int8)\n    assert eq(np.fmod(xi8, 3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int8)\n    assert eq(np.remainder(xi8, 3), [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int8)\n    assert eq(xi8 % -3, [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int8)\n    assert eq(np.fmod(xi8, -3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int8)\n    assert eq(np.remainder(xi8, -3), [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int8)\n\n    assert eq(xu64 % 1, [0, 0, 0, 0, 0], np.uint64)\n    assert eq(np.fmod(xu64, 1), [0, 0, 0, 0, 0], np.uint64)\n    assert eq(np.remainder(xu64, 1), [0, 0, 0, 0, 0], np.uint64)\n\n    assert eq(xu64 % 2, [0, 1, 0, 1, 0], np.uint64)\n    assert eq(np.fmod(xu64, 2), [0, 1, 0, 1, 0], np.uint64)\n    assert eq(np.remainder(xu64, 2), [0, 1, 0, 1, 0], np.uint64)\n\n    assert eq(xu64 % 3, [0, 1, 2, 0, 1], np.uint64)\n    assert eq(np.fmod(xu64, 3), [0, 1, 2, 0, 1], np.uint64)\n    assert eq(np.remainder(xu64, 3), [0, 1, 2, 0, 1], np.uint64)\n\n    assert eq(xu32 % 1, [0, 0, 0, 0, 0], np.uint32)\n    assert eq(np.fmod(xu32, 1), [0, 0, 0, 0, 0], np.uint32)\n    assert eq(np.remainder(xu32, 1), [0, 0, 0, 0, 0], np.uint32)\n\n    assert eq(xu32 % 2, [0, 1, 0, 1, 0], np.uint32)\n    assert eq(np.fmod(xu32, 2), [0, 1, 0, 1, 0], np.uint32)\n    assert eq(np.remainder(xu32, 2), [0, 1, 0, 1, 0], np.uint32)\n\n    assert eq(xu32 % 3, [0, 1, 2, 0, 1], np.uint32)\n    assert eq(np.fmod(xu32, 3), [0, 1, 2, 0, 1], np.uint32)\n    assert eq(np.remainder(xu32, 3), [0, 1, 2, 0, 1], np.uint32)\n\n    assert eq(xu16 % 1, [0, 0, 0, 0, 0], np.uint16)\n    assert eq(np.fmod(xu16, 1), [0, 0, 0, 0, 0], np.uint16)\n    assert eq(np.remainder(xu16, 1), [0, 0, 0, 0, 0], np.uint16)\n\n    assert eq(xu16 % 2, [0, 1, 0, 1, 0], np.uint16)\n    assert eq(np.fmod(xu16, 2), [0, 1, 0, 1, 0], np.uint16)\n    assert eq(np.remainder(xu16, 2), [0, 1, 0, 1, 0], np.uint16)\n\n    assert eq(xu16 % 3, [0, 1, 2, 0, 1], np.uint16)\n    assert eq(np.fmod(xu16, 3), [0, 1, 2, 0, 1], np.uint16)\n    assert eq(np.remainder(xu16, 3), [0, 1, 2, 0, 1], np.uint16)\n\n    assert eq(xu8 % 1, [0, 0, 0, 0, 0], np.uint8)\n    assert eq(np.fmod(xu8, 1), [0, 0, 0, 0, 0], np.uint8)\n    assert eq(np.remainder(xu8, 1), [0, 0, 0, 0, 0], np.uint8)\n\n    assert eq(xu8 % 2, [0, 1, 0, 1, 0], np.uint8)\n    assert eq(np.fmod(xu8, 2), [0, 1, 0, 1, 0], np.uint8)\n    assert eq(np.remainder(xu8, 2), [0, 1, 0, 1, 0], np.uint8)\n\n    assert eq(xu8 % 3, [0, 1, 2, 0, 1], np.uint8)\n    assert eq(np.fmod(xu8, 3), [0, 1, 2, 0, 1], np.uint8)\n    assert eq(np.remainder(xu8, 3), [0, 1, 2, 0, 1], np.uint8)\n\n\n    def eq1(got, exp, dtype: type):\n        return eq(got, np.asarray(exp, dtype) + 1, dtype)\n\n    assert eq1(1 + xf64 % 1, [0., 0., 0., 0., 0., 0., 0., 0., 0.], np.float64)\n    assert eq1(1 + np.fmod(xf64, 1), [-0., -0., -0., -0., 0., 0., 0., 0., 0.],\n              np.float64)\n    assert eq1(1 + np.remainder(xf64, 1), [0., 0., 0., 0., 0., 0., 0., 0., 0.],\n              np.float64)\n    assert eq1(1 + xf64 % -1, [-0., -0., -0., -0., -0., -0., -0., -0., -0.], np.float64)\n    assert eq1(1 + np.fmod(xf64, -1), [-0., -0., -0., -0., 0., 0., 0., 0., 0.],\n              np.float64)\n    assert eq1(1 + np.remainder(xf64, -1),\n              [-0., -0., -0., -0., -0., -0., -0., -0., -0.], np.float64)\n\n    assert eq1(1 + xf64 % 2, [0., 1., 0., 1., 0., 1., 0., 1., 0.], np.float64)\n    assert eq1(1 + np.fmod(xf64, 2), [-0., -1., -0., -1., 0., 1., 0., 1., 0.],\n              np.float64)\n    assert eq1(1 + np.remainder(xf64, 2), [0., 1., 0., 1., 0., 1., 0., 1., 0.],\n              np.float64)\n    assert eq1(1 + xf64 % -2, [-0., -1., -0., -1., -0., -1., -0., -1., -0.], np.float64)\n    assert eq1(1 + np.fmod(xf64, -2), [-0., -1., -0., -1., 0., 1., 0., 1., 0.],\n              np.float64)\n    assert eq1(1 + np.remainder(xf64, -2),\n              [-0., -1., -0., -1., -0., -1., -0., -1., -0.], np.float64)\n\n    assert eq1(1 + xf64 % 3, [2., 0., 1., 2., 0., 1., 2., 0., 1.], np.float64)\n    assert eq1(1 + np.fmod(xf64, 3), [-1., -0., -2., -1., 0., 1., 2., 0., 1.],\n              np.float64)\n    assert eq1(1 + np.remainder(xf64, 3), [2., 0., 1., 2., 0., 1., 2., 0., 1.],\n              np.float64)\n    assert eq1(1 + xf64 % -3, [-1., -0., -2., -1., -0., -2., -1., -0., -2.], np.float64)\n    assert eq1(1 + np.fmod(xf64, -3), [-1., -0., -2., -1., 0., 1., 2., 0., 1.],\n              np.float64)\n    assert eq1(1 + np.remainder(xf64, -3),\n              [-1., -0., -2., -1., -0., -2., -1., -0., -2.], np.float64)\n\n    assert eq1(1 + xf32 % 1, [0., 0., 0., 0., 0., 0., 0., 0., 0.], np.float32)\n    assert eq1(1 + np.fmod(xf32, 1), [-0., -0., -0., -0., 0., 0., 0., 0., 0.],\n              np.float32)\n    assert eq1(1 + np.remainder(xf32, 1), [0., 0., 0., 0., 0., 0., 0., 0., 0.],\n              np.float32)\n    assert eq1(1 + xf32 % -1, [-0., -0., -0., -0., -0., -0., -0., -0., -0.], np.float32)\n    assert eq1(1 + np.fmod(xf32, -1), [-0., -0., -0., -0., 0., 0., 0., 0., 0.],\n              np.float32)\n    assert eq1(1 + np.remainder(xf32, -1),\n              [-0., -0., -0., -0., -0., -0., -0., -0., -0.], np.float32)\n\n    assert eq1(1 + xf32 % 2, [0., 1., 0., 1., 0., 1., 0., 1., 0.], np.float32)\n    assert eq1(1 + np.fmod(xf32, 2), [-0., -1., -0., -1., 0., 1., 0., 1., 0.],\n              np.float32)\n    assert eq1(1 + np.remainder(xf32, 2), [0., 1., 0., 1., 0., 1., 0., 1., 0.],\n              np.float32)\n    assert eq1(1 + xf32 % -2, [-0., -1., -0., -1., -0., -1., -0., -1., -0.], np.float32)\n    assert eq1(1 + np.fmod(xf32, -2), [-0., -1., -0., -1., 0., 1., 0., 1., 0.],\n              np.float32)\n    assert eq1(1 + np.remainder(xf32, -2),\n              [-0., -1., -0., -1., -0., -1., -0., -1., -0.], np.float32)\n\n    assert eq1(1 + xf32 % 3, [2., 0., 1., 2., 0., 1., 2., 0., 1.], np.float32)\n    assert eq1(1 + np.fmod(xf32, 3), [-1., -0., -2., -1., 0., 1., 2., 0., 1.],\n              np.float32)\n    assert eq1(1 + np.remainder(xf32, 3), [2., 0., 1., 2., 0., 1., 2., 0., 1.],\n              np.float32)\n    assert eq1(1 + xf32 % -3, [-1., -0., -2., -1., -0., -2., -1., -0., -2.], np.float32)\n    assert eq1(1 + np.fmod(xf32, -3), [-1., -0., -2., -1., 0., 1., 2., 0., 1.],\n              np.float32)\n    assert eq1(1 + np.remainder(xf32, -3),\n              [-1., -0., -2., -1., -0., -2., -1., -0., -2.], np.float32)\n\n    assert eq1(1 + xf16 % 1, [0., 0., 0., 0., 0., 0., 0., 0., 0.], np.float16)\n    assert eq1(1 + np.fmod(xf16, 1), [-0., -0., -0., -0., 0., 0., 0., 0., 0.],\n              np.float16)\n    assert eq1(1 + np.remainder(xf16, 1), [0., 0., 0., 0., 0., 0., 0., 0., 0.],\n              np.float16)\n    assert eq1(1 + xf16 % -1, [-0., -0., -0., -0., -0., -0., -0., -0., -0.], np.float16)\n    assert eq1(1 + np.fmod(xf16, -1), [-0., -0., -0., -0., 0., 0., 0., 0., 0.],\n              np.float16)\n    assert eq1(1 + np.remainder(xf16, -1),\n              [-0., -0., -0., -0., -0., -0., -0., -0., -0.], np.float16)\n\n    assert eq1(1 + xf16 % 2, [0., 1., 0., 1., 0., 1., 0., 1., 0.], np.float16)\n    assert eq1(1 + np.fmod(xf16, 2), [-0., -1., -0., -1., 0., 1., 0., 1., 0.],\n              np.float16)\n    assert eq1(1 + np.remainder(xf16, 2), [0., 1., 0., 1., 0., 1., 0., 1., 0.],\n              np.float16)\n    assert eq1(1 + xf16 % -2, [-0., -1., -0., -1., -0., -1., -0., -1., -0.], np.float16)\n    assert eq1(1 + np.fmod(xf16, -2), [-0., -1., -0., -1., 0., 1., 0., 1., 0.],\n              np.float16)\n    assert eq1(1 + np.remainder(xf16, -2),\n              [-0., -1., -0., -1., -0., -1., -0., -1., -0.], np.float16)\n\n    assert eq1(1 + xf16 % 3, [2., 0., 1., 2., 0., 1., 2., 0., 1.], np.float16)\n    assert eq1(1 + np.fmod(xf16, 3), [-1., -0., -2., -1., 0., 1., 2., 0., 1.],\n              np.float16)\n    assert eq1(1 + np.remainder(xf16, 3), [2., 0., 1., 2., 0., 1., 2., 0., 1.],\n              np.float16)\n    assert eq1(1 + xf16 % -3, [-1., -0., -2., -1., -0., -2., -1., -0., -2.], np.float16)\n    assert eq1(1 + np.fmod(xf16, -3), [-1., -0., -2., -1., 0., 1., 2., 0., 1.],\n              np.float16)\n    assert eq1(1 + np.remainder(xf16, -3),\n              [-1., -0., -2., -1., -0., -2., -1., -0., -2.], np.float16)\n\n    assert eq1(1 + xi64 % 1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int64)\n    assert eq1(1 + np.fmod(xi64, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int64)\n    assert eq1(1 + np.remainder(xi64, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int64)\n    assert eq1(1 + xi64 % -1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int64)\n    assert eq1(1 + np.fmod(xi64, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int64)\n    assert eq1(1 + np.remainder(xi64, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int64)\n\n    assert eq1(1 + xi64 % 2, [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int64)\n    assert eq1(1 + np.fmod(xi64, 2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int64)\n    assert eq1(1 + np.remainder(xi64, 2), [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int64)\n    assert eq1(1 + xi64 % -2, [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int64)\n    assert eq1(1 + np.fmod(xi64, -2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int64)\n    assert eq1(1 + np.remainder(xi64, -2), [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int64)\n\n    assert eq1(1 + xi64 % 3, [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int64)\n    assert eq1(1 + np.fmod(xi64, 3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int64)\n    assert eq1(1 + np.remainder(xi64, 3), [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int64)\n    assert eq1(1 + xi64 % -3, [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int64)\n    assert eq1(1 + np.fmod(xi64, -3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int64)\n    assert eq1(1 + np.remainder(xi64, -3), [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int64)\n\n    assert eq1(1 + xi32 % 1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int32)\n    assert eq1(1 + np.fmod(xi32, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int32)\n    assert eq1(1 + np.remainder(xi32, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int32)\n    assert eq1(1 + xi32 % -1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int32)\n    assert eq1(1 + np.fmod(xi32, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int32)\n    assert eq1(1 + np.remainder(xi32, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int32)\n\n    assert eq1(1 + xi32 % 2, [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int32)\n    assert eq1(1 + np.fmod(xi32, 2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int32)\n    assert eq1(1 + np.remainder(xi32, 2), [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int32)\n    assert eq1(1 + xi32 % -2, [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int32)\n    assert eq1(1 + np.fmod(xi32, -2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int32)\n    assert eq1(1 + np.remainder(xi32, -2), [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int32)\n\n    assert eq1(1 + xi32 % 3, [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int32)\n    assert eq1(1 + np.fmod(xi32, 3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int32)\n    assert eq1(1 + np.remainder(xi32, 3), [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int32)\n    assert eq1(1 + xi32 % -3, [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int32)\n    assert eq1(1 + np.fmod(xi32, -3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int32)\n    assert eq1(1 + np.remainder(xi32, -3), [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int32)\n\n    assert eq1(1 + xi16 % 1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int16)\n    assert eq1(1 + np.fmod(xi16, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int16)\n    assert eq1(1 + np.remainder(xi16, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int16)\n    assert eq1(1 + xi16 % -1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int16)\n    assert eq1(1 + np.fmod(xi16, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int16)\n    assert eq1(1 + np.remainder(xi16, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int16)\n\n    assert eq1(1 + xi16 % 2, [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int16)\n    assert eq1(1 + np.fmod(xi16, 2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int16)\n    assert eq1(1 + np.remainder(xi16, 2), [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int16)\n    assert eq1(1 + xi16 % -2, [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int16)\n    assert eq1(1 + np.fmod(xi16, -2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int16)\n    assert eq1(1 + np.remainder(xi16, -2), [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int16)\n\n    assert eq1(1 + xi16 % 3, [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int16)\n    assert eq1(1 + np.fmod(xi16, 3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int16)\n    assert eq1(1 + np.remainder(xi16, 3), [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int16)\n    assert eq1(1 + xi16 % -3, [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int16)\n    assert eq1(1 + np.fmod(xi16, -3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int16)\n    assert eq1(1 + np.remainder(xi16, -3), [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int16)\n\n    assert eq1(1 + xi8 % 1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int8)\n    assert eq1(1 + np.fmod(xi8, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int8)\n    assert eq1(1 + np.remainder(xi8, 1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int8)\n    assert eq1(1 + xi8 % -1, [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int8)\n    assert eq1(1 + np.fmod(xi8, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int8)\n    assert eq1(1 + np.remainder(xi8, -1), [0, 0, 0, 0, 0, 0, 0, 0, 0], np.int8)\n\n    assert eq1(1 + xi8 % 2, [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int8)\n    assert eq1(1 + np.fmod(xi8, 2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int8)\n    assert eq1(1 + np.remainder(xi8, 2), [0, 1, 0, 1, 0, 1, 0, 1, 0], np.int8)\n    assert eq1(1 + xi8 % -2, [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int8)\n    assert eq1(1 + np.fmod(xi8, -2), [0, -1, 0, -1, 0, 1, 0, 1, 0], np.int8)\n    assert eq1(1 + np.remainder(xi8, -2), [0, -1, 0, -1, 0, -1, 0, -1, 0], np.int8)\n\n    assert eq1(1 + xi8 % 3, [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int8)\n    assert eq1(1 + np.fmod(xi8, 3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int8)\n    assert eq1(1 + np.remainder(xi8, 3), [2, 0, 1, 2, 0, 1, 2, 0, 1], np.int8)\n    assert eq1(1 + xi8 % -3, [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int8)\n    assert eq1(1 + np.fmod(xi8, -3), [-1, 0, -2, -1, 0, 1, 2, 0, 1], np.int8)\n    assert eq1(1 + np.remainder(xi8, -3), [-1, 0, -2, -1, 0, -2, -1, 0, -2], np.int8)\n\n    assert eq1(1 + xu64 % 1, [0, 0, 0, 0, 0], np.uint64)\n    assert eq1(1 + np.fmod(xu64, 1), [0, 0, 0, 0, 0], np.uint64)\n    assert eq1(1 + np.remainder(xu64, 1), [0, 0, 0, 0, 0], np.uint64)\n\n    assert eq1(1 + xu64 % 2, [0, 1, 0, 1, 0], np.uint64)\n    assert eq1(1 + np.fmod(xu64, 2), [0, 1, 0, 1, 0], np.uint64)\n    assert eq1(1 + np.remainder(xu64, 2), [0, 1, 0, 1, 0], np.uint64)\n\n    assert eq1(1 + xu64 % 3, [0, 1, 2, 0, 1], np.uint64)\n    assert eq1(1 + np.fmod(xu64, 3), [0, 1, 2, 0, 1], np.uint64)\n    assert eq1(1 + np.remainder(xu64, 3), [0, 1, 2, 0, 1], np.uint64)\n\n    assert eq1(1 + xu32 % 1, [0, 0, 0, 0, 0], np.uint32)\n    assert eq1(1 + np.fmod(xu32, 1), [0, 0, 0, 0, 0], np.uint32)\n    assert eq1(1 + np.remainder(xu32, 1), [0, 0, 0, 0, 0], np.uint32)\n\n    assert eq1(1 + xu32 % 2, [0, 1, 0, 1, 0], np.uint32)\n    assert eq1(1 + np.fmod(xu32, 2), [0, 1, 0, 1, 0], np.uint32)\n    assert eq1(1 + np.remainder(xu32, 2), [0, 1, 0, 1, 0], np.uint32)\n\n    assert eq1(1 + xu32 % 3, [0, 1, 2, 0, 1], np.uint32)\n    assert eq1(1 + np.fmod(xu32, 3), [0, 1, 2, 0, 1], np.uint32)\n    assert eq1(1 + np.remainder(xu32, 3), [0, 1, 2, 0, 1], np.uint32)\n\n    assert eq1(1 + xu16 % 1, [0, 0, 0, 0, 0], np.uint16)\n    assert eq1(1 + np.fmod(xu16, 1), [0, 0, 0, 0, 0], np.uint16)\n    assert eq1(1 + np.remainder(xu16, 1), [0, 0, 0, 0, 0], np.uint16)\n\n    assert eq1(1 + xu16 % 2, [0, 1, 0, 1, 0], np.uint16)\n    assert eq1(1 + np.fmod(xu16, 2), [0, 1, 0, 1, 0], np.uint16)\n    assert eq1(1 + np.remainder(xu16, 2), [0, 1, 0, 1, 0], np.uint16)\n\n    assert eq1(1 + xu16 % 3, [0, 1, 2, 0, 1], np.uint16)\n    assert eq1(1 + np.fmod(xu16, 3), [0, 1, 2, 0, 1], np.uint16)\n    assert eq1(1 + np.remainder(xu16, 3), [0, 1, 2, 0, 1], np.uint16)\n\n    assert eq1(1 + xu8 % 1, [0, 0, 0, 0, 0], np.uint8)\n    assert eq1(1 + np.fmod(xu8, 1), [0, 0, 0, 0, 0], np.uint8)\n    assert eq1(1 + np.remainder(xu8, 1), [0, 0, 0, 0, 0], np.uint8)\n\n    assert eq1(1 + xu8 % 2, [0, 1, 0, 1, 0], np.uint8)\n    assert eq1(1 + np.fmod(xu8, 2), [0, 1, 0, 1, 0], np.uint8)\n    assert eq1(1 + np.remainder(xu8, 2), [0, 1, 0, 1, 0], np.uint8)\n\n    assert eq1(1 + xu8 % 3, [0, 1, 2, 0, 1], np.uint8)\n    assert eq1(1 + np.fmod(xu8, 3), [0, 1, 2, 0, 1], np.uint8)\n    assert eq1(1 + np.remainder(xu8, 3), [0, 1, 2, 0, 1], np.uint8)\n\n@test\ndef test_broadcasting():\n    a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n    b = np.array([10, 20, 30])\n    c = b.reshape((1, -1))\n    d = c.T\n    e = a.T\n    f = np.array(100)\n\n    assert eq(a * a + b * b, [[101, 404, 909], [116, 425, 936], [149, 464, 981]], int)\n    assert eq(b / a + 1, [[11., 11., 11.], [3.5, 5., 6.], [2.42857143, 3.5, 4.3333333333]], float)\n    assert eq((a + b) - (b**2 + b + 1) - (b**3 + b + 1), [[ -1111,  -8420, -27929],\n                                                          [ -1108,  -8417, -27926],\n                                                          [ -1105,  -8414, -27923]], int)\n    assert eq(a * a + c * c, [[101, 404, 909], [116, 425, 936], [149, 464, 981]], int)\n    assert eq(c / a + 1, [[11., 11., 11.], [3.5, 5., 6.], [2.42857143, 3.5, 4.3333333333]], float)\n    assert eq((a + c) - (c**2 + c + 1) - (b**3 + b + 1), [[ -1111,  -8420, -27929],\n                                                          [ -1108,  -8417, -27926],\n                                                          [ -1105,  -8414, -27923]], int)\n    assert eq((c + d) ^ (e - a), [[ 20,  28,  44], [-32,  40,  48], [-44, -52,  60]], int)\n    assert eq(f + c + d + a, [[121, 132, 143], [134, 145, 156], [147, 158, 169]], int)\n\n@test\ndef test_forwarding():\n    a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n\n    # Basic\n    L.clear()\n    expr1 = (f(a, 'a1') + 1) * (f(a, 'a2') + 2)\n    expr2 = (f(1, '1') + expr1) ** 2\n    assert eq(expr2, [[   49,   169,   441],\n                      [  961,  1849,  3249],\n                      [ 5329,  8281, 12321]], int)\n    # If expr1 was forwarded into expr2, then the '1' should come first\n    assert L == ['1', 'a1', 'a2']\n\n    # Multiple\n    L.clear()\n    expr1 = (f(a, 'a1') + 1) * (f(a, 'a2') + 2)\n    expr2 = (f(a, 'a3') + 3) * (f(a, 'a4') + 4)\n    expr3 = (expr2 * f(2, '1') + expr1) ** 2\n    assert eq(expr3, [[  2116,   5184,  10816],\n                      [ 20164,  34596,  55696],\n                      [ 85264, 125316, 178084]], int)\n    # '1' comes first because forwarded leaves are appended to original leaves\n    assert L == ['1', 'a3', 'a4', 'a1', 'a2']\n\n    # Chain\n    L.clear()\n    expr1 = (f(a, 'a1') + 1) * (f(a, 'a2') + 2)\n    expr2 = (expr1 + 3) * (f(a, 'a4') + 4)\n    expr3 = (expr2 * f(2, '1')) ** 2\n    assert eq(expr3, [[   8100,   32400,  103684],\n                      [ 278784,  656100, 1392400],\n                      [2722500, 4981824, 8631844]], int)\n    # Similar comment to above\n    assert L == ['1', 'a4', 'a1', 'a2']\n\n    # Double Chain\n    L.clear()\n    expr1 = (f(a, 'a1') + 1) ^ (f(a, 'a2') + 2)\n    expr2 = (expr1 + 3) ^ (f(a, 'a3') + 4)\n\n    expr3 = (f(a, 'a4') + 3) ^ (f(a, 'a5') + 4)\n    expr4 = (expr3 + 5) ^ (f(a, 'a6') + 6)\n\n    expr5 = (expr2 * f(2, '1') + expr4) ** 2\n    assert eq(expr5, [[   9,  576,  441],\n                      [3364, 1521, 2704],\n                      [1681,  484,  729]], int)\n    assert L == ['1', 'a3', 'a1', 'a2', 'a6', 'a4', 'a5']\n\n    # No forwarding\n    L.clear()\n    expr1 = (f(a, 'a1') + 1) * (f(a, 'a2') + 2)\n    L.append('break')\n    expr2 = (f(1, '1') + expr1) ** 2\n    assert eq(expr2, [[   49,   169,   441],\n                      [  961,  1849,  3249],\n                      [ 5329,  8281, 12321]], int)\n    # expr1 is not forwarded, so order is as you'd expect\n    assert L == ['a1', 'a2', 'break', '1']\n\ntest_basic()\ntest_order()\ntest_ops_int64()\ntest_ops_int32()\ntest_ops_int16()\ntest_ops_int8()\ntest_ops_uint64()\ntest_ops_uint32()\ntest_ops_uint16()\ntest_ops_uint8()\ntest_ops_float64()\ntest_ops_float32()\ntest_ops_float16()\ntest_ops_complex128()\ntest_ops_complex64()\ntest_mixed_types()\ntest_div_mod()\ntest_broadcasting()\ntest_forwarding()\n"
  },
  {
    "path": "test/numpy/test_indexing.codon",
    "content": "import numpy as np\nfrom numpy import *\n\n@test\ndef test_basic_indexing():\n    #Single element indexing\n    arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n    assert (arr[[0, 1], [1, 2]] == np.array([2, 6])).all()\n    arr[[0, 2], [1, 2]] = [10, 11]\n    assert arr[0, 1] == 10 and arr[2, 2] == 11\n    assert arr[-1, -1] == 11\n    assert (arr[0] == np.array([1, 10, 3])).all()\n\n    #Slicing and striding\n    x = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])\n    assert (x[1:7:2] == np.array([1, 3, 5])).all()\n    assert (x[-2:10] == np.array([8, 9])).all()\n    assert (x[-3:3:-1] == np.array([7, 6, 5, 4])).all()\n    assert (x[5:] == np.array([5, 6, 7, 8, 9])).all()\n    mx = np.array([[[1], [2], [3]], [[4], [5], [6]]])\n    assert (mx[1:2] == np.array([[[4], [5], [6]]])).all()\n    assert (arr[1:10:5, ::-1] == np.array([[6, 5, 4]])).all()\n    selected_rows = arr[1:3]\n    selected_columns = arr[:, 1]\n    assert (selected_rows == np.array([[4, 5, 6], [7, 8, 11]])).all()\n    assert (selected_columns == np.array([10, 5, 8])).all()\n\n    #Dimensional indexing tools\n    assert (mx[..., 0] == np.array([[1, 2, 3], [4, 5, 6]])).all()\n    assert (mx[:, np.newaxis, :, :].shape == (2, 1, 3, 1))\n    assert (mx[:, None, :, :].shape == (2, 1, 3, 1))\n    dx = np.arange(5)\n    assert (dx[:, np.newaxis] + dx[np.newaxis, :] == np.array([[0, 1, 2, 3, 4],\n                                                               [1, 2, 3, 4, 5],\n                                                               [2, 3, 4, 5, 6],\n                                                               [3, 4, 5, 6, 7],\n                                                               [4, 5, 6, 7,\n                                                                8]])).all()\n\n    arr = np.array(42)\n    assert (arr[np.array(True)] == [42]).all()\n    assert arr[np.array(False)].size == 0\n    arr[np.array(False)] = -1\n    assert arr.item() == 42\n    arr[np.array(True)] = -1\n    assert arr.item() == -1\n\ntest_basic_indexing()\n\n@test\ndef test_advanced_indexing():\n    #Integer array indexing\n    x = np.arange(10, 1, -1)\n    assert (x[np.array([3, 3, 1, 8])] == np.array([7, 7, 9, 2])).all()\n    assert (x[np.array([3, 3, -3, 8])] == np.array([7, 7, 4, 2])).all()\n    ax = np.array([[1, 2], [3, 4], [5, 6]])\n    assert (ax[np.array([1, -1])] == np.array([[3, 4], [5, 6]])).all()\n    try:\n        ax[np.array([3, 4])]\n    except IndexError as e:\n        assert True\n    y = np.arange(35).reshape(5, 7)\n    assert (y[np.array([0, 2, 4]),\n              np.array([0, 1, 2])] == np.array([0, 15, 30])).all()\n    try:\n        y[np.array([0, 2, 4]), np.array([0, 1])]\n    except IndexError as e:\n        assert True\n    assert (y[np.array([0, 2, 4]), 1] == np.array([1, 15, 29])).all()\n    assert (y[np.array([0, 2, 4])] == np.array([[0, 1, 2, 3, 4, 5, 6],\n                                                [14, 15, 16, 17, 18, 19, 20],\n                                                [28, 29, 30, 31, 32, 33,\n                                                 34]])).all()\n    arr = np.arange(12).reshape(4, 3)\n    rows = np.array([0, 3])\n    columns = np.array([0, 2])\n    assert (rows[:, np.newaxis] == np.array([[0], [3]])).all()\n    assert (arr[rows[:, np.newaxis], columns] == np.array([[0, 2],\n                                                           [9, 11]])).all()\n\n    #Boolean array indexing\n    x = np.array([[1., 2.], [np.nan, 3.], [np.nan, np.nan]])\n    assert (x[~np.isnan(x)] == np.array([1., 2., 3.])).all()\n    x = np.array([1., -1., -2., 3])\n    x[x < 0] += 20\n    assert (x == np.array([1., 19., 18., 3.])).all()\n    arr = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])\n    assert (arr[arr > 5] == np.array([6, 7, 8, 9])).all()\n    mask = arr > 4\n    assert (arr[mask] == np.array([5, 6, 7, 8, 9])).all()\n    arr2 = np.arange(12).reshape(4, 3)\n    rows = (arr2.sum(-1) % 2) == 0\n    assert (rows == np.array([False, True, False, True])).all()\n    a = np.arange(30).reshape(2, 3, 5)\n    b = np.array([[True, True, False], [False, True, True]])\n    assert (a[b] == np.array([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9],\n                              [20, 21, 22, 23, 24], [25, 26, 27, 28,\n                                                     29]])).all()\n\ntest_advanced_indexing()\n\n@test\ndef test_combined_indexing():\n    x = np.arange(35).reshape(5, 7)\n    b = x > 20\n    assert (x[b[:, 5], 1:3] == np.array([[22, 23], [29, 30]])).all()\n    assert (np.array([42, 32])[()] == np.array([42, 32])).all()\n    assert np.array(42)[()] == 42\n\ntest_combined_indexing()\n"
  },
  {
    "path": "test/numpy/test_io.codon",
    "content": "import numpy as np\nimport gzip\nimport bz2\n\ntest_dir = \"test/numpy/data\"\n\n# TestSaveLoad\n\n@test\ndef test_array():\n    a = np.array(np.empty((1, )), dtype=float)\n    f = test_dir + \"/bin_0.npy\"\n    np.save(f, a)\n    l = np.load(f, dtype=float)\n    assert np.array_equal(a, l)\n\ntest_array()\n\n@test\ndef test_save_load_int_1D():\n    a = np.array([1, 2, 3, 4], dtype=int)\n    f = test_dir + \"/bin_1.npy\"\n    np.save(f, a)\n    l = np.load(f, dtype=int)\n    assert np.array_equal(a, l)\n\ntest_save_load_int_1D()\n\n@test\ndef test_save_load_float_1D():\n    a = np.array([1.0, 2.0, 3.0, 4.0], dtype=float)\n    f = test_dir + \"/bin_2.npy\"\n    np.save(f, a)\n    l = np.load(f, dtype=float)\n    assert np.array_equal(a, l)\n\ntest_save_load_float_1D()\n\n@test\ndef test_save_load_complex_1D():\n    a = np.array([1 + 2j, 2 + 7j, 3 - 6j, 4 + 12j], dtype=complex)\n    f = test_dir + \"/bin_3.npy\"\n    np.save(f, a)\n    l = np.load(f, dtype=complex)\n    assert np.array_equal(a, l)\n\ntest_save_load_complex_1D()\n\n@test\ndef test_save_load_int_2D():\n    a = np.array([[1, 2], [3, 4]], dtype=int)\n    f = test_dir + \"/bin_4.npy\"\n    np.save(f, a)\n    l = np.load(f, dtype=int, ndim=2)\n    assert np.array_equal(a, l)\n\ntest_save_load_int_2D()\n\n@test\ndef test_save_load_float_2D():\n    a = np.array([[1.0, 2.0], [3.0, 4.0]], dtype=float)\n    f = test_dir + \"/bin_5.npy\"\n    np.save(f, a)\n    l = np.load(f, dtype=float, ndim=2)\n    assert np.array_equal(a, l)\n\ntest_save_load_float_2D()\n\n@test\ndef test_save_load_complex_2D():\n    a = np.array([[1 + 2j, 2 + 7j], [3 - 6j, 4 + 12j]], complex)\n    f = test_dir + \"/bin_6.npy\"\n    np.save(f, a)\n    l = np.load(f, dtype=complex, ndim=2)\n    assert np.array_equal(a, l)\n\ntest_save_load_complex_2D()\n\n@test\ndef test_save_load_int_3D():\n    a = np.array([[[1, 2, 3], [4, 5, 6]], [[7, 8, 9], [10, 11, 12]]],\n                 dtype=int)\n    f = test_dir + \"/bin_7.npy\"\n    np.save(f, a)\n    l = np.load(f, dtype=int, ndim=3)\n    assert np.array_equal(a, l)\n\ntest_save_load_int_3D()\n\n@test\ndef test_save_load_float_3D():\n    a = np.array([[[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]],\n                  [[7.0, 8.0, 9.0], [10.0, 11.0, 12.0]]],\n                 dtype=float)\n    f = test_dir + \"/bin_8.npy\"\n    np.save(f, a)\n    l = np.load(f, dtype=float, ndim=3)\n    assert np.array_equal(a, l)\n\ntest_save_load_float_3D()\n\n@test\ndef test_save_load_complex_3D():\n    a = np.array([[[1 + 2j, 2 + 7j, 3 + 4j], [4 - 5j, 5 + 6j, 6 - 7j]],\n                  [[7 + 8j, 8 - 9j, 9 + 10j], [10 - 11j, 11 + 12j, 12 - 13j]]],\n                 dtype=complex)\n    f = test_dir + \"/bin_9.npy\"\n    np.save(f, a)\n    l = np.load(f, dtype=complex, ndim=3)\n    assert np.array_equal(a, l)\n\ntest_save_load_complex_3D()\n\n# savetxt / loadtxt / genfromtxt\n\n# def test_0D_3D(): TODO\n\n@test\ndef test_txt_int_1D():\n    a = np.array([1, 2, 3, 4], int)\n    f = test_dir + \"/text_1.txt\"\n    np.savetxt(f, a)\n    l = np.loadtxt(f, dtype=int)\n    g = np.genfromtxt(f, dtype=int)\n    assert np.array_equal(l.reshape(-1), a)\n    assert np.array_equal(g.reshape(-1), a)\n\ntest_txt_int_1D()\n\n@test\ndef test_txt_float_1D():\n    a = np.array([1, 2, 3, 4], float)\n    f = test_dir + \"/text_2.txt\"\n    np.savetxt(f, a)\n    l = np.loadtxt(f, dtype=float)\n    g = np.genfromtxt(f, dtype=float)\n    assert np.array_equal(l.reshape(-1), a)\n    assert np.array_equal(g.reshape(-1), a)\n\ntest_txt_float_1D()\n\n@test\ndef test_txt_int_2D():\n    a = np.array([[1, 2], [3, 4]], dtype=int)\n    f = test_dir + \"/text_3.txt\"\n    np.savetxt(f, a)\n    l = np.loadtxt(f, dtype=int)\n    g = np.genfromtxt(f, dtype=int)\n    assert np.array_equal(l, a)\n    assert np.array_equal(g, a)\n\ntest_txt_int_2D()\n\n@test\ndef test_txt_float_2D():\n    a = np.array([[1, 2], [3, 4]], dtype=float)\n    f = test_dir + \"/text_4.txt\"\n    np.savetxt(f, a)\n    l = np.loadtxt(f, dtype=float)\n    g = np.genfromtxt(f, dtype=float)\n    assert np.array_equal(l, a)\n    assert np.array_equal(g, a)\n\ntest_txt_float_2D()\n\n# def test_structured(): TODO\n# def test_structured_padded(): TODO\n# def test_multifield_view(): TODO\n\n@test\ndef test_txt_delimiter_1D():\n    a = np.array([1., 2., 3., 4.])\n    f = test_dir + \"/text_8.txt\"\n    np.savetxt(f, a, delimiter=',')\n    l = np.loadtxt(f, delimiter=',')\n    g = np.genfromtxt(f, delimiter=',')\n    assert np.array_equal(l.reshape(-1), a)\n    assert np.array_equal(g.reshape(-1), a)\n\ntest_txt_delimiter_1D()\n\n@test\ndef test_txt_delimiter_2D():\n    a = np.array([[1., 2.], [3., 4.]])\n    f = test_dir + \"/text_9.txt\"\n    np.savetxt(f, a, delimiter=',')\n    l = np.loadtxt(f, delimiter=',')\n    g = np.genfromtxt(f, delimiter=',')\n    assert np.array_equal(l, a)\n    assert np.array_equal(g, a)\n\ntest_txt_delimiter_2D()\n\n# def test_format(): Not yet implemented\n\n@test\ndef test_header_footer():\n    # Test the functionality of the header and footer keyword argument.\n    f = test_dir + \"/text_11.txt\"\n    a = np.array([(1, 2), (3, 4)], dtype=int)\n    test_header_footer = 'Test header / footer'\n    np.savetxt(f, a, header=test_header_footer)\n    with open(f, 'r') as fh:\n        content = fh.read()\n        assert content.startswith('# ' + test_header_footer)\n\n    # Test the footer keyword argument\n    f = test_dir + \"/text_12.txt\"\n    np.savetxt(f, a, footer=test_header_footer)\n    with open(f, 'r') as fh:\n        content = fh.read()\n        assert content.endswith('# ' + test_header_footer + '\\n')\n\n    # Test the commentstr keyword argument used on the header\n    f = test_dir + \"/text_13.txt\"\n    commentstr = '% '\n    np.savetxt(f, a, header=test_header_footer, comments=commentstr)\n    with open(f, 'r') as fh:\n        content = fh.read()\n        assert content.startswith(commentstr + test_header_footer)\n\n    # Test the commentstr keyword argument used on the footer\n    f = test_dir + \"/text_14.txt\"\n    commentstr = '% '\n    np.savetxt(f, a, footer=test_header_footer, comments=commentstr)\n    with open(f, 'r') as fh:\n        content = fh.read()\n        assert content.endswith(commentstr + test_header_footer + '\\n')\n\ntest_header_footer()\n\n@test\ndef test_file_roundtrip():\n    a = np.array([(1, 2), (3, 4)])\n    f = test_dir + \"/text_15.txt\"\n    np.savetxt(f, a)\n    b = np.loadtxt(f)\n    assert np.array_equal(a, b)\n\ntest_file_roundtrip()\n\n@test\ndef test_complex_arrays():\n    ncols = 2\n    nrows = 2\n    a = np.zeros((ncols, nrows), dtype=np.complex128)\n    re = np.pi\n    im = np.e\n    a[:] = re + 1.0j * im\n\n    f = test_dir + \"/text_16.txt\"\n    np.savetxt(f, a)\n    l = np.loadtxt(f, dtype=np.complex128)\n    assert l.all() == a.all()\n\ntest_complex_arrays()\n\n@test\ndef test_complex_negative_exponent():\n    ncols = 2\n    nrows = 2\n    a = np.zeros((ncols, nrows), dtype=np.complex128)\n    re = np.pi\n    im = np.e\n    a[:] = re - 1.0j * im\n    f = test_dir + \"/text_17.txt\"\n    np.savetxt(f, a)\n    l = np.loadtxt(f, dtype=np.complex128)\n    assert l.all() == a.all()\n\ntest_complex_negative_exponent()\n\n# TestLoadtxt\n\n@test\ndef test_loadtxt_array():\n    data = test_dir + \"/data_1.txt\"\n    TextIO = '1 2\\n3 4'\n    with open(data, \"w\") as fh:\n        fh.write(TextIO)\n    x = np.loadtxt(data, dtype=int)\n    a = np.array([[1, 2], [3, 4]], int)\n    assert np.array_equal(x, a)\n\n    x = np.loadtxt(data, dtype=float)\n    a = np.array([[1, 2], [3, 4]], float)\n    assert np.array_equal(x, a)\n\ntest_loadtxt_array()\n\n@test\ndef test_loadtxt_1D():\n    data = test_dir + \"/data_2.txt\"\n    TextIO = '1\\n2\\n3\\n4\\n'\n    with open(data, \"w\") as fh:\n        fh.write(TextIO)\n    x = np.loadtxt(data, dtype=int)\n    a = np.array([1, 2, 3, 4], int)\n    assert np.array_equal(x.reshape(-1), a)\n\n    data = test_dir + \"/data_3.txt\"\n    TextIO = '1,2,3,4\\n'\n    with open(data, \"w\") as fh:\n        fh.write(TextIO)\n    x = np.loadtxt(data, dtype=int, delimiter=',')\n    a = np.array([1, 2, 3, 4], int)\n    assert np.array_equal(x.reshape(-1), a)\n\ntest_loadtxt_1D()\n\n@test\ndef test_loadtxt_missing():\n    data = test_dir + \"/data_4.txt\"\n    TextIO = '1,2,3,,5\\n'\n    with open(data, \"w\") as fh:\n        fh.write(TextIO)\n    x = np.loadtxt(data,\n                   dtype=int,\n                   delimiter=',',\n                   converters={3: lambda s: int(s) if s else -999})\n    a = np.array([1, 2, 3, -999, 5], int)\n    assert np.array_equal(x.reshape(-1), a)\n\ntest_loadtxt_missing()\n\n@test\ndef test_loadtxt_converters_with_usecols():\n    data = test_dir + \"/data_5.txt\"\n    TextIO = '1,2,3,,5\\n6,7,8,9,10\\n'\n    with open(data, \"w\") as fh:\n        fh.write(TextIO)\n    x = np.loadtxt(data,\n                   dtype=int,\n                   delimiter=',',\n                   converters={3: lambda s: int(s) if s else -999},\n                   usecols=(\n                       1,\n                       3,\n                   ))\n    a = np.array([[2, -999], [7, 9]], int)\n    assert np.array_equal(x, a)\n\ntest_loadtxt_converters_with_usecols()\n\n@test\ndef test_loadtxt_comments_unicode():\n    data = test_dir + \"/data_6.txt\"\n    TextIO = '# comment\\n1,2,3,5\\n'\n    with open(data, \"w\") as fh:\n        fh.write(TextIO)\n    x = np.loadtxt(data, dtype=int, delimiter=',', comments='#')\n    a = np.array([1, 2, 3, 5], int)\n    assert np.array_equal(x.reshape(-1), a)\n\ntest_loadtxt_comments_unicode()\n\n@test\ndef test_loadtxt_comments_byte():\n    data = test_dir + \"/data_7.txt\"\n    TextIO = '# comment\\n1,2,3,5\\n'\n    with open(data, \"w\") as fh:\n        fh.write(TextIO)\n    x = np.loadtxt(data, dtype=int, delimiter=',', comments=b'#')\n    a = np.array([1, 2, 3, 5], int)\n    assert np.array_equal(x.reshape(-1), a)\n\ntest_loadtxt_comments_byte()\n\n@test\ndef test_loadtxt_comments_multi_chars():\n    data = test_dir + \"/data_9.txt\"\n    TextIO = '/* comment\\n1,2,3,5\\n'\n    with open(data, \"w\") as fh:\n        fh.write(TextIO)\n    x = np.loadtxt(data, dtype=int, delimiter=',', comments='/*')\n    a = np.array([1, 2, 3, 5], int)\n    assert np.array_equal(x.reshape(-1), a)\n\ntest_loadtxt_comments_multi_chars()\n\n@test\ndef test_loadtxt_skiprows():\n    data = test_dir + \"/data_10.txt\"\n    TextIO = 'comment\\n1,2,3,5\\n'\n    with open(data, \"w\") as fh:\n        fh.write(TextIO)\n    x = np.loadtxt(data, dtype=int, delimiter=',', skiprows=1)\n    a = np.array([1, 2, 3, 5], int)\n    assert np.array_equal(x.reshape(-1), a)\n\n    data = test_dir + \"/data_11.txt\"\n    TextIO = '# comment\\n1,2,3,5\\n'\n    with open(data, \"w\") as fh:\n        fh.write(TextIO)\n    x = np.loadtxt(data, dtype=int, delimiter=',', skiprows=1)\n    a = np.array([1, 2, 3, 5], int)\n    assert np.array_equal(x.reshape(-1), a)\n\ntest_loadtxt_skiprows()\n\n@test\ndef test_loadtxt_usecols():\n    a = np.array([[1, 2], [3, 4]], float)\n    data = test_dir + \"/data_12.txt\"\n    np.savetxt(data, a)\n    x = np.loadtxt(data, dtype=float, usecols=(1, ))\n    assert np.array_equal(x, a[:, 1])\n\n    a = np.array([[1, 2, 3], [3, 4, 5]], float)\n    data = test_dir + \"/data_13.txt\"\n    np.savetxt(data, a)\n    x = np.loadtxt(data, dtype=float, usecols=(1, 2))\n    assert np.array_equal(x, a[:, 1:])\n\ntest_loadtxt_usecols()\n\n# def test_bad_usecols(): TODO\n# def test_fancy_dtype(self): TODO\n# def test_shaped_dtype(self): TODO\n# def test_3d_shaped_dtype(self): TODO\n# def test_str_dtype(): TODO\n# def test_empty_file(): TODO\n# def test_unused_converter(self): TODO\n# def test_dtype_with_object(self): Not yet implemented\n# def test_uint64_type(self): TODO\n# def test_int64_type(self): TODO\n# def test_from_float_hex(self): TODO\n# def test_default_float_converter_no_default_hex_conversion(self): TODO\n# def test_default_float_converter_exception(self): TODO\n# def test_from_complex(self): TODO\n# def test_complex_misformatted(self): TODO\n# def test_universal_newline(self): TODO\n# def test_empty_field_after_tab(self): TODO\n# def test_unpack_structured(self): TODO\n# def test_ndmin_keyword(self): TODO\n# def test_generator_source(self): TODO\n# def test_bad_line(self): TODO\n# def test_none_as_string(self): TODO\n# def test_binary_load(self): TODO\n\n@test\ndef test_loadtxt_max_rows():\n    data = test_dir + \"/data_36.txt\"\n    TextIO = '1,2,3,5\\n4,5,7,8\\n2,1,4,5'\n    with open(data, \"w\") as fh:\n        fh.write(TextIO)\n    x = np.loadtxt(data, dtype=int, delimiter=',', max_rows=1)\n    a = np.array([1, 2, 3, 5], int)\n    assert np.array_equal(x.reshape(-1), a)\n\ntest_loadtxt_max_rows()\n\n@test\ndef test_loadtxt_max_rows_with_skiprows():\n    data = test_dir + \"/data_37.txt\"\n    TextIO = 'comments\\n1,2,3,5\\n4,5,7,8\\n2,1,4,5'\n    with open(data, \"w\") as fh:\n        fh.write(TextIO)\n    x = np.loadtxt(data, dtype=int, delimiter=',', skiprows=1, max_rows=1)\n    a = np.array([1, 2, 3, 5], int)\n    assert np.array_equal(x.reshape(-1), a)\n\n    data = test_dir + \"/data_38.txt\"\n    TextIO = 'comment\\n1,2,3,5\\n4,5,7,8\\n2,1,4,5'\n    with open(data, \"w\") as fh:\n        fh.write(TextIO)\n    x = np.loadtxt(data, dtype=int, delimiter=',', skiprows=1, max_rows=2)\n    a = np.array([[1, 2, 3, 5], [4, 5, 7, 8]], int)\n    assert np.array_equal(x, a)\n\ntest_loadtxt_max_rows_with_skiprows()\n\n# def test_max_rows_with_read_continuation(self): TODO\n\n@test\ndef test_loadtxt_max_rows_larger():\n    #test max_rows > num rows\n    data = test_dir + \"/data_40.txt\"\n    TextIO = 'comment\\n1,2,3,5\\n4,5,7,8\\n2,1,4,5'\n    with open(data, \"w\") as fh:\n        fh.write(TextIO)\n    x = np.loadtxt(data, dtype=int, delimiter=',', skiprows=1, max_rows=6)\n    a = np.array([[1, 2, 3, 5], [4, 5, 7, 8], [2, 1, 4, 5]], int)\n    assert np.array_equal(x, a)\n\ntest_loadtxt_max_rows_larger()\n\n# TestGenFromTxt, TestSaveTxt, TestLoadTxt\n\n@test\ndef test_gft_array():\n    # Test outputting a standard ndarray\n    filename = test_dir + '/data_41.txt'\n    control = np.array([[1, 2], [3, 4]], dtype=int)\n    np.savetxt(filename, control)\n    test = np.genfromtxt(filename, dtype=int, delimiter=' ')\n    assert np.array_equal(test, control)\n\n    control = np.array([[1, 2], [3, 4]], dtype=float)\n    np.savetxt(filename, control)\n    test = np.loadtxt(filename, dtype=float, delimiter=' ')\n    assert np.array_equal(test, control)\n\ntest_gft_array()\n\n@test\ndef test_gft_1D():\n    # Test squeezing to 1D\n    filename = test_dir + '/data_42.txt'\n    control = np.array([1, 2, 3, 4], dtype=int)\n    np.savetxt(filename, control, delimiter='\\n')\n    test = np.genfromtxt(filename, dtype=int)\n    assert np.array_equal(test.reshape(-1), control)\n\n    np.savetxt(filename, control, delimiter=',')\n    test = np.genfromtxt(filename, dtype=int, delimiter=',')\n    assert np.array_equal(test.reshape(-1), control)\n\ntest_gft_1D()\n\n@test\ndef test_gft_comments():\n    # Test the stripping of comments\n    filename = test_dir + '/data_43.txt'\n    control = np.array([1, 2, 3, 5], dtype=int)\n    content = \"# comment\\n1,2,3,5\\n\"\n    with open(filename, \"w\") as f:\n        f.write(content)\n    test = np.genfromtxt(filename, dtype=int, delimiter=',', comments='#')\n    assert np.array_equal(test.reshape(-1), control)\n    # Comment at the end of a line\n    content = \"1,2,3,5# comment\\n\"\n    with open(filename, \"w\") as f:\n        f.write(content)\n    test = np.genfromtxt(filename, dtype=int, delimiter=',', comments='#')\n    assert np.array_equal(test.reshape(-1), control)\n\ntest_gft_comments()\n\n@test\ndef test_gft_skiprows():\n    # Test row skipping\n    filename = test_dir + '/data_44.txt'\n    control = np.array([1, 2, 3, 5], dtype=int)\n    content = 'comment\\n1,2,3,5\\n'\n    with open(filename, \"w\") as f:\n        f.write(content)\n    test = np.genfromtxt(filename, dtype=int, delimiter=',', skip_header=1)\n    assert np.array_equal(test.reshape(-1), control)\n\n    filename = test_dir + '/data_45.txt'\n    content = '# comment\\n1,2,3,5\\n'\n    with open(filename, \"w\") as f:\n        f.write(content)\n    test = np.loadtxt(filename,\n                      dtype=int,\n                      delimiter=',',\n                      skiprows=1,\n                      comments='#')\n    assert np.array_equal(test.reshape(-1), control)\n\ntest_gft_skiprows()\n\n@test\ndef test_gft_skip_footer():\n    filename = test_dir + '/data_46.txt'\n    control = np.array([1, 2, 3, 5], dtype=int)\n    content = '1,2,3,5\\n comment\\n'\n    with open(filename, \"w\") as f:\n        f.write(content)\n    test = np.genfromtxt(filename, dtype=int, delimiter=',', skip_footer=1)\n    assert np.array_equal(test.reshape(-1), control)\n\ntest_gft_skip_footer()\n\n# def test_skip_footer_with_invalid(self): TODO\n# def test_header(self): TODO\n# def test_auto_dtype(self): TODO\n\n@test\ndef test_auto_dtype_uniform():\n    # Tests whether the output dtype can be uniformized\n    data = test_dir + '/data_50.txt'\n    TextIO = '1 2 3 4\\n5 6 7 8\\n'\n    with open(data, \"w\") as f:\n        f.write(TextIO)\n    test = np.genfromtxt(data, dtype=None)\n    control = np.array([[1, 2, 3, 4], [5, 6, 7, 8]], dtype=None)\n    assert np.array_equal(test, control)\n\n# test_auto_dtype_uniform()\n\n# def test_fancy_dtype(self): TODO\n# def test_names_overwrite(self): TODO\n# def test_bad_fname(self): TODO\n# def test_commented_header(self): TODO\n# def test_names_and_comments_none(self): TODO\n# def test_file_is_closed_on_error(self): TODO\n# def test_autonames_and_usecols(self): TODO\n\n@test\ndef test_converters_with_usecols():\n    # Test the combination user-defined converters and usecol\n    data = test_dir + '/data_58.txt'\n    TextIO = '1,2,3,,5\\n6,7,8,9,10\\n'\n    with open(data, \"w\") as f:\n        f.write(TextIO)\n    test = np.genfromtxt(data,\n                         dtype=int,\n                         delimiter=',',\n                         converters={3: lambda s: int(s) if s else -999},\n                         usecols=(\n                             1,\n                             3,\n                         ))\n    control = np.array([[2, -999], [7, 9]], dtype=int)\n    assert np.array_equal(test, control)\n\ntest_converters_with_usecols()\n\n# def test_converters_with_usecols_and_names(self): TODO\n# def test_converters_cornercases(self): TODO\n# def test_converters_cornercases2(self): TODO\n\n@test\ndef test_unused_converter():\n    TextIO = \"1 21\\n3 42\\n\"\n    data = test_dir + '/data_62.txt'\n    with open(data, \"w\") as fh:\n        fh.write(TextIO)\n\n    test = np.genfromtxt(data,\n                         dtype=int,\n                         usecols=(1, ),\n                         converters={0: lambda s: int(s) if s else 16})\n    assert np.array_equal(test, [21, 42])\n\ntest_unused_converter()\n\n# def test_invalid_converter(self): TODO\n# def test_tricky_converter_bug1666(self): TODO\n# def test_dtype_with_converters(self): TODO\n# def test_dtype_with_converters_and_usecols(self): TODO\n# def test_dtype_with_object(self): TODO\n# def test_dtype_with_object_no_converter(self): TODO\n# def test_userconverters_with_explicit_dtype(self): TODO\n# def test_utf8_userconverters_with_explicit_dtype(self): TODO\n\n@test\ndef test_spacedelimiter():\n    # Test space delimiter\n    TextIO = \"1  2  3  4   5\\n6  7  8  9  10\"\n    data = test_dir + '/data_71.txt'\n    with open(data, \"w\") as file:\n        file.write(TextIO)\n    test = np.genfromtxt(data)\n    control = np.array([[1., 2., 3., 4., 5.], [6., 7., 8., 9., 10.]])\n    assert np.array_equal(test, control)\n\ntest_spacedelimiter()\n\n@test\ndef test_integer_delimiter():\n    # Test using an integer for delimiter\n    TextIO = \"  1  2  3\\n  4  5 67\\n890123  4\"\n    data = test_dir + '/data_72.txt'\n    with open(data, \"w\") as file:\n        file.write(TextIO)\n    test = np.genfromtxt(data, delimiter=3)\n    control = np.array([[1, 2, 3], [4, 5, 67], [890, 123, 4]])\n    assert np.array_equal(test, control)\n\ntest_integer_delimiter()\n\n@test\ndef test_gft_missing():\n    TextIO = '1,2,3,,5\\n'\n    data = test_dir + '/data_73.txt'\n    with open(data, \"w\") as file:\n        file.write(TextIO)\n    test = np.genfromtxt(data,\n                         dtype=int,\n                         delimiter=',',\n                         converters={3: lambda s: int(s) if s else -999})\n    control = np.array([1, 2, 3, -999, 5], int)\n    assert np.array_equal(test.reshape(-1), control)\n\ntest_gft_missing()\n\n# def test_missing_with_tabs(self): TODO\n\n@test\ndef test_gft_usecols():\n    # Test the selection of columns\n    # Select 1 column\n    control = np.array([[1, 2], [3, 4]], float)\n    data = test_dir + '/data_75.txt'\n    np.savetxt(data, control)\n    test = np.genfromtxt(data, dtype=float, usecols=(1, ))\n    assert np.array_equal(test, control[:, 1])\n    #\n    control = np.array([[1, 2, 3], [3, 4, 5]], float)\n    data = test_dir + '/data_76.txt'\n    np.savetxt(data, control)\n    test = np.genfromtxt(data, dtype=float, usecols=(1, 2))\n    assert np.array_equal(test, control[:, 1:])\n\ntest_gft_usecols()\n\n# def test_usecols_as_css(self): TODO\n# def test_usecols_with_structured_dtype(self): TODO\n\n@test\ndef test_usecols_with_integer():\n    # Test usecols with an integer\n    TextIO = b\"1 2 3\\n4 5 6\"\n    data = test_dir + '/data_79.txt'\n    with open(data, \"w\") as f:\n        f.write(TextIO)\n    test = np.genfromtxt(data, usecols=0)\n    assert np.array_equal(test, np.array([1., 4.]))\n\ntest_usecols_with_integer()\n\n# def test_usecols_with_named_columns(self): TODO\n# def test_empty_file(self): TODO\n# def test_fancy_dtype_alt(self): TODO\n# def test_shaped_dtype(self): TODO\n# def test_withmissing(self): TODO\n# def test_user_missing_values(self): TODO\n# def test_user_filling_values(self): TODO\n# def test_withmissing_float(self): TODO\n# def test_with_masked_column_uniform(self): TODO\n# def test_with_masked_column_various(self): TODO\n# def test_invalid_raise(self): TODO\n# def test_invalid_raise_with_usecols(self): TODO\n# def test_inconsistent_dtype(self): TODO\n# def test_default_field_format(self): TODO\n# def test_single_dtype_wo_names(self): TODO\n# def test_single_dtype_w_explicit_names(self): TODO\n# def test_single_dtype_w_implicit_names(self): TODO\n# def test_easy_structured_dtype(self): TODO\n# def test_autostrip(self): TODO\n# def test_replace_space(self): TODO\n# def test_replace_space_known_dtype(self): TODO\n# def test_incomplete_names(self): TODO\n# def test_names_auto_completion(self): TODO\n# def test_names_with_usecols_bug1636(self): TODO\n# def test_fixed_width_names(self): TODO\n# def test_filling_values(self): TODO\n# def test_comments_is_none(self): TODO\n# def test_latin1(self): TODO\n# def test_binary_decode_autodtype(self): TODO\n# def test_utf8_byte_encoding(self): TODO\n# def test_utf8_file(self): TODO\n# def test_utf8_file_nodtype_unicode(self): TODO\n\n# def test_names_auto_completion(): TODO\n\n# def test_names_with_usecols_bug1636(): TODO\n\n@test\ndef test_filling_values():\n    # Test missing values\n    TextIO = b\"1, 2, 3\\n1, , 5\\n0, 6, \\n\"\n    data = test_dir + '/data_113.txt'\n    with open(data, \"w\") as f:\n        f.write(TextIO)\n    ctrl = np.array([[1, 2, 3], [1, -999, 5], [0, 6, -999]], dtype=int)\n    test = np.genfromtxt(data, delimiter=\",\", dtype=int, filling_values=-999)\n    assert np.array_equal(test, ctrl)\n\ntest_filling_values()\n\n@test\ndef test_gft_max_rows():\n    # Test the `max_rows` keyword argument.\n    TextIO = '1 2\\n3 4\\n5 6\\n7 8\\n9 10\\n'\n    data = test_dir + '/data_114.txt'\n    with open(data, \"w\") as f:\n        f.write(TextIO)\n    a1 = np.genfromtxt(data, max_rows=3)\n    a2 = np.genfromtxt(data)\n    assert np.array_equal(a1, [[1, 2], [3, 4], [5, 6]])\n    assert np.array_equal(a2, [[1, 2], [3, 4], [5, 6], [7, 8], [9, 10]])\n\n    # max_rows must be at least 1.\n    try:\n        a3 = np.genfromtxt(data, max_rows=0)\n        assert False\n    except ValueError:\n        pass\n\n    # An input with several invalid rows.\n    data = test_dir + '/data_115.txt'\n    TextIO = '1 1\\n2 2\\n0 \\n3 3\\n4 4\\n5  \\n6  \\n7  \\n'\n    with open(data, \"w\") as f:\n        f.write(TextIO)\n    test = np.genfromtxt(data, max_rows=2)\n    control = np.array([[1., 1.], [2., 2.]])\n    assert np.array_equal(test, control)\n\n    # Test keywords conflict\n    try:\n        a4 = np.genfromtxt(data, skip_footer=1, max_rows=4)\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        a5 = np.genfromtxt(data, max_rows=4)\n        assert False\n    except IOError:\n        pass\n\ntest_gft_max_rows()\n\n@test\ndef test_gft_using_filename():\n    # Test that we can load data from a filename as well as a file\n    # object\n    tgt = np.arange(6).reshape((2, 3))\n    linesep = ('\\n', '\\r\\n', '\\r')\n    i = 116\n    for sep in linesep:\n        f = f\"{test_dir}/data_{i}.txt\"\n        data = '0 1 2' + sep + '3 4 5'\n        with open(f, 'w') as g:\n            g.write(data)\n        i += 1\n        res = np.genfromtxt(f)\n        assert np.array_equal(res, tgt)\n\ntest_gft_using_filename()\n\n@test\ndef test_gft_from_gzip():\n    # Test that we can load data from a gzipped file\n    wanted = np.arange(6).reshape((2, 3))\n    linesep = ('\\n', '\\r\\n', '\\r\\n')\n    i = 120\n    for sep in linesep:\n        f = f\"{test_dir}/data_{i}.gz\"\n        data = '0 1 2' + sep + '3 4 5'\n        with gzip.open(f, mode='w') as g:\n            g.write(data)\n        i += 1\n\n        l = np.genfromtxt(f, int)\n        assert np.array_equal(l, wanted)\n\ntest_gft_from_gzip()\n\n# def test_auto_dtype_largeint(self):\n\n@test\ndef test_unpack_float_data():\n    TextIO = (\"1,2,3\\n4,5,6\\n7,8,9\\n0.0,1.0,2.0\")\n    data = test_dir + '/data_124.txt'\n    with open(data, \"w\") as f:\n        f.write(TextIO)\n    a, b, c = np.loadtxt(data, delimiter=\",\", unpack=True)\n    assert np.array_equal(a, np.array([1.0, 4.0, 7.0, 0.0]))\n    assert np.array_equal(b, np.array([2.0, 5.0, 8.0, 1.0]))\n    assert np.array_equal(c, np.array([3.0, 6.0, 9.0, 2.0]))\n\ntest_unpack_float_data()\n\n# def test_unpack_structured(): TODO\n# def test_unpack_auto_dtype(): TODO\n# def test_unpack_single_name(): TODO\n# def test_squeeze_scalar(): TODO\n@test\ndef test_ndmin_keyword(ndim: Literal[int]):\n    \"\"\"Test ndmin keyword consistency between genfromtxt and loadtxt.\"\"\"\n    txt = \"42\"\n    data = test_dir + '/temp.txt'\n    with open(data, \"w\") as f:\n        f.write(txt)\n\n    a = np.loadtxt(data, ndmin=ndim)\n    b = np.genfromtxt(data, ndmin=ndim)\n    assert np.array_equal(a, b)\n\ntest_ndmin_keyword(0)\ntest_ndmin_keyword(1)\ntest_ndmin_keyword(2)\n\n## XXX\n\n# TestPathUsage\n\n@test\ndef test_loadtxt():\n    path = test_dir + '/data_125.txt'\n    a = np.array([[1.1, 2.1], [3.1, 4.1]], dtype=float)\n    np.savetxt(path, a)\n    x = np.loadtxt(path)\n    assert np.array_equal(x, a)\n\ntest_loadtxt()\n\n@test\ndef test_save_load():\n    path = test_dir + '/data_126.npy'\n    a = np.array([[1, 2], [3, 4]], int)\n    np.save(path, a)\n    data = np.load(path, dtype=int, ndim=2)\n    assert np.array_equal(data, a)\n\ntest_save_load()\n\n@test\ndef test_save_load_memmap():\n    path = test_dir + '/data_127.npy'\n    a = np.array([[1, 2], [3, 4]], int)\n    np.save(path, a)\n    data = np.load(path, dtype=int, mmap_mode='r', ndim=2)\n    assert np.array_equal(data, a)\n\ntest_save_load_memmap()\n\n@test\ndef test_save_load_memmap_readwrite():\n    path = test_dir + '/data_128.npy'\n    a = np.array([[1, 2], [3, 4]], int)\n    np.save(path, a)\n    b = np.load(path, dtype=int, ndim=2, mmap_mode='r+')\n    a[0][0] = 5\n    b[0][0] = 5\n    data = np.load(path)\n    assert np.array_equal(data, a)\n\n# def test_savez_load(): Not yet implemented\n\n# def test_savez_compressed_load(): Not yet implemented\n\n@test\ndef test_genfromtxt():\n    path = test_dir + '/data_129.txt'\n    a = np.array([(1, 2), (3, 4)])\n    np.savetxt(path, a)\n    data = np.genfromtxt(path)\n    assert np.array_equal(a, data)\n\ntest_genfromtxt()\n\ndef check(txt: str, expected, **kwargs):\n    gen = txt.splitlines()\n\n    out = np.genfromtxt(gen, **kwargs)\n    if not np.array_equal(out, expected):\n        return False\n\n    out = np.genfromtxt(gen, delimiter=None, **kwargs)\n    if not np.array_equal(out, expected):\n        return False\n\n    gen_n = [a + '\\n' for a in gen]\n\n    out = np.genfromtxt(gen_n, **kwargs)\n    if not np.array_equal(out, expected):\n        return False\n\n    out = np.genfromtxt(gen_n, delimiter=None, **kwargs)\n    if not np.array_equal(out, expected):\n        return False\n\n    gen_rn = [a + '\\r\\n' for a in gen]\n\n    out = np.genfromtxt(gen_rn, **kwargs)\n    if not np.array_equal(out, expected):\n        return False\n\n    out = np.genfromtxt(gen_rn, delimiter=None, **kwargs)\n    if not np.array_equal(out, expected):\n        return False\n\n    filename = test_dir + '/data_empty_delim.txt'\n    with open(filename, 'w') as f:\n        f.write(txt)\n\n    out = np.genfromtxt(filename, **kwargs)\n    if not np.array_equal(out, expected):\n        return False\n\n    out = np.loadtxt(filename, **kwargs)\n    if not np.array_equal(out, expected):\n        return False\n\n    out = np.genfromtxt(filename, delimiter=None, **kwargs)\n    if not np.array_equal(out, expected):\n        return False\n\n    out = np.loadtxt(filename, delimiter=None, **kwargs)\n    if not np.array_equal(out, expected):\n        return False\n\n    return True\n\n@test\ndef test_empty_delimiter():\n    # Basic\n    txt = (\n        \"\\n\"\n        \"\\n\"\n        \" 1\\t2   3\\n\"\n        \"\\t  4   5\\t6   \\n\"\n        \"\\n\"\n        \"7   8   9   \\t\\n\"\n    )\n    exp = np.array([[1,2,3],[4,5,6],[7,8,9]], float)\n    assert check(txt, exp)\n\n    txt = (\n        \"\\n\"\n        \"\\n\"\n        \" 1\\t2   3   \\n\"\n        \"\\t  4   5\\t6\\n\"\n        \"\\n\"\n        \"7   8   9   \\t\\n\"\n    )\n    exp = np.array([[1,2,3],[4,5,6],[7,8,9]], float)\n    assert check(txt, exp)\n\n    txt = (\n        \"\\n\"\n        \"  # AA\\n\"\n        \" 1\\t2   3  # X \\n\"\n        \"\\t  4   5\\t6\\n\"\n        \"#\\n\"\n        \"7   8   9   \\t\\n\"\n    )\n    exp = np.array([[1,2,3],[4,5,6],[7,8,9]], float)\n    assert check(txt, exp)\n\n    txt = (\n        \" 1\\t2   3  # X \\n\"\n        \"\\t  4   5\\t6\\n\"\n        \"#\\n\"\n        \"7   8   9   \\t\\n\"\n    )\n    exp = np.array([[1,2,3],[4,5,6],[7,8,9]], float)\n    assert check(txt, exp)\n\n    txt = (\n        \" 1\\t2   3  # X \\n\"\n        \"\\t  4   5\\t6\\n\"\n        \"#\\n\"\n        \" # # \\n\"\n        \"   \\n\"\n        \"#\\n\"\n        \"7   8   9   \\t\\n\"\n    )\n    exp = np.array([[1,2,3],[4,5,6],[7,8,9]], float)\n    assert check(txt, exp)\n\n    # Heavy leading indentation; trailing tabs/spaces on each line\n    txt = (\n        \"            1   2   3   \\t \\n\"\n        \"\\t\\t\\t4 5 6   \\t\\n\"\n        \"    7\\t8\\t9     \\n\"\n    )\n    exp = np.array([[1,2,3],[4,5,6],[7,8,9]], float)\n    assert check(txt, exp)\n\n    # Tabs-only separators and mixed tabs/spaces; stray trailing tab after last token\n    txt = (\n        \"\\t\\t10\\t  11\\t12\\t\\n\"\n        \"13\\t14  \\t15\\n\"\n        \"16   \\t17\\t18\\t\\n\"\n    )\n    exp = np.array([[10,11,12],[13,14,15],[16,17,18]], float)\n    assert check(txt, exp)\n\n    # Glued inline comments (no space), spaced comments, and comments after tab\n    txt = (\n        \"1 2 3#nospace\\n\"\n        \"4 5 6    # spaced\\n\"\n        \"7 8 9\\t#\\twithtab\\n\"\n    )\n    exp = np.array([[1,2,3],[4,5,6],[7,8,9]], float)\n    assert check(txt, exp, comments=\"#\")\n\n    # Whitespace-only lines and comment-only lines sprinkled throughout\n    txt = (\n        \"   \\t   # top\\n\"\n        \"1  2  3\\n\"\n        \"  \\t   \\n\"\n        \"4\\t5 6 # mid\\n\"\n        \"   \\n\"\n        \"7 8 9\\n\"\n        \"   # end\\n\"\n    )\n    exp = np.array([[1,2,3],[4,5,6],[7,8,9]], float)\n    assert check(txt, exp, comments=\"#\")\n\n    # Scientific notation, signs, and mixed spacing\n    txt = (\n        \" -1e0   +2E+1   3.5e-1\\n\"\n        \"\\t-4.0 5E0  -6.25e+00\\n\"\n        \"7.0e+00\\t  -8E-1   +9.00\\n\"\n    )\n    exp = np.array([[-1.0, 20.0, 0.35],\n                    [-4.0, 5.0, -6.25],\n                    [7.0, -0.8, 9.0]], float)\n    assert check(txt, exp)\n\n    # Single-column data with leading/trailing blanks\n    txt = (\n        \"\\n\"\n        \"   10\\n\"\n        \"  20   \\n\"\n        \"\\t30\\t\\n\"\n        \"   \\n\"\n    )\n    exp = np.array([[10.0], [20.0], [30.0]], float)\n    assert check(txt, exp)\n\n    # Irregular spacing, extra spaces between tokens\n    txt = (\n        \"1    2           3\\n\"\n        \"   4    5      6\\n\"\n        \"7 8 9\\n\"\n    )\n    exp = np.array([[1,2,3],[4,5,6],[7,8,9]], float)\n    assert check(txt, exp)\n\n    # Inline comments preceded by many spaces; comment-only lines with tabs\n    txt = (\n        \"1 2 3            # c1\\n\"\n        \"\\t\\t# only comment\\n\"\n        \"4   5   6    # c2\\n\"\n        \"7 8 9\\n\"\n        \"\\t  # end\\n\"\n    )\n    exp = np.array([[1,2,3],[4,5,6],[7,8,9]], float)\n    assert check(txt, exp, comments=\"#\")\n\n    # usecols on rectangular data (column reorder check)\n    txt = (\n        \"1 2 3\\n\"\n        \"4 5 6\\n\"\n        \"7 8 9\\n\"\n    )\n    exp = np.array([[3,1],[6,4],[9,7]], float)  # columns (2,0)\n    assert check(txt, exp, usecols=(2,0))\n\n    # Mixed tabs/spaces around signs and decimals\n    txt = (\n        \"  -0.0\\t+0.0  1.0\\n\"\n        \"\\t-2.5  \\t  3.75  5.0\\n\"\n        \" 4.125  -5.5  \\t6.0\\n\"\n    )\n    exp = np.array([[-0.0, 0.0, 1.0],\n                    [-2.5, 3.75, 5.0],\n                    [4.125, -5.5, 6.0]], float)\n    assert check(txt, exp)\n\n    # Trailing whitespace-only lines after data\n    txt = (\n        \"1 2 3\\n\"\n        \"4 5 6\\n\"\n        \"7 8 9\\n\"\n        \"   \\n\"\n        \"\\t   \\n\"\n        \"\\n\"\n    )\n    exp = np.array([[1,2,3],[4,5,6],[7,8,9]], float)\n    assert check(txt, exp)\n\n    # Many blank lines between data rows\n    txt = (\n        \"1 2 3\\n\"\n        \"\\n\"\n        \"\\n\"\n        \"4 5 6\\n\"\n        \"\\n\"\n        \"7 8 9\\n\"\n        \"\\n\"\n        \"\\n\"\n        \"\\n\"\n    )\n    exp = np.array([[1,2,3],[4,5,6],[7,8,9]], float)\n    assert check(txt, exp)\n\n    # Single row, many spaces and tabs (ensures 2D vs 1D behavior aligns)\n    txt = \" \\t  1   2 \\t 3   \\n\"\n    exp = np.array([[1.0, 2.0, 3.0]], float)\n    assert check(txt, exp)\n\n    # Large integers with leading pluses/minuses and mixed whitespace\n    txt = (\n        \"+100   -200   300\\n\"\n        \"   -400 +500\\t -600\\n\"\n        \"\\t700 800   -900\\n\"\n    )\n    exp = np.array([[ 100, -200,  300],\n                    [-400,  500, -600],\n                    [ 700,  800, -900]], float)\n    assert check(txt, exp)\n\n    # Inline comments glued to last token without whitespace; ensure parsing stops correctly\n    txt = (\n        \"1 2 3#c\\n\"\n        \"4 5 6#d\\n\"\n        \"7 8 9#e\\n\"\n    )\n    exp = np.array([[1,2,3],[4,5,6],[7,8,9]], float)\n    assert check(txt, exp, comments=\"#\")\n\ntest_empty_delimiter()\n\n# Testcase fails\n@test\ndef test_gzip_load():\n    values = np.array([1, 2, 3, 4, 5])\n    a = np.array(values, dtype=float)\n\n    s = test_dir + '/data_130.gz'\n    f = gzip.open(s, mode=\"w\")\n\n    np.save(f, a)\n    f.close()\n    f = gzip.open(s, mode=\"r\")\n    g = np.load(f, dtype=float, ndim=1)\n    assert np.array_equal(g, a)\n\n# test_gzip_load()\n\n# def test_gzip_loadtxt(): Not yet implemented\n# def test_gzip_loadtxt_from_string(): Not yet implemented\n# def test_npzfile_dict(): Not yet implemented\n# def test_load_refcount(): Not yet implemented\n# def test_load_multiple_arrays_until_eof()\n\n# Additional tests\n\n# Testcases related to bz2 files\n\n@test\ndef test_gft_from_bz2():\n    # Test that we can load data from a bz2 compressed file\n    wanted = np.arange(6).reshape((2, 3))\n    linesep = ('\\n', '\\r\\n', '\\r\\n')\n    i = 136\n    for sep in linesep:\n        f = f\"{test_dir}/data_{i}.bz2\"\n        data = '0 1 2' + sep + '3 4 5'\n        with bz2.open(f, mode='w') as b:\n            b.write(data)\n        i += 1\n\n        l = np.genfromtxt(f, int)\n        assert np.array_equal(l, wanted)\n\ntest_gft_from_bz2()\n\n# Testcase fails\n@test\ndef test_bz2_load():\n    values = np.array([1, 2, 3, 4, 5])\n    a = np.array(values, dtype=float)\n\n    s = test_dir + '/data_140.bz2'\n    f = bz2.open(s, mode=\"w\")\n    np.save(f, a)\n    f.close()\n    f = bz2.open(s, mode=\"r\")\n    g = np.load(f, dtype=float)\n    assert np.array_equal(g, a)\n\n# test_bz2_load()\n\n@test\ndef test_save_load_int32_1d_v1():\n    array = np.array([1, 2, 3], dtype=np.int32)\n    filename = test_dir + \"/binary_int32_1d_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.int32)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_int32_1d_v1()\n\n@test\ndef test_save_load_int64_1d_v1():\n    array = np.array([1, 2, 3], dtype=np.int64)\n    filename = test_dir + \"/binary_int64_1d_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.int64)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_int64_1d_v1()\n\n@test\ndef test_save_load_float32_1d_v1():\n    array = np.array([1.0, 2.5, 3.7], dtype=np.float32)\n    filename = test_dir + \"/binary_float32_1d_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.float32)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_float32_1d_v1()\n\n@test\ndef test_save_load_float64_1d_v1():\n    array = np.array([1.0, 2.5, 3.7], dtype=np.float64)\n    filename = test_dir + \"/binary_float64_1d_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.float64)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_float64_1d_v1()\n\n@test\ndef test_save_load_complex64_1d_v1():\n    array = np.array([1.0 + 2.0j, 3.5 - 1.2j], dtype=np.complex64)\n    filename = test_dir + \"/binary_complex64_1d_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.complex64)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_complex64_1d_v1()\n\n@test\ndef test_save_load_complex128_1d_v1():\n    array = np.array([1.0 + 2.0j, 3.5 - 1.2j], dtype=np.complex128)\n    filename = test_dir + \"/binary_complex128_1d_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.complex128)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_complex128_1d_v1()\n\n@test\ndef test_save_load_bool_1d_v1():\n    array = np.array([True, False, True], dtype=bool)\n    filename = test_dir + \"/binary_bool_1d_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=bool)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_bool_1d_v1()\n\n@test\ndef test_save_load_int32_2d_3x3_v1():\n    array = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.int32)\n    filename = test_dir + \"/binary_int32_2d_3x3_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.int32, ndim=2)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_int32_2d_3x3_v1()\n\n@test\ndef test_save_load_int64_2d_3x3_v1():\n    array = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]], dtype=np.int64)\n    filename = test_dir + \"/binary_int64_2d_3x3_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.int64, ndim=2)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_int64_2d_3x3_v1()\n\n@test\ndef test_save_load_float32_2d_3x3_v1():\n    array = np.array([[1.0, 2.5, 3.7], [4.2, 5.3, 6.1], [7.0, 8.2, 9.5]],\n                     dtype=np.float32)\n    filename = test_dir + \"/binary_float32_2d_3x3_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.float32, ndim=2)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_float32_2d_3x3_v1()\n\n@test\ndef test_save_load_float64_2d_3x3_v1():\n    array = np.array([[1.0, 2.5, 3.7], [4.2, 5.3, 6.1], [7.0, 8.2, 9.5]],\n                     dtype=np.float64)\n    filename = test_dir + \"/binary_float64_2d_3x3_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.float64, ndim=2)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_float64_2d_3x3_v1()\n\n@test\ndef test_save_load_complex64_2d_3x3_v1():\n    array = np.array([[1.0 + 2.0j, 3.5 - 1.2j, 4.0 + 0.5j],\n                      [5.0 - 2.1j, 6.7 + 1.2j, 7.3 - 0.8j],\n                      [8.0 + 1.5j, 9.2 - 0.7j, 10.0 + 2.0j]],\n                     dtype=np.complex64)\n    filename = test_dir + \"/binary_complex64_2d_3x3_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.complex64, ndim=2)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_complex64_2d_3x3_v1()\n\n@test\ndef test_save_load_complex128_2d_3x3_v1():\n    array = np.array([[1.0 + 2.0j, 3.5 - 1.2j, 4.0 + 0.5j],\n                      [5.0 - 2.1j, 6.7 + 1.2j, 7.3 - 0.8j],\n                      [8.0 + 1.5j, 9.2 - 0.7j, 10.0 + 2.0j]],\n                     dtype=np.complex128)\n    filename = test_dir + \"/binary_complex128_2d_3x3_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.complex128, ndim=2)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_complex128_2d_3x3_v1()\n\n@test\ndef test_save_load_bool_2d_3x3_v1():\n    array = np.array(\n        [[True, False, True], [False, True, False], [True, True, False]],\n        dtype=bool)\n    filename = test_dir + \"/binary_bool_2d_3x3_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=bool, ndim=2)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_bool_2d_3x3_v1()\n\n@test\ndef test_save_load_int32_3d_3x3x3_v1():\n    array = np.array([[[1, 2, 3], [4, 5, 6], [7, 8, 9]],\n                      [[10, 11, 12], [13, 14, 15], [16, 17, 18]],\n                      [[19, 20, 21], [22, 23, 24], [25, 26, 27]]],\n                     dtype=np.int32)\n    filename = test_dir + \"/binary_int32_3d_3x3x3_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.int32, ndim=3)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_int32_3d_3x3x3_v1()\n\n@test\ndef test_save_load_int64_3d_3x3x3_v1():\n    array = np.array([[[1, 2, 3], [4, 5, 6], [7, 8, 9]],\n                      [[10, 11, 12], [13, 14, 15], [16, 17, 18]],\n                      [[19, 20, 21], [22, 23, 24], [25, 26, 27]]],\n                     dtype=np.int64)\n    filename = test_dir + \"/binary_int64_3d_3x3x3_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.int64, ndim=3)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_int64_3d_3x3x3_v1()\n\n@test\ndef test_save_load_float32_3d_3x3x3_v1():\n    array = np.array(\n        [[[1.0, 2.5, 3.7], [4.2, 5.3, 6.1], [7.0, 8.2, 9.5]],\n         [[10.1, 11.5, 12.7], [13.2, 14.8, 15.4], [16.0, 17.3, 18.9]],\n         [[19.5, 20.2, 21.8], [22.0, 23.7, 24.1], [25.3, 26.9, 27.4]]],\n        dtype=np.float32)\n    filename = test_dir + \"/binary_float32_3d_3x3x3_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.float32, ndim=3)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_float32_3d_3x3x3_v1()\n\n@test\ndef test_save_load_float64_3d_3x3x3_v1():\n    array = np.array(\n        [[[1.0, 2.5, 3.7], [4.2, 5.3, 6.1], [7.0, 8.2, 9.5]],\n         [[10.1, 11.5, 12.7], [13.2, 14.8, 15.4], [16.0, 17.3, 18.9]],\n         [[19.5, 20.2, 21.8], [22.0, 23.7, 24.1], [25.3, 26.9, 27.4]]],\n        dtype=np.float64)\n    filename = test_dir + \"/binary_float64_3d_3x3x3_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.float64, ndim=3)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_float64_3d_3x3x3_v1()\n\n@test\ndef test_save_load_complex64_3d_3x3x3_v1():\n    array = np.array([[[1.0 + 2.0j, 3.5 - 1.2j, 4.0 + 0.5j],\n                       [5.0 - 2.1j, 6.7 + 1.2j, 7.3 - 0.8j],\n                       [8.0 + 1.5j, 9.2 - 0.7j, 10.0 + 2.0j]],\n                      [[11.0 - 1.0j, 12.5 + 2.3j, 13.7 - 0.9j],\n                       [14.2 + 1.2j, 15.3 - 2.5j, 16.1 + 0.8j],\n                       [17.0 - 1.5j, 18.2 + 0.7j, 19.5 - 2.0j]],\n                      [[20.0 + 3.0j, 21.5 - 2.3j, 22.7 + 1.9j],\n                       [23.2 - 1.2j, 24.8 + 2.5j, 25.4 - 0.8j],\n                       [26.0 + 1.5j, 27.3 - 0.7j, 28.9 + 2.0j]]],\n                     dtype=np.complex64)\n    filename = test_dir + \"/binary_complex64_3d_3x3x3_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.complex64, ndim=3)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_complex64_3d_3x3x3_v1()\n\n@test\ndef test_save_load_complex128_3d_3x3x3_v1():\n    array = np.array([[[1.0 + 2.0j, 3.5 - 1.2j, 4.0 + 0.5j],\n                       [5.0 - 2.1j, 6.7 + 1.2j, 7.3 - 0.8j],\n                       [8.0 + 1.5j, 9.2 - 0.7j, 10.0 + 2.0j]],\n                      [[11.0 - 1.0j, 12.5 + 2.3j, 13.7 - 0.9j],\n                       [14.2 + 1.2j, 15.3 - 2.5j, 16.1 + 0.8j],\n                       [17.0 - 1.5j, 18.2 + 0.7j, 19.5 - 2.0j]],\n                      [[20.0 + 3.0j, 21.5 - 2.3j, 22.7 + 1.9j],\n                       [23.2 - 1.2j, 24.8 + 2.5j, 25.4 - 0.8j],\n                       [26.0 + 1.5j, 27.3 - 0.7j, 28.9 + 2.0j]]],\n                     dtype=np.complex128)\n    filename = test_dir + \"/binary_complex128_3d_3x3x3_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=np.complex128, ndim=3)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_complex128_3d_3x3x3_v1()\n\n@test\ndef test_save_load_bool_3d_3x3x3_v1():\n    array = np.array(\n        [[[True, False, True], [False, True, False], [True, True, False]],\n         [[False, False, True], [True, False, True], [False, True, False]],\n         [[True, False, False], [False, True, True], [True, True, False]]],\n        dtype=bool)\n    filename = test_dir + \"/binary_bool_3d_3x3x3_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename, dtype=bool, ndim=3)\n    assert np.array_equal(array, loaded_array)\n\ntest_save_load_bool_3d_3x3x3_v1()\n\n# Negative Testcases\n\n@test\ndef test_load_invalid_path_v1():\n    filename = test_dir + \"/binary_invalid_path_v1.npy\"\n    try:\n        loaded_array = np.load(filename, dtype=int)\n        assert False\n    except IOError:\n        pass\n\ntest_load_invalid_path_v1()\n\n@test\ndef test_load_corrupted_file_v1():\n    filename = test_dir + \"/binary_corrupted_file_v1.npy\"\n    try:\n        with open(filename, 'wb') as f:\n            f.write('corrupted_data')\n        np.load(filename)\n        assert False\n    except ValueError:\n        pass\n\ntest_load_corrupted_file_v1()\n\n@test\ndef test_load_version_mismatch_v1():\n    array = np.array([1, 2, 3])\n    filename = test_dir + \"/binary_version_mismatch_v1.npy\"\n    np.save(filename, array)\n    try:\n        with open(filename, 'rb+') as f:\n            f.seek(0, 6)\n            f.write('\\x00\\x00')  # Change the version to 0\n        np.load(filename)\n        assert False\n    except ValueError:\n        pass\n\ntest_load_version_mismatch_v1()\n\n@test\ndef test_load_mismatched_dtype_v1():\n    # Save with dtype int and load with other dtype\n    array = np.array([1, 2, 3], dtype=int)\n    filename = test_dir + \"/binary_mismatched_dtype_v1.npy\"\n    np.save(filename, array)\n    np.load(filename, dtype=int)\n\ntest_load_mismatched_dtype_v1()\n\n@test\ndef test_load_empty_array_v1():\n    # Save an empty array and load\n    array = np.array(np.empty((1, )), dtype=int)\n    filename = test_dir + \"/binary_empty_array_v1.npy\"\n    np.save(filename, array)\n    loaded_array = np.load(filename)\n    assert np.array_equal(array, loaded_array)\n\ntest_load_empty_array_v1()\n\n@test\ndef test_load_incorrect_filename_v1():\n    # Save using filename contains special character or too long\n    array = np.array([1, 2, 3])\n    filename = test_dir + \"/binary_incorrect_filename-@!$%^&*()_+=~`{}[].np_v1.npy\"\n    np.save(filename, array)\n    np.load(filename)\n\ntest_load_incorrect_filename_v1()\n\n@test\ndef test_load_incorrect_save_v1():\n    # Interrupt the saving process to create incomplete .npy file\n    array = np.array([1, 2, 3])\n    filename = test_dir + \"/binary_incorrect_save_v1.npy\"\n    # Save a partial array\n    with open(filename, 'wb') as f:\n        f.write(\"\\x93NUMPY\")\n        f.write(\"\\x01\\x00\\x00\\x00\")\n        f.write(\"\\x00\\x00\\x00\\x00\")\n        f.write(\"\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\")\n        f.write(\"\\x00\")\n        f.write(\"\\x00\")\n    try:\n        np.load(filename)\n        assert False\n    except ValueError:\n        pass\n\ntest_load_incorrect_save_v1()\n\n@test\ndef test_incorrect_extension_v1():\n    filename = test_dir + \"/binary_incorrect_extension.npa\"\n    np.save(filename, np.array([1, 2, 3]))\n    try:\n        np.load(filename, dtype=int)\n        assert False\n    except IOError:\n        pass\n\ntest_incorrect_extension_v1()\n"
  },
  {
    "path": "test/numpy/test_lib.codon",
    "content": "import numpy as np\nimport numpy.lib.stride_tricks as str_tricks\nfrom numpy import *\n\n@test\ndef test_as_strided(x, expected, shape=None, strides=None):\n    view = str_tricks.as_strided(x, shape=shape, strides=strides)\n    assert np.array(view == expected).all()\n\ntest_as_strided(np.array([1, 2, 3, 4, 5, 6, 7, 8, 9]), np.array([1, 3, 5, 7]),\n                (4, ), (16, ))\ntest_as_strided(np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),\n                np.array([[1, 3], [3, 5]]), (2, 2), (16, 16))\n\n@test\ndef test_sliding_window_view(x, window_shape, expected, axis=None):\n    view = str_tricks.sliding_window_view(x, window_shape, axis=axis)\n    assert (view == expected).all()\n\ntest_sliding_window_view(\n    np.arange(6), 3, np.array([[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5]]))\ntest_sliding_window_view(np.array([[0, 1, 2, 3], [10, 11, 12, 13],\n                                   [20, 21, 22, 23]]),\n                         3,\n                         np.array([[[0, 10, 20], [1, 11, 21], [2, 12, 22],\n                                    [3, 13, 23]]]),\n                         axis=0)\n\n@test\ndef test_unique(ar,\n                expected,\n                return_index: Literal[bool] = False,\n                return_inverse: Literal[bool] = False,\n                return_counts: Literal[bool] = False,\n                axis=None,\n                equal_nan: bool = True):\n    if return_index or return_counts or return_inverse:\n        u = np.unique(ar, return_index, return_inverse, return_counts, axis,\n                      equal_nan)\n        for i in range(len(expected)):\n            assert (u[i] == expected[i]).all()\n    else:\n        assert (np.unique(ar, return_index, return_inverse, return_counts,\n                          axis, equal_nan) == expected).all()\n\ntest_unique(empty(0, float), empty(0, float))\ntest_unique(1, np.array([1]))\ntest_unique([1, 1, 2, 2, 3, 3], np.array([1, 2, 3]))\ntest_unique(np.array([1.1, 2.4, 6.1, 4.13, 2.65, 3.22, 2.9]),\n            np.array([1.1, 2.4, 2.65, 2.9, 3.22, 4.13, 6.1]))\ntest_unique(np.array([1, 1, 2, 2, 3, 3]), np.array([1, 2, 3]))\ntest_unique(np.array([[1, 1], [2, 3]]), np.array([1, 2, 3]))\ntest_unique(np.array([[1, 0, 0], [1, 0, 0], [2, 3, 4]]),\n            np.array([[1, 0, 0], [2, 3, 4]]),\n            axis=0)\ntest_unique(np.array([1, 2, 6, 4, 2, 3, 2]),\n            (np.array([1, 2, 3, 4, 6]), np.array([0, 1, 5, 3, 2])),\n            return_index=True)\ntest_unique(np.array([1, 2, 6, 4, 2, 3, 2]),\n            (np.array([1, 2, 3, 4, 6]), np.array([0, 1, 4, 3, 1, 2, 1])),\n            return_inverse=True)\n#test_unique(np.array([1, 2, 6, 4, 2, 3, 2]), (np.array([1, 2, 3, 4, 6]), np.array([1, 3, 1, 1, 1])), return_counts=True)\n#test_unique(np.array(['a', 'b', 'b', 'c', 'a']), array(['a', 'b', 'c']))\n\n@test\ndef test_in1d(ar1,\n              ar2,\n              expected,\n              assume_unique: bool = False,\n              invert: bool = False,\n              kind: Optional[str] = None):\n    assert (np.in1d(ar1, ar2, assume_unique, invert, kind) == expected).all()\n\ntest_in1d(np.array([0, 1, 2, 5, 0]), 0,\n          np.array([True, False, False, False, True]))\ntest_in1d(0, 0, np.array([True]))\ntest_in1d(0, [0], np.array([True]))\ntest_in1d([0, 1, 2, 5, 0], [0, 2], np.array([True, False, True, False, True]))\ntest_in1d(np.array([0, 1, 2, 5, 0]), [0, 2],\n          np.array([True, False, True, False, True]))\ntest_in1d(np.array([[0, 1, 5], [2, 5, 0]]), [0, 5],\n          np.array([True, False, True, False, True, True]))\ntest_in1d(np.array([0, 1, 2, 5, 0]), [0, 2],\n          np.array([False, True, False, True, False]),\n          invert=True)\ntest_in1d(np.array([0, 1, 2, 5, 0]), [0],\n          array([True, False, False, False, True]))\ntest_in1d(np.array([0, 1, 2, 5, 0]), [0],\n          array([True, False, False, False, True]),\n          kind='sort')\ntest_in1d(np.array([0, 1, 2, 5, 0]), [0],\n          array([True, False, False, False, True]),\n          kind='table')\n\n@test\ndef test_intersect1d(ar1,\n                     ar2,\n                     expected,\n                     assume_unique: bool = False,\n                     return_indices: Literal[bool] = False):\n    if return_indices:\n        u = np.intersect1d(ar1, ar2, assume_unique, return_indices)\n        for i in range(len(expected)):\n            assert (u[i] == expected[i]).all()\n    else:\n        assert (np.intersect1d(ar1, ar2, assume_unique,\n                               return_indices) == expected).all()\n\ntest_intersect1d([1, 3, 4, 3], [3, 1, 2, 1], np.array([1, 3]))\ntest_intersect1d(\n    np.array([1, 1, 2, 3, 4]),\n    np.array([2, 1, 4, 6]),\n    (np.array([1, 2, 4]), np.array([0, 2, 4]), np.array([1, 0, 2])),\n    return_indices=True)\ntest_intersect1d(np.array([1, 1, 2, 3, 4]),\n                 np.array([2, 1, 4, 6]),\n                 np.array([1, 2, 4]),\n                 assume_unique=True)\n\n@test\ndef test_isin(element,\n              test_elements,\n              expected,\n              assume_unique: bool = False,\n              invert: bool = False,\n              kind: Optional[str] = None):\n    assert (np.isin(element, test_elements, assume_unique, invert,\n                    kind) == expected).all()\n\ntest_isin(np.array([[0, 2], [4, 6]]), np.array([1, 2, 4, 8]),\n          np.array([[False, True], [True, False]]))\ntest_isin([[0, 2], [4, 6]], [1, 2, 4, 8],\n          np.array([[False, True], [True, False]]))\ntest_isin([[0, 2], [4, 6]], [1, 2, 4, 8],\n          np.array([[True, False], [False, True]]),\n          invert=True)\ntest_isin([[0, 2], [4, 6]], [1, 2, 4, 8],\n          np.array([[False, True], [True, False]]),\n          assume_unique=True)\ntest_isin([[0, 2], [4, 6]], [1, 2, 4, 8],\n          np.array([[False, True], [True, False]]),\n          kind='sort')\ntest_isin([[0, 2], [4, 6]], [1, 2, 4, 8],\n          np.array([[False, True], [True, False]]),\n          kind='table')\n\n@test\ndef test_setdiff1d(ar1, ar2, expected, assume_unique: bool = False):\n    assert (np.setdiff1d(ar1, ar2, assume_unique) == expected).all()\n\ntest_setdiff1d(np.array([1, 2, 3, 2, 4, 1]), np.array([3, 4, 5, 6]),\n               np.array([1, 2]))\ntest_setdiff1d([1, 2, 3, 2, 4, 1], [3, 4, 5, 6], np.array([1, 2]))\ntest_setdiff1d([[0, 2], [4, 6]], np.array([2, 3, 5, 7, 5]), np.array([0, 4,\n                                                                      6]))\ntest_setdiff1d(2, 1, np.array([2]))\ntest_setdiff1d(empty(0, float), [1, 2], empty(0, float))\ntest_setdiff1d(2, 2, empty(0, float))\ntest_setdiff1d(np.array([1, 2, 3, 2, 4, 1]),\n               np.array([3, 4, 5, 6]),\n               np.array([1, 1, 2, 2]),\n               assume_unique=True)\n\n@test\ndef test_setxor1d(ar1, ar2, expected, assume_unique: bool = False):\n    assert (np.setxor1d(ar1, ar2, assume_unique) == expected).all()\n\ntest_setxor1d(np.array([1, 2, 3, 2, 4]), np.array([2, 3, 5, 7, 5]),\n              np.array([1, 4, 5, 7]))\ntest_setxor1d([1, 2, 3, 2, 4], [2, 3, 5, 7, 5], np.array([1, 4, 5, 7]))\ntest_setxor1d([[0, 2], [4, 6]], np.array([2, 3, 5, 7, 5]),\n              np.array([0, 3, 4, 5, 6, 7]))\ntest_setxor1d(2, 1, np.array([1, 2]))\ntest_setxor1d(empty(0, float), [1, 2], np.array([1., 2.]))\ntest_setxor1d(2, 2, empty(0, float))\ntest_setxor1d(np.array([1, 2, 3, 2, 4]),\n              np.array([2, 3, 5, 7, 5]),\n              np.array([1, 2, 4, 5, 5, 7]),\n              assume_unique=True)\n\n@test\ndef test_union1d(ar1, ar2, expected):\n    assert (np.union1d(ar1, ar2) == expected).all()\n\ntest_union1d([-1, 0, 1], [-2, 0, 2], np.array([-2, -1, 0, 1, 2]))\ntest_union1d([[0, 2], [4, 6]], np.array([2, 3, 5, 7, 5]),\n             array([0, 2, 3, 4, 5, 6, 7]))\ntest_union1d([[0, 2], [4, 6]], np.array([[2, 3, 5], [5, 7, 5]]),\n             array([0, 2, 3, 4, 5, 6, 7]))\ntest_union1d(2, 1, np.array([1, 2]))\n"
  },
  {
    "path": "test/numpy/test_linalg.codon",
    "content": "import numpy as np\nimport numpy.linalg as alg\nimport numpy.random as rnd\nfrom numpy import *\nimport itertools\nimport internal.static as static\n\ndef identity_like_generalized(a):\n    a = np.array(a)\n    if a.ndim >= 3:\n        r = np.empty(a.shape, dtype=a.dtype)\n        r[...] = np.identity(a.shape[-2])\n        return r\n    else:\n        return np.identity(a.shape[0])\n\n@test\ndef test_lstsq():\n\n    @test\n    def test_rcond():\n        a = np.array([[0., 1., 0., 1., 2., 0.], [0., 2., 0., 0., 1., 0.],\n                      [1., 0., 1., 0., 0., 4.], [0., 0., 0., 2., 3., 0.]]).T\n\n        b = np.array([1, 0, 0, 0, 0, 0])\n\n        x, residuals, rank, s = alg.lstsq(a, b, rcond=-1)\n        assert rank == 4\n        x, residuals, rank, s = alg.lstsq(a, b)\n        assert rank == 3\n        x, residuals, rank, s = alg.lstsq(a, b, rcond=None)\n        assert rank == 3\n\n    test_rcond()\n\n    @test\n    def test_empty_a_b():\n        check = [(4, 2, 2), (0, 4, 1), (0, 4, 2), (4, 0, 1), (4, 0, 2),\n                 (4, 2, 0), (0, 0, 0)]\n        i = 0\n        for values in check:\n            m = values[0]\n            n = values[1]\n            n_rhs = values[2]\n\n            a = np.arange(m * n).reshape(m, n)\n            b = np.ones((m, n_rhs))\n            x, residuals, rank, s = alg.lstsq(a, b, rcond=None)\n            if m == 0:\n                i += 1\n                if i == 1:\n                    assert x[0] == 0\n                elif i == 2:\n                    assert x[0][0] == 0\n            assert np.array_equal(np.array(x.shape), np.array((n, n_rhs)))\n            if m > n:\n                assert np.array_equal(np.array(residuals.shape),\n                                      np.array((n_rhs, )))\n            else:\n                assert np.array_equal(np.array(residuals.shape), np.array(\n                    (0, )))\n            if m > n and n_rhs > 0:\n                # residuals are exactly the squared norms of b's columns\n                if n == 0:\n                    r = b - np.dot(a, x)\n                    assert np.array_equal(residuals, (r * r).sum(axis=-2))\n            assert rank == np.minimum(m, n)\n            assert np.array_equal(np.array(s.shape),\n                                  np.array((np.minimum(m, n), )))\n\n    test_empty_a_b()\n\ntest_lstsq()\n\n@test\ndef test_matrix_power():\n    rshft_0 = np.eye(4)\n    rshft_1 = rshft_0[[3, 0, 1, 2]]\n    rshft_2 = rshft_0[[2, 3, 0, 1]]\n    rshft_3 = rshft_0[[1, 2, 3, 0]]\n    rshft_all = [rshft_0, rshft_1, rshft_2, rshft_3]\n    noninv = np.array([[1, 0], [0, 0]])\n    stacked = np.block([[[rshft_0]]] * 2)\n\n    # test_large_power\n    rshft = rshft_1.astype(np.float64)\n    assert np.array_equal(alg.matrix_power(rshft, 2**100 + 2**10 + 2**5 + 0),\n                          rshft_0)\n    assert np.array_equal(alg.matrix_power(rshft, 2**100 + 2**10 + 2**5 + 1),\n                          rshft_1)\n    assert np.array_equal(alg.matrix_power(rshft, 2**100 + 2**10 + 2**5 + 2),\n                          rshft_2)\n    assert np.array_equal(alg.matrix_power(rshft, 2**100 + 2**10 + 2**5 + 3),\n                          rshft_3)\n\n    rshft = rshft_1.astype(np.float16)\n    assert np.array_equal(alg.matrix_power(rshft, 2**100 + 2**10 + 2**5 + 0),\n                          rshft_0)\n    assert np.array_equal(alg.matrix_power(rshft, 2**100 + 2**10 + 2**5 + 1),\n                          rshft_1)\n    assert np.array_equal(alg.matrix_power(rshft, 2**100 + 2**10 + 2**5 + 2),\n                          rshft_2)\n    assert np.array_equal(alg.matrix_power(rshft, 2**100 + 2**10 + 2**5 + 3),\n                          rshft_3)\n\n    rshft = rshft_1.astype(np.complex128)\n    assert np.array_equal(alg.matrix_power(rshft, 2**100 + 2**10 + 2**5 + 0),\n                          rshft_0)\n    assert np.array_equal(alg.matrix_power(rshft, 2**100 + 2**10 + 2**5 + 1),\n                          rshft_1)\n    assert np.array_equal(alg.matrix_power(rshft, 2**100 + 2**10 + 2**5 + 2),\n                          rshft_2)\n    assert np.array_equal(alg.matrix_power(rshft, 2**100 + 2**10 + 2**5 + 3),\n                          rshft_3)\n\n    @test\n    def test_power_is_zero(M):\n        mz = alg.matrix_power(M, 0)\n        assert np.array_equal(mz, identity_like_generalized(M))\n        assert isinstance(mz.dtype, M.dtype)\n\n    for mat in rshft_all:\n        test_power_is_zero(mat.astype(np.float64))\n        test_power_is_zero(stacked.astype(np.float64))\n    for mat in rshft_all:\n        test_power_is_zero(mat.astype(np.float16))\n        test_power_is_zero(stacked.astype(np.float16))\n    for mat in rshft_all:\n        test_power_is_zero(mat.astype(np.complex128))\n        test_power_is_zero(stacked.astype(np.complex128))\n\n    @test\n    def test_power_is_one(mat):\n        mz = alg.matrix_power(mat, 1)\n        assert np.array_equal(mz, mat)\n        assert isinstance(mz.dtype, mat.dtype)\n\n    for mat in rshft_all:\n        test_power_is_one(mat.astype(np.float64))\n        test_power_is_one(stacked.astype(np.float64))\n\n    for mat in rshft_all:\n        test_power_is_one(mat.astype(np.float16))\n        test_power_is_one(stacked.astype(np.float16))\n\n    for mat in rshft_all:\n        test_power_is_one(mat.astype(np.complex128))\n        test_power_is_one(stacked.astype(np.complex128))\n\n    @test\n    def test_power_is_two(mat):\n        mz = alg.matrix_power(mat, 2)\n        assert np.array_equal(mz, np.matmul(mat, mat))\n        assert isinstance(mz.dtype, mat.dtype)\n\n    for mat in rshft_all:\n        test_power_is_two(mat.astype(np.float64))\n        test_power_is_two(stacked.astype(np.float64))\n\n    for mat in rshft_all:\n        test_power_is_two(mat.astype(np.float16))\n        test_power_is_two(stacked.astype(np.float16))\n\n    for mat in rshft_all:\n        test_power_is_two(mat.astype(np.complex128))\n        test_power_is_two(stacked.astype(np.complex128))\n\n    @test\n    def test_power_is_minus_one(mat):\n        invmat = alg.matrix_power(mat, -1)\n        assert np.array_equal(identity_like_generalized(mat),\n                              np.matmul(invmat, mat))\n\n    for mat in rshft_all:\n        test_power_is_minus_one(mat.astype(np.float64))\n\n    for mat in rshft_all:\n        test_power_is_minus_one(mat.astype(np.float16))\n\n    for mat in rshft_all:\n        test_power_is_minus_one(mat.astype(np.complex128))\n\ntest_matrix_power()\n\n@test\ndef test_eigvalsh():\n\n    @test\n    def test_types():\n        x = np.array([[1., 0.5], [0.5, 1.]], dtype=np.complex128)\n        w = alg.eigvalsh(x)\n        assert isinstance(w.dtype, np.float64)\n        x = np.array([[1., 0.5], [0.5, 1.]], dtype=np.complex64)\n        w = alg.eigvalsh(x)\n        assert isinstance(w.dtype, np.float32)\n        x = np.array([[1., 0.5], [0.5, 1.]], dtype=np.float64)\n        w = alg.eigvalsh(x)\n        assert isinstance(w.dtype, x.dtype)\n        x = np.array([[1., 0.5], [0.5, 1.]], dtype=np.float32)\n        w = alg.eigvalsh(x)\n        assert isinstance(w.dtype, x.dtype)\n\n    test_types()\n\n    @test\n    def test_invalid():\n        x = np.array([[1, 0.5], [0.5, 1]], dtype=np.float32)\n        try:\n            alg.eigvalsh(x, UPLO=\"lrong\")\n            assert False\n        except ValueError:\n            pass\n        try:\n            alg.eigvalsh(x, UPLO=\"lower\")\n            assert False\n        except ValueError:\n            pass\n        try:\n            alg.eigvalsh(x, UPLO=\"upper\")\n            assert False\n        except ValueError:\n            pass\n\n    test_invalid()\n\n    @test\n    def test_UPLO():\n        Klo = np.array([[0, 0], [1, 0]], dtype=np.double)\n        Kup = np.array([[0, 1], [0, 0]], dtype=np.double)\n        tgt = np.array([-1, 1], dtype=np.double)\n        # rtol = get_rtol(np.double)\n\n        # Check default is 'L'\n        w = alg.eigvalsh(Klo)\n        assert np.array_equal(w, tgt)\n        # Check 'L'\n        w = alg.eigvalsh(Klo, UPLO='L')\n        assert np.array_equal(w, tgt)\n        # Check 'U'\n        w = alg.eigvalsh(Kup, UPLO='U')\n        assert np.array_equal(w, tgt)\n\n    test_UPLO()\n\n    @test\n    def test_0_size():\n        # Check that all kinds of 0-sized arrays work\n        # class ArraySubclass(np.ndarray):\n        #     pass\n        a = np.zeros((0, 1, 1), dtype=np.int_)\n        res = alg.eigvalsh(a)\n        assert isinstance(res.dtype, np.float64)\n        assert np.array_equal(np.array((0, 1)), np.array(res.shape))\n        # This is just for documentation, it might make sense to change:\n        assert isinstance(res, np.ndarray)\n\n        a = np.zeros((0, 0), dtype=np.complex64)\n        res = alg.eigvalsh(a)\n        assert isinstance(res.dtype, np.float32)\n        assert np.array_equal(np.array((0, )), np.array(res.shape))\n        # This is just for documentation, it might make sense to change:\n        assert isinstance(res, np.ndarray)\n\n    test_0_size()\n\ntest_eigvalsh()\n\n@test\ndef test_eigh():\n\n    @test\n    def test_types():\n        x = np.array([[1., 0.5], [0.5, 1.]], dtype=np.complex128)\n        w, v = alg.eigh(x)\n        assert isinstance(w.dtype, np.float64)\n        assert isinstance(v.dtype, np.complex128)\n        x = np.array([[1., 0.5], [0.5, 1.]], dtype=np.complex64)\n        w, v = alg.eigh(x)\n        assert isinstance(w.dtype, np.float32)\n        assert isinstance(v.dtype, np.complex64)\n        x = np.array([[1., 0.5], [0.5, 1.]], dtype=np.float64)\n        w, v = alg.eigh(x)\n        assert isinstance(w.dtype, np.float64)\n        assert isinstance(v.dtype, np.float64)\n        x = np.array([[1., 0.5], [0.5, 1.]], dtype=np.float32)\n        w, v = alg.eigh(x)\n        assert isinstance(w.dtype, np.float32)\n        assert isinstance(v.dtype, np.float32)\n\n    test_types()\n\n    @test\n    def test_invalid():\n        x = np.array([[1, 0.5], [0.5, 1]], dtype=np.float32)\n        try:\n            alg.eigh(x, UPLO=\"lrong\")\n            assert False\n        except ValueError:\n            pass\n        try:\n            alg.eigh(x, UPLO=\"lower\")\n            assert False\n        except ValueError:\n            pass\n        try:\n            alg.eigh(x, UPLO=\"upper\")\n            assert False\n        except ValueError:\n            pass\n\n    test_invalid()\n\n    @test\n    def test_UPLO():\n        Klo = np.array([[0, 0], [1, 0]], dtype=np.double)\n        Kup = np.array([[0, 1], [0, 0]], dtype=np.double)\n        tgt = np.array([-1, 1], dtype=np.double)\n        # rtol = get_rtol(np.double)\n\n        # Check default is 'L'\n        w, v = alg.eigh(Klo)\n        assert np.array_equal(w, tgt)\n        # Check 'L'\n        w, v = alg.eigh(Klo, UPLO='L')\n        assert np.array_equal(w, tgt)\n        # Check 'U'\n        w, v = alg.eigh(Kup, UPLO='U')\n        assert np.array_equal(w, tgt)\n\n    test_UPLO()\n\n    @test\n    def test_0_size():\n        # Check that all kinds of 0-sized arrays work\n        # class ArraySubclass(np.ndarray):\n        #     pass\n        a = np.zeros((0, 1, 1), dtype=np.int_)\n        res, res_v = alg.eigh(a)\n        assert isinstance(res_v.dtype, np.float64)\n        assert isinstance(res.dtype, np.float64)\n        assert np.array_equal(np.array(a.shape), np.array(res_v.shape))\n        assert np.array_equal(np.array((0, 1)), np.array(res.shape))\n        # This is just for documentation, it might make sense to change:\n        assert isinstance(a, np.ndarray)\n\n        a = np.zeros((0, 0), dtype=np.complex64)\n        res, res_v = alg.eigh(a)\n        res = alg.eigvalsh(a)\n        assert isinstance(res_v.dtype, np.complex64)\n        assert isinstance(res.dtype, np.float32)\n        assert np.array_equal(np.array(a.shape), np.array(res_v.shape))\n        assert np.array_equal(np.array((0, )), np.array(res.shape))\n        # This is just for documentation, it might make sense to change:\n        assert isinstance(a, np.ndarray)\n\n    test_0_size()\n\ntest_eigh()\n\n@test\ndef test_norm_general():\n\n    @test\n    def test_vector_return_type():\n\n        a = np.array([1, 0, 1], dtype=np.short)\n        an = alg.norm(a, -np.inf)\n        assert an == 0.0\n        a = np.array([1, 0, 1], dtype=np.ushort)\n        an = alg.norm(a, -np.inf)\n        assert an == 0.0\n        a = np.array([1, 0, 1], dtype=np.intc)\n        an = alg.norm(a, -np.inf)\n        assert an == 0.0\n        a = np.array([1, 0, 1], dtype=np.uintc)\n        an = alg.norm(a, -np.inf)\n        assert an == 0.0\n        a = np.array([1, 0, 1], dtype=np.int_)\n        an = alg.norm(a, -np.inf)\n        assert an == 0.0\n        a = np.array([1, 0, 1], dtype=np.uint)\n        an = alg.norm(a, -np.inf)\n        assert an == 0.0\n        a = np.array([1, 0, 1], dtype=np.longlong)\n        an = alg.norm(a, -np.inf)\n        assert an == 0.0\n        a = np.array([1, 0, 1], dtype=np.ulonglong)\n        an = alg.norm(a, -np.inf)\n        assert an == 0.0\n        a = np.array([1, 0, 1], dtype=np.double)\n        an = alg.norm(a, -np.inf)\n        assert an == 0.0\n        a = np.array([1, 0, 1], dtype=np.longdouble)\n        an = alg.norm(a, -np.inf)\n        assert an == 0.0\n\n        a = np.array([1, 0, 1], dtype=np.short)\n        an = alg.norm(a, 0)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.ushort)\n        an = alg.norm(a, 0)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.intc)\n        an = alg.norm(a, 0)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.uintc)\n        an = alg.norm(a, 0)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.int_)\n        an = alg.norm(a, 0)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.uint)\n        an = alg.norm(a, 0)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.longlong)\n        an = alg.norm(a, 0)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.ulonglong)\n        an = alg.norm(a, 0)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.double)\n        an = alg.norm(a, 0)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.longdouble)\n        an = alg.norm(a, 0)\n        assert an == 2\n\n        a = np.array([1, 0, 1], dtype=np.short)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.ushort)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.intc)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.uintc)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.int_)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.uint)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.longlong)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.ulonglong)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.double)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([1, 0, 1], dtype=np.longdouble)\n        an = alg.norm(a, 1)\n        assert an == 2\n\n        a = np.array([1, 0, 1], dtype=np.short)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.41421)\n        a = np.array([1, 0, 1], dtype=np.ushort)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.41421)\n        a = np.array([1, 0, 1], dtype=np.intc)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.41421)\n        a = np.array([1, 0, 1], dtype=np.uintc)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.41421)\n        a = np.array([1, 0, 1], dtype=np.int_)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.41421)\n        a = np.array([1, 0, 1], dtype=np.uint)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.41421)\n        a = np.array([1, 0, 1], dtype=np.longlong)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.41421)\n        a = np.array([1, 0, 1], dtype=np.ulonglong)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.41421)\n        a = np.array([1, 0, 1], dtype=np.double)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.41421)\n        a = np.array([1, 0, 1], dtype=np.longdouble)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.41421)\n\n        a = np.array([1, 0, 1], dtype=np.short)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 1.0)\n        a = np.array([1, 0, 1], dtype=np.ushort)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 1.0)\n        a = np.array([1, 0, 1], dtype=np.intc)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 1.0)\n        a = np.array([1, 0, 1], dtype=np.uintc)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 1.0)\n        a = np.array([1, 0, 1], dtype=np.int_)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 1.0)\n        a = np.array([1, 0, 1], dtype=np.uint)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 1.0)\n        a = np.array([1, 0, 1], dtype=np.longlong)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 1.0)\n        a = np.array([1, 0, 1], dtype=np.ulonglong)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 1.0)\n        a = np.array([1, 0, 1], dtype=np.double)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 1.0)\n        a = np.array([1, 0, 1], dtype=np.longdouble)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 1.0)\n\n    test_vector_return_type()\n\n    @test\n    def test_vector():\n        a = [1, 2, 3, 4]\n        b = [-1, -2, -3, -4]\n        c = [-1, 2, -3, 4]\n\n        @test\n        def _test(v):\n            assert np.isclose(alg.norm(v), 30**0.5)\n            assert np.isclose(alg.norm(v, np.inf), 4.0)\n            assert np.isclose(alg.norm(v, -np.inf), 1.0)\n            assert np.isclose(alg.norm(v, 1), 10.0)\n            assert np.isclose(alg.norm(v, -1), 12.0 / 25)\n            assert np.isclose(alg.norm(v, 2), 30**0.5)\n            assert np.isclose(alg.norm(v, -2), ((205. / 144)**-0.5))\n            assert np.isclose(alg.norm(v, 0), 4)\n\n        for v in (\n                a,\n                b,\n                c,\n        ):\n            _test(v)\n\n        for v in (np.array(a), np.array(b), np.array(c)):\n            _test(v)\n\n    test_vector()\n\n    @test\n    def test_axis():\n        # Vector norms.\n        # Compare the use of `axis` with computing the norm of each row\n        # or column separately.\n        A = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.int_)\n        for order in (None, -1, 0, 1, 2, 3, np.inf, -np.inf):\n            expected0 = [\n                alg.norm(A[:, k], ord=order) for k in range(A.shape[1])\n            ]\n            assert np.isclose(alg.norm(A, ord=order, axis=0), expected0).all()\n            expected1 = [\n                alg.norm(A[k, :], ord=order) for k in range(A.shape[0])\n            ]\n            assert np.isclose(alg.norm(A, ord=order, axis=1), expected1).all()\n\n        # Matrix norms.\n        B = np.arange(1, 25, dtype=np.int_).reshape(2, 3, 4)\n        nd = B.ndim\n        for order in (None, -2, 2, -1, 1, np.inf, -np.inf, 'fro'):\n            for axis in itertools.combinations(range(-nd, nd), 2):\n                row_axis, col_axis = axis\n                if row_axis < 0:\n                    row_axis += nd\n                if col_axis < 0:\n                    col_axis += nd\n                if row_axis == col_axis:\n                    try:\n                        alg.norm(B, ord=order, axis=axis)\n                        assert False\n                    except ValueError:\n                        pass\n                else:\n                    n = alg.norm(B, ord=order, axis=axis)\n\n                    # The logic using k_index only works for nd = 3.\n                    # This has to be changed if nd is increased.\n                    k_index = nd - (row_axis + col_axis)\n                    if row_axis < col_axis:\n                        expected = [\n                            alg.norm(B[:].take(k, axis=k_index), ord=order)\n                            for k in range(B.shape[k_index])\n                        ]\n                    else:\n                        expected = [\n                            alg.norm(B[:].take(k, axis=k_index).T, ord=order)\n                            for k in range(B.shape[k_index])\n                        ]\n                    assert np.isclose(n, expected).all()\n\n    test_axis()\n\n    @test\n    def test_keepdims():\n        A = np.arange(1, 25, dtype=np.int_).reshape(2, 3, 4)\n\n        # check the order=None, axis=None case\n\n        expected = alg.norm(A, ord=None, axis=None)\n        found = alg.norm(A, ord=None, axis=None, keepdims=True)\n        assert np.isclose(found[0], expected).all()\n        assert np.array_equal(np.array(found.shape), np.array((1, 1, 1)))\n\n        # Vector norms.\n        for order in (None, -1, 0, 1, 2, 3, np.inf, -np.inf):\n            for k in range(A.ndim):\n                expected = alg.norm(A, ord=order, axis=k)\n                found = alg.norm(A, ord=order, axis=k, keepdims=True)\n                assert np.isclose(np.squeeze(found, axis=k), expected).all()\n                expected_shape = list(A.shape)\n                expected_shape[k] = 1\n                assert np.array_equal(np.array(found.shape),\n                                      np.array(expected_shape))\n\n        # Matrix norms.\n        for order in (None, -2, 2, -1, 1, np.inf, -np.inf, 'fro', 'nuc'):\n            for k in itertools.permutations(range(A.ndim), 2):\n                expected = alg.norm(A, ord=order, axis=k)\n                found = alg.norm(A, ord=order, axis=k, keepdims=True)\n                assert np.isclose(np.squeeze(found, axis=k), expected).all()\n                expected_shape = list(A.shape)\n                expected_shape[k[0]] = 1\n                expected_shape[k[1]] = 1\n                assert np.array_equal(np.array(found.shape),\n                                      np.array(expected_shape))\n\n    test_keepdims()\n\ntest_norm_general()\n\n@test\ndef test_norm2D():\n\n    @test\n    def test_matrix_return_type():\n        a = np.array([[1, 0, 1], [0, 1, 1]])\n\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.short)\n        an = alg.norm(a, -np.inf)\n        assert an == 2.0\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.ushort)\n        an = alg.norm(a, -np.inf)\n        assert an == 2.0\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.intc)\n        an = alg.norm(a, -np.inf)\n        assert an == 2.0\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.uintc)\n        an = alg.norm(a, -np.inf)\n        assert an == 2.0\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.int_)\n        an = alg.norm(a, -np.inf)\n        assert an == 2.0\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.uint)\n        an = alg.norm(a, -np.inf)\n        assert an == 2.0\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.longlong)\n        an = alg.norm(a, -np.inf)\n        assert an == 2.0\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.ulonglong)\n        an = alg.norm(a, -np.inf)\n        assert an == 2.0\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.double)\n        an = alg.norm(a, -np.inf)\n        assert an == 2.0\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.longdouble)\n        an = alg.norm(a, -np.inf)\n        assert an == 2.0\n\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.short)\n        an = alg.norm(a, -1)\n        assert np.isclose(an, 1.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.ushort)\n        an = alg.norm(a, -1)\n        assert np.isclose(an, 1.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.intc)\n        an = alg.norm(a, -1)\n        assert np.isclose(an, 1.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.uintc)\n        an = alg.norm(a, -1)\n        assert np.isclose(an, 1.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.int_)\n        an = alg.norm(a, -1)\n        assert np.isclose(an, 1.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.uint)\n        an = alg.norm(a, -1)\n        assert np.isclose(an, 1.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.longlong)\n        an = alg.norm(a, -1)\n        assert np.isclose(an, 1.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.ulonglong)\n        an = alg.norm(a, -1)\n        assert np.isclose(an, 1.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.double)\n        an = alg.norm(a, -1)\n        assert np.isclose(an, 1.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.longdouble)\n        an = alg.norm(a, -1)\n        assert np.isclose(an, 1.0)\n\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.short)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.ushort)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.intc)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.uintc)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.int_)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.uint)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.longlong)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.ulonglong)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.double)\n        an = alg.norm(a, 1)\n        assert an == 2\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.longdouble)\n        an = alg.norm(a, 1)\n        assert an == 2\n\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.short)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.ushort)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.intc)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.uintc)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.int_)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.uint)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.longlong)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.ulonglong)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.double)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.longdouble)\n        an = alg.norm(a, 2)\n        assert np.isclose(an, 1.73205)\n\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.short)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.ushort)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.intc)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.uintc)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.int_)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.uint)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.longlong)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.ulonglong)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.double)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.longdouble)\n        an = alg.norm(a, np.inf)\n        assert np.isclose(an, 2.0)\n\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.short)\n        an = alg.norm(a, 'fro')\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.ushort)\n        an = alg.norm(a, 'fro')\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.intc)\n        an = alg.norm(a, 'fro')\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.uintc)\n        an = alg.norm(a, 'fro')\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.int_)\n        an = alg.norm(a, 'fro')\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.uint)\n        an = alg.norm(a, 'fro')\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.longlong)\n        an = alg.norm(a, 'fro')\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.ulonglong)\n        an = alg.norm(a, 'fro')\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.double)\n        an = alg.norm(a, 'fro')\n        assert np.isclose(an, 2.0)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.longdouble)\n        an = alg.norm(a, 'fro')\n        assert np.isclose(an, 2.0)\n\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.short)\n        an = alg.norm(a, 'nuc')\n        assert np.isclose(an, 2.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.ushort)\n        an = alg.norm(a, 'nuc')\n        assert np.isclose(an, 2.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.intc)\n        an = alg.norm(a, 'nuc')\n        assert np.isclose(an, 2.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.uintc)\n        an = alg.norm(a, 'nuc')\n        assert np.isclose(an, 2.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.int_)\n        an = alg.norm(a, 'nuc')\n        assert np.isclose(an, 2.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.uint)\n        an = alg.norm(a, 'nuc')\n        assert np.isclose(an, 2.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.longlong)\n        an = alg.norm(a, 'nuc')\n        assert np.isclose(an, 2.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.ulonglong)\n        an = alg.norm(a, 'nuc')\n        assert np.isclose(an, 2.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.double)\n        an = alg.norm(a, 'nuc')\n        assert np.isclose(an, 2.73205)\n        a = np.array([[1, 0, 1], [0, 1, 1]], dtype=np.longdouble)\n        an = alg.norm(a, 'nuc')\n        assert np.isclose(an, 2.73205)\n\n    test_matrix_return_type()\n\n    @test\n    def test_matrix_2x2():\n        A = np.array([[1, 3], [5, 7]], dtype=np.int_)\n        assert np.isclose(alg.norm(A), 84**0.5)\n        assert np.isclose(alg.norm(A, 'fro'), 84**0.5)\n        assert np.isclose(alg.norm(A, 'nuc'), 10.0)\n        assert np.isclose(alg.norm(A, np.inf), 12.0)\n        assert np.isclose(alg.norm(A, -np.inf), 4.0)\n        assert np.isclose(alg.norm(A, 1), 10.0)\n        assert np.isclose(alg.norm(A, -1), 6.0)\n        assert np.isclose(alg.norm(A, 2), 9.1231056256176615)\n        assert np.isclose(alg.norm(A, -2), 0.87689437438234041)\n\n        try:\n            alg.norm(A, 'nofro')\n            assert False\n        except ValueError:\n            pass\n        try:\n            alg.norm(A, -3)\n            assert False\n        except ValueError:\n            pass\n        try:\n            alg.norm(A, 0)\n            assert False\n        except ValueError:\n            pass\n\n    test_matrix_2x2()\n\n    @test\n    def test_matrix_3x3():\n        A = (1 / 10) * \\\n            np.array([[1, 2, 3], [6, 0, 5], [3, 2, 1]], dtype=np.int_)\n        assert np.isclose(alg.norm(A), (1 / 10) * 89**0.5)\n        assert np.isclose(alg.norm(A, 'fro'), (1 / 10) * 89**0.5)\n        assert np.isclose(alg.norm(A, 'nuc'), 1.3366836911774836)\n        assert np.isclose(alg.norm(A, np.inf), 1.1)\n        assert np.isclose(alg.norm(A, -np.inf), 0.6)\n        assert np.isclose(alg.norm(A, 1), 1.0)\n        assert np.isclose(alg.norm(A, -1), 0.4)\n        assert np.isclose(alg.norm(A, 2), 0.88722940323461277)\n        assert np.isclose(alg.norm(A, -2), 0.19456584790481812)\n\n    test_matrix_3x3()\n\n    @test\n    def test_bad_args():\n\n        A = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.int_)\n        B = np.arange(1, 25, dtype=np.int_).reshape(2, 3, 4)\n\n        for order in [0, 3]:\n            try:\n                alg.norm(A, order, None)\n                assert False\n            except ValueError:\n                pass\n            try:\n                alg.norm(A, order, (0, 1))\n                assert False\n            except ValueError:\n                pass\n            try:\n                alg.norm(B, order, (1, 2))\n                assert False\n            except ValueError:\n                pass\n\n    test_bad_args()\n\ntest_norm2D()\n\n@test\ndef test_norm_nonsystematic():\n\n    @test\n    def test_longdouble_norm():\n        x = np.arange(10, dtype=np.longdouble)\n        assert np.isclose(alg.norm(x, ord=3), 12.6515)\n\n    test_longdouble_norm()\n\n    @test\n    def test_intmin():\n        x = np.array([-2**31], dtype=np.int32)\n        assert np.isclose(alg.norm(x, ord=3), 2147483647.9999974)\n\n    test_intmin()\n\n    @test\n    def test_complex_high_ord():\n        # gh-4156\n        d = np.empty((2, ), dtype=np.clongdouble)\n        d[0] = 6 + 7j\n        d[1] = -6 + 7j\n        res = 7.55953\n        assert np.isclose(alg.norm(d, ord=3), res)\n        d = d.astype(np.complex128)\n        assert np.isclose(alg.norm(d, ord=3), res)\n        d = d.astype(np.complex64)\n        assert np.isclose(alg.norm(d, ord=3), res)\n\n    test_complex_high_ord()\n\ntest_norm_nonsystematic()\n\n@test\ndef test_matrix_rank():\n\n    @test\n    def test_matrix_rank():\n        # Full rank matrix\n        assert 4 == alg.matrix_rank(np.eye(4))\n        # rank deficient matrix\n        I = np.eye(4)\n        I[-1, -1] = 0.\n        assert alg.matrix_rank(I) == 3\n        # All zeros - zero rank\n        assert alg.matrix_rank(np.zeros((4, 4))) == 0\n        # 1 dimension - rank 1 unless all 0\n        assert alg.matrix_rank([1, 0, 0, 0]) == 1\n        assert alg.matrix_rank(np.zeros((4, ))) == 0\n        # accepts array-like\n        assert alg.matrix_rank([1]) == 1\n        # greater than 2 dimensions treated as stacked matrices\n        ms = np.array((I, np.eye(4), np.zeros((4, 4))))\n        assert np.array_equal(alg.matrix_rank(ms), np.array([3, 4, 0]))\n        # works on scalar\n        assert alg.matrix_rank(1) == 1\n\n    test_matrix_rank()\n\n    @test\n    def test_symmetric_rank():\n        assert 4 == alg.matrix_rank(np.eye(4), hermitian=True)\n        assert 1 == alg.matrix_rank(np.ones((4, 4)), hermitian=True)\n        assert 0 == alg.matrix_rank(np.zeros((4, 4)), hermitian=True)\n        # rank deficient matrix\n        I = np.eye(4)\n        I[-1, -1] = 0.\n        assert 3 == alg.matrix_rank(I, hermitian=True)\n        # manually supplied tolerance\n        I[-1, -1] = 1e-8\n        assert 4 == alg.matrix_rank(I, hermitian=True, tol=0.99e-8)\n        assert 3 == alg.matrix_rank(I, hermitian=True, tol=1.01e-8)\n\n    test_symmetric_rank()\n\ntest_matrix_rank()\n\n@test\ndef test_reduced_rank():\n    # Test matrices with reduced rank\n    X = rnd.normal(size=(40, 10))\n    for i in range(100):\n        # Make a rank deficient matrix\n        X = rnd.normal(size=(40, 10))\n        X[:, 0] = X[:, 1] + X[:, 2]\n        # Assert that matrix_rank detected deficiency\n        assert alg.matrix_rank(X) == 9\n        X[:, 3] = X[:, 4] + X[:, 5]\n        assert alg.matrix_rank(X) == 8\n\ntest_reduced_rank()\n\n@test\ndef test_QR():\n\n    @test\n    def check_qr():\n        a = np.array([[1, 2, 3], [4, 5, 6]], dtype=np.float64)\n        m, n = a.shape\n        k = np.minimum(m, n)\n\n        # mode == 'complete'\n        res = alg.qr(a, mode='complete')\n        Q, R = res.Q, res.R\n        assert isinstance(Q.dtype, a.dtype)\n        assert isinstance(R.dtype, a.dtype)\n        assert np.array_equal(np.array(Q.shape), np.array((m, m)))\n        assert np.array_equal(np.array(R.shape), np.array((m, n)))\n        assert np.isclose(np.dot(Q, R), a).all()\n        assert np.isclose(np.dot(Q.T.conj(), Q), np.eye(m)).all()\n        assert np.isclose(np.triu(R), R).all()\n\n        # mode == 'reduced'\n        q1, r1 = alg.qr(a, mode='reduced')\n        assert isinstance(q1.dtype, a.dtype)\n        assert isinstance(r1.dtype, a.dtype)\n        assert np.array_equal(np.array(q1.shape), np.array((m, k)))\n        assert np.array_equal(np.array(r1.shape), np.array((k, n)))\n        assert np.isclose(np.dot(q1, r1), a).all()\n        assert np.isclose(np.dot(q1.T.conj(), q1), np.eye(k)).all()\n        assert np.isclose(np.triu(r1), r1).all()\n\n        # mode == 'r'\n        r2 = alg.qr(a, mode='r')\n        assert isinstance(r2.dtype, a.dtype)\n        assert np.isclose(r2, r1).all()\n\n    check_qr()\n\n    @test\n    def test_qr_empty():\n        Values = [(3, 0), (0, 3), (0, 0)]\n\n        for val in Values:\n            m = val[0]\n            n = val[1]\n            k = np.minimum(m, n)\n            a = np.empty((m, n))\n            h, tau = alg.qr(a, mode='raw')\n            assert isinstance(h.dtype, np.double)\n            assert isinstance(tau.dtype, np.double)\n            assert np.array_equal(np.array(h.shape), np.array((n, m)))\n            assert np.array_equal(np.array(tau.shape), np.array((k, )))\n\n    test_qr_empty()\n\n    @test\n    def test_mode_raw():\n        # The factorization is not unique and varies between libraries,\n        # so it is not possible to check against known values. Functional\n        # testing is a possibility, but awaits the exposure of more\n        # of the functions in lapack_lite. Consequently, this test is\n        # very limited in scope. Note that the results are in FORTRAN\n        # order, hence the h arrays are transposed.\n        a = np.array([[1, 2], [3, 4], [5, 6]], dtype=np.double)\n\n        # Test double\n        h, tau = alg.qr(a, mode='raw')\n        assert isinstance(h.dtype, np.double)\n        assert isinstance(tau.dtype, np.double)\n        assert np.array_equal(np.array(h.shape), np.array((2, 3)))\n        assert np.array_equal(np.array(tau.shape), np.array((2, )))\n\n        h, tau = alg.qr(a.T, mode='raw')\n        assert isinstance(h.dtype, np.double)\n        assert isinstance(tau.dtype, np.double)\n        assert np.array_equal(np.array(h.shape), np.array((3, 2)))\n        assert np.array_equal(np.array(tau.shape), np.array((2, )))\n\n    test_mode_raw()\n\n    @test\n    def test_mode_all_but_economic():\n        a = np.array([[1, 2], [3, 4]])\n        b = np.array([[1, 2], [3, 4], [5, 6]])\n\n        @test\n        def check_qr(a):\n            m, n = a.shape\n            k = np.minimum(m, n)\n\n            # mode == 'complete'\n            res = alg.qr(a, mode='complete')\n            Q, R = res.Q, res.R\n            assert isinstance(Q.dtype, a.dtype)\n            assert isinstance(R.dtype, a.dtype)\n            assert np.array_equal(np.array(Q.shape), np.array((m, m)))\n            assert np.array_equal(np.array(R.shape), np.array((m, n)))\n            assert np.isclose(np.dot(Q, R), a).all()\n            assert np.isclose(np.dot(Q.T.conj(), Q), np.eye(m)).all()\n            assert np.isclose(np.triu(R), R).all()\n\n            # mode == 'reduced'\n            q1, r1 = alg.qr(a, mode='reduced')\n            assert isinstance(q1.dtype, a.dtype)\n            assert isinstance(r1.dtype, a.dtype)\n            assert np.array_equal(np.array(q1.shape), np.array((m, k)))\n            assert np.array_equal(np.array(r1.shape), np.array((k, n)))\n            assert np.isclose(np.dot(q1, r1), a).all()\n            assert np.isclose(np.dot(q1.T.conj(), q1), np.eye(k)).all()\n            assert np.isclose(np.triu(r1), r1).all()\n\n            # mode == 'r'\n            r2 = alg.qr(a, mode='r')\n            assert isinstance(r2.dtype, a.dtype)\n            assert np.isclose(r2, r1).all()\n\n        m1 = a.astype(np.float64)\n        m2 = b.astype(np.float64)\n        check_qr(m1)\n        check_qr(m2)\n        check_qr(m2.T)\n\n    test_mode_all_but_economic()\n\n    @test\n    def check_qr_stacked(a):\n        # This test expects the argument `a` to be an ndarray or\n        # a subclass of an ndarray of inexact type.\n        m, n = a.shape[-2:]\n        k = np.minimum(m, n)\n\n        # mode == 'complete'\n        q, r = alg.qr(a, mode='complete')\n        assert isinstance(q.dtype, a.dtype)\n        assert isinstance(r.dtype, a.dtype)\n        assert np.array_equal(np.array(q.shape[-2:]), np.array((m, m)))\n        assert np.array_equal(np.array(r.shape[-2:]), np.array((m, n)))\n        assert np.isclose(np.matmul(q, r), a).all()\n\n        I_mat = np.identity(q.shape[-1])\n        stack_I_mat = np.broadcast_to(I_mat,\n                                      q.shape[:-2] + (q.shape[-1], ) * 2)\n        assert np.isclose(np.matmul(np.swapaxes(q, -1, -2).conj(), q),\n                          stack_I_mat).all()\n        assert np.isclose(np.triu(r[..., :, :]), r).all()\n\n        # mode == 'reduced'\n        q1, r1 = alg.qr(a, mode='complete')\n        assert isinstance(q1.dtype, a.dtype)\n        assert isinstance(r1.dtype, a.dtype)\n        assert np.array_equal(np.array(q1.shape[-2:]), np.array((m, k)))\n        assert np.array_equal(np.array(r1.shape[-2:]), np.array((k, n)))\n        assert np.isclose(np.matmul(q1, r1), a).all()\n\n        I_mat = np.identity(q1.shape[-1])\n        stack_I_mat = np.broadcast_to(I_mat,\n                                      q1.shape[:-2] + (q1.shape[-1], ) * 2)\n        assert np.isclose(np.matmul(np.swapaxes(q1, -1, -2).conj(), q1),\n                          stack_I_mat).all()\n        assert np.isclose(np.triu(r1[..., :, :]), r1).all()\n\n        # mode == 'r'\n        r2 = alg.qr(a, mode='r')\n        assert isinstance(r2.dtype, a.dtype)\n        assert np.isclose(r2, r1).all()\n\n    @test\n    def test_stacked_inputs():\n        # Define values for outer_size & size\n        outer_sizes = ((2, 2), (2, ), (2, 3, 4))\n        sizes = ((3, 4), (4, 4), (0, 3), (4, 5))\n\n        # Iterate over all combinations of outer_size, size, and dt\n        for outer_size in outer_sizes:\n            for size in sizes:\n                # Generate random array A with specified parameters\n                rng = rnd.default_rng(123)\n                A = rng.normal(size=outer_size + size).astype(np.double)\n                B = rng.normal(size=outer_size + size).astype(np.double)\n                check_qr_stacked(A)\n\n    test_stacked_inputs()\n\ntest_QR()\n\n@test\ndef test_cholesky():\n\n    @test\n    def test_basic_property():\n        shapes = ((1, 1), (2, 2), (3, 3), (3, 10, 10))\n\n        for shape in shapes:\n            rnd.seed(1)\n            a = rnd.randn(*shape)\n\n            t = list(range(len(shape)))\n            t[-2:] = -1, -2\n\n            a = np.matmul(a.transpose(t).conj(), a)\n            a = np.asarray(a, dtype=np.float64)\n\n            c = alg.cholesky(a)\n            b = np.matmul(c, c.transpose(t).conj())\n            assert np.isclose(a, b).all()\n\n            d = np.diagonal(c, axis1=-2, axis2=-1)\n            assert np.all(np.isreal(d))\n            assert np.all(d >= 0)\n\n        for shape in shapes:\n            rnd.seed(1)\n            a = rnd.randn(*shape)\n\n            t = list(range(len(shape)))\n            t[-2:] = -1, -2\n\n            a = np.matmul(a.transpose(t).conj(), a)\n            a = np.asarray(a, dtype=np.float32)\n\n            c = alg.cholesky(a)\n            b = np.matmul(c, c.transpose(t).conj())\n            assert np.isclose(a, b).all()\n\n            d = np.diagonal(c, axis1=-2, axis2=-1)\n            assert np.all(np.isreal(d))\n            assert np.all(d >= 0)\n\n        for shape in shapes:\n            rnd.seed(1)\n            a = rnd.randn(*shape) + [1j] * rnd.randn(*shape)\n            t = list(range(len(shape)))\n            t[-2:] = -1, -2\n\n            a = np.matmul(a.transpose(t).conj(), a)\n            a = np.asarray(a, dtype=np.complex64)\n\n            c = alg.cholesky(a)\n            b = np.matmul(c, c.transpose(t).conj())\n            # assert np.isclose(a, b).all()\n\n            d = np.diagonal(c, axis1=-2, axis2=-1)\n            assert np.all(np.isreal(d))\n            assert np.all(d >= 0)\n\n        for shape in shapes:\n            rnd.seed(1)\n            a = rnd.randn(*shape) + [1j] * rnd.randn(*shape)\n            t = list(range(len(shape)))\n            t[-2:] = -1, -2\n\n            a = np.matmul(a.transpose(t).conj(), a)\n            a = np.asarray(a, dtype=np.complex128)\n\n            c = alg.cholesky(a)\n            b = np.matmul(c, c.transpose(t).conj())\n            assert np.isclose(a, b).all()\n\n            d = np.diagonal(c, axis1=-2, axis2=-1)\n            assert np.all(np.isreal(d))\n            assert np.all(d >= 0)\n\n    test_basic_property()\n\n    @test\n    def test_0_size():\n        a = np.zeros((0, 1, 1), dtype=np.float64)\n        res = alg.cholesky(a)\n        assert isinstance(res.dtype, np.float64)\n        assert isinstance(res, np.ndarray)\n        assert np.array_equal(np.array(a.shape), np.array(res.shape))\n\n        a = np.zeros((1, 0, 0), dtype=np.complex64)\n        res = alg.cholesky(a)\n        assert isinstance(res.dtype, np.complex64)\n        assert isinstance(res, np.ndarray)\n        assert np.array_equal(np.array(a.shape), np.array(res.shape))\n\n    test_0_size()\n\ntest_cholesky()\n\n@test\ndef test_outer():\n    arr1 = np.arange(3)\n    arr2 = np.arange(3)\n    expected = np.array([[0, 0, 0], [0, 1, 2], [0, 2, 4]])\n\n    assert np.array_equal(alg.outer(arr1, arr2), expected)\n\ntest_outer()\n\n@test\ndef test_generalized_raise_multiloop():\n    # It should raise an error even if the error doesn't occur in the\n    # last iteration of the ufunc inner loop\n\n    invertible = np.array([[1, 2], [3, 4]])\n    non_invertible = np.array([[1, 1], [1, 1]])\n\n    x = np.zeros((4, 4, 2, 2))[1::2]\n    x[...] = invertible\n    x[0, 0] = non_invertible\n\n    try:\n        alg.inv(x)\n        assert False\n    except alg.LinAlgError:\n        pass\n\ntest_generalized_raise_multiloop()\n\n@test\ndef test_multi_dot():\n\n    @test\n    def test_basic_function_with_three_arguments():\n        # multi_dot with three arguments uses a fast hand coded algorithm to\n        # determine the optimal order. Therefore test it separately.\n        A = rnd.random((6, 2))\n        B = rnd.random((2, 6))\n        C = rnd.random((6, 2))\n\n        assert np.isclose(alg.multi_dot([A, B, C]), A.dot(B).dot(C)).all()\n        assert np.isclose(alg.multi_dot([A, B, C]),\n                          np.dot(A, np.dot(B, C))).all()\n\n    test_basic_function_with_three_arguments()\n\n    @test\n    def test_basic_function_with_two_arguments():\n        # separate code path with two arguments\n        A = rnd.random((6, 2))\n        B = rnd.random((2, 6))\n\n        assert np.isclose(alg.multi_dot([A, B]), A.dot(B)).all()\n        assert np.isclose(alg.multi_dot([A, B]), np.dot(A, B)).all()\n\n    test_basic_function_with_two_arguments()\n\n    @test\n    def test_basic_function_with_dynamic_programming_optimization():\n        # multi_dot with four or more arguments uses the dynamic programming\n        # optimization and therefore deserve a separate\n        A = rnd.random((6, 2))\n        B = rnd.random((2, 6))\n        C = rnd.random((6, 2))\n        D = rnd.random((2, 1))\n        assert np.isclose(alg.multi_dot([A, B, C, D]),\n                          A.dot(B).dot(C).dot(D)).all()\n\n    test_basic_function_with_dynamic_programming_optimization()\n\n    @test\n    def test_vector_as_first_argument():\n        # The first argument can be 1-D\n        A1d = rnd.random(2)  # 1-D\n        B = rnd.random((2, 6))\n        C = rnd.random((6, 2))\n        D = rnd.random((2, 2))\n\n        # the result should be 1-D\n        assert np.array_equal(np.array(alg.multi_dot((A1d, B, C, D)).shape),\n                              np.array((2, )))\n\n    test_vector_as_first_argument()\n\n    @test\n    def test_vector_as_last_argument():\n        # The last argument can be 1-D\n        A = rnd.random((6, 2))\n        B = rnd.random((2, 6))\n        C = rnd.random((6, 2))\n        D1d = rnd.random(2)  # 1-D\n\n        # the result should be 1-D\n        assert np.array_equal(np.array(alg.multi_dot((A, B, C, D1d)).shape),\n                              np.array((6, )))\n\n    test_vector_as_last_argument()\n\n    @test\n    def test_vector_as_first_and_last_argument():\n        # The first and last arguments can be 1-D\n        A1d = rnd.random(2)  # 1-D\n        B = rnd.random((2, 6))\n        C = rnd.random((6, 2))\n        D1d = rnd.random(2)  # 1-D\n\n        arr = np.array(alg.multi_dot((A1d, B, C, D1d)))\n        dim = arr.ndim\n        assert dim == 0\n\n    test_vector_as_first_and_last_argument()\n\n    @test\n    def test_three_arguments_and_out():\n        # multi_dot with three arguments uses a fast hand coded algorithm to\n        # determine the optimal order. Therefore test it separately.\n        A = rnd.random((6, 2))\n        B = rnd.random((2, 6))\n        C = rnd.random((6, 2))\n\n        out = np.zeros((6, 2))\n        ret = alg.multi_dot([A, B, C], out=out)\n        assert np.array_equal(out, ret)\n        assert np.isclose(out, A.dot(B).dot(C)).all()\n        assert np.isclose(out, np.dot(A, np.dot(B, C))).all()\n\n    test_three_arguments_and_out()\n\n    @test\n    def test_two_arguments_and_out():\n        # separate code path with two arguments\n        A = rnd.random((6, 2))\n        B = rnd.random((2, 6))\n        out = np.zeros((6, 6))\n        ret = alg.multi_dot([A, B], out=out)\n        assert np.array_equal(out, ret)\n        assert np.isclose(out, A.dot(B)).all()\n        assert np.isclose(out, np.dot(A, B)).all()\n\n    test_two_arguments_and_out()\n\n    @test\n    def test_dynamic_programming_optimization_and_out():\n        # multi_dot with four or more arguments uses the dynamic programming\n        # optimization and therefore deserve a separate test\n        A = rnd.random((6, 2))\n        B = rnd.random((2, 6))\n        C = rnd.random((6, 2))\n        D = rnd.random((2, 1))\n        out = np.zeros((6, 1))\n        ret = alg.multi_dot((A, B, C, D), out=out)\n        assert np.array_equal(out, ret)\n        assert np.isclose(out, A.dot(B).dot(C).dot(D)).all()\n\n    test_dynamic_programming_optimization_and_out()\n\n    # @test\n    # def test_dynamic_programming_logic():\n    #     # Test for the dynamic programming part\n    #     # This test is directly taken from Cormen page 376.\n    #     arrays = (rnd.random((30, 35)), rnd.random(\n    #         (35, 15)), rnd.random((15, 5)), rnd.random(\n    #             (5, 10)), rnd.random((10, 20)), rnd.random((20, 25)))\n    #     m_expected = np.array([[0., 15750., 7875., 9375., 11875., 15125.],\n    #                            [0., 0., 2625., 4375., 7125., 10500.],\n    #                            [0., 0., 0., 750., 2500., 5375.],\n    #                            [0., 0., 0., 0., 1000., 3500.],\n    #                            [0., 0., 0., 0., 0., 5000.],\n    #                            [0., 0., 0., 0., 0., 0.]])\n    #     s_expected = np.array(\n    #         [[0, 1, 1, 3, 3, 3], [0, 0, 2, 3, 3, 3], [0, 0, 0, 3, 3, 3],\n    #          [0, 0, 0, 0, 4, 5], [0, 0, 0, 0, 0, 5], [0, 0, 0, 0, 0, 0]],\n    #         dtype=int)\n    #     s_expected -= 1  # Cormen uses 1-based index, python does not.\n\n    #     s, m = alg._multi_dot_matrix_chain_order(arrays, return_costs=True)\n\n    #     # Only the upper triangular part (without the diagonal) is interesting.\n    #     assert np.isclose(np.triu(s[:-1, 1:]), np.triu(s_expected[:-1,\n    #                                                               1:])).all()\n    #     assert np.isclose(np.triu(m), np.triu(m_expected)).all()\n\n    # test_dynamic_programming_logic()\n\n    @test\n    def test_too_few_input_arrays():\n        try:\n            alg.multi_dot([rnd.random((3, 3))])\n            assert False\n        except ValueError:\n            pass\n\n    test_too_few_input_arrays()\n\ntest_multi_dot()\n\n@test\ndef test_tensorinv():\n\n    @test\n    def test_non_square_handling():\n        try:\n            arr = np.ones((4, 6, 8, 2))\n            ind = 2\n            alg.tensorinv(arr, ind=ind)\n            assert False\n        except alg.LinAlgError:\n            pass\n\n        try:\n            arr = np.ones((3, 3, 2))\n            ind = 1\n            alg.tensorinv(arr, ind=ind)\n            assert False\n        except alg.LinAlgError:\n            pass\n\n    test_non_square_handling()\n\n    @test\n    def test_tensorinv_shape():\n        shape = (4, 6, 8, 3)\n        a = np.reshape(np.eye(24), shape)\n        ind = 2\n        ainv = alg.tensorinv(a=a, ind=ind)\n        expected = np.roll(a.shape, -ind)\n        actual = ainv.shape\n        assert np.array_equal(np.array(actual), np.array(expected))\n        shape = (24, 8, 3)\n        a = np.reshape(np.eye(24), shape)\n        ind = 1\n        ainv = alg.tensorinv(a=a, ind=ind)\n        expected = np.roll(a.shape, -ind)\n        actual = ainv.shape\n        assert np.array_equal(np.array(actual), np.array(expected))\n\n    test_tensorinv_shape()\n\n    @test\n    def test_tensorinv_ind_limit():\n        try:\n            shape = (4, 6, 8, 3)\n            a = np.reshape(np.eye(24), shape)\n            ind = -2\n            alg.tensorinv(a=a, ind=ind)\n            assert False\n        except ValueError:\n            pass\n        try:\n            shape = (4, 6, 8, 3)\n            a = np.reshape(np.eye(24), shape)\n            ind = 0\n            alg.tensorinv(a=a, ind=ind)\n            assert False\n        except ValueError:\n            pass\n\n    test_tensorinv_ind_limit()\n\n    @test\n    def test_tensorinv_result():\n        # mimic a docstring example\n        shape = (24, 8, 3)\n        a = np.reshape(np.eye(24), shape)\n        ainv = alg.tensorinv(a, ind=1)\n        b = np.ones(24)\n        assert np.isclose(np.tensordot(ainv, b, 1), alg.tensorsolve(a,\n                                                                    b)).all()\n\n    test_tensorinv_result()\n\ntest_tensorinv()\n\n@test\ndef test_tensorsolve():\n\n    @test\n    def test_non_square_handling():\n        try:\n            a = np.array([4, 6, 8, 2])\n            b = np.ones(a.shape[:2])\n            alg.tensorsolve(a, b, axes=None)\n            assert False\n        except alg.LinAlgError:\n            pass\n        try:\n            a = np.array((3, 3, 2))\n            b = np.ones(a.shape[:2])\n            alg.tensorsolve(a, b, axes=(0, 2))\n            assert False\n        except ValueError:\n            pass\n\n    test_non_square_handling()\n\n    @test\n    def test_tensorsolve_result():\n        shape = (2, 3, 6)\n        a = rnd.randn(*shape)\n        b = np.ones(a.shape[:2])\n        x = alg.tensorsolve(a, b)\n        axis: Literal[int] = 1\n        assert np.isclose(np.tensordot(a, x, axes=axis), b).all()\n\n        shape = (3, 4, 4, 3)\n        a = rnd.randn(*shape)\n        b = np.ones(a.shape[:2])\n        x = alg.tensorsolve(a, b)\n        axis: Literal[int] = 2\n        assert np.isclose(np.tensordot(a, x, axes=axis), b).all()\n\n    test_tensorsolve_result()\n\ntest_tensorsolve()\n\n@test\ndef test_unsupported_commontype():\n    # linalg gracefully handles unsupported type\n    arr = np.array([[1, -2], [2, 5]], dtype='float16')\n    # with assert_raises_regex(TypeError, \"unsupported in linalg\"):\n    # alg.cholesky(arr)\n\n    #In python this gives type error\n\ntest_unsupported_commontype()\n\n@test\ndef TestSolve_test_type():\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.single)\n    assert alg.solve(x, x)[0][0].__class__.__name__ == 'float32'\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.double)\n    assert alg.solve(x, x)[0][0].__class__.__name__ == 'float'\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.csingle)\n    assert alg.solve(x, x)[0][0].__class__.__name__ == 'complex64'\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.cdouble)\n    assert alg.solve(x, x)[0][0].__class__.__name__ == 'complex'\n\nTestSolve_test_type()\n\n@test\ndef TestSolve_test_0_size():\n\n    a = np.arange(8).reshape(2, 2, 2)\n    b = np.arange(6).reshape(1, 2, 3)\n\n    expected = alg.solve(a, b)[:, 0:0, :]\n    result = alg.solve(a[:, 0:0, 0:0], b[:, 0:0, :])\n    assert array_equal(result, expected)\n    assert (isinstance(result, np.ndarray))\n\n    try:\n        alg.solve(a[:, 0:0, 0:1], b)\n        assert False\n    except alg.LinAlgError:\n        pass\n\n    try:\n        alg.solve(a, b[:, 0:0, :])\n        assert False\n    except ValueError:\n        pass\n\n    b = np.arange(6).reshape(1, 3, 2)\n    try:\n        alg.solve(a, b)\n        assert False\n    except ValueError:\n        pass\n    try:\n        alg.solve(a[0:0], b[0:0])\n        assert False\n    except ValueError:\n        pass\n\n    b = np.arange(2) #.reshape(1, 2)\n    expected = alg.solve(a, b)[0:0]\n    result = alg.solve(a[:, 0:0, 0:0], b[0:0])\n    assert array_equal(result, expected)\n    assert (isinstance(result, np.ndarray))\n\n    b = np.arange(3) #.reshape(1, 3)\n    try:\n        alg.solve(a, b)\n        assert False\n    except ValueError:\n        pass\n    try:\n        alg.solve(a[0:0], b[0:0])\n        assert False\n    except ValueError:\n        pass\n    try:\n        alg.solve(a[:, 0:0, 0:0], b)\n        assert False\n    except ValueError:\n        pass\n\nTestSolve_test_0_size()\n\n@test\ndef TestSolve_test_0_size_k():\n\n    a = np.arange(4).reshape(1, 2, 2)\n    b = np.arange(6).reshape(3, 2, 1)\n\n    expected = alg.solve(a, b)[:, :, 0:0]\n    result = alg.solve(a, b[:, :, 0:0])\n    assert array_equal(result, expected)\n    assert (isinstance(result, np.ndarray))\n\n    expected = alg.solve(a, b)[:, 0:0, 0:0]\n    result = alg.solve(a[:, 0:0, 0:0], b[:, 0:0, 0:0])\n    assert array_equal(result, expected)\n    assert (isinstance(result, np.ndarray))\n\nTestSolve_test_0_size_k()\n\n@test\ndef TestInv_test_type():\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.single)\n    assert alg.inv(x)[0][0].__class__.__name__ == 'float32'\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.double)\n    assert alg.inv(x)[0][0].__class__.__name__ == 'float'\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.csingle)\n    assert alg.inv(x)[0][0].__class__.__name__ == 'complex64'\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.cdouble)\n    assert alg.inv(x)[0][0].__class__.__name__ == 'complex'\n\nTestInv_test_type()\n\n@test\ndef TestInv_test_0_size():\n    a = np.zeros((0, 1, 1), dtype=np.int_)\n    res = alg.inv(a)\n    assert isinstance(res.dtype, np.float64)\n    assert (a.shape == res.shape)\n    assert (isinstance(res, np.ndarray))\n\n    a = np.zeros((0, 0), dtype=np.complex64)\n    res = alg.inv(a)\n    assert isinstance(res.dtype, np.complex64)\n    assert (a.shape == res.shape)\n    assert (isinstance(res, np.ndarray))\n\nTestInv_test_0_size()\n\n@test\ndef TestEigvals_test_type():\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.single)\n    assert alg.eigvals(x)[0].real.__class__.__name__ == 'float32'\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.double)\n    assert alg.eigvals(x)[0].real.__class__.__name__ == 'float'\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.csingle)\n    assert alg.eigvals(x)[0].__class__.__name__ == 'complex64'\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.cdouble)\n    assert alg.eigvals(x)[0].__class__.__name__ == 'complex'\n\n    x = np.array([[1.0, 0.5], [-1.0, 1.0]], dtype=np.single)\n    assert alg.eigvals(x)[0].__class__.__name__ == 'complex64'\n\n    x = np.array([[1.0, 0.5], [-1.0, 1.0]], dtype=np.csingle)\n    assert alg.eigvals(x)[0].__class__.__name__ == 'complex64'\n\n    x = np.array([[1.0, 0.5], [-1.0, 1.0]], dtype=np.double)\n    assert alg.eigvals(x)[0].__class__.__name__ == 'complex'\n\n    x = np.array([[1.0, 0.5], [-1.0, 1.0]], dtype=np.cdouble)\n    assert alg.eigvals(x)[0].__class__.__name__ == 'complex'\n\nTestEigvals_test_type()\n\n@test\ndef TestEigvals_test_0_size():\n\n    a = np.zeros((0, 1, 1), dtype=np.int_)\n    res = alg.eigvals(a)\n    assert isinstance(res.dtype, complex)\n    assert ((0, 1) == res.shape)\n    assert (isinstance(res, np.ndarray))\n\n    a = np.zeros((0, 0), dtype=np.complex64)\n    res = alg.eigvals(a)\n    assert isinstance(res.dtype, np.complex64)\n    assert ((0, ) == res.shape)\n    assert (isinstance(res, np.ndarray))\n\nTestEigvals_test_0_size()\n\n@test\ndef TestEig_test_types():\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.single)\n    w, v = alg.eig(x)\n    assert isinstance(w.dtype, np.complex64)\n    assert isinstance(v.dtype, np.complex64)\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.csingle)\n    w, v = alg.eig(x)\n    assert isinstance(w.dtype, np.complex64)\n    assert isinstance(v.dtype, np.complex64)\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.double)\n    w, v = alg.eig(x)\n    assert isinstance(w.dtype, np.complex)\n    assert isinstance(v.dtype, np.complex)\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.cdouble)\n    w, v = alg.eig(x)\n    assert isinstance(w.dtype, np.complex)\n    assert isinstance(v.dtype, np.complex)\n\n    x = np.array([[1.0, 0.5], [-1.0, 1.0]], dtype=np.single)\n    w, v = alg.eig(x)\n    assert isinstance(w.dtype, np.complex64)\n    assert isinstance(v.dtype, np.complex64)\n\n    x = np.array([[1.0, 0.5], [-1.0, 1.0]], dtype=np.csingle)\n    w, v = alg.eig(x)\n    assert isinstance(w.dtype, np.complex64)\n    assert isinstance(v.dtype, np.complex64)\n\n    x = np.array([[1.0, 0.5], [-1.0, 1.0]], dtype=np.double)\n    w, v = alg.eig(x)\n    assert isinstance(w.dtype, np.complex)\n    assert isinstance(v.dtype, np.complex)\n\n    x = np.array([[1.0, 0.5], [-1.0, 1.0]], dtype=np.cdouble)\n    w, v = alg.eig(x)\n    assert isinstance(w.dtype, np.complex)\n    assert isinstance(v.dtype, np.complex)\n\nTestEig_test_types()\n\n@test\ndef TestEig_test_0_size():\n\n    a = np.zeros((0, 1, 1), dtype=np.int_)\n    res, res_v = alg.eig(a)\n    assert (isinstance(res_v.dtype, np.complex))\n    assert (isinstance(res.dtype, np.complex))\n    assert (a.shape == res_v.shape)\n    assert ((0, 1) == res.shape)\n    assert (isinstance(a, np.ndarray))\n\n    a = np.zeros((0, 0), dtype=np.complex64)\n    res, res_v = alg.eig(a)\n    assert isinstance(res_v.dtype, np.complex64)\n    assert isinstance(res.dtype, np.complex64)\n    assert (a.shape == res_v.shape)\n    assert ((0, ) == res.shape)\n    assert (isinstance(a, np.ndarray))\n\nTestEig_test_0_size()\n\n@test\ndef SVDBaseTests_test_types():\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.single)\n    res = alg.svd(x)\n    U, S, Vh = res.U, res.S, res.Vh\n\n    assert isinstance(U.dtype, np.float32)\n    assert isinstance(S.dtype, np.float32)\n    assert isinstance(Vh.dtype, np.float32)\n\n    s = alg.svd(x, compute_uv=False)\n    assert isinstance(s.dtype, np.float32)\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.csingle)\n    res = alg.svd(x)\n    U, S, Vh = res.U, res.S, res.Vh\n\n    assert isinstance(U.dtype, np.complex64)\n    assert isinstance(S.dtype, np.float32)\n    assert isinstance(Vh.dtype, np.complex64)\n\n    s = alg.svd(x, compute_uv=False)\n    assert isinstance(s.dtype, np.float32)\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.double)\n    res = alg.svd(x)\n    U, S, Vh = res.U, res.S, res.Vh\n\n    assert isinstance(U.dtype, np.float)\n    assert isinstance(S.dtype, np.float)\n    assert isinstance(Vh.dtype, np.float)\n\n    s = alg.svd(x, compute_uv=False)\n    assert isinstance(s.dtype, np.float)\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.cdouble)\n    res = alg.svd(x)\n    U, S, Vh = res.U, res.S, res.Vh\n\n    assert isinstance(U.dtype, np.complex)\n    assert isinstance(S.dtype, np.float)\n    assert isinstance(Vh.dtype, np.complex)\n\n    s = alg.svd(x, compute_uv=False)\n    assert isinstance(s.dtype, np.float)\n\nSVDBaseTests_test_types()\n\n@test\ndef TestSVD_test_empty_identity():\n    x = np.empty((4, 0))\n    u, s, vh = alg.svd(x, compute_uv=True)\n    assert (u.shape == (4, 4))\n    assert (vh.shape == (0, 0))\n    assert (u == np.eye(4)).all()\n\n    x = np.empty((0, 4))\n    u, s, vh = alg.svd(x, compute_uv=True)\n    assert (u.shape == (0, 0))\n    assert (vh.shape == (4, 4))\n    assert (vh == np.eye(4)).all()\n\nTestSVD_test_empty_identity()\n\n@test\ndef TestCond_test_basic_nonsvd():\n    A = np.array([[1, 0, 1], [0, -2, 0], [0, 0, 3]])\n    assert (alg.cond(A, inf) == 4)\n    assert (alg.cond(A, -inf) == 2 / 3)\n    assert (alg.cond(A, 1) == 4)\n    assert (alg.cond(A, -1) == 0.5)\n    assert (alg.cond(A, 'fro') == np.sqrt(265 / 12))\n\nTestCond_test_basic_nonsvd()\n\n@test\ndef TestCond_test_singular():\n\n    assert (alg.cond([[0, 0], [0, 0]], None) > 1e15)\n    assert (alg.cond([[0, 0], [0, 0]], 1) > 1e15)\n    assert (alg.cond([[0, 0], [0, 0]], 2) > 1e15)\n    assert (alg.cond([[0, 0], [0, 0]], 'fro') > 1e15)\n    assert (alg.cond([[0, 0], [0, 0]], None) > 1e15)\n    assert (alg.cond([[1, 1], [1, 1]], 1) > 1e15)\n    assert (alg.cond([[1, 1], [1, 1]], 2) > 1e15)\n    assert (alg.cond([[1, 1], [1, 1]], 'fro') > 1e15)\n\nTestCond_test_singular()\n\n@test\ndef TestCond_test_nan():\n\n    ps = (None, 1, -1, 2, -2, 'fro')\n    p_pos = (None, 1, 2, 'fro')\n    A = np.ones((2, 2))\n    A[0, 1] = np.nan\n\n    for p in ps:\n        for p in (1, -1, 'fro'):\n            c = alg.cond(A, p)\n            assert (isinstance(c, np.float64))\n            assert (np.isnan(c))\n\n        for p in (None, 2, -2):\n            try:\n                alg.cond(A, p)\n                if not __apple__:  # TODO: Accelerate doesn't give an error on SVD(nan)\n                    assert False\n            except alg.LinAlgError:\n                pass\n\n    ps = (1, -1, 'fro')\n    p_pos = (None, 1, 2, 'fro')\n    A = np.ones((3, 2, 2))\n    A[1, 0, 1] = np.nan\n    for p in ps:\n        c = alg.cond(A, p)\n        assert (np.isnan(c[1]))\n        if p in p_pos:\n            assert (c[0] > 1e15)\n            assert (c[2] > 1e15)\n        else:\n            assert (not np.isnan(c[0]))\n            assert (not np.isnan(c[2]))\n\nTestCond_test_nan()\n\n@test\ndef TestCond_test_stacked_singular():\n    rnd.seed(1234)\n    A = rnd.rand(2, 2, 2, 2)\n\n    A[0, 0] = 0\n    A[1, 1] = 0\n\n    for p in (None, 1, 2, 'fro', -1, -2):\n        c = alg.cond(A, p)\n        assert (c[0, 0] == np.inf)\n        assert (c[1, 1] == np.inf)\n        assert (np.isfinite(c[0, 1]))\n        assert (np.isfinite(c[1, 0]))\n\nTestCond_test_stacked_singular()\n\n@test\ndef TestDet_test_zero():\n\n    assert (alg.det([[0.0]]) == 0.0)\n    assert (isinstance(alg.det([[0.0]]), double))\n    assert (alg.det([[0.0j]]) == 0.0)\n    assert (isinstance(alg.det([[0.0j]]), cdouble))\n    assert (tuple(alg.slogdet([[0.0]])) == (0.0, -inf))\n    assert (isinstance(alg.slogdet([[0.0]])[0], double))\n    assert (isinstance(alg.slogdet([[0.0]])[1], double))\n    assert (tuple(alg.slogdet([[0.0j]])) == (0.0j, -inf))\n    assert (isinstance(alg.slogdet([[0.0j]])[0], cdouble))\n    assert (isinstance(alg.slogdet([[0.0j]])[1], double))\n\nTestDet_test_zero()\n\n@test\ndef TestDet_test_types():\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.single)\n    ph, s = alg.slogdet(x)\n    assert s.__class__.__name__ == 'float32'\n    assert ph.__class__.__name__ == 'float32'\n    assert alg.det(x).__class__.__name__ == 'float32'\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.double)\n    ph, s = alg.slogdet(x)\n    assert s.__class__.__name__ == 'float'\n    assert ph.__class__.__name__ == 'float'\n    assert alg.det(x).__class__.__name__ == 'float'\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.csingle)\n    ph, s = alg.slogdet(x)\n    assert s.__class__.__name__ == 'float32'\n    assert ph.__class__.__name__ == 'complex64'\n    assert alg.det(x).__class__.__name__ == 'complex64'\n\n    x = np.array([[1, 0.5], [0.5, 1]], dtype=np.cdouble)\n    ph, s = alg.slogdet(x)\n    assert s.__class__.__name__ == 'float'\n    assert ph.__class__.__name__ == 'complex'\n    assert alg.det(x).__class__.__name__ == 'complex'\n\nTestDet_test_types()\n\n@test\ndef TestDet_test_0_size():\n    a = np.zeros((0, 0), dtype=np.complex64)\n    res = alg.det(a)\n    assert (res == 1.)\n    assert (isinstance(res, np.complex64))\n    res = alg.slogdet(a)\n    assert ((res[0], res[1]) == (np.complex64(1 + 0j), np.float32(0)))\n    assert (isinstance(res[0], np.complex64))\n    assert isinstance(res[1], np.float32)\n\n    a = np.zeros((0, 0), dtype=np.float64)\n    res = alg.det(a)\n    assert (res == 1.)\n    assert isinstance(res, np.float64)\n    res = alg.slogdet(a)\n    assert ((res[0], res[1]) == (1., 0.))\n    assert isinstance(res[0], np.float64)\n    assert isinstance(res[1], np.float64)\n\nTestDet_test_0_size()\n\n@test\ndef test_trace():\n    x = np.arange(60).reshape((3, 4, 5))\n    actual = alg.trace(x)\n\n    expected = np.array([75, 78, 81, 84, 87])\n    assert array_equal(actual, expected)\n\ntest_trace()\n\n@test\ndef test_tensordot():\n    x = np.arange(6).reshape((2, 3))\n\n    assert alg.tensordot(x, x) == 55\n    assert alg.tensordot(x, x, axes=[(0, 1), (0, 1)]) == 55\n\ntest_tensordot()\n\n@test\ndef test_matmul():\n\n    x = np.arange(6).reshape((2, 3))\n    actual = alg.matmul(x, x.T)\n    expected = np.array([[5, 14], [14, 50]])\n    assert array_equal(actual, expected)\n\ntest_matmul()\n\n@test\ndef test_dot(a, b, expected):\n    assert np.dot(a, b) == expected\n\ntest_dot(3, 4, 12)\ntest_dot(np.array([2j, 3j]), np.array([2j, 3j]), -13 + 0j)\ntest_dot(np.array([1, 2, 3]), np.array([3, 2, 1]), 10)\ntest_dot(np.array([1.5, 2.3, 3.2]), np.array([3, 2, 1]), 12.3)\n\n@test\ndef test_dot_multidim(a, b, expected):\n    assert (np.dot(a, b) == expected).all()\n\ntest_dot_multidim(np.array([[1, 0], [0, 1]]), np.array([[4, 1], [2, 2]]),\n                  np.array([[4, 1], [2, 2]]))\ntest_dot_multidim(np.array([[4, 5], [8, 9]]), np.array([2, 3]),\n                  np.array([23, 43]))\n\n@test\ndef test_multi_dot(arrays, expected):\n    assert (alg.multi_dot(arrays) == expected).all()\n\ntest_multi_dot([\n    np.array([[1, 2], [4, 5]]),\n    np.array([[5, 6], [7, 9]]),\n    np.array([[6, 1], [3, 4]])\n], np.array([[186, 115], [537, 331]]))\ntest_multi_dot(\n    (np.array([1, 2]), np.array([[5, 6], [7, 9]]), np.array([[6, 1], [3, 4]])),\n    np.array([186, 115]))\n\n@test\ndef test_vdot(a, b, expected):\n    assert np.vdot(a, b) == expected\n\ntest_vdot(np.array([1 + 2j, 3 + 4j]), np.array([5 + 6j, 7 + 8j]), 70 - 8j)\ntest_vdot(np.array([[1, 4], [5, 6]]), np.array([[4, 1], [2, 2]]), 30)\n\n@test\ndef test_inner(a, b, expected):\n    if isinstance(a, int) or isinstance(b, int):\n        assert (np.inner(a, b) == expected).all()\n    elif (static.len(a.shape) == 1 and static.len(b.shape) == 1):\n        assert np.inner(a, b) == expected\n    else:\n        assert (np.inner(a, b) == expected).all()\n\ntest_inner(np.array([1, 2, 3]), np.array([0, 1, 0]), 2)\ntest_inner(\n    np.arange(24).reshape((2, 3, 4)), np.arange(4),\n    np.array([[14, 38, 62], [86, 110, 134]]))\ntest_inner(np.eye(2), 7, np.array([[7., 0.], [0., 7.]]))\n\n@test\ndef test_outer(a, b, expected):\n    assert (np.outer(a, b) == expected).all()\n\ntest_outer(np.array([4, 5, 6]), np.array([1, 2, 3]),\n           np.array([[4, 8, 12], [5, 10, 15], [6, 12, 18]]))\ntest_outer(np.array([[4, 6], [3, 8]]), np.array([1, 2, 3]),\n           np.array([[4, 8, 12], [6, 12, 18], [3, 6, 9], [8, 16, 24]]))\n\n@test\ndef test_matmul(x1, x2, expected):\n    assert (np.matmul(x1, x2) == expected).all()\n\ntest_matmul(np.array([[1, 0], [0, 1]]), np.array([[4, 1], [2, 2]]),\n            np.array([[4, 1], [2, 2]]))\ntest_matmul(np.array([[1, 0], [0, 1]]), np.array([1, 2]), np.array([1, 2]))\n\n@test\ndef test_matmul_sign(x1, x2, expected):\n    assert (x1 @ x2) == expected\n\ntest_matmul_sign(np.array([2j, 3j]), np.array([2j, 3j]), -13 + 0j)\n\n@test\ndef test_tensordot(a, b, expected, axes=2):\n    assert (np.tensordot(a, b, axes) == expected).all()\n\ntest_tensordot(np.arange(60.).reshape(3, 4, 5),\n               np.arange(24.).reshape(4, 3, 2),\n               np.array([[4400., 4730.], [4532., 4874.], [4664., 5018.],\n                         [4796., 5162.], [4928., 5306.]]),\n               axes=([1, 0], [0, 1]))\n\n#@test\n#def test_einsum(subscripts, operands, expected):\n#    assert (np.einsum(subscripts, operands) == expected).all()\n#\n#test_einsum('ii->i', np.arange(25).reshape(5,5), np.array([0, 6, 12, 18, 24]))\n\n@test\ndef test_matrix_power(a, n, expected):\n    assert (alg.matrix_power(a, n) == expected).all()\n\ntest_matrix_power(np.array([[0, 1], [-1, 0]]), 3, np.array([[0, -1], [1, 0]]))\ntest_matrix_power(np.array([[0, 1], [-1, 0]]), 0, np.array([[1, 0], [0, 1]]))\ntest_matrix_power(np.array([[0., 1.], [1., 0.]]), -3,\n                  np.array([[0., 1.], [1., 0.]]))\ntest_matrix_power(\n    np.array([[0., -1., 0., 0.], [1., 0., 0., 0.], [0., 0., 0., 1.],\n              [0., 0., -1., 0.]]), 2,\n    np.array([[-1., 0., 0., 0.], [0., -1., 0., 0.], [0., 0., -1., 0.],\n              [0., 0., 0., -1.]]))\n\n@test\ndef test_kron(a, b, expected):\n    assert (np.kron(a, b) == expected).all()\n\ntest_kron(np.array([1, 10, 100]), np.array([5, 6, 7]),\n          np.array([5, 6, 7, 50, 60, 70, 500, 600, 700]))\ntest_kron(\n    np.eye(2), np.ones((2, 2)),\n    np.array([[1., 1., 0., 0.], [1., 1., 0., 0.], [0., 0., 1., 1.],\n              [0., 0., 1., 1.]]))\ntest_kron(np.array([1, 10, 100]), np.array([5, 6]),\n          np.array([5, 6, 50, 60, 500, 600]))\ntest_kron(np.array([1, 10]), np.array([5, 6, 7]),\n          np.array([5, 6, 7, 50, 60, 70]))\ntest_kron(10, np.array([5, 6, 7]), np.array([50, 60, 70]))\ntest_kron(np.array([1, 10]), 8.8, np.array([8.8, 88.]))\ntest_kron(np.array([1, 10, 100]), np.array([5, 6, 7.1]),\n          np.array([5., 6., 7.1, 50., 60., 71., 500., 600., 710.]))\n\n@test\ndef test_cholesky(a, expected):\n    assert (alg.cholesky(a) == expected).all()\n\ntest_cholesky(np.array([[1, -2], [2, 5]]), np.array([[1., 0.], [2., 1.]]))\ntest_cholesky(np.array([[1]]), np.array([[1.]]))\n\n@test\ndef test_qr(a, expected_Q, expected_R, mode: Literal[str] = 'reduced'):\n    Q, R = alg.qr(a, mode=mode)\n    assert (round(Q, 8) == expected_Q).all() and (round(R, 8)\n                                                  == expected_R).all()\n\ntest_qr(np.array([[1, 2, 3], [4, 5, 6], [7, 8, 0]]),\n        np.array([[-0.12309149, 0.90453403, 0.40824829],\n                  [-0.49236596, 0.30151134, -0.81649658],\n                  [-0.86164044, -0.30151134, 0.40824829]]),\n        np.array([[-8.1240384, -9.6011363, -3.32347026],\n                  [0., 0.90453403, 4.52267017], [0., 0., -3.67423461]]),\n        mode='reduced')\ntest_qr(np.array([[1, 2, 3], [4, 5, 6], [7, 8, 0]]),\n        np.array([[-0.12309149, 0.90453403, 0.40824829],\n                  [-0.49236596, 0.30151134, -0.81649658],\n                  [-0.86164044, -0.30151134, 0.40824829]]),\n        np.array([[-8.1240384, -9.6011363, -3.32347026],\n                  [0., 0.90453403, 4.52267017], [0., 0., -3.67423461]]),\n        mode='complete')\ntest_qr(np.array([[1, 2, 3], [4, 5, 6], [7, 8, 0]]),\n        np.array([[-8.12403840, 0.43840236, 0.76720414],\n                  [-9.60113630, 0.90453403, 0.90907633],\n                  [-3.32347026, 4.52267017, -3.67423461]]),\n        np.array([1.12309149, 1.09503851, 0.]),\n        mode='raw')\ngen = rnd.Generator(rnd.PCG64(1234))\na = gen.random((2, 3)) + gen.random((2, 3)) * 1j\ntest_qr(\n    a,\n    np.array([[-0.91062381 - 0.22541026j, 0.33533976 + 0.08661254j],\n              [-0.24398834 - 0.24581330j, -0.65410584 - 0.67245153j]]),\n    np.array([[\n        -1.07256120 + 0.j, -0.60427723 - 0.23352661j, -1.23677059 - 0.78957732j\n    ], [0. + 0.j, -0.35019471 + 0.j, -0.09425020 - 0.07618002j]]))\n\n@test\ndef test_svd(a,\n             full_matrices: bool = True,\n             compute_uv: Literal[bool] = True,\n             hermitian: bool = False,\n             expected=0):\n    if hermitian:\n        x = alg.svd(a,\n                    full_matrices=full_matrices,\n                    compute_uv=compute_uv,\n                    hermitian=hermitian)\n        assert (round(x[0], 8) == expected).all()\n    if not compute_uv:\n        x = alg.svd(a,\n                    full_matrices=full_matrices,\n                    compute_uv=compute_uv,\n                    hermitian=hermitian)\n        assert (round(x, 8) == expected).all()\n    else:\n        U, S, Vh = alg.svd(a,\n                           full_matrices=full_matrices,\n                           compute_uv=compute_uv,\n                           hermitian=hermitian)\n        if not full_matrices:\n            np.allclose(a, np.matmul(U * S[..., None, :], Vh))\n        elif full_matrices:\n            np.allclose(a, np.dot(U[:, :len(S)] * S, Vh))\n\ntest_svd(np.array([[1, 2], [3, 4], [5, 6], [7, 8]]), full_matrices=False)\ntest_svd(np.array([[1, 2], [3, 4], [5, 6]]), full_matrices=True)\ntest_svd(np.array([[1, 2], [3, 4]]),\n         compute_uv=False,\n         expected=np.array([5.46498570, 0.36596619]))\ntest_svd(np.array([[1, 2], [3, 4]]),\n         hermitian=True,\n         expected=np.array([[0.52573111, -0.85065081],\n                            [0.85065081, 0.52573111]]))\ngen = rnd.Generator(rnd.PCG64(1234))\na = gen.random((9, 6)) + gen.random((9, 6)) * 1j\ntest_svd(a, full_matrices=True)\n\n@test\ndef test_eig(a, expected_val, expected_vec):\n    eigenvalues, eigenvectors = alg.eig(a)\n    assert (round(eigenvalues, 8) == expected_val).all() and (round(\n        eigenvectors, 8) == expected_vec).all()\n\ntest_eig(np.array([[1, -2], [-2, -5]]), np.array([1.60555128, -5.60555128]),\n         np.array([[0.95709203, 0.28978415], [-0.28978415, 0.95709203]]))\ntest_eig(np.array([[1 + 1e-9, 0], [0, 1 - 1e-9]]), np.array([1., 1.]),\n         np.array([[1., 0.], [0., 1.]]))\n\n@test\ndef test_eigh(a, expected_val, expected_vec, uplo: str = 'L'):\n    eigenvalues, eigenvectors = alg.eigh(a)\n    assert (round(eigenvalues, 8) == expected_val).all() and (round(\n        eigenvectors, 8) == expected_vec).all()\n\ntest_eigh(np.array([[1, -2], [2, 5]]), np.array([0.17157288, 5.82842712]),\n          np.array([[-0.92387953, 0.38268343], [0.38268343, 0.92387953]]))\ntest_eigh(\n    np.array([[1 + 0j, -2j], [2j, 5 + 0j]]), np.array([0.17157288,\n                                                       5.82842712]),\n    np.array([[-0.92387953 + 0.j, -0.38268343 + 0.j],\n              [0. + 0.38268343j, 0. - 0.92387953j]]))\n\n@test\ndef test_eigvals(a, expected):\n    assert (round(alg.eigvals(a), 8) == expected).all()\n\ntest_eigvals(np.array([[9, 8], [6, 7]]), np.array([15., 1.]))\ntest_eigvals(np.array([[1, -2], [-2, -5]]), np.array([1.60555128,\n                                                      -5.60555128]))\n\n@test\ndef test_eigvalsh(a, expected):\n    assert (round(alg.eigvalsh(a), 8) == expected).all()\n\ntest_eigvalsh(np.array([[1, -2], [2, 5]]), np.array([0.17157288, 5.82842712]))\ntest_eigvalsh(np.array([[1 + 0j, -2j], [2j, 5 + 0j]]),\n              np.array([0.17157288, 5.82842712]))\n\n@test\ndef test_norm(x, expected, ord=None, axis=None):\n    if axis is None:\n        assert alg.norm(x, ord, axis) == expected\n    else:\n        assert (round(alg.norm(x, ord, axis), 8) == expected).all()\n\ntest_norm(np.array([-4, -3, -2, -1, 0, 1, 2, 3, 4]), 7.745966692414834)\ntest_norm(np.array([[-4, -3, -2], [-1, 0, 1], [2, 3, 4]]), 7.745966692414834)\ntest_norm(np.array([[-4, -3, -2], [-1, 0, 1], [2, 3, 4]]), 7.745966692414834,\n          'fro')\ntest_norm(np.array([-4, -3, -2, -1, 0, 1, 2, 3, 4]), 4.0, np.inf)\ntest_norm(np.array([-4, -3, -2, -1, 0, 1, 2, 3, 4]), 0.0, -np.inf)\ntest_norm(np.array([[-4, -3, -2], [-1, 0, 1], [2, 3, 4]]), 9.0, np.inf)\ntest_norm(np.array([[-4, -3, -2], [-1, 0, 1], [2, 3, 4]]), 2.0, -np.inf)\ntest_norm(np.array([-4, -3, -2, -1, 0, 1, 2, 3, 4]), 20.0, 1)\ntest_norm(np.array([[-4, -3, -2], [-1, 0, 1], [2, 3, 4]]), 7.0, 1)\ntest_norm(np.array([[-4, -3, -2], [-1, 0, 1], [2, 3, 4]]), 6.0, -1)\ntest_norm(np.array([[1, 2, 3], [-1, 1, 4]]),\n          np.array([1.41421356, 2.23606798, 5.]),\n          axis=0)\ntest_norm(np.array([[1, 2, 3], [-1, 1, 4]]), np.array([6., 6.]), ord=1, axis=1)\n\n@test\ndef test_cond(x, expected, p=None):\n    assert alg.cond(x, p) == expected\n\ntest_cond(np.array([[1, 0, -1], [0, 1, 0], [1, 0, 1]]), 1.4142135623730951)\ntest_cond(np.array([[1, 0, -1], [0, 1, 0], [1, 0, 1]]), 3.1622776601683795,\n          'fro')\ntest_cond(np.array([[1, 0, -1], [0, 1, 0], [1, 0, 1]]), 2.0, np.inf)\ntest_cond(np.array([[1, 0, -1], [0, 1, 0], [1, 0, 1]]), 1.0, -np.inf)\ntest_cond(np.array([[1, 0, -1], [0, 1, 0], [1, 0, 1]]), 2.0, 1)\ntest_cond(np.array([[1, 0, -1], [0, 1, 0], [1, 0, 1]]), 1.0, -1)\ntest_cond(np.array([[1, 0, -1], [0, 1, 0], [1, 0, 1]]), 1.4142135623730951, 2)\ntest_cond(np.array([[1, 0, -1], [0, 1, 0], [1, 0, 1]]), 0.70710678118654746,\n          -2)\n\n@test\ndef test_det(a, expected):\n    if isinstance(expected, float):\n        assert np.isclose(alg.det(a), expected)\n    else:\n        assert np.allclose(alg.det(a), expected)\n\ntest_det(np.array([[1, 2], [3, 4]]), -2.0)\ntest_det(np.array([[[1, 2], [3, 4]], [[1, 2], [2, 1]], [[1, 3], [3, 1]]]),\n         np.array([-2., -3., -8.]))\n\n@test\ndef test_matrix_rank(A, expected, tol=None):\n    assert alg.matrix_rank(A, tol) == expected\n\ntest_matrix_rank(np.ones((4, )), 1)\ntest_matrix_rank(np.zeros((4, )), 0)\ntest_matrix_rank(\n    np.array([[1., 0., 0., 0.], [0., 1., 0., 0.], [0., 0., 1., 0.],\n              [0., 0., 0., 0.]]), 3)\ntest_matrix_rank(np.eye(4), 4)\ntest_matrix_rank(np.array([[1., 2., 3.], [-1., -2., -3.], [4., 5., 6.],\n                           [-4., -5., -6.]]),\n                 1,\n                 tol=1.5)\n\n@test\ndef test_slogdet(a, expected_sign, expected_log):\n    sign, logabsdet = alg.slogdet(a)\n    if (isinstance(expected_sign, float) or isinstance(\n            expected_sign, int)) and isinstance(expected_log, float):\n        assert sign == expected_sign and round(logabsdet, 9) == expected_log\n    else:\n        assert (sign == expected_sign).all() and (round(logabsdet, 8)\n                                                  == expected_log).all()\n\ntest_slogdet(np.array([[[1, 2], [3, 4]], [[1, 2], [2, 1]], [[1, 3], [3, 1]]]),\n             np.array([-1., -1., -1.]),\n             np.array([0.69314718, 1.09861229, 2.07944154]))\ntest_slogdet(np.eye(500) * 0.1, 1, -1151.292546497)\n\n@test\ndef test_trace(a, expected, offset=0, axis1=0, axis2=1):\n    assert np.asarray(np.trace(a, offset, axis1, axis2) == expected).all()\n    assert np.asarray(a.trace(offset, axis1, axis2) == expected).all()\n\ntest_trace(np.eye(3), 3.0)\ntest_trace(\n    np.arange(24).reshape((2, 2, 2, 3)), np.array([[18, 20, 22], [24, 26,\n                                                                  28]]))\ntest_trace(np.arange(24).reshape((2, 2, 2, 3)),\n           np.array([[6, 7, 8], [9, 10, 11]]),\n           offset=1)\ntest_trace(np.arange(24).reshape((2, 2, 2, 3)),\n           np.array([[12, 13, 14], [15, 16, 17]]),\n           offset=-1)\ntest_trace(np.arange(24).reshape((2, 2, 2, 3)),\n           np.array([[0, 0, 0], [0, 0, 0]]),\n           offset=2)\ntest_trace(np.arange(24).reshape((2, 2, 2, 3)),\n           np.array([[0, 0, 0], [0, 0, 0]]),\n           offset=-2)\ntest_trace(np.arange(24).reshape((2, 2, 2, 3)),\n           np.array([[9, 11, 13], [33, 35, 37]]),\n           axis1=2)\ntest_trace(np.arange(24).reshape((2, 2, 2, 3)),\n           np.array([[13, 19], [25, 31]]),\n           axis2=3)\n\n@test\ndef test_solve(a, b):\n    x = alg.solve(a, b)\n    assert np.allclose(np.dot(a, x), b)\n\ntest_solve(np.array([[1, 2], [3, 5]]), np.array([1, 2]))\n\n@test\ndef test_tensorsolve(a, b, expected, axes=None):\n    x = alg.tensorsolve(a, b, axes)\n    assert (round(x, 8) == expected).all()\n\ntest_tensorsolve(np.array([[60, 40, 50], [10, 20, 30], [70, 80, 90]]),\n                 np.array([18, 19, 20]),\n                 np.array([-0.04444444, -1.76111111, 1.82222222]))\n\n@test\ndef test_lstsq(a, b, expected_ls, expected_res, rcond=None):\n    ls, res, m, c = alg.lstsq(a, b, rcond)\n    assert (round(ls, 8) == expected_ls).all() and (round(res, 8)\n                                                    == expected_res).all()\n\ntest_lstsq(np.array([[1, 2], [-1, 1], [0, 3]]), np.array([1, 3, 0]),\n           np.array([-1.22222222, 0.44444444]), np.array([5.33333333]))\ntest_lstsq(np.array([[2, 1], [1, 3]]), np.array([[9, 8], [4, 6]]),\n           round(np.array([[4.6, 3.6], [-0.2, 0.8]]), 8), empty(0, float))\n\n@test\ndef test_inv(a, expected):\n    assert (round(alg.inv(a), 2) == round(expected, 2)).all()\n\ntest_inv(np.array([[1, 2], [3, 4]]), np.array([[-2., 1.], [1.5, -0.5]]))\ntest_inv(np.array([[[1., 2.], [3., 4.]], [[1., 3.], [3., 5.]]]),\n         np.array([[[-2., 1.], [1.5, -0.5]], [[-1.25, 0.75], [0.75, -0.25]]]))\n\n@test\ndef test_pinv(a, expected, hermitian=False):\n    if not hermitian:\n        assert np.allclose(a, np.dot(a, np.dot(alg.pinv(a), a)))\n    else:\n        assert np.allclose(alg.pinv(a, hermitian=hermitian)[0][0], expected)\n\ntest_pinv(\n    np.array([[\n        -0.11701212, 2.00785233, -0.18970224, -1.21453479, 0.65005008,\n        1.54383399\n    ],\n              [\n                  -0.77042552, -2.61734354, -1.94324215, 1.21451409,\n                  0.03406445, -0.02870602\n              ],\n              [\n                  0.77946711, 1.36558027, 1.41045231, -0.85389799, -0.61840915,\n                  0.26268489\n              ]]), 0)\ntest_pinv(np.array([[1, 2], [3, 4], [5, 6]]), 0)\ntest_pinv(np.array([[1, 2], [3, 4]]), -0.79999999, True)\n\n@test\ndef test_tensorinv():\n    a = np.eye(4 * 6)\n    x = a.reshape((4, 6, 8, 3))\n    ainv = alg.tensorinv(x)\n    b = np.array([[\n        1.06690372, -1.18093448, 0.59943811, -0.21417083, -0.74814399,\n        0.22962492\n    ],\n                  [\n                      -0.32909058, -0.06753367, 0.67559711, -1.13889073,\n                      -0.24667704, 0.19231421\n                  ],\n                  [\n                      0.07065959, 0.48421004, -0.24765094, 0.15383651,\n                      0.29206018, 0.24088399\n                  ],\n                  [\n                      -0.46943209, -0.95985002, -0.38301866, 0.01950948,\n                      -1.32758378, 0.84240223\n                  ]])\n    d = (np.tensordot(ainv, b)).reshape((8, 3))\n    assert np.allclose(d, alg.tensorsolve(x, b))\n\ntest_tensorinv()\n\n@test\ndef test_linalg_non_c_contig(x1, x2, expected):\n    assert (np.matmul(x1, x2)[0] == expected[0]).all()\n\nfcontig_matrix1 = (np.array([[1., 3., 6.], [2., 4., 9.]])).T\nfcontig_matrix2 = (np.array([[1.4, 8.8], [3.6, 2.21]])).T\ntest_linalg_non_c_contig(\n    fcontig_matrix1, fcontig_matrix2,\n    np.array([[19.0, 8.02], [39.4, 19.64], [87.6, 41.49]]))\n\nregular_matrix = np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]])\nnoncontig_matrix1 = regular_matrix[::2, ::2]\nnoncontig_matrix2 = regular_matrix[:2, 1::2]\ntest_linalg_non_c_contig(noncontig_matrix1, noncontig_matrix2,\n                         np.array([[20, 28], [84, 124]], dtype=int))\n\n@test\ndef test_matmul_contigs(dtype: type):\n    a = np.array([[1, 2, -3], [4, -5, 6]], dtype)\n    b = np.array([[1, 2, 3, 4], [4, 5, 6, 7], [9, 8, 7, 6]], dtype)\n    x = np.array([[1, 2], [-3, 4], [-5, 6]], dtype).T\n    y = np.array([[1, 2, 3], [4, 4, 5], [6, 7, 9], [8, 7, 6]], dtype).T\n\n    a1 = np.array([[1, 0, 2, 0, -3], [0] * 5, [4, 0, -5, 0, 6]],\n                  dtype)[::2, ::2]\n    b1 = np.array([[1, 0, 2, 0, 3, 0, 4], [0] * 7, [4, 0, 5, 0, 6, 0, 7],\n                   [0] * 7, [9, 0, 8, 0, 7, 0, 6]], dtype)[::2, ::2]\n    x1 = np.array([[1, 0, 2], [0] * 3, [-3, 0, 4], [0] * 3, [-5, 0, 6]],\n                  dtype).T[::2, ::2]\n    y1 = np.array([[1, 0, 2, 0, 3], [0] * 5, [4, 0, 4, 0, 5], [0] * 5,\n                   [6, 0, 7, 0, 9], [0] * 5, [8, 0, 7, 0, 6]],\n                  dtype).T[::2, ::2]\n\n    ab = np.array([[-18, -12, -6, 0], [38, 31, 24, 17]], dtype)\n    xy = np.array([[-20, -33, -60, -43], [28, 54, 94, 80]], dtype)\n    ay = np.array([[-4, -3, -7, 4], [12, 26, 43, 33]], dtype)\n    xb = np.array([[-56, -53, -50, -47], [72, 72, 72, 72]], dtype)\n\n    oc = np.empty((2, 4), order='C', dtype=dtype)\n    of = np.empty((2, 4), order='F', dtype=dtype)\n\n    assert np.array_equal(a @ b, ab)\n    assert np.array_equal(x @ y, xy)\n    assert np.array_equal(a @ y, ay)\n    assert np.array_equal(x @ b, xb)\n    assert np.array_equal(np.matmul(a, b, out=oc), ab)\n    assert np.array_equal(oc, ab)\n    assert np.array_equal(np.matmul(x, y, out=of), xy)\n    assert np.array_equal(of, xy)\n    assert np.array_equal(np.matmul(a, y, out=oc), ay)\n    assert np.array_equal(oc, ay)\n    assert np.array_equal(np.matmul(x, b, out=oc), xb)\n    assert np.array_equal(oc, xb)\n\n    assert np.array_equal(a1 @ b1, ab)\n    assert np.array_equal(x1 @ y1, xy)\n    assert np.array_equal(a1 @ y1, ay)\n    assert np.array_equal(x1 @ b1, xb)\n    assert np.array_equal(np.matmul(a1, b1, out=oc), ab)\n    assert np.array_equal(oc, ab)\n    assert np.array_equal(np.matmul(x1, y1, out=oc), xy)\n    assert np.array_equal(oc, xy)\n    assert np.array_equal(np.matmul(a1, y1, out=oc), ay)\n    assert np.array_equal(oc, ay)\n    assert np.array_equal(np.matmul(x1, b1, out=oc), xb)\n    assert np.array_equal(oc, xb)\n\n@test\ndef test_matmul_vec(dtype: type):\n    # vec-mat\n    a = np.array([11, 22, 33], dtype)\n    b = np.array([[1, 2], [2, 3], [3, -5]], dtype)\n    y = np.array([[1, 2, 2], [3, 3, -5]], dtype).T\n\n    a1 = np.array([11, 0, 22, 0, 33], dtype)[::2]\n    b1 = np.array([[1, 0, 2], [0] * 3, [2, 0, 3], [0] * 3, [3, 0, -5]],\n                  dtype)[::2, ::2]\n    y1 = np.array([[1, 0, 2, 0, 2], [0] * 5, [3, 0, 3, 0, -5]],\n                  dtype).T[::2, ::2]\n\n    ab = np.array([154, -77], dtype)\n    ay = np.array([121, -66], dtype)\n\n    oc = np.empty(2, order='C', dtype=dtype)\n    of = np.empty(2, order='F', dtype=dtype)\n\n    assert np.array_equal(a @ b, ab)\n    assert np.array_equal(a @ y, ay)\n    assert np.array_equal(a1 @ b1, ab)\n    assert np.array_equal(a1 @ y1, ay)\n    assert np.array_equal(a @ b1, ab)\n    assert np.array_equal(a1 @ y, ay)\n    assert np.array_equal(np.matmul(a, b, out=oc), ab)\n    assert np.array_equal(oc, ab)\n    assert np.array_equal(np.matmul(a, y, out=of), ay)\n    assert np.array_equal(of, ay)\n    assert np.array_equal(np.matmul(a1, b1, out=oc), ab)\n    assert np.array_equal(oc, ab)\n    assert np.array_equal(np.matmul(a1, y1, out=oc), ay)\n    assert np.array_equal(oc, ay)\n    assert np.array_equal(np.matmul(a, b1, out=oc), ab)\n    assert np.array_equal(oc, ab)\n    assert np.array_equal(np.matmul(a1, y, out=of), ay)\n    assert np.array_equal(of, ay)\n\n    # mat-vec\n    a = np.array([[11, 22, 33], [-1, -2, -3]], dtype)\n    x = np.array([[11, 22], [33, -1], [-2, -3]], dtype).T\n    b = np.array([1, 2, 3], dtype)\n\n    a1 = np.array([[11, 0, 22, 0, 33], [0] * 5, [-1, 0, -2, 0, -3]],\n                  dtype)[::2, ::2]\n    x1 = np.array([[11, 0, 22], [0] * 3, [33, 0, -1], [0] * 3, [-2, 0, -3]],\n                  dtype).T[::2, ::2]\n    b1 = np.array([1, 0, 2, 0, 3], dtype)[::2]\n\n    ab = np.array([154, -14], dtype)\n    xb = np.array([71, 11], dtype)\n\n    oc = np.empty(2, order='C', dtype=dtype)\n    of = np.empty(2, order='F', dtype=dtype)\n\n    assert np.array_equal(a @ b, ab)\n    assert np.array_equal(x @ b, xb)\n    assert np.array_equal(a1 @ b1, ab)\n    assert np.array_equal(x1 @ b1, xb)\n    assert np.array_equal(a @ b1, ab)\n    assert np.array_equal(x1 @ b, xb)\n    assert np.array_equal(np.matmul(a, b, out=oc), ab)\n    assert np.array_equal(oc, ab)\n    assert np.array_equal(np.matmul(x, b, out=of), xb)\n    assert np.array_equal(of, xb)\n    assert np.array_equal(np.matmul(a1, b1, out=oc), ab)\n    assert np.array_equal(oc, ab)\n    assert np.array_equal(np.matmul(x1, b1, out=oc), xb)\n    assert np.array_equal(oc, xb)\n    assert np.array_equal(np.matmul(a1, b1, out=oc), ab)\n    assert np.array_equal(oc, ab)\n    assert np.array_equal(np.matmul(x1, b1, out=oc), xb)\n    assert np.array_equal(oc, xb)\n\n@test\ndef test_matmul_syrk(dtype: type):\n    a = np.array([[1, 3, -2], [-1, 4, -5]], dtype=dtype)\n    assert np.array_equal(a @ a.T, np.array([[14, 21], [21, 42]], dtype=dtype))\n    assert np.array_equal(\n        a.T @ a,\n        np.array([[2, -1, 3], [-1, 25, -26], [3, -26, 29]], dtype=dtype))\n\n    a = np.array([[1, 3, -2], [-1, 4, -5]], dtype=dtype).T\n    assert np.array_equal(\n        a @ a.T,\n        np.array([[2, -1, 3], [-1, 25, -26], [3, -26, 29]], dtype=dtype))\n    assert np.array_equal(a.T @ a, np.array([[14, 21], [21, 42]], dtype=dtype))\n\n    a = np.array([[1, 3], [-2, -1], [4, -5]], dtype=dtype)\n    assert np.array_equal(\n        a @ a.T,\n        np.array([[10, -5, -11], [-5, 5, -3], [-11, -3, 41]], dtype=dtype))\n    assert np.array_equal(a.T @ a, np.array([[21, -15], [-15, 35]],\n                                            dtype=dtype))\n\n    a = np.array([[1, 3], [-2, -1], [4, -5]], dtype=dtype).T\n    assert np.array_equal(a @ a.T, np.array([[21, -15], [-15, 35]],\n                                            dtype=dtype))\n    assert np.array_equal(\n        a.T @ a,\n        np.array([[10, -5, -11], [-5, 5, -3], [-11, -3, 41]], dtype=dtype))\n\n@test\ndef test_dot_matmul_higher_dims(dtype: type):\n    # MatMul\n    A = np.arange(2*3*4, dtype=dtype).reshape(2, 3, 4)\n    B = np.arange(2*3*4, dtype=dtype).reshape(2, 4, 3)\n    C = A @ B\n    assert np.array_equal(C, np.array([[[42.0, 48.0, 54.0], [114.0, 136.0, 158.0], [186.0, 224.0, 262.0]], [[906.0, 960.0, 1014.0], [1170.0, 1240.0, 1310.0], [1434.0, 1520.0, 1606.0]]], dtype))\n    D = np.zeros((2, 3, 3), dtype)\n    E = np.matmul(A, B, out=D)\n    assert np.array_equal(C, D)\n    assert D.data == E.data\n\n    A = np.arange(3*4, dtype=dtype).reshape(3, 4)\n    B = np.arange(2*3*4, dtype=dtype).reshape(2, 4, 3)\n    C = A @ B\n    assert np.array_equal(C, np.array([[[42.0, 48.0, 54.0], [114.0, 136.0, 158.0], [186.0, 224.0, 262.0]], [[114.0, 120.0, 126.0], [378.0, 400.0, 422.0], [642.0, 680.0, 718.0]]], dtype))\n    D = np.zeros((2, 3, 3), dtype)\n    E = np.matmul(A, B, out=D)\n    assert np.array_equal(C, D)\n    assert D.data == E.data\n\n    A = np.arange(2*3*4, dtype=dtype).reshape(2, 3, 4)\n    B = np.arange(3*4, dtype=dtype).reshape(4, 3)\n    C = A @ B\n    assert np.array_equal(C, np.array([[[42.0, 48.0, 54.0], [114.0, 136.0, 158.0], [186.0, 224.0, 262.0]], [[258.0, 312.0, 366.0], [330.0, 400.0, 470.0], [402.0, 488.0, 574.0]]], dtype))\n    D = np.zeros((2, 3, 3), dtype)\n    E = np.matmul(A, B, out=D)\n    assert np.array_equal(C, D)\n    assert D.data == E.data\n\n    # Dot\n    A = np.arange(2*3*4, dtype=dtype).reshape(2, 3, 4)\n    B = np.arange(2*3*4, dtype=dtype).reshape(2, 4, 3)\n    C = np.dot(A, B)\n    assert np.array_equal(C, np.array([[[[42.0, 48.0, 54.0], [114.0, 120.0, 126.0]], [[114.0, 136.0, 158.0], [378.0, 400.0, 422.0]], [[186.0, 224.0, 262.0], [642.0, 680.0, 718.0]]], [[[258.0, 312.0, 366.0], [906.0, 960.0, 1014.0]], [[330.0, 400.0, 470.0], [1170.0, 1240.0, 1310.0]], [[402.0, 488.0, 574.0], [1434.0, 1520.0, 1606.0]]]], dtype))\n    D = np.zeros((2, 3, 2, 3), dtype)\n    E = np.dot(A, B, out=D)\n    assert np.array_equal(C, D)\n    assert D.data == E.data\n\n    A = np.arange(3*4, dtype=dtype).reshape(3, 4)\n    B = np.arange(2*3*4, dtype=dtype).reshape(2, 4, 3)\n    C = np.dot(A, B)\n    assert np.array_equal(C, np.array([[[42.0, 48.0, 54.0], [114.0, 120.0, 126.0]], [[114.0, 136.0, 158.0], [378.0, 400.0, 422.0]], [[186.0, 224.0, 262.0], [642.0, 680.0, 718.0]]], dtype))\n    D = np.zeros((3, 2, 3), dtype)\n    E = np.dot(A, B, out=D)\n    assert np.array_equal(C, D)\n    assert D.data == E.data\n\n    A = np.arange(2*3*4, dtype=dtype).reshape(2, 3, 4)\n    B = np.arange(3*4, dtype=dtype).reshape(4, 3)\n    C = np.dot(A, B)\n    assert np.array_equal(C, np.array([[[42.0, 48.0, 54.0], [114.0, 136.0, 158.0], [186.0, 224.0, 262.0]], [[258.0, 312.0, 366.0], [330.0, 400.0, 470.0], [402.0, 488.0, 574.0]]], dtype))\n    D = np.zeros((2, 3, 3), dtype)\n    E = np.matmul(A, B, out=D)\n    assert np.array_equal(C, D)\n    assert D.data == E.data\n\ntest_matmul_contigs(np.float64)\ntest_matmul_contigs(np.float32)\ntest_matmul_contigs(np.complex64)\ntest_matmul_contigs(np.complex128)\ntest_matmul_contigs(np.int64)\ntest_matmul_contigs(np.int32)\n\ntest_matmul_vec(np.float64)\ntest_matmul_vec(np.float32)\ntest_matmul_vec(np.complex64)\ntest_matmul_vec(np.complex128)\ntest_matmul_vec(np.int64)\ntest_matmul_vec(np.int32)\n\ntest_matmul_syrk(np.float64)\ntest_matmul_syrk(np.float32)\ntest_matmul_syrk(np.complex128)\ntest_matmul_syrk(np.complex64)\ntest_matmul_syrk(np.int64)\ntest_matmul_syrk(np.int32)\n\ntest_dot_matmul_higher_dims(np.float64)\ntest_dot_matmul_higher_dims(np.float32)\ntest_dot_matmul_higher_dims(np.complex128)\ntest_dot_matmul_higher_dims(np.complex64)\ntest_dot_matmul_higher_dims(np.int64)\ntest_dot_matmul_higher_dims(np.int32)\n"
  },
  {
    "path": "test/numpy/test_loops.codon",
    "content": "import numpy as np\n\ntest_values0 = [\n    3.,4.,5.,-3.,-4.,-5.,\n    0.0,\n    -0.0,\n    1.0,\n    -1.0,\n    2.0,\n    -2.0,\n\n    np.inf,\n    -np.inf,\n    np.nan,\n\n    np.array(9221120237041090560).view(float).item(),  # qnan\n    np.array(-2251799813685248).view(float).item(),  # snan\n\n    np.pi,\n    -np.pi,\n    np.pi/2,\n    -np.pi/2,\n\n    np.finfo(float).max,\n    np.finfo(float).min,\n    np.finfo(float).resolution,\n    np.array(1).view(float).item(),\n\n    float(np.finfo(np.float32).max),\n    float(np.finfo(np.float32).min),\n    float(np.finfo(np.float32).resolution),\n    float(np.array(1, dtype=np.uint32).view(np.float32).item()),\n\n    1.,\n    10.,\n    100.,\n    1000.,\n    10000.,\n    100000.,\n\n    -1.,\n    -10.,\n    -100.,\n    -1000.,\n    -10000.,\n    -100000.,\n\n    1e9,\n    -1e9,\n\n    1e100,\n    -1e100,\n\n    1e-100,\n    -1e-100,\n]\n\ntest_values = test_values0 + list(np.arange(-1_000_001, 1_000_001, dtype=float))\n\nN = len(test_values)\ntest_values *= 4  # Make sure we evenly divide the number of SIMD lanes\n\n@test\ndef test_func(fn, name: Literal[str]):\n    # Test float64\n    vals = np.array(test_values, dtype=float)\n    ans1 = fn(vals)\n    ans2 = vals\n    for i in range(len(ans2)):\n        ans2[i] = fn(ans2[i])\n\n    fail = False\n    for i in range(N):\n        a1 = ans1[i]\n        a2 = ans2[i]\n        n1 = np.asarray(a1).view(np.int64).item()\n        n2 = np.asarray(a2).view(np.int64).item()\n        if not ((np.isfinite(a1) and np.isfinite(a2) and np.isclose(a1, a2, atol=1e-30)) or n1 == n2 or (np.isnan(a1) and np.isnan(a2))):\n            fail = True\n    assert not fail\n\n    # Test float32\n    vals = np.array(test_values).astype(np.float32)\n    ans1 = fn(vals)\n    ans2 = vals.copy()\n    for i in range(len(ans2)):\n        ans2[i] = fn(ans2[i])\n\n    fail = False\n    for i in range(N):\n        a1 = ans1[i]\n        a2 = ans2[i]\n        n1 = np.asarray(a1).view(np.int32).item()\n        n2 = np.asarray(a2).view(np.int32).item()\n        if not ((np.isfinite(a1) and np.isfinite(a2) and np.isclose(a1, a2, atol=1e-30)) or n1 == n2 or (np.isnan(a1) and np.isnan(a2))):\n            fail = True\n    assert not fail\n\ntest_func(np.arccos, 'arccos')\ntest_func(np.arccosh, 'arccosh')\ntest_func(np.arcsin, 'arcsin')\ntest_func(np.arcsinh, 'arcsinh')\ntest_func(np.arctan, 'arctan')\ntest_func(np.arctanh, 'arctanh')\ntest_func(np.cos, 'cos')\ntest_func(np.exp, 'exp')\ntest_func(np.exp2, 'exp2')\ntest_func(np.expm1, 'expm1')\ntest_func(np.log, 'log')\ntest_func(np.log10, 'log10')\ntest_func(np.log1p, 'log1p')\ntest_func(np.log2, 'log2')\ntest_func(np.sin, 'sin')\ntest_func(np.sinh, 'sinh')\ntest_func(np.tanh, 'tanh')\n\n@test\ndef test_func2(fn, name: Literal[str]):\n    for shift in range(1, len(test_values0)):\n        # Test float64\n        vals = np.array(test_values0, dtype=float)\n        vals1 = np.roll(vals, shift)\n        ans1 = fn(vals, vals1)\n        ans2 = vals\n        for i in range(len(ans2)):\n            ans2[i] = fn(ans2[i], vals1[i])\n\n        fail = False\n        for i in range(len(ans1)):\n            a1 = ans1[i]\n            a2 = ans2[i]\n            n1 = np.asarray(a1).view(np.int64).item()\n            n2 = np.asarray(a2).view(np.int64).item()\n            if not ((np.isfinite(a1) and np.isfinite(a2) and np.isclose(a1, a2, atol=1e-30)) or n1 == n2 or (np.isnan(a1) and np.isnan(a2))):\n                fail = True\n        assert not fail\n\n        # Test float32\n        vals = np.array(test_values0).astype(np.float32)\n        vals1 = np.roll(vals, shift)\n        ans1 = fn(vals, vals1)\n        ans2 = vals.copy()\n        for i in range(len(ans2)):\n            ans2[i] = fn(ans2[i], vals1[i])\n\n        fail = False\n        for i in range(len(ans1)):\n            a1 = ans1[i]\n            a2 = ans2[i]\n            n1 = np.asarray(a1).view(np.int32).item()\n            n2 = np.asarray(a2).view(np.int32).item()\n            if not ((np.isfinite(a1) and np.isfinite(a2) and np.isclose(a1, a2, atol=1e-30)) or n1 == n2 or (np.isnan(a1) and np.isnan(a2))):\n                fail = True\n        assert not fail\n\ntest_func2(np.hypot, 'hypot')\ntest_func2(np.arctan2, 'arctan2')\n"
  },
  {
    "path": "test/numpy/test_misc.codon",
    "content": "import numpy as np\n\n@test\ndef test_shares_memory():\n    x = np.array([1, 2, 3, 4])\n    assert np.shares_memory(x, x)\n    assert not np.shares_memory(x, np.array([5, 6, 7]))\n    assert np.shares_memory(x[::2], x)\n    assert not np.shares_memory(x[::2], x[1::2])\n\n    y = np.array([1])\n    assert np.shares_memory(y, y)\n    assert not np.shares_memory(y, y.copy())\n\ntest_shares_memory()\n\ndef check_may_share_memory_exact(a, b):\n    got = np.may_share_memory(a, b, max_work=np.MAY_SHARE_EXACT)\n    cond = np.may_share_memory(a, b) == np.may_share_memory(\n        a, b, max_work=np.MAY_SHARE_BOUNDS)\n\n    a.fill(0)\n    b.fill(0)\n    a.fill(1)\n    exact = b.any()\n\n    return cond and got == exact\n\n@test\ndef test_may_share_memory_manual():\n    # Manual test cases for may_share_memory\n    import itertools\n\n    # Base arrays\n    xs0 = [\n        np.zeros((13, 21, 23, 22), dtype=np.int8),\n        np.zeros((13, 21, 23 * 2, 22), dtype=np.int8)[:, :, ::2, :]\n    ]\n\n    # Generate all negative stride combinations\n    xs = []\n    for x in xs0:\n        for ss in itertools.product(\n                *(([slice(None), slice(None, None, -1)], ) * 4)):\n            xs.append(x[ss])\n\n    for x in xs:\n        # The default is a simple extent check\n        assert np.may_share_memory(x[:, 0, :], x[:, 1, :])\n        assert np.may_share_memory(x[:, 0, :], x[:, 1, :], max_work=None)\n\n        # Exact checks\n        assert check_may_share_memory_exact(x[:, 0, :], x[:, 1, :])\n        assert check_may_share_memory_exact(x[:, ::7], x[:, 3::3])\n\n        try:\n            xp = x.ravel()\n            if xp.flags.owndata:\n                continue\n            yp = xp.view(np.int16)\n        except ValueError:\n            continue\n\n        # 0-size arrays cannot overlap\n        assert check_may_share_memory_exact(x.ravel()[6:6],\n                                            yp.reshape(13, 21, 23, 11)[:, ::7])\n\n        # Test itemsize is dealt with\n        assert check_may_share_memory_exact(x[:, ::7],\n                                            yp.reshape(13, 21, 23, 11))\n        assert check_may_share_memory_exact(\n            x[:, ::7],\n            yp.reshape(13, 21, 23, 11)[:, 3::3])\n        assert check_may_share_memory_exact(x.ravel()[6:7],\n                                            yp.reshape(13, 21, 23, 11)[:, ::7])\n\n    # Check unit size\n    x = np.zeros((1, ), dtype=np.int8)\n    assert check_may_share_memory_exact(x, x)\n    assert check_may_share_memory_exact(x, x.copy())\n\ntest_may_share_memory_manual()\n\n@test\ndef test_shares_memory_api():\n    x = np.zeros((4, 5, 6), dtype=np.int8)\n\n    assert np.shares_memory(x, x)\n    assert not np.shares_memory(x, x.copy())\n\n    a = x[:, ::2, ::3]\n    b = x[:, ::3, ::2]\n    assert np.shares_memory(a, b)\n    assert np.shares_memory(a, b, max_work=None)\n\ntest_shares_memory_api()\n\n@test\ndef test_byte_bounds():\n    a, b = np.byte_bounds(np.array(42))\n    assert b - a == 8\n\n    a, b = np.byte_bounds(np.array([1, 2, 3], np.int8))\n    assert b - a == 3\n\n    a, b = np.byte_bounds(np.ones((3, 3)))\n    assert b - a == 72\n\n    a, b = np.byte_bounds(np.ones((3, 3))[::-1, ::-1])\n    assert b - a == 72\n\n    a, b = np.byte_bounds(np.ones((3, 3))[1:1])\n    assert b == a\n\ntest_byte_bounds()\n"
  },
  {
    "path": "test/numpy/test_ndmath.codon",
    "content": "import numpy as np\nfrom numpy import *\n\n@test\ndef test_sin():\n    assert np.sin(np.pi / 2.) == 1.0\n    assert (round(np.sin(np.array((0., 30., 45., 60., 90.)) * np.pi / 180.),\n                  8) == round(array([0., 0.5, 0.70710678, 0.86602540, 1.]),\n                              8)).all()\n    assert np.sin(-1 + 1j) == (-1.2984575814159773 + 0.6349639147847361j)\n    assert np.isclose(np.sin(2.5 + 2j),\n                      (2.2515693217814876 - 2.905636060226587j))\n    assert np.isclose(np.sin(10**9), 0.5458434494486996)\n    assert np.isclose(np.sin(10**(-5)), 1e-5)\n\ntest_sin()\n\n@test\ndef test_cos():\n    assert (round(np.cos(np.array([0, np.pi / 2, np.pi])),\n                  8) == round(np.array([1., 6.12303177e-17, -1]), 8)).all()\n    assert np.isclose(np.cos(2.5 + 2j),\n                      (-3.0140590583498352 - 2.170574924649956j))\n    assert np.cos(-1.5 + 1j) == (0.10915320605445292 + 1.1722572989107924j)\n    assert np.cos(-1.5 - 1j) == (0.10915320605445292 - 1.1722572989107924j)\n    assert np.isclose(np.cos(10**(-5)), 1)\n    assert np.isclose(np.cos(10**(9)), 0.8378871813639024)\n\ntest_cos()\n\n@test\ndef test_tan():\n    assert round(np.tan(-np.pi), 8) == round(1.22460635e-16, 8)\n    assert round(np.tan(np.pi), 8) == round(-1.22460635e-16, 8)\n    assert np.isclose(np.tan(-1.5 + 1j),\n                      (-0.050905362327228636 + 1.3082952992279357j))\n    assert np.isclose(np.tan(10**(9)), 0.6514522021451413)\n    assert np.tan(10**(-9)) == 1e-9\n\ntest_tan()\n\n@test\ndef test_arcsin():\n    assert np.arcsin(1) == 1.5707963267948966\n    assert np.arcsin(-1) == -1.5707963267948966\n    assert np.arcsin(0) == 0.0\n    assert np.isclose(np.arcsin(-1.5 + 1j), (-0.9063757759747444 + 1.2604751877984541j))\n\ntest_arcsin()\n\n@test\ndef test_arccos():\n    assert np.arccos(1) == 0.0\n    assert np.arccos(-1) == 3.141592653589793\n    assert np.arccos(0) == 1.5707963267948966\n    assert np.isclose(np.arccos(1.5 + 2j), (0.964284808595142 - 1.6224941488715938j))\n\ntest_arccos()\n\n@test\ndef test_arctan():\n    assert np.arctan(1) == 0.7853981633974483\n    assert np.arctan(-1) == -0.7853981633974483\n    assert np.arctan(0) == 0.0\n    assert np.arctan(1.5 + 2j) == (1.311223269671635 + 0.3104282830771958j)\n\ntest_arctan()\n\n@test\ndef test_hypot():\n    assert (np.hypot(3 * np.ones((3, 3)),\n                     4 * np.ones((3, 3))) == np.array([[5., 5., 5.],\n                                                       [5., 5., 5.],\n                                                       [5., 5., 5.]])).all()\n    assert (np.hypot(3 * np.ones((3, 3)), [4]) == np.array([[5., 5., 5.],\n                                                            [5., 5., 5.],\n                                                            [5., 5.,\n                                                             5.]])).all()\n\ntest_hypot()\n\n@test\ndef test_arctan2():\n    assert (round(np.arctan2([1., -1.], [0., 0.]),\n                  8) == np.array([1.57079633, -1.57079633])).all()\n    assert (round(np.arctan2([0., 0., np.inf], [+0., -0., np.inf]),\n                  8) == np.array([0., 3.14159265, 0.78539816])).all()\n    x = np.array([-1, +1, +1, -1])\n    y = np.array([-1, -1, +1, +1])\n    assert (np.arctan2(y, x) * 180 / np.pi == array([-135., -45., 45.,\n                                                     135.])).all()\n\n@test\ndef test_degrees():\n    assert np.degrees(np.pi / 6) == 29.999999999999996\n    assert np.degrees(0.) == 0.0\n\ntest_degrees()\n\n@test\ndef test_radians():\n    assert np.radians(30.) == 0.5235987755982988\n    assert np.radians(180.) == 3.141592653589793\n\ntest_radians()\n\n@test\ndef test_deg2rad():\n    assert np.deg2rad(180) == 3.1415926535897931\n    assert np.deg2rad(-90) == -1.5707963267948966\n\ntest_deg2rad()\n\n@test\ndef test_rad2deg():\n    assert np.rad2deg(np.pi / 2) == 90.0\n    assert np.rad2deg(-np.pi / 2) == -90.0\n    assert np.rad2deg(np.pi) == 180.0\n\ntest_rad2deg()\n\n@test\ndef test_sinh():\n    assert np.sinh(0) == 0.0\n    assert np.sinh(np.pi * 1j / 2) == 1j\n    assert np.isclose(np.sinh(np.pi), 11.548739357257748)\n    assert np.sinh(1.5 - 1j) == (1.1504545994253859 - 1.9794844356103j)\n    assert np.sinh(0 + 1j) == 0.8414709848078965j\n\ntest_sinh()\n\n@test\ndef test_cosh():\n    assert np.cosh(0) == 1.0\n    assert np.cosh(np.pi * 1j) == (-1 + 0j)\n    assert np.cosh(np.pi) == 11.591953275521519\n    assert np.cosh(0 + 1j) == (0.5403023058681398 + 0j)\n    assert np.cosh(-1.5 + 1j) == (1.2710123394623098 - 1.7917268800098571j)\n\ntest_cosh()\n\n@test\ndef test_tanh():\n    assert np.tanh(0) == 0.0\n    assert np.tanh(np.pi * 1j) == -1.2246467991473532e-16j\n    assert np.tanh(np.pi * 1j / 2) == 1.633123935319537e+16j\n    assert round(np.tanh(-1.5 + 1j), 8) == (-1.03795878 + 0.09421292j)\n    assert np.tanh(1 + 0j) == (0.7615941559557649 + 0j)\n\ntest_tanh()\n\n@test\ndef test_arcsinh():\n    assert np.arcsinh(np.e) == 1.725382558852315\n    assert np.arcsinh(10) == 2.99822295029797\n    assert np.arcsinh(np.inf) == np.inf\n    assert np.isclose(np.arcsinh(1 + 0j), (0.881373587019543 + 0j))\n    assert np.isclose(np.arcsinh(1.5 + 1j),\n                      (1.3169578969248166 + 0.5235987755982989j))\n\ntest_arcsinh()\n\n@test\ndef test_arccosh():\n    assert np.arccosh(np.e) == 1.6574544541530771\n    assert np.arccosh(10) == 2.993222846126381\n    assert np.arccosh(np.inf) == np.inf\n    assert np.arccosh(1.5 + 1j) == (1.2604751877984541 + 0.6644205508201522j)\n    assert np.arccosh(1 + 0j) == 0j\n\ntest_arccosh()\n\n@test\ndef test_arctanh():\n    assert np.arctanh(0) == 0.0\n    assert np.isclose(np.arctanh(0.5), 0.5493061443340548)\n    assert np.isclose(np.arctanh(-0.5), -0.5493061443340548)\n    assert np.isclose(np.arctanh(1 + 1j), (0.40235947810852507 + 1.0172219678978514j))\n    assert np.arctanh(-1.5 + 1j) == (-0.4394644793880934 + 1.2074751564540338j)\n\ntest_arctanh()\n\n@test\ndef test_rint():\n    assert (np.rint(np.array([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0\n                              ])) == np.array([-2., -2., -0., 0., 2., 2.,\n                                               2.])).all()\n\ntest_rint()\n\n@test\ndef test_floor():\n    assert (np.floor(np.array([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0\n                               ])) == np.array([-2., -2., -1., 0., 1., 1.,\n                                                2.])).all()\n\ntest_floor()\n\n@test\ndef test_ceil():\n    assert (np.ceil(np.array([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0\n                              ])) == np.array([-1., -1., -0., 1., 2., 2.,\n                                               2.])).all()\n\ntest_ceil()\n\n@test\ndef test_trunc():\n    assert (np.trunc(np.array([-1.7, -1.5, -0.2, 0.2, 1.5, 1.7, 2.0\n                               ])) == np.array([-1., -1., -0., 0., 1., 1.,\n                                                2.])).all()\n\ntest_trunc()\n\n@test\ndef test_prod():\n    a = np.array([[1., 2.], [3., 4.]])\n    assert a.prod() == 24.0\n    assert (a.prod(axis=0) == np.array([3., 8.])).all()\n    assert (a.prod(axis=1) == np.array([2., 12.])).all()\n    assert np.array([2]).prod() == 2.\n    assert np.array([1, 4, 5, 6, 0, 9]).prod() == 0\n    assert np.array([1000000, 1000000, 1000000]).prod() == 1000000000000000000\n    assert np.array(empty(0, float)).prod() == 1.\n\ntest_prod()\n\n@test\ndef test_sum():\n    assert np.array([0.5, 1.5]).sum() == 2.0\n    assert np.array([0.5, 0.7, 0.2, 1.5]).sum() == 2.9\n    assert np.array([[0, 1], [0, 5]]).sum() == 6\n    assert np.array([[0, 1], [0, -5]]).sum() == -4\n    assert (np.array([[0, 1], [0, 5]]).sum(axis=0) == np.array([0, 6])).all()\n    assert (np.array([[0, 1], [0, 5]]).sum(axis=1) == np.array([1, 5])).all()\n    assert np.array([10**9, 10**9]).sum() == 2000000000\n\n#    assert np.array([10**(-9), 10**(-9)]).sum() == 2e-09\n\ntest_sum()\n\n@test\ndef test_gradient_basic():\n    # Make sure that gradient supports subclasses like masked arrays\n    v = [[1, 1], [3, 4]]\n    x = np.array(v)\n    dx = [np.array([[2., 3.], [2., 3.]]), np.array([[0., 0.], [1., 1.]])]\n    assert array_equal(gradient(x), dx)\n    assert array_equal(gradient(v), dx)\n\ntest_gradient_basic()\n\n@test\ndef test_gradient_datetime64():\n    # Make sure gradient() can handle special types like datetime64\n    x = np.array([\n        '1910-08-16', '1910-08-11', '1910-08-10', '1910-08-12', '1910-10-12',\n        '1910-12-12', '1912-12-12'\n    ],\n                 dtype=datetime64['D', 1])\n\n    x1 = np.array([\n        '1910-08-16', '1910-08-11', '1910-08-10', '1910-08-12', '1910-10-12',\n        '1910-12-12', '1912-12-12'\n    ],\n                  dtype=datetime64['Y', 1])\n\n    dx = np.array([-5, -3, 0, 31, 61, 396, 731], dtype=timedelta64['D', 1])\n\n    dx1 = np.array([0, 0, 0, 0, 0, 0, 0], dtype=timedelta64['Y', 1])\n\n    assert array_equal(gradient(x), dx)\n    assert array_equal(gradient(x1, [2, 3, 7, 9, 12, 56, 34]), dx1)\n\n    assert isinstance(gradient(x).dtype, np.timedelta64['D', 1])\n    assert isinstance(\n        gradient(x1, [2, 3, 7, 9, 12, 56, 34]).dtype, np.timedelta64['Y', 1])\n\ntest_gradient_datetime64()\n\n@test\ndef test_gradient_timedelta64():\n    # Make sure gradient() can handle special types like timedelta64\n    x = np.array([-5, -3, 10, 12, 61, 321, 300], dtype=np.timedelta64['D', 1])\n\n    dx = np.array([2, 7, 7, 25, 154, 119, -21], dtype=np.timedelta64['D', 1])\n\n    dx1 = np.array([2, 2, 2, 7, 15, -5, 0], dtype=np.timedelta64['D', 1])\n\n    assert array_equal(gradient(x), dx)\n    assert array_equal(gradient(x, [2, 3, 7, 9, 12, 56, 34]), dx1)\n\n    assert isinstance(gradient(x).dtype, np.timedelta64['D', 1])\n    assert isinstance(\n        gradient(x, [2, 3, 7, 9, 12, 56, 34]).dtype, np.timedelta64['D', 1])\n\ntest_gradient_timedelta64()\n\n@test\ndef test_gradient_args():\n    dx = np.cumsum(np.ones(5))\n    dx_uneven = [1., 2., 5., 9., 11.]\n    f_2d = np.arange(25).reshape(5, 5)\n\n    # distances must be scalars or have size equal to gradient[axis]\n    assert np.isclose(\n        np.gradient(np.arange(5), 3.),\n        np.array([0.333333, 0.333333, 0.333333, 0.333333, 0.333333])).all()\n    assert np.isclose(\n        np.gradient(np.arange(5), np.array(3.)),\n        np.array([0.333333, 0.333333, 0.333333, 0.333333, 0.333333])).all()\n    assert np.isclose(np.gradient(np.arange(5), dx),\n                      np.array([1., 1., 1., 1., 1.])).all()\n    # dy is set equal to dx because scalar\n    np.gradient(f_2d, 1.5)\n\n    np.gradient(f_2d, dx_uneven, dx_uneven)\n    # mix between even and uneven spaces and\n    # mix between scalar and vector\n    np.gradient(f_2d, dx, 2)\n\n    # 2D but axis specified\n    np.gradient(f_2d, dx, axis=1)\n\ntest_gradient_args()\n\n@test\ndef test_gradient_badargs():\n    f_2d = np.arange(25).reshape(5, 5)\n    x = np.cumsum(np.ones(5))\n\n    try:\n        np.gradient(x, axis=3)\n        assert False\n    except AxisError:\n        pass\n    try:\n        np.gradient(f_2d, 1, np.ones(2))\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.gradient(f_2d, np.ones(2), np.ones(2))\n        assert False\n    except ValueError:\n        pass\n\n    # TODO: Below tests returns comiplation errors and\n    # will be active once test infra in updated to handle them.\n\n    # try:\n    #     np.gradient(f_2d, x)\n    #     assert False\n    # except ValueError:\n    #     pass\n\n    # try:\n    #     np.gradient(f_2d, x, axis=(0,1))\n    #     assert False\n    # except ValueError:\n    #     pass\n\n    # try:\n    #     np.gradient(f_2d, x, x, x)\n    #     assert False\n    # except ValueError:\n    #     pass\n\n    # try:\n    #     np.gradient(f_2d, 1, 1, 1)\n    #     assert False\n    # except ValueError:\n    #     pass\n\n    # try:\n    #     np.gradient(f_2d, x, x, axis=1)\n    #     assert False\n    # except ValueError:\n    #     pass\n\n    # try:\n    #     np.gradient(f_2d, 1, 1, axis=1)\n    #     assert False\n    # except ValueError:\n\ntest_gradient_badargs()\n\n@test\ndef test_gradient_second_order_accurate():\n    # Testing that the relative numerical error is less that 3% for\n    # this example problem. This corresponds to second order\n    # accurate finite differences for all interior and boundary\n    # points.\n    x = np.linspace(0, 1, 10)\n    dx = x[1] - x[0]\n    y = 2 * x**3 + 4 * x**2 + 2 * x\n    analytical = 6 * x**2 + 8 * x + 2\n    num_error = np.abs((np.gradient(y, dx, edge_order=2) / analytical) - 1)\n    assert (np.all(num_error < 0.03) == True)\n\n    #test with unevenly spaced\n    import numpy.random as rnd\n    rnd.seed(0)\n    x = np.sort(rnd.random(10))\n    y = 2 * x**3 + 4 * x**2 + 2 * x\n    analytical = 6 * x**2 + 8 * x + 2\n    num_error = np.abs((np.gradient(y, x, edge_order=2) / analytical) - 1)\n    assert (np.all(num_error < 0.03) == True)\n\ntest_gradient_second_order_accurate()\n\n@test\ndef test_gradient_spacing():\n    f = np.array([0, 2., 3., 4., 5., 5.])\n    f = np.tile(f, (6, 1)) + f.reshape(-1, 1)\n    x_uneven = np.array([0., 0.5, 1., 3., 5., 7.])\n    x_even = np.arange(6.)\n    fdx_even_ord1 = np.tile([2., 1.5, 1., 1., 0.5, 0.], (6, 1))\n    fdx_even_ord2 = np.tile([2.5, 1.5, 1., 1., 0.5, -0.5], (6, 1))\n    fdx_uneven_ord1 = np.tile([4., 3., 1.7, 0.5, 0.25, 0.], (6, 1))\n    fdx_uneven_ord2 = np.tile([5., 3., 1.7, 0.5, 0.25, -0.25], (6, 1))\n    # evenly spaced\n    for edge_order, exp_res in [(1, fdx_even_ord1), (2, fdx_even_ord2)]:\n        res1 = np.gradient(f, 1., axis=(0, 1), edge_order=edge_order)\n        res2 = np.gradient(f,\n                           x_even,\n                           x_even,\n                           axis=(0, 1),\n                           edge_order=edge_order)\n        res3 = np.gradient(f, x_even, x_even, axis=None, edge_order=edge_order)\n        assert array_equal(res1, res2)\n        assert array_equal(res2, res3)\n        assert array_equal(res1[0], exp_res.T)\n        assert array_equal(res1[1], exp_res)\n\n        res1 = gradient(f, 1., axis=0, edge_order=edge_order)\n        res2 = gradient(f, x_even, axis=0, edge_order=edge_order)\n        assert (res1.shape == res2.shape)\n        assert array_equal(res2, exp_res.T)\n\n        res1 = gradient(f, 1., axis=1, edge_order=edge_order)\n        res2 = gradient(f, x_even, axis=1, edge_order=edge_order)\n        assert (res1.shape == res2.shape)\n        assert array_equal(res2, exp_res)\n\n        # unevenly spaced\n    for edge_order, exp_res in [(1, fdx_uneven_ord1), (2, fdx_uneven_ord2)]:\n        res1 = gradient(f,\n                        x_uneven,\n                        x_uneven,\n                        axis=(0, 1),\n                        edge_order=edge_order)\n        res2 = gradient(f,\n                        x_uneven,\n                        x_uneven,\n                        axis=None,\n                        edge_order=edge_order)\n        assert array_equiv(res1, res2)\n        assert np.isclose(res1[0], exp_res.T).all()\n        assert np.isclose(res1[1], exp_res).all()\n\n        res1 = gradient(f, x_uneven, axis=0, edge_order=edge_order)\n        assert isclose(res1, exp_res.T).all()\n\n        res1 = gradient(f, x_uneven, axis=1, edge_order=edge_order)\n        assert isclose(res1, exp_res).all()\n\n    # mixed\n    res1 = gradient(f, x_even, x_uneven, axis=(0, 1), edge_order=1)\n    res2 = gradient(f, x_uneven, x_even, axis=(1, 0), edge_order=1)\n    assert array_equal(res1[0], res2[1])\n    assert array_equal(res1[1], res2[0])\n    assert array_equal(res1[0], fdx_even_ord1.T)\n    assert np.isclose(res1[1], fdx_uneven_ord1).all()\n\n    res1 = gradient(f, x_even, x_uneven, axis=(0, 1), edge_order=2)\n    res2 = gradient(f, x_uneven, x_even, axis=(1, 0), edge_order=2)\n    assert array_equal(res1[0], res2[1])\n    assert array_equal(res1[1], res2[0])\n    assert array_equal(res1[0], fdx_even_ord2.T)\n    assert np.isclose(res1[1], fdx_uneven_ord2).all()\n\ntest_gradient_spacing()\n\n@test\ndef test_gradient_specific_axes():\n    # Testing that gradient can work on a given axis only\n    v = [[1, 1], [3, 4]]\n    x = np.array(v)\n    dx = [np.array([[2., 3.], [2., 3.]]), np.array([[0., 0.], [1., 1.]])]\n    assert array_equal(gradient(x, axis=0), dx[0])\n    assert array_equal(gradient(x, axis=1), dx[1])\n    assert array_equal(gradient(x, axis=-1), dx[1])\n    assert array_equal(gradient(x, axis=(1, 0)), [dx[1], dx[0]])\n\n    # test axis=None which means all axes\n    assert array_equal(gradient(x, axis=None), [dx[0], dx[1]])\n    # and is the same as no axis keyword given\n    assert array_equal(gradient(x, axis=None), gradient(x))\n\n    # test vararg order\n    assert array_equal(gradient(x, 2, 3, axis=(1, 0)),\n                       [dx[1] / 2.0, dx[0] / 3.0])\n    # test maximal number of varargs\n\n    try:\n        np.gradient(x, axis=3)\n        assert False\n    except AxisError:\n        pass\n\n    try:\n        np.gradient(x, axis=-3)\n        assert False\n    except AxisError:\n        pass\n\ntest_gradient_specific_axes()\n\n@test\ndef test_gradient_inexact_dtypes():\n    x = np.array([1, 2, 3], dtype=float16)\n    assert isinstance(gradient(x).dtype, np.diff(x).dtype)\n\n    x = np.array([1, 2, 3], dtype=float32)\n    assert isinstance(gradient(x).dtype, np.diff(x).dtype)\n\n    x = np.array([1, 2, 3], dtype=float64)\n    assert isinstance(gradient(x).dtype, np.diff(x).dtype)\n\n    f = np.array([1.2, 3.4, -1.0, 2.5], np.float32)\n    d = np.array([1.0, 2.1, 2.9, 6.1], np.float32)\n\n    res = np.gradient(f, d)\n    assert np.isclose(res, np.array([2, -2.3421, -4.18125, 1.09375])).all()\n    assert isinstance(gradient(f, d).dtype, np.diff(f).dtype)\n\n    f = np.array([1.2, 3.4, -1.0, 2.5], np.float32)\n    d = np.array([1.0, 2.1, 2.9, 6.1], np.float16)\n\n    res = np.gradient(f, d)\n    assert np.isclose(res, np.array([2.00071, -2.33627, -4.17639,\n                                     1.09335])).all()\n    assert isinstance(gradient(f, d).dtype, np.diff(f).dtype)\n\n    #test when dx is linear\n    f = np.array([1.2, 3.4, -1.0, 2.5], np.float16)\n    d = np.array([1.0, 2.1, 2.9, 6.1], np.float32)\n\n    res = np.gradient(f, d)\n    assert np.isclose(res, np.array([2, -2.33594, -4.17969, 1.09375])).all()\n    assert isinstance(gradient(f, d).dtype, np.diff(f).dtype)\n\n    #test when dx is scaler\n    f = np.array([1.2, 3.4, -1.0, 2.5], np.float16)\n    d = np.float32(2.0)\n\n    res = np.gradient(f, d)\n    assert np.isclose(res, np.array([1.09961, -0.549805, -0.225098,\n                                     1.75])).all()\n    assert isinstance(gradient(f, d).dtype, np.diff(f).dtype)\n\ntest_gradient_inexact_dtypes()\n\n@test\ndef test_gradient_values():\n    # needs at least 2 points for edge_order ==1\n    np.gradient(np.arange(2), edge_order=1)\n    # needs at least 3 points for edge_order ==1\n    np.gradient(np.arange(3), edge_order=2)\n\n    try:\n        np.gradient(np.arange(0), edge_order=1)\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.gradient(np.arange(0), edge_order=2)\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.gradient(np.arange(1), edge_order=1)\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.gradient(np.arange(1), edge_order=2)\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.gradient(np.arange(2), edge_order=2)\n        assert False\n    except ValueError:\n        pass\n\ntest_gradient_values()\n\n@test\ndef test_gradient_x_decreasing_unsigned(x_dtype: type):\n    x = np.array([3, 2, 1], x_dtype)\n    f = np.array([0, 2, 4])\n    dfdx = np.gradient(f, x)\n    assert array_equal(dfdx, [-2] * len(x))\n\ntest_gradient_x_decreasing_unsigned(x_dtype=float)\ntest_gradient_x_decreasing_unsigned(x_dtype=int)\ntest_gradient_x_decreasing_unsigned(x_dtype=double)\ntest_gradient_x_decreasing_unsigned(x_dtype=int64)\ntest_gradient_x_decreasing_unsigned(x_dtype=int8)\ntest_gradient_x_decreasing_unsigned(x_dtype=int32)\ntest_gradient_x_decreasing_unsigned(x_dtype=int16)\n\n@test\ndef test_gradient_f_signed_int_big_jump(f_dtype: type):\n    maxint = 2.2250738585072014e-308\n    x = np.array([1, 3])\n    f = np.array([-1, maxint], dtype=f_dtype)\n    dfdx = gradient(f, x)\n    assert array_equal(dfdx, [0.5] * len(x))\n\ntest_gradient_f_signed_int_big_jump(float)\n\n@test\ndef test_gradient_f_decreasing_unsigned_int(f_dtype: type):\n    f = np.array([5, 4, 3, 2, 1], dtype=f_dtype)\n    g = np.gradient(f)\n    assert array_equal(g, [-1] * len(f))\n\ntest_gradient_f_decreasing_unsigned_int(f_dtype=uint)\ntest_gradient_f_decreasing_unsigned_int(f_dtype=uint8)\ntest_gradient_f_decreasing_unsigned_int(f_dtype=uint16)\ntest_gradient_f_decreasing_unsigned_int(f_dtype=uint32)\ntest_gradient_f_decreasing_unsigned_int(f_dtype=uint64)\ntest_gradient_f_decreasing_unsigned_int(f_dtype=float)\ntest_gradient_f_decreasing_unsigned_int(f_dtype=double)\n\n@test\ndef test_gradient():\n\n    def gradient_test_helper(res, out):\n        for i in range(len(res)):\n            assert np.isclose(res[i], out[i]).all()\n\n    #(1d,2d,3d) without spacing axis is none, edge_order = 1\n    assert np.isclose(np.gradient(np.array([3, 5, 8])), np.array([2, 2.5,\n                                                                  3])).all()\n    assert np.isclose(\n        np.gradient(\n            np.array([[[1, 2], [4, 5]], [[10, 11], [13, 14]],\n                      [[19, 20], [22, 23]]])),\n        np.array([[[[9, 9], [9, 9]], [[9, 9], [9, 9]], [[9, 9], [9, 9]]],\n                  [[[3, 3], [3, 3]], [[3, 3], [3, 3]], [[3, 3], [3, 3]]],\n                  [[[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1,\n                                                                 1]]]])).all()\n\n    #(1d,2d,3d) without spacing axis is none, edge_order = 2\n    assert np.isclose(np.gradient(np.array([3, 5, 8]), edge_order=2),\n                      np.array([1.5, 2.5, 3.5])).all()\n    assert np.isclose(\n        np.gradient(np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 10],\n                              [11, 12, 13, 14, 15], [16, 17, 18, 19, 20],\n                              [21, 22, 23, 24, 25]]),\n                    edge_order=2),\n        np.array([[[5, 5, 5, 5, 5], [5, 5, 5, 5, 5], [5, 5, 5, 5, 5],\n                   [5, 5, 5, 5, 5], [5, 5, 5, 5, 5]],\n                  [[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1],\n                   [1, 1, 1, 1, 1], [1, 1, 1, 1, 1]]])).all()\n    assert np.isclose(\n        np.gradient(np.array([[[1, 2, 3], [4, 5, 6], [7, 8, 9]],\n                              [[10, 11, 12], [13, 14, 15], [16, 17, 18]],\n                              [[19, 20, 21], [22, 23, 24], [25, 26, 27]]]),\n                    edge_order=2),\n        np.array([[[[9, 9, 9], [9, 9, 9], [9, 9, 9]],\n                   [[9, 9, 9], [9, 9, 9], [9, 9, 9]],\n                   [[9, 9, 9], [9, 9, 9], [9, 9, 9]]],\n                  [[[3, 3, 3], [3, 3, 3], [3, 3, 3]],\n                   [[3, 3, 3], [3, 3, 3], [3, 3, 3]],\n                   [[3, 3, 3], [3, 3, 3], [3, 3, 3]]],\n                  [[[1, 1, 1], [1, 1, 1], [1, 1, 1]],\n                   [[1, 1, 1], [1, 1, 1], [1, 1, 1]],\n                   [[1, 1, 1], [1, 1, 1], [1, 1, 1]]]])).all()\n\n    #(1d,2d,3d) without spacing axis is not none, edge_order = 1\n    assert np.isclose(np.gradient(np.array([3, 5, 8]), axis=-1),\n                      np.array([2, 2.5, 3])).all()\n    assert np.isclose(np.gradient(np.array([[3, 5, 8], [5, 7, 9]]), axis=-1),\n                      np.array([[2, 2.5, 3], [2, 2.0, 2]])).all()\n    assert np.isclose(\n        np.gradient(np.array([[[1, 2], [4, 5]], [[10, 11], [13, 14]],\n                              [[19, 20], [22, 23]]]),\n                    axis=-1),\n        np.array([[[1, 1], [1, 1]], [[1, 1], [1, 1]], [[1, 1], [1,\n                                                                1]]])).all()\n\n    assert np.isclose(\n        np.gradient(np.array([1, 2, 4, 7, 11]), axis=0, edge_order=1),\n        np.array([1., 1.5, 2.5, 3.5, 4.])).all()\n    assert np.isclose(\n        np.gradient(np.array([[2.5, 3, 4], [7, 11, 8.9]]),\n                    axis=1,\n                    edge_order=1), np.array([[0.5, 0.75, 1.], [4., 0.95,\n                                                               -2.1]])).all()\n    assert np.isclose(\n        np.gradient(np.array([[[2.5, 3.566, 4.47], [7.98, 11.67, 8.9]],\n                              [[1.89, 9, 4.23], [3.55, 11.877, 10.9]]]),\n                    axis=2,\n                    edge_order=1),\n        np.array([[[1.066, 0.985, 0.904], [3.69, 0.46, -2.77]],\n                  [[7.11, 1.17, -4.77], [8.327, 3.675, -0.977]]])).all()\n\n    assert np.isclose(\n        np.gradient(np.array([1, 2, 4, 7, 11]), axis=0, edge_order=2),\n        np.array([0.5, 1.5, 2.5, 3.5, 4.5])).all()\n    assert np.isclose(\n        np.gradient(np.array([[2.5, 3, 4], [7, 11, 8.9]]),\n                    axis=1,\n                    edge_order=2),\n        np.array([[0.25, 0.75, 1.25], [7.05, 0.95, -5.15]])).all()\n\n    assert np.isclose(\n        np.gradient(np.array([[[2.5, 3.566, 4.47], [7.98, 11.67, 8.9]],\n                              [[1.89, 9, 4.23], [3.55, 11.877, 10.9]]]),\n                    axis=2,\n                    edge_order=2),\n        np.array([[[1.147, 0.985, 0.823], [6.92, 0.46, -6.]],\n                  [[13.05, 1.17, -10.71], [12.979, 3.675, -5.629]]])).all()\n    assert np.isclose(\n        np.gradient(np.array([1, 2, 4, 7, 11]), 2.5, axis=0, edge_order=1),\n        np.array([0.4, 0.6, 1., 1.4, 1.6])).all()\n    assert np.isclose(\n        np.gradient(np.array([[2.5, 3, 4], [7, 11, 8.9]]),\n                    9.45,\n                    axis=1,\n                    edge_order=1),\n        np.array([[0.05291005, 0.07936508, 0.10582011],\n                  [0.42328042, 0.1005291, -0.22222222]])).all()\n\n    assert np.isclose(\n        np.gradient(np.array([[[2.5, 3.566, 4.47], [7.98, 11.67, 8.9]],\n                              [[1.89, 9, 4.23], [3.55, 11.877, 10.9]]]),\n                    6.7,\n                    axis=2,\n                    edge_order=1),\n        np.array([[[0.15910448, 0.14701493, 0.13492537],\n                   [0.55074627, 0.06865672, -0.41343284]],\n                  [[1.06119403, 0.17462687, -0.7119403],\n                   [1.24283582, 0.54850746, -0.1458209]]])).all()\n    assert np.isclose(\n        np.gradient(np.array([1, 2, 4, 7, 11]), 2.5, axis=0, edge_order=2),\n        np.array([0.2, 0.6, 1., 1.4, 1.8])).all()\n    assert np.isclose(\n        np.gradient(np.array([[2.5, 3, 4], [7, 11, 8.9]]),\n                    9.45,\n                    axis=1,\n                    edge_order=2),\n        np.array([[0.02645503, 0.07936508, 0.13227513],\n                  [0.74603175, 0.1005291, -0.54497354]])).all()\n    assert np.isclose(\n        np.gradient(np.array([[[2.5, 3.566, 4.47], [7.98, 11.67, 8.9]],\n                              [[1.89, 9, 4.23], [3.55, 11.877, 10.9]]]),\n                    6.7,\n                    axis=2,\n                    edge_order=2),\n        np.array([[[0.17119403, 0.14701493, 0.12283582],\n                   [1.03283582, 0.06865672, -0.89552239]],\n                  [[1.94776119, 0.17462687, -1.59850746],\n                   [1.93716418, 0.54850746, -0.84014925]]])).all()\n\n    assert np.isclose(\n        np.gradient(np.array([1, 2, 4, 7, 11]), axis=None, edge_order=1),\n        np.array([1., 1.5, 2.5, 3.5, 4.])).all()\n\n    # arr_2d = [[2, 4], [3, 6]]\n    res = np.gradient(np.array([[2.5, 3, 4], [7, 11, 8.9]]),\n                      axis=None,\n                      edge_order=1)\n\n    out = []\n    a = np.array([[4.5, 8., 4.9], [4.5, 8., 4.9]])\n    out.append(a)\n    b = np.array([[0.5, 0.75, 1.], [4., 0.95, -2.1]])\n    out.append(b)\n\n    assert np.array_equal(res[0], out[0])\n    gradient_test_helper(res, out)\n\n    res = np.gradient(np.array([[[2.5, 3.566, 4.47], [7.98, 11.67, 8.9]],\n                                [[1.89, 9, 4.23], [3.55, 11.877, 10.9]]]),\n                      axis=None,\n                      edge_order=1)\n    out = []\n    a = np.array([[[-0.61, 5.434, -0.24], [-4.43, 0.207, 2.]],\n                  [[-0.61, 5.434, -0.24], [-4.43, 0.207, 2.]]])\n    out.append(a)\n    b = np.array([[[5.48, 8.104, 4.43], [5.48, 8.104, 4.43]],\n                  [[1.66, 2.877, 6.67], [1.66, 2.877, 6.67]]])\n    out.append(b)\n    c = np.array([[[1.066, 0.985, 0.904], [3.69, 0.46, -2.77]],\n                  [[7.11, 1.17, -4.77], [8.327, 3.675, -0.977]]])\n    out.append(c)\n    gradient_test_helper(res, out)\n\n    assert np.isclose(\n        np.gradient(np.array([1, 2, 4, 7, 11]), axis=None, edge_order=2),\n        np.array([0.5, 1.5, 2.5, 3.5, 4.5])).all()\n\n    # arr_2d = [[2, 4], [3, 6]]\n    res = np.gradient(np.array([[2.5, 3, 4], [7, 11, 8.9], [3, 4, 8.9]]),\n                      axis=None,\n                      edge_order=2)\n    out = []\n    a = np.array([[8.75, 15.5, 7.35], [0.25, 0.5, 2.45], [-8.25, -14.5,\n                                                          -2.45]])\n    out.append(a)\n    b = np.array([[0.25, 0.75, 1.25], [7.05, 0.95, -5.15], [-0.95, 2.95,\n                                                            6.85]])\n    out.append(b)\n    gradient_test_helper(res, out)\n\n    res = np.gradient(np.array([[[2.5, 3.566, 4.47], [7.98, 11.67, 8.9],\n                                 [7.98, 11.67, 8.9]],\n                                [[1.89, 9, 4.23], [3.55, 11.877, 10.9],\n                                 [1.89, 9, 4.23]],\n                                [[2.5, 3.566, 4.47], [7.98, 11.67, 8.9],\n                                 [2.5, 3.566, 4.47]],\n                                [[1.89, 9, 4.23], [3.55, 11.877, 10.9],\n                                 [1.89, 9, 4.23]]]),\n                      axis=None,\n                      edge_order=2)\n    out = []\n    a = np.array([[[-1.22, 10.868, -0.48], [-8.86, 0.414, 4.],\n                   [-9.44, -1.288, -7.125]],\n                  [[0., 0., 0.], [0., 0., 0.], [-2.74, -4.052, -2.215]],\n                  [[0., 0., 0.], [0., 0., 0.], [0., 0., 0.]],\n                  [[-1.22, 10.868, -0.48], [-8.86, 0.414, 4.],\n                   [-1.22, 10.868, -0.48]]])\n    out.append(a)\n    b = np.array([[[8.22, 12.156, 6.645], [2.74, 4.052, 2.215],\n                   [-2.74, -4.052, -2.215]],\n                  [[3.32, 5.754, 13.34], [0., 0., 0.], [-3.32, -5.754,\n                                                        -13.34]],\n                  [[10.96, 16.208, 8.86], [0., 0., 0.],\n                   [-10.96, -16.208, -8.86]],\n                  [[3.32, 5.754, 13.34], [0., 0., 0.], [-3.32, -5.754,\n                                                        -13.34]]])\n    out.append(b)\n    c = np.array([[[1.147, 0.985, 0.823], [6.92, 0.46, -6.], [6.92, 0.46,\n                                                              -6.]],\n                  [[13.05, 1.17, -10.71], [12.979, 3.675, -5.629],\n                   [13.05, 1.17, -10.71]],\n                  [[1.147, 0.985, 0.823], [6.92, 0.46, -6.],\n                   [1.147, 0.985, 0.823]],\n                  [[13.05, 1.17, -10.71], [12.979, 3.675, -5.629],\n                   [13.05, 1.17, -10.71]]])\n    out.append(c)\n\n    gradient_test_helper(res, out)\n\n    assert np.isclose(np.gradient(np.array([3j, 5j, 8j])),\n                      np.array([0. + 2.j, 0. + 2.5j, 0. + 3.j])).all()\n    assert np.array_equal(\n        np.gradient(np.array([[3j, 5j, 8j], [5j, 7j, 9j]])),\n        [[[2j, 2j, 1j], [2j, 2j, 1j]], [[2j, 2.5j, 3j], [2j, 2j, 2j]]])\n    assert np.array_equal(\n        np.gradient(((((1j, 2), (4, 5j)), ((10j, 11), (13, 14j)),\n                      ((19, 20), (22, 23))))),\n        [[[[0. + 9.j, 9. + 0.j], [9. + 0.j, 0. + 9.j]],\n          [[9.5 - 0.5j, 9. + 0.j], [9. + 0.j, 11.5 - 2.5j]],\n          [[19. - 10.j, 9. + 0.j], [9. + 0.j, 23. - 14.j]]],\n         [[[4. - 1.j, -2. + 5.j], [4. - 1.j, -2. + 5.j]],\n          [[13. - 10.j, -11. + 14.j], [13. - 10.j, -11. + 14.j]],\n          [[3. + 0.j, 3. + 0.j], [3. + 0.j, 3. + 0.j]]],\n         [[[2. - 1.j, 2. - 1.j], [-4. + 5.j, -4. + 5.j]],\n          [[11. - 10.j, 11. - 10.j], [-13. + 14.j, -13. + 14.j]],\n          [[1. + 0.j, 1. + 0.j], [1. + 0.j, 1. + 0.j]]]])\n\n    # (1d,2d,3d) without spacing axis is none, edge_order = 2\n    assert np.isclose(np.gradient(np.array([3j, 5j, 8j]), edge_order=2),\n                      np.array([0. + 1.5j, 0. + 2.5j, 0. + 3.5j])).all()\n    assert np.array_equal(\n        np.gradient(\n            ((1j, 2j, 3, 4j, 5), (6j, 7, 8, 9j, 10), (11j, 12, 13j, 14, 15j),\n             (16j, 17j, 18, 19j, 20), (21j, 22, 23, 24, 25j)),\n            edge_order=2),\n        [[[5j, (8 - 3j), (11.5 - 6.5j), (-7 + 12j), (12.5 - 7.5j)],\n          [5j, (6 - 1j), (-1.5 + 6.5j), (7 - 2j),\n           (-2.5 + 7.5j)], [5j, (-3.5 + 8.5j), (5 + 0j), 5j, (5 + 0j)],\n          [5j, (5 + 0j), (11.5 - 6.5j), (5 + 0j), 5j],\n          [5j, (39 - 34j), (-1.5 + 6.5j), (43 - 38j), (-40 + 45j)]],\n         [[(-1.5 + 2.5j), (1.5 - 0.5j), 1j, (1 + 0j),\n           (9 - 8j)], [(10 - 9j), (4 - 3j), (-3.5 + 4.5j), (1 + 0j),\n                       (19 - 18j)], [(24 - 23j), 1j, (1 + 0j), 1j,\n                                     (-28 + 29j)],\n          [(-9 + 10j), (9 - 8j), 1j, (1 + 0j), (39 - 38j)],\n          [(32.5 - 31.5j), (11.5 - 10.5j), (1 + 0j), (-11.5 + 12.5j),\n           (-36.5 + 37.5j)]]])\n    assert np.array_equal(\n        np.gradient(np.array([[[1j, 2j, 3j], [4j, 5j, 6j], [7j, 8j, 9j]],\n                              [[10j, 11j, 12j], [13j, 14j, 15j],\n                               [16j, 17j, 18j]],\n                              [[19j, 20j, 21j], [22j, 23j, 24j],\n                               [25j, 26j, 27j]]]),\n                    edge_order=2),\n        [[[[0. + 9.j, 0. + 9.j, 0. + 9.j], [0. + 9.j, 0. + 9.j, 0. + 9.j],\n           [0. + 9.j, 0. + 9.j, 0. + 9.j]],\n          [[0. + 9.j, 0. + 9.j, 0. + 9.j], [0. + 9.j, 0. + 9.j, 0. + 9.j],\n           [0. + 9.j, 0. + 9.j, 0. + 9.j]],\n          [[0. + 9.j, 0. + 9.j, 0. + 9.j], [0. + 9.j, 0. + 9.j, 0. + 9.j],\n           [0. + 9.j, 0. + 9.j, 0. + 9.j]]],\n         [[[0. + 3.j, 0. + 3.j, 0. + 3.j], [0. + 3.j, 0. + 3.j, 0. + 3.j],\n           [0. + 3.j, 0. + 3.j, 0. + 3.j]],\n          [[0. + 3.j, 0. + 3.j, 0. + 3.j], [0. + 3.j, 0. + 3.j, 0. + 3.j],\n           [0. + 3.j, 0. + 3.j, 0. + 3.j]],\n          [[0. + 3.j, 0. + 3.j, 0. + 3.j], [0. + 3.j, 0. + 3.j, 0. + 3.j],\n           [0. + 3.j, 0. + 3.j, 0. + 3.j]]],\n         [[[0. + 1.j, 0. + 1.j, 0. + 1.j], [0. + 1.j, 0. + 1.j, 0. + 1.j],\n           [0. + 1.j, 0. + 1.j, 0. + 1.j]],\n          [[0. + 1.j, 0. + 1.j, 0. + 1.j], [0. + 1.j, 0. + 1.j, 0. + 1.j],\n           [0. + 1.j, 0. + 1.j, 0. + 1.j]],\n          [[0. + 1.j, 0. + 1.j, 0. + 1.j], [0. + 1.j, 0. + 1.j, 0. + 1.j],\n           [0. + 1.j, 0. + 1.j, 0. + 1.j]]]])\n\n    # #(1d,2d,3d) without spacing axis is not none, edge_order = 1\n    assert np.isclose(np.gradient(np.array([3j, 5j, 8j]), axis=-1),\n                      np.array([0. + 2.j, 0. + 2.5j, 0. + 3.j])).all()\n    assert np.array_equal(\n        np.gradient(np.array([[3j, 5j, 8j], [5j, 7j, 9j]]), axis=-1),\n        [[0. + 2.j, 0. + 2.5j, 0. + 3.j], [0. + 2.j, 0. + 2.j, 0. + 2.j]])\n    assert np.array_equal(\n        np.gradient(np.array([[[1j, 2j], [4j, 5j]], [[10j, 11j], [13j, 14j]],\n                              [[19j, 20j], [22j, 23j]]]),\n                    axis=-1), [[[0. + 1.j, 0. + 1.j], [0. + 1.j, 0. + 1.j]],\n                               [[0. + 1.j, 0. + 1.j], [0. + 1.j, 0. + 1.j]],\n                               [[0. + 1.j, 0. + 1.j], [0. + 1.j, 0. + 1.j]]])\n\n    assert np.isclose(\n        np.gradient(np.array([1j, 2j, 4j, 7j, 11j]), axis=0, edge_order=1),\n        np.array([0. + 1.j, 0. + 1.5j, 0. + 2.5j, 0. + 3.5j, 0. + 4.j])).all()\n    assert np.isclose(\n        np.gradient(np.array([[2.5j, 3j, 4j], [7j, 11j, 8.9j]]),\n                    axis=1,\n                    edge_order=1),\n        np.array([[0. + 0.5j, 0. + 0.75j, 0. + 1.j],\n                  [0. + 4.j, 0. + 0.95j, 0. - 2.1j]])).all()\n    assert np.isclose(\n        np.gradient(np.array([[[2.5j, 3.566j, 4.47j], [7.98j, 11.67j, 8.9j]],\n                              [[1.89j, 9j, 4.23j], [3.55j, 11.877j, 10.9j]]]),\n                    axis=2,\n                    edge_order=1),\n        np.array([[[0. + 1.066j, 0. + 0.985j, 0. + 0.904j],\n                   [0. + 3.69j, 0. + 0.46j, 0. - 2.77j]],\n                  [[0. + 7.11j, 0. + 1.17j, 0. - 4.77j],\n                   [0. + 8.327j, 0. + 3.675j, 0. - 0.977j]]])).all()\n\n    assert np.isclose(\n        np.gradient(np.array([1j, 5 + 2j, 2 + 4j, 6 + 7j, 11j]),\n                    axis=0,\n                    edge_order=2),\n        np.array([9. + 0.5j, 1. + 1.5j, 0.5 + 2.5j, -1. + 3.5j,\n                  -11. + 4.5j])).all()\n    assert np.isclose(\n        np.gradient(np.array([[2.5 + 3.6j, 1.77 + 5.3j, 4.1 + 8.9j],\n                              [7.7j, 11.23j, 8.9j]]),\n                    axis=1,\n                    edge_order=2),\n        np.array([[-2.26 + 0.75j, 0.8 + 2.65j, 3.86 + 4.55j],\n                  [0. + 6.46j, 0. + 0.6j, 0. - 5.26j]])).all()\n\n    assert np.isclose(\n        np.gradient(np.array([[[2.5j, 3.566 + 3.45j, 4.47j],\n                               [7.98 + 2.34j, 11.67j + 4.13j, 8.9j]],\n                              [[1.89j, 9.59 + 7.34j, 4.23j],\n                               [3.55 + 5.3j, 11.877 + 6.987j, 10.9 + 6.98j]]]),\n                    axis=2,\n                    edge_order=2),\n        np.array([[[(7.132 + 0.915j), 0.985j, (-7.132 + 1.055j)],\n                   [(-11.97 + 23.64j), (-3.99 + 3.28j), (3.99 - 17.08j)]],\n                  [[(19.18 + 9.73j), 1.17j, (-19.18 - 7.39j)],\n                   [(12.979 + 2.534j), (3.675 + 0.84j),\n                    (-5.629 - 0.854j)]]])).all()\n    assert np.isclose(\n        np.gradient(np.array([1j, 2j, 4j, 7j, 11j]),\n                    2.5j,\n                    axis=0,\n                    edge_order=1),\n        np.array([0.4 + 0.j, 0.6 + 0.j, 1. + 0.j, 1.4 + 0.j,\n                  1.6 + 0.j])).all()\n    assert np.isclose(\n        np.gradient(((4.6 + 2.5j, 3, 4.9j), (7.8 + 3.6j, 11.9, 8.9j)),\n                    9.45,\n                    axis=1,\n                    edge_order=1),\n        np.array([[\n            -0.16931217 - 0.26455026j, -0.24338624 + 0.12698413j,\n            -0.31746032 + 0.51851852j\n        ],\n                  [\n                      0.43386243 - 0.38095238j, -0.41269841 + 0.28042328j,\n                      -1.25925926 + 0.94179894j\n                  ]])).all()\n\n    assert np.isclose(\n        np.gradient(np.array([[[2.5j, 3.566 + 3.45j, 4.47j],\n                               [7.98 + 2.34j, 11.67j + 4.13j, 8.9j]],\n                              [[1.89j, 9.59 + 7.34j, 4.23j],\n                               [3.55 + 5.3j, 11.877 + 6.987j, 10.9 + 6.98j]]]),\n                    6.7,\n                    axis=2,\n                    edge_order=1),\n        np.array([[[\n            0.53223881 + 1.41791045e-01j, 0. + 1.47014925e-01j,\n            -0.53223881 + 1.52238806e-01j\n        ],\n                   [\n                       -1.19104478 + 2.00895522e+00j,\n                       -0.59552239 + 4.89552239e-01j, 0. - 1.02985075e+00j\n                   ]],\n                  [[\n                      1.43134328 + 8.13432836e-01j, 0. + 1.74626866e-01j,\n                      -1.43134328 - 4.64179104e-01j\n                  ],\n                   [\n                       1.24283582 + 2.51791045e-01j,\n                       0.54850746 + 1.25373134e-01j,\n                       -0.1458209 - 1.04477612e-03j\n                   ]]])).all()\n    assert np.isclose(\n        np.gradient(np.array([1j, 2j, 4j, 7j, 11j]),\n                    2.5j,\n                    axis=0,\n                    edge_order=2),\n        np.array([0.2 + 0.j, 0.6 + 0.j, 1. + 0.j, 1.4 + 0.j,\n                  1.8 + 0.j])).all()\n\ntest_gradient()\n\n@test\ndef test_cumsum():\n    assert (np.empty(0, float).cumsum() == np.empty(0, float)).all()\n    assert (np.array([0.5, 1.5]).cumsum() == np.array([0.5, 2.0])).all()\n    assert (np.array([1, 2, 3, 4, 5]).cumsum() == np.array([1, 3, 6, 10,\n                                                            15])).all()\n    assert (np.array([[1, 2, 3],\n                      [4, 5, 6]]).cumsum(axis=0) == np.array([[1, 2, 3],\n                                                              [5, 7,\n                                                               9]])).all()\n    assert (np.array([[1, 2, 3],\n                      [4, 5, 6]]).cumsum(axis=1) == np.array([[1, 3, 6],\n                                                              [4, 9,\n                                                               15]])).all()\n    assert (np.array([[1, 2, 3],\n                      [4, 5, 6]]).cumsum(axis=-2) == np.array([[1, 2, 3],\n                                                               [5, 7,\n                                                                9]])).all()\n    assert (np.array([[1, 2, 3],\n                      [4, 5, 6]]).cumsum(axis=-1) == np.array([[1, 3, 6],\n                                                               [4, 9,\n                                                                15]])).all()\n    assert (np.array([[1, 1, 1],\n                      [1, 1, 1]]).cumsum(axis=0) == np.array([[1, 1, 1],\n                                                              [2, 2,\n                                                               2]])).all()\n    assert (np.array([[1, 1, 1],\n                      [1, 1, 1]]).cumsum(axis=1) == np.array([[1, 2, 3],\n                                                              [1, 2,\n                                                               3]])).all()\n    assert (np.array([1, 2, 3, 4, 5,\n                      6]).cumsum() == np.array([1, 3, 6, 10, 15, 21])).all()\n    assert (np.cumsum([[1.4, 2], [3.4, 2]], axis=0,\n                      dtype=int) == np.array([[1, 2], [4, 4]])).all()\n    assert (np.cumsum([[1.4, 2], [3.4, 2]], axis=1,\n                      dtype=int) == np.array([[1, 3], [3, 5]])).all()\n    assert (np.cumsum([[1.4, 2], [3.4, 2]], axis=0,\n                      dtype=float) == np.array([[1.4, 2], [4.8, 4]])).all()\n    assert (np.cumsum([[1.4, 2], [3.4, 2]], axis=1,\n                      dtype=float) == np.array([[1.4, 3.4], [3.4,\n                                                             5.4]])).all()\n\n    ar = np.array([1, 2, 3, 4, 5])\n    b = np.empty_like(ar, dtype=int)\n    np.cumsum(ar, dtype=int, out=b)\n    assert (b.tolist() == np.array([1, 3, 6, 10, 15])).all()\n\n    ar = np.array([1, 2, 3, 4, 5])\n    b = np.empty_like(ar, dtype=float)\n    np.cumsum(ar, dtype=int, out=b)\n    assert (b.tolist() == np.array([1, 3, 6, 10, 15])).all()\n\n    ar = np.array([[1.4, 2], [3.4, 2]])\n    b = np.empty_like(ar, dtype=int)\n    np.cumsum(ar, axis=1, dtype=int, out=b)\n    assert (b.tolist() == np.array([[1, 3], [3, 5]])).all()\n\n    b = np.empty_like(ar, dtype=float)\n    np.cumsum(ar, axis=1, dtype=float, out=b)\n    assert (b.tolist() == np.array([[1.4, 3.4], [3.4, 5.4]])).all()\n\n    b = np.empty_like(ar, dtype=int)\n    np.cumsum(ar, axis=1, dtype=float, out=b)\n    assert (b.tolist() == np.array([[1, 3], [3, 5]])).all()\n\n    b = np.empty((4, ), dtype=float)\n    np.cumsum(ar, dtype=int, out=b)\n    assert (b.tolist() == np.array([1, 3, 6, 8])).all()\n\n    b = np.empty((4, ), dtype=int)\n    np.cumsum(ar, dtype=float, out=b)\n    assert (b.tolist() == np.array([1, 3, 6, 8])).all()\n\n    b = np.empty((4, ), dtype=float)\n    np.cumsum(ar, dtype=float, out=b)\n    assert (b.tolist() == np.array([1.4, 3.4, 6.8, 8.8])).all()\n\n    #NEWTEST\n\n    # Create a 3D NumPy array (3x3x2)\n    ar = np.array([[[1.1, 2.2], [3.3, 4.4], [5.5, 6.6]],\n                   [[7.7, 8.8], [9.9, 10.1], [11.1, 12.2]],\n                   [[13.3, 14.4], [15.5, 16.6], [17.7, 18.8]]])\n\n    # Create an empty array with the same shape as ar and integer dtype\n    b = np.empty_like(ar, dtype=int)\n\n    # Calculate cumulative sum along the last axis (axis=2)\n    np.cumsum(ar, axis=2, dtype=int, out=b)\n\n    # Define the expected output array for the given operation\n    expected_output = np.array([[[1, 3], [3, 7], [5, 11]],\n                                [[7, 15], [9, 19], [11, 23]],\n                                [[13, 27], [15, 31], [17, 35]]])\n\n    # Check if the calculated array matches the expected output\n    assert (b == expected_output).all()\n\n    try:\n        b = np.empty((3, ), dtype=float)\n        np.cumsum(ar, dtype=float, out=b)\n        assert False\n    except ValueError:\n        pass\n\ntest_cumsum()\n\n@test\ndef test_cumprod():\n    assert (np.empty(0, float).cumprod() == np.empty(0, float)).all()\n    assert (np.array([0.5, 1.5]).cumprod() == np.array([0.5, 0.75])).all()\n    assert (np.array([1, 2, 3, 4, 5]).cumprod() == np.array([1, 2, 6, 24,\n                                                             120])).all()\n    assert (np.array([[1, 2, 3],\n                      [4, 5, 6]]).cumprod(axis=0) == np.array([[1, 2, 3],\n                                                               [4, 10,\n                                                                18]])).all()\n    assert (np.array([[1, 2, 3],\n                      [4, 5, 6]]).cumprod(axis=1) == np.array([[1, 2, 6],\n                                                               [4, 20,\n                                                                120]])).all()\n    assert (np.array([[1, 1, 1],\n                      [1, 1, 1]]).cumprod(axis=0) == np.array([[1, 1, 1],\n                                                               [1, 1,\n                                                                1]])).all()\n    assert (np.array([[1, 1, 1],\n                      [1, 1, 1]]).cumprod(axis=1) == np.array([[1, 1, 1],\n                                                               [1, 1,\n                                                                1]])).all()\n    assert (np.array([1, 2, 3, 4, 5,\n                      6]).cumprod() == np.array([1, 2, 6, 24, 120,\n                                                 720])).all()\n    assert (np.cumprod([[1.4, 2], [3.4, 2]], axis=0,\n                       dtype=int) == np.array([[1, 2], [3, 4]])).all()\n    assert (np.cumprod([[1.4, 2], [3.4, 2]], axis=1,\n                       dtype=int) == np.array([[1, 2], [3, 6]])).all()\n    assert (np.cumprod([[1.4, 2], [3.4, 2]], axis=0,\n                       dtype=float) == np.array([[1.4, 2], [4.76, 4]])).all()\n    assert (np.cumprod([[1.4, 2], [3.4, 2]], axis=1,\n                       dtype=float) == np.array([[1.4, 2.8], [3.4,\n                                                              6.8]])).all()\n\n    assert (np.array([9223372036854775807, 1, 1, 1, 1,\n                      1]).cumprod() == np.array([\n                          9223372036854775807, 9223372036854775807,\n                          9223372036854775807, 9223372036854775807,\n                          9223372036854775807, 9223372036854775807\n                      ])).all()\n\n    ar = np.array([1, 2, 3, 4, 5])\n    b = np.empty_like(ar, dtype=int)\n    np.cumprod(ar, dtype=int, out=b)\n    assert (b.tolist() == np.array([1, 2, 6, 24, 120])).all()\n\n    ar = np.array([1, 2, 3, 4, 5])\n    b = np.empty_like(ar, dtype=float)\n    np.cumprod(ar, dtype=int, out=b)\n    assert (b.tolist() == np.array([1, 2, 6, 24, 120])).all()\n\n    ar = np.array([[1.4, 2], [3.4, 2]])\n\n    b = np.empty_like(ar, dtype=float)\n    np.cumprod(ar, axis=1, dtype=float, out=b)\n    assert (b.tolist() == np.array([[1.4, 2.8], [3.4, 6.8]])).all()\n\n    b = np.empty_like(ar, dtype=int)\n    np.cumprod(ar, axis=1, dtype=int, out=b)\n    assert (b.tolist() == np.array([[1, 2], [3, 6]])).all()\n\n    b = np.empty_like(ar, dtype=float)\n    np.cumprod(ar, axis=1, dtype=float, out=b)\n    assert (b.tolist() == np.array([[1.4, 2.8], [3.4, 6.8]])).all()\n\n    b = np.empty_like(ar, dtype=int)\n    np.cumprod(ar, axis=1, dtype=float, out=b)\n    assert (b.tolist() == np.array([[1, 2], [3, 6]])).all()\n\n    b = np.empty((4, ), dtype=float)\n    np.cumprod(ar, dtype=int, out=b)\n    assert (b.tolist() == np.array([1, 2, 6, 12])).all()\n\n    b = np.empty((4, ), dtype=int)\n    np.cumprod(ar, dtype=float, out=b)\n    assert (b.tolist() == np.array([1, 2, 9, 19])).all()\n\n    b = np.empty((4, ), dtype=float)\n    np.cumprod(ar, dtype=float, out=b)\n    assert (b.tolist() == np.array([1.4, 2.8, 9.52, 19.04])).all()\n\n    #NEWTEST\n\n    # Create a 3D NumPy array (3x3x2)\n    ar = np.array([[[1.1, 2.2], [3.3, 4.4], [5.5, 6.6]],\n                   [[7.7, 8.8], [9.9, 10.1], [11.1, 12.2]],\n                   [[13.3, 14.4], [15.5, 16.6], [17.7, 18.8]]])\n\n    # Create an empty array with the same shape as ar and integer dtype\n    b = np.empty_like(ar, dtype=int)\n\n    # Calculate cumulative product along the last axis (axis=2)\n    np.cumprod(ar, axis=2, dtype=int, out=b)\n\n    # Define the expected output array for the given operation\n    expected_output = np.array([[[1, 2], [3, 12], [5, 30]],\n                                [[7, 56], [9, 90], [11, 132]],\n                                [[13, 182], [15, 240], [17, 306]]])\n\n    # Check if the calculated array matches the expected output\n    assert (b == expected_output).all()\n\n    try:\n        b = np.empty((3, ), dtype=float)\n        np.cumprod(ar, dtype=float, out=b)\n        assert False\n    except ValueError:\n        pass\n\ntest_cumprod()\n\n@test\ndef test_nancumsum():\n    assert (np.nancumsum(np.empty(0, float)) == np.empty(0, float)).all()\n    assert (np.nancumsum([0.5, 1.5]) == np.array([0.5, 2.0])).all()\n    assert (np.nancumsum([np.nan, 2, 3, 4, 5]) == np.array([0, 2, 5, 9,\n                                                            14])).all()\n    assert (np.nancumsum([[1.3, 2.4, 3],\n                          [np.nan, 5,\n                           6]]) == np.array([1.3, 3.7, 6.7, 6.7, 11.7,\n                                             17.7])).all()\n    assert (np.nancumsum([[1.3, 2.4, 3], [np.nan, 5, 6]],\n                         axis=0) == np.array([[1.3, 2.4, 3], [1.3, 7.4,\n                                                              9]])).all()\n    assert (np.nancumsum([[1, 2, 3], [4, 5, 6]],\n                         axis=1) == np.array([[1, 3, 6], [4, 9, 15]])).all()\n    assert (np.nancumsum([[np.nan, np.nan, 1], [np.nan, np.nan, 1]],\n                         axis=0) == np.array([[0, 0, 1], [0, 0, 2]])).all()\n    assert (np.nancumsum([[1, np.nan, 1], [1, np.nan, np.nan]],\n                         axis=1) == np.array([[1, 1, 2], [1, 1, 1]])).all()\n    assert (np.nancumsum([1, 2, 3, 4, 5, 6]) == np.array([1, 3, 6, 10, 15,\n                                                          21])).all()\n    assert (np.nancumsum([[1.4, 2], [3.4, 2]], axis=0,\n                         dtype=int) == np.array([[1, 2], [4, 4]])).all()\n    assert (np.nancumsum([[1.4, np.nan], [3.4, 2]], axis=1,\n                         dtype=int) == np.array([[1, 1], [3, 5]])).all()\n    assert (np.nancumsum([[1.4, 2], [3.4, 2]], axis=0,\n                         dtype=float) == np.array([[1.4, 2], [4.8, 4]])).all()\n    assert (np.nancumsum([[1.4, np.nan], [3.4, 2]],\n                         axis=1, dtype=float) == np.array([[1.4, 1.4],\n                                                           [3.4, 5.4]])).all()\n\n    ar = np.array([[np.nan, 2], [3.4, np.nan]])\n\n    b = np.empty_like(ar, dtype=int)\n    np.nancumsum(ar, axis=1, dtype=int, out=b)\n    assert (b.tolist() == np.array([[0, 2], [3, 3]])).all()\n\n    b = np.empty_like(ar, dtype=int)\n    np.nancumsum(ar, axis=1, dtype=float, out=b)\n    assert (b.tolist() == np.array([[0, 2], [3, 3]])).all()\n\n    b = np.empty((4, ), dtype=float)\n    np.nancumsum(ar, dtype=int, out=b)\n    assert (b.tolist() == np.array([0, 2, 5, 5])).all()\n\n    b = np.empty((4, ), dtype=int)\n    np.nancumsum(ar, dtype=float, out=b)\n    assert (b.tolist() == np.array([0, 2, 5, 5])).all()\n\n    b = np.empty((4, ), dtype=float)\n    np.nancumsum(ar, dtype=float, out=b)\n    assert (b.tolist() == np.array([0, 2, 5.4, 5.4])).all()\n    #NEWTEST\n\n    # Create a 3D NumPy array (3x3x2)\n    ar = np.array([[[1.1, 2.2], [3.3, 4.4], [5.5, 6.6]],\n                   [[7.7, 8.8], [9.9, 10.1], [11.1, 12.2]],\n                   [[13.3, 14.4], [15.5, 16.6], [17.7, 18.8]]])\n\n    # Create an empty array with the same shape as ar and integer dtype\n    b = np.empty_like(ar, dtype=int)\n\n    # Calculate cumulative sum along the last axis (axis=2)\n    np.nancumsum(ar, axis=2, dtype=int, out=b)\n\n    # Define the expected output array for the given operation\n    expected_output = np.array([[[1, 3], [3, 7], [5, 11]],\n                                [[7, 15], [9, 19], [11, 23]],\n                                [[13, 27], [15, 31], [17, 35]]])\n\n    # Check if the calculated array matches the expected output\n    assert (b == expected_output).all()\n\n    try:\n        b = np.empty((3, ), dtype=float)\n        np.nancumsum(ar, dtype=float, out=b)\n        assert False\n    except ValueError:\n        pass\n\ntest_nancumsum()\n\n@test\ndef test_nancumprod():\n    assert (np.nancumprod(np.empty(0, float)) == np.empty(0, float)).all()\n    assert (np.nancumprod([0.5, 1.5]) == np.array([0.5, 0.75])).all()\n    assert (np.nancumprod([np.nan, 2, 3, 4, 5]) == np.array([1, 2, 6, 24,\n                                                             120])).all()\n    assert (np.nancumprod([[1.3, 2.4, 3], [np.nan, 5, 6]],\n                          axis=0) == np.array([[1.3, 2.4, 3], [1.3, 12,\n                                                               18]])).all()\n    assert (np.nancumprod([[1, 2, 3], [4, 5, 6]],\n                          axis=1) == np.array([[1, 2, 6], [4, 20,\n                                                           120]])).all()\n    assert (np.nancumprod([[np.nan, np.nan, 1], [np.nan, np.nan, 1]],\n                          axis=0) == np.array([[1, 1, 1], [1, 1, 1]])).all()\n    assert (np.nancumprod([[1, 2, 3], [4, 5, 6]],\n                          axis=1) == np.array([[1, 2, 6], [4, 20,\n                                                           120]])).all()\n    assert (np.nancumprod([1, 2, 3, 4, 5,\n                           6]) == np.array([1, 2, 6, 24, 120, 720])).all()\n    assert (np.nancumprod([[1.4, 2], [3.4, 2]], axis=0,\n                          dtype=int) == np.array([[1, 2], [3, 4]])).all()\n    assert (np.nancumprod([[1.4, np.nan], [3.4, 2]], axis=1,\n                          dtype=int) == np.array([[1, 1], [3, 6]])).all()\n    assert (np.nancumprod([[1.4, 2], [3.4, 2]], axis=0,\n                          dtype=float) == np.array([[1.4, 2], [4.76,\n                                                               4]])).all()\n\n    ar = np.array([[np.nan, 2], [3.4, np.nan]])\n\n    b = np.empty_like(ar, dtype=int)\n    np.nancumprod(ar, axis=1, dtype=int, out=b)\n    assert (b.tolist() == np.array([[1, 2], [3, 3]])).all()\n\n    b = np.empty_like(ar, dtype=float)\n    np.nancumprod(ar, axis=1, dtype=float, out=b)\n    assert (b.tolist() == np.array([[1.0, 2.0], [3.4, 3.4]])).all()\n\n    b = np.empty_like(ar, dtype=float)\n    np.nancumprod(ar, axis=1, dtype=int, out=b)\n    assert (b.tolist() == np.array([[1, 2], [3, 3]])).all()\n\n    b = np.empty_like(ar, dtype=int)\n    np.nancumprod(ar, axis=1, dtype=float, out=b)\n    assert (b.tolist() == np.array([[1, 2], [3, 3]])).all()\n\n    b = np.empty((4, ), dtype=float)\n    np.nancumprod(ar, dtype=int, out=b)\n    assert (b.tolist() == np.array([1, 2, 6, 6])).all()\n\n    b = np.empty((4, ), dtype=int)\n    np.nancumprod(ar, dtype=float, out=b)\n    assert (b.tolist() == np.array([1, 2, 6, 6])).all()\n\n    b = np.empty((4, ), dtype=float)\n    np.nancumprod(ar, dtype=float, out=b)\n    assert (b.tolist() == np.array([1, 2, 6.8, 6.8])).all()\n\n    # Create a 3D NumPy array (3x3x2)\n    ar = np.array([[[1.1, 2.2], [3.3, 4.4], [5.5, 6.6]],\n                   [[7.7, 8.8], [9.9, 10.1], [11.1, 12.2]],\n                   [[13.3, 14.4], [15.5, 16.6], [17.7, 18.8]]])\n\n    # Create an empty array with the same shape as ar and integer dtype\n    b = np.empty_like(ar, dtype=int)\n\n    # Calculate cumulative product along the last axis (axis=2)\n    np.nancumprod(ar, axis=2, dtype=int, out=b)\n\n    # Define the expected output array for the given operation\n    expected_output = np.array([[[1, 2], [3, 12], [5, 30]],\n                                [[7, 56], [9, 90], [11, 132]],\n                                [[13, 182], [15, 240], [17, 306]]])\n\n    # Check if the calculated array matches the expected output\n    assert (b == expected_output).all()\n\n    b = np.empty((3, ), dtype=float)\n    try:\n        np.nancumprod(ar, dtype=float, out=b)\n        assert False\n    except ValueError:\n        pass\n\ntest_nancumprod()\n\n@test\ndef test_diff():\n    assert (np.diff([1, 2, 3], n=3) == np.empty(0, float)).all()\n\n    a = np.array([[1, 3, 4], [5, 7, 6]])\n\n    assert (np.diff(a, n=2, axis=0, append=a,\n                    prepend=a) == np.array([[-8, -8, -4], [8, 8, 4],\n                                            [-8, -8, -4], [8, 8, 4]])).all()\n    assert (np.diff(a, n=2, axis=1, append=a,\n                    prepend=a) == np.array([[-1, -4, 5, -1, -4, 5, -1],\n                                            [-3, 0, 3, -3, 0, 3, -3]])).all()\n    assert (np.diff(a, n=1, axis=0, append=a,\n                    prepend=a) == np.array([[4, 4, 2], [-4, -4, -2], [4, 4, 2],\n                                            [-4, -4, -2], [4, 4, 2]])).all()\n    assert (np.diff(a, n=1, axis=1, append=a,\n                    prepend=a) == np.array([[2, 1, -3, 2, 1, -3, 2, 1],\n                                            [2, -1, -1, 2, -1, -1, 2,\n                                             -1]])).all()\n    assert (np.diff(a, n=2, append=a,\n                    prepend=a) == np.array([[-1, -4, 5, -1, -4, 5, -1],\n                                            [-3, 0, 3, -3, 0, 3, -3]])).all()\n    assert (np.diff(a, n=1, append=a,\n                    prepend=a) == np.array([[2, 1, -3, 2, 1, -3, 2, 1],\n                                            [2, -1, -1, 2, -1, -1, 2,\n                                             -1]])).all()\n    assert (np.diff(a, append=a,\n                    prepend=a) == np.array([[2, 1, -3, 2, 1, -3, 2, 1],\n                                            [2, -1, -1, 2, -1, -1, 2,\n                                             -1]])).all()\n\n    assert (np.diff(\n        np.arange('1066-10-13', '1066-10-16',\n                  dtype=np.datetime64['D', 1])) == np.array(\n                      [1, 1], dtype=np.timedelta64['D', 1])).all()\n\n    assert (np.diff(np.arange('1066-10-13',\n                              '1066-10-16',\n                              dtype=np.datetime64['D', 1]),\n                    n=2) == np.array([0], dtype=np.timedelta64['D', 1])).all()\n\n    assert (np.diff(np.arange('1066-10-13',\n                              '1066-10-16',\n                              dtype=np.datetime64['D', 1]),\n                    append=np.arange('1066-10-13',\n                                     '1066-10-16',\n                                     dtype=np.datetime64['D', 1]),\n                    prepend=np.arange(\n                        '1066-10-13',\n                        '1066-10-16',\n                        dtype=np.datetime64['D', 1])) == np.array(\n                            [1, 1, -2, 1, 1, -2, 1, 1],\n                            dtype=np.timedelta64['D', 1])).all()\n\n    x = np.array([[\n        '1910-08-16', '1910-08-11', '1910-08-10', '1910-08-12', '1910-10-12',\n        '1910-12-12', '1912-12-12'\n    ]],\n                 dtype=np.datetime64['Y', 1])\n\n    assert (np.diff(x, axis=1) == np.array([0, 0, 0, 0, 0, 2],\n                                           dtype=np.timedelta64['Y',\n                                                                1])).all()\n\n    td = np.array([100, 200, 300], dtype=np.timedelta64['D', 1])\n\n    td_2d = np.array([[100, 200, 300]], dtype=np.timedelta64['D', 1])\n\n    assert (np.diff(td) == np.array([100, 100],\n                                    dtype=np.timedelta64['D', 1])).all()\n\n    assert (np.diff(td, n=2) == np.array([0], dtype=np.timedelta64['D',\n                                                                   1])).all()\n\n    assert (np.diff(td, append=td, prepend=td) == np.array(\n        [100, 100, -200, 100, 100, -200, 100, 100],\n        dtype=np.timedelta64['D', 1])).all()\n\n    assert (np.diff(td_2d, axis=1) == np.array([100, 100],\n                                               dtype=np.timedelta64['D',\n                                                                    1])).all()\n\ntest_diff()\n\n@test\ndef test_cross():\n    assert np.cross([1, 2], [1, 2]) == 0\n    assert (np.cross([1, 2, 3], [1, 2]) == np.array([6, -3, 0])).all()\n    assert np.isclose(np.cross([1, 2.4, 3], [1.4, 2]),\n                      np.array([6, -4.2, -1.36])).all()\n    assert (np.cross([[1, 2, 3], [4, 5, 6]],\n                     [[4, 5, 6], [1, 2, 3]]) == np.array([[-3, 6, -3],\n                                                          [3, -6, 3]])).all()\n    assert (np.cross([[1, 2, 3], [4, 5, 6]], [[4, 5, 6], [1, 2, 3]],\n                     axis=1) == np.array([[-3, 6, -3], [3, -6, 3]])).all()\n    assert (np.cross([[1, 2, 3], [4, 5, 6]], [[4, 5, 6], [1, 2, 3]],\n                     axisa=1,\n                     axisb=1) == np.array([[-3, 6, -3], [3, -6, 3]])).all()\n\n    try:\n        np.cross(np.empty(0), np.empty(0))\n        assert False\n    except ValueError:\n        pass\n\ntest_cross()\n\n@test\ndef test_ediff1d():\n    assert (np.ediff1d([1, 2, 4, 7, 0]) == np.array([1, 2, 3, -7])).all()\n    assert (np.ediff1d([[1, 4], [4, 6]]) == np.array([3, 0, 2])).all()\n    assert (np.ediff1d([1, 2, 4, 7, 0],\n                       to_begin=-99,\n                       to_end=np.array([88, 99])) == np.array(\n                           [-99, 1, 2, 3, -7, 88, 99])).all()\n    assert (np.ediff1d([[1, 2, 4],\n                        [1, 6, 24]]) == np.array([1.0, 2, -3, 5, 18])).all()\n    assert np.isclose(np.ediff1d([[1.5, 2.2, 4.5], [1.2, 6.3, 5.4]]),\n                      np.array([0.7, 2.3, -3.3, 5.1, -0.9])).all()\n    assert (np.ediff1d([11, 2, 24, 37, 10],\n                       to_begin=-86,\n                       to_end=np.array([103, 104])) == np.array(\n                           [-86, -9, 22, 13, -27, 103, 104])).all()\n    assert (np.ediff1d([23, 22, 41, 27, 5],\n                       to_begin=1,\n                       to_end=np.array([49, 59])) == np.array(\n                           [1, -1, 19, -14, -22, 49, 59])).all()\n    assert np.isclose(\n        np.ediff1d([1, 2, 4.6, 3, 0.8],\n                   to_begin=-99,\n                   to_end=np.array([45, 99.8])),\n        np.array([-99, 1, 2.6, -1.6, -2.2, 45, 99.8])).all()\n    assert np.isclose(\n        np.ediff1d([22.4, 48.9, 4.86, 33.66, 0.845],\n                   to_begin=-55,\n                   to_end=np.array([101.46, 180.8])),\n        np.array([-55., 26.5, -44.04, 28.8, -32.815, 101.46, 180.8])).all()\n    assert np.isclose(np.ediff1d([1.465, 2.3445, 7.45326, 3.231, 0.9238]),\n                      np.array([0.8795, 5.10876, -4.22226, -2.3072])).all()\n    assert (np.ediff1d([1 + 2j, 3 + 2j, 24j]) == np.array([2 + 0j,\n                                                           -3 + 22j])).all()\n\ntest_ediff1d()\n\n@test\ndef test_trapz():\n    x = np.array([3 + 2j, 4 + 5j, 5 + 6j])\n    assert np.trapz(x) == 8 + 9j\n    assert np.trapz([1, 2, 3], x=[4, 6, 8]) == 8\n    assert (np.trapz([[1, 2, 3], [4, 5, 6]], x=[0, 2],\n                     axis=0) == np.array([5, 7, 9])).all()\n    assert (np.trapz([[1, 2, 3], [4, 5, 6]], x=[0, 2, 4],\n                     axis=1) == np.array([8, 20])).all()\n    assert (np.trapz([[1, 2, 3], [4, 5, 6]], x=[[0, 1, 2], [3, 4, 5]],\n                     axis=1) == np.array([4, 10])).all()\n\n    array_3d = np.array([[[1, 2, 7], [3, 4, 15], [5, 6, 13]],\n                         [[7, 8, 8], [9, 10, 5], [11, 12, 6]]])\n\n    assert (np.trapz(array_3d, axis=1) == np.array([[6, 8, 25], [18, 20,\n                                                                 12]])).all()\n\ntest_trapz()\n\n@test\ndef test_convolve():\n\n    assert (np.convolve(3, 4) == np.array([12])).all()\n    assert (np.convolve(3.2, 4.5) == np.array([14.4])).all()\n    assert (np.convolve(np.array([1, 2, 3]),\n                        np.array([4, 5, 6])) == np.array([4, 13, 28, 27,\n                                                          18])).all()\n    assert (np.convolve(np.array([1.5, 2, 3]),\n                        np.array([4, 5,\n                                  6])) == np.array([6., 15.5, 31., 27.,\n                                                    18.])).all()\n    assert (np.convolve(np.array([1, 2, 3]),\n                        np.array([4, 5, 6, 7,\n                                  8])) == np.array([4, 13, 28, 34, 40, 37,\n                                                    24])).all()\n    assert (np.convolve(np.array([4, 5, 6, 7, 8]),\n                        np.array([1, 2,\n                                  3])) == np.array([4, 13, 28, 34, 40, 37,\n                                                    24])).all()\n    assert (np.convolve(np.array([1j, 2 + 6j, 3j]),\n                        np.array([10, 1, 0.5, 6, 8, 9])) == np.array([\n                            0. + 10.j, 20. + 61.j, 2. + 36.5j, 1. + 12.j,\n                            12. + 45.5j, 16. + 75.j, 18. + 78.j, 0. + 27.j\n                        ])).all()\n\n    assert (np.convolve(3, 4, 'full') == np.array([12])).all()\n    assert (np.convolve(3.2, 4.5, 'full') == np.array([14.4])).all()\n    assert (np.convolve(np.array([1, 2, 3]), np.array([4, 5, 6]),\n                        'full') == np.array([4, 13, 28, 27, 18])).all()\n    assert (np.convolve(np.array([1.5, 2, 3]), np.array([4, 5, 6]),\n                        'full') == np.array([6., 15.5, 31., 27., 18.])).all()\n    assert (np.convolve(np.array([1, 2, 3]), np.array([4, 5, 6, 7, 8]),\n                        'full') == np.array([4, 13, 28, 34, 40, 37,\n                                             24])).all()\n    assert (np.convolve(np.array([4, 5, 6, 7, 8]), np.array([1, 2, 3]),\n                        'full') == np.array([4, 13, 28, 34, 40, 37,\n                                             24])).all()\n    assert (np.convolve(np.array([1j, 2 + 6j, 3j]),\n                        np.array([10, 1, 0.5, 6, 8, 9]), 'full') == np.array([\n                            0. + 10.j, 20. + 61.j, 2. + 36.5j, 1. + 12.j,\n                            12. + 45.5j, 16. + 75.j, 18. + 78.j, 0. + 27.j\n                        ])).all()\n\n    assert (np.convolve(3, 4, 'same') == np.array([12])).all()\n    assert (np.convolve(3.2, 4.5, 'same') == np.array([14.4])).all()\n    assert (np.convolve(np.array([1, 2, 3]), np.array([4, 5, 6]),\n                        'same') == np.array([13, 28, 27])).all()\n    assert (np.convolve(np.array([1.5, 2, 3]), np.array([4, 5, 6]),\n                        'same') == np.array([15.5, 31., 27.])).all()\n    assert (np.convolve(np.array([1, 2, 3]), np.array([4, 5, 6, 7, 8]),\n                        'same') == np.array([13, 28, 34, 40, 37])).all()\n    assert (np.convolve(np.array([4, 5, 6, 7, 8]), np.array([1, 2, 3]),\n                        'same') == np.array([13, 28, 34, 40, 37])).all()\n    assert (np.convolve(np.array([1j, 2 + 6j, 3j]),\n                        np.array([10, 1, 0.5, 6, 8, 9]), 'same') == np.array([\n                            20. + 61.j, 2. + 36.5j, 1. + 12.j, 12. + 45.5j,\n                            16. + 75.j, 18. + 78.j\n                        ])).all()\n\n    assert (np.convolve(3, 4, 'valid') == np.array([12])).all()\n    assert (np.convolve(3.2, 4.5, 'valid') == np.array([14.4])).all()\n    assert (np.convolve(np.array([1, 2, 3]), np.array([4, 5, 6]),\n                        'valid') == np.array([28])).all()\n    assert (np.convolve(np.array([1.5, 2, 3]), np.array([4, 5, 6]),\n                        'valid') == np.array([31.])).all()\n    assert (np.convolve(np.array([1, 2, 3]), np.array([4, 5, 6, 7, 8]),\n                        'valid') == np.array([28, 34, 40])).all()\n    assert (np.convolve(np.array([4, 5, 6, 7, 8]), np.array([1, 2, 3]),\n                        'valid') == np.array([28, 34, 40])).all()\n    assert (np.convolve(np.array([1j, 2 + 6j, 3j]),\n                        np.array([10, 1, 0.5, 6, 8, 9]), 'valid') == np.array(\n                            [2. + 36.5j, 1. + 12.j, 12. + 45.5j,\n                             16. + 75.j])).all()\n\n    try:\n        np.convolve(np.empty(0), np.empty(0))\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.convolve(np.empty(0), [1, 2, 3])\n        assert False\n    except ValueError:\n        pass\n\ntest_convolve()\n\n@test\ndef test_interp():\n    assert np.interp(2.5, np.array([1, 2, 3]), np.array([3, 2, 0])) == 1.0\n    assert np.isclose(\n        np.interp(np.array([0, 1, 1.5, 2.72, 3.14]), np.array([1, 2, 3]),\n                  np.array([3, 2, 0])), np.array([3, 3, 2.5, 0.56, 0])).all()\n    assert np.interp(3.14,\n                     np.array([1, 2, 3]),\n                     np.array([3, 2, 0]),\n                     right=-99.0) == -99.0\n    assert np.interp(0.14,\n                     np.array([1, 2, 3]),\n                     np.array([3, 2, 0]),\n                     left=-99.0) == -99.0\n    assert np.isclose(\n        np.interp(np.array([-180, -170, -185, 185, -10, -5, 0, 365]),\n                  np.array([190, -190, 350, -350]),\n                  np.array([5, 10, 3, 4]),\n                  period=360),\n        np.array([7.5, 5., 8.75, 6.25, 3., 3.25, 3.5, 3.75])).all()\n    assert np.isclose(\n        np.interp(np.array([1.5, 4.0]), np.array([2, 3, 5]),\n                  np.array((1.0j, 0, 2 + 3j))), np.array([0. + 1.j,\n                                                          1. + 1.5j])).all()\n    assert np.isclose(\n        np.interp(np.array([6, 9, 7.5]), np.array([6, 9, 12, 15, 18]),\n                  np.array((3, 2, 0, 5, 8))), np.array([3, 2, 2.5])).all()\n    assert np.isclose(\n        np.interp(np.array([6, 9, 7.5]), np.array([18, 15, 12, 9, 6]),\n                  np.array((3, 2, 0, 5, 8))), np.array([3., 8., 8.])).all()\n    assert np.isclose(\n        np.interp(np.array([6, 9, 7.5]),\n                  np.array([18, 15, 12, 9, 6, 9, 12, 15, 18]),\n                  np.array((3, 2, 0, 5, 8, 9, 12, 34, 9))),\n        np.array([3., 3., 3.])).all()\n    assert np.isclose(\n        np.interp(np.array([6, 9, 7.5]),\n                  np.array([6, 9, 12, 15, 18, 18, 15, 12, 9]),\n                  np.array((3, 2, 0, 5, 8, 9, 12, 34, 9))),\n        np.array([3., 2., 2.5])).all()\n    assert np.isclose(\n        np.interp(np.array([6, 9, 7.5]), np.array([1, 6, 2, 5, 9, 4, 7, 3, 8]),\n                  np.array((3, 2, 0, 5, 8, 9, 12, 34, 9))),\n        np.array([5.75, 9., 11.5])).all()\n\n    try:\n        np.interp(np.array([6, 9, 7.5]), np.array([1, 6, 2, 5, 9, 4, 7, 3, 8]),\n                  np.array((3, 0, 5, 8, 9, 12, 34, 9)))\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.interp(np.array([6, 9, 7.5]),\n                  np.array([1, 6, 2, 5, 9, 4, 7, 3, 8]),\n                  np.array([3, 2, 0, 5, 8, 9, 12, 34, 9]),\n                  left=None,\n                  right=None,\n                  period=0)\n        assert False\n    except ValueError:\n        pass\n\ntest_interp()\n\n@test\ndef test_exp():\n    assert np.exp(-2 * np.pi) == 0.0018674427317079893\n    assert np.exp(2 * np.pi) == 535.4916555247646\n    assert np.exp(-np.pi) == 0.04321391826377226\n    assert np.exp(np.pi) == 23.140692632779267\n    assert np.exp(0) == 1.0\n    assert np.exp(np.e) == 15.154262241479262\n    assert np.exp(-1.5 + 1j) == (0.12055774003692393 + 0.1877575556004429j)\n    assert np.exp(0 + 1.5j) == (0.0707372016677029 + 0.9974949866040544j)\n\ntest_exp()\n\n@test\ndef test_expm1():\n    assert np.expm1(1e-10) == 1.00000000005e-10\n    assert np.expm1(np.e) == 14.154262241479262\n    assert np.expm1(-2 * np.pi) == -0.998132557268292\n    assert np.expm1(2 * np.pi) == 534.4916555247646\n    assert np.isclose(np.expm1(0 + 1.5j), -0.929262798332297 + 0.9974949866040544j)\n    assert np.expm1(1.5 + 1j) == (1.4214669388876962 + 3.771211315620157j)\n\ntest_expm1()\n\n@test\ndef test_exp2():\n    assert (np.exp2([2, 3]) == np.array([4., 8.])).all()\n    assert np.exp2(0) == 1.0\n    assert np.exp2(np.e) == 6.5808859910179205\n    assert np.exp2(1.5 + 1j) == (2.1757361740278176 + 1.8072554055879297j)\n    assert np.exp2(0 + 1j) == (0.7692389013639721 + 0.6389612763136348j)\n\ntest_exp2()\n\n@test\ndef test_log():\n    assert (np.log([1, np.e, np.e**2, 0]) == np.array([0., 1., 2.,\n                                                       -np.inf])).all()\n    assert np.log(0 + 1j) == 1.5707963267948966j\n    assert np.isclose(np.log(1.5 + 1j), (0.5893274981708231 + 0.5880026035475675j))\n\ntest_log()\n\n@test\ndef test_log10():\n    assert np.log10(1e-15) == -15.\n    assert np.isnan(np.log10(-3))\n    assert np.isclose(np.log10(1.5 + 1j), (0.2559416804894372 + 0.25536628606545403j))\n    assert np.log10(0 + 1j) == 0.6821881769209206j\n    assert isnan(np.log(np.nan))\n    assert np.log10(10**9) == 9.\n\ntest_log10()\n\n@test\ndef test_log2():\n    assert (np.log2(np.array([0, 1, 2,\n                              2**4])) == np.array([-np.inf, 0., 1.,\n                                                   4.])).all()\n    assert (round(np.log2(np.array([0 + 1.j, 1 + 0.j, 2 + 0.j, 4.j])),\n                  8) == np.array(\n                      [0. + 2.26618007j, 0. + 0.j, 1. + 0.j,\n                       2. + 2.26618007j])).all()\n    assert np.log2(0 + 1j) == 2.2661800709135966j\n    assert np.isclose(np.log2(1.5 + 1j), (0.8502198590705461 + 0.8483084401678749j))\n    assert np.log2(10**18) == 59.794705707972525\n\ntest_log2()\n\n@test\ndef test_log1p():\n    assert np.log1p(1e-99) == 1e-99\n    assert np.log1p(1 + 1e-99) == 0.6931471805599453\n    assert np.log1p(1.5 + 1j) == (0.9905007344332917 + 0.3805063771123649j)\n    assert np.log1p(1 + 0j) == (0.6931471805599453 + 0j)\n\ntest_log1p()\n\n@test\ndef test_logaddexp():\n    assert np.logaddexp(np.log(1e-50), np.log(2.5e-50)) == -113.87649168120691\n    assert np.logaddexp(np.log(1e-5), np.log(2.5e-5)) == -10.26016249647486\n\ntest_logaddexp()\n\n@test\ndef test_logaddexp2():\n    assert np.logaddexp2(np.log(1e-50), np.log(2.5e-50)) == -113.59955522772194\n    assert np.logaddexp2(np.log(1e-5), np.log(2.5e-5)) == -9.983226042989871\n\ntest_logaddexp2()\n\n@test\ndef test_signbit():\n    assert np.signbit(-1.2)\n    assert (np.signbit(np.array([1, -2.3,\n                                 2.1])) == np.array([False, True,\n                                                     False])).all()\n\ntest_signbit()\n\n@test\ndef test_copysign():\n    assert np.copysign(1.3, -1) == -1.3\n    assert 1 / np.copysign(0, 1) == np.inf\n    assert 1 / np.copysign(0, -1) == -np.inf\n    assert (np.copysign([-1, 0, 1], -1.1) == np.array([-1., -0., -1.])).all()\n\ntest_copysign()\n\n@test\ndef test_frexp():\n    y1, y2 = np.frexp(np.arange(9))\n    assert (y1 == np.array([\n        0., 0.5, 0.5, 0.75, 0.5, 0.625, 0.75, 0.875, 0.5\n    ])).all() and (y2 == np.array([0, 1, 2, 2, 3, 3, 3, 3, 4])).all()\n\ntest_frexp()\n\n@test\ndef test_ldexp():\n    assert (np.ldexp(5, np.arange(4)) == np.array([5., 10., 20., 40.])).all()\n\ntest_ldexp()\n\n@test\ndef test_nextafter():\n    assert np.nextafter(1, 2) == 1.0000000000000002\n    assert (np.nextafter([1, 2], [2, 1]) == np.array(\n        [1.0000000000000002, 1.9999999999999998])).all()\n\ntest_nextafter()\n\n@test\ndef test_spacing():\n    assert np.spacing(1) == 2.220446049250313e-16\n    assert np.spacing(np.e) == 4.440892098500626e-16\n    assert np.spacing(1024) == 2.2737367544323206e-13\n\ntest_spacing()\n\n@test\ndef test_lcm():\n    assert np.lcm(12, 20) == 60\n    assert np.lcm.reduce([3, 12, 20]) == 60\n    assert np.lcm.reduce([40, 12, 20]) == 120\n    assert (np.lcm(np.arange(6), 20) == np.array([0, 20, 20, 60, 20,\n                                                  20])).all()\n\ntest_lcm()\n\n@test\ndef test_gcd():\n    assert np.gcd(12, 20) == 4\n    assert np.gcd.reduce([15, 25, 35]) == 5\n    assert (np.gcd(np.arange(6), 20) == np.array([20, 1, 2, 1, 4, 5])).all()\n\ntest_gcd()\n\n@test\ndef test_reciprocal():\n    assert np.reciprocal(2.) == 0.5\n    assert (np.reciprocal([1, 2.,\n                           3.33]) == np.array([1., 0.5,\n                                               0.3003003003003003])).all()\n\ntest_reciprocal()\n\n@test\ndef test_positive():\n    assert (np.positive(np.array(([1., -1.]))) == np.array([1., -1.])).all()\n    assert (+np.array(([1., -1.])) == np.array([1., -1.])).all()\n\ntest_positive()\n\n@test\ndef test_negative():\n    assert (np.negative(np.array(([1., -1.]))) == np.array([-1., 1.])).all()\n    assert (-np.array(([1., -1.])) == np.array([-1., 1.])).all()\n\ntest_negative()\n\n@test\ndef test_multiply():\n    assert np.multiply(2.0, 4.0) == 8.0\n    x1 = np.arange(9.0).reshape((3, 3))\n    x2 = np.arange(3.0)\n    assert (np.multiply(x1, x2) == np.array([[0., 1., 4.], [0., 4., 10.],\n                                             [0., 7., 16.]])).all()\n    assert (x1 * x2 == np.array([[0., 1., 4.], [0., 4., 10.], [0., 7.,\n                                                               16.]])).all()\n\ntest_multiply()\n\n@test\ndef test_divide():\n    assert np.divide(-2.0, 4.0) == -0.5\n    assert np.divide(-2.0, -4.0) == 0.5\n    x1 = np.arange(9.0).reshape((3, 3))\n    x2 = 2 * np.ones(3)\n    assert (np.divide(x1, x2) == np.array([[0., 0.5, 1.], [1.5, 2., 2.5],\n                                           [3., 3.5, 4.]])).all()\n    assert (x1 / x2 == np.array([[0., 0.5, 1.], [1.5, 2., 2.5], [3., 3.5,\n                                                                 4.]])).all()\n    assert np.divide(10**18, 10**9) == 10**9\n\ntest_divide()\n\n@test\ndef test_power():\n    assert (np.power([0, 1, 2, 3, 4, 5], 3) == np.array([0, 1, 8, 27, 64,\n                                                         125])).all()\n    x2 = [1.0, 2.0, 3.0, 3.0, 2.0, 1.0]\n    assert (np.power([0, 1, 2, 3, 4, 5],\n                     x2) == np.array([0., 1., 8., 27., 16., 5.])).all()\n    x3 = np.array([[1, 2, 3, 3, 2, 1], [1, 2, 3, 3, 2, 1]])\n    assert (np.power([0, 1, 2, 3, 4, 5], x3) == np.array([[0, 1, 8, 27, 16, 5],\n                                                          [0, 1, 8, 27, 16,\n                                                           5]])).all()\n    assert (np.array([0, 1, 2, 3, 4,\n                      5])**x2 == np.array([0., 1., 8., 27., 16., 5.])).all()\n\ntest_power()\n\n@test\ndef test_subtract():\n    assert np.subtract(1.0, 4.0) == -3.0\n    x1 = np.arange(9.0).reshape((3, 3))\n    x2 = np.arange(3.0)\n    assert (np.subtract(x1, x2) == np.array([[0., 0., 0.], [3., 3., 3.],\n                                             [6., 6., 6.]])).all()\n    assert (x1 - x2 == np.array([[0., 0., 0.], [3., 3., 3.], [6., 6.,\n                                                              6.]])).all()\n\ntest_subtract()\n\n@test\ndef test_true_divide():\n    assert np.true_divide(2.0, 4.0) == 0.5\n    x1 = np.arange(9.0).reshape((3, 3))\n    x2 = 2 * np.ones(3)\n    assert (np.true_divide(x1, x2) == np.array([[0., 0.5, 1.], [1.5, 2., 2.5],\n                                                [3., 3.5, 4.]])).all()\n    assert (x1 / x2 == np.array([[0., 0.5, 1.], [1.5, 2., 2.5], [3., 3.5,\n                                                                 4.]])).all()\n\ntest_true_divide()\n\n@test\ndef test_floor_divide():\n    assert np.floor_divide(7, 3) == 2\n    x1 = np.array([1., 2., 3., 4.])\n    assert (np.floor_divide(x1, 2.5) == np.array([0., 0., 1., 1.])).all()\n    assert (x1 // 2.5 == np.array([0., 0., 1., 1.])).all()\n\ntest_floor_divide()\n\n@test\ndef test_fmod():\n    assert (np.fmod([-3, -2, -1, 1, 2, 3], 2) == np.array([-1, 0, -1, 1, 0,\n                                                           1])).all()\n    assert (np.fmod([5, 3], [2, 2.]) == np.array([1., 1.])).all()\n\ntest_fmod()\n\n@test\ndef test_mod():\n    assert (np.mod([4, 7], [2, 3]) == np.array([0, 1])).all()\n    assert (np.mod(np.arange(7), 5) == np.array([0, 1, 2, 3, 4, 0, 1])).all()\n\ntest_mod()\n\n@test\ndef test_modf():\n    x1, x2 = np.modf([0, 3.5])\n    assert (x1 == array([0., 0.5])).all() and (x2 == np.array([0., 3.])).all()\n    x1, x2 = np.modf(-0.5)\n    assert x1 == -0.5 and x2 == -0\n\ntest_modf()\n\n@test\ndef test_remainder():\n    assert (np.remainder([4, 7], [2, 3]) == np.array([0, 1])).all()\n    assert (np.remainder(np.arange(7), 5) == np.array([0, 1, 2, 3, 4, 0,\n                                                       1])).all()\n\ntest_remainder()\n\n@test\ndef test_divmod():\n    x1, x2 = divmod(np.arange(5), 3)\n    assert (x1 == np.array([0, 0, 0, 1, 1])).all() and (x2 == np.array(\n        [0, 1, 2, 0, 1])).all()\n\ntest_divmod()\n\n###########\n# Complex #\n###########\n\n@test\ndef test_real():\n    a = np.array([1 + 2j, 3 + 4j, 5 + 6j])\n    assert (a.real == np.array([1., 3., 5.])).all()\n\n#    a.real = 9\n#    assert (a == np.array([9.+2.j,  8.+4.j,  7.+6.j])).all()\n#    a.real = np.array([9, 8, 7])\n#    assert (a == np.array([9.+2.j,  8.+4.j,  7.+6.j])).all()\n\ntest_real()\n\n@test\ndef test_imag():\n    a = np.array([1 + 2j, 3 + 4j, 5 + 6j])\n    assert (a.imag == np.array([2., 4., 6.])).all()\n\n#    a.imag = 9\n#    assert (a == np.array([1.+9.j,  3.+9.j,  5.+9.j])).all()\n#    a.imag = np.array([9, 8, 7])\n#    assert (a == np.array([1.+9.j,  3.+8.j,  5.+7.j])).all()\n\ntest_imag()\n\n@test\ndef test_conjugate():\n    assert np.conj(1 + 2j) == (1 - 2j)\n    assert np.conjugate(1 + 2j) == (1 - 2j)\n    x = array([[1. + 1.j, 0. + 0.j], [0. + 0.j, 1. + 1.j]])\n    assert (np.conj(x) == np.array([[1. - 1.j, 0. - 0.j], [0. - 0.j,\n                                                           1. - 1.j]])).all()\n    assert (np.conjugate(x) == np.array([[1. - 1.j, 0. - 0.j],\n                                         [0. - 0.j, 1. - 1.j]])).all()\n\ntest_conjugate()\n\n@test\ndef test_maximum():\n    assert np.maximum(np.inf, 1) == np.inf\n    assert (np.maximum([2, 3, 4], [1, 5, 2]) == np.array([2, 5, 4])).all()\n    assert (np.maximum(np.eye(2), [0.5, 2]) == np.array([[1., 2.],\n                                                         [0.5, 2.]])).all()\n    assert isnan(np.maximum(np.nan, 0))\n\ntest_maximum()\n\n@test\ndef test_max():\n    a = np.arange(4).reshape((2, 2))\n    assert a.max() == 3\n    assert (a.max(axis=0) == np.array([2, 3])).all()\n    assert (a.max(axis=1) == np.array([1, 3])).all()\n\ntest_max()\n\n@test\ndef test_fmax():\n    assert (np.fmax([2, 3, 4], [1, 5, 2]) == np.array([2., 5., 4.])).all()\n    assert (np.fmax(np.eye(2), [0.5, 2]) == np.array([[1., 2.], [0.5,\n                                                                 2.]])).all()\n    assert np.fmax(np.nan, 0) == 0.\n\ntest_fmax()\n\n@test\ndef test_minimum():\n    assert (np.minimum([2, 3, 4], [1, 5, 2]) == np.array([1, 3, 2])).all()\n    assert (np.minimum(np.eye(2), [0.5, 2]) == np.array([[0.5, 0.],\n                                                         [0., 1.]])).all()\n    assert np.minimum(-np.inf, 1) == -np.inf\n    assert isnan(np.minimum(np.nan, 0))\n\ntest_minimum()\n\n@test\ndef test_min():\n    a = np.arange(4).reshape((2, 2))\n    assert a.min() == 0\n    assert (a.min(axis=0) == np.array([0, 1])).all()\n    assert (a.min(axis=1) == np.array([0, 2])).all()\n\ntest_min()\n\n@test\ndef test_fmin():\n    assert (np.fmin([2, 3, 4], [1, 5, 2]) == np.array([1., 3., 2.])).all()\n    assert (np.fmin(np.eye(2), [0.5, 2]) == np.array([[0.5, 0.], [0.,\n                                                                  1.]])).all()\n    assert np.fmin(np.nan, 0) == 0.\n\ntest_fmin()\n\n@test\ndef test_clip():\n    a = np.arange(10)\n    assert (a.clip(1, 8) == np.array([1, 1, 2, 3, 4, 5, 6, 7, 8, 8])).all()\n    assert (a.clip(3, 6) == np.array([3, 3, 3, 3, 4, 5, 6, 6, 6, 6])).all()\n    assert (a.clip([3, 4, 1, 1, 1, 4, 4, 4, 4, 4],\n                   8) == np.array([3, 4, 2, 3, 4, 5, 6, 7, 8, 8])).all()\n    assert (np.clip(a, 1, 8) == np.array([1, 1, 2, 3, 4, 5, 6, 7, 8, 8])).all()\n    assert (np.clip(a, 3, 6) == np.array([3, 3, 3, 3, 4, 5, 6, 6, 6, 6])).all()\n    assert (np.clip(a, [3, 4, 1, 1, 1, 4, 4, 4, 4, 4],\n                    8) == np.array([3, 4, 2, 3, 4, 5, 6, 7, 8, 8])).all()\n\ntest_clip()\n\n@test\ndef test_sqrt():\n    assert np.sqrt(49) == 7.0\n    assert np.sqrt(1) == 1.0\n    assert (np.sqrt([1, 4]) == np.array([1., 2.])).all()\n    assert np.isnan(np.sqrt(-1))\n    assert (np.sqrt([4 + 0j, -1 + 0j,\n                     -3 + 4j]) == np.array([2. + 0.j, 0. + 1.j,\n                                            1. + 2.j])).all()\n\ntest_sqrt()\n\n@test\ndef test_cbrt():\n    assert (np.cbrt([1, 8]) == np.array([1.0, 2.0])).all()\n    assert (np.cbrt([-1, -8]) == np.array([-1.0, -2.0])).all()\n    assert np.cbrt(0) == 0.0\n    assert isnan(np.cbrt(np.nan))\n    assert np.cbrt(-np.inf) == -np.inf\n\ntest_cbrt()\n\n@test\ndef test_square():\n    assert (np.square([1, 2, 3]) == np.array([1, 4, 9])).all()\n    assert (np.square([-1, -2, -3]) == np.array([1, 4, 9])).all()\n    assert (np.square([-1j, 1 + 0j]) == np.array([-1. - 0.j, 1. + 0.j])).all()\n    assert isnan(np.square(np.nan))\n\ntest_square()\n\n@test\ndef test_absolute():\n    assert (np.absolute(np.array([-1.2, 1.2])) == np.array([1.2, 1.2])).all()\n    assert np.absolute(1.2 + 1j) == 1.5620499351813308\n    assert (np.abs(np.array([-1.2, 1.2])) == np.array([1.2, 1.2])).all()\n    assert np.absolute(-np.inf) == np.inf\n\ntest_absolute()\n\n@test\ndef test_fabs():\n    assert np.fabs(-1) == 1.0\n    assert (np.fabs([-1.2, 1.2]) == np.array([1.2, 1.2])).all()\n    assert np.fabs(-np.inf) == np.inf\n\ntest_fabs()\n\n@test\ndef test_sign():\n    assert (np.sign([-5., 4.5]) == np.array([-1., 1.])).all()\n    assert np.sign(0) == 0\n    assert np.sign(5 - 2j) == (1 + 0j)\n\ntest_sign()\n\n@test\ndef test_heaviside():\n    assert (np.heaviside([-1.5, 0, 2.0], 0.5) == np.array([0., 0.5, 1.])).all()\n    assert (np.heaviside([-1.5, 0, 2.0], 1) == np.array([0., 1., 1.])).all()\n\ntest_heaviside()\n\n@test\ndef test_mean():\n    a = np.array([[1, 2], [3, 4]])\n    assert a.mean() == 2.5\n    assert (a.mean(axis=0) == np.array([2., 3.])).all()\n    assert (a.mean(axis=1) == np.array([1.5, 3.5])).all()\n    b = np.zeros((2, 512 * 512))\n    b[0, :] = 1.0\n    b[1, :] = 0.1\n    assert round(b.mean(), 8) == 0.550000\n    assert round(np.array([10**8, 10**6, 10**(-3)]).mean(), 3) == 33666666.667\n\ntest_mean()\n\n@test\ndef test_var():\n    a = np.array([[1, 2], [3, 4]])\n    assert a.var() == 1.25\n    assert (a.var(axis=0) == np.array([1., 1.])).all()\n    assert (a.var(axis=1) == np.array([0.25, 0.25])).all()\n    b = np.zeros((2, 512 * 512))\n    b[0, :] = 1.0\n    b[1, :] = 0.1\n    assert round(b.var(), 4) == 0.2025\n\n#    assert round(np.array([10**4, 10**6, 10**(-3)]).var(), 4) == 220022221997.7778\n\ntest_var()\n\n@test\ndef test_std():\n    a = np.array([[1, 2], [3, 4]])\n    assert a.std() == 1.1180339887498949\n    assert (a.std(axis=0) == np.array([1., 1.])).all()\n    assert (a.std(axis=1) == np.array([0.5, 0.5])).all()\n    b = np.zeros((2, 512 * 512))\n    b[0, :] = 1.0\n    b[1, :] = 0.1\n    assert round(b.std(), 4) == 0.4500\n    assert np.array([-2, -5, 7, 12, 0.4]).std() == 6.187212619588888\n\n#    assert np.array([10**4, 10**6, 10**(-3)]).std() == 469065.\n\ntest_std()\n\n@test\ndef test_ptp():\n    x = np.array([[4, 9, 2, 10], [6, 9, 7, 12]])\n    assert x.ptp() == 10\n    assert (x.ptp(axis=0) == np.array([2, 0, 5, 2])).all()\n    assert (x.ptp(axis=1) == np.array([8, 6])).all()\n    y = np.array([[1, 127], [0, 127], [-1, 127], [-2, 127]])\n    assert (y.ptp(axis=1) == np.array([126, 127, 128, 129])).all()\n    assert np.array([-8, -12, -0.5, -2, -4]).ptp() == 11.5\n\ntest_ptp()\n\n#########\n# Logic #\n#########\n\n@test\ndef test_any():\n    assert np.any([[True, False], [True, True]])\n    assert np.any([-1, 0, 5])\n    assert not np.any([0, 0, 0])\n    assert (np.array([[True, False],\n                      [False, False]]).any(axis=0) == np.array([True,\n                                                                False])).all()\n    assert (np.any([[True, False], [False, False]],\n                   axis=0) == np.array([True, False])).all()\n    assert not np.any([[True, False], [False, False]], where=[[False], [True]])\n    assert np.any(np.nan)\n\ntest_any()\n\n@test\ndef test_all():\n    assert not np.all([[True, False], [True, True]])\n    assert np.all([-1, 1, 5])\n    assert not np.all([-1, 0, 5])\n    assert (np.array([[True, False],\n                      [True, True]]).all(axis=0) == np.array([True,\n                                                              False])).all()\n    assert (np.all([[True, False], [True, True]],\n                   axis=0) == np.array([True, False])).all()\n    assert np.all([[True, True], [False, True]], where=[[True], [False]])\n    assert np.all(np.nan)\n\ntest_all()\n\n@test\ndef test_i0():\n    assert np.i0(0) == np.array(1.)\n    assert np.allclose(np.i0([0, 1, 2, 3]),\n                       np.array([1., 1.26606588, 2.2795853, 4.88079259]))\n    assert np.allclose(np.i0([[0, 1], [2, 3]]),\n                       np.array([[1., 1.26606588], [2.2795853, 4.88079259]]))\n    assert np.allclose(np.i0([8, 9]), np.array([427.56411572, 1093.58835451]))\n\n    assert np.allclose(np.i0(np.float32(0)), np.array(np.float32(1.)))\n    assert np.allclose(\n        np.i0(np.array([0, 1, 2, 3], np.float32)),\n        np.array([1., 1.26606588, 2.2795853, 4.88079259], np.float32))\n    assert np.allclose(\n        np.i0(np.array([[0, 1], [2, 3]], np.float32)),\n        np.array([[1., 1.26606588], [2.2795853, 4.88079259]], np.float32))\n    assert np.allclose(np.i0(np.array([8, 9], np.float32)),\n                       np.array([427.56411572, 1093.58835451], np.float32))\n\ntest_i0()\n\n@test\ndef test_sinc():\n    assert np.allclose(np.sinc(2), -3.8981718325193755e-17)\n    assert np.sinc(0.0) == 1.0\n    assert np.allclose(np.sinc(1 + 2j),\n                       -34.09033869939481 - 17.04516934969741j)\n    got = np.sinc(np.linspace(-4, 4, 41))\n    exp = np.array([\n        -3.89804309e-17, -4.92362781e-02, -8.40918587e-02, -8.90384387e-02,\n        -5.84680802e-02, 3.89804309e-17, 6.68206631e-02, 1.16434881e-01,\n        1.26137788e-01, 8.50444803e-02, -3.89804309e-17, -1.03943254e-01,\n        -1.89206682e-01, -2.16236208e-01, -1.55914881e-01, 3.89804309e-17,\n        2.33872321e-01, 5.04551152e-01, 7.56826729e-01, 9.35489284e-01,\n        1.00000000e+00, 9.35489284e-01, 7.56826729e-01, 5.04551152e-01,\n        2.33872321e-01, 3.89804309e-17, -1.55914881e-01, -2.16236208e-01,\n        -1.89206682e-01, -1.03943254e-01, -3.89804309e-17, 8.50444803e-02,\n        1.26137788e-01, 1.16434881e-01, 6.68206631e-02, 3.89804309e-17,\n        -5.84680802e-02, -8.90384387e-02, -8.40918587e-02, -4.92362781e-02,\n        -3.89804309e-17\n    ])\n    assert np.allclose(got, exp)\n\ntest_sinc()\n"
  },
  {
    "path": "test/numpy/test_npdatetime.codon",
    "content": "import numpy as np\nfrom numpy import *\n\n@test\ndef test_compare_generic_nat():\n    # regression tests for gh-6452\n    assert np.datetime64('NaT') != (np.datetime64('2000') +\n                                    np.timedelta64('NaT'))\n    assert np.datetime64('NaT') != np.datetime64('NaT', 'us')\n    assert np.datetime64('NaT', 'us') != np.datetime64('NaT')\n\ntest_compare_generic_nat()\n\n@test\ndef test_datetime_nat_argsort_stability():\n    # NaT < NaT should be False internally for sort stability\n    sizes = [3, 21, 217, 1000]\n    for size in sizes:\n        expected = np.arange(size)\n        arr = np.tile(np.datetime64('NaT'), size)\n        assert array_equal(np.argsort(arr, kind='mergesort'), expected)\n\ntest_datetime_nat_argsort_stability()\n\ndef test_datetime_maximum_reduce():\n    a = np.array(['2010-01-02', '1999-03-14', '1833-03'], np.datetime64['D',\n                                                                        1])\n    assert isinstance(np.maximum.reduce(a), np.datetime64['D', 1])\n    assert np.maximum.reduce(a) == np.datetime64('2010-01-02')\n\n    a = np.array([1, 4, 0, 7, 2], np.timedelta64['s', 1])\n    assert isinstance(np.maximum.reduce(a), np.timedelta64['s', 1])\n    assert np.maximum.reduce(a) == np.timedelta64(7, 's')\n\ntest_datetime_maximum_reduce()\n\n@test\ndef test_timedelta_nat_argsort_stability():\n    # NaT < NaT should be False internally for sort stability\n    sizes = [3, 21, 217, 1000]\n    for size in sizes:\n        expected = np.arange(size)\n        arr = np.tile(np.timedelta64('NaT'), size)\n        assert array_equal(np.argsort(arr, kind='mergesort'), expected)\n\ntest_timedelta_nat_argsort_stability()\n\n@test\ndef test_datetime_timedelta_sort_nat():\n    arr = (\n        # the example provided in gh-12629\n        ('NaT', 1, 2, 3),\n        (\n            'NaT',\n            1,\n            -2,\n            3,\n        ))\n\n    expected = ((1, 2, 3, 'NaT'), (-2, 1, 3, 'NaT'))\n\n    dtypes = ['M8[ns]', 'M8[us]']\n    for i in range(2):\n        arr1 = np.array(arr[i], dtype=dtypes[i])\n\n        expect = np.array(expected[i], dtype=dtypes[i])\n        arr1.sort()\n        assert array_equal(arr1, expect)\n\ntest_datetime_timedelta_sort_nat()\n\n@test\ndef test_datetime_scalar_construction():\n    # Construct with different units\n    assert np.datetime64('1950-03-12', 'D') == np.datetime64('1950-03-12')\n    assert np.datetime64('1950-03-12T13',\n                         's') == np.datetime64('1950-03-12T13', 'm')\n\n    # Default construction means NaT\n    # TODO: Datetme64 currently does not have default constructor.\n    # assert array_equal(np.datetime64(), np.datetime64('NaT'))\n\n    # Some basic strings and repr\n    assert str(np.datetime64('NaT')) == 'NaT'\n    assert repr(np.datetime64('NaT')) == \"numpy.datetime64('NaT', 'us')\"\n    assert str(np.datetime64('2011-02')) == '2011-02-01T00:00:00.000000'\n    assert repr(np.datetime64(\n        '2011-02')) == \"numpy.datetime64('2011-02-01T00:00:00.000000', 'us')\"\n\n    # Construction with time units from a date is okay\n    assert np.datetime64('1920-03-13', 'h') == np.datetime64('1920-03-13T00')\n    assert np.datetime64('1920-03', 'm') == np.datetime64('1920-03-01T00:00')\n    assert np.datetime64('1920', 's') == np.datetime64('1920-01-01T00:00:00')\n    assert np.datetime64(datetime.date(2045, 3, 25),\n                         'ms') == np.datetime64('2045-03-25T00:00:00.000')\n\n    # Construction with date units from a datetime is also okay\n    assert np.datetime64('1920-03-13T18', 'D') == np.datetime64('1920-03-13')\n    assert np.datetime64('1920-03-13T18:33:12',\n                         'M') == np.datetime64('1920-03')\n    assert np.datetime64('1920-03-13T18:33:12.5', 'Y') == np.datetime64('1920')\n\n    # None gets constructed as NaT\n    # TODO: Datetme64 currently does not work without None parameter as well.\n    # assert np.datetime64(None) == np.datetime64('NaT')\n\n    # Default construction of NaT is in generic units\n    # TODO: Datetme64 currently does not have default constructor.\n    # assert np.datetime64().dtype == dtype = 'M8'\n    assert isinstance(np.datetime64('NaT', 'h'), np.datetime64['h', 1])\n\n    # When constructing from a scalar or zero-dimensional array,\n    # it either keeps the units or you can override them.\n    a = np.datetime64('2000-03-18T16', 'h')\n    b = np.array('2000-03-18T16', dtype=np.datetime64['h', 1])\n\n    assert isinstance(a, np.datetime64['h', 1])\n    assert isinstance(b.dtype, np.datetime64['h', 1])\n    assert isinstance(np.datetime64(a, 'h'), np.datetime64['h', 1])\n    assert np.datetime64(a, 'h') == a\n\n    assert b == a\n    # assert isinstance(np.datetime64(b, 'h'), np.datetime64['h',1])\n    # print(np.datetime64(b, 'h'))\n\n    assert np.datetime64(a, 's') == a\n    assert isinstance(np.datetime64(a, 's'), np.datetime64['s', 1])\n\n    # assert array_equal(np.datetime64(b, 's'), a)\n    # assert array_equal(np.datetime64(b, 's').dtype, dtype='M8[s]')\n\n    # Construction from datetime.date\n    assert np.datetime64('1945-03-25') == np.datetime64(\n        datetime.date(1945, 3, 25))\n    assert np.datetime64('2045-03-25',\n                         'D') == np.datetime64(datetime.date(2045, 3, 25), 'D')\n    # Construction from datetime.datetime\n    assert np.datetime64('1980-01-25T14:36:22.5') == np.datetime64(\n        datetime.datetime(1980, 1, 25, 14, 36, 22, 500000))\n\n    # Construction with time units from a date is okay\n    assert np.datetime64('1920-03-13',\n                         'h') == np.datetime64('1920-03-13T00', 'h')\n    assert np.datetime64('1920-03',\n                         'm') == np.datetime64('1920-03-01T00:00', 'm')\n    assert np.datetime64('1920', 's') == np.datetime64('1920-01-01T00:00:00',\n                                                       's')\n    assert np.datetime64(datetime.date(2045, 3, 25),\n                         'ms') == np.datetime64('2045-03-25T00:00:00.000',\n                                                'ms')\n\n    # Construction with date units from a datetime is also okay\n    assert np.datetime64('1920-03-13T18',\n                         'D') == np.datetime64('1920-03-13', 'D')\n    assert np.datetime64('1920-03-13T18:33:12',\n                         'M') == np.datetime64('1920-03', 'M')\n    assert np.datetime64('1920-03-13T18:33:12.5',\n                         'Y') == np.datetime64('1920', 'Y')\n\ntest_datetime_scalar_construction()\n\n@test\ndef test_timedelta_np_int_construction():\n    assert np.timedelta64(np.int64(123), \"Y\") == np.timedelta64(123, \"Y\")\n    assert np.timedelta64(np.int64(123), \"M\") == np.timedelta64(123, \"M\")\n    assert np.timedelta64(np.int64(123), \"W\") == np.timedelta64(123, \"W\")\n    assert np.timedelta64(np.int64(123), \"D\") == np.timedelta64(123, \"D\")\n    assert np.timedelta64(np.int64(123), \"h\") == np.timedelta64(123, \"h\")\n    assert np.timedelta64(np.int64(123), \"m\") == np.timedelta64(123, \"m\")\n    assert np.timedelta64(np.int64(123), \"s\") == np.timedelta64(123, \"s\")\n    assert np.timedelta64(np.int64(123), \"ms\") == np.timedelta64(123, \"ms\")\n    assert np.timedelta64(np.int64(123), \"us\") == np.timedelta64(123, \"us\")\n    assert np.timedelta64(np.int64(123), \"ns\") == np.timedelta64(123, \"ns\")\n    assert np.timedelta64(np.int64(123), \"ps\") == np.timedelta64(123, \"ps\")\n    assert np.timedelta64(np.int64(123), \"fs\") == np.timedelta64(123, \"fs\")\n    assert np.timedelta64(np.int64(123), \"as\") == np.timedelta64(123, \"as\")\n\ntest_timedelta_np_int_construction()\n\n@test()\ndef test_timedelta_scalar_construction():\n    # Construct with different units\n    assert np.timedelta64(7, 'D') == np.timedelta64(1, 'W')\n    assert np.timedelta64(120, 's') == np.timedelta64(2, 'm')\n\n    # Default construction means 0\n    # TODO: Timedelta64 currently does not have default constructor.\n    # assert np.timedelta64() == np.timedelta64(0)\n\n    # None gets constructed as NaT\n    # assert np.timedelta64(None) == np.timedelta64('NaT')\n\n    # Some basic strings and repr\n    assert str(np.timedelta64('NaT')) == 'NaT'\n    assert repr(np.timedelta64('NaT')) == \"numpy.timedelta64(NaT, 'generic')\"\n    assert str(np.timedelta64(3, 's')) == '3 seconds'\n    assert repr(np.timedelta64(-3, 's')) == \"numpy.timedelta64(-3)\"\n    assert repr(np.timedelta64(12, 's')) == \"numpy.timedelta64(12)\"\n\n    # Construction from an integer produces generic units\n    assert isinstance(np.timedelta64(12, 'h'), np.timedelta64['h', 1])\n\n    # When constructing from a scalar or zero-dimensional array,\n    # it either keeps the units or you can override them.\n    a = np.timedelta64(2, 'h')\n    b = np.array(2, dtype=np.timedelta64['h', 1])\n\n    assert isinstance(a, np.timedelta64['h', 1])\n    assert isinstance(b.dtype, np.timedelta64['h', 1])\n\n    assert np.timedelta64(a, 'h') == a\n\n    assert isinstance(a, np.timedelta64['h', 1])\n    assert isinstance(b.dtype, np.timedelta64['h', 1])\n    assert isinstance(np.timedelta64(a, 'h'), np.timedelta64['h', 1])\n\n    assert np.timedelta64(a, 's') == a\n    assert isinstance(np.timedelta64(a, 's'), np.timedelta64['s', 1])\n\n    # Construction from datetime.timedelta\n    assert np.timedelta64(5, 'D') == np.timedelta64(datetime.timedelta(days=5))\n    assert np.timedelta64(102347621, 's') == np.timedelta64(\n        datetime.timedelta(seconds=102347621))\n    assert np.timedelta64(-10234760000, 'us') == np.timedelta64(\n        datetime.timedelta(microseconds=-10234760000))\n    assert np.timedelta64(10234760000, 'us') == np.timedelta64(\n        datetime.timedelta(microseconds=10234760000))\n    assert np.timedelta64(1023476, 'ms') == np.timedelta64(\n        datetime.timedelta(milliseconds=1023476))\n    assert np.timedelta64(10, 'm') == np.timedelta64(\n        datetime.timedelta(minutes=10))\n    assert np.timedelta64(281,\n                          'h') == np.timedelta64(datetime.timedelta(hours=281))\n    assert np.timedelta64(28,\n                          'W') == np.timedelta64(datetime.timedelta(weeks=28))\n\ntest_timedelta_scalar_construction()\n\n@test()\ndef test_timedelta_array_str():\n    a = np.array([-1, 0, 100], dtype='m')\n    assert np.array_equal(a, np.array(['-1', '0', '100']))\n    a = np.array(['NaT', 'NaT'], dtype='m')\n    assert np.array_equal(a, np.array(['NaT', 'NaT']))\n    # Check right-alignment with NaTs\n    a = np.array(('NaT', -1, 0), dtype='m')\n    assert np.array_equal(a, np.array(['NaT', '-1', '0']))\n\ntest_timedelta_array_str()\n\n@test\ndef test_datetime_array_find_type():\n    dt = np.datetime64('1970-01-01', 'M')\n    arr = np.array([dt])\n    assert isinstance(arr.dtype, np.datetime64['M', 1])\n\ntest_datetime_array_find_type()\n\n@test\ndef test_datetime_nat_casting():\n    a = np.array('NaT', dtype='M8[D]')\n    b = np.datetime64('NaT', 'D')\n\n    assert np.isnat(a.astype(np.datetime64['s', 1]),\n                    np.array('NaT', np.datetime64['s', 1]))\n    assert np.isnat(a.astype(np.datetime64['ms', 1]),\n                    np.array('NaT', np.datetime64['ms', 1]))\n    assert np.isnat(a.astype(np.datetime64['M', 1]),\n                    np.array('NaT', np.datetime64['M', 1]))\n    assert np.isnat(a.astype(np.datetime64['Y', 1]),\n                    np.array('NaT', np.datetime64['Y', 1]))\n    assert np.isnat(a.astype(np.datetime64['W', 1]),\n                    np.array('NaT', np.datetime64['W', 1]))\n    # Scalars -> Scalars\n    assert np.isnat(np.datetime64(b, 's'))\n    assert np.isnat(np.datetime64(b, 'ms'))\n    assert np.isnat(np.datetime64(b, 'M'))\n    assert np.isnat(np.datetime64(b, 'Y'))\n    assert np.isnat(np.datetime64(b, 'W'))\n\n    # Arrays -> Scalars\n    # assert np.datetime64(a, 's') == np.datetime64('NaT', 's')\n    # assert np.datetime64(a, 'ms]') == np.datetime64('NaT', 'ms')\n    # assert np.datetime64(a, 'M') == np.datetime64('NaT', 'M')\n    # assert np.datetime64(a, 'Y') == np.datetime64('NaT', 'Y')\n    # assert np.datetime64(a, 'W') == np.datetime64('NaT', 'W')\n\n    nan = np.array((np.nan, ) * 8 + (0, ))\n    fnan = nan.astype(np.float32)\n    lnan = nan.astype(np.float128)\n    cnan = nan.astype(complex)\n    cfnan = nan.astype(np.complex64)\n    clnan = nan.astype(np.complex128)\n    nat = np.array([np.datetime64('NaT')] * 8 + [np.datetime64(0, 'us')])\n    assert np.array_equal(nan.astype(np.datetime64['ns', 1]),\n                          nat,\n                          equal_nan=True)\n    assert np.array_equal(fnan.astype(np.datetime64['ns', 1]),\n                          nat,\n                          equal_nan=True)\n    assert np.array_equal(cnan.astype(np.datetime64['ns', 1]),\n                          nat,\n                          equal_nan=True)\n    assert np.array_equal(cfnan.astype(np.datetime64['ns', 1]),\n                          nat,\n                          equal_nan=True)\n    assert np.array_equal(clnan.astype(np.datetime64['ns', 1]),\n                          nat,\n                          equal_nan=True)\n\n    nat = np.array([np.timedelta64('NaT', 'us')] * 8 +\n                   [np.timedelta64(0, 'us')])\n    assert np.array_equal(nan.astype(np.timedelta64['ns', 1]),\n                          nat,\n                          equal_nan=True)\n    assert np.array_equal(fnan.astype(np.timedelta64['ns', 1]),\n                          nat,\n                          equal_nan=True)\n    assert np.array_equal(cnan.astype(np.timedelta64['ns', 1]),\n                          nat,\n                          equal_nan=True)\n    assert np.array_equal(cfnan.astype(np.timedelta64['ns', 1]),\n                          nat,\n                          equal_nan=True)\n    assert np.array_equal(clnan.astype(np.timedelta64['ns', 1]),\n                          nat,\n                          equal_nan=True)\n\ntest_datetime_nat_casting()\n\n@test\ndef test_pydatetime_creation():\n    a = np.array(('1960-03-12', datetime.date(1960, 3, 12)), dtype='M8[D]')\n    assert a[0] == a[1]\n    a = np.array(('1999-12-31', datetime.date(1999, 12, 31)), dtype='M8[D]')\n    assert a[0] == a[1]\n    a = np.array(('2000-01-01', datetime.date(2000, 1, 1)), dtype='M8[D]')\n    assert a[0] == a[1]\n\ntest_pydatetime_creation()\n\n@test\ndef test_timedelta_scalar_construction_units():\n    # String construction detecting units\n    assert isinstance(np.datetime64('2010', 'Y'), np.datetime64['Y', 1])\n    assert isinstance(np.datetime64('2010-03', 'M'), np.datetime64['M', 1])\n    assert isinstance(np.datetime64('2010-03-12', 'D'), np.datetime64['D', 1])\n    assert isinstance(np.datetime64('2010-03-12T17', 'h'), np.datetime64['h',\n                                                                         1])\n    assert isinstance(np.datetime64('2010-03-12T17:15', 'm'),\n                      np.datetime64['m', 1])\n    assert isinstance(np.datetime64('2010-03-12T17:15:08', 's'),\n                      np.datetime64['s', 1])\n\n    assert isinstance(np.datetime64('2010-03-12T17:15:08.1', 'ms'),\n                      np.datetime64['ms', 1])\n    assert isinstance(np.datetime64('2010-03-12T17:15:08.12', 'ms'),\n                      np.datetime64['ms', 1])\n    assert isinstance(np.datetime64('2010-03-12T17:15:08.123', 'ms'),\n                      np.datetime64['ms', 1])\n\n    assert isinstance(np.datetime64('2010-03-12T17:15:08.1234', 'us'),\n                      np.datetime64['us', 1])\n    assert isinstance(np.datetime64('2010-03-12T17:15:08.12345', 'us'),\n                      np.datetime64['us', 1])\n    assert isinstance(np.datetime64('2010-03-12T17:15:08.123456', 'us'),\n                      np.datetime64['us', 1])\n\n    assert isinstance(np.datetime64('1970-01-01T00:00:02.1234567', 'ns'),\n                      np.datetime64['ns', 1])\n    assert isinstance(np.datetime64('1970-01-01T00:00:02.12345678', 'ns'),\n                      np.datetime64['ns', 1])\n    assert isinstance(np.datetime64('1970-01-01T00:00:02.123456789', 'ns'),\n                      np.datetime64['ns', 1])\n\n    assert isinstance(np.datetime64('1970-01-01T00:00:02.1234567890', 'ps'),\n                      np.datetime64['ps', 1])\n    assert isinstance(np.datetime64('1970-01-01T00:00:02.12345678901', 'ps'),\n                      np.datetime64['ps', 1])\n    assert isinstance(np.datetime64('1970-01-01T00:00:02.123456789012', 'ps'),\n                      np.datetime64['ps', 1])\n\n    assert isinstance(np.datetime64('1970-01-01T00:00:02.1234567890123', 'fs'),\n                      np.datetime64['fs', 1])\n    assert isinstance(\n        np.datetime64('1970-01-01T00:00:02.12345678901234', 'fs'),\n        np.datetime64['fs', 1])\n    assert isinstance(\n        np.datetime64('1970-01-01T00:00:02.123456789012345', 'fs'),\n        np.datetime64['fs', 1])\n\n    assert isinstance(\n        np.datetime64('1970-01-01T00:00:02.1234567890123456', 'as'),\n        np.datetime64['as', 1])\n    assert isinstance(\n        np.datetime64('1970-01-01T00:00:02.12345678901234567', 'as'),\n        np.datetime64['as', 1])\n    assert isinstance(\n        np.datetime64('1970-01-01T00:00:02.123456789012345678', 'as'),\n        np.datetime64['as', 1])\n\n    # Python date object\n    assert isinstance(np.datetime64(datetime.date(2010, 4, 16)),\n                      np.datetime64['D', 1])\n\n    # Python datetime object\n    assert isinstance(\n        np.datetime64(datetime.datetime(2010, 4, 16, 13, 45, 18)),\n        np.datetime64['us', 1])\n\n    # 'today' special value\n    assert isinstance(np.datetime64('today'), np.datetime64['us', 1])\n\n    # 'now' special value\n    assert isinstance(np.datetime64('now'), np.datetime64['us', 1])\n\ntest_timedelta_scalar_construction_units()\n\n@test()\ndef test_days_creation():\n    assert np.array_equal(\n        np.array('1599', np.datetime64['D', 1]).astype(int),\n        np.array((1600 - 1970) * 365 - (1972 - 1600) / 4 + 3 - 365))\n    assert np.array_equal(\n        np.array('1600', np.datetime64['D', 1]).astype(int),\n        np.array((1600 - 1970) * 365 - (1972 - 1600) / 4 + 3))\n    assert np.array_equal(\n        np.array('1601', np.datetime64['D', 1]).astype(int),\n        np.array((1600 - 1970) * 365 - (1972 - 1600) / 4 + 3 + 366))\n    assert np.array_equal(\n        np.array('1900', np.datetime64['D', 1]).astype(int),\n        np.array((1900 - 1970) * 365 - (1970 - 1900) // 4))\n    assert np.array_equal(\n        np.array('1901', np.datetime64['D', 1]).astype(int),\n        np.array((1900 - 1970) * 365 - (1970 - 1900) // 4 + 365))\n    assert np.array_equal(\n        np.array('1967', np.datetime64['D', 1]).astype(int),\n        np.array(-3 * 365 - 1))\n    assert np.array_equal(\n        np.array('1968', np.datetime64['D', 1]).astype(int),\n        np.array(-2 * 365 - 1))\n    assert np.array_equal(\n        np.array('1969', np.datetime64['D', 1]).astype(int),\n        np.array(-1 * 365))\n    assert np.array_equal(\n        np.array('1970', np.datetime64['D', 1]).astype(int), np.array(0 * 365))\n    assert np.array_equal(\n        np.array('1971', np.datetime64['D', 1]).astype(int), np.array(1 * 365))\n    assert np.array_equal(\n        np.array('1972', np.datetime64['D', 1]).astype(int), np.array(2 * 365))\n    assert np.array_equal(\n        np.array('1973', np.datetime64['D', 1]).astype(int),\n        np.array(3 * 365 + 1))\n    assert np.array_equal(\n        np.array('1974', np.datetime64['D', 1]).astype(int),\n        np.array(4 * 365 + 1))\n    assert np.array_equal(\n        np.array('2000', np.datetime64['D', 1]).astype(int),\n        np.array((2000 - 1970) * 365 + (2000 - 1972) // 4))\n    assert np.array_equal(\n        np.array('2001', np.datetime64['D', 1]).astype(int),\n        np.array((2000 - 1970) * 365 + (2000 - 1972) // 4 + 366))\n    assert np.array_equal(\n        np.array('2400', np.datetime64['D', 1]).astype(int),\n        np.array((2400 - 1970) * 365 + (2400 - 1972) // 4 - 3))\n    assert np.array_equal(\n        np.array('2401', np.datetime64['D', 1]).astype(int),\n        np.array((2400 - 1970) * 365 + (2400 - 1972) // 4 - 3 + 366))\n\n    assert np.array_equal(\n        np.array('1600-02-29', np.datetime64['D', 1]).astype(int),\n        np.array((1600 - 1970) * 365 - (1972 - 1600) // 4 + 3 + 31 + 28))\n    assert np.array_equal(\n        np.array('1600-03-01', np.datetime64['D', 1]).astype(int),\n        np.array((1600 - 1970) * 365 - (1972 - 1600) // 4 + 3 + 31 + 29))\n    assert np.array_equal(\n        np.array('2000-02-29', np.datetime64['D', 1]).astype(int),\n        np.array((2000 - 1970) * 365 + (2000 - 1972) // 4 + 31 + 28))\n    assert np.array_equal(\n        np.array('2000-03-01', np.datetime64['D', 1]).astype(int),\n        np.array((2000 - 1970) * 365 + (2000 - 1972) // 4 + 31 + 29))\n    assert np.array_equal(\n        np.array('2001-03-22', np.datetime64['D', 1]).astype(int),\n        np.array((2000 - 1970) * 365 + (2000 - 1972) // 4 + 366 + 31 + 28 +\n                 21))\n\ntest_days_creation()\n\n@test\ndef test_datetime_string_conversion():\n    a = ['2011-03-16', '1920-01-01', '2013-05-19']\n    str_a = np.array(a, dtype='S')\n    uni_a = np.array(a, dtype='U')\n    dt_a = np.array(a, np.datetime64['D', 1])\n    # String to datetime\n    assert np.array_equal(dt_a, str_a.astype(np.datetime64['D', 1]))\n    assert isinstance(dt_a.dtype, str_a.astype(np.datetime64['D', 1]).dtype)\n    dt_b = np.empty_like(dt_a)\n    dt_b[...] = str_a\n    assert np.array_equal(dt_a, dt_b)\n\n    assert np.array_equal(dt_a, uni_a.astype(np.datetime64['D', 1]))\n    assert isinstance(dt_a.dtype, uni_a.astype(np.datetime64['D', 1]).dtype)\n    dt_b = np.empty_like(dt_a)\n    dt_b[...] = uni_a\n    assert np.array_equal(dt_a, dt_b)\n\ntest_datetime_string_conversion()\n\n@test\ndef test_month_truncation():\n    # Make sure that months are truncating correctly\n    assert np.array_equal(np.array('1945-03-01', np.datetime64['M', 1]),\n                          np.array('1945-03-31', np.datetime64['M', 1]))\n    assert np.array_equal(\n        np.array('1969-11-01', np.datetime64['M', 1]),\n        np.array('1969-11-30T23:59:59.99999',\n                 np.datetime64['M', 1]).astype(np.datetime64['M', 1]))\n    assert np.array_equal(\n        np.array('1969-12-01', np.datetime64['M', 1]),\n        np.array('1969-12-31T23:59:59.99999',\n                 np.datetime64['M', 1]).astype(np.datetime64['M', 1]))\n    assert np.array_equal(\n        np.array('1970-01-01', np.datetime64['M', 1]),\n        np.array('1970-01-31T23:59:59.99999',\n                 np.datetime64['M', 1]).astype(np.datetime64['M', 1]))\n    assert np.array_equal(\n        np.array('1980-02-01', np.datetime64['M', 1]),\n        np.array('1980-02-29T23:59:59.99999',\n                 np.datetime64['M', 1]).astype(np.datetime64['M', 1]))\n\ntest_month_truncation()\n\n@test\ndef test_dtype_promotion():\n    a = np.array('2011-03-16', np.datetime64['Y', 12])\n    b = np.array('200', np.timedelta64['Y', 15])\n    assert np.array_equal(np.array('5006', np.datetime64['Y', 3]), a + b)\n    a = np.array('2011-03-15', np.datetime64['Y', 2])\n    b = np.array('2001', np.timedelta64['Y', 2])\n    assert np.array_equal(np.array('6012', np.datetime64['Y', 2]), a + b)\n    a = np.array('2011-03-15', np.datetime64['M', 62])\n    b = np.array('200', np.timedelta64['M', 24])\n    assert np.array_equal(np.array('2406-03', np.datetime64['M', 2]), a + b)\n    a = np.array('2011-03-15', np.datetime64['W', 1])\n    b = np.array('200', np.timedelta64['D', 2])\n    assert np.array_equal(np.array('2012-04-13', np.datetime64['D', 1]), a + b)\n    a = np.array('2011-03-15', np.datetime64['W', 1])\n    b = np.array('200', np.timedelta64['s', 13])\n    assert np.array_equal(\n        np.array('2011-03-10T00:43:20', np.datetime64['s', 1]), a + b)\n\ntest_dtype_promotion()\n\n@test\ndef test_pyobject_roundtrip():\n    # All datetime types should be able to roundtrip through object\n    a = np.array([\n        0, 0, 0, 0, 0, 0, 0, 0, 0, -1020040340, -2942398, -1, 0, 1, 234523453,\n        1199164176\n    ],\n                 dtype=np.int64)\n    # ['Y', 'M', 'D']\n    # With date units\n    b = a.copy().view(dtype=np.datetime64['D', 1])\n    b[0] = '-0001-01-01'\n    b[1] = '-0001-12-31'\n    b[2] = '0000-01-01'\n    b[3] = '0001-01-01'\n    b[4] = '1969-12-31'\n    b[5] = '1970-01-01'\n    b[6] = '9999-12-31'\n    b[7] = '10000-01-01'\n\n    assert np.array_equal(b.astype(np.datetime64['D', 1]), b)\n\n    b = a.copy().view(dtype=np.datetime64['W', 1])\n    b[0] = '-0001-01-01'\n    b[1] = '-0001-12-31'\n    b[2] = '0000-01-01'\n    b[3] = '0001-01-01'\n    b[4] = '1969-12-31'\n    b[5] = '1970-01-01'\n    b[6] = '9999-12-31'\n    b[7] = '10000-01-01'\n\n    assert np.array_equal(b.astype(np.datetime64['W', 1]), b)\n\n    b = a.copy().view(dtype=np.datetime64['M', 1])\n    b[0] = '-0001-01-01'\n    b[1] = '-0001-12-31'\n    b[2] = '0000-01-01'\n    b[3] = '0001-01-01'\n    b[4] = '1969-12-31'\n    b[5] = '1970-01-01'\n    b[6] = '9999-12-31'\n    b[7] = '10000-01-01'\n\n    assert np.array_equal(b.astype(np.datetime64['M', 1]), b)\n\n    b = a.copy().view(dtype=np.datetime64['Y', 1])\n    b[0] = '-0001-01-01'\n    b[1] = '-0001-12-31'\n    b[2] = '0000-01-01'\n    b[3] = '0001-01-01'\n    b[4] = '1969-12-31'\n    b[5] = '1970-01-01'\n    b[6] = '9999-12-31'\n    b[7] = '10000-01-01'\n\n    assert np.array_equal(b.astype(np.datetime64['Y', 1]), b)\n\n    # With time units\n    b = a.copy().view(dtype=np.datetime64['as', 1])\n    b[0] = '-0001-01-01T00'\n    b[1] = '-0001-12-31T00'\n    b[2] = '0000-01-01T00'\n    b[3] = '0001-01-01T00'\n    b[4] = '1969-12-31T23:59:59.999999'\n    b[5] = '1970-01-01T00'\n    b[6] = '9999-12-31T23:59:59.999999'\n    b[7] = '10000-01-01T00'\n\n    assert np.array_equal(b.astype(np.datetime64['as', 1]), b)\n\n    b = a.copy().view(dtype=np.datetime64['fs', 16])\n    b[0] = '-0001-01-01T00'\n    b[1] = '-0001-12-31T00'\n    b[2] = '0000-01-01T00'\n    b[3] = '0001-01-01T00'\n    b[4] = '1969-12-31T23:59:59.999999'\n    b[5] = '1970-01-01T00'\n    b[6] = '9999-12-31T23:59:59.999999'\n    b[7] = '10000-01-01T00'\n\n    assert np.array_equal(b.astype(np.datetime64['fs', 16]), b)\n\n    b = a.copy().view(dtype=np.datetime64['ps', 16])\n    b[0] = '-0001-01-01T00'\n    b[1] = '-0001-12-31T00'\n    b[2] = '0000-01-01T00'\n    b[3] = '0001-01-01T00'\n    b[4] = '1969-12-31T23:59:59.999999'\n    b[5] = '1970-01-01T00'\n    b[6] = '9999-12-31T23:59:59.999999'\n    b[7] = '10000-01-01T00'\n\n    assert np.array_equal(b.astype(np.datetime64['ps', 16]), b)\n\n    b = a.copy().view(dtype=np.datetime64['us', 16])\n    b[0] = '-0001-01-01T00'\n    b[1] = '-0001-12-31T00'\n    b[2] = '0000-01-01T00'\n    b[3] = '0001-01-01T00'\n    b[4] = '1969-12-31T23:59:59.999999'\n    b[5] = '1970-01-01T00'\n    b[6] = '9999-12-31T23:59:59.999999'\n    b[7] = '10000-01-01T00'\n\n    assert np.array_equal(b.astype(np.datetime64['us', 16]), b)\n\n    b = a.copy().view(dtype=np.datetime64['as', 300])\n    b[0] = '-0001-01-01T00'\n    b[1] = '-0001-12-31T00'\n    b[2] = '0000-01-01T00'\n    b[3] = '0001-01-01T00'\n    b[4] = '1969-12-31T23:59:59.999999'\n    b[5] = '1970-01-01T00'\n    b[6] = '9999-12-31T23:59:59.999999'\n    b[7] = '10000-01-01T00'\n\n    assert np.array_equal(b.astype(np.datetime64['as', 300]), b)\n\n    b = a.copy().view(dtype=np.datetime64['us', 20])\n    b[0] = '-0001-01-01T00'\n    b[1] = '-0001-12-31T00'\n    b[2] = '0000-01-01T00'\n    b[3] = '0001-01-01T00'\n    b[4] = '1969-12-31T23:59:59.999999'\n    b[5] = '1970-01-01T00'\n    b[6] = '9999-12-31T23:59:59.999999'\n    b[7] = '10000-01-01T00'\n\n    assert np.array_equal(b.astype(np.datetime64['us', 20]), b)\n\n    b = a.copy().view(dtype=np.datetime64['us', 56])\n    b[0] = '-0001-01-01T00'\n    b[1] = '-0001-12-31T00'\n    b[2] = '0000-01-01T00'\n    b[3] = '0001-01-01T00'\n    b[4] = '1969-12-31T23:59:59.999999'\n    b[5] = '1970-01-01T00'\n    b[6] = '9999-12-31T23:59:59.999999'\n    b[7] = '10000-01-01T00'\n\n    assert np.array_equal(b.astype(np.datetime64['us', 56]), b)\n\ntest_pyobject_roundtrip()\n\n@test\ndef test_different_unit_comparison():\n    assert np.array_equal(np.array('1945', dtype=(np.datetime64['Y', 1])),\n                          np.array('1945', dtype=(np.datetime64['Y', 1])))\n    assert np.array_equal(np.array('1970', dtype=(np.datetime64['Y', 1])),\n                          np.array('1970', dtype=(np.datetime64['Y', 1])))\n    assert np.array_equal(np.array('9999', dtype=(np.datetime64['Y', 1])),\n                          np.array('9999', dtype=(np.datetime64['Y', 1])))\n    assert np.array_equal(\n        np.array('10000', dtype=(np.datetime64['Y', 1])),\n        np.array('10000-01-01', dtype=(np.datetime64['Y', 1])))\n    assert np.datetime64('1945', 'Y') == np.datetime64('1945', 'Y')\n    assert np.datetime64('1970', 'Y') == np.datetime64('1970', 'Y')\n    assert np.datetime64('9999', 'Y') == np.datetime64('9999', 'Y')\n    assert np.datetime64('10000', 'Y') == np.datetime64('10000-01-01', 'Y')\n\n    assert np.array_equal(np.array('1945', dtype=(np.datetime64['Y', 1])),\n                          np.array('1945', dtype=(np.datetime64['M', 1])))\n    assert np.array_equal(np.array('1970', dtype=(np.datetime64['Y', 1])),\n                          np.array('1970', dtype=(np.datetime64['M', 1])))\n    assert np.array_equal(np.array('9999', dtype=(np.datetime64['Y', 1])),\n                          np.array('9999', dtype=(np.datetime64['M', 1])))\n    assert np.array_equal(\n        np.array('10000', dtype=(np.datetime64['Y', 1])),\n        np.array('10000-01-01', dtype=(np.datetime64['M', 1])))\n    assert np.datetime64('1945', 'Y') == np.datetime64('1945', 'M')\n    assert np.datetime64('1970', 'Y') == np.datetime64('1970', 'M')\n    assert np.datetime64('9999', 'Y') == np.datetime64('9999', 'M')\n    assert np.datetime64('10000', 'Y') == np.datetime64('10000-01-01', 'M')\n\n    assert np.array_equal(np.array('1945', dtype=(np.datetime64['Y', 1])),\n                          np.array('1945', dtype=(np.datetime64['D', 1])))\n    assert np.array_equal(np.array('1970', dtype=(np.datetime64['Y', 1])),\n                          np.array('1970', dtype=(np.datetime64['D', 1])))\n    assert np.array_equal(np.array('9999', dtype=(np.datetime64['Y', 1])),\n                          np.array('9999', dtype=(np.datetime64['D', 1])))\n    assert np.array_equal(\n        np.array('10000', dtype=(np.datetime64['Y', 1])),\n        np.array('10000-01-01', dtype=(np.datetime64['D', 1])))\n    assert np.datetime64('1945', 'Y') == np.datetime64('1945', 'D')\n    assert np.datetime64('1970', 'Y') == np.datetime64('1970', 'D')\n    assert np.datetime64('9999', 'Y') == np.datetime64('9999', 'D')\n    assert np.datetime64('10000', 'Y') == np.datetime64('10000-01-01', 'D')\n\n    assert np.array_equal(np.array('1945', dtype=(np.datetime64['M', 1])),\n                          np.array('1945', dtype=(np.datetime64['M', 1])))\n    assert np.array_equal(np.array('1970', dtype=(np.datetime64['M', 1])),\n                          np.array('1970', dtype=(np.datetime64['M', 1])))\n    assert np.array_equal(np.array('9999', dtype=(np.datetime64['M', 1])),\n                          np.array('9999', dtype=(np.datetime64['M', 1])))\n    assert np.array_equal(\n        np.array('10000', dtype=(np.datetime64['M', 1])),\n        np.array('10000-01-01', dtype=(np.datetime64['M', 1])))\n    assert np.datetime64('1945', 'M') == np.datetime64('1945', 'M')\n    assert np.datetime64('1970', 'M') == np.datetime64('1970', 'M')\n    assert np.datetime64('9999', 'M') == np.datetime64('9999', 'M')\n    assert np.datetime64('10000', 'M') == np.datetime64('10000-01-01', 'M')\n\n    assert np.array_equal(np.array('1945', dtype=(np.datetime64['D', 1])),\n                          np.array('1945', dtype=(np.datetime64['D', 1])))\n    assert np.array_equal(np.array('1970', dtype=(np.datetime64['D', 1])),\n                          np.array('1970', dtype=(np.datetime64['D', 1])))\n    assert np.array_equal(np.array('9999', dtype=(np.datetime64['D', 1])),\n                          np.array('9999', dtype=(np.datetime64['D', 1])))\n    assert np.array_equal(\n        np.array('10000', dtype=(np.datetime64['D', 1])),\n        np.array('10000-01-01', dtype=(np.datetime64['D', 1])))\n    assert np.datetime64('1945', 'D') == np.datetime64('1945', 'D')\n    assert np.datetime64('1970', 'D') == np.datetime64('1970', 'D')\n    assert np.datetime64('9999', 'D') == np.datetime64('9999', 'D')\n    assert np.datetime64('10000', 'D') == np.datetime64('10000-01-01', 'D')\n\n    assert np.array_equal(\n        np.array('10000', dtype=(np.datetime64['M', 1])),\n        np.array('10000-01-01', dtype=(np.datetime64['D', 1])))\n    assert np.datetime64('10000', 'M') == np.datetime64('10000-01-01', 'D')\n\n    assert np.array_equal(\n        np.array('10000', dtype=(np.datetime64['D', 1])),\n        np.array('10000-01-01', dtype=(np.datetime64['Y', 1])))\n    assert np.datetime64('10000', 'D') == np.datetime64('10000-01-01', 'Y')\n\n    assert np.array_equal(\n        np.array('10000', dtype=(np.datetime64['D', 1])),\n        np.array('10000-01-01', dtype=(np.datetime64['Y', 1])))\n    assert np.datetime64('10000', 'D') == np.datetime64('10000-01-01', 'Y')\n\n    assert np.array_equal(\n        np.array('1932-02-17',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['D', 1]),\n        np.array('1932-02-17T00:00:00',\n                 dtype=np.datetime64['s', 1]).astype(np.datetime64['D', 1]))\n    assert np.array_equal(\n        np.array('10000-04-27',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['D', 1]),\n        np.array('10000-04-27T00:00:00', dtype='s').astype(np.datetime64['D',\n                                                                         1]))\n\n    assert np.array_equal(\n        np.array('1932-02-17',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['h', 12]),\n        np.array('1932-02-17T00:00:00',\n                 dtype=np.datetime64['s', 1]).astype(np.datetime64['h', 1]))\n    assert np.array_equal(\n        np.array('10000-04-27',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['h', 12]),\n        np.array('10000-04-27T00:00:00', dtype='s').astype(np.datetime64['h',\n                                                                         1]))\n\n    assert np.array_equal(\n        np.array('1932-02-17',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['h', 1]),\n        np.array('1932-02-17T00:00:00',\n                 dtype=np.datetime64['s', 1]).astype(np.datetime64['h', 1]))\n    assert np.array_equal(\n        np.array('10000-04-27',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['h', 1]),\n        np.array('10000-04-27T00:00:00', dtype='s').astype(np.datetime64['h',\n                                                                         1]))\n\n    assert np.array_equal(\n        np.array('1932-02-17',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['m', 1]),\n        np.array('1932-02-17T00:00:00',\n                 dtype=np.datetime64['s', 1]).astype(np.datetime64['m', 1]))\n    assert np.array_equal(\n        np.array('10000-04-27',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['m', 1]),\n        np.array('10000-04-27T00:00:00', dtype='s').astype(np.datetime64['m',\n                                                                         1]))\n\n    assert np.array_equal(\n        np.array('1932-02-17',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['s', 4]),\n        np.array('1932-02-17T00:00:00',\n                 dtype=np.datetime64['s', 1]).astype(np.datetime64['s', 1]))\n    assert np.array_equal(\n        np.array('10000-04-27',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['s', 4]),\n        np.array('10000-04-27T00:00:00', dtype='s').astype(np.datetime64['s',\n                                                                         1]))\n\n    assert np.array_equal(\n        np.array('1932-02-17',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['s', 1]),\n        np.array('1932-02-17T00:00:00',\n                 dtype=np.datetime64['s', 1]).astype(np.datetime64['s', 1]))\n    assert np.array_equal(\n        np.array('10000-04-27',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['s', 1]),\n        np.array('10000-04-27T00:00:00', dtype='s').astype(np.datetime64['s',\n                                                                         1]))\n\n    assert np.array_equal(\n        np.array('1932-02-17',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['ms', 1]),\n        np.array('1932-02-17T00:00:00',\n                 dtype=np.datetime64['s', 1]).astype(np.datetime64['ms', 1]))\n    assert np.array_equal(\n        np.array('10000-04-27',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['ms', 1]),\n        np.array('10000-04-27T00:00:00', dtype='s').astype(np.datetime64['ms',\n                                                                         1]))\n\n    assert np.array_equal(\n        np.array('1932-02-17',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['us', 1]),\n        np.array('1932-02-17T00:00:00',\n                 dtype=np.datetime64['s', 1]).astype(np.datetime64['us', 1]))\n    assert np.array_equal(\n        np.array('10000-04-27',\n                 dtype=np.datetime64['D', 1]).astype(np.datetime64['us', 1]),\n        np.array('10000-04-27T00:00:00', dtype='s').astype(np.datetime64['us',\n                                                                         1]))\n\ntest_different_unit_comparison()\n\n@test\ndef test_timedelta_modulus_error():\n    a = np.timedelta64(7, 's')\n    b = np.timedelta64(3, 's')\n    assert a % b == np.timedelta64(1, 's')\n    # negative value cases\n    a = np.timedelta64(3, 's')\n    b = np.timedelta64(-2, 's')\n    assert a % b == np.timedelta64(-1, 's')\n    a = np.timedelta64(-3, 's')\n    b = np.timedelta64(2, 's')\n    assert a % b == np.timedelta64(1, 's')\n    # larger value cases\n    a = np.timedelta64(17, 's')\n    b = np.timedelta64(22, 's')\n    assert a % b == np.timedelta64(17, 's')\n    a = np.timedelta64(22, 's')\n    b = np.timedelta64(17, 's')\n    assert a % b == np.timedelta64(5, 's')\n    # different units\n    a = np.timedelta64(1, 'm')\n    b = np.timedelta64(57, 's')\n    assert a % b == np.timedelta64(3, 's')\n    a = np.timedelta64(1, 'us')\n    b = np.timedelta64(727, 'ns')\n    assert a % b == np.timedelta64(273, 'ns')\n    # Y % M works\n    a = np.timedelta64(2, 'Y')\n    b = np.timedelta64(22, 'M')\n    assert a % b == np.timedelta64(2, 'M')\n    # NaT is propagated\n    a = np.timedelta64('NaT', 'ns')\n    b = np.timedelta64(50, 'ns')\n    assert np.isnat(a % b)\n\ntest_timedelta_modulus_error()\n\n@test\ndef test_timedelta_modulus_div_by_zero():\n    actual = np.timedelta64(10, 's') % np.timedelta64(0, 's')\n    assert np.isnat(actual)\n\ntest_timedelta_modulus_div_by_zero()\n\n@test\ndef test_timedelta_arange_no_dtype():\n    d = np.array([5], dtype=np.timedelta64['D', 1])\n    assert np.array_equal(np.arange(5, 6, np.timedelta64['D', 1]), d)\n    assert np.array_equal(np.arange(5, np.timedelta64['D', 1]),\n                          np.arange(0, 5, np.timedelta64['D', 1]))\n\ntest_timedelta_arange_no_dtype()\n\n@test\ndef test_datetime_busday_offset():\n    assert np.busday_offset('2011-06', 0, roll='forward',\n                            weekmask='Mon') == np.datetime64(\n                                '2011-06-06', 'D')\n    assert np.busday_offset('2011-07', -1, roll='forward',\n                            weekmask='Mon') == np.datetime64(\n                                '2011-06-27', 'D')\n    assert np.busday_offset('2010-08', 0, roll='backward') == np.datetime64(\n        '2010-07-30', 'D')\n    assert np.busday_offset('2010-08', 0, roll='preceding') == np.datetime64(\n        '2010-07-30', 'D')\n    assert np.busday_offset('2010-08', 0,\n                            roll='modifiedpreceding') == np.datetime64(\n                                '2010-08-02', 'D')\n    assert np.busday_offset('2010-08', 0,\n                            roll='modifiedfollowing') == np.datetime64(\n                                '2010-08-02', 'D')\n    assert np.busday_offset('2010-08', 0, roll='forward') == np.datetime64(\n        '2010-08-02', 'D')\n    assert np.busday_offset('2010-08', 0, roll='following') == np.datetime64(\n        '2010-08-02', 'D')\n    assert np.busday_offset('2010-10-30', 0,\n                            roll='following') == np.datetime64(\n                                '2010-11-01', 'D')\n    assert np.busday_offset('2010-10-30', 0,\n                            roll='modifiedfollowing') == np.datetime64(\n                                '2010-10-29', 'D')\n    assert np.busday_offset('2010-10-30', 0,\n                            roll='modifiedpreceding') == np.datetime64(\n                                '2010-10-29', 'D')\n    assert np.busday_offset('2010-10-16', 0,\n                            roll='modifiedpreceding') == np.datetime64(\n                                '2010-10-15', 'D')\n    assert np.busday_offset('2010-10-16', 0,\n                            roll='modifiedfollowing') == np.datetime64(\n                                '2010-10-18', 'D')\n\n    # Bigger offset values\n    assert np.busday_offset('2006-02-01',\n                            25) == np.datetime64('2006-03-08', 'D')\n    assert np.busday_offset('2006-03-08',\n                            -25) == np.datetime64('2006-02-01', 'D')\n\n    # NaT values when roll is not raise\n    assert np.isnat(np.busday_offset(np.datetime64('NaT', 'D'), 1, roll='nat'))\n    assert np.isnat(\n        np.busday_offset(np.datetime64('NaT', 'D'), 1, roll='following'))\n    assert np.isnat(\n        np.busday_offset(np.datetime64('NaT', 'D'), 1, roll='preceding'))\n\ntest_datetime_busday_offset()\n\n@test\ndef test_datetime_busdaycalendar():\n    # Check that it removes NaT, duplicates, and weekends\n    # and sorts the result.\n    bdd = np.busdaycalendar(holidays=[\n        'NaT', '2011-01-17', '2011-03-06', 'NaT', '2011-12-26', '2011-05-30',\n        '2011-01-17'\n    ])\n    assert np.array_equal(\n        bdd.holidays,\n        np.array(['2011-01-17', '2011-05-30', '2011-12-26'],\n                 np.datetime64('NaT', 'D')))\n    # Default M-F weekmask\n    assert np.array_equal(bdd.weekmask,\n                          np.array([1, 1, 1, 1, 1, 0, 0], dtype=bool))\n\n    # Check length 7 0/1 string\n    bdd = np.busdaycalendar(weekmask=\"0011001\")\n    assert np.array_equal(bdd.weekmask,\n                          np.array([0, 0, 1, 1, 0, 0, 1], dtype=bool))\n\n    # Check length 7 string weekmask.\n    bdd = np.busdaycalendar(weekmask=\"Mon Tue\")\n    assert np.array_equal(bdd.weekmask,\n                          np.array([1, 1, 0, 0, 0, 0, 0], dtype=bool))\n\n    # All-zeros weekmask should raise\n    try:\n        np.busdaycalendar(weekmask=[0, 0, 0, 0, 0, 0, 0])\n        assert False\n    except ValueError:\n        pass\n\n    # weekday names must be correct case\n    try:\n        np.busdaycalendar(weekmask=\"satsun\")\n        assert False\n    except ValueError:\n        pass\n\n    # All-zeros weekmask should raise\n    try:\n        np.busdaycalendar(weekmask=\"\")\n        assert False\n    except ValueError:\n        pass\n\n    # Invalid weekday name codes should raise\n    try:\n        np.busdaycalendar(weekmask=\"Mon Tue We\")\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.busdaycalendar(weekmask=\"Max\")\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.busdaycalendar(weekmask=\"Monday Tue\")\n        assert False\n    except ValueError:\n        pass\n\ntest_datetime_busdaycalendar()\n\n@test\ndef test_datetime_busday_holidays_offset():\n    # With exactly one holiday\n    assert np.busday_offset('2011-11-10', 1,\n                            holidays=['2011-11-11'\n                                      ]) == np.datetime64('2011-11-14', 'D')\n    assert np.busday_offset('2011-11-04', 5,\n                            holidays=['2011-11-11'\n                                      ]) == np.datetime64('2011-11-14', 'D')\n    assert np.busday_offset('2011-11-10', 5,\n                            holidays=['2011-11-11'\n                                      ]) == np.datetime64('2011-11-18', 'D')\n\n    # With the holiday appearing twice\n    assert np.busday_offset('2011-11-10',\n                            1,\n                            holidays=['2011-11-11', '2011-11-11'\n                                      ]) == np.datetime64('2011-11-14', 'D')\n\n    # With a NaT holiday\n    assert np.busday_offset('2011-11-10', 1,\n                            holidays=['2011-11-11', 'NaT'\n                                      ]) == np.datetime64('2011-11-14', 'D')\n\n    # With another holiday after\n    assert np.busday_offset('2011-11-10',\n                            1,\n                            holidays=['2011-11-11', '2011-11-24'\n                                      ]) == np.datetime64('2011-11-14', 'D')\n    assert np.busday_offset('2011-11-14',\n                            -1,\n                            holidays=['2011-11-11', '2011-11-24'\n                                      ]) == np.datetime64('2011-11-10', 'D')\n\n    # With another holiday before\n    assert np.busday_offset('2011-11-10',\n                            1,\n                            holidays=['2011-10-10', '2011-11-11'\n                                      ]) == np.datetime64('2011-11-14', 'D')\n\n    # With another holiday before and after\n    assert np.busday_offset('2011-11-10',\n                            1,\n                            holidays=[\n                                '2011-10-10', '2011-11-11', '2011-11-24'\n                            ]) == np.datetime64('2011-11-14', 'D')\n    assert np.busday_offset('2011-11-14',\n                            -1,\n                            holidays=[\n                                '2011-10-10', '2011-11-11', '2011-11-24'\n                            ]) == np.datetime64('2011-11-10', 'D')\n\n    # A bigger forward jump across more than one week/holiday\n    holidays = [\n        '2011-10-10', '2011-11-11', '2011-11-24', '2011-12-25', '2011-05-30',\n        '2011-02-21', '2011-12-26', '2012-01-02'\n    ]\n    bdd = np.busdaycalendar(weekmask='1111100', holidays=holidays)\n    assert np.busday_offset('2011-10-03', 4,\n                            holidays=holidays) == np.busday_offset(\n                                '2011-10-03', 4)\n    assert np.busday_offset('2011-10-03', 5,\n                            holidays=holidays) == np.busday_offset(\n                                '2011-10-03', 5 + 1)\n    assert np.busday_offset('2011-10-03', 27,\n                            holidays=holidays) == np.busday_offset(\n                                '2011-10-03', 27 + 1)\n    assert np.busday_offset('2011-10-03', 28,\n                            holidays=holidays) == np.busday_offset(\n                                '2011-10-03', 28 + 2)\n    assert np.busday_offset('2011-10-03', 35,\n                            holidays=holidays) == np.busday_offset(\n                                '2011-10-03', 35 + 2)\n    assert np.busday_offset('2011-10-03', 36,\n                            holidays=holidays) == np.busday_offset(\n                                '2011-10-03', 36 + 3)\n    assert np.busday_offset('2011-10-03', 56,\n                            holidays=holidays) == np.busday_offset(\n                                '2011-10-03', 56 + 3)\n    assert np.busday_offset('2011-10-03', 57,\n                            holidays=holidays) == np.busday_offset(\n                                '2011-10-03', 57 + 4)\n    assert np.busday_offset('2011-10-03', 60,\n                            holidays=holidays) == np.busday_offset(\n                                '2011-10-03', 60 + 4)\n    assert np.busday_offset('2011-10-03', 61,\n                            holidays=holidays) == np.busday_offset(\n                                '2011-10-03', 61 + 5)\n\n    # Roll with the holidays\n    assert np.busday_offset('2011-12-25', 0, roll='forward',\n                            holidays=holidays) == np.datetime64('2011-12-27')\n\n    assert np.busday_offset('2011-12-26', 0, roll='forward',\n                            holidays=holidays) == np.datetime64('2011-12-27')\n    assert np.busday_offset('2011-12-26',\n                            0,\n                            roll='backward',\n                            holidays=holidays) == np.datetime64('2011-12-23')\n    assert np.busday_offset('2012-02-27',\n                            0,\n                            roll='modifiedfollowing',\n                            holidays=[\n                                '2012-02-27', '2012-02-26', '2012-02-28',\n                                '2012-03-01', '2012-02-29'\n                            ]) == np.datetime64('2012-02-24')\n    assert np.busday_offset('2012-03-06',\n                            0,\n                            roll='modifiedpreceding',\n                            holidays=[\n                                '2012-03-02', '2012-03-03', '2012-03-01',\n                                '2012-03-05', '2012-03-07', '2012-03-06'\n                            ]) == np.datetime64('2012-03-08')\n\ntest_datetime_busday_holidays_offset()\n\n@test\ndef test_datetime_busday_holidays_count():\n    holidays = [\n        '2011-01-01', '2011-10-10', '2011-11-11', '2011-11-24', '2011-12-25',\n        '2011-05-30', '2011-02-21', '2011-01-17', '2011-12-26', '2012-01-02',\n        '2011-02-21', '2011-05-30', '2011-07-01', '2011-07-04', '2011-09-05',\n        '2011-10-10'\n    ]\n    bdd = np.busdaycalendar(weekmask='1111100', holidays=holidays)\n\n    # Validate against busday_offset broadcast against\n    # a range of offsets\n    dates = np.busday_offset('2011-01-01',\n                             np.arange(366),\n                             roll='forward',\n                             busdaycal=bdd)\n    assert np.array_equal(np.busday_count('2011-01-01', dates, busdaycal=bdd),\n                          np.arange(366))\n    # Returns negative value when reversed\n    # -1 since the '2011-01-01' is not a busday\n    assert np.array_equal(np.busday_count(dates, '2011-01-01', busdaycal=bdd),\n                          -np.arange(366) - 1)\n\n    # 2011-12-31 is a saturday\n    dates = np.busday_offset('2011-12-31',\n                             -np.arange(366),\n                             roll='forward',\n                             busdaycal=bdd)\n    # only the first generated date is in the future of 2011-12-31\n    expected = np.arange(366)\n    expected[0] = -1\n    # Returns negative value when reversed\n    expected = -np.arange(366) + 1\n    expected[0] = 0\n\n    # Number of Mondays in March 2011\n    assert np.busday_count('2011-03', '2011-04', weekmask='Mon') == 4\n    # Returns negative value when reversed\n    assert np.busday_count('2011-04', '2011-03', weekmask='Mon') == -4\n\n    sunday = np.datetime64('2023-03-05')\n    monday = sunday + 1\n    friday = sunday + 5\n    saturday = sunday + 6\n    assert np.busday_count(sunday, monday) == 0\n    assert np.busday_count(saturday, friday) == 0\n\ntest_datetime_busday_holidays_count()\n\n@test\ndef test_datetime_add():\n    for dta, dtb, dtc, dtnat, tda, tdb, tdc in \\\n                (\n\n                 (np.array(['2012-12-21'], dtype=np.datetime64['D', 1]),\n                  np.array(['2012-12-24'], dtype=np.datetime64['D', 1]),\n                  np.array(['2012-12-21T11'], dtype=np.datetime64['h', 1]),\n                  np.array(['NaT'], dtype=np.datetime64['D', 1]),\n                  np.array([3], dtype=np.timedelta64['D', 1]),\n                  np.array([11], dtype=np.timedelta64['h', 1]),\n                  np.array([3*24 + 11], dtype=np.timedelta64['h', 1])),\n\n                 (np.datetime64('2012-12-21', 'D'),\n                  np.datetime64('2012-12-24', 'D'),\n                  np.datetime64('2012-12-21T11', 'h'),\n                  np.datetime64('NaT', 'D'),\n                  np.timedelta64(3, 'D'),\n                  np.timedelta64(11, 'h'),\n                  np.timedelta64(3*24 + 11, 'h'))):\n\n        assert array_equal(tda + tdb, tdc)\n        if isinstance((tda + tdb), ndarray):\n            assert isinstance((tda + tdb).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((tda + tdb), np.timedelta64['h', 1])\n\n        assert array_equal(tdb + True, tdb + 1)\n        if isinstance((tdb + True), ndarray):\n            assert isinstance((tdb + True).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((tdb + True), np.timedelta64['h', 1])\n\n        assert array_equal(tdb + 3 * 24, tdc)\n        if isinstance((tdb + 3 * 24), ndarray):\n            assert isinstance((tdb + 3 * 24).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((tdb + 3 * 24), np.timedelta64['h', 1])\n\n        assert array_equal(False + tdb, tdb)\n        if isinstance((False + tdb), ndarray):\n            assert isinstance((False + tdb).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((False + tdb), np.timedelta64['h', 1])\n\n        assert array_equal(3 * 24 + tdb, tdc)\n        if isinstance((3 * 24 + tdb), ndarray):\n            assert isinstance((3 * 24 + tdb).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((3 * 24 + tdb), np.timedelta64['h', 1])\n\n        assert array_equal(dta + True, dta + 1)\n        assert array_equal(isnat(dtnat + True), isnat(dtnat))\n        if isinstance((dta + True), ndarray):\n            assert isinstance((dta + True).dtype, np.datetime64['D', 1])\n        else:\n            assert isinstance((dta + True), np.datetime64['D', 1])\n\n        assert array_equal(dta + 3, dtb)\n        assert array_equal(isnat(dtnat + 3), isnat(dtnat))\n        if isinstance((dta + 3), ndarray):\n            assert isinstance((dta + 3).dtype, np.datetime64['D', 1])\n        else:\n            assert isinstance((dta + 3), np.datetime64['D', 1])\n\n        assert array_equal(False + dta, dta)\n        assert array_equal(isnat(False + dtnat), isnat(dtnat))\n        if isinstance((False + dta), ndarray):\n            assert isinstance((False + dta).dtype, np.datetime64['D', 1])\n        else:\n            assert isinstance((False + dta), np.datetime64['D', 1])\n\n        assert array_equal(3 + dta, dtb)\n        assert array_equal(isnat(3 + dtnat), isnat(dtnat))\n        if isinstance((3 + dta), ndarray):\n            assert isinstance((3 + dta).dtype, np.datetime64['D', 1])\n        else:\n            assert isinstance((3 + dta), np.datetime64['D', 1])\n\n        assert array_equal(dta + tda, dtb)\n        assert array_equal(isnat(dtnat + tda), isnat(dtnat))\n        if isinstance((dta + tda), ndarray):\n            assert isinstance((dta + tda).dtype, np.datetime64['D', 1])\n        else:\n            assert isinstance((dta + tda), np.datetime64['D', 1])\n\n        assert array_equal(tda + dta, dtb)\n        assert array_equal(isnat(tda + dtnat), isnat(dtnat))\n        if isinstance((tda + dta), ndarray):\n            assert isinstance((tda + dta).dtype, np.datetime64['D', 1])\n        else:\n            assert isinstance((tda + dta), np.datetime64['D', 1])\n\n        assert array_equal(np.add(dta, tdb), dtc)\n        if isinstance(np.add(dta, tdb), ndarray):\n            assert isinstance(np.add(dta, tdb).dtype, np.datetime64['h', 1])\n        else:\n            assert isinstance(np.add(dta, tdb), np.datetime64['h', 1])\n\n        assert array_equal(np.add(tdb, dta), dtc)\n        if isinstance(np.add(dta, tdb), ndarray):\n            assert isinstance(np.add(tdb, dta).dtype, np.datetime64['h', 1])\n        else:\n            assert isinstance(np.add(tdb, dta), np.datetime64['h', 1])\n\ntest_datetime_add()\n\n@test\ndef test_datetime_subtract():\n    for dta, dtb, dtc, dtd, dte, dtnat, tda, tdb, tdc in \\\n                    (\n\n                     (np.array(['2012-12-21'], dtype=np.datetime64['D', 1]),\n                      np.array(['2012-12-24'], dtype=np.datetime64['D', 1]),\n                      np.array(['1940-12-24'], dtype=np.datetime64['D', 1]),\n                      np.array(['1940-12-24T00'], dtype=np.datetime64['h',1]),\n                      np.array(['1940-12-23T13'], dtype=np.datetime64['h', 1]),\n                      np.array(['NaT'], dtype=np.datetime64['D', 1]),\n                      np.array([3], dtype=np.timedelta64['D', 1]),\n                      np.array([11], dtype=np.timedelta64['h', 1]),\n                      np.array([3*24 - 11], dtype=np.timedelta64['h', 1])),\n\n                     (np.datetime64('2012-12-21', 'D'),\n                      np.datetime64('2012-12-24', 'D'),\n                      np.datetime64('1940-12-24', 'D'),\n                      np.datetime64('1940-12-24T00', 'h'),\n                      np.datetime64('1940-12-23T13', 'h'),\n                      np.datetime64('NaT', 'D'),\n                      np.timedelta64(3, 'D'),\n                      np.timedelta64(11, 'h'),\n                      np.timedelta64(3*24 - 11, 'h'))):\n\n        assert array_equal(tda - tdb, tdc)\n        if isinstance((tda - tdb), ndarray):\n            assert isinstance((tda - tdb).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((tda - tdb), np.timedelta64['h', 1])\n\n        assert array_equal(tdb - tda, -tdc)\n        if isinstance((tdb - tda), ndarray):\n            assert isinstance((tdb - tda).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((tdb - tda), np.timedelta64['h', 1])\n\n        assert array_equal(tdc - True, tdc - 1)\n        if isinstance((tdc - True), ndarray):\n            assert isinstance((tdc - True).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((tdc - True), np.timedelta64['h', 1])\n\n        assert array_equal(tdc - 3 * 24, -tdb)\n        if isinstance((tdb - 3 * 24), ndarray):\n            assert isinstance((tdb - 3 * 24).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((tdb - 3 * 24), np.timedelta64['h', 1])\n\n        assert array_equal(False - tdb, -tdb)\n        if isinstance((False - tdb), ndarray):\n            assert isinstance((False - tdb).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((False - tdb), np.timedelta64['h', 1])\n\n        assert array_equal(3 * 24 - tdb, tdc)\n        if isinstance((3 * 24 - tdb), ndarray):\n            assert isinstance((3 * 24 - tdb).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((3 * 24 - tdb), np.timedelta64['h', 1])\n\n        assert array_equal(dtb - True, dtb - 1)\n        assert array_equal(isnat(dtnat - True), isnat(dtnat))\n        if isinstance((tdb - True), ndarray):\n            assert isinstance((tdb - True).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((tdb - True), np.timedelta64['h', 1])\n\n        assert array_equal(dtb - 3, dta)\n        assert array_equal(isnat(dtnat - 3), isnat(dtnat))\n        if isinstance((dtb - 3), ndarray):\n            assert isinstance((dtb - 3).dtype, np.datetime64['D', 1])\n        else:\n            assert isinstance((dtb - 3), np.datetime64['D', 1])\n\n        assert array_equal(dtb - tda, dta)\n        assert array_equal(isnat(dtnat - tda), isnat(dtnat))\n        if isinstance((dtb - tda), ndarray):\n            assert isinstance((dtb - tda).dtype, np.datetime64['D', 1])\n        else:\n            assert isinstance((dtb - tda), np.datetime64['D', 1])\n\n        assert array_equal(np.subtract(dtc, tdb), dte)\n\n        if isinstance(np.subtract(dtc, tdb), ndarray):\n            assert isinstance(\n                np.subtract(dtc, tdb).dtype, np.datetime64['h', 1])\n        else:\n            assert isinstance(np.subtract(dtc, tdb), np.datetime64['h', 1])\n\n        assert (np.subtract(dtc, dtd) == np.array(0,\n                                                  dtype=np.timedelta64['h',\n                                                                       1]))\n\n        if isinstance(np.subtract(dtc, dtd), ndarray):\n            assert isinstance(\n                np.subtract(dtc, dtd).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance(np.subtract(dtc, dtd), np.timedelta64['h', 1])\n\n        assert (np.subtract(dtd, dtc) == np.array(0,\n                                                  dtype=np.timedelta64['h',\n                                                                       1]))\n\n        if isinstance(np.subtract(dtd, dtc), ndarray):\n            assert isinstance(\n                np.subtract(dtd, dtc).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance(np.subtract(dtd, dtc), np.timedelta64['h', 1])\n\ntest_datetime_subtract()\n\n@test\ndef test_datetime_multiply():\n    for dta, tda, tdb, tdc in \\\n                (\n                 (np.array(['2012-12-21'], dtype=np.datetime64['D',1]),\n                  np.array([6], dtype=np.timedelta64['h',1]),\n                  np.array([9], dtype=np.timedelta64['h',1]),\n                  np.array([12], dtype=np.timedelta64['h',1])),\n                 (np.datetime64('2012-12-21', 'D'),\n                  np.timedelta64(6, 'h'),\n                  np.timedelta64(9, 'h'),\n                  np.timedelta64(12, 'h'))):\n\n        assert array_equal(tda * 2, tdc)\n        if isinstance((tda * 2), ndarray):\n            assert isinstance((tda * 2).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((tda * 2), np.timedelta64['h', 1])\n\n        assert array_equal(2 * tda, tdc)\n        if isinstance((2 * tda), ndarray):\n            assert isinstance((2 * tda).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((2 * tda), np.timedelta64['h', 1])\n\n        assert array_equal(tda * 1.5, tdb)\n        if isinstance((tda * 1.5), ndarray):\n            assert isinstance((tda * 1.5).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((tda * 1.5), np.timedelta64['h', 1])\n\n        assert array_equal(1.5 * tda, tdb)\n        if isinstance((1.5 * tda), ndarray):\n            assert isinstance((1.5 * tda).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((1.5 * tda), np.timedelta64['h', 1])\n\ntest_datetime_multiply()\n\n@test\ndef test_datetime_divide():\n    for dta, tda, tdb, tdc, tdd in \\\n                (\n                 (np.array(['2012-12-21'], dtype=np.datetime64['D',1]),\n                  np.array([6], dtype=np.timedelta64['h',1]),\n                  np.array([9], dtype=np.timedelta64['h',1]),\n                  np.array([12], dtype=np.timedelta64['h',1]),\n                  np.array([6], dtype=np.timedelta64['h',1])),\n\n                 (np.datetime64('2012-12-21', 'D'),\n                  np.timedelta64(6, 'h'),\n                  np.timedelta64(9, 'h'),\n                  np.timedelta64(12, 'h'),\n                  np.timedelta64(6, 'm'))):\n\n        assert array_equal(tdc / 2, tda)\n        if isinstance((tdc / 2), ndarray):\n            assert isinstance((tdc / 2).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((tdc / 2), np.timedelta64['h', 1])\n\n        assert array_equal(tda / 0.5, tdc)\n        if isinstance((tdc / 0.5), ndarray):\n            assert isinstance((tdc / 0.5).dtype, np.timedelta64['h', 1])\n        else:\n            assert isinstance((tdc / 0.5), np.timedelta64['h', 1])\n\ntest_datetime_divide()\n\n@test\ndef test_datetime_compare():\n    a = np.datetime64('2000-03-12T18:00:00.000000', 'us')\n    b = np.array([\n        '2000-03-12T18:00:00.000000', '2000-03-12T17:59:59.999999',\n        '2000-03-12T18:00:00.000001', '1970-01-11T12:00:00.909090',\n        '2016-01-11T12:00:00.909090'\n    ],\n                 dtype=np.datetime64['us', 1])\n    assert array_equal(np.equal(a, b), [1, 0, 0, 0, 0])\n    assert array_equal(np.not_equal(a, b), [0, 1, 1, 1, 1])\n    assert (np.less(a, b) == np.array([0, 0, 1, 0, 1])).all()\n    assert array_equal(np.less_equal(a, b), [1, 0, 1, 0, 1])\n    assert array_equal(np.greater(a, b), [0, 1, 0, 1, 0])\n    assert array_equal(np.greater_equal(a, b), [1, 1, 0, 1, 0])\n\ntest_datetime_compare()\n\n@test\ndef test_datetime_compare_nat():\n    dt_nat = np.datetime64('NaT', 'D')\n    dt_other = np.datetime64('2000-01-01', 'D')\n    td_nat = np.timedelta64('NaT', 'h')\n    td_other = np.timedelta64(1, 'h')\n\n    for op in (np.equal, np.less, np.less_equal, np.greater, np.greater_equal):\n\n        assert (not op(dt_nat, dt_nat))\n        assert (not op(dt_nat, dt_other))\n        assert (not op(dt_other, dt_nat))\n        assert (not op(td_nat, td_nat))\n        assert (not op(td_nat, td_other))\n        assert (not op(td_other, td_nat))\n\n    assert (np.not_equal(dt_nat, dt_nat))\n    assert (np.not_equal(dt_nat, dt_other))\n    assert (np.not_equal(dt_other, dt_nat))\n\n    assert (np.not_equal(td_nat, td_nat))\n    assert (np.not_equal(td_nat, td_other))\n    assert (np.not_equal(td_other, td_nat))\n\ntest_datetime_compare_nat()\n\n@test\ndef test_datetime_minmax():\n\n    a = np.array('1999-03-12T13', dtype=np.datetime64['m', 2])\n    b = np.array('1999-03-12T12', dtype=np.datetime64['s', 1])\n    assert array_equal(np.minimum(a, b), b)\n    assert (isinstance(np.minimum(a, b), np.datetime64['s', 1]))\n    assert array_equal(np.fmin(a, b), b)\n    assert isinstance(np.fmin(a, b), np.datetime64['s', 1])\n    assert array_equal(np.maximum(a, b), a)\n    assert isinstance(np.maximum(a, b), np.datetime64['s', 1])\n    assert array_equal(np.fmax(a, b), a)\n    assert array_equal(np.minimum(a.view(int), b.view(int)), a.view(int))\n\n    a = np.array('1999-03-12T13', dtype=np.datetime64['m', 2])\n    dtnat = np.array('NaT', dtype=np.datetime64['h', 1])\n    assert array_equal(isnan(np.minimum(a, dtnat)), isnan(dtnat))\n    assert array_equal(isnan(np.minimum(dtnat, a)), isnan(dtnat))\n    assert array_equal(isnan(np.maximum(a, dtnat)), isnan(dtnat))\n    assert array_equal(isnan(np.maximum(dtnat, a)), isnan(dtnat))\n    assert array_equal(np.fmin(dtnat, a), a)\n    assert array_equal(np.fmin(a, dtnat), a)\n    assert array_equal(np.fmax(dtnat, a), a)\n    assert array_equal(np.fmax(a, dtnat), a)\n\n    a = np.array(3, dtype=np.timedelta64['h', 1])\n    b = np.array(3 * 3600 - 3, dtype=np.timedelta64['s', 1])\n    assert array_equal(np.minimum(a, b), b)\n    assert isinstance(np.minimum(a, b), np.timedelta64['s', 1])\n    assert array_equal(np.fmin(a, b), b)\n    assert isinstance(np.fmin(a, b), np.timedelta64['s', 1])\n    assert array_equal(np.maximum(a, b), a)\n    assert isinstance(np.maximum(a, b), np.timedelta64['s', 1])\n    assert array_equal(np.fmax(a, b), a)\n    assert isinstance(np.fmax(a, b), np.timedelta64['s', 1])\n    assert array_equal(np.minimum(a.view(int), b.view(int)), a.view(int))\n\ntest_datetime_minmax()\n\n@test\ndef test_string_parser_variants():\n    assert array_equal(\n        np.array(['1980-02-29T01:02:03'], dtype=np.datetime64['s', 1]),\n        np.array(['1980-02-29 01:02:03'], dtype=np.datetime64['s', 1]))\n    assert array_equal(\n        np.array(['+1980-02-29T01:02:03'], dtype=np.datetime64['s', 1]),\n        np.array(['+1980-02-29 01:02:03'], dtype=np.datetime64['s', 1]))\n    assert array_equal(\n        np.array(['-1980-02-29T01:02:03'], dtype=np.datetime64['s', 1]),\n        np.array(['-1980-02-29 01:02:03'], dtype=np.datetime64['s', 1]))\n    assert array_equal(\n        np.array(['+1980-02-29T01:02:03'], dtype=np.datetime64['s', 1]),\n        np.array(['+1980-02-29 01:02:03Z'], dtype=np.datetime64['s', 1]))\n    assert array_equal(\n        np.array(['-1980-02-29T01:02:03'], np.datetime64['s', 1]),\n        np.array(['-1980-02-29 01:02:03Z'], np.datetime64['s', 1]))\n    assert array_equal(\n        np.array(['1980-02-29T02:02:03'], np.datetime64['s', 1]),\n        np.array(['1980-02-29 00:32:03-0130'], np.datetime64['s', 1]))\n    assert array_equal(\n        np.array(['1980-02-28T22:32:03'], np.datetime64['s', 1]),\n        np.array(['1980-02-29 00:02:03+01:30'], np.datetime64['s', 1]))\n    assert array_equal(\n        np.array(['1980-02-29T02:32:03.506'], np.datetime64['s', 1]),\n        np.array(['1980-02-29 00:32:03.506-02'], np.datetime64['s', 1]))\n    assert array_equal(np.datetime64('1977-03-02T12:30-0230'),\n                       np.datetime64('1977-03-02T15:00'))\n\ntest_string_parser_variants()\n\n@test\ndef test_string_parser_error_check():\n    msg = \"no explicit representation of timezones available for \" \\\n          \"np.datetime64\"\n\n    try:\n        np.array('badvalue', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980X', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-00', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1980-13', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-1', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1980-1-02', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-Mor', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-01-', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-01-0', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1980-01-00', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1980-01-32', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1979-02-29', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1980-02-30', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1980-03-32', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1980-04-31', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1980-05-32', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1980-06-31', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1980-07-32', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1980-08-32', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1980-09-31', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1980-10-32', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1980-11-31', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.array('1980-12-32', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03%', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03 q', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03 25', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03T25', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03 24:01', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03T24:01', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03 -1', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03 01:', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03 01:-1', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03 01:60', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03 01:60:', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03 01:10:-1', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03 01:01:60', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03 01:01:00+0661', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03 01:01:00+2500', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03 01:01:00-0070', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03 01:01:00-3000', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        np.array('1980-02-03 01:01:00-25:00', np.datetime64['us', 1])\n        assert False\n    except ValueError:\n        pass\n\ntest_string_parser_error_check()\n\n@test\ndef test_datetime_as_string():\n    # Check all the units with default string conversion\n    date = '1959-10-13'\n    datetime = '1959-10-13T12:34:56.789012345678901234'\n\n    assert array_equal(np.datetime_as_string(np.datetime64(date, 'Y')), '1959')\n    assert array_equal(np.datetime_as_string(np.datetime64(date, 'M')),\n                       '1959-10')\n    assert array_equal(np.datetime_as_string(np.datetime64(date, 'D')),\n                       '1959-10-13')\n    assert array_equal(np.datetime_as_string(np.datetime64(datetime, 'h')),\n                       '1959-10-13T12')\n    assert array_equal(np.datetime_as_string(np.datetime64(datetime, 'm')),\n                       '1959-10-13T12:34')\n    assert array_equal(np.datetime_as_string(np.datetime64(datetime, 's')),\n                       '1959-10-13T12:34:56')\n    assert array_equal(np.datetime_as_string(np.datetime64(datetime, 'ms')),\n                       '1959-10-13T12:34:56.789')\n\n    assert array_equal(np.datetime_as_string(np.datetime64(datetime, 'us')),\n                       '1959-10-13T12:34:56.789012')\n\n    datetime = '1969-12-31T23:34:56.789012345678901234'\n\n    assert array_equal(np.datetime_as_string(np.datetime64(datetime, 'ns')),\n                       '1969-12-31T23:34:56.789012345')\n    assert array_equal(np.datetime_as_string(np.datetime64(datetime, 'ps')),\n                       '1969-12-31T23:34:56.789012345678')\n    assert array_equal(np.datetime_as_string(np.datetime64(datetime, 'fs')),\n                       '1969-12-31T23:34:56.789012345678901')\n\n    datetime = '1969-12-31T23:59:57.789012345678901234'\n\n    assert array_equal(np.datetime_as_string(np.datetime64(datetime, 'as')),\n                       datetime)\n    datetime = '1970-01-01T00:34:56.789012345678901234'\n\n    assert array_equal(np.datetime_as_string(np.datetime64(datetime, 'ns')),\n                       '1970-01-01T00:34:56.789012345')\n    assert array_equal(np.datetime_as_string(np.datetime64(datetime, 'ps')),\n                       '1970-01-01T00:34:56.789012345678')\n    assert array_equal(np.datetime_as_string(np.datetime64(datetime, 'fs')),\n                       '1970-01-01T00:34:56.789012345678901')\n\n    datetime = '1970-01-01T00:00:05.789012345678901234'\n\n    assert array_equal(np.datetime_as_string(np.datetime64(datetime, 'as')),\n                       datetime)\n\n    a = np.datetime64('2032-07-18T12:23:34.123456', 'us')\n    assert array_equal(np.datetime_as_string(a, unit='h'), '2032-07-18T12')\n    assert array_equal(np.datetime_as_string(a, unit='m'), '2032-07-18T12:23')\n    assert array_equal(np.datetime_as_string(a, unit='s'),\n                       '2032-07-18T12:23:34')\n    assert array_equal(np.datetime_as_string(a, unit='ms'),\n                       '2032-07-18T12:23:34.123')\n    assert array_equal(np.datetime_as_string(a, unit='us'),\n                       '2032-07-18T12:23:34.123456')\n    assert array_equal(np.datetime_as_string(a, unit='ns'),\n                       '2032-07-18T12:23:34.123456000')\n    assert array_equal(np.datetime_as_string(a, unit='ps'),\n                       '2032-07-18T12:23:34.123456000000')\n    assert array_equal(np.datetime_as_string(a, unit='fs'),\n                       '2032-07-18T12:23:34.123456000000000')\n    assert array_equal(np.datetime_as_string(a, unit='as'),\n                       '2032-07-18T12:23:34.123456000000000000')\n\n    # unit='auto' parameter\n    assert array_equal(\n        np.datetime_as_string(np.datetime64('2032-07-18T12:23:34.123456',\n                                            'us'),\n                              unit='auto'), '2032-07-18T12:23:34.123456')\n    assert array_equal(\n        np.datetime_as_string(np.datetime64('2032-07-18T12:23:34.12', 'us'),\n                              unit='auto'), '2032-07-18T12:23:34.120')\n    assert array_equal(\n        np.datetime_as_string(np.datetime64('2032-07-18T12:23:34', 'us'),\n                              unit='auto'), '2032-07-18T12:23:34')\n    assert array_equal(\n        np.datetime_as_string(np.datetime64('2032-07-18T12:23:00', 'us'),\n                              unit='auto'), '2032-07-18T12:23')\n    # 'auto' doesn't split up hour and minute\n    assert array_equal(\n        np.datetime_as_string(np.datetime64('2032-07-18T12:00:00', 'us'),\n                              unit='auto'), '2032-07-18T12:00')\n    assert array_equal(\n        np.datetime_as_string(np.datetime64('2032-07-18T00:00:00', 'us'),\n                              unit='auto'), '2032-07-18')\n    # 'auto' doesn't split up the date\n    assert array_equal(\n        np.datetime_as_string(np.datetime64('2032-07-01T00:00:00', 'us'),\n                              unit='auto'), '2032-07-01')\n    assert array_equal(\n        np.datetime_as_string(np.datetime64('2032-01-01T00:00:00', 'us'),\n                              unit='auto'), '2032-01-01')\n\ntest_datetime_as_string()\n\ndef test_datetime_as_string_timezone():\n    # timezone='local' vs 'UTC'\n    a = np.datetime64('2010-03-15T06:30', 'm')\n    assert array_equal(np.datetime_as_string(a), '2010-03-15T06:30')\n    assert array_equal(np.datetime_as_string(a, timezone='naive'),\n                       '2010-03-15T06:30')\n    assert array_equal(np.datetime_as_string(a, timezone='UTC'),\n                       '2010-03-15T06:30Z')\n    assert (np.datetime_as_string(a, timezone='local') != '2010-03-15T06:30')\n\n    b = np.datetime64('2010-02-15T06:30', 'm')\n\ntest_datetime_as_string_timezone()\n\ndef test_datetime_arange():\n    # With two datetimes provided as strings\n    a = np.arange('2010-01-05', '2010-01-10', dtype=np.datetime64['D', 1])\n    assert isinstance(a.dtype, np.datetime64['D', 1])\n    assert array_equal(\n        a,\n        np.array([\n            '2010-01-05', '2010-01-06', '2010-01-07', '2010-01-08',\n            '2010-01-09'\n        ],\n                 dtype=np.datetime64['D', 1]))\n\n    a = np.arange('1950-02-10', '1950-02-06', -1, dtype=np.datetime64['D', 1])\n    assert isinstance(a.dtype, np.datetime64['D', 1])\n    assert array_equal(\n        a,\n        np.array(['1950-02-10', '1950-02-09', '1950-02-08', '1950-02-07'],\n                 dtype=np.datetime64['D', 1]))\n\n    # #     # Unit should be detected as months here\n    a = np.arange('1969-05', '1970-05', 2, dtype=np.datetime64['M', 1])\n    assert isinstance(a.dtype, np.datetime64['M', 1])\n    assert array_equal(\n        a,\n        np.array('1969-05', np.datetime64['M', 1]) + np.arange(12, step=2))\n\n    a = np.arange('1969', 18, 3, dtype=np.datetime64['Y', 1])\n    assert isinstance(a.dtype, np.datetime64['Y', 1])\n    assert array_equal(\n        a,\n        np.array('1969', dtype=np.datetime64['Y', 1]) + np.arange(18, step=3))\n    a = np.arange('1969-12-19',\n                  22,\n                  np.timedelta64(2, 'D'),\n                  dtype=np.datetime64['D', 1])\n    assert isinstance(a.dtype, np.datetime64['D', 1])\n    assert array_equal(\n        a,\n        np.array('1969-12-19', dtype=np.datetime64['D', 1]) +\n        np.arange(22, step=2))\n\n    try:\n        np.arange(np.datetime64('today'), np.datetime64('today') + 3, 0)\n        assert False\n    except ValueError:\n        pass\n\ntest_datetime_arange()\n\n@test\ndef test_timedelta_arange():\n    a = np.arange(3, 10, dtype=np.timedelta64['generic', 1])\n    assert a.dtype is np.timedelta64['generic', 1]\n    assert array_equal(\n        a,\n        np.array(0, dtype=np.timedelta64['generic', 1]) + np.arange(3, 10))\n    a = np.arange(np.timedelta64(3, 'h'),\n                  10,\n                  2,\n                  dtype=np.timedelta64['m', 1])\n    assert a.dtype is np.timedelta64['m', 1]\n    a = np.arange(np.timedelta64(3, 's'),\n                  10,\n                  2,\n                  dtype=np.timedelta64['generic', 1])\n    assert a.dtype is np.timedelta64['s', 1]\n    assert array_equal(\n        a,\n        np.array(0, dtype=np.timedelta64['s', 1]) + np.arange(3, 10, 2))\n\n    try:\n        np.arange(np.timedelta64(0, 'generic'), np.timedelta64(5, 'generic'),\n                  0)\n        assert False\n    except ValueError:\n        pass\n\ntest_timedelta_arange()\n\n@test\ndef test_datetime_is_busday():\n    holidays = [\n        '2011-01-01', '2011-10-10', '2011-11-11', '2011-11-24', '2011-12-25',\n        '2011-05-30', '2011-02-21', '2011-01-17', '2011-12-26', '2012-01-02',\n        '2011-02-21', '2011-05-30', '2011-07-01', '2011-07-04', '2011-09-05',\n        '2011-10-10', 'NaT'\n    ]\n    bdd = np.busdaycalendar(weekmask='1111100', holidays=holidays)\n\n    # Weekend/weekday tests\n    assert array_equal(np.is_busday('2011-01-01'), False)\n    assert array_equal(np.is_busday('2011-01-02'), False)\n    assert array_equal(np.is_busday('2011-01-03'), True)\n\ntest_datetime_is_busday()\n\n@test\ndef test_isnat():\n    assert (isnat(np.datetime64('NaT', 'ms')))\n    assert (isnat(np.datetime64('NaT', 'ns')))\n    assert (not isnat(np.datetime64('2038-01-19T03:14:07')))\n\n    assert (isnat(np.timedelta64('NaT', \"ms\")))\n    assert (not isnat(np.timedelta64(34, \"ms\")))\n\ntest_isnat()\n\n@test\ndef test_isfinite_scalar():\n    assert (not isfinite(np.datetime64('NaT', 'ms')))\n    assert (not isfinite(np.datetime64('NaT', 'ns')))\n    assert (isfinite(np.datetime64('2038-01-19T03:14:07')))\n\n    assert (not isfinite(np.timedelta64('NaT', \"ms\")))\n    assert (isfinite(np.timedelta64(34, \"ms\")))\n\ntest_isfinite_scalar()\n\n@test\ndef test_datetime64():\n    assert np.datetime64(np.datetime64('2009', 'Y'),\n                         'Y') == np.datetime64(\"2009\")\n    assert np.datetime64(np.datetime64('2009', 'M'),\n                         'M') == np.datetime64(\"2009-01\")\n    assert np.datetime64(np.datetime64('2009', 'D'),\n                         'D') == np.datetime64(\"2009-01-01\")\n\n    assert np.timedelta64(np.timedelta64(1, 'Y'), 'Y') == np.timedelta64(\"1\")\n    assert np.timedelta64(np.timedelta64(1, 'Y'), 'M') == np.timedelta64(\"12\")\n    assert np.timedelta64(np.timedelta64(1, 'Y'), 'D') == np.timedelta64(\"365\")\n\n    assert array_equal(\n        np.timedelta64(1, 'Y') + np.int8(10), np.timedelta64(11, 'Y'))\n    assert array_equal(\n        np.timedelta64(1, 'Y') + np.int16(10), np.timedelta64(11, 'Y'))\n    assert array_equal(\n        np.timedelta64(1, 'Y') + np.int32(10), np.timedelta64(11, 'Y'))\n    assert array_equal(\n        np.timedelta64(1, 'Y') + np.int64(10), np.timedelta64(11, 'Y'))\n\n    assert array_equal(\n        np.timedelta64(21, 'Y') - np.uint8(10), np.timedelta64(11, 'Y'))\n    assert array_equal(\n        np.timedelta64(21, 'Y') - np.uint16(10), np.timedelta64(11, 'Y'))\n    assert array_equal(\n        np.timedelta64(21, 'Y') - np.uint32(10), np.timedelta64(11, 'Y'))\n    assert array_equal(\n        np.timedelta64(21, 'Y') - np.uint64(10), np.timedelta64(11, 'Y'))\n\n    assert array_equal(\n        np.timedelta64(1, 'Y') + 0x12AF, np.timedelta64(4784, 'Y'))\n\n    assert array_equal(\n        np.timedelta64(1, 'h') * np.float(10.0), np.timedelta64(10, 'h'))\n    assert array_equal(\n        np.timedelta64(1, 'h') * np.float16(10.0), np.timedelta64(10, 'h'))\n    assert array_equal(\n        np.timedelta64(1, 'h') * np.float32(10.0), np.timedelta64(10, 'h'))\n\n    assert array_equal(\n        np.timedelta64(1, 's') / np.float(10.0), np.timedelta64(0, 's'))\n    assert array_equal(\n        np.timedelta64(1, 's') / np.float16(10.0), np.timedelta64(0, 's'))\n    assert array_equal(\n        np.timedelta64(1, 's') / np.float32(10.0), np.timedelta64(0, 's'))\n\n    assert array_equal(\n        np.timedelta64(1, 's') / np.double(10.0), np.timedelta64(0, 's'))\n\n    assert np.datetime64('2005-02-25') == np.datetime64(\n        \"2005-02-25T00:00:00.000000\")\n    assert np.datetime64('2005-02') == np.datetime64(\n        \"2005-02-01T00:00:00.000000\")\n    assert np.datetime64(1, 'Y') == np.datetime64(\"1971\")\n    assert np.datetime64('2005-02-25T03:30') == np.datetime64(\n        \"2005-02-25T03:30:00.000000\")\n    assert (np.array(['2001-01-01T12:00', '2002-02-03T13:56:03.172'],\n                     dtype='datetime64') == np.array(\n                         [\"2001-01-01T12:00\",\n                          \"2002-02-03T13:56:03.172\"])).all()\n    assert (np.array([0, 1577836800],\n                     dtype='datetime64[s]') == np.array([\"0\",\n                                                         \"1577836800\"])).all()\n    assert (np.datetime64('2009-01-01') -\n            np.datetime64('2008-01-01')) == np.timedelta64(366, \"D\")\n    assert (\n        np.datetime64('2009') +\n        np.timedelta64(20, 'D')) == np.datetime64('2009-01-21T00:00:00.000000')\n    assert (\n        np.datetime64('2011-06-15T00:00') +\n        np.timedelta64(12, 'h')) == np.datetime64('2011-06-15T12:00:00.000000')\n    assert (np.timedelta64(1, 'W') / np.timedelta64(1, 'D')) == 7\n    assert (np.timedelta64(1, 'W') %\n            np.timedelta64(10, 'D')) == np.timedelta64(7, \"D\")\n    assert np.isnat(np.datetime64('nat') - np.datetime64('2009-01-01'))\n    assert np.isnat(np.datetime64('2009-01-01') + np.timedelta64('nat'))\n\n    assert (np.datetime64('2010-01-01') -\n            np.datetime64('2008-01-01')) == np.timedelta64(731, \"D\")\n    assert (np.datetime64('2010-01-01T04:30') -\n            np.datetime64('2008-01-01T05:30')) == np.timedelta64(1052580, \"m\")\n    assert (np.datetime64('2009', 'Y') -\n            np.datetime64('2010', 'Y')) == np.timedelta64(-1, \"Y\")\n    assert (np.timedelta64(4, 'h') +\n            np.timedelta64(30, 'D')) == np.timedelta64(724, \"h\")\n    assert (np.timedelta64(4, 'h') +\n            np.timedelta64(30, 'ns')) == np.timedelta64(14400000000030, \"ns\")\n    assert (np.timedelta64(2, 'W') / np.timedelta64(1, 'D')) == 14\n    assert (np.timedelta64(2, 'W') %\n            np.timedelta64(11, 'D')) == np.timedelta64(3, \"D\")\n    assert (np.timedelta64(3, 'W') %\n            np.timedelta64(12, 'D')) == np.timedelta64(9, \"D\")\n    assert (np.datetime64('2010-01-01') -\n            np.datetime64('2012-01-01')) == np.timedelta64(-730, \"D\")\n    assert (np.datetime64('2011-06-15T03:00') -\n            np.datetime64('2019-11-01')) == np.timedelta64(-4407660, \"m\")\n    assert (np.timedelta64(1, 'W') / np.timedelta64(-8, 'D')) == -0.875\n    assert (np.timedelta64(2, 'W') %\n            np.timedelta64(15, 'D')) == np.timedelta64(14, \"D\")\n\n    assert (np.datetime64('2022-01-01', 'D') +\n            5) == np.datetime64(\"2022-01-06\")\n    assert (np.datetime64('2022-01-10', 'D') -\n            3) == np.datetime64(\"2022-01-07\")\n    assert (np.datetime64('2022-01-05', 'D') +\n            2) == np.datetime64(\"2022-01-07\")\n    assert (np.datetime64('2022-01-10', 'D') -\n            9) == np.datetime64(\"2022-01-01\")\n    assert (np.datetime64('2022-01-01T12:00', 's') +\n            3) == np.datetime64(\"2022-01-01T12:00:03\")\n    assert (np.datetime64('2022-01-01T12:00', 'ns') + 67) == np.datetime64(\n        \"2022-01-01T12:00:00.000000067\", 'ns')\n    assert (np.datetime64('2022-01-10T10:45', 'm') -\n            5) == np.datetime64(\"2022-01-10T10:40\")\n\n    assert (np.datetime64('2022-01-01', 'Y') +\n            np.byte(5)) == np.datetime64(\"2027\")\n    assert (np.datetime64('2022-01-01', 'Y') +\n            np.ubyte(5)) == np.datetime64(\"2027\")\n    assert (np.datetime64('2022-01-01', 'M') -\n            np.short(4)) == np.datetime64(\"2021-09\")\n    assert (np.datetime64('2022-01-01', 'M') -\n            np.ushort(3)) == np.datetime64(\"2021-10\")\n    assert (np.datetime64('2022-01-01', 'D') +\n            np.intc(5)) == np.datetime64(\"2022-01-06\")\n    assert (np.datetime64('2022-01-01', 'D') +\n            np.int_(7)) == np.datetime64(\"2022-01-08\")\n    assert (np.datetime64('2022-01-01', 'Y') -\n            np.uint(3)) == np.datetime64(\"2019\")\n    assert (np.datetime64('2022-01-01', 'D') +\n            np.longlong(2)) == np.datetime64(\"2022-01-03\")\n    assert (np.datetime64('2022-01-01', 'W') +\n            np.ulonglong(7)) == np.datetime64(\"2022-02-17\")\n    assert (np.datetime64('2022-01-01', 'W') +\n            np.int8(74)) == np.datetime64(\"2023-06-01\")\n\n    x = np.array(['1910-08-16', '1910-08-11', '1910-08-10'],\n                 dtype=np.datetime64['Y', 1])\n    assert array_equal(x - np.array([6, -1, 4], dtype=np.datetime64['Y', 1]),\n                       np.array([-66, -59, -64], dtype=np.datetime64['Y', 1]))\n    assert array_equal(\n        x - np.array(['2022-01-23', '2022-01-23', '2022-01-23'],\n                     dtype=np.datetime64['Y', 1]),\n        np.array([-112, -112, -112], dtype=np.datetime64['Y', 1]))\n    assert array_equal(x - np.array([-5, -3, 0], dtype=np.datetime64['D', 1]),\n                       np.array([-21910, -21912, -21915]))\n    assert array_equal(\n        x - np.array(['2019-01-23', '2020-11-06', '1998-08-23'],\n                     dtype=np.datetime64['D', 1]),\n        np.array([-39834, -40487, -32376]))\n    assert array_equal(\n        x - np.array(['2019-01-23', '2020-11-06', '1998-08-23'],\n                     dtype=np.datetime64['s', 1]),\n        np.array([-3441657600, -3498076800, -2797286400]))\n\n    x = np.array(['2002-10-27', '2002-10-27', '2002-10-27', '2002-10-27'],\n                 dtype=np.datetime64['m', 1])\n    assert (np.datetime_as_string(x, timezone='UTC') == np.array([\n        '2002-10-27T00:00Z', '2002-10-27T00:00Z', '2002-10-27T00:00Z',\n        '2002-10-27T00:00Z'\n    ])).all()\n    assert (np.datetime_as_string(x, unit='h') == np.array(\n        ['2002-10-27T00', '2002-10-27T00', '2002-10-27T00',\n         '2002-10-27T00'])).all()\n    assert (np.datetime_as_string(x, unit='s') == np.array([\n        '2002-10-27T00:00:00', '2002-10-27T00:00:00', '2002-10-27T00:00:00',\n        '2002-10-27T00:00:00'\n    ])).all()\n\ntest_datetime64()\n\n@test\ndef test_busday_offset():\n    assert array_equal(np.busday_offset('2011-10', 0, roll='forward'),\n                       np.datetime64['D', 1]('2011-10-03'))\n    assert array_equal(np.busday_offset('2012-03', -1, roll='forward'),\n                       np.datetime64['D', 1]('2012-02-29'))\n    assert array_equal(\n        np.busday_offset('2011-01', 2, roll='forward', weekmask='Wed'),\n        np.datetime64['D', 1]('2011-01-19'))\n    assert array_equal(\n        np.busday_offset('2012-05', 1, roll='forward', weekmask='Sun'),\n        np.datetime64['D', 1]('2012-05-13'))\n    assert array_equal(np.busday_offset('2011-03-20', 0, roll='forward'),\n                       np.datetime64['D', 1]('2011-03-21'))\n    assert array_equal(np.busday_offset('2011-03-22', 0, roll='forward'),\n                       np.datetime64['D', 1]('2011-03-22'))\n    assert array_equal(np.busday_offset('2011-03-20', 1, roll='backward'),\n                       np.datetime64['D', 1]('2011-03-21'))\n    assert array_equal(np.busday_offset('2011-03-22', 1, roll='backward'),\n                       np.datetime64['D', 1]('2011-03-23'))\n\ntest_busday_offset()\n\n@test\ndef test_is_busday():\n    assert array_equal(\n        np.is_busday(['2011-07-01', '2011-07-02', '2011-07-18'],\n                     holidays=['2011-07-01', '2011-07-04', '2011-07-17']),\n        np.array([False, False, True]))\n    assert array_equal(np.is_busday(\"2012\"), False)\n    assert array_equal(np.is_busday([\"2012\"]), np.array([False]))\n    assert array_equal(\n        np.is_busday(\n            np.array([\n                '2022-02-14', '2022-02-15', '2022-02-16', '2022-02-17',\n                '2022-02-18'\n            ],\n                     dtype=datetime64['D', 1])),\n        np.array([True, True, True, True, True]))\n    assert array_equal(\n        np.is_busday(\n            np.array(['2022-02-19', '2022-02-20'], dtype=datetime64['D', 1])),\n        np.array([False, False]))\n    assert array_equal(\n        np.is_busday(np.array([\n            '2022-02-14', '2022-02-15', '2022-02-16', '2022-02-17',\n            '2022-02-18'\n        ],\n                              dtype=datetime64['D', 1]),\n                     holidays=np.array(['2022-02-17'],\n                                       dtype=datetime64['D', 1])),\n        np.array([True, True, True, False, True]))\n    assert array_equal(\n        np.is_busday(np.array([\n            '2022-02-14', '2022-02-15', '2022-02-16', '2022-02-17',\n            '2022-02-18'\n        ],\n                              dtype=datetime64['D', 1]),\n                     weekmask='1111000'),\n        np.array([True, True, True, True, False]))\n    assert array_equal(\n        np.is_busday(np.array([\n            '2022-02-14', '2022-02-15', '2022-02-16', '2022-02-17',\n            '2022-02-18'\n        ],\n                              dtype=datetime64['D', 1]),\n                     holidays=np.array(['2022-02-17'],\n                                       dtype=datetime64['D', 1]),\n                     weekmask='1111000'),\n        np.array([True, True, True, False, False]))\n\ntest_is_busday()\n\n@test\ndef test_busday_count():\n    assert array_equal(np.busday_count('2022-02-14', '2022-02-18'), 4)\n    assert array_equal(np.busday_count('2022-02-18', '2022-02-14'), -4)\n    assert array_equal(\n        np.busday_count('2022-02-14', '2022-02-18', holidays=['2022-02-16']),\n        3)\n    assert array_equal(\n        np.busday_count('2022-02-14', '2022-02-18', weekmask='1111000'), 4)\n    assert array_equal(\n        np.busday_count('2022-02-14',\n                        '2022-02-18',\n                        busdaycal=np.busdaycalendar(weekmask='1111100',\n                                                    holidays=['2022-02-16'])),\n        3)\n    assert array_equal(\n        np.busday_count('2022-02-14', '2022-02-18', out=np.array(0)),\n        np.array(4))\n    begindates = '2022-02-14'\n    enddates = '2022-02-18'\n\ntest_busday_count()\n\n@test\ndef test_datetime_data():\n    assert np.datetime_data(np.datetime64['Y', 1]) == ('Y', 1)\n    assert np.datetime_data(np.datetime64['M', 1]) == ('M', 1)\n    assert np.datetime_data(np.datetime64['W', 1]) == ('W', 1)\n    assert np.datetime_data(np.datetime64['D', 1]) == ('D', 1)\n    assert np.datetime_data(np.datetime64['h', 1]) == ('h', 1)\n    assert np.datetime_data(np.datetime64['m', 1]) == ('m', 1)\n    assert np.datetime_data(np.datetime64['s', 1]) == ('s', 1)\n    assert np.datetime_data(np.timedelta64['D', 1]) == ('D', 1)\n    assert np.datetime_data(np.timedelta64['h', 1]) == ('h', 1)\n    assert np.datetime_data(np.timedelta64['m', 1]) == ('m', 1)\n    assert np.datetime_data(np.timedelta64['s', 1]) == ('s', 1)\n\ntest_datetime_data()\n\n@test\ndef test_datetime_as_string():\n\n    assert array_equal(\n        np.datetime_as_string(\n            np.array(['2022-02-14T12:34:56', '2022-02-15T08:45:30'],\n                     dtype=datetime64['s', 1])),\n        np.array(['2022-02-14T12:34:56', '2022-02-15T08:45:30']))\n    arr = np.array(['2022-02-14T12:34:56', '2022-02-15T08:45:30'],\n                   dtype=datetime64['Y', 1])\n    result = np.datetime_as_string(arr, unit='Y', timezone='UTC')\n    x = np.array(['2002-10-27', '2002-10-27', '2002-10-27', '2002-10-27'],\n                 dtype=np.datetime64['m', 1])\n    assert (np.datetime_as_string(x, timezone='UTC') == np.array([\n        '2002-10-27T00:00Z', '2002-10-27T00:00Z', '2002-10-27T00:00Z',\n        '2002-10-27T00:00Z'\n    ])).all()\n    assert (np.datetime_as_string(x, unit='h') == np.array(\n        ['2002-10-27T00', '2002-10-27T00', '2002-10-27T00',\n         '2002-10-27T00'])).all()\n    assert (np.datetime_as_string(x, unit='s') == np.array([\n        '2002-10-27T00:00:00', '2002-10-27T00:00:00', '2002-10-27T00:00:00',\n        '2002-10-27T00:00:00'\n    ])).all()\n\ntest_datetime_as_string()\n\n@test\ndef test_datetime_timedelta_op():\n    assert array_equal(\n        np.datetime64('2009', 'Y') + np.timedelta64(20, 'Y'),\n        np.datetime64['Y', 1]('2029'))\n    assert array_equal(\n        np.datetime64('2009', 'Y') + np.timedelta64(20, 'M'),\n        np.datetime64['M', 1]('2010-09'))\n    assert array_equal(\n        np.datetime64('2009', 'Y') + np.timedelta64(20, 'W'),\n        np.datetime64['W', 1]('2009-05-21'))\n    assert array_equal(\n        np.datetime64('2009', 'Y') + np.timedelta64(20, 'D'),\n        np.datetime64['D', 1]('2009-01-21'))\n    assert array_equal(\n        np.datetime64('2009', 'Y') + np.timedelta64(20, 'h'),\n        np.datetime64['h', 1]('2009-01-01T20'))\n    assert array_equal(\n        np.datetime64('2009', 'Y') + np.timedelta64(20, 'm'),\n        np.datetime64['m', 1]('2009-01-01T00:20'))\n    assert array_equal(\n        np.datetime64('2009', 'Y') + np.timedelta64(20, 's'),\n        np.datetime64['s', 1]('2009-01-01T00:00:20'))\n\n    assert array_equal(\n        np.datetime64('2009', 'Y') - np.timedelta64(20, 'Y'),\n        np.datetime64['Y', 1]('1989'))\n    assert array_equal(\n        np.datetime64('2009', 'Y') - np.timedelta64(20, 'M'),\n        np.datetime64['M', 1]('2007-05'))\n    assert array_equal(\n        np.datetime64('2009', 'Y') - np.timedelta64(20, 'W'),\n        np.datetime64['W', 1]('2008-08-14'))\n    assert array_equal(\n        np.datetime64('2009', 'Y') - np.timedelta64(20, 'D'),\n        np.datetime64['D', 1]('2008-12-12'))\n    assert array_equal(\n        np.datetime64('2009', 'Y') - np.timedelta64(20, 'h'),\n        np.datetime64['h', 1]('2008-12-31T04'))\n    assert array_equal(\n        np.datetime64('2009', 'Y') - np.timedelta64(20, 'm'),\n        np.datetime64['m', 1]('2008-12-31T23:40'))\n    assert array_equal(\n        np.datetime64('2009', 'Y') - np.timedelta64(20, 's'),\n        np.datetime64['s', 1]('2008-12-31T23:59:40'))\n\n    x = np.array(['1910-08-16', '1910-08-11', '1910-08-10'],\n                 dtype=np.datetime64['Y', 1])\n    assert array_equal(\n        x + np.array([-5, -3, 0], dtype=np.timedelta64['Y', 1]),\n        np.array(['1905', '1907', '1910'], dtype=np.datetime64['Y', 1]))\n    assert array_equal(\n        x + np.array([-5, -3, 0], dtype=np.timedelta64['M', 1]),\n        np.array(['1909-08', '1909-10', '1910-01'],\n                 dtype=np.datetime64['M', 1]))\n    assert array_equal(\n        x + np.array([-5, -3, 0], dtype=np.timedelta64['D', 1]),\n        np.array(['1909-12-27', '1909-12-29', '1910-01-01'],\n                 dtype=np.datetime64['D', 1]))\n\n    assert array_equal(\n        x - np.array([-5, -3, 0], dtype=np.timedelta64['Y', 1]),\n        np.array(['1915', '1913', '1910'], dtype=np.datetime64['Y', 1]))\n    assert array_equal(\n        x - np.array([-5, -3, 0], dtype=np.timedelta64['M', 1]),\n        np.array(['1910-06', '1910-04', '1910-01'],\n                 dtype=np.datetime64['M', 1]))\n    assert array_equal(\n        x - np.array([-5, -3, 0], dtype=np.timedelta64['D', 1]),\n        np.array(['1910-01-06', '1910-01-04', '1910-01-01'],\n                 dtype=np.datetime64['D', 1]))\n\n    assert array_equal(np.timedelta64(1, 'Y') + 10, np.timedelta64(11, 'Y'))\n    assert array_equal(np.timedelta64(1, 'Y') - 10, np.timedelta64(-9, 'Y'))\n    assert array_equal(np.timedelta64(1, 'Y') * 10, np.timedelta64(10, 'Y'))\n    assert array_equal(np.timedelta64(1, 'Y') / 10, np.timedelta64(0, 'Y'))\n    assert array_equal(np.timedelta64(1, 'Y') * 10.0, np.timedelta64(10, 'Y'))\n    assert array_equal(np.timedelta64(1, 'Y') / 10.0, np.timedelta64(0, 'Y'))\n\n    assert array_equal(np.timedelta64(1, 'M') + 10, np.timedelta64(11, 'M'))\n    assert array_equal(np.timedelta64(1, 'D') - 10, np.timedelta64(-9, 'D'))\n    assert array_equal(np.timedelta64(1, 'h') * 10, np.timedelta64(10, 'h'))\n    assert array_equal(np.timedelta64(1, 's') / 10, np.timedelta64(0, 's'))\n    assert array_equal(np.timedelta64(1, 'h') * 10.0, np.timedelta64(10, 'h'))\n    assert array_equal(np.timedelta64(1, 's') / 10.0, np.timedelta64(0, 's'))\n\ntest_datetime_timedelta_op()\n\n@test\ndef test_busdaycalender():\n    assert array_equal((np.busdaycalendar()).weekmask,\n                       np.array([True, True, True, True, True, False, False]))\n    assert array_equal(\n        (np.busdaycalendar(holidays=['2022-02-16', '2022-05-01'])).weekmask,\n        np.array([True, True, True, True, True, False, False]))\n    assert array_equal(\n        (np.busdaycalendar(holidays=['2022-02-16', '2022-05-01'])).holidays,\n        np.array(['2022-02-16'], dtype=np.datetime64['D', 1]))\n    holidays = '2024-02-14', np.datetime64('NaT')\n    assert array_equal((np.busdaycalendar(holidays=holidays)).holidays,\n                       np.array(['2024-02-14'], dtype=np.datetime64['D', 1]))\n    assert array_equal(\n        (np.busdaycalendar(holidays=['2022-02-16', '2022-05-01'],\n                           weekmask='0011111')).holidays,\n        np.array(['2022-02-16', '2022-05-01'], dtype=np.datetime64['D', 1]))\n    assert array_equal(\n        (np.busdaycalendar(weekmask='1100011',\n                           holidays=['2022-02-16', '2022-05-01']).weekmask),\n        np.array([True, True, False, False, False, True, True]))\n\ntest_busdaycalender()\n\n@test\ndef miscellaneous():\n    assert np.busday_offset('2007-02-25', 11,\n                            weekmask='SatSun') == np.datetime64(\n                                '2007-04-07', 'D')\n    assert np.busday_offset('2007-04-07', -11,\n                            weekmask='SatSun') == np.datetime64(\n                                '2007-02-25', 'D')\n    bdd = np.busdaycalendar(weekmask=\"Sun TueWed  Thu\\tFri\")\n    assert np.array_equal(bdd.weekmask,\n                          np.array([0, 1, 1, 1, 1, 0, 1], dtype=bool))\n    assert np.busday_offset('2011-11-14', -1,\n                            holidays=['2011-11-11'\n                                      ]) == np.datetime64('2011-11-10', 'D')\n    assert np.busday_offset('2011-11-18', -5,\n                            holidays=['2011-11-11'\n                                      ]) == np.datetime64('2011-11-10', 'D')\n    assert np.busday_offset('2011-11-14', -5,\n                            holidays=['2011-11-11'\n                                      ]) == np.datetime64('2011-11-04', 'D')\n    assert np.busday_offset('2011-11-14',\n                            -1,\n                            holidays=['2011-11-11', '2011-11-11'\n                                      ]) == np.datetime64('2011-11-10', 'D')\n\n    # With a NaT holiday\n    assert np.busday_offset('2011-11-14', -1,\n                            holidays=['NaT', '2011-11-11'\n                                      ]) == np.datetime64('2011-11-10', 'D')\n\n    # With another holiday after\n    assert np.busday_offset('2011-11-14',\n                            -1,\n                            holidays=['2011-11-11', '2011-11-24'\n                                      ]) == np.datetime64('2011-11-10', 'D')\n    holidays = [\n        '2011-10-10', '2011-11-11', '2011-11-24', '2011-12-25', '2011-05-30',\n        '2011-02-21', '2011-12-26', '2012-01-02'\n    ]\n    # With another holiday before\n    assert np.busday_offset('2011-11-14',\n                            -1,\n                            holidays=['2011-10-10', '2011-11-11'\n                                      ]) == np.datetime64('2011-11-10', 'D')\n    assert np.busday_offset('2012-01-03', -1,\n                            holidays=holidays) == np.busday_offset(\n                                '2012-01-03', -1 - 1)\n    assert np.busday_offset('2012-01-03', -4,\n                            holidays=holidays) == np.busday_offset(\n                                '2012-01-03', -4 - 1)\n    assert np.busday_offset('2012-01-03', -5,\n                            holidays=holidays) == np.busday_offset(\n                                '2012-01-03', -5 - 2)\n    assert np.busday_offset('2012-01-03', -25,\n                            holidays=holidays) == np.busday_offset(\n                                '2012-01-03', -25 - 2)\n    assert np.busday_offset('2012-01-03', -26,\n                            holidays=holidays) == np.busday_offset(\n                                '2012-01-03', -26 - 3)\n    assert np.busday_offset('2012-01-03', -33,\n                            holidays=holidays) == np.busday_offset(\n                                '2012-01-03', -33 - 3)\n    assert np.busday_offset('2012-01-03', -34,\n                            holidays=holidays) == np.busday_offset(\n                                '2012-01-03', -34 - 4)\n    assert np.busday_offset('2012-01-03', -56,\n                            holidays=holidays) == np.busday_offset(\n                                '2012-01-03', -56 - 4)\n    assert np.busday_offset('2012-01-03', -57,\n                            holidays=holidays) == np.busday_offset(\n                                '2012-01-03', -57 - 5)\n\nmiscellaneous()\n"
  },
  {
    "path": "test/numpy/test_pybridge.codon",
    "content": "import numpy as np\nimport numpy.pybridge\n\ndef check_roundtrip(a):\n    return (type(a).__from_py__(a.__to_py__()) == a).all()\n\n@test\ndef test_pybridge():\n    a = np.arange(9).reshape(3, 3)\n    assert check_roundtrip(a)\n    assert check_roundtrip(a.astype(float))\n    assert check_roundtrip(a.astype(np.float32))\n    assert check_roundtrip(a + a * 2j)\n    assert check_roundtrip((a + a * 2j).astype(np.complex64))\n    assert check_roundtrip(a.T)\n    assert check_roundtrip(a[::2, ::2])\n    assert check_roundtrip(np.array(['aa', 'bb', 'cc']))\n\ntest_pybridge()\n"
  },
  {
    "path": "test/numpy/test_reductions.codon",
    "content": "import numpy as np\nimport numpy.util as util\nimport numpy.random as rnd\n\n# Imported tests\n@test\ndef test_median():\n\n    @test\n    def test_basic():\n        a0 = np.array(1)\n        a1 = np.arange(2)\n        a2 = np.arange(6).reshape(2, 3)\n        assert np.median(a0) == 1\n        assert np.isclose(np.median(a1), 0.5)\n        assert np.isclose(np.median(a2), 2.5)\n        assert np.isclose(np.median(a2, axis=0), [1.5, 2.5, 3.5]).all()\n        assert np.isclose(np.median(a2, axis=1), [1, 4]).all()\n        assert np.isclose(np.median(a2, axis=None), 2.5)\n\n        a = np.array([0.0444502, 0.0463301, 0.141249, 0.0606775])\n        np.isclose((a[1] + a[3]) / 2., np.median(a))\n        a = np.array([0.0463301, 0.0444502, 0.141249])\n        assert np.isclose(a[0], np.median(a))\n        a = np.array([0.0444502, 0.141249, 0.0463301])\n        assert np.isclose(a[-1], np.median(a))\n        # check array scalar result\n        assert np.array(np.median(a)).ndim == 0\n        a[1] = np.nan\n        assert np.array(np.median(a)).ndim == 0\n\n    test_basic()\n\n    @test\n    def test_axis_keyword():\n        a3 = np.array([[2, 3], [0, 1], [6, 7], [4, 5]])\n        for a in (a3, rnd.randint(0, 100, size=(2, 3, 4))):\n            orig = a.copy()\n            np.median(a, axis=None)\n            for ax in range(a.ndim):\n                np.median(a, axis=ax)\n            assert np.array_equal(a, orig)\n\n        assert np.isclose(np.median(a3, axis=0), [3, 4]).all()\n        assert np.isclose(np.median(a3.T, axis=1), [3, 4]).all()\n        assert np.isclose(np.median(a3), 3.5)\n        assert np.isclose(np.median(a3, axis=None), 3.5)\n        assert np.isclose(np.median(a3.T), 3.5)\n\n    test_axis_keyword()\n\n    @test\n    def test_overwrite_keyword():\n        a3 = np.array([[2, 3], [0, 1], [6, 7], [4, 5]])\n        a0 = np.array(1)\n        a1 = np.arange(2)\n        a2 = np.arange(6).reshape(2, 3)\n        assert np.isclose(np.median(a0.copy(), overwrite_input=True), 1)\n        assert np.isclose(np.median(a1.copy(), overwrite_input=True), 0.5)\n        assert np.isclose(np.median(a2.copy(), overwrite_input=True), 2.5)\n        assert np.isclose(np.median(a2.copy(), overwrite_input=True, axis=0),\n                          [1.5, 2.5, 3.5]).all()\n        assert np.isclose(np.median(a2.copy(), overwrite_input=True, axis=1),\n                          [1, 4]).all()\n        assert np.isclose(\n            np.median(a2.copy(), overwrite_input=True, axis=None), 2.5)\n        assert np.isclose(np.median(a3.copy(), overwrite_input=True, axis=0),\n                          [3, 4]).all()\n        assert np.isclose(np.median(a3.T.copy(), overwrite_input=True, axis=1),\n                          [3, 4]).all()\n\n        a4 = np.arange(3 * 4 * 5, dtype=np.float32).reshape((3, 4, 5))\n        rnd.shuffle(a4.ravel())\n        assert np.isclose(\n            np.median(a4, axis=None),\n            np.median(a4.copy(), axis=None, overwrite_input=True))\n        assert np.isclose(np.median(a4, axis=0),\n                          np.median(a4.copy(), axis=0,\n                                    overwrite_input=True)).all()\n        assert np.isclose(np.median(a4, axis=1),\n                          np.median(a4.copy(), axis=1,\n                                    overwrite_input=True)).all()\n        assert np.isclose(np.median(a4, axis=2),\n                          np.median(a4.copy(), axis=2,\n                                    overwrite_input=True)).all()\n\n    test_overwrite_keyword()\n\n    @test\n    def test_array_like():\n        x = [1, 2, 3]\n        assert np.isclose(np.median(x), 2)\n        x2 = [x]\n        assert np.isclose(np.median(x2), 2)\n        assert np.isclose(np.median(x2, axis=0), x).all()\n\n    test_array_like()\n\n    @test\n    def test_out():\n        o = np.zeros((4, ))\n        d = np.ones((3, 4))\n        assert np.array_equal(np.median(d, 0, out=o), o)\n        o = np.zeros((3, ))\n        assert np.array_equal(np.median(d, 1, out=o), o)\n        o = np.zeros(())\n        assert np.array_equal(np.median(d, out=o), o)\n\n    test_out()\n\n    @test\n    def test_out_nan():\n        o = np.zeros((4, ))\n        d = np.ones((3, 4))\n        o = np.zeros((3, ))\n        assert np.array_equal(np.median(d, 1, out=o), o)\n        o = np.zeros(())\n        assert np.array_equal(np.median(d, out=o), o)\n\n    test_out_nan()\n\n    @test\n    def test_nan_behavior():\n        a = np.arange(24, dtype=float)\n        a[2] = np.nan\n        assert np.isnan(np.median(a))\n        assert np.isnan(np.median(a, axis=0))\n\n        a = np.arange(24, dtype=float).reshape(2, 3, 4)\n        a[1, 2, 3] = np.nan\n        a[1, 1, 2] = np.nan\n\n        # no axis\n        assert np.isnan(np.median(a))\n        assert np.array(np.median(a)).ndim == 0\n\n        # axis0\n        b = np.median(np.arange(24, dtype=float).reshape(2, 3, 4), 0)\n        b[2, 3] = np.nan\n        b[1, 2] = np.nan\n        c = np.median(a, 0)\n        for i in range(3):\n            for j in range(4):\n                if np.isnan(c[i][j]):\n                    assert np.isnan(c[i][j])\n                else:\n                    assert np.isclose(c[i][j], b[i][j])\n\n        # axis1\n        b = np.median(np.arange(24, dtype=float).reshape(2, 3, 4), 1)\n        b[1, 3] = np.nan\n        b[1, 2] = np.nan\n        c = np.median(a, 1)\n        for i in range(2):\n            for j in range(4):\n                if np.isnan(c[i][j]):\n                    assert np.isnan(c[i][j])\n                else:\n                    assert np.isclose(c[i][j], b[i][j])\n\n    test_nan_behavior()\n\n    @test\n    def test_empty():\n        # mean(empty array) emits two warnings: empty slice and divide by 0\n        a = np.empty(shape=(0, ), dtype=float)\n        assert np.isnan(np.median(a))\n\n        # multiple dimensions\n        a = np.empty(shape=(0, 0, 0), dtype=float)\n        assert np.isnan(np.median(a))\n\n    test_empty()\n\n    @test\n    def test_object():\n        o = np.arange(7.)\n        assert isinstance((np.median(o)), float)\n        o[2] = np.nan\n        assert isinstance((np.median(o)), float)\n\n    test_object()\n\n    @test\n    def test_extended_axis():\n        o = rnd.normal(size=(71, 23))\n        x = np.dstack([o] * 10)\n\n        assert np.median(x, axis=(0, 1))[0] == np.median(o)\n        x = np.moveaxis(x, -1, 0)\n\n        assert np.isclose(np.median(x, axis=(-2, -1)), np.median(o)).all()\n        x = x.swapaxes(0, 1).copy()\n\n        assert np.isclose(np.median(x, axis=(0, -1)), np.median(o)).all()\n\n        assert np.isclose(np.median(x, axis=(0, 1, 2)), np.median(x,\n                                                                  axis=None))\n        assert np.isclose(np.median(x, axis=(0, )), np.median(x, axis=0)).all()\n        assert np.isclose(np.median(x, axis=(-1, )), np.median(x,\n                                                               axis=-1)).all()\n\n        d = np.arange(3 * 5 * 7 * 11).reshape((3, 5, 7, 11))\n        np.random.shuffle(d.ravel())\n        assert np.median(d, axis=(0, 1, 2))[0] == np.median(d[:, :, :,\n                                                              0].flatten())\n        assert np.median(d, axis=(0, 1, 3))[1] == np.median(d[:, :,\n                                                              1, :].flatten())\n        assert np.median(d, axis=(3, 1, -4))[2] == np.median(d[:, :,\n                                                               2, :].flatten())\n        assert np.median(d, axis=(3, 1,\n                                  2))[2] == np.median(d[2, :, :, :].flatten())\n        assert np.median(d, axis=(3, 2))[2,\n                                         1] == np.median(d[2,\n                                                           1, :, :].flatten())\n        assert np.median(d, axis=(1, -2))[2, 1] == np.median(d[2, :, :,\n                                                               1].flatten())\n        assert np.median(d, axis=(1, 3))[2, 2] == np.median(d[2, :,\n                                                              2, :].flatten())\n\n    test_extended_axis()\n\n    @test\n    def test_extended_axis_invalid():\n        d = np.ones((3, 5, 7, 11))\n        try:\n            np.median(d, axis=-5)\n            assert False\n        except np.AxisError:\n            pass\n        try:\n            np.median(d, axis=(0, -5))\n            assert False\n        except np.AxisError:\n            pass\n        try:\n            np.median(d, axis=4)\n            assert False\n        except np.AxisError:\n            pass\n        try:\n            np.median(d, axis=(0, -4))\n            assert False\n        except ValueError:\n            pass\n        try:\n            np.median(d, axis=(1, 1))\n            assert False\n        except ValueError:\n            pass\n\n    test_extended_axis_invalid()\n\n    @test\n    def test_keepdims():\n        d = np.ones((3, 5, 7, 11))\n        assert np.median(d, axis=None, keepdims=True).shape == (1, 1, 1, 1)\n        assert np.median(d, axis=(0, 1), keepdims=True).shape == (1, 1, 7, 11)\n        assert np.median(d, axis=(0, 3), keepdims=True).shape == (1, 5, 7, 1)\n        assert np.median(d, axis=(1, ), keepdims=True).shape == (3, 1, 7, 11)\n        assert np.median(d, axis=(0, 1, 2, 3),\n                         keepdims=True).shape == (1, 1, 1, 1)\n        assert np.median(d, axis=(0, 1, 3),\n                         keepdims=True).shape == (1, 1, 7, 1)\n\n    test_keepdims()\n\ntest_median()\n\n@test\ndef test_scalar_reduction():\n    # The functions 'sum', 'prod', etc allow specifying axis=0\n    # even for scalars\n    a = np.array(3)\n    assert np.sum(3, axis=0) == 3\n    assert np.prod(3.5, axis=0) == 3.5\n    assert np.any(True, axis=0) == True\n    assert np.all(False, axis=0) == False\n    # assert np.max(3) == 3\n    # assert np.min(2.5) == 2.5\n\n    # Check scalar behaviour for ufuncs without an identity\n    # assert np.power.reduce(3) == 3\n\n    # Make sure that scalars are coming out from this operation\n    assert isinstance(np.prod(np.float32(2.5), axis=0), np.float32)\n    assert isinstance(np.sum(np.float32(2.5), axis=0), np.float32)\n\n    # check if scalars/0-d arrays get cast\n    assert isinstance(np.any(0, axis=0), np.bool)\n\ntest_scalar_reduction()\n\n@test\ndef test_object_array_reduction():\n    # Reductions on object arrays\n    a = np.array(['a', 'b', 'c'], dtype=str)\n\n    assert np.sum(a) == '0abc'\n    assert np.max(a) == 'c'\n    assert np.min(a) == 'a'\n    a = np.array([True, False, True])\n    assert np.sum(a) == 2\n    assert np.prod(a) == 0\n    assert np.any(a) == True\n    assert np.all(a) == False\n    assert np.max(a) == True\n    assert np.min(a) == False\n    assert np.array([[1]]).sum() == 1\n    assert np.array_equal(np.array([[[1, 2]]]).sum((0, 1)), [1, 2])\n    assert np.array([1]).sum(initial=1), 2\n\ntest_object_array_reduction()\n\n@test\ndef test_axis_out_of_bounds():\n    try:\n        a = np.array([False, False])\n        np.all(a, axis=1)\n        assert False\n    except np.AxisError:\n        pass\n    try:\n        a = np.array([False, False])\n        np.all(a, axis=-2)\n        assert False\n    except np.AxisError:\n        pass\n    try:\n        a = np.array([False, False])\n        np.any(a, axis=1)\n        assert False\n    except np.AxisError:\n        pass\n    try:\n        a = np.array([False, False])\n        np.any(a, axis=-2)\n        assert False\n    except np.AxisError:\n        pass\n\ntest_axis_out_of_bounds()\n\n@test\ndef test_sum_initial():\n    # Integer, single axis\n    assert np.sum([3], initial=2) == 5\n\n    # Floating point\n    assert np.isclose(np.sum([0.2], initial=0.1), 0.3)\n\n    # Multiple non-adjacent axes\n    assert np.array_equal(\n        np.sum(np.ones((2, 3, 5), dtype=np.int64), axis=(0, 2), initial=2),\n        [12, 12, 12])\n\ntest_sum_initial()\n\n@test\ndef test_sum():\n\n    for v in (0, 1, 2, 7, 8, 9, 15, 16, 19, 127, 128, 1024, 1235):\n\n        tgt = np.float32(v * (v + 1) / 2)\n        overflow = not np.isfinite(tgt)\n\n        d = np.arange(1, v + 1, dtype=np.float32)\n\n        assert np.isclose(np.sum(d), tgt)\n\n        assert np.isclose(np.sum(d[::-1]), tgt)\n\n    for v in (0, 1, 2, 7, 8, 9, 15, 16, 19, 127, 128, 1024, 1235):\n\n        tgt = np.float32(v * (v + 1) / 2)\n        overflow = not np.isfinite(tgt)\n\n        d = np.arange(1, v + 1, dtype=np.float64)\n\n        assert np.isclose(np.sum(d), tgt)\n\n        assert np.isclose(np.sum(d[::-1]), tgt)\n\n    for v in (0, 1, 2, 7, 8, 9, 15, 16, 19, 127, 128, 1024, 1235):\n\n        tgt = np.float32(v * (v + 1) / 2)\n        overflow = not np.isfinite(tgt)\n\n        d = np.arange(1, v + 1, dtype=np.longdouble)\n\n        assert np.isclose(np.sum(d), tgt)\n\n        assert np.isclose(np.sum(d[::-1]), tgt)\n\n    d = np.ones(500, dtype=np.float32)\n    assert np.isclose(np.sum(d[::2]), 250.)\n    assert np.isclose(np.sum(d[1::2]), 250.)\n    assert np.isclose(np.sum(d[::3]), 167.)\n    assert np.isclose(np.sum(d[1::3]), 167.)\n    assert np.isclose(np.sum(d[::-2]), 250.)\n    assert np.isclose(np.sum(d[-1::-2]), 250.)\n    assert np.isclose(np.sum(d[::-3]), 167.)\n    assert np.isclose(np.sum(d[-1::-3]), 167.)\n    # sum with first reduction entry != 0\n    d = np.ones((1, ), dtype=np.float32)\n    d += d\n    assert np.isclose(d, 2.)\n\n    d = np.ones(500, dtype=np.float64)\n    assert np.isclose(np.sum(d[::2]), 250.)\n    assert np.isclose(np.sum(d[1::2]), 250.)\n    assert np.isclose(np.sum(d[::3]), 167.)\n    assert np.isclose(np.sum(d[1::3]), 167.)\n    assert np.isclose(np.sum(d[::-2]), 250.)\n    assert np.isclose(np.sum(d[-1::-2]), 250.)\n    assert np.isclose(np.sum(d[::-3]), 167.)\n    assert np.isclose(np.sum(d[-1::-3]), 167.)\n    # sum with first reduction entry != 0\n    d = np.ones((1, ), dtype=np.float64)\n    d += d\n    assert np.isclose(d, 2.)\n\n    d = np.ones(500, dtype=np.longdouble)\n    assert np.isclose(np.sum(d[::2]), 250.)\n    assert np.isclose(np.sum(d[1::2]), 250.)\n    assert np.isclose(np.sum(d[::3]), 167.)\n    assert np.isclose(np.sum(d[1::3]), 167.)\n    assert np.isclose(np.sum(d[::-2]), 250.)\n    assert np.isclose(np.sum(d[-1::-2]), 250.)\n    assert np.isclose(np.sum(d[::-3]), 167.)\n    assert np.isclose(np.sum(d[-1::-3]), 167.)\n    # sum with first reduction entry != 0\n    d = np.ones((1, ), dtype=np.longdouble)\n    d += d\n    assert np.isclose(d, 2.)\n\ntest_sum()\n\n@test\ndef test_sum_complex():\n    for v in (0, 1, 2, 7, 8, 9, 15, 16, 19, 127, 128, 1024, 1235):\n        tgt = -(v * (v + 1) / 2)\n        d = np.empty(v, dtype=np.complex64)\n        d.imag = -np.arange(1, v + 1)\n        assert np.isclose(np.sum(d.imag), tgt)\n\n    for v in (0, 1, 2, 7, 8, 9, 15, 16, 19, 127, 128, 1024, 1235):\n        tgt = -(v * (v + 1) / 2)\n        d = np.empty(v, dtype=np.complex128)\n        d.imag = -np.arange(1, v + 1)\n        assert np.isclose(np.sum(d.imag), tgt)\n\n    for v in (0, 1, 2, 7, 8, 9, 15, 16, 19, 127, 128, 1024, 1235):\n        tgt = -(v * (v + 1) / 2)\n        d = np.empty(v, dtype=np.clongdouble)\n        d.imag = -np.arange(1, v + 1)\n        assert np.isclose(np.sum(d.imag), tgt)\n\n    d = np.ones(500, dtype=np.complex64) + 1j\n    assert np.isclose(np.sum(d[::2]), 250. + 250j)\n    assert np.isclose(np.sum(d[1::2]), 250. + 250j)\n    assert np.isclose(np.sum(d[::3]), 167. + 167j)\n    assert np.isclose(np.sum(d[1::3]), 167. + 167j)\n    assert np.isclose(np.sum(d[::-2]), 250. + 250j)\n    assert np.isclose(np.sum(d[-1::-2]), 250. + 250j)\n    assert np.isclose(np.sum(d[::-3]), 167. + 167j)\n    assert np.isclose(np.sum(d[-1::-3]), 167. + 167j)\n    # sum with first reduction entry != 0\n    d = np.ones((1, ), dtype=np.complex64) + 1j\n    d += d\n    assert np.isclose(d, 2. + 2j)\n\n    d = np.ones(500, dtype=np.complex128) + 1j\n    assert np.isclose(np.sum(d[::2]), 250. + 250j)\n    assert np.isclose(np.sum(d[1::2]), 250. + 250j)\n    assert np.isclose(np.sum(d[::3]), 167. + 167j)\n    assert np.isclose(np.sum(d[1::3]), 167. + 167j)\n    assert np.isclose(np.sum(d[::-2]), 250. + 250j)\n    assert np.isclose(np.sum(d[-1::-2]), 250. + 250j)\n    assert np.isclose(np.sum(d[::-3]), 167. + 167j)\n    assert np.isclose(np.sum(d[-1::-3]), 167. + 167j)\n    # sum with first reduction entry != 0\n    d = np.ones((1, ), dtype=np.complex128) + 1j\n    d += d\n    assert np.isclose(d, 2. + 2j)\n\n    d = np.ones(500, dtype=np.clongdouble) + 1j\n    assert np.isclose(np.sum(d[::2]), 250. + 250j)\n    assert np.isclose(np.sum(d[1::2]), 250. + 250j)\n    assert np.isclose(np.sum(d[::3]), 167. + 167j)\n    assert np.isclose(np.sum(d[1::3]), 167. + 167j)\n    assert np.isclose(np.sum(d[::-2]), 250. + 250j)\n    assert np.isclose(np.sum(d[-1::-2]), 250. + 250j)\n    assert np.isclose(np.sum(d[::-3]), 167. + 167j)\n    assert np.isclose(np.sum(d[-1::-3]), 167. + 167j)\n    # sum with first reduction entry != 0\n    d = np.ones((1, ), dtype=np.clongdouble) + 1j\n    d += d\n    assert np.isclose(d, 2. + 2j)\n\ntest_sum_complex()\n\n@test\ndef test_sum_stability():\n    a = np.ones(500, dtype=np.float64)\n    assert np.isclose((a / 10.).sum() - a.size / 10., 0)\n\ntest_sum_stability()\n\n@test\ndef test_sum(a,\n             expected,\n             axis=None,\n             dtype: type = NoneType,\n             out=None,\n             keepdims: Literal[bool] = False,\n             initial=0,\n             where=util._NoValue()):\n    if isinstance(expected, int) or isinstance(expected, float):\n        assert np.sum(a,\n                      axis=axis,\n                      dtype=dtype,\n                      out=out,\n                      keepdims=keepdims,\n                      initial=initial,\n                      where=where) == expected\n    else:\n        assert (np.sum(a,\n                       axis=axis,\n                       dtype=dtype,\n                       out=out,\n                       keepdims=keepdims,\n                       initial=initial,\n                       where=where) == expected).all()\n\ntest_sum(np.empty(0, float), 0.0)\ntest_sum([0.5, 1.5], 2.0)\ntest_sum([0.5, 0.7, 0.2, 1.5], 1, dtype=int)\ntest_sum([[0, 1], [0, 5]], 6)\ntest_sum([[0, 1], [0, 5]], np.array([0, 6]), axis=0)\ntest_sum([[0, 1], [0, 5]], np.array([1, 5]), axis=1)\ntest_sum([10], 15, initial=5)\ntest_sum([[0, 1], [0, 5]], np.array([[6]]), keepdims=True)\ntest_sum([[0, 1], [0, 5]], np.array(6.), out=np.empty((), dtype=float))\ntest_sum([[0, 1], [0, 5]],\n         np.array([1., 5.]),\n         axis=1,\n         out=np.empty(2, dtype=float))\ntest_sum([[0., 1.], [np.nan, 5.]],\n         np.array([1., 5.]),\n         axis=1,\n         where=[False, True])\ntest_sum([[0, 1], [0, 5]], np.array([[0, 6]]), axis=0, keepdims=True)\ntest_sum(np.array([[1.0, 2.0], [3.0, 4.0], [5.0, 6.0]]), 21.)\ntest_sum(np.array([[1 + 2j, 3 + 4j, 5 + 6j], [7 + 8j, 9 + 10j, 11 + 12j]]),\n         np.array([9. + 12.j, 27. + 30.j]),\n         axis=1)\n\n@test\ndef test_prod(a,\n              expected,\n              axis=None,\n              dtype: type = NoneType,\n              out=None,\n              keepdims: Literal[bool] = False,\n              initial=1,\n              where=util._NoValue()):\n    if isinstance(expected, int) or isinstance(expected, float):\n        assert np.prod(a,\n                       axis=axis,\n                       dtype=dtype,\n                       out=out,\n                       keepdims=keepdims,\n                       initial=initial,\n                       where=where) == expected\n    else:\n        assert (np.prod(a,\n                        axis=axis,\n                        dtype=dtype,\n                        out=out,\n                        keepdims=keepdims,\n                        initial=initial,\n                        where=where) == expected).all()\n\ntest_prod(np.empty(0, float), 1.0)\ntest_prod([0.5, 1.5], 0.75)\ntest_prod([0.5, 0.7, 0.2, 1.5], 0, dtype=int)\ntest_prod([[2, 1], [3, 5]], 30)\ntest_prod([[2, 1], [3, 5]], np.array([6, 5]), axis=0)\ntest_prod([[2, 1], [3, 5]], np.array([2, 15]), axis=1)\ntest_prod([10], 50, initial=5)\ntest_prod([[2, 1], [3, 5]], np.array([[30]]), keepdims=True)\ntest_prod([[2, 1], [3, 5]], np.array(30.), out=np.empty((), dtype=float))\ntest_prod([[2, 1], [3, 5]],\n          np.array([2., 15.]),\n          axis=1,\n          out=np.empty(2, dtype=float))\ntest_prod([[2., 1.], [np.nan, 5.]],\n          np.array([1., 5.]),\n          axis=1,\n          where=[False, True])\ntest_prod(np.array([[1 + 2j, 3 + 4j, 5 + 6j], [7 + 8j, 9 + 10j, 11 + 12j]]),\n          np.array([-85. + 20.j, -1891. + 1358.j]),\n          axis=1)\n\n@test\ndef test_mean(a,\n              expected,\n              axis=None,\n              dtype: type = NoneType,\n              out=None,\n              keepdims: Literal[bool] = False,\n              where=util._NoValue()):\n    if isinstance(expected, int) or isinstance(expected, float) or isinstance(\n            expected, complex):\n        assert np.mean(a,\n                       axis=axis,\n                       dtype=dtype,\n                       out=out,\n                       keepdims=keepdims,\n                       where=where) == expected\n    else:\n        assert (np.mean(a,\n                        axis=axis,\n                        dtype=dtype,\n                        out=out,\n                        keepdims=keepdims,\n                        where=where) == expected).all()\n\ntest_mean(np.array([[1, 2], [3, 4]]), 2.5)\ntest_mean(np.array([[1, 2], [3, 4]]), np.array([2., 3.]), axis=0)\ntest_mean(np.array([[1, 2], [3, 4]]), np.array([1.5, 3.5]), axis=1)\ntest_mean(np.array([[1, 2], [3, 4]]), np.array([[2.5]]), keepdims=True)\ntest_mean([[2, 1], [3, 5]], np.array(2.75), out=np.empty((), dtype=float))\ntest_mean([[2, 1], [3, 5]],\n          np.array([1.5, 4.]),\n          axis=1,\n          out=np.empty(2, dtype=float))\ntest_mean(np.array([[1, 2], [3, 4]]),\n          np.array([[1.5], [3.5]]),\n          axis=1,\n          keepdims=True)\ntest_mean(np.array([[5, 9, 13], [14, 10, 12], [11, 15, 19]]),\n          9.0,\n          where=[[True], [False], [False]])\ntest_mean(np.array([1.5, 2.5, 3.5, 4.5]), 3.0)\ntest_mean(np.array([1 + 2j, 3 + 4j, 5 + 6j]), (3 + 4j))\ntest_mean(np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),\n          np.array([2., 5.]),\n          axis=1)\ntest_mean(np.array([[1 + 2j, 3 + 4j, 5 + 6j], [7 + 8j, 9 + 10j, 11 + 12j]]),\n          np.array([4. + 5.j, 6. + 7.j, 8. + 9.j]),\n          axis=0)\n\n@test\ndef test_nanmean(a,\n                 expected,\n                 axis=None,\n                 dtype: type = NoneType,\n                 out=None,\n                 keepdims: Literal[bool] = False,\n                 where=util._NoValue()):\n    if isinstance(expected, int) or isinstance(expected, float):\n        assert np.nanmean(a,\n                          axis=axis,\n                          dtype=dtype,\n                          out=out,\n                          keepdims=keepdims,\n                          where=where) == expected\n    else:\n        assert (np.nanmean(a,\n                           axis=axis,\n                           dtype=dtype,\n                           out=out,\n                           keepdims=keepdims,\n                           where=where) == expected).all()\n\ntest_nanmean(np.array([[np.nan, 1, 2], [3, 4, np.nan]]), 2.5)\ntest_nanmean(np.array([[np.nan, 1, 2], [3, np.nan, 4]]),\n             np.array([3., 1., 3.]),\n             axis=0)\ntest_nanmean(np.array([[1, 2, np.nan], [3, 4, np.nan]]),\n             np.array([1.5, 3.5]),\n             axis=1)\ntest_nanmean(np.array([[1, np.nan, 2], [3, np.nan, 4]]),\n             np.array([[2.5]]),\n             keepdims=True)\ntest_nanmean([[2, np.nan, 1], [3, 5, np.nan]],\n             np.array(2.75),\n             out=np.empty((), dtype=float))\ntest_nanmean([[np.nan, 2, 1], [np.nan, 3, 5]],\n             np.array([1.5, 4.]),\n             axis=1,\n             out=np.empty(2, dtype=float))\ntest_nanmean(np.array([[np.nan, 1, 2], [3, np.nan, 4]]),\n             np.array([[1.5], [3.5]]),\n             axis=1,\n             keepdims=True)\ntest_nanmean(np.array([[5, np.nan, 9, 13], [np.nan, 14, 10, 12],\n                       [11, 15, np.nan, 19]]),\n             9.0,\n             where=[[True], [False], [False]])\n\n@test\ndef test_var(a,\n             expected,\n             axis=None,\n             dtype: type = NoneType,\n             out=None,\n             keepdims: Literal[bool] = False,\n             where=util._NoValue()):\n    if isinstance(expected, int) or isinstance(expected, float) or isinstance(\n            expected, complex):\n        assert np.var(a,\n                      axis=axis,\n                      dtype=dtype,\n                      out=out,\n                      keepdims=keepdims,\n                      where=where) == expected\n    else:\n        assert (np.var(a,\n                       axis=axis,\n                       dtype=dtype,\n                       out=out,\n                       keepdims=keepdims,\n                       where=where) == expected).all()\n\ntest_var(np.array([[1, 2], [3, 4]]), 1.25)\ntest_var(np.array([[1, 2], [3, 4]]), np.array([1., 1.]), axis=0)\ntest_var(np.array([[1, 2], [3, 4]]), np.array([0.25, 0.25]), axis=1)\ntest_var(np.array([[1., 2.], [3., 4.]]), np.array([0.25, 0.25]), axis=1)\ntest_var(np.array([[1, 2], [3, 4]]), np.array([[1.25]]), keepdims=True)\ntest_var([[2, 1], [3, 5]], np.array(2.1875), out=np.empty((), dtype=float))\ntest_var([[2, 1], [3, 5]],\n         np.array([0.25, 1.]),\n         axis=1,\n         out=np.empty(2, dtype=float))\ntest_var(np.array([[1, 2], [3, 4]]),\n         np.array([[0.25], [0.25]]),\n         axis=1,\n         keepdims=True)\ntest_var(np.array([[5, 9, 13], [14, 10, 12], [11, 15, 19]]),\n         2.6666666666666665,\n         where=[[False], [True], [False]])\ntest_var(np.array([1.0, 2.0, 3.0, 4.0, 5.0]), 2.)\ntest_var(np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),\n         np.array([2.25, 2.25, 2.25]),\n         axis=0)\n\n@test\ndef test_nanvar(a,\n                expected,\n                axis=None,\n                dtype: type = NoneType,\n                out=None,\n                keepdims: Literal[bool] = False,\n                where=util._NoValue()):\n    if isinstance(expected, int) or isinstance(expected, float):\n        assert np.nanvar(a,\n                         axis=axis,\n                         dtype=dtype,\n                         out=out,\n                         keepdims=keepdims,\n                         where=where) == expected\n    else:\n        assert (np.nanvar(a,\n                          axis=axis,\n                          dtype=dtype,\n                          out=out,\n                          keepdims=keepdims,\n                          where=where) == expected).all()\n\ntest_nanvar(np.array([[1, np.nan, 2], [np.nan, 3, 4]]), 1.25)\ntest_nanvar(np.array([[1, np.nan, 2], [3, 4, np.nan]]),\n            np.array([1., 0., 0.]),\n            axis=0)\ntest_nanvar(np.array([[1, 2, np.nan], [3, np.nan, 4]]),\n            np.array([0.25, 0.25]),\n            axis=1)\ntest_nanvar(np.array([[1, np.nan, 2], [np.nan, 3, 4]]),\n            np.array([[1.25]]),\n            keepdims=True)\ntest_nanvar([[np.nan, 2, 1], [np.nan, 3, 5]],\n            np.array(2.1875),\n            out=np.empty((), dtype=float))\ntest_nanvar([[2, np.nan, 1], [3, 5, np.nan]],\n            np.array([0.25, 1.]),\n            axis=1,\n            out=np.empty(2, dtype=float))\ntest_nanvar(np.array([[1, 2, np.nan], [np.nan, 3, 4]]),\n            np.array([[0.25], [0.25]]),\n            axis=1,\n            keepdims=True)\ntest_nanvar(np.array([[5, 9, 13], [14, 10, 12], [11, 15, 19]]),\n            2.6666666666666665,\n            where=[[False], [True], [False]])\n\n@test\ndef test_std(a,\n             expected,\n             axis=None,\n             dtype: type = NoneType,\n             out=None,\n             keepdims: Literal[bool] = False,\n             where=util._NoValue()):\n    if isinstance(expected, int) or isinstance(expected, float) or isinstance(\n            expected, complex):\n        assert np.round(\n            np.std(a,\n                   axis=axis,\n                   dtype=dtype,\n                   out=out,\n                   keepdims=keepdims,\n                   where=where), 8) == np.round(expected, 8)\n    else:\n        assert (np.round(\n            np.std(a,\n                   axis=axis,\n                   dtype=dtype,\n                   out=out,\n                   keepdims=keepdims,\n                   where=where), 8) == expected).all()\n\ntest_std(np.array([[1, 2], [3, 4]]), 1.118033988749895)\ntest_std(np.array([[1, 2], [3, 4]]), np.array([1., 1.]), axis=0)\ntest_std(np.array([[1, 2], [3, 4]]), np.array([0.5, 0.5]), axis=1)\ntest_std(np.array([[1, 2], [3, 4]]), np.array([[1.11803399]]), keepdims=True)\ntest_std([[2, 1], [3, 5]], np.array(1.47901995), out=np.empty((), dtype=float))\ntest_std([[2, 1], [3, 5]],\n         np.array([0.5, 1.]),\n         axis=1,\n         out=np.empty(2, dtype=float))\ntest_std(np.array([[1, 2], [3, 4]]),\n         np.array([[0.5], [0.5]]),\n         axis=1,\n         keepdims=True)\ntest_std(np.array([[5, 9, 13], [14, 10, 12], [11, 15, 19]]),\n         3.265986323710904,\n         where=[[True], [False], [False]])\ntest_std(np.array([1.0, 2.0, 3.0, 4.0, 5.0]), 1.4142135623730951)\ntest_std(np.array([1 + 2j, 3 + 4j, 5 + 6j]), 2.309401076758503)\ntest_std(np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]]),\n         np.array([1.5, 1.5, 1.5]),\n         axis=0)\ntest_std(np.array([[1 + 2j, 3 + 4j, 5 + 6j], [7 + 8j, 9 + 10j, 11 + 12j]]),\n         np.array([2.30940108, 2.30940108]),\n         axis=1)\n\n@test\ndef test_nanstd(a,\n                expected,\n                axis=None,\n                dtype: type = NoneType,\n                out=None,\n                keepdims: Literal[bool] = False,\n                where=util._NoValue()):\n    if isinstance(expected, int) or isinstance(expected, float):\n        assert np.nanstd(a,\n                         axis=axis,\n                         dtype=dtype,\n                         out=out,\n                         keepdims=keepdims,\n                         where=where) == expected\n    else:\n        assert (np.round(\n            np.nanstd(a,\n                      axis=axis,\n                      dtype=dtype,\n                      out=out,\n                      keepdims=keepdims,\n                      where=where), 8) == expected).all()\n\ntest_nanstd(np.array([[1, 2, np.nan], [np.nan, 3, 4]]), 1.118033988749895)\ntest_nanstd(np.array([[np.nan, 1, 2], [3, 4, np.nan]]),\n            np.array([0., 1.5, 0.]),\n            axis=0)\ntest_nanstd(np.array([[np.nan, np.nan, 1, 2], [3, 4, np.nan, np.nan]]),\n            np.array([0.5, 0.5]),\n            axis=1)\ntest_nanstd(np.array([[1, 2, np.nan], [3, np.nan, 4]]),\n            np.array([[1.11803399]]),\n            keepdims=True)\ntest_nanstd([[2, np.nan, 1], [np.nan, 3, 5]],\n            np.array(1.47901995),\n            out=np.empty((), dtype=float))\ntest_nanstd([[2, 1, np.nan], [3, 5, np.nan]],\n            np.array([0.5, 1.]),\n            axis=1,\n            out=np.empty(2, dtype=float))\ntest_nanstd(np.array([[1, np.nan, 2], [3, np.nan, 4]]),\n            np.array([[0.5], [0.5]]),\n            axis=1,\n            keepdims=True)\ntest_nanstd(np.array([[np.nan, 5, 9, 13], [14, 10, 12, np.nan],\n                      [11, np.nan, 15, 19]]),\n            3.265986323710904,\n            where=[[True], [False], [False]])\n\n@test\ndef test_min(a,\n             expected,\n             axis=None,\n             out=None,\n             keepdims: Literal[bool] = False,\n             initial=util._NoValue(),\n             where=util._NoValue()):\n    if isinstance(expected, int) or isinstance(expected, float):\n        assert a.min(axis=axis,\n                     out=out,\n                     keepdims=keepdims,\n                     initial=initial,\n                     where=where) == expected\n    else:\n        assert (a.min(axis=axis,\n                      out=out,\n                      keepdims=keepdims,\n                      initial=initial,\n                      where=where) == expected).all()\n\ntest_min(np.array([0.5, 1.5]), 0.5)\ntest_min(np.array([[0, 1], [2, 5]]), 0)\ntest_min(np.array([10]), 5, initial=5)\ntest_min(np.array([[-np.inf, 1.], [0., 5.]]),\n         np.array([[-np.inf]]),\n         keepdims=True)\ntest_min(np.array([[0, 1], [0, 5]]),\n         np.array(0.),\n         out=np.empty((), dtype=float))\ntest_min(np.array([[3, 1], [-2, 5]]), np.array([-2, 1]), axis=0)\ntest_min(np.array([[3, 1], [-2, 5]]), np.array([1, -2]), axis=1)\ntest_min(np.array([[3, 1], [8, 5]]),\n         np.array([1., 5.]),\n         axis=1,\n         out=np.empty(2, dtype=float))\ntest_min(np.array([[0., 1.], [np.nan, 5.]]),\n         np.array([1., 5.]),\n         axis=1,\n         initial=6,\n         where=[False, True])\ntest_min(np.array([[0, 1], [0, 5]]), np.array([[0, 1]]), axis=0, keepdims=True)\ntest_min(np.array([[1 + 2j, 3 + 4j, 5 + 6j], [7 + 8j, 9 + 10j, 11 + 12j]]),\n         np.array([1. + 2.j, 7. + 8.j]),\n         axis=1)\n\n@test\ndef test_max(a,\n             expected,\n             axis=None,\n             out=None,\n             keepdims: Literal[bool] = False,\n             initial=util._NoValue(),\n             where=util._NoValue()):\n    if isinstance(expected, int) or isinstance(expected, float):\n        assert np.max(a,\n                      axis=axis,\n                      out=out,\n                      keepdims=keepdims,\n                      initial=initial,\n                      where=where) == expected\n    else:\n        assert (np.max(a,\n                       axis=axis,\n                       out=out,\n                       keepdims=keepdims,\n                       initial=initial,\n                       where=where) == expected).all()\n\ntest_max([0.5, 1.5], 1.5)\ntest_max([[0, 1], [2, 5]], 5)\ntest_max([[3, 1], [-2, 5]], np.array([3, 5]), axis=0)\ntest_max([[3, 1], [-2, 5]], np.array([3, 5]), axis=1)\ntest_max([10], 10, initial=5)\ntest_max([[-np.inf, 1.], [0., 5.]], np.array([[5.]]), keepdims=True)\ntest_max([[0, 1], [0, 5]], np.array(5.), out=np.empty((), dtype=float))\ntest_max([[3, 1], [8, 5]],\n         np.array([3., 8.]),\n         axis=1,\n         out=np.empty(2, dtype=float))\ntest_max([[0., 1.], [np.nan, 5.]],\n         np.array([6., 6.]),\n         axis=1,\n         initial=6,\n         where=[False, True])\ntest_max([[0, 1], [0, 5]], np.array([[0, 5]]), axis=0, keepdims=True)\ntest_max(np.array([[1 + 2j, 3 + 4j, 5 + 6j], [7 + 8j, 9 + 10j, 11 + 12j]]),\n         np.array([5. + 6.j, 11. + 12.j]),\n         axis=1)\n\n@test\ndef test_ptp(a, expected, axis=None, out=None, keepdims: Literal[bool] = False):\n    if isinstance(expected, int) or isinstance(expected, float) or isinstance(\n            expected, complex):\n        assert np.ptp(a, axis=axis, out=out, keepdims=keepdims) == expected\n    else:\n        assert (np.ptp(a, axis=axis, out=out,\n                       keepdims=keepdims) == expected).all()\n\ntest_ptp(np.array([[4, 9, 2, 10], [6, 9, 7, 12]]), 10)\ntest_ptp(np.array([[4, 9, 2, 10], [6, 9, 7, 12]]), np.array([8, 6]), axis=1)\ntest_ptp(np.array([[4, 9, 2, 10], [6, 9, 7, 12]]),\n         np.array([2, 0, 5, 2]),\n         axis=0)\ntest_ptp(np.array([[4, 9, 2, 10], [6, 9, 7, 12]]),\n         np.array([[2, 0, 5, 2]]),\n         axis=0,\n         keepdims=True)\ntest_ptp([[3, 1], [8, 5]],\n         np.array([2., 3.]),\n         axis=1,\n         out=np.empty(2, dtype=float))\ntest_ptp([[0, 1], [0, 5]], np.array(5.), out=np.empty((), dtype=float))\ntest_ptp([[0, 1], [0, 5]], np.array([[0, 4]]), axis=0, keepdims=True)\ntest_ptp(np.array([1.5, 2.8, 5.3, 7.1, 2.0]), 5.6)\ntest_ptp(np.array([1 + 2j, 3 + 1j, 5 + 4j, 7 + 0j, 2 - 3j]), (6 - 2j))\ntest_ptp(np.array([[1 + 2j, 3 + 1j, 5 + 4j], [7 + 0j, 2 - 3j, 4 - 0j]]),\n         np.array([4. + 2.j, 5. + 3.j]),\n         axis=1)\n\n@test\ndef test_argmin(a,\n                expected,\n                axis=None,\n                out=None,\n                keepdims: Literal[bool] = False):\n    if isinstance(expected, int):\n        assert np.argmin(a, axis=axis, out=out, keepdims=keepdims) == expected\n    else:\n        assert (np.argmin(a, axis=axis, out=out,\n                          keepdims=keepdims) == expected).all()\n\ntest_argmin(np.array([[4, 9, 2, 10], [6, 9, 7, 12]]), 2)\ntest_argmin(np.array([[4, 9, 2, 10], [6, 9, 7, 12]]), np.array([2, 0]), axis=1)\ntest_argmin(np.array([[4, 9, 2, 10], [6, 9, 7, 12]]),\n            np.array([0, 0, 0, 0]),\n            axis=0)\ntest_argmin(np.array([[4, 9, 2, 10], [6, 9, 7, 12]]),\n            np.array([[0, 0, 0, 0]]),\n            axis=0,\n            keepdims=True)\ntest_argmin([[0, 1], [0, 5]], np.array(0), out=np.empty((), dtype=int))\ntest_argmin([[0, 1], [2, 5]], np.array([[0, 0]]), axis=0, keepdims=True)\ntest_argmin(np.array([[1 + 2j, 3 + 4j, 5 + 6j], [7 + 8j, 9 + 10j, 11 + 12j]]),\n            np.array([0, 0]),\n            axis=1)\n\n@test\ndef test_argmax(a,\n                expected,\n                axis=None,\n                out=None,\n                keepdims: Literal[bool] = False):\n    if isinstance(expected, int):\n        assert np.argmax(a, axis=axis, out=out, keepdims=keepdims) == expected\n    else:\n        assert (np.argmax(a, axis=axis, out=out,\n                          keepdims=keepdims) == expected).all()\n\ntest_argmax(np.array([[4, 9, 2, 10], [6, 9, 7, 12]]), 7)\ntest_argmax(np.array([[4, 9, 2, 10], [6, 9, 7, 12]]), np.array([3, 3]), axis=1)\ntest_argmax(np.array([[4, 9, 2, 10], [6, 9, 7, 12]]),\n            np.array([1, 0, 1, 1]),\n            axis=0)\ntest_argmax(np.array([[4, 9, 2, 10], [6, 9, 7, 12]]),\n            np.array([[1, 0, 1, 1]]),\n            axis=0,\n            keepdims=True)\ntest_argmax([[0, 1], [0, 5]], np.array(3), out=np.empty((), dtype=int))\ntest_argmax([[0, 1], [2, 5]], np.array([[1, 1]]), axis=0, keepdims=True)\ntest_argmax(np.array([[1 + 2j, 3 + 4j, 5 + 6j], [7 + 8j, 9 + 10j, 11 + 12j]]),\n            np.array([2, 2]),\n            axis=1)\n\n@test\ndef test_any():\n    assert (np.any([[True, False], [False, False]],\n                   axis=0) == np.array([True, False])).all()\n    assert np.any([-1, 0, 5]) == True\n    assert np.any(np.nan) == True\n    assert np.any([[True, False], [False, False]], where=[[False],\n                                                          [True]]) == False\n    assert (np.any([-1, 4, 5], out=np.array(False)) == np.array(True)).all()\n\ntest_any()\n\n@test\ndef test_all():\n    assert np.all([[True, False], [True, True]]) == False\n    assert (np.all([[True, False], [True, True]],\n                   axis=0) == np.array([True, False])).all()\n    assert np.all([-1, 4, 5]) == True\n    assert np.all([1.0, np.nan]) == True\n    assert np.all([[True, True], [False, True]], where=[[True],\n                                                        [False]]) == True\n    assert (np.all([-1, 4, 5], out=np.array(False)) == np.array(True)).all()\n\ntest_all()\n\n@test\ndef test_count_nonzero(a, expected, axis=None, keepdims: Literal[bool] = False):\n    if isinstance(expected, int) or isinstance(expected, float):\n        assert np.count_nonzero(a, axis=axis, keepdims=keepdims) == expected\n    else:\n        assert (np.count_nonzero(a, axis=axis,\n                                 keepdims=keepdims) == expected).all()\n\ntest_count_nonzero(np.eye(4), 4)\ntest_count_nonzero(np.array([[0, 1, 7, 0], [3, 0, 2, 19]]), 5)\ntest_count_nonzero(np.array([[0, 1, 7, 0], [3, 0, 2, 19]]),\n                   np.array([1, 1, 2, 1]),\n                   axis=0)\ntest_count_nonzero(np.array([[0, 1, 7, 0], [3, 0, 2, 19]]),\n                   np.array([2, 3]),\n                   axis=1)\ntest_count_nonzero(np.array([[0, 1, 7, 0], [3, 0, 2, 19]]),\n                   np.array([[2], [3]]),\n                   axis=1,\n                   keepdims=True)\n\n@test\ndef test_median(a,\n                expected,\n                axis=None,\n                out=None,\n                overwrite_input: bool = False,\n                keepdims: Literal[bool] = False):\n    if isinstance(expected, int) or isinstance(expected, float) or isinstance(\n            expected, complex):\n        if np.isnan(expected):\n            assert np.isnan(\n                np.median(a,\n                          axis=axis,\n                          out=out,\n                          overwrite_input=overwrite_input,\n                          keepdims=keepdims))\n        else:\n            assert np.median(a,\n                             axis=axis,\n                             out=out,\n                             overwrite_input=overwrite_input,\n                             keepdims=keepdims) == expected\n    else:\n        assert (np.median(a,\n                          axis=axis,\n                          out=out,\n                          overwrite_input=overwrite_input,\n                          keepdims=keepdims) == expected).all()\n\ntest_median(np.empty(0, int), np.nan)\ntest_median(np.array([[np.nan]]), np.nan)\na = np.arange(24, dtype=float)\na[2] = np.nan\ntest_median(a, np.nan, axis=0)\na1 = np.arange(24, dtype=float).reshape(2, 3, 4)\na1[1, 2, 3] = np.nan\na1[1, 1, 2] = np.nan\ntest_median(a1, np.nan)\ntest_median(np.array(1), 1.0)\ntest_median(np.array([0.0444502, 0.0463301, 0.141249, 0.0606775]),\n            0.053503800000000004)\ntest_median(np.arange(2), 0.5)\ntest_median(np.arange(6).reshape(2, 3), 2.5)\ntest_median([[1, 2, 3]], 2.0)\ntest_median([[1, 2, 3]], np.array([1., 2., 3.]), axis=0)\ntest_median(np.array([[2, 3], [0, 1], [6, 7], [4, 5]]),\n            np.array([3., 4.]),\n            axis=0)\ntest_median(np.array([[2, 3], [0, 1], [6, 7], [4, 5]]), 3.5)\ntest_median(np.array([[2, 3], [0, 1], [6, 7], [4, 5]]),\n            3.5,\n            overwrite_input=True)\ntest_median(np.array([[10, 7, 4], [3, 2, 1]]), 3.5)\ntest_median(np.array([[10, 7, 4], [3, 2, 1]]),\n            np.array([6.5, 4.5, 2.5]),\n            axis=0)\ntest_median(np.array([[10, 7, 4], [3, 2, 1]]), np.array([[7., 2.]]), axis=1)\ntest_median(np.array([[10, 7, 4], [3, 2, 1]]),\n            np.array([6.5, 4.5, 2.5]),\n            axis=0,\n            out=np.zeros((3, )))\ntest_median(np.array([[10, 7, 4], [3, 2, 1]]),\n            np.array([7., 2.]),\n            axis=1,\n            overwrite_input=True)\ntest_median(np.array([[10, 7, 4], [3, 2, 1]]),\n            np.array([[7.], [2.]]),\n            axis=1,\n            overwrite_input=True,\n            keepdims=True)\ntest_median(np.array([1.5, 2.8, 5.3, 7.1, 2.0]), 2.8)\ntest_median(\n    np.array([[1., 1., 1., 1.], [1., 1., 1., 1.], [1., np.nan, 1., 1.]]),\n    np.nan)\ntest_median(np.array([1 + 2j, 3 + 1j, 5 + 4j, 7 + 0j, 2 - 3j]), (3 + 1j))\ntest_median(np.array([[1.2, 3.4, 5.6], [7.8, 2.1, 4.3]]),\n            np.array([3.4, 4.3]),\n            axis=1)\ntest_median(np.array([[1 + 2j, 3 + 1j, 5 + 4j], [7 - 0j, 2 - 3j, 4 - 0j]]),\n            np.array([4. + 1.j, 2.5 - 1.j, 4.5 + 2.j]),\n            axis=0)\ntest_median(np.array([[[[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]],\n                       [[13, 14, 15, 16], [17, 18, 19, 20], [21, 22, 23, 24]],\n                       [[25, 26, 27, 28], [29, 30, 31, 32], [33, 34, 35, 36]]],\n                      [[[37, 38, 39, 40], [41, 42, 43, 44], [45, 46, 47, 48]],\n                       [[49, 50, 51, 52], [53, 54, 55, 56], [57, 58, 59, 60]],\n                       [[61, 62, 63, 64], [65, 66, 67, 68], [69, 70, 71,\n                                                             72]]]]),\n            np.array([[23., 24., 25., 26.], [35., 36., 37., 38.],\n                      [47., 48., 49., 50.]]),\n            axis=(0, 2))\n\n@test\ndef test_median_axis_keepdims_out():\n    d = np.ones((3, 5, 7, 11))\n    assert np.median(d, axis=None) == 1.0\n    axis = 1\n    shape_out = (3, 1, 7, 11)\n    out = np.ones(shape_out)\n    result = np.median(d, axis=axis, keepdims=True, out=out)\n    assert (result == out).all()\n    assert result.shape == shape_out\n\n    axis = (1, )\n    shape_out = (3, 1, 7, 11)\n    out = np.ones(shape_out)\n    result = np.median(d, axis=axis, keepdims=True, out=out)\n    assert (result == out).all()\n    assert result.shape == shape_out\n\n    axis = (0, 1)\n    shape_out = (1, 1, 7, 11)\n    out = np.ones(shape_out)\n    result = np.median(d, axis=axis, keepdims=True, out=out)\n    assert (result == out).all()\n    assert result.shape == shape_out\n\n    axis = (-3, -1)\n    shape_out = (3, 1, 7, 1)\n    out = np.ones(shape_out)\n    result = np.median(d, axis=axis, keepdims=True, out=out)\n    assert (result == out).all()\n    assert result.shape == shape_out\n\ntest_median_axis_keepdims_out()\n\n@test\ndef test_median_random():\n    o = np.random.normal(size=(71, 23))\n    x = np.dstack([o] * 10)\n    assert np.median(x) == np.median(o)\n    x = np.moveaxis(x, -1, 0)\n    assert np.median(x) == np.median(o)\n    assert np.median(x, axis=(0, 1, 2)) == np.median(x, axis=None)\n    assert (np.median(x, axis=(0, )) == np.median(x, axis=0)).all()\n    d = np.arange(3 * 5 * 7 * 11).reshape((3, 5, 7, 11))\n    assert np.median(d, axis=(0, 1, 2))[0] == np.median(d[:, :, :,\n                                                          0].flatten())\n    assert np.median(d, axis=(0, 1, 3))[1] == np.median(d[:, :,\n                                                          1, :].flatten())\n    assert np.median(d, axis=(3, 1, -4))[2] == np.median(d[:, :,\n                                                           2, :].flatten())\n    assert np.median(d,\n                     axis=(3, 1, 2))[2] == np.median(d[2, :, :, :].flatten())\n    assert np.median(d, axis=(3, 2))[2, 1] == np.median(d[2,\n                                                          1, :, :].flatten())\n    assert np.median(d, axis=(1, -2))[2, 1] == np.median(d[2, :, :,\n                                                           1].flatten())\n    assert np.median(d, axis=(1, 3))[2, 2] == np.median(d[2, :,\n                                                          2, :].flatten())\n\n    d = np.ones((3, 5, 7, 11))\n    assert np.median(d, axis=None, keepdims=True).shape == (1, 1, 1, 1)\n    assert np.median(d, axis=(0, 1), keepdims=True).shape == (1, 1, 7, 11)\n    assert np.median(d, axis=(0, 3), keepdims=True).shape == (1, 5, 7, 1)\n    assert np.median(d, axis=(1, ), keepdims=True).shape == (3, 1, 7, 11)\n    assert np.median(d, axis=(0, 1, 2, 3), keepdims=True).shape == (1, 1, 1, 1)\n    assert np.median(d, axis=(0, 1, 3), keepdims=True).shape == (1, 1, 7, 1)\n\ntest_median_random()\n\n@test\ndef test_quantile(a,\n                  q,\n                  expected,\n                  axis=None,\n                  out=None,\n                  overwrite_input=False,\n                  method: str = \"linear\",\n                  keepdims: Literal[bool] = False,\n                  interpolation=None):\n    if isinstance(expected, float):\n        assert np.quantile(a,\n                           q,\n                           axis=axis,\n                           out=out,\n                           overwrite_input=overwrite_input,\n                           method=method,\n                           keepdims=keepdims,\n                           interpolation=interpolation) == expected\n    else:\n        assert (np.quantile(a,\n                            q,\n                            axis=axis,\n                            out=out,\n                            overwrite_input=overwrite_input,\n                            method=method,\n                            keepdims=keepdims,\n                            interpolation=interpolation) == expected).all()\n\ntest_quantile(np.array([1., 2., 3.]), [0.3], np.array([1.6]))\ntest_quantile(np.array([1, 2, 3]), [0.3], np.array([1.6]))\ntest_quantile(np.array([[10, 7, 4], [3, 2, 1]]), 0.3, 2.5)\ntest_quantile(np.array([[10, 7, 4], [3, 2, 1]]), [0.3], np.array([2.5]))\ntest_quantile(np.array([[10, 7, 4], [3, 2, 1]]),\n              0.3,\n              np.array([5.8, 1.6]),\n              axis=1)\ntest_quantile(np.array([[10, 7, 4], [3, 2, 1]]),\n              0.3,\n              np.array([[5.8], [1.6]]),\n              axis=1,\n              keepdims=True)\ntest_quantile(np.array([[10, 7, 4], [3, 2, 1]]),\n              0.3,\n              np.array([5.8, 1.6]),\n              axis=1,\n              out=np.array([0., 0.]))\ntest_quantile(np.array([[10, 7, 4], [3, 2, 1]]), [0.3, 0.5, 1],\n              np.array([2.5, 3.5, 10.]))\ntest_quantile(np.array([[10, 7, 4], [3, 2, 1]]), [0.3, 0.5, 1],\n              np.array([[[2.5]], [[3.5]], [[10.]]]),\n              keepdims=True)\ntest_quantile(np.array([[10, 7, 4], [3, 2, 1]]), [0.3, 0.5, 1],\n              np.array([[5.8, 1.6], [7., 2.], [10., 3.]]),\n              axis=1)\nx = np.arange(8) * 0.5\ntest_quantile(x, 0, 0.)\ntest_quantile(x, 1, 3.5)\n\n@test\ndef test_percentile(a,\n                    q,\n                    expected,\n                    axis=None,\n                    out=None,\n                    overwrite_input=False,\n                    method: str = \"linear\",\n                    keepdims: Literal[bool] = False,\n                    interpolation=None):\n    if isinstance(expected, float):\n        assert np.percentile(a,\n                             q,\n                             axis=axis,\n                             out=out,\n                             overwrite_input=overwrite_input,\n                             method=method,\n                             keepdims=keepdims,\n                             interpolation=interpolation) == expected\n    else:\n        assert (np.round(\n            np.percentile(a,\n                          q,\n                          axis=axis,\n                          out=out,\n                          overwrite_input=overwrite_input,\n                          method=method,\n                          keepdims=keepdims,\n                          interpolation=interpolation),\n            8) == np.round(expected, 8)).all()\n\ntest_percentile(np.array([1., 2., 3.]), [0.3], np.array([1.006]))\ntest_percentile(np.array([1, 2, 3]), [0.3], np.array([1.006]))\ntest_percentile(np.array([[10, 7, 4], [3, 2, 1]]), [3], 1.15)\ntest_percentile(np.array([[10, 7, 4], [3, 2, 1]]),\n                40,\n                np.array([6.4, 1.8]),\n                axis=1)\ntest_percentile(np.array([10, 7, 4, 3, 2, 1]), [40], np.array([3.]), axis=0)\ntest_percentile(np.array([[10, 7, 4], [3, 2, 1]]), [40],\n                np.array([[[6.4], [1.8]]]),\n                axis=1,\n                keepdims=True)\ntest_percentile(np.array([[10, 7, 4], [3, 2, 1]]),\n                78.25,\n                np.array([8.695, 2.565]),\n                axis=1,\n                out=np.array([0., 0.]))\ntest_percentile(np.array([[10, 7, 4], [3, 2, 1]]),\n                40,\n                np.array([6.4, 1.8]),\n                axis=1)\ntest_percentile(np.array([[10, 7, 4], [3, 2, 1]]), [42.11, 74.21, 1.2, 55.98],\n                np.array([3.1055, 6.1315, 1.06, 3.799]))\ntest_percentile(np.array([[10, 7, 4], [3, 2, 1]]),\n                84.14,\n                np.array([[8.8898, 6.207, 3.5242]]),\n                axis=0,\n                keepdims=True)\ntest_percentile(np.array([[10, 7, 4], [3, 2, 1]]), [42.11, 74.21, 1.2, 55.98],\n                np.array([[[3.1055]], [[6.1315]], [[1.06]], [[3.799]]]),\n                keepdims=True)\ntest_percentile(np.array([[10, 7, 4], [3, 2, 1]]), [42.11, 74.21, 1.2, 55.98],\n                np.array([[6.5266, 1.8422], [8.4526, 2.4842], [4.072, 1.024],\n                          [7.3588, 2.1196]]),\n                axis=1)\ntest_percentile(np.array([[10, 7, 4], [3, 2, 1]]), [40],\n                np.array([[5.8, 4., 2.2]]),\n                axis=0)\ntest_percentile(np.array([[10, 7, 4], [3, 2, 1]]),\n                84.14,\n                np.array([[7.621]]),\n                keepdims=True)\nx = np.arange(8) * 0.5\ntest_percentile(x, 100, 3.5)\ntest_percentile(x, 0, 0.)\ntest_percentile(x, 50, 1.75)\nxx = np.array([[1, 1, 1], [1, 1, 1], [4, 4, 3], [1, 1, 1], [1, 1, 1]])\ntest_percentile(xx, 50, np.array([1, 1, 1]), axis=0)\narr = np.array([15.0, 35.0, 40.0, 50.0])\ntest_percentile(arr, 40, 35.0, method=\"inverted_cdf\")\ntest_percentile(arr, 40, 35.0, method=\"averaged_inverted_cdf\")\ntest_percentile(arr, 40, 35.0, method=\"closest_observation\")\ntest_percentile(arr, 40, 27.0, method=\"interpolated_inverted_cdf\")\ntest_percentile(arr, 40, 35.5, method=\"hazen\")\ntest_percentile(arr, 40, 35.0, method=\"weibull\")\ntest_percentile(arr, 40, 36.0, method=\"linear\")\ntest_percentile(arr, 40, 35.333333333333336, method=\"median_unbiased\")\ntest_percentile(arr, 40, 35.375, method=\"normal_unbiased\")\ntest_percentile(np.arange(10), 50, 4., method='lower')\ntest_percentile(np.arange(10), 50, 5., method='higher')\ntest_percentile(np.arange(10), 51, 4.5, method='midpoint')\ntest_percentile(np.arange(9) + 1, 50, 5., method='midpoint')\ntest_percentile(np.arange(11), 51, 5.5, method='midpoint')\ntest_percentile(np.arange(11), 50, 5., method='midpoint')\ntest_percentile(np.arange(10), 51, 5., method='nearest')\ntest_percentile(np.arange(10), 49, 4., method='nearest')\ntest_percentile(x, [0, 100, 50], np.array([0, 3.5, 1.75]))\nax = np.arange(12).reshape(3, 4)\ntest_percentile(ax, (25, 50, 100), [2.75, 5.5, 11.0])\ntest_percentile(ax, (25, 50, 100),\n                [[2, 3, 4, 5], [4, 5, 6, 7], [8, 9, 10, 11]],\n                axis=0)\ntest_percentile(ax, (25, 50, 100),\n                np.array([[0.75, 1.5, 3], [4.75, 5.5, 7], [8.75, 9.5, 11]]).T,\n                axis=1)\ntest_percentile(np.arange(12).reshape(3, 4), (25, 50),\n                np.array([[0., 1., 2., 3.], [4., 5., 6., 7.]]),\n                method='lower',\n                axis=0,\n                out=np.empty((2, 4)))\ntest_percentile(np.arange(12).reshape(3, 4), (25, 50),\n                np.array([[0.75, 4.75, 8.75], [1.5, 5.5, 9.5]]),\n                axis=1)\nd = np.arange(3 * 5 * 7 * 11).reshape((3, 5, 7, 11))\ntest_percentile(d,\n                25,\n                np.array([\n                    286., 287., 288., 289., 290., 291., 292., 293., 294., 295.,\n                    296.\n                ]),\n                axis=(0, 1, 2))\ntest_percentile(d,\n                25,\n                np.array([239., 250., 261., 272., 283., 294., 305.]),\n                axis=(3, 1, -4))\n\n@test\ndef test_nan_percentile():\n    x = np.arange(8) * 0.5\n    x[1] = np.nan\n    assert np.isnan(np.percentile(x, 0))\n    assert np.isnan(np.percentile(x, 0, method='nearest'))\n    assert np.isnan(np.percentile(x, 0, method='inverted_cdf'))\n\ntest_nan_percentile()\n\n@test\ndef test_percentile_shape():\n\n    x = np.arange(4 * 5 * 6).reshape(4, 5, 6)\n    assert np.percentile(x, (25, 50)).shape == (2, )\n    assert np.percentile(x, (25, 50, 75)).shape == (3, )\n\n    assert np.percentile(x, (25, 50), axis=0).shape == (2, 5, 6)\n    assert np.percentile(x, (25, 50), axis=1).shape == (2, 4, 6)\n    a = np.array([2, 3, 4, 1])\n    np.percentile(a, [50], overwrite_input=False)\n    assert (a == np.array([2, 3, 4, 1])).all()\n    b = np.percentile(a, [50], overwrite_input=True)\n    assert (b == np.array([2.5]))\n    d = np.ones((3, 5, 7, 11))\n    assert np.percentile(d, 7, axis=(0, 1),\n                         keepdims=True).shape == (1, 1, 7, 11)\n    assert np.percentile(d, 7, axis=(0, 3),\n                         keepdims=True).shape == (1, 5, 7, 1)\n    assert np.percentile(d, 7, axis=(1, ),\n                         keepdims=True).shape == (3, 1, 7, 11)\n    assert np.percentile(d, 7, axis=(0, 1, 3),\n                         keepdims=True).shape == (1, 1, 7, 1)\n    assert np.percentile(d, 7, axis=None, keepdims=True).shape == (1, 1, 1, 1)\n    assert np.percentile(d, 7, axis=(0, 1, 2, 3),\n                         keepdims=True).shape == (1, 1, 1, 1)\n    assert np.percentile(d, 50, axis=1).shape == (3, 7, 11)\n    assert np.percentile(d, 50, axis=3).shape == (3, 5, 7)\n    assert np.percentile(d, 50, axis=-1).shape == (3, 5, 7)\n    assert np.percentile(d, 50, axis=-3).shape == (3, 7, 11)\n\n    assert (np.percentile(x, [25, 60],\n                          axis=(0, 1, 2)) == np.percentile(x, [25, 60],\n                                                           axis=None)).all()\n    assert (np.percentile(x, [25, 60],\n                          axis=(0, )) == np.percentile(x, [25, 60],\n                                                       axis=0)).all()\n\ntest_percentile_shape()\n\n@test\ndef test_nanpercentile(a,\n                       q,\n                       expected,\n                       axis=None,\n                       out=None,\n                       overwrite_input=False,\n                       method: str = \"linear\",\n                       keepdims: Literal[bool] = False,\n                       interpolation=None):\n    if isinstance(expected, float):\n        assert np.nanpercentile(a,\n                                q,\n                                axis=axis,\n                                out=out,\n                                overwrite_input=overwrite_input,\n                                method=method,\n                                keepdims=keepdims,\n                                interpolation=interpolation) == expected\n    else:\n        assert (np.round(\n            np.nanpercentile(a,\n                             q,\n                             axis=axis,\n                             out=out,\n                             overwrite_input=overwrite_input,\n                             method=method,\n                             keepdims=keepdims,\n                             interpolation=interpolation),\n            8) == np.round(expected, 8)).all()\n\ntest_nanpercentile(np.array([1., 2., np.nan, 3.]), [0.3], np.array([1.006]))\n\ntest_nanpercentile(np.array([1, 2, np.nan, 3]), [0.3], np.array([1.006]))\n\ntest_nanpercentile(np.array([[np.nan, 10, 7, 4], [3, 2, np.nan, 1]]), [3],\n                   1.15)\ntest_nanpercentile(np.array([[10, np.nan, 7, 4], [3, np.nan, 2, 1]]),\n                   40,\n                   np.array([6.4, 1.8]),\n                   axis=1)\ntest_nanpercentile(np.array([10, 7, 4, 3, np.nan, 2, 1]), [40],\n                   np.array([3.]),\n                   axis=0)\ntest_nanpercentile(np.array([[10, 7, 4, np.nan], [np.nan, 3, 2, 1]]), [40],\n                   np.array([[[6.4], [1.8]]]),\n                   axis=1,\n                   keepdims=True)\ntest_nanpercentile(np.array([[10, 7, np.nan, 4], [3, np.nan, 2, 1]]),\n                   78.25,\n                   np.array([8.695, 2.565]),\n                   axis=1,\n                   out=np.array([0., 0.]))\nx = np.arange(8, dtype=float) * 0.5\nx[1] = np.nan\ntest_nanpercentile(x, 100, 3.5)\ntest_nanpercentile(x, 0, 0.)\ntest_nanpercentile(x, 50, 2.)\n\ntest_nanpercentile(x, [0, 100, 50], np.array([0, 3.5, 2.]))\nax = np.arange(12, dtype=float).reshape(3, 4)\nax[2][2] = np.nan\ntest_nanpercentile(ax, (25, 50, 100), [2.5, 5., 11.0])\ntest_nanpercentile(ax, (25, 50, 100),\n                   np.array([[2., 3., 3., 5.], [4., 5., 4., 7.],\n                             [8., 9., 6., 11.]]),\n                   axis=0)\n\n# Imported tests\n@test\ndef tests_sum():\n    test_sum([[1, 2, 3], [4, 5, 6], [7, 8, 9]], [[6], [15], [24]],\n             axis=1,\n             keepdims=True)\n    x = np.array([[1, 2], [4, 3]])\n    assert (x.sum() == 10)\n    assert np.array_equal(x.sum(axis=1), np.array([3, 7]))\n    assert np.array_equal(x.sum(axis=1, dtype=float), np.array([3., 7.]))\n    assert (np.sum([np.empty(0, float)]) == 0)\n    assert (np.sum([0.5, 1.5]) == 2.0)\n    assert (np.isclose(np.sum([0.5, 0.7, 0.2, 1.5], dtype=np.int32), 1))\n    assert (np.sum([[0, 1], [0, 5]]) == 6)\n    assert (np.array_equal(np.sum([[0, 1], [0, 5]], axis=0), np.array([0, 6])))\n    assert (np.array_equal(np.sum([[0, 1], [0, 5]], axis=1), np.array([1, 5])))\n    assert (np.isclose(np.ones(128, dtype=np.int8).sum(dtype=np.int8), -128))\n    assert (np.isclose((np.array([1, 2e-9, 3e-9] * 1000000)).sum(), 1e+06))\n    assert (np.sum([10], initial=5) == 15)\n    assert (np.array_equal(\n        np.sum(((0, 1), (np.nan, 5)), where=[False, True], axis=1),\n        np.array([1, 5])))\n\ntests_sum()\n\n@test\ndef tests_prod():\n    test_prod([[1, 2, 3, 4], [5, 6, 7, 9], [10, 3, 4, 5]], [24, 1890, 600],\n              axis=-1)\n    x = np.arange(12).reshape((3, 4))\n    assert (x.prod() == 0)\n    assert np.array_equal(x.prod(0), [0, 45, 120, 231])\n    assert np.array_equal(x.prod(1), [0, 840, 7920])\n    assert (np.prod(np.array([536870910, 536870910, 536870910,\n                              536870910])) == 6917529010461212688)\n    assert (np.prod([1.0, 2.0]) == 2.0)\n    assert (np.prod(np.array([[1., 2.], [3., 4.]])) == 24.0)\n    assert (np.array_equal(np.prod(np.array([[1., 2.], [3., 4.]]), axis=1),\n                           np.array([2., 12.])))\n    assert (np.array_equal(np.prod(np.array([[1., 2.], [3., 4.]]), axis=0),\n                           np.array([3., 8.])))\n    assert (np.prod((1., np.nan, 3.), where=[True, False, True]) == 3.0)\n    assert (np.prod(np.array([1, 2, 3],\n                             dtype=np.uint8)).__class__.__name__ == 'UInt[8]')\n    assert (np.prod(np.array([1, 2, 3],\n                             dtype=np.int8)).__class__.__name__ == 'Int[8]')\n    assert (np.prod([1, 2], initial=5) == 10)\n\ntests_prod()\n\n@test\ndef tests_mean():\n    test_mean([[1, 2, 3], [4, 5, 6]], 3.5)\n    assert (np.all(\n        np.mean([[1, 2, 3], [4, 5, 6]], 0) == np.array([2.5, 3.5, 4.5])))\n    assert (np.all(np.mean([[1, 2, 3], [4, 5, 6]], 1) == np.array([2., 5.])))\n    assert (np.isnan(np.mean([np.empty(0, float)])))\n\n    x = np.arange(12).reshape((3, 4))\n    assert (x.mean() == 5.5)\n    assert np.array_equal(x.mean(0), [4., 5., 6., 7.])\n    assert np.array_equal(x.mean(1), [1.5, 5.5, 9.5])\n\n    rnd.seed(1234)\n    A = rnd.rand(10, 20, 5) + 0.5\n    mean_out = np.zeros((10, 1, 5))\n    mean = np.mean(A, out=mean_out, axis=1, keepdims=True)\n    assert np.array_equal(mean_out, mean)\n\n    d = np.arange(10)\n    out = np.array(0.)\n    r = np.mean(d, out=out)\n    assert np.array_equal(r, out)\n    a = np.zeros((2, 512 * 512), dtype=np.float32)\n    a[0, :] = 1.0\n    a[1, :] = 0.1\n    assert (np.isclose(np.mean(a), 0.55))\n    assert (np.isclose(np.mean(a, dtype=np.float), 0.55))\n    a = np.array([[5, 9, 13], [14, 10, 12], [11, 15, 19]])\n    assert (np.mean(a) == 12.0)\n    assert (np.mean(a, where=[[True], [False], [False]]) == 9)\n\ntests_mean()\n\n@test\ndef tests_var():\n    test_var([[1, 2, 3], [4, 5, 6]], 2.9166666666666665)\n    test_var([1j, 0j], 0.25)\n    test_var(np.array([1, -1, 1, -1]), 1)\n    test_var(1, 0)\n    test_var(np.array([1.j, 1.j, -1.j, -1.j]), 1)\n    test_var(1j, 0)\n\n    assert np.array_equal(np.var([[1, 2, 3], [4, 5, 6]], 0),\n                          np.array([2.25, 2.25, 2.25]))\n    assert (np.isclose(np.var([[1, 2, 3], [4, 5, 6]], 1),\n                       np.array([0.666667, 0.666667])).all())\n    assert (np.isnan(np.var([np.empty(0, float)])))\n\n    x = np.arange(12).reshape((3, 4))\n    assert np.isclose(x.var(), 11.916666666666666)\n    assert np.isclose(\n        x.var(0), [10.66666667, 10.66666667, 10.66666667, 10.66666667]).all()\n    assert np.array_equal(x.var(1), [1.25, 1.25, 1.25])\n\n    rnd.seed(1234)\n    A = rnd.rand(10, 20, 5) + 0.5\n    var_out = np.zeros((10, 1, 5))\n    var = np.var(A, out=var_out, axis=1, keepdims=True)\n    assert np.array_equal(var_out, var)\n    assert var.shape == (10, 1, 5)\n\n    var_old = np.var(A, axis=1, keepdims=True)\n    assert np.array_equal(var, var_old)\n\n    var = np.var(A, axis=1, keepdims=False)\n    assert var.shape == (10, 5)\n\n    where = A > 0.5\n    var = np.var(A, axis=1, keepdims=False, where=where)\n    assert var.shape == (10, 5)\n\n    var_old = np.var(A, axis=1, where=where)\n    assert np.array_equal(var, var_old)\n\n    A = np.array([1, -1, 1, -1])\n    assert (np.array_equal(np.var(A, ddof=1), (1 * len(A) / (len(A) - 1))))\n    assert (np.array_equal(np.var(A, ddof=2), (1 * len(A) / (len(A) - 2))))\n\ntests_var()\n\n@test\ndef tests_std():\n    test_std([[1, 2, 3], [4, 5, 6]], 1.707825127659933)\n\n    rnd.seed(1234)\n    A = rnd.rand(10, 20, 5) + 0.5\n    std_out = np.zeros((10, 1, 5))\n    std = np.std(A, out=std_out, axis=1, keepdims=True)\n    assert (np.array_equal(std_out, std))\n\n    A = [[1, 2, 3], [4, 5, 6]]\n\n    assert (np.isclose(np.std(A, 0), np.array([1.5, 1.5, 1.5])).all())\n    assert (np.isclose(np.std(A, 1), np.array([0.81649658, 0.81649658])).all())\n    assert (np.isnan(np.std([np.empty(0, float)])))\n\n    rnd.seed(1234)\n    A = rnd.rand(10, 20, 5) + 0.5\n    std_out = np.zeros((10, 1, 5))\n    std = np.std(A, out=std_out, axis=1, keepdims=True)\n    assert (np.array_equal(std_out, std))\n    assert std.shape == (10, 1, 5)\n\n    A = np.array([1, -1, 1, -1])\n    assert (np.std(A)**2 == 1)\n    assert (np.std(1) == 0)\n    assert (np.array_equal(np.std(A, ddof=1)**2, (1 * len(A) / (len(A) - 1))))\n    assert (np.isclose(np.std(A, ddof=2)**2, (1 * len(A) / (len(A) - 2))))\n\n    d = np.arange(10)\n    out = np.array(0.)\n    r = np.std(d, out=out)\n    assert (np.isclose(r, out))\n    assert (np.isclose(np.std(1j), 0))\n\n    x = np.arange(12).reshape((3, 4))\n    assert np.isclose(x.std(), 3.4520525295346629)\n    assert (np.isclose(x.std(0), np.array([3.26599, 3.26599, 3.26599,\n                                           3.26599])).all())\n    assert (np.isclose(x.std(1), np.array([1.11803, 1.11803, 1.11803])).all())\n\ntests_std()\n\n@test\ndef tests_min():\n    x = -1 * np.arange(12).reshape((3, 4))\n    assert (x.min() == -11)\n    assert np.array_equal(x.min(0), np.array([-8, -9, -10, -11]))\n    assert np.array_equal(x.min(1), np.array([-3, -7, -11]))\n\ntests_min()\n\n@test\ndef tests_max():\n    x = np.arange(12).reshape((3, 4))\n    assert (x.max() == 11)\n    assert np.array_equal(x.max(0), np.array([8, 9, 10, 11]))\n    assert np.array_equal(x.max(1), np.array([3, 7, 11]))\n\ntests_max()\n\n@test\ndef tests_ptp():\n    a = [3, 4, 5, 10, -3, -5, 6.0]\n    test_ptp(a, 15.0, axis=0)\n\n    x = np.arange(12).reshape((3, 4))\n    assert (x.ptp() == 11)\n    assert (np.array_equal(x.ptp(0), np.array([8, 8, 8, 8])))\n    assert (np.array_equal(x.ptp(1), np.array([3, 3, 3])))\n\ntests_ptp()\n\n@test\ndef tests_argmin():\n    x = -1 * np.arange(12).reshape((3, 4))\n    assert (x.argmin() == 11)\n    assert (np.array_equal(x.argmin(0), np.array([2, 2, 2, 2])))\n    assert (np.array_equal(x.argmin(1), np.array([3, 3, 3])))\n\ntests_argmin()\n\n@test\ndef tests_argmax():\n    x = np.arange(12).reshape((3, 4))\n    assert (x.argmax() == 11)\n    assert (np.array_equal(x.argmax(0), np.array([2, 2, 2, 2])))\n    assert (np.array_equal(x.argmax(1), np.array([3, 3, 3])))\n\ntests_argmax()\n\n@test\ndef tests_any_all():\n    t = np.array([True] * 41, dtype=bool)[1::]\n    f = np.array([False] * 41, dtype=bool)[1::]\n    o = np.array([False] * 42, dtype=bool)[2::]\n    nm = f.copy()\n    im = t.copy()\n    nm[3] = True\n    nm[-2] = True\n    im[3] = False\n    im[-2] = False\n\n    assert (t.all())\n    assert (t.any())\n    assert (not f.all())\n    assert (not f.any())\n    assert (nm.any())\n    assert (im.any())\n    assert (not nm.all())\n    assert (not im.all())\n\n    for i in range(256 - 7):\n\n        d = np.array([False] * 256, dtype=bool)[7::]\n        d[i] = True\n        assert (np.any(d))\n        e = np.array([True] * 256, dtype=bool)[7::]\n        e[i] = False\n        assert (not np.all(e))\n        assert np.array_equal(e, ~d)\n\n    for i in list(range(9, 6000, 507)) + [7764, 90021, -10]:\n        d = np.array([False] * 100043, dtype=bool)\n        d[i] = True\n        assert (np.any(d))\n        e = np.array([True] * 100043, dtype=bool)\n        e[i] = False\n        assert (not np.all(e))\n\n    x = np.arange(12).reshape((3, 4))\n    y = x[0]\n\n    assert ((x == y).all() == False)\n    assert np.array_equal((x == y).all(0),\n                          np.array([False, False, False, False]))\n    assert np.array_equal((x == y).all(1), np.array([True, False, False]))\n\ntests_any_all()\n\n@test\ndef test_sum_where():\n    # More extensive tests done in test_reduction_with_where.\n    assert np.sum([[1., 2.], [3., 4.]], where=[True, False]) == 4.\n    assert np.array_equal(\n        np.sum([[1., 2.], [3., 4.]], axis=0, initial=5., where=[True, False]),\n        [9., 5.])\n\ntest_sum_where()\n\n@test\ndef test_empty_min_max():\n    assert np.empty(0).min(initial=42) == 42.0\n    assert np.empty(0).max(initial=42) == 42.0\n    try:\n        np.empty(0).min()\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.empty(0).max()\n        assert False\n    except ValueError:\n        pass\n\n    assert np.empty((1, 2, 0, 2, 1)).min(initial=42) == 42.0\n    assert np.empty((1, 2, 0, 2, 1)).max(initial=42) == 42.0\n    try:\n        np.empty((1, 2, 0, 2, 1)).min()\n        assert False\n    except ValueError:\n        pass\n    try:\n        np.empty((1, 2, 0, 2, 1)).max()\n        assert False\n    except ValueError:\n        pass\n\n    assert np.asarray(42).min() == 42\n    assert np.asarray(42).max() == 42\n    assert np.asarray(42).min(initial=25) == 25\n    assert np.asarray(42).max(initial=25) == 42\n    assert np.asarray(42).min(initial=99) == 42\n    assert np.asarray(42).max(initial=99) == 99\n\ntest_empty_min_max()\n"
  },
  {
    "path": "test/numpy/test_routines.codon",
    "content": "import numpy as np\nfrom numpy import *\n\n############\n# creation #\n############\n\n@test\ndef test_empty(shape, expected_shape):\n    assert np.empty(shape).shape == expected_shape\n\ntest_empty((2, 2), (2, 2))\ntest_empty(2, (2, ))\n\n@test\ndef test_empty_like(prototype, expected_shape):\n    assert np.empty_like(prototype).shape == expected_shape\n\ntest_empty_like(np.array([[1, 2, 3], [4, 5, 6]]), (2, 3))\ntest_empty_like(np.array([[1, 2, 3], [4, 5, 6]])[::2], (1, 3))\n\n@test\ndef test_eye(N, expected, M: Optional[int] = None):\n    assert (np.eye(N, M) == expected).all()\n\ntest_eye(2, np.array([[1., 0., 0.], [0., 1., 0.]]), 3)\ntest_eye(3, np.array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]))\n\n@test\ndef test_identity(N, expected):\n    assert (np.identity(N) == expected).all()\n\ntest_identity(2, np.array([[1., 0.], [0., 1.]]))\ntest_identity(3, np.array([[1., 0., 0.], [0., 1., 0.], [0., 0., 1.]]))\n\n@test\ndef test_ones(shape, expected):\n    assert (np.ones(shape) == expected).all()\n\ntest_ones(5, np.array([1., 1., 1., 1., 1.]))\ntest_ones((2, 1), np.array([[1.], [1.]]))\n\n@test\ndef test_ones_like(a, expected):\n    assert (np.ones_like(a) == expected).all()\n\ntest_ones_like(np.array([[0, 1, 2], [3, 4, 5]]),\n               np.array([[1, 1, 1], [1, 1, 1]]))\ntest_ones_like(np.array([0, 1, 2, 3, 4, 5])[::2], np.array([1, 1, 1]))\n\n@test\ndef test_zeros(shape, expected):\n    assert (np.zeros(shape) == expected).all()\n\ntest_zeros(5, np.array([0., 0., 0., 0., 0.]))\ntest_zeros((2, 1), np.array([[0.], [0.]]))\n\n@test\ndef test_zeros_like(a, expected):\n    assert (np.zeros_like(a) == expected).all()\n\ntest_zeros_like(np.array([[0, 1, 2], [3, 4, 5]]),\n                np.array([[0, 0, 0], [0, 0, 0]]))\ntest_zeros_like(np.array([0, 1, 2, 3, 4, 5])[::2], np.array([0, 0, 0]))\n\n@test\ndef test_full(shape, fill_val, expected):\n    assert (np.full(shape, fill_val) == expected).all()\n\ntest_full(5, 5, np.array([5, 5, 5, 5, 5]))\ntest_full((2, 1), np.inf, np.array([[inf], [inf]]))\ntest_full((2, 2), [1, 2], np.array([[1, 2], [1, 2]]))\n\n@test\ndef test_full_like(a, fill_val, expected):\n    assert (np.full_like(a, fill_val) == expected).all()\n\ntest_full_like(np.array([[0, 1, 2], [3, 4, 5]]), 0.1,\n               np.array([[0.1, 0.1, 0.1], [0.1, 0.1, 0.1]]))\ntest_full_like(np.array([0, 1, 2, 3, 4, 5])[::2], 10, np.array([10, 10, 10]))\n\n@test\ndef test_array(a,\n               expected,\n               dtype: type = NoneType,\n               copy: bool = True,\n               order: str = 'K',\n               ndmin: Literal[int] = 0):\n    assert (np.array(a, dtype, copy, order, ndmin) == expected).all()\n\ntest_array(np.array([1, 2, 3.0]), np.array([1., 2., 3.]))\ntest_array([[1, 2], [3, 4]], np.array([[1, 2], [3, 4]]))\ntest_array([1, 2, 3], np.array([1. + 0.j, 2. + 0.j, 3. + 0.j]), dtype=complex)\ntest_array([1, 2, 3], np.array([[1, 2, 3]]), ndmin=2)\n\n@test\ndef test_copy(a):\n    x = a.copy()\n    assert (x == a).all()\n\ntest_copy(np.array([1, 2, 3]))\n\n@test\ndef test_frombuffer(buffer,\n                    expected,\n                    dtype: type = float,\n                    count: int = -1,\n                    offset: int = 0):\n    print(np.frombuffer(buffer, dtype, count, offset))\n    assert (np.frombuffer(buffer, dtype, count, offset) == expected).all()\n\n#test_frombuffer('hello world', array(['w', 'o', 'r', 'l', 'd'], dtype='|S1'), dtype='S1', count=5, offset=6)\n\n@test\ndef test_fromfunction(function, shape, expected, dtype: type = float):\n    assert (np.fromfunction(function, shape, dtype) == expected).all()\n\ntest_fromfunction(lambda i, j: i, (2, 2),\n                  np.array([[0., 0.], [1., 1.]]),\n                  dtype=float)\ntest_fromfunction(lambda i, j: i + j, (3, 3),\n                  np.array([[0, 1, 2], [1, 2, 3], [2, 3, 4]]),\n                  dtype=int)\n\n@test\ndef test_fromiter(iter, expected, dtype: type = float, count: int = -1):\n    assert (np.fromiter(iter, dtype, count) == expected).all()\n\ntest_fromiter((x * x for x in range(5)), np.array([0., 1., 4., 9., 16.]))\n\n@test\ndef test_arange(expected, start, stop, step, dtype: type = float):\n    assert (np.arange(start, stop, step) == expected).all()\n\ntest_arange(np.array([3, 5]), 3, 7, 2)\n\n@test\ndef test_linspace(start,\n                  stop,\n                  expected,\n                  num: int = 50,\n                  endpoint: bool = True,\n                  retstep: Literal[bool] = False,\n                  dtype: type = float):\n    assert (np.linspace(start, stop, num, endpoint, retstep,\n                        dtype) == expected).all()\n\ntest_linspace(2.0, 3.0, np.array([2., 2.25, 2.5, 2.75, 3.]), num=5)\ntest_linspace(2.0,\n              3.0,\n              np.array([2., 2.2, 2.4, 2.6, 2.8]),\n              num=5,\n              endpoint=False)\n\n@test\ndef test_logspace(start,\n                  stop,\n                  expected,\n                  num: int = 50,\n                  endpoint: bool = True,\n                  base: float = 10.0,\n                  retstep: Literal[bool] = False,\n                  dtype: type = float):\n    assert (round(\n        np.logspace(start, stop, num, endpoint, base, retstep, dtype),\n        8) == round(expected, 8)).all()\n\ntest_logspace(2.0,\n              3.0,\n              np.array([100., 215.44346900, 464.15888336, 1000.]),\n              num=4)\ntest_logspace(2.0,\n              3.0,\n              np.array([100., 177.82794100, 316.22776602, 562.34132519]),\n              num=4,\n              endpoint=False)\ntest_logspace(2.0,\n              3.0,\n              np.array([4., 5.03968420, 6.34960421, 8.]),\n              num=4,\n              base=2)\n\n@test\ndef test_geomspace(start,\n                   stop,\n                   expected,\n                   num: int = 50,\n                   endpoint: bool = True):\n    assert (np.geomspace(start, stop, num, endpoint) == expected).all()\n\ntest_geomspace(1000, 1, np.array([1000., 100., 10., 1.]), num=4)\ntest_geomspace(-1000, -1, np.array([-1000., -100., -10., -1.]), num=4)\n#test_geomspace(1j, 1000j, np.array([0.+1.j, 0.+10.j, 0.+100.j, 0.+1000.j]), num=4)\n\n@test\ndef test_meshgrid(expected, *xi):\n    list1 = np.meshgrid(*xi)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_meshgrid([\n    np.array([[0., 0.5, 1.], [0., 0.5, 1.]]),\n    np.array([[0., 0., 0.], [1., 1., 1.]])\n]), np.array([0., 0.5, 1.]), np.array([0., 1.])\n\n@test\ndef test_diag(v, expected, k: int = 0):\n    assert (np.diag(v, k) == expected).all()\n\ntest_diag(np.arange(9).reshape((3, 3)), np.array([0, 4, 8]))\ntest_diag(np.arange(9).reshape((3, 3)), np.array([1, 5]), k=1)\ntest_diag(np.arange(9).reshape((3, 3)), np.array([3, 7]), k=-1)\n\n@test\ndef test_diagflat(v, expected, k: int = 0):\n    assert (np.diagflat(v, k) == expected).all()\n\ntest_diagflat(\n    np.array([[1, 2], [3, 4]]),\n    np.array([[1, 0, 0, 0], [0, 2, 0, 0], [0, 0, 3, 0], [0, 0, 0, 4]]))\ntest_diagflat([1, 2], np.array([[0, 1, 0], [0, 0, 2], [0, 0, 0]]), k=1)\n\n@test\ndef test_tri(N,\n             expected,\n             M: Optional[int] = None,\n             k: int = 0,\n             dtype: type = float):\n    assert (np.tri(N, M, k, dtype) == expected).all()\n\ntest_tri(3, np.array([[1, 1, 1, 0, 0], [1, 1, 1, 1, 0], [1, 1, 1, 1, 1]]), 5,\n         2, int)\ntest_tri(\n    3,\n    np.array([[0., 0., 0., 0., 0.], [1., 0., 0., 0., 0.], [1., 1., 0., 0.,\n                                                           0.]]), 5, -1)\n\n@test\ndef test_tril(m, expected, k: int = 0):\n    assert (np.tril(m, k) == expected).all()\n\ntest_tril([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]],\n          np.array([[0, 0, 0], [4, 0, 0], [7, 8, 0], [10, 11, 12]]), -1)\ntest_tril(\n    np.arange(1 * 2 * 3).reshape(1, 2, 3), np.array([[[0, 0, 0], [3, 4, 0]]]))\n\n@test\ndef test_triu(m, expected, k: int = 0):\n    assert (np.triu(m, k) == expected).all()\n\ntest_triu([[1, 2, 3], [4, 5, 6], [7, 8, 9], [10, 11, 12]],\n          np.array([[1, 2, 3], [4, 5, 6], [0, 8, 9], [0, 0, 12]]), -1)\ntest_triu(\n    np.arange(1 * 2 * 3).reshape(1, 2, 3), np.array([[[0, 1, 2], [0, 4, 5]]]))\n\n@test\ndef test_vander(x,\n                expected,\n                N: Optional[int] = None,\n                increasing: bool = False):\n    assert (np.vander(x, N, increasing) == expected).all()\n\ntest_vander(np.array([1, 2, 3, 5]),\n            np.array([[1, 1, 1], [4, 2, 1], [9, 3, 1], [25, 5, 1]]),\n            N=3)\ntest_vander(\n    np.array([1, 2, 3, 5]),\n    np.array([[1, 1, 1, 1], [8, 4, 2, 1], [27, 9, 3, 1], [125, 25, 5, 1]]))\ntest_vander(np.array([1, 2, 3, 5]),\n            np.array([[1, 1, 1, 1], [1, 2, 4, 8], [1, 3, 9, 27],\n                      [1, 5, 25, 125]]),\n            increasing=True)\n\n################\n# manipulation #\n################\n\n@test\ndef test_copyto(dst, src):\n    np.copyto(dst, src)\n    if len(dst) == len(src):\n        assert (dst == src).all()\n    else:\n        arr = np.array([src[0]] * len(dst))\n        assert (dst == arr).all()\n\ntest_copyto(np.array([4, 5, 6]), np.array([1, 2, 3]))\ntest_copyto(np.array([4, 5, 6]), np.array([4, 5, 6]))\ntest_copyto(np.array([1, 2]), np.array([1]))\ntest_copyto(np.array([[1, 2, 3], [4, 5, 6]]), [[4, 5, 6], [7, 8, 9]])\n\n@test\ndef test_shape(a, expected_shape):\n    assert np.shape(a) == expected_shape\n\ntest_shape(np.eye(3), (3, 3))\ntest_shape([[1, 3]], (1, 2))\ntest_shape([0], (1, ))\ntest_shape(0, ())\n\n@test\ndef test_reshape(a, newshape, expected):\n    assert (np.reshape(a, newshape) == expected).all()\n\ntest_reshape(np.arange(6), (3, 2), np.array([[0, 1], [2, 3], [4, 5]]))\ntest_reshape(np.array([[1, 2, 3], [4, 5, 6]]), 6, np.array([1, 2, 3, 4, 5, 6]))\ntest_reshape(np.arange(6), (3, -1), np.array([[0, 1], [2, 3], [4, 5]]))\n\n@test\ndef test_ravel(a, expected):\n    assert (np.ravel(a) == expected).all()\n\ntest_ravel(np.array([[1, 2, 3], [4, 5, 6]]), np.array([1, 2, 3, 4, 5, 6]))\ntest_ravel(np.arange(3)[::-1], np.array([2, 1, 0]))\n\n@test\ndef test_flat(a, index, expected):\n    assert a.flat[index] == expected\n\ntest_flat(np.array([[1, 2, 3], [4, 5, 6]]), 3, 4)\ntest_flat(np.array([[1, 4], [2, 5], [3, 6]]), 3, 5)\n\n@test\ndef test_flatten(a, expected):\n    assert (a.flatten() == expected).all()\n\ntest_flatten(np.array([[1, 2], [3, 4]]), np.array([1, 2, 3, 4]))\ntest_flatten(np.array([[1, 4], [2, 5], [3, 6]]), np.array([1, 4, 2, 5, 3, 6]))\n\n@test\ndef test_moveaxis(a, source, dest, expected_shape):\n    assert np.moveaxis(a, source, dest).shape == expected_shape\n\ntest_moveaxis(np.zeros((3, 4, 5)), 0, -1, (4, 5, 3))\ntest_moveaxis(np.zeros((3, 4, 5)), -1, 0, (5, 3, 4))\ntest_moveaxis(np.zeros((3, 4, 5)), [0, 1], [-1, -2], (5, 4, 3))\n\n@test\ndef test_swapaxes(a, axis1, axis2, expected_shape):\n    assert np.swapaxes(a, axis1, axis2).shape == expected_shape\n\ntest_swapaxes(np.array([[1, 2, 3]]), 0, 1, (3, 1))\ntest_swapaxes(np.array([[[0, 1], [2, 3]], [[4, 5], [6, 7]]]), 0, 2, (2, 2, 2))\n\n@test\ndef test_transpose(a, expected):\n    assert (a.T == expected).all()\n    assert (np.transpose(a) == expected).all()\n\ntest_transpose(np.array([[1, 2], [3, 4]]), np.array([[1, 3], [2, 4]]))\ntest_transpose(np.array([1, 2, 3, 4]), np.array([1, 2, 3, 4]))\n\n@test\ndef test_atleast_1d(arr, expected):\n    assert (np.atleast_1d(arr) == expected).all()\n\ntest_atleast_1d(1.0, np.array([1.]))\ntest_atleast_1d(\n    np.arange(9.0).reshape(3, 3),\n    np.array([[0., 1., 2.], [3., 4., 5.], [6., 7., 8.]]))\n\n@test\ndef test_atleast_2d(arr, expected):\n    assert (np.atleast_2d(arr) == expected).all()\n\ntest_atleast_2d(3.0, np.array([[3.]]))\ntest_atleast_2d(np.arange(3.0), np.array([[0., 1., 2.]]))\n\n@test\ndef test_atleast_3d(arr, expected_shape):\n    assert np.atleast_3d(arr).shape == expected_shape\n\ntest_atleast_3d(3.0, (1, 1, 1))\ntest_atleast_3d(np.arange(3.0), (1, 3, 1))\ntest_atleast_3d(np.arange(12.0).reshape(4, 3), (4, 3, 1))\n\n@test\ndef test_broadcast_to(x, dim):\n    assert np.broadcast_to(x, dim).shape == dim\n\ntest_broadcast_to(np.array([1, 2, 3]), (3, 3))\n\n@test\ndef test_broadcast_arrays(expected1, expected2, *args):\n    x = np.broadcast_arrays(*args)\n    assert (x[0] == expected1).all() and (x[1] == expected2).all()\n\ntest_broadcast_arrays(np.array([[1, 2, 3], [1, 2, 3]]),\n                      np.array([[4, 4, 4], [5, 5, 5]]), np.array([[1, 2, 3]]),\n                      np.array([[4], [5]]))\n\n@test\ndef test_expand_dims(a, axis, expected_shape):\n    assert np.expand_dims(a, axis).shape == expected_shape\n\ntest_expand_dims(np.array([1, 2]), 0, (1, 2))\ntest_expand_dims(np.array([1, 2]), 1, (2, 1))\ntest_expand_dims(np.array([1, 2]), (0, 1), (1, 1, 2))\n\n@test\ndef test_allclose(a,\n                  b,\n                  expected,\n                  rtol: float = 1e-05,\n                  atol: float = 1e-08,\n                  equal_nan: bool = False):\n    assert np.allclose(a, b, rtol=rtol, atol=atol,\n                       equal_nan=equal_nan) == expected\n\ntest_allclose([1e10, 1e-7], [1.00001e10, 1e-8], False)\ntest_allclose([1e10, 1e-8], [1.00001e10, 1e-9], True)\ntest_allclose([1e10, 1e-8], [1.0001e10, 1e-9], False)\ntest_allclose([1.0, np.nan], [1.0, np.nan], False)\ntest_allclose([1.0, np.nan], [1.0, np.nan], True, equal_nan=True)\n\n@test\ndef test_isclose(a,\n                 b,\n                 expected,\n                 rtol: float = 1e-05,\n                 atol: float = 1e-08,\n                 equal_nan: bool = False):\n    assert (np.isclose(a, b, rtol=rtol, atol=atol,\n                       equal_nan=equal_nan) == expected).all()\n\ntest_isclose([1e10, 1e-7], [1.00001e10, 1e-8], np.array([True, False]))\ntest_isclose([1e10, 1e-8], [1.00001e10, 1e-9], np.array([True, True]))\ntest_isclose([1e10, 1e-8], [1.0001e10, 1e-9], np.array([False, True]))\ntest_isclose([1.0, np.nan], [1.0, np.nan], np.array([True, False]))\ntest_isclose([1.0, np.nan], [1.0, np.nan],\n             np.array([True, True]),\n             equal_nan=True)\ntest_isclose([1e-8, 1e-7], [0.0, 0.0], np.array([True, False]))\ntest_isclose([1e-100, 1e-7], [0.0, 0.0], np.array([False, False]), atol=0.0)\ntest_isclose([1e-10, 1e-10], [1e-20, 0.0], np.array([True, True]))\ntest_isclose([1e-10, 1e-10], [1e-20, 0.999999e-10],\n             np.array([False, True]),\n             atol=0.0)\n\n@test\ndef test_array_equal():\n    assert np.array_equal([1, 2], [1, 2])\n    assert np.array_equal(np.array([1, 2]), np.array([1, 2]))\n    assert not np.array_equal([1, 2], [1, 2, 3])\n    assert not np.array_equal([1, 2], [1, 4])\n    a = np.array([1, np.nan])\n    assert not np.array_equal(a, a)\n    assert np.array_equal(a, a, equal_nan=True)\n    a = np.array([np.nan + 1j])\n    b = np.array([1 + np.nan * 1j])\n    assert np.array_equal(a, b, equal_nan=True)\n    assert np.array_equal([1, 2, 3], [1.0, 2.0, 3.0])\n\ntest_array_equal()\n\n@test\ndef test_array_equiv():\n    assert np.array_equiv([1, 2], [1, 2])\n    assert not np.array_equiv([1, 2], [1, 3])\n    assert np.array_equiv([1, 2], [[1, 2], [1, 2]])\n    assert not np.array_equiv([1, 2], [[1, 2, 1, 2], [1, 2, 1, 2]])\n    assert not np.array_equiv([1, 2], [[1, 2], [1, 3]])\n    assert np.array_equiv([[1, 1]], [[1], [1]])\n    assert not np.array_equiv([[1, 2]], [[1], [2]])\n    assert np.array_equiv([1, 2, 3], [1.0, 2.0, 3.0])\n\ntest_array_equiv()\n\n@test\ndef test_squeeze(a, axis, expected_shape):\n    assert np.squeeze(a, axis).shape == expected_shape\n\ntest_squeeze(np.array([[[0], [1], [2]]]), 0, (3, 1))\ntest_squeeze(np.array([[[0], [1], [2]]]), (0, 2), (3, ))\n\n@test\ndef test_asarray(a, expected, dtype: type = NoneType, order: str = 'K'):\n    assert (np.asarray(a, dtype=dtype, order=order) == expected).all()\n\ntest_asarray([1, 2], np.array([1, 2]))\ntest_asarray(np.array([1, 2]), np.array([1, 2]))\ntest_asarray([1, 2], np.array([1., 2.]), float)\ntest_asarray([[1, 2], [3, 4]], np.array([[1, 2], [3, 4]]), order='F')\n\n@test\ndef test_asanyarray(a, expected, dtype: type = NoneType, order: str = 'K'):\n    assert (np.asanyarray(a, dtype=dtype, order=order) == expected).all()\n\ntest_asanyarray([1, 2], np.array([1, 2]))\ntest_asanyarray([[1, 2], [3, 4]], np.array([[1, 2], [3, 4]]), order='F')\n\n@test\ndef test_asfarray(a, expected, dtype: type = NoneType):\n    assert (np.asfarray(a, dtype=dtype) == expected).all()\n\ntest_asfarray([2, 3], np.array([2., 3.]))\ntest_asfarray([2, 3], np.array([2., 3.]), float)\ntest_asfarray([2, 3], np.array([2., 3.]), int)\n\n@test\ndef test_asfortranarray(a, expected, dtype: type = NoneType):\n    assert (np.asfortranarray(a, dtype=dtype) == expected).all()\n\ntest_asfortranarray(np.ones((2, 3), order='F'), np.ones((2, 3), order='F'))\ntest_asfortranarray(np.ones((2, 3), order='C'), np.ones((2, 3), order='F'))\n\n@test\ndef test_ascontiguousarray(a, expected, dtype: type = NoneType):\n    assert (np.ascontiguousarray(a, dtype=dtype) == expected).all()\n\ntest_ascontiguousarray(np.ones((2, 3), order='F'), np.ones((2, 3), order='C'))\ntest_ascontiguousarray(np.ones((2, 3), order='C'), np.ones((2, 3), order='C'))\n\n@test\ndef test_asarray_chkfinite(a, expected, dtype: type = NoneType):\n    assert (np.asarray_chkfinite(a, dtype=dtype) == expected).all()\n\ntest_asarray_chkfinite([1, 2], np.array([1., 2.]), float)\n#test_asarray_chkfinite([1, 2, np.inf], error)\n\n@test\ndef test_require(a, expected, req=None, dt: type = NoneType):\n    assert (np.require(a, dtype=dt, requirements=req) == expected).all()\n\ntest_require(np.ones((2, 3), order='C'), np.ones((2, 3), order='C'))\ntest_require(\n    np.arange(6).reshape(2, 3), np.array([[0., 1., 2.], [3., 4., 5.]]),\n    ['A', 'O', 'W', 'F'], float)\n\n@test\ndef test_concatenate(arrays, expected, axis=0):\n    assert (np.concatenate((arrays), axis) == expected).all()\n\ntest_concatenate((np.array([[1, 2], [3, 4]]), np.array([[5, 6]])),\n                 np.array([[1, 2], [3, 4], [5, 6]]))\ntest_concatenate((np.array([[1, 2], [3, 4]]), np.array([[5, 6]])),\n                 np.array([1, 2, 3, 4, 5, 6]),\n                 axis=None)\ntest_concatenate((np.array([[1, 2], [3, 4]]), np.array([[5], [6]])),\n                 np.array([[1, 2, 5], [3, 4, 6]]), 1)\n\n# Test concatenation of integer arrays\n@test\ndef test_concatenate_int_arrays():\n    a = np.array([1, 2, 3])\n    b = np.array([4, 5, 6])\n\n    result = np.concatenate((a, b))\n    expected = np.array([1, 2, 3, 4, 5, 6])\n    assert np.array_equal(result, expected)\n\n    result = np.concatenate((a, b), dtype=float)\n    expected = np.array([1, 2, 3, 4, 5, 6], dtype=float)\n    assert np.array_equal(result, expected)\n\n# Test concatenation of float arrays\n@test\ndef test_concatenate_float_arrays():\n    a = np.array([1.1, 2.2, 3.3])\n    b = np.array([4.4, 5.5, 6.6])\n    result = np.concatenate((a, b))\n    expected = np.array([1.1, 2.2, 3.3, 4.4, 5.5, 6.6])\n    assert np.array_equal(result, expected)\n\n# Test concatenation along axis 0\n@test\ndef test_concatenate_axis_0():\n    a = np.array([[1, 2], [3, 4]])\n    b = np.array([[5, 6], [7, 8]])\n    result = np.concatenate((a, b), axis=0)\n    expected = np.array([[1, 2], [3, 4], [5, 6], [7, 8]])\n    assert np.array_equal(result, expected)\n\n# Test concatenation along axis 1\n@test\ndef test_concatenate_axis_1():\n    a = np.array([[1, 2], [3, 4]])\n    b = np.array([[5, 6], [7, 8]])\n    result = np.concatenate((a, b), axis=1)\n    expected = np.array([[1, 2, 5, 6], [3, 4, 7, 8]])\n    assert np.array_equal(result, expected)\n\n# Test concatenation with None axis (flattened arrays)\n@test\ndef test_concatenate_none_axis():\n    a = np.array([[1, 2], [3, 4]])\n    b = np.array([[5, 6], [7, 8]])\n    result = np.concatenate((a, b), axis=None)\n    expected = np.array([1, 2, 3, 4, 5, 6, 7, 8])\n    assert np.array_equal(result, expected)\n\n# Test concatenation of arrays of different data types\n@test\ndef test_concatenate_mixed_dtypes():\n    a = np.array([1, 2, 3])\n    b = np.array([4.0, 5.0, 6.0])\n    result = np.concatenate((a, b))\n    expected = np.array([1, 2, 3, 4.0, 5.0, 6.0])\n    assert np.array_equal(result, expected)\n\n# Test concatenation of list and ndarray\n@test\ndef test_concatenate_list_and_ndarray():\n    a = [1, 2, 3]\n    b = np.array([4, 5, 6])\n    result = np.concatenate((a, b))\n    expected = np.array([1, 2, 3, 4, 5, 6])\n    assert np.array_equal(result, expected)\n\n# Test concatenation of tuple and ndarray\n@test\ndef test_concatenate_tuple_and_ndarray():\n    a = (1, 2, 3)\n    b = np.array([4, 5, 6])\n    result = np.concatenate((a, b))\n    expected = np.array([1, 2, 3, 4, 5, 6])\n    assert np.array_equal(result, expected)\n\n# Test concatenation of non-contiguous arrays\n@test\ndef test_concatenate_non_contiguous():\n    a = np.arange(10)[::2]  # Non-contiguous array\n    b = np.arange(10, 20)[::2]  # Non-contiguous array\n    result = np.concatenate((a, b))\n    expected = np.array([0, 2, 4, 6, 8, 10, 12, 14, 16, 18])\n    assert np.array_equal(result, expected)\n\n# Test concatenation along higher dimensions\n@test\ndef test_concatenate_higher_dims():\n    a = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]]])\n    b = np.array([[[9, 10], [11, 12]], [[13, 14], [15, 16]]])\n\n    result = np.concatenate((a, b), axis=0)\n    expected = np.array([[[1, 2], [3, 4]], [[5, 6], [7, 8]], [[9, 10], [11, 12]], [[13, 14], [15, 16]]])\n    assert np.array_equal(result, expected)\n\n    result = np.concatenate((a, a.T, b, b.T), axis=0)\n    expected = np.array([[[ 1,  2],\n                          [ 3,  4]],\n                         [[ 5,  6],\n                          [ 7,  8]],\n                         [[ 1,  5],\n                          [ 3,  7]],\n                         [[ 2,  6],\n                          [ 4,  8]],\n                         [[ 9, 10],\n                          [11, 12]],\n                         [[13, 14],\n                          [15, 16]],\n                         [[ 9, 13],\n                          [11, 15]],\n                         [[10, 14],\n                          [12, 16]]])\n    assert np.array_equal(result, expected)\n\n@test\ndef test_concatenate_one_array():\n    a = np.array([[1, 2], [3, 4]])\n    assert np.array_equal(np.concatenate(a), [1, 2, 3, 4])\n    assert np.array_equal(np.concatenate([a]), a)\n    assert np.array_equal(np.concatenate((a,)), a)\n    assert np.array_equal(np.concatenate([a], axis=None), a.ravel())\n    assert np.array_equal(np.concatenate((a,), axis=None), a.ravel())\n\ntest_concatenate_int_arrays()\ntest_concatenate_float_arrays()\ntest_concatenate_axis_0()\ntest_concatenate_axis_1()\ntest_concatenate_none_axis()\ntest_concatenate_mixed_dtypes()\ntest_concatenate_list_and_ndarray()\ntest_concatenate_tuple_and_ndarray()\ntest_concatenate_non_contiguous()\ntest_concatenate_higher_dims()\ntest_concatenate_one_array()\n\n@test\ndef test_stack(arrays, expected_shape, axis=0):\n    assert np.stack((arrays), axis).shape == expected_shape\n\ntest_stack((np.array([1, 2, 3]), np.array([4, 5, 6])), (2, 3))\ntest_stack((np.array([1, 2, 3]), np.array([4, 5, 6])), (3, 2), -1)\ntest_stack([np.zeros((3, 4)) for _ in range(10)], (10, 3, 4))\ntest_stack([np.zeros((3, 4)) for _ in range(10)], (3, 10, 4), 1)\ntest_stack([np.zeros((3, 4)) for _ in range(10)], (3, 4, 10), 2)\n\n@test\ndef test_block(arrays, expected):\n    assert (np.block(arrays) == expected).all()\n\ntest_block([1, 2, 3], np.array([1, 2, 3]))\ntest_block([np.eye(2) * 2, np.zeros((2, 2))],\n           np.array([[2., 0., 0., 0.], [0., 2., 0., 0.]]))\ntest_block([np.array([1, 2, 3]),\n            np.array([4, 5, 6]),\n            np.array([10])], np.array([1, 2, 3, 4, 5, 6, 10]))\n\n@test\ndef test_vstack(arrays, expected_shape):\n    assert np.vstack((arrays)).shape == expected_shape\n\ntest_vstack((np.array([1, 2, 3]), np.array([4, 5, 6])), (2, 3))\ntest_vstack((np.array([[1], [2], [3]]), np.array([[4], [5], [6]])), (6, 1))\n\n@test\ndef test_hstack(arrays, expected_shape):\n    assert np.hstack((arrays)).shape == expected_shape\n\ntest_hstack((np.array([1, 2, 3]), np.array([4, 5, 6])), (6, ))\ntest_hstack((np.array([[1], [2], [3]]), np.array([[4], [5], [6]])), (3, 2))\n\n@test\ndef test_dstack(arrays, expected_shape):\n    assert np.dstack((arrays)).shape == expected_shape\n\ntest_dstack((np.array([1, 2, 3]), np.array([4, 5, 6])), (1, 3, 2))\ntest_dstack((np.array([[1], [2], [3]]), np.array([[4], [5], [6]])), (3, 1, 2))\n\n@test\ndef test_column_stack(arrays, expected_shape):\n    assert np.column_stack((arrays)).shape == expected_shape\n\ntest_column_stack((np.array([1, 2, 3]), np.array([4, 5, 6])), (3, 2))\ntest_column_stack((np.array([[1], [2], [3]]), np.array([[4], [5], [6]])),\n                  (3, 2))\n\n@test\ndef test_row_stack(arrays, expected_shape):\n    assert np.row_stack((arrays)).shape == expected_shape\n\ntest_row_stack((np.array([1, 2, 3]), np.array([4, 5, 6])), (2, 3))\ntest_row_stack((np.array([[1], [2], [3]]), np.array([[4], [5], [6]])), (6, 1))\n\n@test\ndef test_split(ary, splits, expected, axis=0):\n    list1 = np.split(ary, splits, axis)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_split(\n    np.arange(9.0), 3,\n    [np.array([0., 1., 2.]),\n     np.array([3., 4., 5.]),\n     np.array([6., 7., 8.])])\ntest_split(np.arange(8.0), [3, 5, 6], [\n    np.array([0., 1., 2.]),\n    np.array([3., 4.]),\n    np.array([5.]),\n    np.array([6., 7.])\n])\n\n@test\ndef test_array_split(ary, splits, expected, axis=0):\n    list1 = np.array_split(ary, splits, axis)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_array_split(\n    np.arange(8.0), 3,\n    [np.array([0., 1., 2.]),\n     np.array([3., 4., 5.]),\n     np.array([6., 7.])])\ntest_array_split(np.arange(9), 4, [\n    np.array([0, 1, 2]),\n    np.array([3, 4]),\n    np.array([5, 6]),\n    np.array([7, 8])\n])\n\n@test\ndef test_dsplit(ary, splits, expected):\n    list1 = np.dsplit(ary, splits)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_dsplit(\n    np.arange(16.0).reshape(2, 2, 4), 2, [\n        np.array([[[0., 1.], [4., 5.]], [[8., 9.], [12., 13.]]]),\n        np.array([[[2., 3.], [6., 7.]], [[10., 11.], [14., 15.]]])\n    ])\n\n@test\ndef test_hsplit(ary, splits, expected):\n    list1 = np.hsplit(ary, splits)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_hsplit(\n    np.arange(16.0).reshape(4, 4), 2, [\n        np.array([[0., 1.], [4., 5.], [8., 9.], [12., 13.]]),\n        np.array([[2., 3.], [6., 7.], [10., 11.], [14., 15.]])\n    ])\ntest_hsplit(\n    np.arange(8.0).reshape(2, 2, 2), 2,\n    [np.array([[[0., 1.]], [[4., 5.]]]),\n     np.array([[[2., 3.]], [[6., 7.]]])])\ntest_hsplit(np.array([0, 1, 2, 3, 4, 5]), 2,\n            [np.array([0, 1, 2]), np.array([3, 4, 5])])\n\n@test\ndef test_vsplit(ary, splits, expected):\n    list1 = np.vsplit(ary, splits)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_vsplit(\n    np.arange(16.0).reshape(4, 4), 2, [\n        np.array([[0., 1., 2., 3.], [4., 5., 6., 7.]]),\n        array([[8., 9., 10., 11.], [12., 13., 14., 15.]])\n    ])\ntest_vsplit(\n    np.arange(8.0).reshape(2, 2, 2), 2,\n    [np.array([[[0., 1.], [2., 3.]]]),\n     np.array([[[4., 5.], [6., 7.]]])])\n\n@test\ndef test_tile(a, reps, expected):\n    assert (np.tile(a, reps) == expected).all()\n\ntest_tile(np.array([0, 1, 2]), 2, np.array([0, 1, 2, 0, 1, 2]))\ntest_tile(np.array([0, 1, 2]), (2, 2),\n          np.array([[0, 1, 2, 0, 1, 2], [0, 1, 2, 0, 1, 2]]))\ntest_tile(np.array([[1, 2], [3, 4]]), (2, 1),\n          np.array([[1, 2], [3, 4], [1, 2], [3, 4]]))\ntest_tile(np.array([[1, 2], [3, 4]]), 2, np.array([[1, 2, 1, 2], [3, 4, 3,\n                                                                  4]]))\n\n@test\ndef test_repeat(a, repeats, expected, axis=None):\n    assert (expected == np.repeat(a, repeats, axis)).all()\n    assert (expected == np.asarray(a).repeat(repeats, axis)).all()\n\ntest_repeat(3, 4, np.array([3, 3, 3, 3]))\ntest_repeat(np.array([[1, 2], [3, 4]]), 2, np.array([1, 1, 2, 2, 3, 3, 4, 4]))\ntest_repeat(np.array([[1, 2], [3, 4]]),\n            3,\n            np.array([[1, 1, 1, 2, 2, 2], [3, 3, 3, 4, 4, 4]]),\n            axis=1)\ntest_repeat(np.array([[1, 2], [3, 4]]), [1, 2],\n            np.array([[1, 2], [3, 4], [3, 4]]),\n            axis=0)\n\n@test\ndef test_delete(arr, obj, expected, axis=None):\n    assert (np.delete(arr, obj, axis) == expected).all()\n\ntest_delete(np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]), 1,\n            np.array([[1, 2, 3, 4], [9, 10, 11, 12]]), 0)\ntest_delete(np.array([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]), [1, 3, 5],\n            np.array([1, 3, 5, 7, 8, 9, 10, 11, 12]))\n\n@test\ndef test_insert(arr, obj, values, expected, axis=None):\n    assert (np.insert(arr, obj, values, axis) == expected).all()\n\ntest_insert(np.array([[1, 1], [2, 2], [3, 3]]), 1, 5,\n            np.array([1, 5, 1, 2, 2, 3, 3]))\ntest_insert(np.array([[1, 1], [2, 2], [3, 3]]),\n            1,\n            5,\n            np.array([[1, 5, 1], [2, 5, 2], [3, 5, 3]]),\n            axis=1)\ntest_insert(np.array([1, 1, 2, 2, 3, 3]), [2, 2], [5, 6],\n            np.array([1, 1, 5, 6, 2, 2, 3, 3]))\n\n@test\ndef test_append(arr, values, expected, axis=None):\n    assert (np.append(arr, values, axis) == expected).all()\n\ntest_append([1, 2, 3], [4, 5, 6], np.array([1, 2, 3, 4, 5, 6]))\ntest_append([[1, 2, 3], [4, 5, 6]], [[7, 8, 9]],\n            np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),\n            axis=0)\n\n@test\ndef test_resize(a, newshape, expected):\n    assert (np.resize(a, newshape) == expected).all()\n\ntest_resize(np.array([[0, 1], [2, 3]]), (2, 3), np.array([[0, 1, 2], [3, 0,\n                                                                      1]]))\ntest_resize(np.array([[0, 1], [2, 3]]), (1, 4), np.array([[0, 1, 2, 3]]))\ntest_resize(np.array([[0, 1], [2, 3]]), (2, 4),\n            np.array([[0, 1, 2, 3], [0, 1, 2, 3]]))\n\n@test\ndef test_trim_zeros(filt, expected, trim: str = 'fb'):\n    assert (np.trim_zeros(filt, trim) == expected).all()\n\ntest_trim_zeros(np.array((0, 0, 0, 1, 2, 3, 0, 2, 1, 0)),\n                np.array([1, 2, 3, 0, 2, 1]))\ntest_trim_zeros(np.array((0, 0, 0, 1, 2, 3, 0, 2, 1, 0)),\n                np.array([0, 0, 0, 1, 2, 3, 0, 2, 1]), 'b')\n\n@test\ndef test_flip(m, expected, axis=None):\n    assert (np.flip(m, axis) == expected).all()\n\ntest_flip(\n    np.arange(8).reshape((2, 2, 2)),\n    np.array([[[4, 5], [6, 7]], [[0, 1], [2, 3]]]), 0)\ntest_flip(\n    np.arange(8).reshape((2, 2, 2)),\n    np.array([[[2, 3], [0, 1]], [[6, 7], [4, 5]]]), 1)\ntest_flip(\n    np.arange(8).reshape((2, 2, 2)),\n    np.array([[[7, 6], [5, 4]], [[3, 2], [1, 0]]]))\ntest_flip(\n    np.arange(8).reshape((2, 2, 2)),\n    np.array([[[5, 4], [7, 6]], [[1, 0], [3, 2]]]), (0, 2))\n\n@test\ndef test_fliplr(m, expected):\n    assert (np.fliplr(m) == expected).all()\n\ntest_fliplr(np.diag([1., 2., 3.]),\n            np.array([[0., 0., 1.], [0., 2., 0.], [3., 0., 0.]]))\n\n@test\ndef test_flipud(m, expected):\n    assert (np.flipud(m) == expected).all()\n\ntest_flipud(np.diag([1., 2., 3.]),\n            np.array([[0., 0., 3.], [0., 2., 0.], [1., 0., 0.]]))\n\n@test\ndef test_roll(a, shift, expected, axis=None):\n    assert (np.roll(a, shift, axis) == expected).all()\n\ntest_roll(np.arange(10), 2, np.array([8, 9, 0, 1, 2, 3, 4, 5, 6, 7]))\ntest_roll(np.arange(10), -2, np.array([2, 3, 4, 5, 6, 7, 8, 9, 0, 1]))\ntest_roll(np.reshape(np.arange(10), (2, 5)), 1,\n          np.array([[9, 0, 1, 2, 3], [4, 5, 6, 7, 8]]))\ntest_roll(np.reshape(np.arange(10), (2, 5)), -1,\n          np.array([[1, 2, 3, 4, 5], [6, 7, 8, 9, 0]]))\ntest_roll(np.reshape(np.arange(10), (2, 5)), (1, 1),\n          np.array([[9, 5, 6, 7, 8], [4, 0, 1, 2, 3]]),\n          axis=(1, 0))\ntest_roll(np.reshape(np.arange(10), (2, 5)),\n          -1,\n          np.array([[1, 2, 3, 4, 0], [6, 7, 8, 9, 5]]),\n          axis=1)\n\n@test\ndef test_rot90(m, expected, k=1, axes=(0, 1)):\n    assert (np.rot90(m, k, axes) == expected).all()\n\ntest_rot90(np.array([[1, 2], [3, 4]]), np.array([[2, 4], [1, 3]]))\ntest_rot90(np.array([[1, 2], [3, 4]]), np.array([[4, 3], [2, 1]]), k=2)\ntest_rot90(\n    np.arange(8).reshape((2, 2, 2)),\n    np.array([[[1, 3], [0, 2]], [[5, 7], [4, 6]]]), 1, (1, 2))\n\n############\n# indexing #\n############\n\n@test\ndef test_indices(dimensions,\n                 expected,\n                 dtype: type = int,\n                 sparse: Literal[bool] = False):\n    assert (np.indices(dimensions, dtype, sparse) == expected).all()\n\ntest_indices((2, 3), np.array([[[0, 0, 0], [1, 1, 1]], [[0, 1, 2], [0, 1,\n                                                                    2]]]))\n\n@test\ndef test_ix_(expected, *args):\n    list1 = np.ix_(*args)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_ix_((np.array([[0], [1]]), np.array([[2, 4]])), [0, 1], [2, 4])\ntest_ix_((np.array([[0]]), np.array([[2, 4]])), [True, False], [2, 4])\n\n@test\ndef test_ravel_multi_index(multi_index,\n                           dims,\n                           expected,\n                           mode: str = 'raise',\n                           order: str = 'C'):\n    assert (np.ravel_multi_index(multi_index, dims, mode,\n                                 order) == expected).all()\n\ntest_ravel_multi_index(np.array([[3, 6, 6], [4, 5, 1]]), (7, 6),\n                       np.array([22, 41, 37]))\ntest_ravel_multi_index(np.array([[3, 6, 6], [4, 5, 1]]), (7, 6),\n                       np.array([31, 41, 13]),\n                       order='F')\ntest_ravel_multi_index(np.array([[3, 6, 6], [4, 5, 1]]), (4, 6),\n                       np.array([22, 23, 19]),\n                       mode='clip')\ntest_ravel_multi_index(np.array([[3, 6, 6], [4, 5, 1]]), (4, 4),\n                       np.array([12, 9, 9]),\n                       mode='wrap')\n\n@test\ndef test_unravel_index(indices, shape, expected, order: str = 'C'):\n    list1 = np.unravel_index(indices, shape, order)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_unravel_index([22, 41, 37], (7, 6),\n                   (np.array([3, 6, 6]), np.array([4, 5, 1])))\ntest_unravel_index([31, 41, 13], (7, 6),\n                   (np.array([3, 6, 6]), np.array([4, 5, 1])),\n                   order='F')\n\n@test\ndef test_diag_indices(n, expected, ndim: int = 2):\n    list1 = np.diag_indices(n, ndim)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_diag_indices(4, (array([0, 1, 2, 3]), array([0, 1, 2, 3])))\ntest_diag_indices(2, (array([0, 1]), array([0, 1]), array([0, 1])), 3)\n\n@test\ndef test_diag_indices_from(arr, expected):\n    list1 = np.diag_indices_from(arr)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_diag_indices_from(\n    np.arange(16).reshape(4, 4), (array([0, 1, 2, 3]), array([0, 1, 2, 3])))\n\n@test\ndef test_mask_indices(n, mask_func, expected, k=0):\n    list1 = np.mask_indices(n, mask_func, k)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_mask_indices(3, np.triu,\n                  (array([0, 0, 0, 1, 1, 2]), array([0, 1, 2, 1, 2, 2])))\ntest_mask_indices(3, np.triu, (array([0, 0, 1]), array([1, 2, 2])), 1)\n\n@test\ndef test_tril_indices(n, expected, k: int = 0, m: Optional[int] = None):\n    list1 = np.tril_indices(n, k, m)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_tril_indices(4, (array([0, 1, 1, 2, 2, 2, 3, 3, 3, 3\n                             ]), array([0, 0, 1, 0, 1, 2, 0, 1, 2, 3])))\ntest_tril_indices(4, (array([0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3]),\n                      array([0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3])),\n                  k=2)\ntest_tril_indices(\n    np.arange(16).reshape(4, 4).shape[0],\n    (array([0, 1, 1, 2, 2, 2, 3, 3, 3, 3\n            ]), array([0, 0, 1, 0, 1, 2, 0, 1, 2, 3])))\n\n@test\ndef test_tril_indices_from(arr, expected, k: int = 0):\n    list1 = np.tril_indices_from(arr, k)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_tril_indices_from(\n    np.arange(16).reshape(4, 4),\n    (array([0, 1, 1, 2, 2, 2, 3, 3, 3, 3\n            ]), array([0, 0, 1, 0, 1, 2, 0, 1, 2, 3])))\ntest_tril_indices_from(np.arange(16).reshape(4, 4),\n                       (array([0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3]),\n                        array([0, 1, 0, 1, 2, 0, 1, 2, 3, 0, 1, 2, 3])),\n                       k=1)\n\n@test\ndef test_triu_indices(n, expected, k: int = 0, m: Optional[int] = None):\n    list1 = np.triu_indices(n, k, m)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_triu_indices(4, (array([0, 0, 0, 0, 1, 1, 1, 2, 2, 3\n                             ]), array([0, 1, 2, 3, 1, 2, 3, 2, 3, 3])))\ntest_triu_indices(4, (array([0, 0, 1]), array([2, 3, 3])), k=2)\ntest_triu_indices(\n    np.arange(16).reshape(4, 4).shape[0],\n    (array([0, 0, 0, 0, 1, 1, 1, 2, 2, 3\n            ]), array([0, 1, 2, 3, 1, 2, 3, 2, 3, 3])))\n\n@test\ndef test_triu_indices_from(arr, expected, k: int = 0):\n    list1 = np.triu_indices_from(arr, k)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_triu_indices_from(\n    np.arange(16).reshape(4, 4),\n    (array([0, 0, 0, 0, 1, 1, 1, 2, 2, 3\n            ]), array([0, 1, 2, 3, 1, 2, 3, 2, 3, 3])))\ntest_triu_indices_from(np.arange(16).reshape(4, 4),\n                       (array([0, 0, 0, 1, 1, 2]), array([1, 2, 3, 2, 3, 3])),\n                       k=1)\n\n@test\ndef test_take(a, indices, expected, m: str = 'raise', axis=None):\n    assert (np.asarray(np.take(a, indices, mode=m,\n                               axis=axis)) == np.asarray(expected)).all()\n\ntest_take([4, 3, 5, 7, 6, 8], [0, 1, 4], np.array([4, 3, 6]))\ntest_take([4, 3, 5, 7, 6, 8], [[0, 1], [2, 3]], np.array([[4, 3], [5, 7]]))\ntest_take([4, 3, 5, 7, 6, 8], [0, 1, 4], np.array([4, 3, 6]), m='wrap')\ntest_take([42], 0, 42)\ntest_take([[42, 1], [99, 2]], 2, 99)\ntest_take(np.array([[42, 1], [99, 2]]), 1, np.array([1, 2]), axis=1)\n\n@test\ndef test_take_along_axis(a, indices, axis, expected):\n    assert (np.take_along_axis(a, indices, axis) == expected).all()\n\ntest_take_along_axis(np.array([[10, 30, 20], [60, 40, 50]]),\n                     np.array([[0, 2, 1], [1, 2, 0]]), 1,\n                     np.array([[10, 20, 30], [40, 50, 60]]))\ntest_take_along_axis(np.array([[10, 30, 20], [60, 40, 50]]),\n                     np.array([[0, 1], [1, 0]]), 1,\n                     np.array([[10, 30], [40, 60]]))\n\n@test\ndef test_choose(a, choices, expected, m: str = 'raise'):\n    assert (np.choose(a, choices, mode=m) == expected).all()\n\ntest_choose(\n    [2, 3, 1, 0],\n    [[0, 1, 2, 3], [10, 11, 12, 13], [20, 21, 22, 23], [30, 31, 32, 33]],\n    np.array([20, 31, 12, 3]))\ntest_choose(\n    [2, 3, 1, 0],\n    [[0, 1, 2, 3], [10, 11, 12, 13], [20, 21, 22, 23], [30, 31, 32, 33]],\n    np.array([20, 31, 12, 3]),\n    m='clip')\ntest_choose(\n    [2, 4, 1, 0],\n    [[0, 1, 2, 3], [10, 11, 12, 13], [20, 21, 22, 23], [30, 31, 32, 33]],\n    np.array([20, 1, 12, 3]),\n    m='wrap')\n\n@test\ndef test_compress(condition, a, expected, axis=None):\n    assert (np.compress(condition, a, axis) == expected).all()\n    assert (np.asarray(a).compress(condition, axis) == expected).all()\n\ntest_compress([0, 1],\n              np.array([[1, 2], [3, 4], [5, 6]]),\n              np.array([[3, 4]]),\n              axis=0)\ntest_compress([False, True, True],\n              np.array([[1, 2], [3, 4], [5, 6]]),\n              np.array([[3, 4], [5, 6]]),\n              axis=0)\ntest_compress([False, True],\n              np.array([[1, 2], [3, 4], [5, 6]]),\n              np.array([[2], [4], [6]]),\n              axis=1)\n\n@test\ndef test_diagonal(a,\n                  expected,\n                  offset: int = 0,\n                  axis1: int = 0,\n                  axis2: int = 1):\n    assert (np.diagonal(a, offset, axis1, axis2) == expected).all()\n\ntest_diagonal(np.arange(4).reshape(2, 2), np.array([0, 3]))\ntest_diagonal(np.arange(4).reshape(2, 2), np.array([1]), 1)\ntest_diagonal(\n    np.arange(8).reshape(2, 2, 2), np.array([[0, 6], [1, 7]]), 0, 0, 1)\n\n@test\ndef test_select(condlist, choicelist, expected, default: int = 0):\n    assert (np.select(condlist, choicelist, default) == expected).all()\n\ntest_select([np.arange(6) < 3, np.arange(6) > 3],\n            [np.arange(6), np.arange(6)**2], np.array([0, 1, 2, 42, 16,\n                                                       25]), 42)\ntest_select([np.arange(6) <= 4, np.arange(6) > 3],\n            [np.arange(6), np.arange(6)**2], np.array([0, 1, 2, 3, 4, 25]), 55)\n\n@test\ndef test_place(arr, mask, val, expected):\n    np.place(arr, mask, val)\n    assert (arr == expected).all()\n\ntest_place(\n    np.arange(6).reshape(2, 3),\n    np.arange(6).reshape(2, 3) > 2, [44, 55],\n    np.array([[0, 1, 2], [44, 55, 44]]))\n\n@test\ndef test_put(a, ind, v, expected, mode: str = 'raise'):\n    np.put(a, ind, v, mode=mode)\n    assert (a == expected).all()\n\ntest_put(np.arange(5), [0, 2], [-44, -55], np.array([-44, 1, -55, 3, 4]))\ntest_put(np.arange(5), 22, -5, np.array([0, 1, 2, 3, -5]), mode='clip')\n\n@test\ndef test_put_along_axis(arr, indices, values, axis, expected):\n    np.put_along_axis(arr, indices, values, axis)\n    assert (arr == expected).all()\n\ntest_put_along_axis(np.array([[10, 30, 20], [60, 40, 50]]), array([[1], [0]]),\n                    99, 1, np.array([[10, 99, 20], [99, 40, 50]]))\n\n@test\ndef test_putmask(a, mask, values, expected):\n    np.putmask(a, mask, values)\n    assert (a == expected).all()\n\ntest_putmask(\n    np.arange(6).reshape(2, 3),\n    np.arange(6).reshape(2, 3) > 2,\n    np.arange(6).reshape(2, 3)**2, np.array([[0, 1, 2], [9, 16, 25]]))\ntest_putmask(np.arange(5),\n             np.arange(5) > 1, [-33, -44], np.array([0, 1, -33, -44, -33]))\n\n@test\ndef test_fill_diagonal(a, val, expected, wrap: bool = False):\n    np.fill_diagonal(a, val, wrap=wrap)\n    assert (a == expected).all()\n\ntest_fill_diagonal(np.zeros((3, 3), int), 5,\n                   np.array([[5, 0, 0], [0, 5, 0], [0, 0, 5]]))\ntest_fill_diagonal(\n    np.zeros((5, 3), int), 4,\n    np.array([[4, 0, 0], [0, 4, 0], [0, 0, 4], [0, 0, 0], [0, 0, 0]]))\ntest_fill_diagonal(np.zeros((5, 3), int),\n                   4,\n                   np.array([[4, 0, 0], [0, 4, 0], [0, 0, 4], [0, 0, 0],\n                             [4, 0, 0]]),\n                   wrap=True)\ntest_fill_diagonal(np.zeros((3, 5), int),\n                   4,\n                   np.array([[4, 0, 0, 0, 0], [0, 4, 0, 0, 0], [0, 0, 4, 0,\n                                                                0]]),\n                   wrap=True)\n\n@test\ndef test_pad(array, pad_width, expected, mode='constant', **kwargs):\n    assert np.allclose(np.pad(array, pad_width, mode, **kwargs), expected)\n\ntest_pad([1, 2, 3, 4, 5], (2, 3),\n         np.array([4, 4, 1, 2, 3, 4, 5, 6, 6, 6]),\n         'constant',\n         constant_values=(4, 6))\ntest_pad([1, 2, 3, 4, 5], (2, 3), np.array([1, 1, 1, 2, 3, 4, 5, 5, 5, 5]),\n         'edge')\ntest_pad([1, 2, 3, 4, 5], (2, 3),\n         np.array([5, 3, 1, 2, 3, 4, 5, 2, -1, -4]),\n         'linear_ramp',\n         end_values=(5, -4))\ntest_pad([1, 2, 3, 4, 5], (2, ), np.array([5, 5, 1, 2, 3, 4, 5, 5, 5]),\n         'maximum')\ntest_pad([1, 2, 3, 4, 5], (2, ), np.array([3, 3, 1, 2, 3, 4, 5, 3, 3]), 'mean')\ntest_pad([1, 2, 3, 4, 5], (2, ), np.array([3, 3, 1, 2, 3, 4, 5, 3, 3]),\n         'median')\ntest_pad([1, 2, 3, 4, 5], (2, 3), np.array([3, 2, 1, 2, 3, 4, 5, 4, 3, 2]),\n         'reflect')\ntest_pad([1, 2, 3, 4, 5], (2, 3),\n         np.array([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8]),\n         'reflect',\n         reflect_type='odd')\ntest_pad([1, 2, 3, 4, 5], (2, 3), np.array([2, 1, 1, 2, 3, 4, 5, 5, 4, 3]),\n         'symmetric')\ntest_pad([1, 2, 3, 4, 5], (2, 3),\n         np.array([0, 1, 1, 2, 3, 4, 5, 5, 6, 7]),\n         'symmetric',\n         reflect_type='odd')\ntest_pad([1, 2, 3, 4, 5], (2, 3), np.array([4, 5, 1, 2, 3, 4, 5, 1, 2, 3]),\n         'wrap')\n\ntest_pad(np.array([1, 2, 3, 4, 5], dtype=np.float32), (2, 3),\n         np.array([4, 4, 1, 2, 3, 4, 5, 6, 6, 6], dtype=np.float32),\n         'constant',\n         constant_values=(4, 6))\ntest_pad(np.array([1, 2, 3, 4, 5], dtype=np.float32), (2, 3),\n         np.array([1, 1, 1, 2, 3, 4, 5, 5, 5, 5], dtype=np.float32),\n         'edge')\ntest_pad(np.array([1, 2, 3, 4, 5], dtype=np.float32), (2, 3),\n         np.array([5, 3, 1, 2, 3, 4, 5, 2, -1, -4], dtype=np.float32),\n         'linear_ramp',\n         end_values=(5, -4))\ntest_pad(np.array([1, 2, 3, 4, 5], dtype=np.float32), (2, ),\n         np.array([5, 5, 1, 2, 3, 4, 5, 5, 5], dtype=np.float32),\n         'maximum')\ntest_pad(np.array([1, 2, 3, 4, 5], dtype=np.float32), (2, ),\n         np.array([3, 3, 1, 2, 3, 4, 5, 3, 3], dtype=np.float32),\n         'mean')\ntest_pad(np.array([1, 2, 3, 4, 5], dtype=np.float32), (2, ),\n         np.array([3, 3, 1, 2, 3, 4, 5, 3, 3], dtype=np.float32),\n         'median')\ntest_pad(np.array([1, 2, 3, 4, 5], dtype=np.float32), (2, 3),\n         np.array([3, 2, 1, 2, 3, 4, 5, 4, 3, 2], dtype=np.float32),\n         'reflect')\ntest_pad(np.array([1, 2, 3, 4, 5], dtype=np.float32), (2, 3),\n         np.array([-1, 0, 1, 2, 3, 4, 5, 6, 7, 8], dtype=np.float32),\n         'reflect',\n         reflect_type='odd')\ntest_pad(np.array([1, 2, 3, 4, 5], dtype=np.float32), (2, 3),\n         np.array([2, 1, 1, 2, 3, 4, 5, 5, 4, 3], dtype=np.float32),\n         'symmetric')\ntest_pad(np.array([1, 2, 3, 4, 5], dtype=np.float32), (2, 3),\n         np.array([0, 1, 1, 2, 3, 4, 5, 5, 6, 7], dtype=np.float32),\n         'symmetric',\n         reflect_type='odd')\ntest_pad(np.array([1, 2, 3, 4, 5], dtype=np.float32), (2, 3),\n         np.array([4, 5, 1, 2, 3, 4, 5, 1, 2, 3], dtype=np.float32),\n         'wrap')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[0., 0., 0., 0., 0., 0., 0.],\n                   [0., 0., 0., 0., 0., 0., 0.],\n                   [0., 0., 0., 0., 0., 0., 0.],\n                   [0., 0., 1., 2., 0., 0., 0.],\n                   [0., 0., 3., 4., 0., 0., 0.],\n                   [0., 0., 0., 0., 0., 0., 0.],\n                   [0., 0., 0., 0., 0., 0., 0.]], dtype=np.float32),\n         'constant')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[1., 1., 1., 2., 2., 2., 2.],\n                   [1., 1., 1., 2., 2., 2., 2.],\n                   [1., 1., 1., 2., 2., 2., 2.],\n                   [1., 1., 1., 2., 2., 2., 2.],\n                   [3., 3., 3., 4., 4., 4., 4.],\n                   [3., 3., 3., 4., 4., 4., 4.],\n                   [3., 3., 3., 4., 4., 4., 4.]], dtype=np.float32),\n         'edge')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[0.        , 0.        , 0.        , 0.        , 0.        ,\n                    0.        , 0.        ],\n                   [0.        , 0.16666667, 0.33333334, 0.6666667 , 0.44444448,\n                    0.22222224, 0.        ],\n                   [0.        , 0.33333334, 0.6666667 , 1.3333334 , 0.88888896,\n                    0.44444448, 0.        ],\n                   [0.        , 0.5       , 1.        , 2.        , 1.3333334 ,\n                    0.6666667 , 0.        ],\n                   [0.        , 1.5       , 3.        , 4.        , 2.6666667 ,\n                    1.3333334 , 0.        ],\n                   [0.        , 0.75      , 1.5       , 2.        , 1.3333334 ,\n                    0.6666667 , 0.        ],\n                   [0.        , 0.        , 0.        , 0.        , 0.        ,\n                    0.        , 0.        ]], dtype=np.float32),\n         'linear_ramp')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[4., 4., 3., 4., 4., 4., 4.],\n                   [4., 4., 3., 4., 4., 4., 4.],\n                   [4., 4., 3., 4., 4., 4., 4.],\n                   [2., 2., 1., 2., 2., 2., 2.],\n                   [4., 4., 3., 4., 4., 4., 4.],\n                   [4., 4., 3., 4., 4., 4., 4.],\n                   [4., 4., 3., 4., 4., 4., 4.]], dtype=np.float32),\n         'maximum')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [1.5, 1.5, 1. , 2. , 1.5, 1.5, 1.5],\n                   [3.5, 3.5, 3. , 4. , 3.5, 3.5, 3.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5]], dtype=np.float32),\n         'mean')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [1.5, 1.5, 1. , 2. , 1.5, 1.5, 1.5],\n                   [3.5, 3.5, 3. , 4. , 3.5, 3.5, 3.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5]], dtype=np.float32),\n         'median')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[1., 1., 1., 2., 1., 1., 1.],\n                   [1., 1., 1., 2., 1., 1., 1.],\n                   [1., 1., 1., 2., 1., 1., 1.],\n                   [1., 1., 1., 2., 1., 1., 1.],\n                   [3., 3., 3., 4., 3., 3., 3.],\n                   [1., 1., 1., 2., 1., 1., 1.],\n                   [1., 1., 1., 2., 1., 1., 1.]], dtype=np.float32),\n         'minimum')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[3., 4., 3., 4., 3., 4., 3.],\n                   [1., 2., 1., 2., 1., 2., 1.],\n                   [3., 4., 3., 4., 3., 4., 3.],\n                   [1., 2., 1., 2., 1., 2., 1.],\n                   [3., 4., 3., 4., 3., 4., 3.],\n                   [1., 2., 1., 2., 1., 2., 1.],\n                   [3., 4., 3., 4., 3., 4., 3.]], dtype=np.float32),\n         'reflect')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[4., 3., 3., 4., 4., 3., 3.],\n                   [4., 3., 3., 4., 4., 3., 3.],\n                   [2., 1., 1., 2., 2., 1., 1.],\n                   [2., 1., 1., 2., 2., 1., 1.],\n                   [4., 3., 3., 4., 4., 3., 3.],\n                   [4., 3., 3., 4., 4., 3., 3.],\n                   [2., 1., 1., 2., 2., 1., 1.]], dtype=np.float32),\n         'symmetric')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[3., 4., 3., 4., 3., 4., 3.],\n                   [1., 2., 1., 2., 1., 2., 1.],\n                   [3., 4., 3., 4., 3., 4., 3.],\n                   [1., 2., 1., 2., 1., 2., 1.],\n                   [3., 4., 3., 4., 3., 4., 3.],\n                   [1., 2., 1., 2., 1., 2., 1.],\n                   [3., 4., 3., 4., 3., 4., 3.]], dtype=np.float32),\n         'wrap')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[0., 0., 0., 0., 0., 0., 0.],\n                   [0., 0., 0., 0., 0., 0., 0.],\n                   [0., 0., 0., 0., 0., 0., 0.],\n                   [0., 0., 1., 2., 0., 0., 0.],\n                   [0., 0., 3., 4., 0., 0., 0.],\n                   [0., 0., 0., 0., 0., 0., 0.],\n                   [0., 0., 0., 0., 0., 0., 0.]], dtype=np.float32),\n         'constant')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[-1., -1., -1., -1., -1., -1., -1.],\n                   [-1., -1., -1., -1., -1., -1., -1.],\n                   [-1., -1., -1., -1., -1., -1., -1.],\n                   [-1., -1.,  1.,  2., -1., -1., -1.],\n                   [-1., -1.,  3.,  4., -1., -1., -1.],\n                   [-1., -1., -1., -1., -1., -1., -1.],\n                   [-1., -1., -1., -1., -1., -1., -1.]], dtype=np.float32),\n         'constant', constant_values=-1)\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[-1., -1., -1., -1., -2., -2., -2.],\n                   [-1., -1., -1., -1., -2., -2., -2.],\n                   [-1., -1., -1., -1., -2., -2., -2.],\n                   [-1., -1.,  1.,  2., -2., -2., -2.],\n                   [-1., -1.,  3.,  4., -2., -2., -2.],\n                   [-1., -1., -2., -2., -2., -2., -2.],\n                   [-1., -1., -2., -2., -2., -2., -2.]], dtype=np.float32),\n         'constant', constant_values=((-1, -2)))\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[-3., -3., -1., -1., -4., -4., -4.],\n                   [-3., -3., -1., -1., -4., -4., -4.],\n                   [-3., -3., -1., -1., -4., -4., -4.],\n                   [-3., -3.,  1.,  2., -4., -4., -4.],\n                   [-3., -3.,  3.,  4., -4., -4., -4.],\n                   [-3., -3., -2., -2., -4., -4., -4.],\n                   [-3., -3., -2., -2., -4., -4., -4.]], dtype=np.float32),\n         'constant', constant_values=((-1, -2), (-3, -4)))\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[1., 1., 1., 2., 2., 2., 2.],\n                   [1., 1., 1., 2., 2., 2., 2.],\n                   [1., 1., 1., 2., 2., 2., 2.],\n                   [1., 1., 1., 2., 2., 2., 2.],\n                   [3., 3., 3., 4., 4., 4., 4.],\n                   [3., 3., 3., 4., 4., 4., 4.],\n                   [3., 3., 3., 4., 4., 4., 4.]], dtype=np.float32),\n         'edge')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[0.        , 0.        , 0.        , 0.        , 0.        ,\n                    0.        , 0.        ],\n                   [0.        , 0.16666667, 0.33333334, 0.6666667 , 0.44444448,\n                    0.22222224, 0.        ],\n                   [0.        , 0.33333334, 0.6666667 , 1.3333334 , 0.88888896,\n                    0.44444448, 0.        ],\n                   [0.        , 0.5       , 1.        , 2.        , 1.3333334 ,\n                    0.6666667 , 0.        ],\n                   [0.        , 1.5       , 3.        , 4.        , 2.6666667 ,\n                    1.3333334 , 0.        ],\n                   [0.        , 0.75      , 1.5       , 2.        , 1.3333334 ,\n                    0.6666667 , 0.        ],\n                   [0.        , 0.        , 0.        , 0.        , 0.        ,\n                    0.        , 0.        ]], dtype=np.float32),\n         'linear_ramp')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[1.       , 1.       , 1.       , 1.       , 1.       , 1.       ,\n                    1.       ],\n                   [1.       , 1.       , 1.       , 1.3333334, 1.2222222, 1.1111112,\n                    1.       ],\n                   [1.       , 1.       , 1.       , 1.6666667, 1.4444445, 1.2222222,\n                    1.       ],\n                   [1.       , 1.       , 1.       , 2.       , 1.6666667, 1.3333334,\n                    1.       ],\n                   [1.       , 2.       , 3.       , 4.       , 3.       , 2.       ,\n                    1.       ],\n                   [1.       , 1.5      , 2.       , 2.5      , 2.       , 1.5      ,\n                    1.       ],\n                   [1.       , 1.       , 1.       , 1.       , 1.       , 1.       ,\n                    1.       ]], dtype=np.float32),\n         'linear_ramp', end_values=1)\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[1.       , 1.       , 1.       , 1.       , 1.3333333, 1.6666666,\n                    2.       ],\n                   [1.       , 1.       , 1.       , 1.3333334, 1.5555556, 1.7777778,\n                    2.       ],\n                   [1.       , 1.       , 1.       , 1.6666667, 1.7777778, 1.888889 ,\n                    2.       ],\n                   [1.       , 1.       , 1.       , 2.       , 2.       , 2.       ,\n                    2.       ],\n                   [1.       , 2.       , 3.       , 4.       , 3.3333335, 2.6666667,\n                    2.       ],\n                   [1.       , 1.75     , 2.5      , 3.       , 2.6666667, 2.3333333,\n                    2.       ],\n                   [1.       , 1.5      , 2.       , 2.       , 2.       , 2.       ,\n                    2.       ]], dtype=np.float32),\n         'linear_ramp', end_values=(1, 2))\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[2.       , 1.5      , 1.       , 1.       , 1.       , 1.       ,\n                    1.       ],\n                   [2.       , 1.5      , 1.       , 1.3333334, 1.2222222, 1.1111112,\n                    1.       ],\n                   [2.       , 1.5      , 1.       , 1.6666667, 1.4444445, 1.2222222,\n                    1.       ],\n                   [2.       , 1.5      , 1.       , 2.       , 1.6666667, 1.3333334,\n                    1.       ],\n                   [2.       , 2.5      , 3.       , 4.       , 3.       , 2.       ,\n                    1.       ],\n                   [2.       , 2.25     , 2.5      , 3.       , 2.3333335, 1.6666667,\n                    1.       ],\n                   [2.       , 2.       , 2.       , 2.       , 1.6666667, 1.3333334,\n                    1.       ]], dtype=np.float32),\n         'linear_ramp', end_values=((1, 2), (2, 1)))\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[4., 4., 3., 4., 4., 4., 4.],\n                   [4., 4., 3., 4., 4., 4., 4.],\n                   [4., 4., 3., 4., 4., 4., 4.],\n                   [2., 2., 1., 2., 2., 2., 2.],\n                   [4., 4., 3., 4., 4., 4., 4.],\n                   [4., 4., 3., 4., 4., 4., 4.],\n                   [4., 4., 3., 4., 4., 4., 4.]], dtype=np.float32),\n         'maximum')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [1.5, 1.5, 1. , 2. , 1.5, 1.5, 1.5],\n                   [3.5, 3.5, 3. , 4. , 3.5, 3.5, 3.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5]], dtype=np.float32),\n         'mean')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[1., 1., 1., 2., 2., 2., 2.],\n                   [1., 1., 1., 2., 2., 2., 2.],\n                   [1., 1., 1., 2., 2., 2., 2.],\n                   [1., 1., 1., 2., 2., 2., 2.],\n                   [3., 3., 3., 4., 4., 4., 4.],\n                   [3., 3., 3., 4., 4., 4., 4.],\n                   [3., 3., 3., 4., 4., 4., 4.]], dtype=np.float32),\n         'mean', stat_length=1)\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[1. , 1. , 1. , 2. , 1.5, 1.5, 1.5],\n                   [1. , 1. , 1. , 2. , 1.5, 1.5, 1.5],\n                   [1. , 1. , 1. , 2. , 1.5, 1.5, 1.5],\n                   [1. , 1. , 1. , 2. , 1.5, 1.5, 1.5],\n                   [3. , 3. , 3. , 4. , 3.5, 3.5, 3.5],\n                   [2. , 2. , 2. , 3. , 2.5, 2.5, 2.5],\n                   [2. , 2. , 2. , 3. , 2.5, 2.5, 2.5]], dtype=np.float32),\n         'mean', stat_length=(1, 2))\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[1.5, 1.5, 1. , 2. , 2. , 2. , 2. ],\n                   [1.5, 1.5, 1. , 2. , 2. , 2. , 2. ],\n                   [1.5, 1.5, 1. , 2. , 2. , 2. , 2. ],\n                   [1.5, 1.5, 1. , 2. , 2. , 2. , 2. ],\n                   [3.5, 3.5, 3. , 4. , 4. , 4. , 4. ],\n                   [2.5, 2.5, 2. , 3. , 3. , 3. , 3. ],\n                   [2.5, 2.5, 2. , 3. , 3. , 3. , 3. ]], dtype=np.float32),\n         'mean', stat_length=((1, 2), (2, 1)))\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [1.5, 1.5, 1. , 2. , 1.5, 1.5, 1.5],\n                   [3.5, 3.5, 3. , 4. , 3.5, 3.5, 3.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5],\n                   [2.5, 2.5, 2. , 3. , 2.5, 2.5, 2.5]], dtype=np.float32),\n         'median')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[1., 1., 1., 2., 1., 1., 1.],\n                   [1., 1., 1., 2., 1., 1., 1.],\n                   [1., 1., 1., 2., 1., 1., 1.],\n                   [1., 1., 1., 2., 1., 1., 1.],\n                   [3., 3., 3., 4., 3., 3., 3.],\n                   [1., 1., 1., 2., 1., 1., 1.],\n                   [1., 1., 1., 2., 1., 1., 1.]], dtype=np.float32),\n         'minimum')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[3., 4., 3., 4., 3., 4., 3.],\n                   [1., 2., 1., 2., 1., 2., 1.],\n                   [3., 4., 3., 4., 3., 4., 3.],\n                   [1., 2., 1., 2., 1., 2., 1.],\n                   [3., 4., 3., 4., 3., 4., 3.],\n                   [1., 2., 1., 2., 1., 2., 1.],\n                   [3., 4., 3., 4., 3., 4., 3.]], dtype=np.float32),\n         'reflect')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[4., 3., 3., 4., 4., 3., 3.],\n                   [4., 3., 3., 4., 4., 3., 3.],\n                   [2., 1., 1., 2., 2., 1., 1.],\n                   [2., 1., 1., 2., 2., 1., 1.],\n                   [4., 3., 3., 4., 4., 3., 3.],\n                   [4., 3., 3., 4., 4., 3., 3.],\n                   [2., 1., 1., 2., 2., 1., 1.]], dtype=np.float32),\n         'symmetric')\n\ntest_pad(np.array([[1, 2], [3, 4]], np.float32), ((3, 2), (2, 3)),\n         np.array([[3., 4., 3., 4., 3., 4., 3.],\n                   [1., 2., 1., 2., 1., 2., 1.],\n                   [3., 4., 3., 4., 3., 4., 3.],\n                   [1., 2., 1., 2., 1., 2., 1.],\n                   [3., 4., 3., 4., 3., 4., 3.],\n                   [1., 2., 1., 2., 1., 2., 1.],\n                   [3., 4., 3., 4., 3., 4., 3.]], dtype=np.float32),\n         'wrap')\n\ntest_pad([[[[1]]]], 1,\n    np.array([[[[2, 2, 2],\n                [2, 2, 2],\n                [2, 2, 2]],\n               [[2, 2, 2],\n                [2, 2, 2],\n                [2, 2, 2]],\n               [[2, 2, 2],\n                [2, 2, 2],\n                [2, 2, 2]]],\n              [[[2, 2, 2],\n                [2, 2, 2],\n                [2, 2, 2]],\n               [[2, 2, 2],\n                [2, 1, 2],\n                [2, 2, 2]],\n               [[2, 2, 2],\n                [2, 2, 2],\n                [2, 2, 2]]],\n              [[[2, 2, 2],\n                [2, 2, 2],\n                [2, 2, 2]],\n               [[2, 2, 2],\n                [2, 2, 2],\n                [2, 2, 2]],\n               [[2, 2, 2],\n                [2, 2, 2],\n                [2, 2, 2]]]]), 'constant', constant_values=2)\n\ntest_pad([[[[1]]]], 0, np.array([[[[1]]]]), 'constant', constant_values=2)\n\n#############\n# searching #\n#############\n\n@test\ndef test_argmax(a, expected, axis=None):\n    if isinstance(expected, int):\n        assert a.argmax() == expected\n    else:\n        assert (a.argmax(axis) == expected).all()\n\ntest_argmax(\n    np.arange(24).reshape((2, 3, 4)), np.array([[2, 2, 2, 2], [2, 2, 2, 2]]),\n    1)\ntest_argmax(np.array([[4, 2, 3], [1, 0, 3]]), np.array([0, 2]), -1)\ntest_argmax(np.arange(6), 5)\ntest_argmax(np.arange(6).reshape(2, 3) + 10, 5)\ntest_argmax(np.arange(6).reshape(2, 3) + 10, np.array([1, 1, 1]), 0)\ntest_argmax(np.arange(6).reshape(2, 3) + 10, np.array([2, 2]), 1)\n\n@test\ndef test_argmin(a, expected, axis=None):\n    if isinstance(expected, int):\n        assert a.argmin() == expected\n    else:\n        assert (a.argmin(axis) == expected).all()\n\ntest_argmin(\n    np.arange(24).reshape((2, 3, 4)), np.array([[0, 0, 0, 0], [0, 0, 0, 0]]),\n    1)\ntest_argmin(np.array([[4, 2, 3], [1, 0, 3]]), np.array([1, 1]), -1)\ntest_argmin(np.arange(6), 0)\ntest_argmin(np.arange(6).reshape(2, 3) + 10, 0)\ntest_argmin(np.arange(6).reshape(2, 3) - 10, np.array([0, 0, 0]), 0)\ntest_argmin(np.arange(6).reshape(2, 3) + 10, np.array([0, 0]), 1)\n\n@test\ndef test_argwhere(a, expected):\n    assert (np.argwhere(a) == expected).all()\n\ntest_argwhere(\n    np.arange(6).reshape(2, 3) > 1, np.array([[0, 2], [1, 0], [1, 1], [1, 2]]))\n\n@test\ndef test_nonzero(a, expected):\n    list1 = np.nonzero(a)\n    for arr1, arr2 in zip(list1, expected):\n        assert (arr1 == arr2).all()\n\ntest_nonzero(np.array([[3, 0, 0], [0, 4, 0], [5, 6, 0]]),\n             (np.array([0, 1, 2, 2]), np.array([0, 1, 0, 1])))\n\n@test\ndef test_flatnonzero(a, expected):\n    assert (np.flatnonzero(a) == expected).all()\n\ntest_flatnonzero(np.arange(-2, 3), np.array([0, 1, 3, 4]))\n\n@test\ndef test_where(condition, x, y, expected):\n    assert (np.where(condition, x, y) == expected).all()\n\ntest_where(\n    np.arange(10) < 5, np.arange(10), 10 * np.arange(10),\n    np.array([0, 1, 2, 3, 4, 50, 60, 70, 80, 90]))\ntest_where([[True, False], [True, True]], [[1, 2], [3, 4]], [[9, 8], [7, 6]],\n           np.array([[1, 8], [3, 4]]))\n\n@test\ndef test_searchsorted(a, v, expected, side: str = 'left', sorter=None):\n    if isinstance(v, int):\n        assert np.searchsorted(a, v, side=side, sorter=sorter) == expected\n    else:\n        assert (np.searchsorted(a, v, side=side,\n                                sorter=sorter) == expected).all()\n\ntest_searchsorted([1, 2, 3, 4, 5], 3, 2)\ntest_searchsorted([1, 2, 3, 4, 5], 3, 3, side='right')\ntest_searchsorted([1, 2, 3, 4, 5], [-10, 10, 2, 3], array([0, 5, 1, 2]))\n\n@test\ndef test_extract(arr, condition, expected):\n    assert (np.extract(condition, arr) == expected).all()\n\ntest_extract(\n    np.arange(12).reshape((3, 4)),\n    np.mod(np.arange(12).reshape((3, 4)), 3) == 0, np.array([0, 3, 6, 9]))\n\n@test\ndef test_count_nonzero(a, expected, axis=None, keepdims: Literal[bool] = False):\n    x = np.count_nonzero(a, axis=axis, keepdims=keepdims)\n    if isinstance(x, int):\n        assert x == expected\n    else:\n        assert (x == expected).all()\n\ntest_count_nonzero(np.eye(4), 4)\ntest_count_nonzero(np.array([[0, 1, 7, 0], [3, 0, 2, 19]]),\n                   np.array([1, 1, 2, 1]),\n                   axis=0)\ntest_count_nonzero(np.array([[0, 1, 7, 0], [3, 0, 2, 19]]),\n                   np.array([2, 3]),\n                   axis=1)\ntest_count_nonzero(np.array([[0, 1, 7, 0], [3, 0, 2, 19]]),\n                   np.array([[2], [3]]),\n                   axis=1,\n                   keepdims=True)\n\n########\n# Misc #\n########\n\n@test\ndef test_round(a, expected, decimals: int = 0):\n    assert (np.round(a, decimals=decimals) == expected).all()\n    assert (np.around(a, decimals=decimals) == expected).all()\n\ntest_round([0.37, 1.64], np.array([0., 2.]))\ntest_round([0.37, 1.64], np.array([0.4, 1.6]), 1)\ntest_round([.5, 1.5, 2.5, 3.5, 4.5], np.array([0., 2., 2., 4., 4.]))\ntest_round([1, 2, 3, 11], np.array([1, 2, 3, 11]), 1)\ntest_round([1, 2, 3, 11], np.array([0, 0, 0, 10]), -1)\n\n@test\ndef test_ndenumerate(arr, expected_index, expected_val):\n    i = 0\n    for index, x in np.ndenumerate(arr):\n        assert (index == expected_index[i]).all() and x == expected_val[i]\n        i += 1\n\ntest_ndenumerate(np.array([[1, 2], [3, 4]]),\n                 np.array([[0, 0], [0, 1], [1, 0], [1, 1]]),\n                 np.array([1, 2, 3, 4]))\n\n@test\ndef test_ndindex(expected, *shape):\n    i = 0\n    for index in np.ndindex(*shape):\n        assert (index == expected[i]).all()\n        i += 1\n\ntest_ndindex(\n    np.array([(0, 0, 0), (0, 1, 0), (1, 0, 0), (1, 1, 0), (2, 0, 0),\n              (2, 1, 0)]), 3, 2, 1)\ntest_ndindex(\n    np.array([(0, 0, 0), (0, 1, 0), (1, 0, 0), (1, 1, 0), (2, 0, 0),\n              (2, 1, 0)]), (3, 2, 1))\n\n@test\ndef test_iterable(y, expected):\n    assert np.iterable(y) == expected\n\ntest_iterable(np.array(1.), False)\ntest_iterable([1, 2, 3], True)\ntest_iterable(2, False)\n\n@test\ndef test_view():\n    a = np.array([[1, 2], [3, 4]])\n    assert (a.view(np.int32) == np.array([[1, 0, 2, 0], [3, 0, 4, 0]],\n                                         dtype=np.int32)).all()\n    assert (a.T.view(float).view(int) == a.T).all()\n    assert (a.view(np.int32).view(np.int64) == a).all()\n\n    try:\n        a.T.view(np.int32)\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        a.T.view(Int[256])\n        assert False\n    except ValueError:\n        pass\n\ntest_view()\n\n@test\ndef test_isposinf():\n    assert not np.isposinf(np.NINF)\n    assert np.isposinf(np.inf)\n    assert np.isposinf(np.PINF)\n    assert (np.isposinf([-np.inf, 0.,\n                         np.inf]) == np.array([False, False, True])).all()\n    x = np.array([-np.inf, 0., np.inf])\n    y = np.array([2, 2, 2])\n    assert (np.isposinf(x, y) == np.array([0, 0, 1])).all()\n    assert (y == np.array([0, 0, 1])).all()\n\ntest_isposinf()\n\n@test\ndef test_isneginf():\n    assert np.isneginf(np.NINF)\n    assert not np.isneginf(np.inf)\n    assert not np.isneginf(np.PINF)\n    assert (np.isneginf([-np.inf, 0.,\n                         np.inf]) == np.array([True, False, False])).all()\n    x = np.array([-np.inf, 0., np.inf])\n    y = np.array([2, 2, 2])\n    assert (np.isneginf(x, y) == np.array([1, 0, 0])).all()\n    assert (y == np.array([1, 0, 0])).all()\n\ntest_isneginf()\n\n@test\ndef test_iscomplex():\n    assert np.iscomplex(1 + 2j)\n    assert not np.iscomplex(1 + 0j)\n    assert not np.iscomplex(42)\n    assert (np.iscomplex([1 + 1j, 1 + 0j, 4.5 + 0j, 3 + 0j, 2 + 0j,\n                          2j]) == np.array(\n                              [True, False, False, False, False, True])).all()\n    assert (np.iscomplex([1.1, 2.2, 3.3]) == np.array([False, False,\n                                                       False])).all()\n\ntest_iscomplex()\n\n@test\ndef test_iscomplexobj():\n    assert not np.iscomplexobj(1)\n    assert np.iscomplexobj(1 + 0j)\n    assert not np.iscomplexobj(np.array(1))\n    assert np.iscomplexobj(np.array(1 + 0j))\n    assert not np.iscomplexobj(np.array([1]))\n    assert np.iscomplexobj(np.array([1 + 0j]))\n    assert np.iscomplexobj([3 + 0j, 1 + 0j, 0 + 0j])\n    assert not np.iscomplexobj([1.1, 2.2, 3.3])\n    assert not np.iscomplexobj(np.array([1.1, 2.2, 3.3]))\n\ntest_iscomplexobj()\n\n@test\ndef test_isreal():\n    assert not np.isreal(1 + 2j)\n    assert np.isreal(1 + 0j)\n    assert np.isreal(42)\n    assert (np.isreal([1 + 1j, 1 + 0j, 4.5 + 0j, 3 + 0j, 2 + 0j,\n                       2j]) == np.array([False, True, True, True, True,\n                                         False])).all()\n    assert (np.isreal([1.1, 2.2, 3.3]) == np.array([True, True, True])).all()\n\ntest_isreal()\n\n@test\ndef test_isrealobj():\n    assert np.isrealobj(1)\n    assert not np.isrealobj(1 + 0j)\n    assert np.isrealobj(np.array(1))\n    assert not np.isrealobj(np.array(1 + 0j))\n    assert np.isrealobj(np.array([1]))\n    assert not np.isrealobj(np.array([1 + 0j]))\n    assert not np.isrealobj([3 + 0j, 1 + 0j, 0 + 0j])\n    assert np.isrealobj([1.1, 2.2, 3.3])\n    assert np.isrealobj(np.array([1.1, 2.2, 3.3]))\n\ntest_isrealobj()\n\n@test\ndef test_isfortran():\n    a = np.array([[1, 2, 3], [4, 5, 6]], order='C')\n    assert not np.isfortran(a)\n    assert np.isfortran(a.T)\n    b = np.array([[1, 2, 3], [4, 5, 6]], order='F')\n    assert np.isfortran(b)\n    assert not np.isfortran(np.array([1, 2], order='F'))\n\ntest_isfortran()\n\n@test\ndef test_isscalar():\n    assert np.isscalar(3.1)\n    assert not np.isscalar(np.array(3.1))\n    assert not np.isscalar([3.1])\n    assert np.isscalar(False)\n    assert np.isscalar('numpy')\n    import re\n    assert not np.isscalar(re.compile('x'))\n\ntest_isscalar()\n\n@test\ndef test_byteswap():\n    assert np.array(0x1122334455667788).byteswap() == np.array(\n        -8613303245920329199)\n\n    a = np.array([1, 256, 8755], dtype=np.int16)\n    assert (a.byteswap(inplace=False) == np.array([256, 1, 13090],\n                                                  dtype=np.int16)).all()\n    assert (a == np.array([1, 256, 8755], dtype=np.int16)).all()\n\n    a = np.array([1, 256, 8755], dtype=np.int16)\n    assert (a.byteswap(inplace=True) == np.array([256, 1, 13090],\n                                                 dtype=np.int16)).all()\n    assert (a == np.array([256, 1, 13090], dtype=np.int16)).all()\n    assert np.isclose(np.array(1e100).byteswap(), 6.40220504e+297)\n\n    x = np.array(1e100).byteswap().item()\n    assert np.isclose(x, 6.40220504e+297)\n\n    x = np.array(1e10, dtype=np.float32).byteswap().item()\n    assert np.isclose(x, -4.221443e+34)\n\n    x = np.array(1e100 + 2e200j).byteswap().item()\n    assert x.real == np.array(1e100).byteswap().item()\n    assert x.imag == np.array(2e200).byteswap().item()\n\n    x = np.array(1e10 + 2e20j, dtype=np.complex64).byteswap().item()\n    assert x.real == np.array(1e10, dtype=np.float32).byteswap().item()\n    assert x.imag == np.array(2e20, dtype=np.float32).byteswap().item()\n\n    assert np.array(True).byteswap() == True\n    assert np.array(False).byteswap() == False\n\ntest_byteswap()\n\n@test\ndef test_packbits():\n    # Test with binary data, default axis and bitorder\n    data = np.array([[1, 0, 1], [0, 1, 0]], dtype=bool)\n    packed_data = np.packbits(data)\n    assert np.array_equal(packed_data, np.array([168], dtype=np.uint8))\n\n    # Test with non-binary data, default axis and bitorder\n    data = np.array([[2, 3, 0], [1, 4, 5]], dtype=np.uint8)\n    packed_data = np.packbits(data)\n    assert np.array_equal(packed_data, np.array([220], dtype=np.uint8))\n\n    # Test with empty input\n    data = np.empty((0, ), dtype=bool)\n    packed_data = np.packbits(data)\n    assert np.array_equal(packed_data, np.empty((0, ), dtype=bool))\n\n    # Test with specified bitorder\n    data = np.array([1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0], dtype=bool)\n    packed_data = np.packbits(data, bitorder='little')\n    assert np.array_equal(packed_data, np.array([35, 33], dtype=np.uint8))\n    packed_data = np.packbits(data, bitorder='big')\n    assert np.array_equal(packed_data, np.array([196, 132], dtype=np.uint8))\n\n    # Test with truncated data\n    data = np.array([True])\n    assert np.array_equal(np.packbits(data), np.array([128], dtype=np.uint8))\n    assert np.array_equal(np.packbits(data, bitorder='big'),\n                          np.array([128], dtype=np.uint8))\n    assert np.array_equal(np.packbits(data, bitorder='little'),\n                          np.array([1], dtype=np.uint8))\n\n    data = np.array([[True]])\n    assert np.array_equal(np.packbits(data, axis=0),\n                          np.array([[128]], dtype=np.uint8))\n    assert np.array_equal(np.packbits(data, bitorder='big', axis=0),\n                          np.array([[128]], dtype=np.uint8))\n    assert np.array_equal(np.packbits(data, bitorder='little', axis=0),\n                          np.array([[1]], dtype=np.uint8))\n\ntest_packbits()\n\n@test\ndef test_unpackbits():\n    # Test with packed binary data, default axis and count\n    packed_data = np.array([160, 64], dtype=np.uint8)\n    unpacked_data = np.unpackbits(packed_data)\n    assert np.array_equal(\n        unpacked_data,\n        np.array([1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0],\n                 dtype=np.uint8))\n    unpacked_data = np.unpackbits(packed_data, bitorder='little')\n    assert np.array_equal(\n        unpacked_data,\n        np.array([0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0],\n                 dtype=np.uint8))\n\n    # Test with packed non-binary data, default axis and count\n    packed_data = np.array([147, 128, 120, 64], dtype=np.uint8)\n    unpacked_data = np.unpackbits(packed_data)\n    assert np.array_equal(\n        unpacked_data,\n        np.array([\n            1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0,\n            0, 0, 0, 1, 0, 0, 0, 0, 0, 0\n        ],\n                 dtype=np.uint8))\n\n    # Test with empty input\n    packed_data = np.empty((0, ), dtype=np.uint8)\n    unpacked_data = np.unpackbits(packed_data)\n    assert np.array_equal(unpacked_data, np.empty((0, ), dtype=bool))\n\n    # Test with specified axis\n    packed_data = np.array([[160, 64], [99, 42]], dtype=np.uint8)\n    unpacked_data = np.unpackbits(packed_data, axis=0)\n    assert np.array_equal(\n        unpacked_data,\n        np.array(\n            [[1, 0], [0, 1], [1, 0], [0, 0], [0, 0], [0, 0], [0, 0], [0, 0],\n             [0, 0], [1, 0], [1, 1], [0, 0], [0, 1], [0, 0], [1, 1], [1, 0]],\n            dtype=np.uint8))\n    unpacked_data = np.unpackbits(packed_data, axis=1, count=-3)\n    assert np.array_equal(\n        unpacked_data,\n        np.array([[1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0],\n                  [0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1]],\n                 dtype=np.uint8))\n\n    # Test with specified count\n    packed_data = np.array([160, 64], dtype=np.uint8)\n    unpacked_data = np.unpackbits(packed_data, count=6)\n    assert np.array_equal(unpacked_data,\n                          np.array([1, 0, 1, 0, 0, 0], dtype=np.uint8))\n    unpacked_data = np.unpackbits(packed_data, count=6, bitorder='little')\n    assert np.array_equal(unpacked_data,\n                          np.array([0, 0, 0, 0, 0, 1], dtype=np.uint8))\n\n    packed_data = np.array([111], dtype=np.uint8)\n    assert np.array_equal(\n        np.unpackbits(packed_data, count=3, bitorder='little'),\n        np.array([1, 1, 1], dtype=np.uint8))\n    assert np.array_equal(\n        np.unpackbits(packed_data, count=-3, bitorder='little'),\n        np.array([1, 1, 1, 1, 0], dtype=np.uint8))\n    assert np.array_equal(np.unpackbits(packed_data, count=3, bitorder='big'),\n                          np.array([0, 1, 1], dtype=np.uint8))\n    assert np.array_equal(np.unpackbits(packed_data, count=-3, bitorder='big'),\n                          np.array([0, 1, 1, 0, 1], dtype=np.uint8))\n    assert np.array_equal(np.unpackbits(packed_data, count=3),\n                          np.array([0, 1, 1], dtype=np.uint8))\n    assert np.array_equal(np.unpackbits(packed_data, count=-3),\n                          np.array([0, 1, 1, 0, 1], dtype=np.uint8))\n    assert np.array_equal(np.unpackbits(packed_data, count=0),\n                          np.empty((0, ), dtype=np.uint8))\n\n    packed_data = np.array([[111]], dtype=np.uint8)\n    assert np.array_equal(\n        np.unpackbits(packed_data, count=3, bitorder='little', axis=0),\n        np.array([[1], [1], [1]], dtype=np.uint8))\n    assert np.array_equal(\n        np.unpackbits(packed_data, count=3, bitorder='little', axis=1),\n        np.array([[1, 1, 1]], dtype=np.uint8))\n    assert np.array_equal(\n        np.unpackbits(packed_data, count=-3, bitorder='little', axis=1),\n        np.array([[1, 1, 1, 1, 0]], dtype=np.uint8))\n    assert np.array_equal(\n        np.unpackbits(packed_data, count=3, bitorder='big', axis=1),\n        np.array([[0, 1, 1]], dtype=np.uint8))\n    assert np.array_equal(\n        np.unpackbits(packed_data, count=-3, bitorder='big', axis=1),\n        np.array([[0, 1, 1, 0, 1]], dtype=np.uint8))\n    assert np.array_equal(np.unpackbits(packed_data, count=3, axis=1),\n                          np.array([[0, 1, 1]], dtype=np.uint8))\n    assert np.array_equal(np.unpackbits(packed_data, count=-3, axis=1),\n                          np.array([[0, 1, 1, 0, 1]], dtype=np.uint8))\n    assert np.array_equal(np.unpackbits(packed_data, count=0, axis=1),\n                          np.empty((1, 0), dtype=np.uint8))\n\ntest_unpackbits()\n\n@test\ndef test_tobytes():\n    a = np.arange(9).reshape(3, 3) + .5\n    assert a.tobytes(\n    ) == '\\x00\\x00\\x00\\x00\\x00\\x00\\xe0?\\x00\\x00\\x00\\x00\\x00\\x00\\xf8?\\x00\\x00\\x00\\x00\\x00\\x00\\x04@\\x00\\x00\\x00\\x00\\x00\\x00\\x0c@\\x00\\x00\\x00\\x00\\x00\\x00\\x12@\\x00\\x00\\x00\\x00\\x00\\x00\\x16@\\x00\\x00\\x00\\x00\\x00\\x00\\x1a@\\x00\\x00\\x00\\x00\\x00\\x00\\x1e@\\x00\\x00\\x00\\x00\\x00\\x00!@'\n    assert a.tobytes(\n        order='C'\n    ) == '\\x00\\x00\\x00\\x00\\x00\\x00\\xe0?\\x00\\x00\\x00\\x00\\x00\\x00\\xf8?\\x00\\x00\\x00\\x00\\x00\\x00\\x04@\\x00\\x00\\x00\\x00\\x00\\x00\\x0c@\\x00\\x00\\x00\\x00\\x00\\x00\\x12@\\x00\\x00\\x00\\x00\\x00\\x00\\x16@\\x00\\x00\\x00\\x00\\x00\\x00\\x1a@\\x00\\x00\\x00\\x00\\x00\\x00\\x1e@\\x00\\x00\\x00\\x00\\x00\\x00!@'\n    assert a.tobytes(\n        order='F'\n    ) == '\\x00\\x00\\x00\\x00\\x00\\x00\\xe0?\\x00\\x00\\x00\\x00\\x00\\x00\\x0c@\\x00\\x00\\x00\\x00\\x00\\x00\\x1a@\\x00\\x00\\x00\\x00\\x00\\x00\\xf8?\\x00\\x00\\x00\\x00\\x00\\x00\\x12@\\x00\\x00\\x00\\x00\\x00\\x00\\x1e@\\x00\\x00\\x00\\x00\\x00\\x00\\x04@\\x00\\x00\\x00\\x00\\x00\\x00\\x16@\\x00\\x00\\x00\\x00\\x00\\x00!@'\n    assert a.tobytes(\n        order='A'\n    ) == '\\x00\\x00\\x00\\x00\\x00\\x00\\xe0?\\x00\\x00\\x00\\x00\\x00\\x00\\xf8?\\x00\\x00\\x00\\x00\\x00\\x00\\x04@\\x00\\x00\\x00\\x00\\x00\\x00\\x0c@\\x00\\x00\\x00\\x00\\x00\\x00\\x12@\\x00\\x00\\x00\\x00\\x00\\x00\\x16@\\x00\\x00\\x00\\x00\\x00\\x00\\x1a@\\x00\\x00\\x00\\x00\\x00\\x00\\x1e@\\x00\\x00\\x00\\x00\\x00\\x00!@'\n    assert a.tobytes(\n        order='K'\n    ) == '\\x00\\x00\\x00\\x00\\x00\\x00\\xe0?\\x00\\x00\\x00\\x00\\x00\\x00\\xf8?\\x00\\x00\\x00\\x00\\x00\\x00\\x04@\\x00\\x00\\x00\\x00\\x00\\x00\\x0c@\\x00\\x00\\x00\\x00\\x00\\x00\\x12@\\x00\\x00\\x00\\x00\\x00\\x00\\x16@\\x00\\x00\\x00\\x00\\x00\\x00\\x1a@\\x00\\x00\\x00\\x00\\x00\\x00\\x1e@\\x00\\x00\\x00\\x00\\x00\\x00!@'\n    assert a.T.tobytes(\n    ) == '\\x00\\x00\\x00\\x00\\x00\\x00\\xe0?\\x00\\x00\\x00\\x00\\x00\\x00\\x0c@\\x00\\x00\\x00\\x00\\x00\\x00\\x1a@\\x00\\x00\\x00\\x00\\x00\\x00\\xf8?\\x00\\x00\\x00\\x00\\x00\\x00\\x12@\\x00\\x00\\x00\\x00\\x00\\x00\\x1e@\\x00\\x00\\x00\\x00\\x00\\x00\\x04@\\x00\\x00\\x00\\x00\\x00\\x00\\x16@\\x00\\x00\\x00\\x00\\x00\\x00!@'\n    assert a.T.tobytes(\n        order='C'\n    ) == '\\x00\\x00\\x00\\x00\\x00\\x00\\xe0?\\x00\\x00\\x00\\x00\\x00\\x00\\x0c@\\x00\\x00\\x00\\x00\\x00\\x00\\x1a@\\x00\\x00\\x00\\x00\\x00\\x00\\xf8?\\x00\\x00\\x00\\x00\\x00\\x00\\x12@\\x00\\x00\\x00\\x00\\x00\\x00\\x1e@\\x00\\x00\\x00\\x00\\x00\\x00\\x04@\\x00\\x00\\x00\\x00\\x00\\x00\\x16@\\x00\\x00\\x00\\x00\\x00\\x00!@'\n    assert a.T.tobytes(\n        order='F'\n    ) == '\\x00\\x00\\x00\\x00\\x00\\x00\\xe0?\\x00\\x00\\x00\\x00\\x00\\x00\\xf8?\\x00\\x00\\x00\\x00\\x00\\x00\\x04@\\x00\\x00\\x00\\x00\\x00\\x00\\x0c@\\x00\\x00\\x00\\x00\\x00\\x00\\x12@\\x00\\x00\\x00\\x00\\x00\\x00\\x16@\\x00\\x00\\x00\\x00\\x00\\x00\\x1a@\\x00\\x00\\x00\\x00\\x00\\x00\\x1e@\\x00\\x00\\x00\\x00\\x00\\x00!@'\n    assert a.T.tobytes(\n        order='A'\n    ) == '\\x00\\x00\\x00\\x00\\x00\\x00\\xe0?\\x00\\x00\\x00\\x00\\x00\\x00\\xf8?\\x00\\x00\\x00\\x00\\x00\\x00\\x04@\\x00\\x00\\x00\\x00\\x00\\x00\\x0c@\\x00\\x00\\x00\\x00\\x00\\x00\\x12@\\x00\\x00\\x00\\x00\\x00\\x00\\x16@\\x00\\x00\\x00\\x00\\x00\\x00\\x1a@\\x00\\x00\\x00\\x00\\x00\\x00\\x1e@\\x00\\x00\\x00\\x00\\x00\\x00!@'\n    assert a.T.tobytes(\n        order='K'\n    ) == '\\x00\\x00\\x00\\x00\\x00\\x00\\xe0?\\x00\\x00\\x00\\x00\\x00\\x00\\x0c@\\x00\\x00\\x00\\x00\\x00\\x00\\x1a@\\x00\\x00\\x00\\x00\\x00\\x00\\xf8?\\x00\\x00\\x00\\x00\\x00\\x00\\x12@\\x00\\x00\\x00\\x00\\x00\\x00\\x1e@\\x00\\x00\\x00\\x00\\x00\\x00\\x04@\\x00\\x00\\x00\\x00\\x00\\x00\\x16@\\x00\\x00\\x00\\x00\\x00\\x00!@'\n    assert a[::2, ::2].tobytes(\n    ) == '\\x00\\x00\\x00\\x00\\x00\\x00\\xe0?\\x00\\x00\\x00\\x00\\x00\\x00\\x04@\\x00\\x00\\x00\\x00\\x00\\x00\\x1a@\\x00\\x00\\x00\\x00\\x00\\x00!@'\n    assert a[::2, ::2].tobytes(\n        order='C'\n    ) == '\\x00\\x00\\x00\\x00\\x00\\x00\\xe0?\\x00\\x00\\x00\\x00\\x00\\x00\\x04@\\x00\\x00\\x00\\x00\\x00\\x00\\x1a@\\x00\\x00\\x00\\x00\\x00\\x00!@'\n    assert a[::2, ::2].tobytes(\n        order='F'\n    ) == '\\x00\\x00\\x00\\x00\\x00\\x00\\xe0?\\x00\\x00\\x00\\x00\\x00\\x00\\x1a@\\x00\\x00\\x00\\x00\\x00\\x00\\x04@\\x00\\x00\\x00\\x00\\x00\\x00!@'\n    assert a[::2, ::2].tobytes(\n        order='A'\n    ) == '\\x00\\x00\\x00\\x00\\x00\\x00\\xe0?\\x00\\x00\\x00\\x00\\x00\\x00\\x04@\\x00\\x00\\x00\\x00\\x00\\x00\\x1a@\\x00\\x00\\x00\\x00\\x00\\x00!@'\n    assert a[::2, ::2].tobytes(\n        order='K'\n    ) == '\\x00\\x00\\x00\\x00\\x00\\x00\\xe0?\\x00\\x00\\x00\\x00\\x00\\x00\\x04@\\x00\\x00\\x00\\x00\\x00\\x00\\x1a@\\x00\\x00\\x00\\x00\\x00\\x00!@'\n\ntest_tobytes()\n\n@test\ndef test_real_imag():\n    a = np.arange(9).reshape(3, 3)\n    assert np.array_equal(a.real, a)\n    assert np.array_equal(a.imag, np.zeros((3, 3)))\n    assert np.array_equal(a.conj(), a)\n    assert np.array_equal(a.conjugate(), a)\n    a_copy = a.copy()\n    a_copy.real = [33.9, 22.2, 11.1]\n    assert np.array_equal(a_copy, [[33, 22, 11], [33, 22, 11], [33, 22, 11]])\n\n    b = np.arange(9).reshape(3, 3) * (1 + 2j)\n    assert np.array_equal(b.real, a)\n    assert np.array_equal(b.imag, 2 * a)\n    assert np.array_equal(b.conj(), a * (1 - 2j))\n    assert np.array_equal(b.conjugate(), a * (1 - 2j))\n    assert np.array_equal(b[::2, ::2].real, a[::2, ::2])\n    assert np.array_equal(b[::2, ::2].imag, 2 * a[::2, ::2])\n    assert np.array_equal(b[::2, ::2].conj(), a[::2, ::2] * (1 - 2j))\n    assert np.array_equal(b[::2, ::2].conjugate(), a[::2, ::2] * (1 - 2j))\n    b.real = 7 * a\n    assert np.array_equal(b.real, 7 * a)\n    b.imag = 8 * a\n    assert np.array_equal(b.imag, 8 * a)\n    assert np.array_equal(b, a * (7 + 8j))\n\n    b = np.arange(9).reshape(3, 3) * np.complex64(1 + 2j)\n    assert np.array_equal(b.real, a)\n    assert np.array_equal(b.imag, 2 * a.astype(float32))\n    assert np.array_equal(b.conj(), a * np.complex64(1 - 2j))\n    assert np.array_equal(b.conjugate(), a * np.complex64(1 - 2j))\n    assert np.array_equal(b[::2, ::2].real, a.astype(float32)[::2, ::2])\n    assert np.array_equal(b[::2, ::2].imag, 2 * a.astype(float32)[::2, ::2])\n    assert np.array_equal(b[::2, ::2].conj(),\n                          a[::2, ::2] * np.complex64(1 - 2j))\n    assert np.array_equal(b[::2, ::2].conjugate(),\n                          a[::2, ::2] * np.complex64(1 - 2j))\n    b.real = 7 * a.astype(float32)\n    assert np.array_equal(b.real, 7 * a.astype(float32))\n    b.imag = 8 * a.astype(float32)\n    assert np.array_equal(b.imag, 8 * a.astype(float32))\n    assert np.array_equal(b, a * np.complex64(7 + 8j))\n\ntest_real_imag()\n\n@test\ndef test_flat_setter():\n    a = np.array(42)\n    a.flat = 3.14\n    assert a.item() == 3\n\n    a.flat = [9, 8]\n    assert a.item() == 9\n\n    a = np.array([[4, 4], [1, 1], [0, 1]]).T\n    a.flat = [[11.1], [22.2], [33.3], [44.4]]\n    assert np.array_equal(a, [[11, 22, 33], [44, 11, 22]])\n    a.flat = 99\n    assert np.array_equal(a, [[99, 99, 99], [99, 99, 99]])\n    a.flat = np.empty(0)\n    assert np.array_equal(a, [[99, 99, 99], [99, 99, 99]])\n    b = np.array([[999, 888, 777, 666], [555, 444, 333, 222]])\n    a.flat = b\n    assert np.array_equal(a, [[999, 888, 777], [666, 555, 444]])\n\ntest_flat_setter()\n\n@test\ndef test_dragon4():\n    # these tests are adapted from Ryan Juckett's dragon4 implementation,\n    # see dragon4.c for details.\n\n    def fpos32(x, **k):\n        return np.format_float_positional(np.float32(float(x)), **k)\n\n    def fsci32(x, **k):\n        return np.format_float_scientific(np.float32(float(x)), **k)\n\n    def fpos64(x, **k):\n        return np.format_float_positional(np.float64(x), **k)\n\n    def fsci64(x, **k):\n        return np.format_float_scientific(np.float64(x), **k)\n\n    def equal(a, b):\n        return a == b\n\n    assert equal(fpos32('1.0'), \"1.\")\n    assert equal(fsci32('1.0'), \"1.e+00\")\n    assert equal(fpos32('10.234'), \"10.234\")\n    assert equal(fpos32('-10.234'), \"-10.234\")\n    assert equal(fsci32('10.234'), \"1.0234e+01\")\n    assert equal(fsci32('-10.234'), \"-1.0234e+01\")\n    assert equal(fpos32('1000.0'), \"1000.\")\n    assert equal(fpos32('1.0', precision=0), \"1.\")\n    assert equal(fsci32('1.0', precision=0), \"1.e+00\")\n    assert equal(fpos32('10.234', precision=0), \"10.\")\n    assert equal(fpos32('-10.234', precision=0), \"-10.\")\n    assert equal(fsci32('10.234', precision=0), \"1.e+01\")\n    assert equal(fsci32('-10.234', precision=0), \"-1.e+01\")\n    assert equal(fpos32('10.234', precision=2), \"10.23\")\n    assert equal(fsci32('-10.234', precision=2), \"-1.02e+01\")\n    assert equal(fsci64('9.9999999999999995e-08', unique=False, precision=16),\n                 '9.9999999999999995e-08')\n    assert equal(fsci64('9.8813129168249309e-324', unique=False, precision=16),\n                 '9.8813129168249309e-324')\n    assert equal(fsci64('9.9999999999999694e-311', unique=False, precision=16),\n                 '9.9999999999999694e-311')\n\n    # test rounding\n    # 3.1415927410 is closest float32 to np.pi\n    assert equal(fpos32('3.14159265358979323846', unique=False, precision=10),\n                 \"3.1415927410\")\n    assert equal(fsci32('3.14159265358979323846', unique=False, precision=10),\n                 \"3.1415927410e+00\")\n    assert equal(fpos64('3.14159265358979323846', unique=False, precision=10),\n                 \"3.1415926536\")\n    assert equal(fsci64('3.14159265358979323846', unique=False, precision=10),\n                 \"3.1415926536e+00\")\n    # 299792448 is closest float32 to 299792458\n    assert equal(fpos32('299792458.0', unique=False, precision=5),\n                 \"299792448.00000\")\n    assert equal(fsci32('299792458.0', unique=False, precision=5),\n                 \"2.99792e+08\")\n    assert equal(fpos64('299792458.0', unique=False, precision=5),\n                 \"299792458.00000\")\n    assert equal(fsci64('299792458.0', unique=False, precision=5),\n                 \"2.99792e+08\")\n\n    assert equal(fpos32('3.14159265358979323846', unique=False, precision=25),\n                 \"3.1415927410125732421875000\")\n    assert equal(fpos64('3.14159265358979323846', unique=False, precision=50),\n                 \"3.14159265358979311599796346854418516159057617187500\")\n    assert equal(fpos64('3.14159265358979323846'), \"3.141592653589793\")\n\n    # smallest numbers\n    assert equal(\n        fpos32(0.5**(126 + 23), unique=False, precision=149),\n        \"0.00000000000000000000000000000000000000000000140129846432\"\n        \"4817070923729583289916131280261941876515771757068283889791\"\n        \"08268586060148663818836212158203125\")\n\n    assert equal(\n        fpos64('5e-324', unique=False, precision=1074),\n        \"0.00000000000000000000000000000000000000000000000000000000\"\n        \"0000000000000000000000000000000000000000000000000000000000\"\n        \"0000000000000000000000000000000000000000000000000000000000\"\n        \"0000000000000000000000000000000000000000000000000000000000\"\n        \"0000000000000000000000000000000000000000000000000000000000\"\n        \"0000000000000000000000000000000000049406564584124654417656\"\n        \"8792868221372365059802614324764425585682500675507270208751\"\n        \"8652998363616359923797965646954457177309266567103559397963\"\n        \"9877479601078187812630071319031140452784581716784898210368\"\n        \"8718636056998730723050006387409153564984387312473397273169\"\n        \"6151400317153853980741262385655911710266585566867681870395\"\n        \"6031062493194527159149245532930545654440112748012970999954\"\n        \"1931989409080416563324524757147869014726780159355238611550\"\n        \"1348035264934720193790268107107491703332226844753335720832\"\n        \"4319360923828934583680601060115061698097530783422773183292\"\n        \"4790498252473077637592724787465608477820373446969953364701\"\n        \"7972677717585125660551199131504891101451037862738167250955\"\n        \"8373897335989936648099411642057026370902792427675445652290\"\n        \"87538682506419718265533447265625\")\n\n    # largest numbers\n    f32x = np.float32(3.4028235e+38)  # np.finfo(np.float32).max\n    assert equal(fpos32(f32x, unique=False, precision=0),\n                 \"340282346638528859811704183484516925440.\")\n    assert equal(\n        fpos64(1.7976931348623157e+308, unique=False, precision=0),\n        \"1797693134862315708145274237317043567980705675258449965989\"\n        \"1747680315726078002853876058955863276687817154045895351438\"\n        \"2464234321326889464182768467546703537516986049910576551282\"\n        \"0762454900903893289440758685084551339423045832369032229481\"\n        \"6580855933212334827479782620414472316873817718091929988125\"\n        \"0404026184124858368.\")\n\n    # Warning: In unique mode only the integer digits necessary for\n    # uniqueness are computed, the rest are 0.\n    assert equal(fpos32(f32x), \"340282350000000000000000000000000000000.\")\n\n    # Further tests of zero-padding vs rounding in different combinations\n    # of unique, fractional, precision, min_digits\n    # precision can only reduce digits, not add them.\n    # min_digits can only extend digits, not reduce them.\n    assert equal(fpos32(f32x, unique=True, fractional=True, precision=0),\n                 \"340282350000000000000000000000000000000.\")\n    assert equal(fpos32(f32x, unique=True, fractional=True, precision=4),\n                 \"340282350000000000000000000000000000000.\")\n    assert equal(fpos32(f32x, unique=True, fractional=True, min_digits=0),\n                 \"340282346638528859811704183484516925440.\")\n    assert equal(fpos32(f32x, unique=True, fractional=True, min_digits=4),\n                 \"340282346638528859811704183484516925440.0000\")\n    assert equal(\n        fpos32(f32x, unique=True, fractional=True, min_digits=4, precision=4),\n        \"340282346638528859811704183484516925440.0000\")\n    try:\n        fpos32(f32x, unique=True, fractional=False, precision=0)\n        assert False\n    except ValueError:\n        pass\n    assert equal(fpos32(f32x, unique=True, fractional=False, precision=4),\n                 \"340300000000000000000000000000000000000.\")\n    assert equal(fpos32(f32x, unique=True, fractional=False, precision=20),\n                 \"340282350000000000000000000000000000000.\")\n    assert equal(fpos32(f32x, unique=True, fractional=False, min_digits=4),\n                 \"340282350000000000000000000000000000000.\")\n    assert equal(fpos32(f32x, unique=True, fractional=False, min_digits=20),\n                 \"340282346638528859810000000000000000000.\")\n    assert equal(fpos32(f32x, unique=True, fractional=False, min_digits=15),\n                 \"340282346638529000000000000000000000000.\")\n    assert equal(fpos32(f32x, unique=False, fractional=False, precision=4),\n                 \"340300000000000000000000000000000000000.\")\n    # test that unique rounding is preserved when precision is supplied\n    # but no extra digits need to be printed (gh-18609)\n    a = np.array(np.uint64('13393705291799855104')).view(\n        np.float64).item()  #np.float64.fromhex('-1p-97')\n    assert equal(fsci64(a, unique=True), '-6.310887241768095e-30')\n    assert equal(fsci64(a, unique=False, precision=15),\n                 '-6.310887241768094e-30')\n    assert equal(fsci64(a, unique=True, precision=15),\n                 '-6.310887241768095e-30')\n    assert equal(fsci64(a, unique=True, min_digits=15),\n                 '-6.310887241768095e-30')\n    assert equal(fsci64(a, unique=True, precision=15, min_digits=15),\n                 '-6.310887241768095e-30')\n    # adds/remove digits in unique mode with unbiased rnding\n    assert equal(fsci64(a, unique=True, precision=14), '-6.31088724176809e-30')\n    assert equal(fsci64(a, unique=True, min_digits=16),\n                 '-6.3108872417680944e-30')\n    assert equal(fsci64(a, unique=True, precision=16),\n                 '-6.310887241768095e-30')\n    assert equal(fsci64(a, unique=True, min_digits=14),\n                 '-6.310887241768095e-30')\n    # test min_digits in unique mode with different rounding cases\n    assert equal(fsci64('1e120', min_digits=3), '1.000e+120')\n    assert equal(fsci64('1e100', min_digits=3), '1.000e+100')\n\n    # test trailing zeros\n    assert equal(fpos32('1.0', unique=False, precision=3), \"1.000\")\n    assert equal(fpos64('1.0', unique=False, precision=3), \"1.000\")\n    assert equal(fsci32('1.0', unique=False, precision=3), \"1.000e+00\")\n    assert equal(fsci64('1.0', unique=False, precision=3), \"1.000e+00\")\n    assert equal(fpos32('1.5', unique=False, precision=3), \"1.500\")\n    assert equal(fpos64('1.5', unique=False, precision=3), \"1.500\")\n    assert equal(fsci32('1.5', unique=False, precision=3), \"1.500e+00\")\n    assert equal(fsci64('1.5', unique=False, precision=3), \"1.500e+00\")\n    # gh-10713\n    assert equal(fpos64('324', unique=False, precision=5, fractional=False),\n                 \"324.00\")\n\ntest_dragon4()\n\n@test\ndef test_dragon4_interface():\n    tps = (np.float16(), np.float32(), np.float64(), np.float128())\n    fpos = np.format_float_positional\n    fsci = np.format_float_scientific\n\n    def f(s: str, tp: type):\n        return tp(float(s))\n\n    def equal(a, b):\n        return a == b\n\n    for tp_ in tps:\n        tp = type(tp_)\n        # test padding\n        assert equal(fpos(f('1.0', tp), pad_left=4, pad_right=4), \"   1.    \")\n        assert equal(fpos(f('-1.0', tp), pad_left=4, pad_right=4), \"  -1.    \")\n        assert equal(fpos(f('-10.2', tp), pad_left=4, pad_right=4),\n                     (\" -10.2   \" if tp is not np.float128 else\n                      \" -10.199999999999999289457264239899814\"))\n\n        # test exp_digits\n        assert equal(fsci(f('1.23e1', tp), exp_digits=5),\n                     (\"1.23e+00001\" if tp is not np.float128 else\n                      \"1.2300000000000000710542735760100186e+00001\"))\n\n        # test fixed (non-unique) mode\n        assert equal(fpos(f('1.0', tp), unique=False, precision=4), \"1.0000\")\n        assert equal(fsci(f('1.0', tp), unique=False, precision=4),\n                     \"1.0000e+00\")\n\n        # test trimming\n        # trim of 'k' or '.' only affects non-unique mode, since unique\n        # mode will not output trailing 0s.\n        assert equal(fpos(f('1.', tp), unique=False, precision=4, trim='k'),\n                     \"1.0000\")\n\n        assert equal(fpos(f('1.', tp), unique=False, precision=4, trim='.'),\n                     \"1.\")\n        assert equal(fpos(f('1.2', tp), unique=False, precision=4, trim='.'),\n                     \"1.2\" if tp is not np.float16 else \"1.2002\")\n\n        assert equal(fpos(f('1.', tp), unique=False, precision=4, trim='0'),\n                     \"1.0\")\n        assert equal(fpos(f('1.2', tp), unique=False, precision=4, trim='0'),\n                     \"1.2\" if tp is not np.float16 else \"1.2002\")\n        assert equal(fpos(f('1.', tp), trim='0'), \"1.0\")\n\n        assert equal(fpos(f('1.', tp), unique=False, precision=4, trim='-'),\n                     \"1\")\n        assert equal(fpos(f('1.2', tp), unique=False, precision=4, trim='-'),\n                     \"1.2\" if tp is not np.float16 else \"1.2002\")\n        assert equal(fpos(f('1.', tp), trim='-'), \"1\")\n        assert equal(fpos(f('1.001', tp), precision=1, trim='-'), \"1\")\n\ntest_dragon4_interface()\n\n@test\ndef test_fusion_coercion():\n    # more extensive tests in test_fusion.codon\n    a = np.array([1.0, 2.0])\n    b = np.multiply(a, np.float32(2)) + np.float32(0.5)\n    assert np.array_equal(b, [2.5, 4.5])\n\ntest_fusion_coercion()\n"
  },
  {
    "path": "test/numpy/test_sorting.codon",
    "content": "import numpy as np\nimport numpy.random as rnd\n\ng = rnd.default_rng(1234)\n\ndef less(a: T, b: T, T: type):\n    if T is float or T is float32:\n        return a < b or (b != b and a == a)\n    elif T is complex or T is complex64:\n        if a.real < b.real:\n            return a.imag == a.imag or b.imag != b.imag\n        elif a.real > b.real:\n            return b.imag != b.imag and a.imag == a.imag\n        elif a.real == b.real or (a.real != a.real and b.real != b.real):\n            return a.imag < b.imag or (b.imag != b.imag and a.imag == a.imag)\n        else:\n            return b.real != b.real\n    else:\n        return a < b\n\ndef less_equal(a, b):\n    return a == b or less(a, b)\n\ndef check_sorted(vec):\n    for i in range(1, len(vec)):\n        if not less_equal(vec[i - 1], vec[i]):\n            return False\n    return True\n\ndef check_argsorted(vec, tosort):\n    for i in range(1, len(vec)):\n        if not less_equal(vec[tosort[i - 1]], vec[tosort[i]]):\n            return False\n    return True\n\ndef gen_data(generator, length: int, dtype: type):\n    if dtype is float or dtype is float32:\n        data = generator.normal(100, 10, size=length).astype(dtype)\n    elif dtype is complex or dtype is complex64:\n        F = type(dtype().real)\n        data = generator.normal(1000, 100, size=length).astype(\n            F) + generator.normal(100, 10, size=length).astype(F) * 1j\n    elif isinstance(dtype, UInt):\n        data = generator.integers(0, 100000, size=length).astype(dtype)\n    else:\n        data = generator.integers(-100000, 100000, size=length).astype(dtype)\n\n    return data\n\n@test\ndef test_sort(generator, length: int, kind: str, dtype: type = int):\n    print(f'TEST kind={kind} length={length} dtype={dtype.__name__}')\n    data = gen_data(generator, length, dtype)\n    data_copy = data.copy()\n    assert check_sorted(np.sort(data, kind=kind))\n    assert check_argsorted(data, np.argsort(data, kind=kind))\n    assert (data == data_copy).all()\n    data.sort(kind=kind)\n    assert check_sorted(data)\n\ndef test_sorts(dtype: type):\n    for kind in ('quick', 'merge', 'heap'):\n        for length in (0, 1, 10, 101, 1111, 12345, 1000000):\n            test_sort(g, length, kind, dtype)\n\ndef check_partitioned(vec, kth):\n    if isinstance(kth, int):\n        return check_partitioned(vec, (kth, ))\n\n    s = np.sort(vec)\n    for k in kth:\n        if vec[k] != s[k]:\n            return False\n    return True\n\ndef check_apartitioned(vec, kth, apart):\n    if isinstance(kth, int):\n        return check_apartitioned(vec, (kth, ), apart)\n\n    s = np.sort(vec)\n    for k in kth:\n        if vec[apart[k]] != s[k]:\n            return False\n    return True\n\n@test\ndef test_partition_specific(generator, length, kth):\n    data = gen_data(generator, length, float)\n    part = np.partition(data, kth)\n    assert check_partitioned(part, kth)\n    data.partition(kth)\n    assert check_partitioned(data, kth)\n\ndef test_partition():\n    for length in (101, 1111, 12345, 1000000):\n        for kth in (0, 42, -1, (0, -1), (99, -99), (10, 20, 30 - 10)):\n            test_partition_specific(g, length, kth)\n\n@test\ndef test_apartition_specific(generator, length, kth):\n    data = gen_data(generator, length, float)\n    apart = np.argpartition(data, kth)\n    assert check_apartitioned(data, kth, apart)\n    apart = data.argpartition(kth)\n    assert check_apartitioned(data, kth, apart)\n\ndef test_apartition():\n    for length in (101, 1111, 12345, 1000000):\n        for kth in (0, 42, -1, (0, -1), (99, -99), (10, 20, 30 - 10)):\n            test_apartition_specific(g, length, kth)\n\n@test\ndef test_sorts_along_axis():\n    a = np.array([[2, 22, 222], [1, 11, 111], [3, 33, 333]])\n    assert (np.sort(a, axis=0) == np.array([[1, 11, 111], [2, 22, 222],\n                                            [3, 33, 333]])).all()\n    assert (np.partition(a, kth=1, axis=0)[1] == np.array([2, 22, 222])).all()\n    assert (np.argsort(a, axis=0) == np.array([[1, 1, 1], [0, 0, 0],\n                                               [2, 2, 2]])).all()\n    assert (np.argpartition(a, kth=1, axis=0)[1] == np.array([0, 0, 0])).all()\n    assert (a.argsort(axis=0) == np.array([[1, 1, 1], [0, 0, 0], [2, 2,\n                                                                  2]])).all()\n    assert (a.argpartition(kth=1, axis=0)[1] == np.array([0, 0, 0])).all()\n\n    a.sort(axis=0)\n    assert (a == np.array([[1, 11, 111], [2, 22, 222], [3, 33, 333]])).all()\n\n    a = np.array([[2, 22, 222], [1, 11, 111], [3, 33, 333]])\n    a.partition(kth=1, axis=0)\n    assert (a[1] == np.array([2, 22, 222])).all()\n\n@test\ndef test_sort_complex():\n    assert (np.sort_complex([5, 3, 6, 2, 1]) == np.array(\n        [1. + 0.j, 2. + 0.j, 3. + 0.j, 5. + 0.j, 6. + 0.j])).all()\n    assert (np.sort_complex([\n        1 + 2j, 2 - 1j, 3 - 2j, 3 - 3j, 3 + 5j\n    ]) == np.array([1. + 2.j, 2. - 1.j, 3. - 3.j, 3. - 2.j, 3. + 5.j])).all()\n\n@test\ndef test_lexsort():\n    a = [1, 5, 1, 4, 3, 4, 4]\n    b = [9, 4, 0, 4, 0, 2, 1]\n    assert np.lexsort(a) == 0\n    assert (np.lexsort((a, )) == np.array([0, 2, 4, 3, 5, 6, 1])).all()\n    assert (np.lexsort((a, b)) == np.array([2, 4, 6, 5, 3, 1, 0])).all()\n    assert (np.lexsort((b, a)) == np.array([2, 0, 4, 6, 5, 3, 1])).all()\n    assert np.lexsort(np.array(a)) == 0\n    assert (np.lexsort(np.array([a])) == np.array([0, 2, 4, 3, 5, 6, 1])).all()\n    assert (np.lexsort(np.array([a, b])) == np.array([2, 4, 6, 5, 3, 1,\n                                                      0])).all()\n    assert (np.lexsort(np.array([b, a])) == np.array([2, 0, 4, 6, 5, 3,\n                                                      1])).all()\n    assert (np.lexsort(np.array([[a, b],\n                                 [a, b]])) == np.array([[0, 2, 4, 3, 5, 6, 1],\n                                                        [2, 4, 6, 5, 1, 3,\n                                                         0]])).all()\n    assert (np.lexsort(np.array([[a, b], [a, b]]),\n                       axis=0) == np.array([[0, 1, 1, 0, 1, 1, 1],\n                                            [1, 0, 0, 1, 0, 0, 0]])).all()\n    assert (np.lexsort(np.array([[a, b], [a, b]]).T) == np.array([[0, 1],\n                                                                  [0,\n                                                                   1]])).all()\n    assert (np.lexsort(np.array([[a, b], [a, b]]).T,\n                       axis=0) == np.array([[1, 1], [0, 0]])).all()\n\ntest_sorts(int)\ntest_sorts(u64)\ntest_sorts(u32)\ntest_sorts(i32)\ntest_sorts(u8)\ntest_sorts(i8)\ntest_sorts(u128)\ntest_sorts(i128)\ntest_sorts(float)\ntest_sorts(float32)\ntest_sorts(complex)\ntest_sorts(complex64)\ntest_partition()\ntest_apartition()\ntest_sorts_along_axis()\ntest_sort_complex()\ntest_lexsort()\n"
  },
  {
    "path": "test/numpy/test_statistics.codon",
    "content": "import numpy as np\n\n@test\ndef test_average(a,\n                 expected,\n                 axis=None,\n                 weights=None,\n                 returned: Literal[bool] = False,\n                 keepdims: Literal[bool] = False):\n    if isinstance(expected, int) or isinstance(expected, float):\n        assert np.average(a,\n                          axis=axis,\n                          weights=weights,\n                          returned=returned,\n                          keepdims=keepdims) == expected\n    else:\n        assert (np.average(a,\n                           axis=axis,\n                           weights=weights,\n                           returned=returned,\n                           keepdims=keepdims) == expected).all()\n\ntest_average([5], 5)\ntest_average([2, 2, 2, 2, 2], 2)\ntest_average(np.arange(-5, 0), -3)\ntest_average(np.arange(1, 5), 2.5)\ntest_average([-1, 2, -3, 4, -5], -0.6)\ntest_average(np.arange(1, 11), 4.0, weights=np.arange(10, 0, -1))\ntest_average(np.arange(6).reshape((3, 2)),\n             np.array([0.75, 2.75, 4.75]),\n             axis=1,\n             weights=[1. / 4, 3. / 4])\ntest_average(np.arange(6).reshape((3, 2)),\n             np.array([[0.5], [2.5], [4.5]]),\n             axis=1,\n             keepdims=True)\nw1 = [0, 1, 1, 1, 1, 0]\nw2 = [[0, 1, 1, 1, 1, 0], [1, 0, 0, 0, 0, 1]]\ny = np.array([[0., 1., 2., 3., 4., 5.], [0., 2., 4., 6., 8., 10.]])\ntest_average(np.arange(6), 2.5, axis=0, weights=w1)\ntest_average(y, np.array([0., 1.5, 3., 4.5, 6., 7.5]), axis=0)\ntest_average(y, np.array([2.5, 5.]), axis=1)\ntest_average(y, 3.3333333333333335, weights=w2)\ntest_average(y, [0., 1., 2., 3., 4., 10.], axis=0, weights=w2)\ny = np.arange(12).reshape(2, 2, 3)\nw = np.array([0., 0., 1., .5, .5, 0., 0., .5, .5, 1., 0., 0.]).reshape(2, 2, 3)\ntest_average(y, np.array([7., 5.5, 4.]), axis=(0, 1), weights=w)\n\ndef test_average2():\n    assert np.isnan(np.average(np.empty(0, float)))\n    assert np.isnan(np.average([1, 2, np.nan, 4, 5]))\n    assert np.isinf(np.average([1, 2, np.inf, 4, 5]))\n    assert np.isnan(np.average([1, 2, np.nan, np.inf, 4, 5]))\n    assert np.average(np.arange(1, 11),\n                      weights=np.arange(10, 0, -1),\n                      returned=True) == (4.0, 55.0)\n    assert (np.average(np.array([[1, 2, 3], [4, 5, 6]]), axis=1,\n                       returned=True)[0] == np.array([2., 5.])).all()\n    assert (np.average(np.array([[1, 2, 3], [4, 5, 6]]), axis=1,\n                       returned=True)[1] == np.array([3., 3.])).all()\n    a = np.arange(6)\n    b = np.arange(6) * 3\n    assert (np.average([[a, b], [b, a]], axis=1, returned=True)[0] == np.array(\n        [[0., 2., 4., 6., 8., 10.], [0., 2., 4., 6., 8., 10.]])).all()\n    assert (np.average([[a, b], [b, a]], axis=1,\n                       returned=True)[1] == np.array([[2., 2., 2., 2., 2., 2.],\n                                                      [2., 2., 2., 2., 2.,\n                                                       2.]])).all()\n    assert (np.average([[a, b], [b, a]], axis=1, keepdims=True,\n                       returned=True)[0] == np.array(\n                           [[[0., 2., 4., 6., 8., 10.]],\n                            [[0., 2., 4., 6., 8., 10.]]])).all()\n    x = np.arange(6).reshape(2, 3)\n    w2 = [[0, 0, 1], [1, 2, 3]]\n    assert (np.round(\n        np.average(x, axis=1, keepdims=True, returned=True, weights=w2)[0],\n        8) == np.round(np.array([[2.], [4.33333333]]), 8)).all()\n    assert (np.average(x, axis=1, keepdims=True, returned=True,\n                       weights=w2)[1] == np.array([[1.], [6.]])).all()\n\ntest_average2()\n\n@test\ndef test_cov(m,\n             expected,\n             y=None,\n             rowvar=True,\n             bias=False,\n             ddof: Optional[int] = None,\n             fweights=None,\n             aweights=None,\n             dtype: type = NoneType):\n    assert np.isnan(np.cov(np.empty(0, float)))\n    assert np.isnan(np.cov(np.array([2])))\n    assert np.asarray(\n        np.round(\n            np.cov(m,\n                   y=y,\n                   rowvar=rowvar,\n                   bias=bias,\n                   ddof=ddof,\n                   fweights=fweights,\n                   aweights=aweights,\n                   dtype=dtype), 8) == expected).all()\n\ntest_cov(\n    np.array([[0, 2], [1, 1], [2, 0]]).T, np.array([[1., -1.], [-1., 1.]]))\ntest_cov(np.array([1, 2, 3, 4, 5]), np.array(2.5))\ntest_cov(np.array([1, 2, 3, 4, 5]), np.array(2.5), rowvar=False)\ntest_cov(np.array([1, 2, 3, 4, 5]), np.array(10.), ddof=4)\ntest_cov(np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),\n         np.array([[1., 1., 1.], [1., 1., 1.], [1., 1., 1.]]))\ntest_cov(np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),\n         np.array([[9., 9., 9.], [9., 9., 9.], [9., 9., 9.]]),\n         rowvar=False)\ntest_cov(np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),\n         np.array([[0.66666667, 0.66666667, 0.66666667],\n                   [0.66666667, 0.66666667, 0.66666667],\n                   [0.66666667, 0.66666667, 0.66666667]]),\n         rowvar=True,\n         bias=True)\ntest_cov(np.array([1, 2, 3, 4, 5]), np.array(2.), bias=True)\ntest_cov(np.array([1, 2, 3]),\n         np.array([[1., 0.5], [0.5, 0.25]]),\n         y=np.array([0.5, 1.0, 1.5]))\ntest_cov(np.array([1, 2, 3]),\n         np.array(0.98095238),\n         fweights=np.array([6, 1, 8]))\ntest_cov(np.array([1, 2, 3]),\n         np.array(1.66129032),\n         aweights=np.array([6, 1, 8]))\ntest_cov(np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]),\n         np.array([[0.475, 0.475, 0.475], [0.475, 0.475, 0.475],\n                   [0.475, 0.475, 0.475]]),\n         fweights=np.array([1, 2, 3]),\n         aweights=np.array([0.5, 1.0, 1.5]))\ntest_cov(np.array([1, 2, 3, 4, 5]),\n         np.array(1.74193548 + 0.j),\n         aweights=np.array([0.1, 0.2, 0.3, 0.2, 0.1]),\n         dtype=complex)\ntest_cov(np.array([[1 + 0j, 2 + 0j, 3 + 0j], [1j, 2j, 3j]]),\n         np.array([[1. + 0.j, 0. - 1.j], [0. + 1.j, 1. + 0.j]]))\ntest_cov(np.array([[1, 2, 3]]),\n         np.array([[1. + 0j, -1.j], [1.j, 1. + 0j]]),\n         y=np.array([[1j, 2j, 3j]]))\ntest_cov(np.array([0.3942, 0.5969, 0.7730, 0.9918, 0.7964]),\n         np.array(0.05084135))\n\n@test\ndef test_corrcoef(x, expected, y=None, rowvar=True, dtype: type = NoneType):\n    assert np.isnan(np.corrcoef(np.empty(0, float)))\n    assert np.isnan(np.corrcoef(np.array([2])))\n    if isinstance(expected, int) or isinstance(expected, float):\n        assert np.corrcoef(x, y=y, rowvar=rowvar, dtype=dtype) == expected\n    else:\n        assert (np.round(np.corrcoef(x, y=y, rowvar=rowvar, dtype=dtype),\n                         6) == np.round(expected, 6)).all()\n\ntest_corrcoef(\n    np.array([[0.77395605, 0.43887844, 0.85859792],\n              [0.69736803, 0.09417735, 0.97562235],\n              [0.7611397, 0.78606431, 0.12811363]]),\n    np.array([[1., 0.99256089, -0.68080986], [0.99256089, 1., -0.76492172],\n              [-0.68080986, -0.76492172, 1.]]))\ntest_corrcoef(\n    np.array([[0.77395605, 0.43887844, 0.85859792],\n              [0.69736803, 0.09417735, 0.97562235],\n              [0.7611397, 0.78606431, 0.12811363]]),\n    np.array(\n        [[1., 0.99256089, -0.68080986, 0.75008178, -0.934284, -0.99004057],\n         [0.99256089, 1., -0.76492172, 0.82502011, -0.97074098, -0.99981569],\n         [-0.68080986, -0.76492172, 1., -0.99507202, 0.89721355, 0.77714685],\n         [0.75008178, 0.82502011, -0.99507202, 1., -0.93657855, -0.83571711],\n         [-0.934284, -0.97074098, 0.89721355, -0.93657855, 1., 0.97517215],\n         [-0.99004057, -0.99981569, 0.77714685, -0.83571711, 0.97517215, 1.]]),\n    y=np.array([[0.45038594, 0.37079802, 0.92676499],\n                [0.64386512, 0.82276161, 0.4434142],\n                [0.22723872, 0.55458479, 0.06381726]]))\ntest_corrcoef(\n    np.array([[0.77395605, 0.43887844, 0.85859792],\n              [0.69736803, 0.09417735, 0.97562235],\n              [0.7611397, 0.78606431, 0.12811363]]),\n    np.array(\n        [[1., 0.77598074, -0.47458546, -0.75078643, -0.9665554, 0.22423734],\n         [0.77598074, 1., -0.92346708, -0.99923895, -0.58826587, -0.44069024],\n         [-0.47458546, -0.92346708, 1., 0.93773029, 0.23297648, 0.75137473],\n         [-0.75078643, -0.99923895, 0.93773029, 1., 0.55627469, 0.47536961],\n         [-0.9665554, -0.58826587, 0.23297648, 0.55627469, 1., -0.46666491],\n         [0.22423734, -0.44069024, 0.75137473, 0.47536961, -0.46666491, 1.]]),\n    y=np.array([[0.45038594, 0.37079802, 0.92676499],\n                [0.64386512, 0.82276161, 0.4434142],\n                [0.22723872, 0.55458479, 0.06381726]]),\n    rowvar=False)\ntest_corrcoef(np.array([[1 + 0j, 2 + 0j, 3 + 0j], [1j, 2j, 3j]]),\n              np.array([[1. + 0.j, 0. - 1.j], [0. + 1.j, 1. + 0.j]]))\ntest_corrcoef(np.array([[1, 2, 3]]),\n              np.array([[1. + 0j, -1.j], [1.j, 1. + 0j]]),\n              y=np.array([[1j, 2j, 3j]]))\ntest_corrcoef([[1e-100, 1e100], [1e100, 1e-100]],\n              np.array([[1., -1.], [-1., 1.]]))\n\n@test\ndef test_correlate(a, b, expected, mode: Literal[str] = 'valid'):\n    assert (np.round(np.correlate(a, b, mode=mode),\n                     8) == np.round(expected, 8)).all()\n\ntest_correlate([1, 2, 3], [0., 1., 0.5], np.array(3.5))\ntest_correlate([0 + 0j, 4.5 + 2.1 - 9j, 1 + 0j, 0 + 0.5j],\n               [1 + 1j, 2 + 0j, 3 - 1j], np.array([16.2 - 17.j, -0.9 - 14.1j]))\ntest_correlate([0.8, 4.6, 3.4], [19j, -8 + 5j, 2 + 5j, 13 - 1j, 9 - 10j],\n               np.array([92. + 34.6j, 47. - 23.6j, -30. - 55.2j]))\ntest_correlate([1, 2, 3], [0., 1., 0.5], np.array([2., 3.5, 3.]), mode=\"same\")\ntest_correlate([0 + 0j, 4.5 + 2.1 - 9j, 1 + 0j, 0 + 0.5j],\n               [1 + 1j, 2 + 0j, 3 - 1j],\n               np.array([28.8 - 20.4j, 16.2 - 17.j, -0.9 - 14.1j, 1. + 0.j]),\n               mode='same')\ntest_correlate([0.8, 4.6, 3.4], [19j, -8 + 5j, 2 + 5j, 13 - 1j, 9 - 10j],\n               np.array([\n                   51.8 + 46.8j, 92. + 34.6j, 47. - 23.6j, -30. - 55.2j,\n                   -27.2 - 104.4j\n               ]),\n               mode='same')\ntest_correlate([1, 2, 3], [0., 1., 0.5],\n               np.array([0.5, 2., 3.5, 3., 0.]),\n               mode=\"full\")\ntest_correlate([0 + 0j, 4.5 + 2.1 - 9j, 1 + 0j, 0 + 0.5j],\n               [1 + 1j, 2 + 0j, 3 - 1j],\n               np.array([\n                   0. + 0.j, 28.8 - 20.4j, 16.2 - 17.j, -0.9 - 14.1j, 1. + 0.j,\n                   0.5 + 0.5j\n               ]),\n               mode='full')\ntest_correlate([0.8, 4.6, 3.4], [19j, -8 + 5j, 2 + 5j, 13 - 1j, 9 - 10j],\n               np.array([\n                   7.2 + 8.j, 51.8 + 46.8j, 92. + 34.6j, 47. - 23.6j,\n                   -30. - 55.2j, -27.2 - 104.4j, 0. - 64.6j\n               ]),\n               mode='full')\ntest_correlate([1 + 1j, 2 + 0j, 3 - 1j], [0 + 0j, 1 + 0j, 0 + 0.5j],\n               np.array(\n                   [0.5 - 0.5j, 1.0 + 0.j, 1.5 - 1.5j, 3.0 - 1.j, 0.0 + 0.j]),\n               mode='full')\ntest_correlate([0 + 0j, 1 + 0j, 0 + 0.5j], [1 + 1j, 2 + 0j, 3 - 1j],\n               np.array([0.j, 3. + 1.j, 1.5 + 1.5j, 1. + 0.j, 0.5 + 0.5j]),\n               mode='full')\ntest_correlate(np.array([1 + 0j, 2 + 0j, 3 + 0j, 4 + 1j]),\n               np.array([-1 + 0j, -2j, 3 + 1j]),\n               np.array([8. + 1.j, 11. + 5.j]))\n\n@test\ndef test_correlate2():\n    # Integer inputs\n    a = np.array([1, 2, 3])\n    b = np.array([4, 5, 6])\n    assert np.allclose(np.correlate(a, b, mode=\"valid\"), [32])\n    assert np.allclose(np.correlate(a, b, mode=\"same\"), [17, 32, 23])\n    assert np.allclose(np.correlate(a, b, mode=\"full\"), [6, 17, 32, 23, 12])\n\n    # Floating-point inputs\n    a = np.array([1.5, 2.5, 3.5])\n    b = np.array([4.0, 5.0, 6.0])\n    assert np.allclose(np.correlate(a, b, mode=\"valid\"), [39.5])\n    assert np.allclose(np.correlate(a, b, mode=\"same\"), [22.5, 39.5, 27.5])\n    assert np.allclose(np.correlate(a, b, mode=\"full\"), [9.0, 22.5, 39.5, 27.5, 14.0])\n\n    # Complex numbers\n    a = np.array([1+2j, 3+4j])\n    b = np.array([5+6j, 7+8j])\n    assert np.allclose(np.correlate(a, b, mode=\"valid\"), [70+8j])\n    assert np.allclose(np.correlate(a, b, mode=\"same\"), [23+6j, 70+8j])\n    assert np.allclose(np.correlate(a, b, mode=\"full\"), [23+6j, 70+8j, 39+2j])\n\n    # Different-length arrays\n    a = np.array([1, 2, 3, 4])\n    b = np.array([0, 1])\n    assert np.allclose(np.correlate(a, b, mode=\"valid\"), [2, 3, 4])\n    assert np.allclose(np.correlate(a, b, mode=\"same\"), [1, 2, 3, 4])\n    assert np.allclose(np.correlate(a, b, mode=\"full\"), [1, 2, 3, 4, 0])\n    a = np.array([0, 1])\n    b = np.array([1, 2, 3, 4])\n    assert np.allclose(np.correlate(a, b, mode=\"valid\")[::-1], [2, 3, 4])\n    assert np.allclose(np.correlate(a, b, mode=\"same\")[::-1], [1, 2, 3, 4])\n    assert np.allclose(np.correlate(a, b, mode=\"full\")[::-1], [1, 2, 3, 4, 0])\n\n    # Large array test\n    a = np.arange(20)\n    b = np.arange(10)\n    expected_valid = np.array([np.sum(a[i : i + len(b)] * b) for i in range(len(a) - len(b) + 1)])\n    expected_full = np.correlate(a, b, mode=\"full\")\n    expected_same = np.correlate(a, b, mode=\"same\")\n    assert np.allclose(np.correlate(a, b, mode=\"valid\"), expected_valid)\n    assert np.allclose(np.correlate(a, b, mode=\"full\"), expected_full)\n    assert np.allclose(np.correlate(a, b, mode=\"same\"), expected_same)\n\n    # Different dtypes (int and float)\n    a = np.array([1, 2, 3], dtype=int)\n    b = np.array([1.5, 2.5, 3.5], dtype=float)\n    assert np.allclose(np.correlate(a, b, mode=\"valid\"), [17.0])\n    assert np.allclose(np.correlate(a, b, mode=\"same\"), [9.5, 17.0, 10.5])\n    assert np.allclose(np.correlate(a, b, mode=\"full\"), [3.5, 9.5, 17.0, 10.5, 4.5])\n\n    # Edge case: Single-element arrays\n    a = np.array([5])\n    b = np.array([10])\n    assert np.allclose(np.correlate(a, b, mode=\"valid\"), [50])\n    assert np.allclose(np.correlate(a, b, mode=\"same\"), [50])\n    assert np.allclose(np.correlate(a, b, mode=\"full\"), [50])\n\ntest_correlate2()\n\n@test\ndef test_bincount(x, expected, weights=None, minlength=0):\n    assert (np.bincount(x, weights=weights,\n                        minlength=minlength) == expected).all()\n\ntest_bincount([1], np.array([0, 1]))\ntest_bincount(np.array([0, 1, 2, 3, 4]), np.array([1, 1, 1, 1, 1]))\ntest_bincount(\n    np.array([0, 1, 1, 3, 2, 1, 7, 23]),\n    np.array([\n        1, 3, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1\n    ]))\ntest_bincount(np.array([0, 1, 1, 2, 2, 2]),\n              np.array([0.3, 0.7, 1.1]),\n              weights=np.array([0.3, 0.5, 0.2, 0.7, 1., -0.6]))\ntest_bincount(np.array([0, 1, 1, 3, 2, 1, 7]),\n              np.array([1, 3, 1, 1, 0, 0, 0, 1]),\n              minlength=2)\ntest_bincount(np.array([0, 1, 1, 3, 2, 1, 7]),\n              np.array([\n                  1, 3, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n                  0, 0\n              ]),\n              minlength=22)\ntest_bincount(np.array([1, 2, 4, 5, 2]), np.array([0, 0.2, 0.5, 0, 0.5, 0.1]),\n              np.array([0.2, 0.3, 0.5, 0.1, 0.2]))\ntest_bincount(np.array([0, 1, 1, 2, 2, 3, 3]),\n              np.array([1, 2, 2, 2]),\n              minlength=2)\ntest_bincount(np.array([0, 1, 1, 2, 2, 3, 3]),\n              np.array([1, 2, 2, 2]),\n              minlength=0)\ntest_bincount(np.array([1, 2, 4, 5, 2]),\n              np.array([0, 0.2, 0.5, 0, 0.5, 0.1, 0, 0]),\n              np.array([0.2, 0.3, 0.5, 0.1, 0.2]), 8)\ntest_bincount(np.empty(0, int), np.empty(0, int))\n\n@test\ndef test_digitize(x, bins, expected, right=False):\n    assert (np.digitize(x, bins, right=right) == expected).all()\n\ntest_digitize(np.array([[0, 1, 1], [3, 2, 7]]), np.array([0, 1, 2, 4, 8]),\n              np.array([[1, 2, 2], [3, 3, 4]]))\ntest_digitize(np.array([0.2, 6.4, 3.0, 1.6]),\n              np.array([0.0, 1.0, 2.5, 4.0, 10.0]), np.array([1, 4, 3, 2]))\ntest_digitize(np.array([1.2, 10.0, 12.4, 15.5, 20.]),\n              np.array([0, 5, 10, 15, 20]),\n              np.array([1, 2, 3, 4, 4]),\n              right=True)\ntest_digitize(np.array([1.2, 10.0, 12.4, 15.5, 20.]),\n              np.array([0, 5, 10, 15, 20]), np.array([1, 3, 3, 4, 5]))\ntest_digitize(np.array([[0, 1, 1], [3, 2, 7]]),\n              np.array([0.0, 1.0, 2.5, 4.0, 10.0]),\n              np.array([[0, 1, 1], [3, 2, 4]]),\n              right=True)\ntest_digitize(np.arange(-6, 5), np.arange(-5, 5), np.arange(11))\ntest_digitize(np.arange(5, -6, -1), np.arange(5, -5, -1), np.arange(11))\ntest_digitize(np.arange(-6, 5), np.arange(-6, 4), np.arange(11), True)\nx = [-1, 0, 1, 2]\nbins = [0, 0, 1]\ntest_digitize(x, bins, np.array([0, 2, 3, 3]), False)\ntest_digitize(x, bins, np.array([0, 0, 2, 3]), True)\nbins = [1, 1, 0]\ntest_digitize(x, bins, np.array([3, 2, 0, 0]), False)\ntest_digitize(x, bins, np.array([3, 3, 2, 0]), True)\nbins = [1, 1, 1, 1]\ntest_digitize(x, bins, np.array([0, 0, 4, 4]), False)\ntest_digitize(x, bins, np.array([0, 0, 0, 4]), True)\n\n@test\ndef test_histogram_bin_edges(a, expected, bins=10, range=None, weights=None):\n    assert (np.round(\n        np.histogram_bin_edges(a, bins=bins, range=range, weights=weights),\n        8) == np.round(expected, 8)).all()\n\ntest_histogram_bin_edges(\n    np.empty(0, float),\n    np.array([0., 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.]))\ntest_histogram_bin_edges(\n    [1], np.array([0.5, 0.6, 0.7, 0.8, 0.9, 1., 1.1, 1.2, 1.3, 1.4, 1.5]))\ntest_histogram_bin_edges(\n    [1, 100],\n    np.array([1., 10.9, 20.8, 30.7, 40.6, 50.5, 60.4, 70.3, 80.2, 90.1, 100.]))\ntest_histogram_bin_edges(\n    np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n    np.array([0., 0.5, 1., 1.5, 2., 2.5, 3., 3.5, 4., 4.5, 5.]))\ntest_histogram_bin_edges(np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n                         np.array([\n                             0., 0.38461538, 0.76923077, 1.15384615,\n                             1.53846154, 1.92307692, 2.30769231, 2.69230769,\n                             3.07692308, 3.46153846, 3.84615385, 4.23076923,\n                             4.61538462, 5.\n                         ]),\n                         bins=13)\ntest_histogram_bin_edges(np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n                         np.array([-1., -0.33333333, 0.33333333, 1.]),\n                         bins=3,\n                         range=(-1, 1))\ntest_histogram_bin_edges(np.array([0, 0, 1, 2, 3, 5]),\n                         np.array([-5., 0., 5., 10.]),\n                         bins=3,\n                         range=(-5, 10),\n                         weights=[8, 3, 1, 5, -9, 2])\n\ntest_histogram_bin_edges(np.empty(0, float), np.array([0., 1.]), bins='doane')\ntest_histogram_bin_edges([1], np.array([0.5, 1.5]), bins='doane')\ntest_histogram_bin_edges([1, 100], np.array([1., 100.]), bins='doane')\ntest_histogram_bin_edges(np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n                         np.array([0., 1., 2., 3., 4., 5.]),\n                         bins='doane')\ntest_histogram_bin_edges(\n    np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n    np.array([-1., -0.8, -0.6, -0.4, -0.2, 0., 0.2, 0.4, 0.6, 0.8, 1.]),\n    bins='doane',\n    range=(-1, 1))\n\ntest_histogram_bin_edges(np.empty(0, float), np.array([0., 1.]), bins='rice')\ntest_histogram_bin_edges([1], np.array([0.5, 1.5]), bins='rice')\ntest_histogram_bin_edges([1, 100], np.array([1., 34., 67., 100.]), bins='rice')\ntest_histogram_bin_edges(np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n                         np.array([0., 1., 2., 3., 4., 5.]),\n                         bins='rice')\ntest_histogram_bin_edges(np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n                         np.array([\n                             -1., -0.71428571, -0.42857143, -0.14285714,\n                             0.14285714, 0.42857143, 0.71428571, 1.\n                         ]),\n                         bins='rice',\n                         range=(-1, 1))\n\ntest_histogram_bin_edges(np.empty(0, float), np.array([0., 1.]), bins='scott')\ntest_histogram_bin_edges([7], np.array([6.5, 7.5]), bins='scott')\ntest_histogram_bin_edges([1, 100], np.array([1., 100.]), bins='scott')\ntest_histogram_bin_edges(np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n                         np.array([0., 2.5, 5.]),\n                         bins='scott')\ntest_histogram_bin_edges(np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n                         np.array([-1., -0.33333333, 0.33333333, 1.]),\n                         bins='scott',\n                         range=(-1, 1))\n\ntest_histogram_bin_edges(np.empty(0, float), np.array([0., 1.]), bins='sqrt')\ntest_histogram_bin_edges([3], np.array([2.5, 3.5]), bins='sqrt')\ntest_histogram_bin_edges([1, 100], np.array([1., 50.5, 100.]), bins='sqrt')\ntest_histogram_bin_edges(np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n                         np.array([0., 1.66666667, 3.33333333, 5.]),\n                         bins='sqrt')\ntest_histogram_bin_edges(np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n                         np.array([-1., -0.5, 0., 0.5, 1.]),\n                         bins='sqrt',\n                         range=(-1, 1))\n\ntest_histogram_bin_edges(np.empty(0, float),\n                         np.array([0., 1.]),\n                         bins='sturges')\ntest_histogram_bin_edges([3], np.array([2.5, 3.5]), bins='sturges')\ntest_histogram_bin_edges([1, 100], np.array([1., 50.5, 100.]), bins='sturges')\ntest_histogram_bin_edges(np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n                         np.array([0., 1., 2., 3., 4., 5.]),\n                         bins='sturges')\ntest_histogram_bin_edges(\n    np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n    np.array([-1., -0.66666667, -0.33333333, 0., 0.33333333, 0.66666667, 1.]),\n    bins='sturges',\n    range=(-1, 1))\n\ntest_histogram_bin_edges(np.empty(0, float), np.array([0., 1.]), bins='fd')\ntest_histogram_bin_edges([3], np.array([2.5, 3.5]), bins='fd')\ntest_histogram_bin_edges([1, 100], np.array([1., 50.5, 100.]), bins='fd')\ntest_histogram_bin_edges(np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n                         np.array([0., 2.5, 5.]),\n                         bins='fd')\ntest_histogram_bin_edges(\n    np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n    np.array([\n        -12., -9.17857143, -6.35714286, -3.53571429, -0.71428571, 2.10714286,\n        4.92857143, 7.75, 10.57142857, 13.39285714, 16.21428571, 19.03571429,\n        21.85714286, 24.67857143, 27.5, 30.32142857, 33.14285714, 35.96428571,\n        38.78571429, 41.60714286, 44.42857143, 47.25, 50.07142857, 52.89285714,\n        55.71428571, 58.53571429, 61.35714286, 64.17857143, 67.\n    ]),\n    bins='fd',\n    range=(-12, 67))\n\ntest_histogram_bin_edges(np.empty(0, float), np.array([0., 1.]), bins='auto')\ntest_histogram_bin_edges([3], np.array([2.5, 3.5]), bins='auto')\ntest_histogram_bin_edges([1, 100], np.array([1., 50.5, 100.]), bins='auto')\ntest_histogram_bin_edges(np.array([0, 0, 0, 1, 2, 3, 3, 4, 5]),\n                         np.array([0., 1., 2., 3., 4., 5.]),\n                         bins='auto')\ntest_histogram_bin_edges(\n    np.array([0, 0, 2, 3, 3, 4]),\n    np.array([\n        -43., -41.88888889, -40.77777778, -39.66666667, -38.55555556,\n        -37.44444444, -36.33333333, -35.22222222, -34.11111111, -33.,\n        -31.88888889, -30.77777778, -29.66666667, -28.55555556, -27.44444444,\n        -26.33333333, -25.22222222, -24.11111111, -23., -21.88888889,\n        -20.77777778, -19.66666667, -18.55555556, -17.44444444, -16.33333333,\n        -15.22222222, -14.11111111, -13., -11.88888889, -10.77777778,\n        -9.66666667, -8.55555556, -7.44444444, -6.33333333, -5.22222222,\n        -4.11111111, -3., -1.88888889, -0.77777778, 0.33333333, 1.44444444,\n        2.55555556, 3.66666667, 4.77777778, 5.88888889, 7., 8.11111111,\n        9.22222222, 10.33333333, 11.44444444, 12.55555556, 13.66666667,\n        14.77777778, 15.88888889, 17.\n    ]),\n    bins='auto',\n    range=(-43, 17))\n\ntest_histogram_bin_edges(np.empty(0, float), np.array([0., 1.]), bins='stone')\ntest_histogram_bin_edges([3], np.array([2.5, 3.5]), bins='stone')\ntest_histogram_bin_edges([1, 100], np.array([1., 100.]), bins='stone')\ntest_histogram_bin_edges(np.array([0, 0, 1, 2, 3, 3, 4, 5]),\n                         np.array([0., 5.]),\n                         bins='stone')\ntest_histogram_bin_edges(\n    np.array([0, 0, 2, 3, 3, 4]),\n    np.array([\n        -4., -3.43243243, -2.86486486, -2.2972973, -1.72972973, -1.16216216,\n        -0.59459459, -0.02702703, 0.54054054, 1.10810811, 1.67567568,\n        2.24324324, 2.81081081, 3.37837838, 3.94594595, 4.51351351, 5.08108108,\n        5.64864865, 6.21621622, 6.78378378, 7.35135135, 7.91891892, 8.48648649,\n        9.05405405, 9.62162162, 10.18918919, 10.75675676, 11.32432432,\n        11.89189189, 12.45945946, 13.02702703, 13.59459459, 14.16216216,\n        14.72972973, 15.2972973, 15.86486486, 16.43243243, 17.\n    ]),\n    bins='stone',\n    range=(-4, 17))\n\n@test\ndef test_histogram(a,\n                   expected_hist,\n                   expected_edges,\n                   bins=10,\n                   range=None,\n                   density: Literal[bool] = False,\n                   weights=None):\n    hist, bin_edges = np.histogram(a,\n                                   bins=bins,\n                                   range=range,\n                                   density=density,\n                                   weights=weights)\n    assert (np.round(hist, 8) == np.round(expected_hist, 8)).all()\n    assert (np.round(bin_edges, 8) == np.round(expected_edges, 8)).all()\n\ntest_histogram(np.empty(0, float), np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),\n               np.array([0., 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.]))\ntest_histogram([1, 2, 3, 4],\n               np.array([3, 1]),\n               np.array([0, 4, 7]),\n               bins=[0, 4, 7],\n               range=(-3, 3))\ntest_histogram([1, 2, 3, 4],\n               np.array([-2.9, 12.]),\n               np.array([0, 4, 7]),\n               bins=[0, 4, 7],\n               weights=[0.6, 0.5, -4, 12])\ntest_histogram([0, 1, 2, 3],\n               np.array([1, 1, 1, 1]),\n               np.array([0, 1, 2, 3, 4]),\n               bins=[0, 1, 2, 3, 4])\ntest_histogram(\n    [0], np.array([0, 0, 0, 0, 0, 1, 0, 0, 0, 0]),\n    np.array([-0.5, -0.4, -0.3, -0.2, -0.1, 0., 0.1, 0.2, 0.3, 0.4, 0.5]))\ntest_histogram(np.linspace(0, 10, 100),\n               np.array([10, 10, 10, 10, 10, 10, 10, 10, 10, 10]),\n               np.array([0., 1., 2., 3., 4., 5., 6., 7., 8., 9., 10.]))\ntest_histogram([1, 2], np.array([2]), np.array([1., 2.]), bins=1)\ntest_histogram([1, 2, 3, 4, 5, 6, 7, 8],\n               np.array([\n                   0.17857143, 0.17857143, 0.17857143, 0., 0.17857143,\n                   0.17857143, 0., 0.17857143, 0.17857143, 0.17857143\n               ]),\n               np.array([1., 1.7, 2.4, 3.1, 3.8, 4.5, 5.2, 5.9, 6.6, 7.3, 8.]),\n               density=True)\ntest_histogram([1, 2, 3, 4, 5, 6, 7, 8],\n               np.array([0., 0.125, 0.125, 0.09375]),\n               np.array([0, 1, 3, 6, 10]),\n               bins=[0, 1, 3, 6, 10],\n               density=True)\ntest_histogram([1, 2, 3, 4, 5, 6, 7, 8],\n               np.array([0, 2, 3, 3]),\n               np.array([0, 1, 3, 6, 10]),\n               bins=[0, 1, 3, 6, 10],\n               density=False)\ntest_histogram([1, 2, 3, 4, 5, 6, 7, 8],\n               np.array([0., 0.125, 0.125, 0.]),\n               np.array([0, 1, 3, 6, np.inf]),\n               bins=[0, 1, 3, 6, np.inf],\n               density=True)\ntest_histogram([1, 2, 3, 4],\n               np.array([0.25, 0.]),\n               np.array([0.5, 1.5, np.inf]),\n               bins=[0.5, 1.5, np.inf],\n               density=True)\n#Test outliers\ntest_histogram(np.arange(10) + .5,\n               np.array([1, 1, 1, 1, 0, 1, 1, 1, 1, 1]),\n               np.array([0., 0.9, 1.8, 2.7, 3.6, 4.5, 5.4, 6.3, 7.2, 8.1, 9.]),\n               range=[0, 9])\ntest_histogram(np.arange(10) + .5,\n               np.array([1, 1, 1, 1, 0, 1, 1, 1, 1, 1]),\n               np.array([1., 1.9, 2.8, 3.7, 4.6, 5.5, 6.4, 7.3, 8.2, 9.1,\n                         10.]),\n               range=[1, 10])\ntest_histogram(np.arange(10) + .5,\n               np.array([\n                   0.12345679, 0.12345679, 0.12345679, 0.12345679, 0.,\n                   0.12345679, 0.12345679, 0.12345679, 0.12345679, 0.12345679\n               ]),\n               np.array([0., 0.9, 1.8, 2.7, 3.6, 4.5, 5.4, 6.3, 7.2, 8.1, 9.]),\n               range=[0, 9],\n               density=True)\ntest_histogram(np.arange(10) + .5,\n               np.array([\n                   0.01371742, 0.04115226, 0.06858711, 0.09602195, 0.,\n                   0.12345679, 0.15089163, 0.17832647, 0.20576132, 0.23319616\n               ]),\n               np.array([0., 0.9, 1.8, 2.7, 3.6, 4.5, 5.4, 6.3, 7.2, 8.1, 9.]),\n               range=[0, 9],\n               weights=np.arange(10) + .5,\n               density=True)\ntest_histogram(np.arange(10) + .5,\n               np.array([0.5, 1.5, 2.5, 3.5, 10., 6.5, 7.5, 8.5]),\n               np.array([0., 1.125, 2.25, 3.375, 4.5, 5.625, 6.75, 7.875, 9.]),\n               bins=8,\n               range=[0, 9],\n               weights=np.arange(10) + .5)\ntest_histogram(np.array([0., 0., 0., 1., 2., 3., 3., 4., 5.]),\n               np.array([\n                   0, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2,\n                   0, 0, 0, 0, 1, 0, 0, 0, 0, 1\n               ]),\n               np.array([\n                   -0.5, -0.31666667, -0.13333333, 0.05, 0.23333333,\n                   0.41666667, 0.6, 0.78333333, 0.96666667, 1.15, 1.33333333,\n                   1.51666667, 1.7, 1.88333333, 2.06666667, 2.25, 2.43333333,\n                   2.61666667, 2.8, 2.98333333, 3.16666667, 3.35, 3.53333333,\n                   3.71666667, 3.9, 4.08333333, 4.26666667, 4.45, 4.63333333,\n                   4.81666667, 5.\n               ]),\n               bins=30,\n               range=(-0.5, 5))\ntest_histogram(np.linspace(0, 10, 10),\n               np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]),\n               np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),\n               bins=np.arange(11),\n               weights=[0, 0, 0, 0, 0, 1, 1, 1, 1, 1])\ntest_histogram(np.arange(9),\n               np.array([0.2, 0.1, 0.1, 0.075]),\n               np.array([0, 1, 3, 6, 10]),\n               bins=[0, 1, 3, 6, 10],\n               weights=[2, 1, 1, 1, 1, 1, 1, 1, 1],\n               density=True)\ntest_histogram(np.array([1.3, 2.5, 2.3]),\n               np.array([1. + 2.j, 1. + 3.j]),\n               np.array([0, 2, 3]),\n               bins=[0, 2, 3],\n               weights=np.array([1. + 2.j, -1. + 1.j, 2. + 2.j]))\ntest_histogram(np.array([1.3, 2.5, 2.3]),\n               np.array([1. + 2.j, 1. + 3.j]),\n               np.array([1., 2., 3.]),\n               bins=2,\n               range=[1, 3],\n               weights=np.array([1. + 2.j, -1. + 1.j, 2. + 2.j]))\ntest_histogram(\n    np.array([1.3, 2.5, 2.3]),\n    np.array([\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,\n        0, 0, 0, 0\n    ]),\n    np.array([\n        -10., -9.8, -9.6, -9.4, -9.2, -9., -8.8, -8.6, -8.4, -8.2, -8., -7.8,\n        -7.6, -7.4, -7.2, -7., -6.8, -6.6, -6.4, -6.2, -6., -5.8, -5.6,\n        -5.4, -5.2, -5., -4.8, -4.6, -4.4, -4.2, -4., -3.8, -3.6,\n        -3.4, -3.2, -3., -2.8, -2.6, -2.4, -2.2, -2., -1.8, -1.6,\n        -1.4, -1.2, -1., -0.8, -0.6, -0.4, -0.2, 0., 0.2, 0.4, 0.6, 0.8, 1.,\n        1.2, 1.4, 1.6, 1.8, 2., 2.2, 2.4, 2.6, 2.8, 3., 3.2, 3.4, 3.6, 3.8, 4.,\n        4.2, 4.4, 4.6, 4.8, 5., 5.2, 5.4, 5.6, 5.8, 6., 6.2, 6.4, 6.6, 6.8, 7.,\n        7.2, 7.4, 7.6, 7.8, 8., 8.2, 8.4, 8.6, 8.8, 9., 9.2, 9.4, 9.6, 9.8, 10.\n    ]),\n    range=[-10, 10],\n    bins=100)\ntest_histogram(np.array([0., 0., 0., 1., 2., 3., 3., 4., 5.]),\n               np.array([\n                   0, 0, 3, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 2,\n                   0, 0, 0, 0, 1, 0, 0, 0, 0, 1\n               ]),\n               np.array([\n                   -0.5, -0.31666667, -0.13333333, 0.05, 0.23333333,\n                   0.41666667, 0.6, 0.78333333, 0.96666667, 1.15, 1.33333333,\n                   1.51666667, 1.7, 1.88333333, 2.06666667, 2.25, 2.43333333,\n                   2.61666667, 2.8, 2.98333333, 3.16666667, 3.35, 3.53333333,\n                   3.71666667, 3.9, 4.08333333, 4.26666667, 4.45, 4.63333333,\n                   4.81666667, 5.\n               ]),\n               bins=30,\n               range=(-0.5, 5))\ntest_histogram(np.ones(100),\n               np.array([100]),\n               np.array([0.5, 1.5]),\n               bins='scott')\ntest_histogram(np.ones(100), np.array([100]), np.array([0.5, 1.5]), bins='fd')\nxcenter = np.linspace(-10, 10, 50)\na = np.hstack((np.linspace(-110, -100, 5), xcenter))\ntest_histogram(a,\n               np.array([\n                   3, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 13,\n                   15, 14\n               ]),\n               np.array([\n                   -110., -104.28571429, -98.57142857, -92.85714286,\n                   -87.14285714, -81.42857143, -75.71428571, -70.,\n                   -64.28571429, -58.57142857, -52.85714286, -47.14285714,\n                   -41.42857143, -35.71428571, -30., -24.28571429,\n                   -18.57142857, -12.85714286, -7.14285714, -1.42857143,\n                   4.28571429, 10.\n               ]),\n               bins='fd')\ntest_histogram(a,\n               np.array([5, 0, 0, 0, 50]),\n               np.array([-110., -86., -62., -38., -14., 10.]),\n               bins='scott')\ntest_histogram(a,\n               np.array([5, 0, 0, 0, 0, 0, 0, 0, 0, 23, 27]),\n               np.array([\n                   -110., -99.09090909, -88.18181818, -77.27272727,\n                   -66.36363636, -55.45454545, -44.54545455, -33.63636364,\n                   -22.72727273, -11.81818182, -0.90909091, 10.\n               ]),\n               bins='doane')\ntest_histogram(a,\n               np.array([5, 0, 0, 0, 0, 50]),\n               np.array([-110., -90., -70., -50., -30., -10., 10.]),\n               bins='stone')\n\n@test\ndef test_histogram_fast(dtype: type):\n    data = np.array([1, 1, 2, 2, 3, 3, 4, 4, 4, 5], dtype=dtype)\n\n    if dtype is float or dtype is np.float32 or dtype is np.float16:\n        dt = dtype()\n    else:\n        dt = float()\n\n    bins_dtype = type(dt)\n\n    hist, bins = np.histogram(data)\n    assert np.array_equal(hist, [2, 0, 2, 0, 0, 2, 0, 3, 0, 1])\n    assert np.allclose(bins, [1. , 1.4, 1.8, 2.2, 2.6, 3. , 3.4, 3.8, 4.2, 4.6, 5. ])\n    assert hist.dtype is int\n    assert bins.dtype is bins_dtype\n\n    hist, bins = np.histogram(data, bins=1)\n    assert np.array_equal(hist, [10])\n    assert np.allclose(bins, [1., 5.])\n    assert hist.dtype is int\n    assert bins.dtype is bins_dtype\n\n    hist, bins = np.histogram(data, range=(1.5, 3.5))\n    assert np.array_equal(hist, [0, 0, 2, 0, 0, 0, 0, 2, 0, 0])\n    assert np.allclose(bins, [1.5, 1.7, 1.9, 2.1, 2.3, 2.5, 2.7, 2.9, 3.1, 3.3, 3.5])\n    assert hist.dtype is int\n    assert bins.dtype is bins_dtype\n\n    hist, bins = np.histogram(data, range=(5, 6))\n    assert np.array_equal(hist, [1, 0, 0, 0, 0, 0, 0, 0, 0, 0])\n    assert np.allclose(bins, [5. , 5.1, 5.2, 5.3, 5.4, 5.5, 5.6, 5.7, 5.8, 5.9, 6.])\n    assert hist.dtype is int\n    assert bins.dtype is bins_dtype\n\n    hist, bins = np.histogram(data, range=(1.5, 3.5), bins=5)\n    assert np.array_equal(hist, [0, 2, 0, 2, 0])\n    assert np.allclose(bins, [1.5, 1.9, 2.3, 2.7, 3.1, 3.5])\n    assert hist.dtype is int\n    assert bins.dtype is bins_dtype\n\n    hist, bins = np.histogram(data, bins=[-100., 2., 4., 4.2, 4.7, 5.0, 5.2])\n    assert np.array_equal(hist, [2, 4, 3, 0, 0, 1])\n    assert np.allclose(bins, [-100., 2., 4., 4.2, 4.7, 5., 5.2])\n    assert hist.dtype is int\n    assert bins.dtype is float\n\n    bins = np.array([-100., 2., 4., 4.2, 4.7, 5.0, 5.2], np.float32)\n    hist, bins = np.histogram(data, bins=bins)\n    assert np.array_equal(hist, [2, 4, 3, 0, 0, 1])\n    assert np.allclose(bins, [-100., 2., 4., 4.2, 4.7, 5., 5.2])\n    assert hist.dtype is int\n    assert bins.dtype is np.float32\n\n    hist, bins = np.histogram(data, bins=[-100., 2., 4., 4.2, 4.7, 5.0, 5.2], range=(2, 4))\n    assert np.array_equal(hist, [2, 4, 3, 0, 0, 1])\n    assert np.allclose(bins, [-100., 2., 4., 4.2, 4.7, 5., 5.2])\n    assert hist.dtype is int\n    assert bins.dtype is float\n\n    hist, bins = np.histogram(data, bins=[-100, 2, 4, 5, 100], range=(-1, 100))\n    assert np.array_equal(hist, [2, 4, 3, 1])\n    assert np.array_equal(bins, [-100,    2,    4,    5,  100])\n    assert hist.dtype is int\n    assert bins.dtype is int\n\n    data = np.array([2, 2, 2], dtype=dtype)\n\n    hist, bins = np.histogram(data)\n    assert np.array_equal(hist, [0, 0, 0, 0, 0, 3, 0, 0, 0, 0])\n    assert np.allclose(bins, [1.5, 1.6, 1.7, 1.8, 1.9, 2. , 2.1, 2.2, 2.3, 2.4, 2.5])\n    assert hist.dtype is int\n    assert bins.dtype is bins_dtype\n\n    hist, bins = np.histogram(data, bins=5)\n    assert np.array_equal(hist, [0, 0, 3, 0, 0])\n    assert np.allclose(bins, [1.5, 1.7, 1.9, 2.1, 2.3, 2.5])\n    assert hist.dtype is int\n    assert bins.dtype is bins_dtype\n\n    hist, bins = np.histogram(data, bins=5, range=(3, 4))\n    assert np.array_equal(hist, [0, 0, 0, 0, 0])\n    assert np.allclose(bins, [3. , 3.2, 3.4, 3.6, 3.8, 4.])\n    assert hist.dtype is int\n    assert bins.dtype is bins_dtype\n\n    data = np.empty(0, dtype=dtype)\n\n    hist, bins = np.histogram(data)\n    assert np.array_equal(hist, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0])\n    assert np.allclose(bins, [0. , 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.])\n    assert hist.dtype is int\n    assert bins.dtype is bins_dtype\n\n    hist, bins = np.histogram(data, bins=1)\n    assert np.array_equal(hist, [0])\n    assert np.allclose(bins, [0., 1.])\n    assert hist.dtype is int\n    assert bins.dtype is bins_dtype\n\n    data = np.arange(81, dtype=dtype) ** 0.2\n    data = data.reshape((3, 3, 3, 3))\n\n    hist, bins = np.histogram(data)\n    assert np.array_equal(hist, [1,  0,  0,  0,  2,  4,  7, 13, 21, 33])\n    assert np.allclose(bins, [0.        , 0.24022489, 0.48044977, 0.72067466, 0.96089955,\n                              1.20112443, 1.44134932, 1.68157421, 1.92179909, 2.16202398,\n                              2.40224887])\n    assert hist.dtype is int\n    assert bins.dtype is bins_dtype\n\n    hist, bins = np.histogram(data[::2,::2,::2,::2])\n    assert np.array_equal(hist, [1, 0, 0, 0, 1, 1, 1, 4, 0, 8])\n    assert np.allclose(bins, [0.        , 0.24022489, 0.48044977, 0.72067466, 0.96089955,\n                              1.20112443, 1.44134932, 1.68157421, 1.92179909, 2.16202398,\n                              2.40224887])\n    assert hist.dtype is int\n    assert bins.dtype is bins_dtype\n\n    hist, bins = np.histogram(data[::2,::2,::2,::2], bins=[0, 0.2, 0.8, 3], range=(0, 2.1))\n    assert np.array_equal(hist, [1, 0, 15])\n    assert np.allclose(bins, [0., 0.2, 0.8, 3.])\n    assert hist.dtype is int\n    assert bins.dtype is float\n\ntest_histogram_fast(int)\ntest_histogram_fast(np.int8)\ntest_histogram_fast(np.float64)\ntest_histogram_fast(np.float32)\n"
  },
  {
    "path": "test/numpy/test_ufunc.codon",
    "content": "import numpy as np\n\n@test\ndef test_add_at_repeated_indices_scalar_b():\n    a = np.zeros((5,), dtype=float)\n    idx = np.array([2, 2, 2, 4], dtype=int)\n    np.add.at(a, idx, 3.0)\n    # a[2] += 3 three times, a[4] += 3 once\n    assert np.allclose(a, np.array([0, 0, 9, 0, 3], dtype=float))\n\n@test\ndef test_add_at_repeated_indices_vector_b_aligned_by_iteration_not_idx():\n    a = np.zeros((6,), dtype=float)\n    idx = np.array([4, 4, 1, 4], dtype=int)\n    b = np.array([10.0, 1.5, -2.0, 0.5], dtype=float)\n    np.add.at(a, idx, b)\n    # iteration-aligned:\n    # a[4]+=10 ; a[4]+=1.5 ; a[1]+=-2 ; a[4]+=0.5  => a[4]=12.0, a[1]=-2\n    assert np.allclose(a, np.array([0, -2, 0, 0, 12.0, 0], dtype=float))\n\n@test\ndef test_add_at_rowwise():\n    A = np.arange((27 * 16), dtype=float).reshape(27, 16)\n    B = np.ones((7,), dtype=int) * 26\n    C = np.zeros((7 * 16), dtype=float).reshape(7, 16)\n\n    # Make C non-trivial so we can validate accumulation\n    for i in range(7):\n        C[i, :] = i + 1  # rows: all 1s, all 2s, ..., all 7s\n\n    A_ref = A.copy()\n    np.add.at(A, B, C)\n\n    # Expected: only row 26 changes; it gets sum of all C rows\n    A_ref[26, :] += C.sum(axis=0)\n    assert np.allclose(A, A_ref)\n\n@test\ndef test_add_at_preserves_other_rows():\n    rng = np.random.default_rng(seed=0)\n    A = rng.standard_normal((10, 4))\n    B = np.array([3, 3, 3], dtype=int)\n    C = np.ones((3, 4), dtype=float)\n    A0 = A.copy()\n\n    np.add.at(A, B, C)\n\n    # rows != 3 unchanged\n    mask = np.ones((10,), dtype=bool)\n    mask[3] = False\n    assert np.allclose(A[mask], A0[mask])\n\n@test\ndef test_add_at_negative_indices():\n    a = np.zeros((5,), dtype=float)\n    idx = np.array([-1, -1, 0], dtype=int)\n    b = np.array([1.0, 2.0, 3.0], dtype=float)\n    np.add.at(a, idx, b)\n    # a[-1]+=1 ; a[-1]+=2 ; a[0]+=3  => a[4]=3, a[0]=3\n    assert np.allclose(a, np.array([3, 0, 0, 0, 3], dtype=float))\n\n@test\ndef test_mul_at_repeated_indices_vector_b():\n    a = np.ones((4,), dtype=float)\n    idx = np.array([2, 2, 2], dtype=int)\n    b = np.array([2.0, 3.0, 4.0], dtype=float)\n    np.multiply.at(a, idx, b)\n    # sequential: a[2] = 1*2*3*4 = 24\n    assert np.allclose(a, np.array([1, 1, 24, 1], dtype=float))\n\n@test\ndef test_at_sequential_semantics_noncommutative_like_true_divide():\n    # This catches buffering bugs: if you accidentally buffer reads/writes,\n    # repeated indices won’t behave like sequential application.\n    a = np.array([100.0], dtype=float)\n    idx = np.array([0, 0, 0], dtype=int)\n    b = np.array([2.0, 5.0, 2.0], dtype=float)\n    np.true_divide.at(a, idx, b)\n    # sequential: ((100 / 2) / 5) / 2 = 5\n    assert np.allclose(a, np.array([5.0], dtype=float))\n\ntest_add_at_repeated_indices_scalar_b()\ntest_add_at_repeated_indices_vector_b_aligned_by_iteration_not_idx()\ntest_add_at_rowwise()\ntest_add_at_preserves_other_rows()\ntest_add_at_negative_indices()\ntest_mul_at_repeated_indices_vector_b()\ntest_at_sequential_semantics_noncommutative_like_true_divide()\n"
  },
  {
    "path": "test/numpy/test_window.codon",
    "content": "import numpy as np\nfrom numpy import *\n\n@test\ndef test_bartlett():\n    assert (round(np.bartlett(4), 8) == array([0., 0.66666667, 0.66666667,\n                                               0.])).all()\n    assert (np.bartlett(0) == np.empty(0)).all()\n    assert (np.bartlett(-7) == np.empty(0)).all()\n    assert (np.bartlett(1) == np.array([1.])).all()\n\n#    assert (round(np.bartlett(4.5), 8) == np.array([0., 0.57142857, 0.85714286, 0.28571429])).all()\n\ntest_bartlett()\n\n@test\ndef test_blackman():\n    assert (np.blackman(-8) == np.empty(0)).all()\n    assert (np.blackman(1) == np.array([1.])).all()\n    x = np.blackman(3)\n    assert x[0] == -1.3877787807814457e-17\n\n#    a = np.blackman(6.6)\n#    assert a[6] == 0.01939475019748911\n\ntest_blackman()\n\n@test\ndef test_hamming():\n    assert (np.hamming(-9) == np.empty(0)).all()\n    assert (np.hamming(1) == np.array([1.])).all()\n    x = np.hamming(12)\n    assert np.isclose(x[1], 0.15302337489765672)\n    assert np.isclose(x[6], 0.9813667678626689)\n    # a = np.hamming(6.6)\n    # assert np.isclose(a[0], 0.08)\n\ntest_hamming()\n\n@test\ndef test_hanning():\n    assert (np.hanning(-9) == np.empty(0)).all()\n    assert (np.hanning(1) == np.array([1.])).all()\n    x = np.hanning(12)\n    assert np.isclose(x[1], 0.07937323358440945)\n    assert np.isclose(x[6], 0.9797464868072487)\n    # a = np.hanning(6.3)\n    # assert np.isclose(a[4], 0.4851833360887201)\n\ntest_hanning()\n\n@test\ndef test_kaiser():\n    assert (np.kaiser(0, 0) == np.empty(0)).all()\n    assert (np.kaiser(0, 3) == np.empty(0)).all()\n    assert (np.kaiser(0, -4) == np.empty(0)).all()\n    assert (np.kaiser(1, -3) == np.array([1.])).all()\n    assert (np.kaiser(8, 0) == np.array([1.])).all()\n    assert (np.kaiser(1, 3) == np.array([1.])).all()\n    assert (np.kaiser(1, -3) == np.array([1.])).all()\n    assert (np.kaiser(-1, 0) == np.empty(0)).all()\n    assert (np.kaiser(-5, 7) == np.empty(0)).all()\n    assert (np.kaiser(-8, -4) == np.empty(0)).all()\n    x = np.kaiser(8, 4)\n    assert x[3] == 0.9652241821181469\n    y = np.kaiser(8, -5)\n    assert y[7] == 0.036710892271286676\n    assert (round(np.kaiser(4, 5), 8) == np.array(\n        [0.03671089, 0.77532210, 0.77532210, 0.03671089])).all()\n    assert (round(np.kaiser(4, 6), 8) == np.array(\n        [0.01487334, 0.73189542, 0.73189542, 0.01487334])).all()\n\n#    assert (round(np.kaiser(4, 8.6), 8) == np.array([0.00133251, 0.63041193, 0.63041193, 0.00133251])).all()\n\ntest_kaiser()\n"
  },
  {
    "path": "test/parser/llvm.codon",
    "content": "#%% ptr,barebones\nimport internal.gc as gc\nprint gc.sizeof(Ptr[int]) #: 8\nprint gc.atomic(Ptr[int]) #: False\ny = Ptr[int](1)\ny[0] = 11\nprint y[0] #: 11\n_y = y.as_byte()\nprint int(_y[0]) #: 11\ny = Ptr[int](5)\ny[0] = 1; y[1] = 2; y[2] = 3; y[3] = 4; y[4] = 5\nz = Ptr[int](y)\nprint y[1], z[2] #: 2 3\nz = Ptr[int](y.as_byte())\nprint y[1], z[2] #: 2 3\nprint z.__bool__() #: True\nz.__int__() # big number...\nzz = z.__copy__() # this is not a deep copy!\nzz[2] = 10\nprint z[2], zz[2] #: 10 10\nprint y.__getitem__(1) #: 2\ny.__setitem__(1, 3)\nprint y[1] #: 3\nprint y.__add__(1)[0] #: 3\nprint (y + 3).__sub__(y + 1) #: 2\nprint y.__eq__(z) #: True\nprint y.__eq__(zz) #: True\nprint y.as_byte().__eq__('abc'.ptr) #: False\nprint y.__ne__(z) #: False\nprint y.__lt__(y+1) #: True\nprint y.__gt__(y+1) #: False\nprint (y+1).__le__(y) #: False\nprint y.__ge__(y) #: True\ny.__prefetch_r0__()\ny.__prefetch_r1__()\ny.__prefetch_r2__()\ny.__prefetch_r3__()\ny.__prefetch_w0__()\ny.__prefetch_w1__()\ny.__prefetch_w2__()\ny.__prefetch_w3__()\n\n#%% int,barebones\na = int()\nb = int(5)\nc = int(True)\nd = int(byte(1))\ne = int(1.1)\nprint a, b, c, d, e #: 0 5 1 1 1\nprint a.__repr__() #: 0\nprint b.__copy__() #: 5\nprint b.__hash__() #: 5\nprint a.__bool__(), b.__bool__() #: False True\nprint a.__pos__() #: 0\nprint b.__neg__() #: -5\nprint (-b).__abs__() #: 5\nprint c.__truediv__(5) #: 0.2\nprint b.__lshift__(1) #: 10\nprint b.__rshift__(1) #: 2\nprint b.__truediv__(5.15) #: 0.970874\nprint a.__add__(1) #: 1\nprint a.__add__(1.1) #: 1.1\nprint a.__sub__(1) #: -1\nprint a.__sub__(1.1) #: -1.1\nprint b.__mul__(1) #: 5\nprint b.__mul__(1.1) #: 5.5\nprint b.__floordiv__(2) #: 2\nprint b.__floordiv__(1.1) #: 4\nprint b.__mod__(2) #: 1\nprint b.__mod__(1.1) #: 0.6\nprint a.__eq__(1) #: False\nprint a.__eq__(1.1) #: False\nprint a.__ne__(1) #: True\nprint a.__ne__(1.1) #: True\nprint a.__lt__(1) #: True\nprint a.__lt__(1.1) #: True\nprint a.__le__(1) #: True\nprint a.__le__(1.1) #: True\nprint a.__gt__(1) #: False\nprint a.__gt__(1.1) #: False\nprint a.__ge__(1) #: False\nprint a.__ge__(1.1) #: False\n\n#%% uint,barebones\nau = Int[123](15)\na = UInt[123]()\nb = UInt[123](a)\na = UInt[123](15)\na = UInt[123](au)\nprint a.__copy__() #: 15\nprint a.__hash__() #: 15\nprint a.__bool__() #: True\nprint a.__pos__() #: 15\nprint a.__neg__() #: 10633823966279326983230456482242756593\nprint a.__invert__() #: 10633823966279326983230456482242756592\nm = UInt[123](4)\nprint a.__add__(m), a.__sub__(m), a.__mul__(m), a.__floordiv__(m), a.__truediv__(m) #: 19 11 60 3 3.75\nprint a.__mod__(m), a.__lshift__(m), a.__rshift__(m) #: 3 240 0\nprint a.__eq__(m), a.__ne__(m), a.__lt__(m), a.__gt__(m), a.__le__(m), a.__ge__(m) #: False True False True False True\nprint a.__and__(m), a.__or__(m), a.__xor__(m) #: 4 15 11\nprint a, a.popcnt() #: 15 4\nac = Int[128](5)\nbc = Int[32](5)\nprint ac, bc, int(ac), int(bc) #: 5 5 5 5\n\nprint int(Int[12](12)) #: 12\nprint int(Int[122](12)) #: 12\nprint int(Int[64](12)) #: 12\nprint int(UInt[12](12)) #: 12\nprint int(UInt[122](12)) #: 12\nprint int(UInt[64](12)) #: 12\n\nprint Int[32](212) #: 212\nprint Int[64](212) #: 212\nprint Int[66](212) #: 212\nprint UInt[32](112) #: 112\nprint UInt[64](112) #: 112\nprint UInt[66](112) #: 112\n\n\n#%% float,barebones\nz = float.__new__()\nz = 5.5\nprint z.__repr__() #: 5.5\nprint z.__copy__() #: 5.5\nprint z.__bool__(), z.__pos__(), z.__neg__() #: True 5.5 -5.5\nf = 1.3\nprint z.__floordiv__(f), z.__floordiv__(2) #: 4 2\nprint z.__truediv__(f), z.__truediv__(2) #: 4.23077 2.75\nprint z.__pow__(2.2), z.__pow__(2) #: 42.54 30.25\nprint z.__add__(2) #: 7.5\nprint z.__sub__(2) #: 3.5\nprint z.__mul__(2) #: 11\nprint z.__truediv__(2) #: 2.75\nprint z.__mod__(2) #: 1.5\nprint z.__eq__(2) #: False\nprint z.__ne__(2) #: True\nprint z.__lt__(2) #: False\nprint z.__gt__(2) #: True\nprint z.__le__(2) #: False\nprint z.__ge__(2) #: True\n\n#%% bool,barebones\nz = bool.__new__()\nprint z.__repr__() #: False\nprint z.__copy__() #: False\nprint z.__bool__(), z.__invert__() #: False True\nprint z.__eq__(True) #: False\nprint z.__ne__(True) #: True\nprint z.__lt__(True) #: True\nprint z.__gt__(True) #: False\nprint z.__le__(True) #: True\nprint z.__ge__(True) #: False\nprint z.__and__(True), z.__or__(True), z.__xor__(True) #: False True True\n\n#%% byte,barebones\nz = byte.__new__()\nz = byte(65)\nprint z.__repr__() #: byte('A')\nprint z.__bool__() #: True\nprint z.__eq__(byte(5)) #: False\nprint z.__ne__(byte(5)) #: True\nprint z.__lt__(byte(5)) #: False\nprint z.__gt__(byte(5)) #: True\nprint z.__le__(byte(5)) #: False\nprint z.__ge__(byte(5)) #: True\n\n#%% array,barebones\na = Array[float](5)\npa = Ptr[float](3)\nz = Array[float](pa, 3)\nz.__copy__()\nprint z.__len__() #: 3\nprint z.__bool__() #: True\nz.__setitem__(0, 1.0)\nz.__setitem__(1, 2.0)\nz.__setitem__(2, 3.0)\nprint z.__getitem__(1) #: 2\nprint z.slice(0, 2).len #: 2\n\n#%% optional,barebones\na = Optional[float]()\nprint bool(a) #: False\na = Optional[float](0.0)\nprint bool(a) #: False\na = Optional[float](5.5)\nprint a.__bool__(), a.__val__() #: True 5.5\n\n#%% generator,barebones\ndef foo():\n    yield 1\n    yield 2\n    yield 3\nz = foo()\ny = z.__iter__()\nprint str(y.__raw__())[:2] #: 0x\nprint y.__done__() #: False\nprint y.__promise__()[0] #: 0\ny.__resume__()\nprint y.__repr__()[:16] #: <generator at 0x\nprint y.__next__() #: 1\nprint y.done() #: False\ny.send(1)\ny.destroy()\nprint y.done() #: True\n"
  },
  {
    "path": "test/parser/simplify_expr.codon",
    "content": ""
  },
  {
    "path": "test/parser/simplify_stmt.codon",
    "content": ""
  },
  {
    "path": "test/parser/typecheck/a/__init__.codon",
    "content": "def foo():\n    print 'a.foo'\ndef bar():\n    print 'a.bar'\nprint 'a'\n\nzoo = 5\n_zoo = 3\n\nclass B:\n    pass\nC = B\ntt = Ptr[B]\n@tuple\nclass Foo:\n    t: tt\n\ndef ha():\n    c = C()\n    print(c.__class__.__name__)\n\n\npar = 'y'\nif len('x') == 1:\n    par = 'x'\n"
  },
  {
    "path": "test/parser/typecheck/a/b/__init__.codon",
    "content": "c = 'a.b.c'\ndef har():\n    print 'a.b.har', __name__[-12:], c\n\nfrom .. import foo as fx\n\nclass A:\n    class B:\n        def b_foo():\n            print 'a.b.A.B.b_foo()'\n            return 1\n\nstt: Literal[int] = 5\n"
  },
  {
    "path": "test/parser/typecheck/a/b/rec1.codon",
    "content": "print 'import rec1'\n\nvar = 'x'\n\nimport rec2\nrec2.foo()\n\ndef bar():\n    print 'rec1.bar'\n\nprint 'done rec1'\n"
  },
  {
    "path": "test/parser/typecheck/a/b/rec1_err.codon",
    "content": "print 'import rec1'\n\nimport rec2_err\nrec2_err.foo()\n\ndef bar():\n    print 'rec1.bar'\n"
  },
  {
    "path": "test/parser/typecheck/a/b/rec2.codon",
    "content": "print 'import rec2'\n\nfrom .rec1 import var\n\ndef foo():\n    print 'rec2.' + var\n\nprint 'done rec2'\n"
  },
  {
    "path": "test/parser/typecheck/a/b/rec2_err.codon",
    "content": "print 'import rec2'\n\nfrom .rec1_err import bar\nbar()\n\ndef foo():\n    print 'rec2.foo'\n"
  },
  {
    "path": "test/parser/typecheck/a/sub/__init__.codon",
    "content": "print('a.sub')\n\ndef foo():\n    print('a.sub.foo')\n"
  },
  {
    "path": "test/parser/typecheck/test_access.codon",
    "content": "#%% __ignore__\nfrom typing import Optional\nimport internal.static as static\n\n#%% id_fstring_error,barebones\nf\"a{b + 3}\" #! name 'b' is not defined\n\n#%% id_access,barebones\ndef foo():\n    a = 5\n    def bar():\n        print(a)\n    bar()  #: 5\n    a = 4\n    bar()  #: 4\nfoo()\n\nd = {}\ndef foo():\n    a = 5\n    def goo():\n        d['x'] = 'y'\n        print(a)\n    return goo\nfoo()()\nprint(d)\n#: 5\n#: {'x': 'y'}\n\n#%% nonlocal,barebones\ndef goo(ww):\n    z = 0\n    def foo(x):\n        f = 10\n        def bar(y):\n            nonlocal z\n            f = x + y\n            z += y\n            print('goo.foo.bar', f, z)\n        bar(5)\n        print('goo.foo', f)\n        return bar\n    b = foo(10)\n    print('goo', z)\n    return b\nb = goo('s')\n# goo.foo.bar 15 5\n# goo.foo 10\n# goo 5\nb(11)\n# goo.foo.bar 21 16\nb(12)\n# goo.foo.bar 22 28\nb = goo(1)  # test another instantiation\n# goo.foo.bar 15 5\n# goo.foo 10\n# goo 5\nb(11)\n# goo.foo.bar 21 16\nb(13)\n# goo.foo.bar 23 29\n\n#%% nonlocal_error,barebones\ndef goo():\n    z = 0\n    def foo():\n        z += 1\n    foo()\ngoo()  #! local variable 'z' referenced before assignment\n\n#%% new_scoping,barebones\ntry:\n    if True and (x := (True or (y := 1 + 2))):\n        pass\n    try:\n        print(x)  #: True\n        print(y)\n    except NameError:\n        print(\"Error\")  #: Error\n    print(x) #: True\n    if len(\"s\") > 0:\n        print(x)  #: True\n        print(y)\n    print(y)  # TODO: test for __used__ usage\n    print(y)  # (right now manual inspection is needed)\nexcept NameError as e:\n    print(e)  #: name 'y' is not defined\n\nt = True\ny = 0 if t else (xx := 1)\ntry:\n    print(xx)\nexcept NameError:\n    print(\"Error\")  #: Error\n\ndef foo():\n    if len(\"s\") == 3:\n        x = 3\n    def bar(y):\n        print(x + y)\n    x = 5\n    return bar\nf = foo()\nf(5)  #: 10\n\n# This should compile.\ndef rad4f(ido: int, l1: int, cxai):\n    def CC(a: int, b: int, c: int):\n        return cxai[a+ido*(b+l1*c)]\n    for k in range(l1):\n        # Make sure that cxai[0] assignment does not mark\n        # cxai as \"adding\" variable\n        # See scoping.cpp:visit(DotExpr*) (or IndexExpr*)\n        tr1, cxai[0] = 1, 1\nrad4f(1, 2, [1, 2])\n\n#%% new_capture_scoping,barebones\ng = 5\ndef foo():\n    def f0():\n        print('f0')\n\n    def f1():\n        print('f1', a)\n\n    def f2(a):\n        print('f2', a)\n\n    def f3():\n        f0()\n\n    def f4(x = 1):\n        if x > 5: print('f4', x)\n        else: f4(x + x)\n\n    def f5(x = 1):\n        if x > 5: print('f5', x, a)\n        else: f5(x + x)\n\n    def f6():\n        nonlocal a\n        print('f6', a)\n        a += a\n        print('f6', a, a.__class__.__name__)\n        return a\n\n    def f7():  # capture A\n        a = 1\n\n        def f7_1():  # capture A\n\n            def f7_11():  # capture A\n                global g\n                nonlocal a\n                a += a\n                g += g\n                print('f7_11', a, a.__class__.__name__)\n            f7_11()\n        f7_1()\n        print('f7', a)\n\n    a = 5\n    f0()  #: f0\n    f1()  #: f1 5\n    f2(1)  #: f2 1\n    print(a)  #: 5\n    f3()  #: f0\n    f4()  #: f4 8\n    f5()  #: f5 8 5\n    f6()  #: f6 5\n          #: f6 10 Capsule[int]\n    print(a)  #: 10\n    f7()  #: f7_11 2 Capsule[int]\n          #: f7 2\n    print(a)  #: 10\n\n    fp = f6(...)  # this captures stuff at this point!\n\n    a = 's'\n    f0()  #: f0\n    f1()  #: f1 s\n    f2(1)  #: f2 1\n    print(a)  #: s\n    f3()  #: f0\n    f4()  #: f4 8\n    f5()  #: f5 8 s\n    f6()  #: f6 s\n          #: f6 ss Capsule[str]\n    print(a)  #: ss\n    f7()  #: f7_11 2 Capsule[int]\n          #: f7 2\n    print(a)  #: ss\n\n    # if len(\"abc\") < 5:\n    a = 1.1\n    f6()  #: f6 1.1\n          #: f6 2.2 Capsule[float]\n    print(a)  #: 2.2\n\n    return fp\n\nfp = foo()\na = fp()  #: f6 10\n          #: f6 20 Capsule[int]\nprint(a, a.__class__.__name__)  #: 20 Capsule[int]\nprint(g, g.__class__.__name__)  #: 20 int\n\n# Lambda capture test\nl = [1, 2, 3, 4]\nfns = []\nfor i in range(len(l)):\n   fns.append(lambda x: l[i] ** x)  # this captures always _here_!\nfor f in fns:\n  print(f(2))\n#: 16\n#: 16\n#: 16\n#: 16\n\ndef enclose():\n    a = 3\n    def fp(x): return (a,x)\n    ff = fp\n    a = 's'\n    return fp\nf = enclose()\nprint(f(3))\n#: ('s', 3)\nprint(f('x'))\n#: ('s', 'x')\n\ndef enclose():\n  a = 3\n  f = lambda x: (a, x)  # captures a in the block\n  a = 's'\n  return f\nf = enclose()\nprint(f(3))\n#: ('s', 3)\nprint(f('x'))\n#: ('s', 'x')\n\ndef foo(x = None, dtype: type = bool):\n    if x is None:\n        ans: Optional[dtype] = None\n    else:\n        ans: dtype = dtype()\n    print(ans.__class__.__name__)\n\n    if x is None:\n        var: Literal[bool] = True\n\n    print(var.__class__.__name__, var.__is_static__)\n\nfoo()\n#: Optional[bool]\n#: bool True\nfoo(int)\n#: bool\n#: NoneType False\n\n#%% new_scoping_loops_try,barebones\nfor i in range(10):\n    pass\nprint(i) #: 9\n\nj = 6\nfor j in range(0):\n    pass\nprint(j) #: 6\n\nfor j in range(1):\n    pass\nprint(j) #: 0\n\nz = 6\nfor z in []:\n    pass\nprint(z) #: 6\n\nfor z in [1, 2]:\n    pass\nprint(z) #: 2\n\ntry:\n    raise ValueError(\"hi\")\nexcept ValueError as e:\n    ee = e\nprint(ee) #: hi\n\n#%% new_scoping_loops_try_error,barebones\ntry:\n    pass\nexcept ValueError as f:\n    pass\ntry:\n    print(f.message)  #! no module named 'f'\nexcept NameError:\n    print('error')\n\n#%% new_scoping_loops,barebones\ngo = True\nwhile go:\n    for idx in range(10):\n        if idx == 5:\n            go = False\n            break\nprint(\"hi\")  #: hi\n\nl = 0\ni = 0\nfn = lambda x: x+1\nwhile l < 10:\n    l = fn(i)\n    i += 1\nprint(\"hi\")  #: hi\n\n#%% dot_access_error_NOPY,barebones\nclass Foo:\n    x: int = 1\nFoo.x #! 'Foo' object has no attribute 'x'\n\n#%% scoping_same_name,barebones\ndef match(pattern: str, string: str, flags: int = 0):\n    pass\n\ndef match(match):\n    if True:\n        match = 0\n    match\n\nmatch(1)\n\n#%% dot_case_1,barebones\na = []\nprint(a[0].loop())  #! 'int' object has no attribute 'loop'\na.append(5)\n\n#%% dot_case_2_NOPY,barebones\na = Optional(0)\nprint(a.__bool__()) #: False\nprint(a.__add__(1)) #: 1\n\n#%% dot_case_4_NOPY,barebones\na = [5]\nprint(a.len) #: 1\n\n#%% dot_case_4_err,barebones\na = [5]\na.foo #! 'List[int]' object has no attribute 'foo'\n\n#%% dot_case_6_NOPY,barebones\n# Did heavy changes to this testcase because\n# of the automatic optional wraps/unwraps and promotions\nclass Foo:\n    def bar(self, a):\n        print('generic', a, a.__class__.__name__)\n    def bar(self, a: Optional[float]):\n        print('optional', a)\n    def bar(self, a: int):\n        print('normal', a)\nf = Foo()\nf.bar(1) #: normal 1\nf.bar(1.1) #: optional 1.1\nf.bar(Optional('s')) #: generic s Optional[str]\n# Check static caching\nf.bar(Optional('t')) #: generic t Optional[str]\nf.bar('hehe') #: generic hehe str\n\n\n#%% dot_case_6b_NOPY,barebones\nclass Foo:\n    def bar(self, a, b):\n        print('1', a, b)\n    def bar(self, a, b: str):\n        print('2', a, b)\n    def bar(self, a: str, b):\n        print('3', a, b)\nf = Foo()\n# Take the newest highest scoring method\nf.bar('s', 't') #: 3 s t\nf.bar(1, 't') #: 2 1 t\nf.bar('s', 1) #: 3 s 1\nf.bar(1, 2) #: 1 1 2\n\n#%% dot,barebones\nclass Foo:\n    def clsmethod():\n        print('foo')\n    def method(self, a):\n        print(a)\nFoo.clsmethod() #: foo\nFoo.method(Foo(), 1) #: 1\nm1 = Foo.method\nm1(Foo(), 's') #: s\nm2 = Foo().method\nm2(1.1) #: 1.1\n\n#%% dot_error_static,barebones\nclass Foo:\n    def clsmethod():\n        print('foo')\n    def method(self, a):\n        print(a)\nFoo().clsmethod() #! clsmethod() takes 0 arguments (1 given)\n\n#%% nested_class_error,barebones\nclass X:\n    def foo(self, x):\n        return x\n    class Y:\n        def bar(self, x):\n            return x\ny = X.Y()\ny.foo(1) #! 'X.Y' object has no attribute 'foo'\n\n#%% nested_deep_class_NOPY,barebones\nclass A[T]:\n    a: T\n    class B[U]:\n        b: U\n        class C[V]:\n            c: V\n            def foo[W](t: V, u: V, v: V, w: W):\n                return (t, u, v, w)\n\nprint(A.B.C[bool].foo(W=str, ...).__fn_name__) #: foo[str;bool,bool,bool,str]\nprint(A.B.C.foo(1,1,1,True)) #: (1, 1, 1, True)\nprint(A.B.C.foo('x', 'x', 'x', 'x')) #: ('x', 'x', 'x', 'x')\nprint(A.B.C.foo('x', 'x', 'x', 'x')) #: ('x', 'x', 'x', 'x')\nprint(A.B.C.foo('x', 'x', 'x', 'x')) #: ('x', 'x', 'x', 'x')\n\nx = A.B.C[bool](False)\nprint(x.__class__.__name__) #: A.B.C[bool]\n\n#%% nested_deep_class_error_NOPY,barebones\nclass A[T]:\n    a: T\n    class B[U]:\n        b: U\n        class C[V]:\n            c: V\n            def foo[W](t: V, u: V, v: V, w: W):\n                return (t, u, v, w)\n\nprint A.B.C[str].foo(1,1,1,True) #! 'int' does not match expected type 'str'\n\n#%% nested_deep_class_error_2_NOPY,barebones\nclass A[T]:\n    a: T\n    class B[U]:\n        b: U\n        class C[V]:\n            c: V\n            def foo[W](t: V, u: V, v: V, w: W):\n                return (t, u, v, w)\nprint A.B[int].C[float].foo(1,1,1,True) #! 'A.B[int]' object has no attribute 'C'\n\n#%% nested_class_function,barebones\ndef f(x):\n    def g(y):\n        return y\n    a = g(1)\n    b = g('s')\n    c = g(x)\n    return a, b, c\nprint f(1.1).__class__.__name__ #: Tuple[int,str,float]\nprint f(False).__class__.__name__ #: Tuple[int,str,bool]\n\nclass A[T]:\n    a: T\n    class B[U]:\n        b: U\n        class C[V]:\n            c: V\n            def f(x):\n                def g(y):\n                    return y\n                a = g(1)\n                b = g('s')\n                c = g(x)\n                return a, b, c\nprint A.B.C.f(1.1).__class__.__name__ #: Tuple[int,str,float]\nprint A.B.C[Optional[int]].f(False).__class__.__name__ #: Tuple[int,str,bool]\n\n#%% rec_class_1_NOPY,barebones\nclass A:\n    y: A\n    def __init__(self): pass  # necessary to prevent recursive instantiation!\nx = A()\nprint(x.__class__.__name__, x.y.__class__.__name__) #: A A\n\n#%% rec_class_2_NOPY,barebones\nclass A[T]:\n    a: T\n    b: A[T]\n    c: A[str]\n    def __init__(self): pass\na = A[int]()\nprint a.__class__.__name__, a.b.__class__.__name__, a.c.__class__.__name__, a.b.b.__class__.__name__, a.b.c.__class__.__name__\n#: A[int] A[int] A[str] A[int] A[str]\nprint a.c.b.__class__.__name__, a.c.c.__class__.__name__, a.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.b.__class__.__name__\n#: A[str] A[str] A[int]\n\n#%% rec_class_3_NOPY,barebones\nclass X:\n    x: int\n    rec: X\n    def __init__(self): pass\n    def foo(x: X, y: int):\n        return y\n    class Y:\n        y: int = 0\n        def bar(self, y):\n            print y\n            return self.y\nx, y = X(), X.Y()\nprint x.__class__.__name__, y.__class__.__name__\n#: X X.Y\nprint X.foo(x, 4), x.foo(5)\n#: 4 5\nprint y.bar(1), y.bar('s'), X.Y.bar(y, True)\n#: 1\n#: s\n#: True\n#: 0 0 0\n\n#%% rec_class_4_NOPY,barebones\nclass A[T]:\n    a: T\n    b: A[T]\n    c: A[str]\n    def __init__(self): pass\nclass B[T]:\n    a: T\n    b: A[T]\n    c: B[T]\n    def __init__(self): pass\n    class Nest1[U]:\n        n: U\n    class Nest2[T, U]:\n        m: T\n        n: U\nb = B[float]()\nprint b.__class__.__name__, b.a.__class__.__name__, b.b.__class__.__name__, b.c.__class__.__name__, b.c.b.c.a.__class__.__name__\n#: B[float] float A[float] B[float] str\n\nn1 = B.Nest1[int](0)\nprint n1.n, n1.__class__.__name__, n1.n.__class__.__name__ #: 0 B.Nest1[int] int\n\nn1: B.Nest2 = B.Nest2[float, int](0, 0)\nprint (n1.m, n1.n), n1.__class__.__name__, n1.m.__class__.__name__, n1.n.__class__.__name__ #: (0, 0) B.Nest2[float,int] float int\n\n#%% class_fn_access_NOPY,barebones\nclass X[T]:\n    def foo[U](self, x: T, y: U):\n        return (x+x, y+y)\ny = X[X[int]]()\nprint y.__class__.__name__ #: X[X[int]]\nprint X[float].foo(U=int, ...).__fn_name__ #: foo[int;X[float],float,int]\nprint X[int]().foo(1, 's') #: (2, 'ss')\n\n#%% class_partial_access_NOPY,barebones\nclass X[T]:\n    def foo[U](self, x, y: U):\n        return (x+x, y+y)\ny = X[X[int]]()\nprint y.foo(U=float,...).__class__.__name__  #: foo(X[X[int]],...,...,float)\nprint y.foo(1, 2.2, float) #: (2, 4.4)\n\n#%% fn_overloads_NOPY,barebones\ndef foo(x):\n    return 1, x\n\nprint(foo(''))  #: (1, '')\n\n@overload\ndef foo(x, y):\n    def foo(x, y):\n        return f'{x}_{y}'\n    return 2, foo(x, y)\n\n@overload\ndef foo(x):\n    if x == '':\n        return 3, 0\n    return 3, 1 + foo(x[1:])[1]\n\nprint foo('hi') #: (3, 2)\nprint foo('hi', 1) #: (2, 'hi_1')\n\ndef fox(a: int, b: int, c: int, dtype: type = int):\n    print('fox 1:', a, b, c)\n\n@overload\ndef fox(a: int, b: int, dtype: type = int):\n    print('fox 2:', a, b, dtype.__class__.__name__)\n\nfox(1, 2, float)\n#: fox 2: 1 2 float\nfox(1, 2)\n#: fox 2: 1 2 int\nfox(1, 2, 3)\n#: fox 1: 1 2 3\n\n# Test whether recursive self references override overloads (they shouldn't)\n\ndef arange(start: int, stop: int, step: int):\n    return (start, stop, step)\n\n@overload\ndef arange(stop: int):\n    return arange(0, stop, 1)\n\nprint(arange(0, 1, 2))\n#: (0, 1, 2)\nprint(arange(12))\n#: (0, 12, 1)\n\n\n#%% fn_shadow,barebones\ndef foo(x):\n    return 1, x\nprint(foo('hi')) #: (1, 'hi')\n\ndef foo(x):\n    return 2, x\nprint(foo('hi')) #: (2, 'hi')\n\n#%% fn_overloads_error_NOPY,barebones\ndef foo(x):\n    return 1, x\n@overload\ndef foo(x, y):\n    return 2, x, y\nfoo('hooooooooy!', 1, 2)\n#! no function 'foo' with arguments (str, int, int)\n\n#%% fn_overloads_dispatch\nimport math\nprint(math.sqrt(4.0))  #: 2\n\n#%% generator_capture_nonglobal,barebones\n# Issue #49\ndef foo(iter):\n    print(iter.__class__.__name__, list(iter))\n\nfor x in range(2):\n    foo(1 for _ in range(x))\n#: Generator[int] []\n#: Generator[int] [1]\nfor x in range(2):\n    for y in range(x):\n        foo('z' for _ in range(y))\n#: Generator[str] []\n\n#%% nonlocal_capture_loop,barebones\n# Issue #51\ndef kernel(fn):\n    def wrapper(*args, grid, block):\n        print(grid, block, fn(*args))\n    return wrapper\ndef test_mandelbrot():\n    MAX    = 10  # maximum Mandelbrot iterations\n    N      = 2   # width and height of image\n    pixels = [0 for _ in range(N)]\n    def scale(x, a, b):\n        return a + (x/N)*(b - a)\n    @kernel\n    def k(pixels):\n        i = 0\n        while i < MAX: i += 1  # this is needed for test to make sense\n        return (MAX, N, pixels, scale(N, -2, 0.4))\n    k(pixels, grid=(N*N)//1024, block=1024)\ntest_mandelbrot()  #: 0 1024 (10, 2, [0, 0], 0.4)\n\n#%% id_shadow_overload_call,barebones\ndef foo():\n    def bar():\n        return -1\n    def xo():\n        return bar()\n    @overload # w/o this this fails because xo cannot capture bar\n    def bar(a):\n        return a\n    bar(1)\nfoo()\n\n#%% domination_nested,barebones\ndef correlate(a, b, mode = 'valid'):\n    if mode == 'valid':\n        if isinstance(a, List):\n            xret = '1'\n        else:\n            xret = '2'\n        for i in a:\n            for j in b:\n                xret += 'z'\n    elif mode == 'same':\n        if isinstance(a, List):\n            xret = '3'\n        else:\n            xret = '4'\n        for i in a:\n            for j in b:\n                xret += 'z'\n    elif mode == 'full':\n        if isinstance(a, List):\n            xret = '5'\n        else:\n            xret = '6'\n        for i in a:\n            for j in b:\n                xret += 'z'\n    else:\n        raise ValueError(f\"mode must be one of 'valid', 'same', or 'full' (got {repr(mode)})\")\n    return xret\nprint(correlate([1], [2], 'full'))  #: 5z\n\ndef foo(x, y):\n    a = 5\n    if isinstance(a, int):\n        if static.len(y) == 0:\n            a = 0\n        elif static.len(y) == 1:\n            a = 1\n        else:\n            for i in range(10):\n                a = 40\n            return a\n    return a\nprint foo(5, (1, 2, 3))  #: 40\n\n#%% nontype_name,barebones\n# Fix #357\nclass Foo:\n    def goo(self):\n        print(self.__name__)\nFoo().goo()\n#! 'Foo' object has no attribute '__name__'\n#! during the realization of goo\n\n\n#%% getattr_setattr\nclass A:\n    def __getattr__(self, attr: Literal[str]):\n        if attr == \"abc\":\n            return 1\n        elif attr == \"dxf\":\n            return attr + '!'\n        elif attr == \"foo\":\n            def foo(x: str):\n                return x + \"_foo\"\n\n            return foo\n        else:\n            return None\n\n    def __setattr__(self, attr: Literal[str], x):\n        print(attr, '<-', x)\n\na = A()\nprint(a.abc)  #: 1\nprint(a.dxf)  #: dxf!\nprint(a.foo(\"lala\"))  #: lala_foo\n\na.xyz = 'hello'  #: xyz <- hello\nprint(a.xyz)  #: None\n\nclass dotdict[V](Static[Dict[str, V]]):\n    def __getattr__(self, attr: Literal[str]) -> Optional[V]:\n        return self.get(attr)\n    def __setattr__(self, attr: Literal[str], v: V):\n        print(\"update!\")\n        self[attr] = v\n\nmydict = dotdict({'a': 1})\nmydict.val = 3  #: update!\nprint(mydict.val)  #: 3\nprint(mydict)  #: {'a': 1, 'val': 3}\nmydict.x = 5  #: update!\nprint(mydict.x)  #: 5\nmydict.x = 4  #: update!\nprint(mydict.x)  #: 4\nprint(mydict)  #: {'x': 4, 'a': 1, 'val': 3}\nprint(mydict.foo)  #: None\n"
  },
  {
    "path": "test/parser/typecheck/test_assign.codon",
    "content": "#%% __ignore__\nfrom typing import Optional, List, Dict, Generator\nfrom dataclasses import dataclass\n\n#%% basic,barebones\na = 5\nb: float = 6.16\nc: Optional[str] = None\nprint(a, b, c)  #: 5 6.16 None\n\n#%% walrus,barebones\ndef foo(x):\n    return x * x\nif x := foo(3):\n    pass\nif (x := foo(4)) and False:\n    print('Nope')\nif False and (x := foo(5)):\n    print('Nope')\nprint(x) #: 16\n\na = [y := foo(1), y+1, y+2]\nprint(a) #: [1, 2, 3]\n\nprint({y: b for y in [1,2,3] if (b := (y - 1))}) #: {2: 1, 3: 2}\nprint(list(b for y in [1,2,3] if (b := (y // 3)))) #: [1]\n\n#%% walrus_update,barebones\ndef foo(x):\n    return x * x\nx = 5\nif x := foo(4):\n    pass\nprint(x) #: 16\n\n#%% walrus_cond_1,barebones\ndef foo(x):\n    return x * x\nif False or (x := foo(4)):\n    pass\nprint(x) #: 16\n\ny = (z := foo(5)) if True else 0\nprint(z) #: 25\n\n#%% walrus_err,barebones\ndef foo(x):\n    return x * x\nif False and (x := foo(4)):\n    pass\ntry:\n    print(x)\nexcept NameError:\n    print(\"Error\") #: Error\n\nt = True\ny = 0 if t else (z := foo(4))\ntry:\n    print(z)\nexcept NameError:\n    print(\"Error\") #: Error\n\n#%% unpack_specials,barebones\nx, = 1,\nprint(x)  #: 1\n\na = (2, 3)\nb = (1, *a[1:])\nprint(a, b)  #: (2, 3) (1, 3)\n\n#%% assign,barebones\na = 1\nprint(a) #: 1\na = 2\nprint(a) #: 2\n\nx, y = 1, 2\nprint(x, y) #: 1 2\n(x, y) = (3, 4)\nprint(x, y) #: 3 4\nx, y = (1, 2)\nprint(x, y) #: 1 2\n(x, y) = 3, 4\nprint(x, y) #: 3 4\n(x, y) = [3, 4]\nprint(x, y) #: 3 4\n[x, y] = [1, 2]\nprint(x, y) #: 1 2\n[x, y] = (4, 3)\nprint(x, y) #: 4 3\n\nl = list(iter(range(10)))\n[a, b, *lx, c, d] = l\nprint(a, b, lx, c, d) #: 0 1 [2, 3, 4, 5, 6, 7] 8 9\na, b, *lx = l\nprint(a, b, lx) #: 0 1 [2, 3, 4, 5, 6, 7, 8, 9]\n*lx, a, b = l\nprint(lx, a, b) #: [0, 1, 2, 3, 4, 5, 6, 7] 8 9\n*xz, a, b = (1, 2, 3, 4, 5)\nprint(xz, a, b) #: (1, 2, 3) 4 5\n(*ex,) = [1, 2, 3]\nprint(ex) #: [1, 2, 3]\n\n#%% assign_str\nsa, sb = 'XY'\nprint(sa, sb) #: X Y\n(sa, sb), sc = 'XY', 'Z'\nprint(sa, sb, sc) #: X Y Z\nsa, *la = 'X'\nprint(sa, la, 1) #: X [] 1\nsa, *la = 'XYZ'\nprint(sa, la) #: X ['Y', 'Z']\n(xa,xb), *xc, xd = [1,2],'this'\nprint(xa, xb, xc, xd) #: 1 2 () this\n(a, b), (sc, *sl) = [1,2], 'this'\nprint(a, b, sc, sl) #: 1 2 t ['h', 'i', 's']\n\n#%% assign_index_dot,barebones\nclass Foo:\n    a: int = 0\n    def __setitem__(self, i: int, t: int):\n        self.a += i * t\nf = Foo()\nf.a = 5\nprint(f.a) #: 5\nf[3] = 5\nprint(f.a) #: 20\nf[1] = -8\nprint(f.a) #: 12\n\ndef foo():\n    print('foo')\n    return 0\nv = [0]\nv[foo()] += 1\n#: foo\nprint(v)\n#: [1]\n\n#%% assign_err_1,barebones\na, *b, c, *d = 1,2,3,4,5 #! multiple starred expressions in assignment\n\n#%% assign_err_4,barebones\n*x = range(5) #! cannot assign to given expression\n\n#%% assign_err_5_NOPY,barebones\n# TODO in Python, this is a ValueError\ntry:\n    (sa, sb), sc = 'XYZ'\nexcept IndexError:\n    print(\"assign failed\") #: assign failed\n\n#%% assign_comprehension\ng = ((b, a, c) for a, *b, c in ['ABC','DEEEEF','FHGIJ'])\nx, *q, y = list(g)\nprint(x, y, q) #: (['B'], 'A', 'C') (['H', 'G', 'I'], 'F', 'J') [(['E', 'E', 'E', 'E'], 'D', 'F')]\n\n#%% assign_shadow,barebones\na = 5\nprint(a) #: 5\na : str = 's'\nprint(a) #: s\n\n#%% assign_err_must_exist,barebones\na = 1\ndef foo():\n    a += 2 #! local variable 'a' referenced before assignment\nfoo()\n\n#%% assign_rename,barebones\ny = int\nz = y(5)\nprint(z) #: 5\n\ndef foo(x): return x + 1\nx = foo\nprint(x(1)) #: 2\n\n#%% assign_err_6,barebones\nx = bar #! name 'bar' is not defined\n\n#%% assign_err_7,barebones\nfoo() += bar #! cannot assign to given expression\n\n#%% assign_update_eq,barebones\na = 5\na += 3\nprint(a) #: 8\na -= 1\nprint(a) #: 7\n\n@dataclass\nclass Foo:\n    a: int\n    def __add__(self, i: int):\n        print('add!')\n        return Foo(self.a + i)\n    def __iadd__(self, i: int):\n        print('iadd!')\n        self.a += i\n        return self\n    def __str__(self):\n        return str(self.a)\nf = Foo(3)\nprint(f + 2) #: add!\n#: 5\nf += 6 #: iadd!\nprint(f) #: 9\n\n#%% del,barebones\na = 5\ndel a\nprint(a) #! name 'a' is not defined\n\n#%% del_index,barebones\ny = [1, 2]\ndel y[0]\nprint(y) #: [2]\n\n#%% del_error,barebones\na = [1]\ndel a.ptr #! cannot delete given expression\n\n#%% assign_underscore,barebones\n_ = 5\n_ = 's'\n\n#%% assign_optional_NOPY,barebones\na = None\nprint(a)  #: None\na = 5\nprint(a)  #: 5\n\nb: Optional[float] = Optional[float](6.5)\nc: Optional[float] = 5.5\nprint(b, c) #: 6.5 5.5\n\n#%% assign_type_alias,barebones\nI = int\nprint(I(5)) #: 5\n\nL = dict[int, str]\nl = L()\nprint(l) #: {}\nl[5] = 'haha'\nprint(l) #: {5: 'haha'}\n\n#%% assign_type_annotation,barebones\na: List[int] = []\nprint(a)  #: []\n\n#%% assign_type_err,barebones\na = 5\nif 1:\n    a = 3.3  #! 'float' does not match expected type 'int'\na\n\n#%% assign_atomic_NOPY,barebones\ni = 1\nf = 1.1\n\n@llvm\ndef xchg(d: Ptr[int], b: int) -> None:\n    %tmp = atomicrmw xchg i64* %d, i64 %b seq_cst\n    ret {} {}\n@llvm\ndef aadd(d: Ptr[int], b: int) -> int:\n    %tmp = atomicrmw add i64* %d, i64 %b seq_cst\n    ret i64 %tmp\n@llvm\ndef amin(d: Ptr[int], b: int) -> int:\n    %tmp = atomicrmw min i64* %d, i64 %b seq_cst\n    ret i64 %tmp\n@llvm\ndef amax(d: Ptr[int], b: int) -> int:\n    %tmp = atomicrmw max i64* %d, i64 %b seq_cst\n    ret i64 %tmp\ndef min(a, b): return a if a < b else b\ndef max(a, b): return a if a > b else b\n\n@extend\nclass int:\n    def __atomic_xchg__(self: Ptr[int], i: int):\n        print('atomic:', self[0], '<-', i)\n        xchg(self, i)\n    def __atomic_add__(self: Ptr[int], i: int):\n        print('atomic:', self[0], '+=', i)\n        return aadd(self, i)\n    def __atomic_min__(self: Ptr[int], b: int):\n        print('atomic:', self[0], '<?=', b)\n        return amin(self, b)\n    def __atomic_max__(self: Ptr[int], b: int):\n        print('atomic:', self[0], '>?=', b)\n        return amax(self, b)\n\n@atomic\ndef foo(x):\n    global i, f\n\n    i += 1 #: atomic: 1 += 1\n    print(i) #: 2\n    i //= 2 #: atomic: 2 <- 1\n    print(i) #: 1\n    i = 3 #: atomic: 1 <- 3\n    print(i) #: 3\n    i = min(i, 10) #: atomic: 3 <?= 10\n    print(i) #: 3\n    i = max(i, 10) #: atomic: 3 >?= 10\n    print(i) #: 10\n    i = max(20, i) #: atomic: 10 <- 20\n    print(i) #: 20\n\n    f += 1.1\n    f = 3.3\n    f = max(f, 5.5)\nfoo(1)\nprint(i, f) #: 20 5.5\n\n#%% assign_atomic_real_NOPY\ni = 1\nf = 1.1\n@atomic\ndef foo(x):\n    global i, f\n\n    i += 1\n    print(i) #: 2\n    i //= 2\n    print(i) #: 1\n    i = 3\n    print(i) #: 3\n    i = min(i, 10)\n    print(i) #: 3\n    i = max(i, 10)\n    print(i) #: 10\n\n    f += 1.1\n    f = 3.3\n    f = max(f, 5.5)\nfoo(1)\nprint(i, f) #: 10 5.5\n\n#%% assign_member_NOPY,barebones\nclass Foo:\n    x: Optional[int] = None\nf = Foo()\nprint(f.x) #: None\nf.x = 5\nprint(f.x) #: 5\n\nfo = Optional(Foo())\nfo.x = 6\nprint(fo.x) #: 6\n\n#%% assign_member_err_1_NOPY,barebones\nclass Foo:\n    x: Optional[int] = None\nFoo().y = 5 #! 'Foo' object has no attribute 'y'\n\n#%% assign_member_err_2_NOPY,barebones\n@tuple\nclass Foo:\n    x: Optional[int] = None\nFoo().x = 5 #! cannot modify tuple attributes\n\n#%% assign_wrappers_NOPY,barebones\na = 1.5\nprint(a) #: 1.5\nif 1:\n    a = 1\nprint(a, a.__class__.__name__) #: 1 float\n\na: Optional[int] = None\nif 1:\n    a = 5\nprint(a.__class__.__name__, a) #: Optional[int] 5\n\nb = 5\nc: Optional[int] = 6\nif 1:\n    b = c\nprint(b.__class__.__name__, c.__class__.__name__, b, c) #: int Optional[int] 6 6\n\nz: Generator[int] = [1, 2]\nprint(z.__class__.__name__) #: Generator[int]\n\nzx: float = 1\nprint(zx.__class__.__name__, zx) #: float 1\n\ndef test(v: Optional[int]):\n    v: int = v if v is not None else 3\n    print(v.__class__.__name__)\ntest(5) #: int\ntest(None) #: int\n\n# %%\n"
  },
  {
    "path": "test/parser/typecheck/test_basic.codon",
    "content": "#%% none,barebones\na = None\nprint(a.__class__.__name__, a) #: Optional[int] None\nif True: a = 5 # wrap with `if`` to avoid shadowing\nprint(a.__class__.__name__, a) #: Optional[int] 5\n\n#%% none_unbound,barebones\na = None\nprint(a.__class__.__name__, a) #: Optional[NoneType] None\n\n#%% bool,barebones\nprint(True, False) #: True False\na = True\nprint(a.__class__.__name__, a) #: bool True\n\n#%% int,barebones\ni = 15\nprint(i.__class__.__name__, i) #: int 15\nprint(0b0000_1111) #: 15\nprint(0B101) #: 5\nprint(3) #: 3\nprint(18_446_744_073_709_551_000) #: -616\nprint(0b11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111) #: -1\nprint(0b11111111_11111111_11111111_11111111_11111111_11111111_11111111_11111111u) #: 18446744073709551615\nprint(18_446_744_073_709_551_000u) #: 18446744073709551000\nprint(65i7) #: -63\nprint(-1u7) #: 127\n\n#%% int_suffix,barebones\n@extend\nclass int:\n    def __suffix_test__(s):\n        return 'TEST: ' + str(s)\nprint(123_456test) #: TEST: 123456\n\n#%% int_large,barebones\nprint(1844674407_3709551999)  #: 383\nprint(1844674407_3709551999i256)  #: 18446744073709551999\n\n#%% float,barebones\nf = 1.11\nprint(f.__class__.__name__, f) #: float 1.11\nprint(5.15) #: 5.15\nprint(2e2) #: 200\nprint(2.e-2) #: 0.02\nprint 1_000.0 #: 1000\nprint 1_000e9 #: 1e+12\n\n#%% float_suffix,barebones\n@extend\nclass float:\n    def __suffix_zoo__(x):\n        return str(x) + '_zoo'\nprint(1.2e-1zoo) #: 0.12_zoo\n\n#%% string,barebones\na = 'hi'\nprint(a.__class__.__name__, a) #: str hi\nprint('kthxbai', \"kthxbai\") #: kthxbai kthxbai\nprint(\"\"\"hi\nhello\"\"\", '''hai\nhallo''')\n#: hi\n#: hello hai\n#: hallo\n\n#%% fstring,barebones\na, b = 1, 2\nprint(f\"string {a}\") #: string 1\nprint(F\"{b} string\") #: 2 string\nprint(f\"str {a+b} end\") #: str 3 end\nprint(f\"str {a+b=}\") #: str a+b=3\nc = f'and this is {a} followed by {b}'\nprint(c, f'{b}{a}', f'. {1+a=} .. {b} ...')\n#: and this is 1 followed by 2 21 . 1+a=2 .. 2 ...\n\n#%% fstring_error_1,barebones\nf\"a{1 + 3}}\" #! single '}' is not allowed in f-string\n\n#%% fstring_error_2,barebones\nf\"a{{1 + 3}\" #! expecting '}' in f-string\n\n#%% string_prefix,barebones\n@extend\nclass str:\n    def __prefix_pfx__(s: str, N: Literal[int]):\n        return 'PFX ' + s\nprint(pfx'HELLO') #: PFX HELLO\n\n@extend\nclass str:\n    def __prefix_pxf__(s: str, N: Literal[int]):\n        return 'PXF ' + s + \" \" + str(N)\nprint(pxf'HELLO') #: PXF HELLO 5\n\n#%% string_raw,barebones\nprint('a\\\\b') #: a\\b\nprint(r'a\\tb') #: a\\tb\nprint(R'\\n\\r\\t\\\\') #: \\n\\r\\t\\\\\n\n#%% string_format\na = 'xyz'\nprint(f\"{a:>10}\")\n#:        xyz\nprint(f\"{a!r:>10}\")\n#:      'xyz'\nprint(f\"{a=!r:>10}\")\n#: a=     'xyz'\nprint(f\"{a=}\")\n#: a=xyz\nprint(f\"{a=:>10}\")\n#: a=       xyz\nprint(f\"{a!r}\")\n#: 'xyz'\nprint(f'{1000000=:,}')\n#: 1000000=1,000,000\nprint(f\"{'':=<30}\")\n#: ==============================\nprint(f'{1000000:,}')\n#: 1,000,000\n"
  },
  {
    "path": "test/parser/typecheck/test_call.codon",
    "content": "\n#%% call_ptr,barebones\nv = 5\np = __ptr__(v)\nprint p[0] #: 5\n\ndef test_tuple():\n    @tuple\n    class Foo:\n        x: int\n\n    f = Foo(6)\n    pf = __ptr__(f.x)\n    print pf[0]  #: 6\n\n    def foo(x):\n        return Foo(x)\n\n    ff = foo(16)\n    pff = __ptr__(ff.x)\n    print pff[0]  #: 16\ntest_tuple()\n\ndef test_class():\n    class Foo:\n        x: int\n\n    f = Foo(6)\n    pf = __ptr__(f.x)\n    print pf[0]  # 6\n    f.x = 7\n    print pf[0]  # 7\n\n    def foo(x):\n        return Foo(x)\n    ff = foo(16)\n    pff = __ptr__(ff.x)\n    print pff[0]  # 16\n    ff.x = 17\n    print pff[0]  # 17\n# test_class()\n\n\n@tuple\nclass A:\n    n: int\n\n@tuple\nclass B:\n    a: A\n\n@tuple\nclass C:\n    b: B\n\nx = C(B(A(1)))\np = __ptr__(x.b.a.n)\np[0] = 55\nprint(x)  #: (b: (a: (n: 55)))\n\n\n#%% call_ptr_error,barebones\n__ptr__(1) #! __ptr__() only takes identifiers or tuple fields as arguments\n\n#%% call_ptr_error_3,barebones\nv = 1\n__ptr__(v, 1) #! __ptr__() takes 1 arguments (2 given)\n\n#%% call_ptr_error_4,barebones\n@tuple\nclass A:\n    n: int\n\nclass B:\n    a: A\n\n@tuple\nclass C:\n    b: B\n\nx = C(B(A(1)))\nprint(__ptr__(x.b.a.n)) #! __ptr__() only takes identifiers or tuple fields as arguments\n\n#%% call_ptr_error_5,barebones\n@tuple\nclass A:\n    n: int\n\nclass B:\n    a: A\n\n@tuple\nclass C:\n    b: B\n\nx = C(B(A(1)))\nprint(__ptr__(A(1).n))   #! __ptr__() only takes identifiers or tuple fields as arguments\n\n\n#%% call_array,barebones\na = __array__[int](2)\na[0] = a[1] = 5\nprint a[0], a[1] #: 5 5\n\n#%% call_array_error,barebones\na = __array__[int](2, 3) #! __new__() takes 1 arguments (2 given)\n\n#%% call_err_1,barebones\nseq_print(1, name=\"56\", 2) #! positional argument follows keyword argument\n\n#%% call_err_2,barebones\nx = (1, 2)\nseq_print(1, name=*x) #! syntax error, unexpected '*'\n\n#%% call_err_3,barebones\nx = (1, 2)\nseq_print(1, name=**x) #! syntax error, unexpected '*'\n\n#%% call_collections\nfrom collections import namedtuple as nt\n\nee = nt('Foo', ('x', 'y'))\nf = ee(1, 2)\nprint f #: (x: 1, y: 2)\n\nee = nt('FooX', (('x', str), 'y'))\nfd = ee('s', 2)\nprint fd #: (x: 's', y: 2)\n\n#%% call_partial_functools\nfrom functools import partial\ndef foo(x, y, z):\n    print x,y,z\nf1 = partial(foo, 1, z=3)\nf1(2) #: 1 2 3\nf2 = partial(foo, y=2)\nf2(1, 2) #: 1 2 2\n\n#%% call,barebones\ndef foo(a, b, c='hi'):\n    print 'foo', a, b, c\n    return 1\nclass Foo:\n    def __init__(self):\n        print 'Foo.__init__'\n    def foo(self, a):\n        print 'Foo.foo', a\n        return 's'\n    def bar[T](self, a: T):\n        print 'Foo.bar', a\n        return a.__class__.__name__\n    def __call__(self, y):\n        print 'Foo.__call__'\n        return foo(2, y)\n\nfoo(1, 2.2, True) #: foo 1 2.2 True\nfoo(1, 2.2) #: foo 1 2.2 hi\nfoo(b=2.2, a=1) #: foo 1 2.2 hi\nfoo(b=2.2, c=12u, a=1) #: foo 1 2.2 12\n\nf = Foo() #: Foo.__init__\nprint f.foo(a=5) #: Foo.foo 5\n#: s\nprint f.bar(a=1, T=int) #: Foo.bar 1\n#: int\nprint Foo.bar(Foo(), 1.1, T=float) #: Foo.__init__\n#: Foo.bar 1.1\n#: float\nprint Foo.bar(Foo(), 's') #: Foo.__init__\n#: Foo.bar s\n#: str\nprint f('hahaha') #: Foo.__call__\n#: foo 2 hahaha hi\n#: 1\n\n@tuple\nclass Moo:\n    moo: int\n    def __new__(i: int) -> Moo:\n        print 'Moo.__new__'\n        return superf(i)\nprint Moo(1) #: Moo.__new__\n#: (moo: 1)\n\n#%% call_err_6,barebones\nseq_print_full(1, name=\"56\", name=2) #! keyword argument repeated: name\n\n#%% call_partial,barebones\ndef foo(i, j, k):\n    return i + j + k\nprint foo(1.1, 2.2, 3.3)  #: 6.6\np = foo(6, ...)\nprint p.__class__.__name__ #: foo(int,...,...)\nprint p(2, 1)  #: 9\nprint p(k=3, j=6) #: 15\nq = p(k=1, ...)\nprint q(3)  #: 10\nqq = q(2, ...)\nprint qq()  #: 9\n#\nadd_two = foo(3, k=-1, ...)\nprint add_two(42)  #: 44\nprint 3 |> foo(1, 2)  #: 6\nprint 42 |> add_two  #: 44\n#\ndef moo(a, b, c=3):\n    print a, b, c\nm = moo(b=2, ...)\nprint m.__class__.__name__ #: moo(...,int,...)\nm('s', 1.1) #: s 2 1.1\n# #\nn = m(c=2.2, ...)\nprint n.__class__.__name__ #: moo(...,int,float)\nn('x') #: x 2 2.2\nprint n('y').__class__.__name__ #: NoneType\n\ndef ff(a, b, c):\n    return a, b, c\nprint ff(1.1, 2, True).__class__.__name__ #: Tuple[float,int,bool]\nprint ff(1.1, ...)(2, True).__class__.__name__ #: Tuple[float,int,bool]\ny = ff(1.1, ...)(c=True, ...)\nprint y.__class__.__name__ #: ff(float,...,bool)\nprint ff(1.1, ...)(2, ...)(True).__class__.__name__ #: Tuple[float,int,bool]\nprint y('hei').__class__.__name__ #: Tuple[float,str,bool]\nz = ff(1.1, ...)(c='s', ...)\nprint z.__class__.__name__ #: ff(float,...,str)\n\ndef fx(*args, **kw):\n    print(args, kw)\nf1 = fx(1, x=1, ...)\nf2 = f1(2, y=2, ...)\nf3 = f2(3, z=3, ...)\nf3()\n#: (1, 2, 3) (x: 1, y: 2, z: 3)\n\n#%% call_arguments_partial,barebones\ndef doo[R, T](a: CallableTrait[[T], R], b: Generator[T], c: Optional[T], d: T):\n    print R.__class__.__name__, T.__class__.__name__\n    print a.__class__.__name__[:8], b.__class__.__name__\n    for i in b:\n        print a(i)\n    print c, c.__class__.__name__\n    print d, d.__class__.__name__\n\nl = [1, 2, 3]\ndoo(b=l, d=Optional(5), c=l[0], a=lambda x: x+1)\n#: int int\n#: %_lambda Generator[int]\n#: 2\n#: 3\n#: 4\n#: 1 Optional[int]\n#: 5 int\n\nl = [1]\ndef adder(a, b): return a+b\ndoo(b=l, d=Optional(5), c=l[0], a=adder(b=4, ...))\n#: int int\n#: adder(.. Generator[int]\n#: 5\n#: 1 Optional[int]\n#: 5 int\n\n#%% call_partial_star,barebones\ndef foo(x, *args, **kwargs):\n    print x, args, kwargs\np = foo(...)\np(1, z=5) #: 1 () (z: 5)\np('s', zh=65) #: s () (zh: 65)\nq = p(zh=43, ...)\nq(1) #: 1 () (zh: 43)\nr = q(5, 38, ...)\nr() #: 5 (38,) (zh: 43)\nr(1, a=1) #: 5 (38, 1) (zh: 43, a: 1)\n\n# kwarg overwrite!\nq(1, zh=99)  #: 1 () (zh: 99)\nr(zh=55)  #: 5 (38,) (zh: 55)\n\n#%% call_args_kwargs_type,barebones\ndef foo(*args: float, **kwargs: int):\n    print(args, kwargs, args.__class__.__name__)\n\nfoo(1, f=1)  #: (1,) (f: 1) Tuple[float]\nfoo(1, 2.1, 3, z=2)  #: (1, 2.1, 3) (z: 2) Tuple[float,float,float]\n\ndef sum(x: Generator[int]):\n    a = 0\n    for i in x:\n        a += i\n    return a\n\ndef sum_gens(*x: Generator[int]) -> int:\n    a = 0\n    for i in x:\n        a += sum(i)\n    return a\nprint sum_gens([1, 2, 3])  #: 6\nprint sum_gens({1, 2, 3})  #: 6\nprint sum_gens(iter([1, 2, 3]))  #: 6\n\n#%% call_kwargs,barebones\ndef kwhatever(**kwargs):\n    print 'k', kwargs\ndef whatever(*args):\n    print 'a', args\ndef foo(a, b, c=1, *args, **kwargs):\n    print a, b, c, args, kwargs\n    whatever(a, b, *args, c)\n    kwhatever(x=1, **kwargs)\nfoo(1, 2, 3, 4, 5, arg1='s', kwa=2)\n#: 1 2 3 (4, 5) (arg1: 's', kwa: 2)\n#: a (1, 2, 4, 5, 3)\n#: k (arg1: 's', kwa: 2, x: 1)\nfoo(1, 2)\n#: 1 2 1 () ()\n#: a (1, 2, 1)\n#: k (x: 1)\nfoo(1, 2, 3)\n#: 1 2 3 () ()\n#: a (1, 2, 3)\n#: k (x: 1)\nfoo(1, 2, 3, 4)\n#: 1 2 3 (4,) ()\n#: a (1, 2, 4, 3)\n#: k (x: 1)\nfoo(1, 2, zamboni=3)\n#: 1 2 1 () (zamboni: 3)\n#: a (1, 2, 1)\n#: k (x: 1, zamboni: 3)\n\n#%% call_unpack,barebones\ndef foo(*args, **kwargs):\n    print args, kwargs\n\n@tuple\nclass Foo:\n    x: int = 5\n    y: bool = True\n\nt = (1, 's')\nf = Foo(6)\nfoo(*t, **f) #: (1, 's') (x: 6, y: True)\nfoo(*(1,2)) #: (1, 2) ()\nfoo(3, f) #: (3, (x: 6, y: True)) ()\nfoo(k = 3, **f) #: () (k: 3, x: 6, y: True)\n\n#%% call_partial_args_kwargs,barebones\ndef foo(*args):\n    print(args)\na = foo(1, 2, ...)\nb = a(3, 4, ...)\nc = b(5, ...)\nc('zooooo')\n#: (1, 2, 3, 4, 5, 'zooooo')\n\ndef fox(*args, **kwargs):\n    print(args, kwargs)\nxa = fox(1, 2, x=5, ...)\nxb = xa(3, 4, q=6, ...)\nxc = xb(5, ...)\nxd = xc(z=5.1, ...)\nxd('zooooo', w='lele')\n#: (1, 2, 3, 4, 5, 'zooooo') (x: 5, q: 6, z: 5.1, w: 'lele')\n\nclass Foo:\n    i: int\n    def __str__(self):\n        return f'#{self.i}'\n    def foo(self, a):\n        return f'{self}:generic'\n    def foo(self, a: float):\n        return f'{self}:float'\n    def foo(self, a: int):\n        return f'{self}:int'\nf = Foo(4)\n\ndef pacman(x, f):\n    print f(x, '5')\n    print f(x, 2.1)\n    print f(x, 4)\npacman(f, Foo.foo)\n#: #4:generic\n#: #4:float\n#: #4:int\n\ndef macman(f):\n    print f('5')\n    print f(2.1)\n    print f(4)\nmacman(f.foo)\n#: #4:generic\n#: #4:float\n#: #4:int\n\nclass Fox:\n    i: int\n    def __str__(self):\n        return f'#{self.i}'\n    def foo(self, a, b):\n        return f'{self}:generic b={b}'\n    def foo(self, a: float, c):\n        return f'{self}:float, c={c}'\n    def foo(self, a: int):\n        return f'{self}:int'\n    def foo(self, a: int, z, q):\n        return f'{self}:int z={z} q={q}'\nff = Fox(5)\ndef maxman(f):\n    print f('5', b=1)\n    print f(2.1, 3)\n    print f(4)\n    print f(5, 1, q=3)\nmaxman(ff.foo)\n#: #5:generic b=1\n#: #5:float, c=3\n#: #5:int\n#: #5:int z=1 q=3\n\n\n#%% call_static,barebones\nprint isinstance(1, int), isinstance(2.2, float), isinstance(3, bool)\n#: True True False\nprint isinstance((1, 2), Tuple), isinstance((1, 2), Tuple[int, int]), isinstance((1, 2), Tuple[float, int])\n#: True True False\nprint isinstance([1, 2], List), isinstance([1, 2], List[int]), isinstance([1, 2], List[float])\n#: True True False\nprint isinstance({1, 2}, List), isinstance({1, 2}, Set[float])\n#: False False\nprint isinstance(Optional(5), Optional[int]), isinstance(Optional(), Optional)\n#: True True\nprint isinstance(Optional(), Optional[int]), isinstance(Optional('s'), Optional[int])\n#: False False\nprint isinstance(None, Optional), isinstance(None, Optional[int])\n#: True False\nprint isinstance(None, Optional[NoneType])\n#: True\nprint isinstance({1, 2}, List)\n#: False\n\nprint static.len((1, 2, 3)), static.len((1, )), static.len('hehe')\n#: 3 1 4\n\nprint hasattr([1, 2], \"__getitem__\")\n#: True\nprint hasattr(type([1, 2]), \"__getitem__\")\n#: True\nprint hasattr(int, \"__getitem__\")\n#: False\nprint hasattr([1, 2], \"__getitem__\") #: True\nprint hasattr([1, 2], \"__getitem__\", int) #: True\nprint hasattr([1, 2], \"__getitem__\", str) #: False\nprint hasattr([1, 2], \"__getitem__\", idx=int) #: True\nprint hasattr([1, 2], \"__getitem__\", idx=str) #: False\n\ndef kw(**kwargs):\n    print hasattr(kwargs, \"yes\"), hasattr(kwargs, \"no\")\nkw(yes=1)  #: True False\nkw()  #: False False\nkw(no=1)  #: False True\nkw(no=1, yes=1)  #: True True\n\n#%% isinstance_inheritance,barebones\nclass AX[T]:\n    a: T\n    def __init__(self, a: T):\n        self.a = a\nclass Side:\n    def __init__(self):\n        pass\nclass BX[T,U](Static[AX[T]], Static[Side]):\n    b: U\n    def __init__(self, a: T, b: U):\n        super().__init__(a)\n        self.b = b\nclass CX[T,U](Static[BX[T,U]]):\n    c: int\n    def __init__(self, a: T, b: U):\n        super().__init__(a, b)\n        self.c = 1\nc = CX('a', False)\nprint isinstance(c, CX), isinstance(c, BX), isinstance(c, AX), isinstance(c, Side)\n#: True True True True\nprint isinstance(c, BX[str, bool]), isinstance(c, BX[str, str]), isinstance(c, AX[int])\n#: True False False\n\n#%% staticlen_err,barebones\nprint static.len([1, 2]) #! expected tuple type\n\n#%% compile_error,barebones\ncompile_error(\"woo-hoo\") #! woo-hoo\n\n#%% stack_alloc,barebones\na = __array__[int](2)\nprint a.__class__.__name__ #: Array[int]\n\n#%% typeof,barebones\na = 5\nz = []\nz.append(6)\nprint z.__class__.__name__, z, type(1.1).__class__.__name__  #: List[int] [6] float\n\n#%% ptr,barebones\nv = 5\nc = __ptr__(v)\nprint c.__class__.__name__ #: Ptr[int]\n\n#%% tuple_fn,barebones\n@tuple\nclass unpackable_plain:\n  a: int\n  b: str\n\nu = unpackable_plain(1, 'str')\na, b = tuple(u)\nprint a, b #: 1 str\n\n@tuple\nclass unpackable_gen:\n  a: int\n  b: T\n  T: type\n\nu2 = unpackable_gen(1, 'str')\na2, b2 = tuple(u2)\nprint a2,b2  #: 1 str\n\nclass plain:\n  a: int\n  b: str\n\nc = plain(3, 'heh')\nz = tuple(c)\nprint z, z.__class__.__name__  #: (3, 'heh') Tuple[int,str]\n\n#%% super,barebones\nclass A[T]:\n    a: T\n    def __init__(self, t: T):\n        self.a = t\n    def foo(self):\n        return f'A:{self.a}'\nclass B(Static[A[str]]):\n    b: int\n    def __init__(self):\n        super().__init__('s')\n        self.b = 6\n    def baz(self):\n        return f'{super().foo()}::{self.b}'\nb = B()\nprint b.foo() #: A:s\nprint b.baz() #: A:s::6\n\nclass AX[T]:\n    a: T\n    def __init__(self, a: T):\n        self.a = a\n    def foo(self):\n        return f'[AX:{self.a}]'\nclass BX[T,U](Static[AX[T]]):\n    b: U\n    def __init__(self, a: T, b: U):\n        print super().__class__.__name__\n        super().__init__(a)\n        self.b = b\n    def foo(self):\n        return f'[BX:{super().foo()}:{self.b}]'\nclass CX[T,U](Static[BX[T,U]]):\n    c: int\n    def __init__(self, a: T, b: U):\n        print super().__class__.__name__\n        super().__init__(a, b)\n        self.c = 1\n    def foo(self):\n        return f'CX:{super().foo()}:{self.c}'\nc = CX('a', False)\nprint c.__class__.__name__, c.foo()\n#: BX[str,bool]\n#: AX[str]\n#: CX[str,bool] CX:[BX:[AX:a]:False]:1\n\n#%% super_vtable_2\nclass Base:\n    def test(self):\n        print('base.test')\nclass A(Base):\n    def test(self):\n        super().test()\n        Base.test(self)\n        print('a.test')\na = A()\na.test()\ndef moo(x: Base):\n    x.test()\nmoo(a)\nBase.test(a)\n#: base.test\n#: base.test\n#: a.test\n#: base.test\n#: base.test\n#: a.test\n#: base.test\n\nclass A:\n    n: int\n    def __init__(self, n: int):\n        print(f\"A init, set {n}\")\n        self.n = n\n\nclass B(A):\n    def __init__(self, n: int):\n        print(f\"B init, set {n}\")\n        super().__init__(n)\n\nclass C(B):\n    def __init__(self, n: int):\n        print(f\"C init, set {n}\")\n        super().__init__(n)\n\nprint(B(42).n)\n#: B init, set 42\n#: A init, set 42\n#: 42\nprint(C(42).n)\n#: C init, set 42\n#: B init, set 42\n#: A init, set 42\n#: 42\n\n#%% super_tuple,barebones\n@tuple\nclass A[T]:\n    a: T\n    x: int\n    def __new__(a: T) -> A[T]:\n        return A[T](a, 1)\n    def foo(self):\n        return f'A:{self.a}'\n@tuple\nclass B(Static[A[str]]):\n    b: int\n    def __new__() -> B:\n        return B(*(A('s')), 6)\n    def baz(self):\n        return f'{super().foo()}::{self.b}'\n\nb = B()\nprint b.foo() #: A:s\nprint b.baz() #: A:s::6\n\n\n#%% super_error,barebones\nclass A:\n    def __init__(self):\n        super().__init__()\na = A()\n#! no super methods found\n#! during the realization of __init__(self: A)\n\n#%% super_error_2,barebones\nsuper().foo(1) #! no super methods found\n\n#%% superf,barebones\nclass Foo:\n    def foo(a):\n        # superf(a)\n        print 'foo-1', a\n    def foo(a: int):\n        superf(a)\n        print 'foo-2', a\n    def foo(a: str):\n        superf(a)\n        print 'foo-3', a\n    def foo(a):\n        superf(a)\n        print 'foo-4', a\nFoo.foo(1)\n#: foo-1 1\n#: foo-2 1\n#: foo-4 1\n\nclass Bear:\n    def woof(x):\n        return f'bear woof {x}'\n@extend\nclass Bear:\n    def woof(x):\n        return superf(x) + f' bear w--f {x}'\nprint Bear.woof('!')\n#: bear woof ! bear w--f !\n\nclass PolarBear(Static[Bear]):\n    def woof():\n        return 'polar ' + superf('@')\nprint PolarBear.woof()\n#: polar bear woof @ bear w--f @\n\n#%% superf_error,barebones\nclass Foo:\n    def foo(a):\n        superf(a)\n        print 'foo-1', a\nFoo.foo(1)\n#! no superf methods found\n#! during the realization of foo(a: int)\n\n#%% static_getitem\nprint Int[static.len(\"ee\")].__class__.__name__ #: Int[2]\n\ny = [1, 2]\nprint getattr(y, \"len\") #: 2\nprint y.len #: 2\ngetattr(y, 'append')(1)\nprint y #: [1, 2, 1]\n\n@extend\nclass Dict:\n    def __getitem2__(self, attr: Literal[str]):\n        if hasattr(self, attr):\n            return getattr(self, attr)\n        else:\n            return self[attr]\n    def __getitem1__(self, attr: Literal[int]):\n        return self[attr]\n\nd = {'s': 3.19}\nprint d.__getitem2__('_upper_bound') #: 3\nprint d.__getitem2__('s') #: 3.19\ne = {1: 3.33}\nprint e.__getitem1__(1) #: 3.33\n\n#%% forward,barebones\ndef foo(f, x):\n    f(x, type(x))\n    print f.__class__.__name__\ndef bar[T](x):\n    print x, T.__class__.__name__\nfoo(bar, 1)\n#: 1 int\n#: bar(...,...)\nfoo(bar(...), 's')\n#: s str\n#: bar(...,...)\nz = bar\nz('s', int)\n#: s int\nz(1, T=str)\n#: 1 str\n\nzz = bar(T=int,...)\nzz(1)\n#: 1 int\n\n#%% forward_error,barebones\ndef foo(f, x):\n    f(x, type(x))\n    print f.__class__.__name__\ndef bar[T](x):\n    print x, T.__class__.__name__\nfoo(bar(T=int,...), 1)\n#! bar() takes 2 arguments (2 given)\n#! during the realization of foo(f: bar(...,int), x: int)\n# TODO fix this error message\n\n#%% sort_partial\ndef foo(x, y):\n    return y**x\nprint sorted([1,2,3,4,5], key=foo(y=2, ...))\nprint sorted([1,2,3,4,5], key=foo(y=-2, ...))\n#: [1, 2, 3, 4, 5]\n#: [5, 3, 1, 2, 4]\n\n#%% type_loc,barebones\na = 1\nT = type(a)\nprint T.__class__.__name__  #: int\n\n#%% methodcaller,barebones\ndef foo():\n    def bar(a, b):\n        print 'bar', a, b\n    return bar\nfoo()(1, 2) #: bar 1 2\n\ndef methodcaller(foo: Literal[str]):\n    def caller(foo: Literal[str], obj, *args, **kwargs):\n        if isinstance(getattr(obj, foo)(*args, **kwargs), None):\n            getattr(obj, foo)(*args, **kwargs)\n        else:\n            return getattr(obj, foo)(*args, **kwargs)\n    return caller(foo=foo, ...)\nv = [1]\nmethodcaller('append')(v, 42)\nprint v #: [1, 42]\nprint methodcaller('index')(v, 42) #: 1\n\n#%% constructor_passing\nclass A:\n    s: str\n    def __init__(self, x):\n        self.s = str(x)[::-1]\n    def __lt__(self, o): return self.s < o.s\n    def __eq__(self, o): return self.s == o.s\n    def __ge__(self, o): return self.s >= o.s\nfoo = [1,2,11,30]\nprint(sorted(foo, key=str))\n#: [1, 11, 2, 30]\nprint(sorted(foo, key=A))\n#: [30, 1, 11, 2]\n\n@tuple\nclass AT:\n    s: str\n    def __new__(i: int) -> AT: return AT(str(i))\nprint(sorted(foo, key=AT))\n#: [1, 11, 2, 30]\n\n#%% polymorphism,barebones\nclass A:\n    a: int\n    def foo(self, a: int): return (f'A({self.a})', a)\n    def bar(self): return 'A.bar'\n    def aaz(self): return 'A.aaz'\nclass B(A):\n    b: int\n    def foo(self, a): return (f'B({self.a},{self.b})', a + self.b)\n    def bar(self): return 'B.bar'\n    def baz(self): return 'B.baz'\nclass M[T]:\n    m: T\n    def moo(self): return (f'M_{T.__class__.__name__}', self.m)\nclass X(B,M[int]):\n    def foo(self, a): return (f'X({self.a},{self.b},{self.m})', a + self.b + self.m)\n    def bar(self): return 'X.bar'\n\ndef foo(i):\n    x = i.foo(1)\n    y = i.bar()\n    z = i.aaz()\n    print(*x, y, z)\na = A(1)\nl = [a, B(2,3), X(2,3,-1)]\nfor i in l: foo(i)\n#: A(1) 1 A.bar A.aaz\n#: B(2,3) 4 B.bar A.aaz\n#: X(2,3,-1) 3 X.bar A.aaz\n\ndef moo(m: M):\n    print(m.moo())\nmoo(M[float](5.5))\nmoo(X(1,2,3))\n#: ('M_float', 5.5)\n#: ('M_int', 3)\n\n\nclass A[T]:\n    def __init__(self):\n        print(\"init A\", T.__class__.__name__)\nclass Ho:\n    def __init__(self):\n        print(\"init Ho\")\n# TODO: this throws and error: B[U](U)\nclass B[U](A[U], Ho):\n    def __init__(self):\n        super().__init__()\n        print(\"init B\", U.__class__.__name__)\nB[Ho]()\n#: init A Ho\n#: init B Ho\n\n\nclass Vehicle:\n    def drive(self):\n        return \"I'm driving a vehicle\"\n\nclass Car(Vehicle):\n    def drive(self):\n        return \"I'm driving a car\"\n\nclass Truck(Vehicle):\n    def drive(self):\n        return \"I'm driving a truck\"\n\nclass SUV(Car, Truck):\n    def drive(self):\n        return \"I'm driving an SUV\"\n\nsuv = SUV()\ndef moo(s):\n    print(s.drive())\nmoo(suv)\nmoo(Truck())\nmoo(Car())\nmoo(Vehicle())\n#: I'm driving an SUV\n#: I'm driving a truck\n#: I'm driving a car\n#: I'm driving a vehicle\n\nclass YugoSUV(SUV):\n    def drive(self):\n        return \"I'm driving a Yugo SUV\"\n\nl: List[Vehicle] = [Car(), Truck(), suv, Vehicle(), YugoSUV()]\nfor i in l:\n    print(i.drive(),\n          isinstance(i, Vehicle),\n          isinstance(i, Car),\n          isinstance(i, Truck),\n          isinstance(i, SUV),\n          isinstance(i, YugoSUV))\n#: I'm driving a car True True False False False\n#: I'm driving a truck True False True False False\n#: I'm driving an SUV True True True True False\n#: I'm driving a vehicle True False False False False\n#: I'm driving a Yugo SUV True True True True True\n\n\n#%% polymorphism_error_1,barebones\nclass M[T]:\n    m: T\nclass X(M[int]):\n    pass\nl = [M[float](1.1), X(2)]\n#! 'X' does not match expected type 'M[float]'\n\n#%% polymorphism_2\nclass Expr:\n    def __init__(self):\n        pass\n    def eval(self):\n        raise ValueError('invalid expr')\n        return 0.0\n    def __str__(self):\n        return \"Expr\"\nclass Const(Expr):\n    x: float\n    def __init__(self, x):\n        self.x=x\n    def __str__(self):\n        return f\"{self.x}\"\n    def eval(self):\n        return self.x\nclass Add(Expr):\n    lhs: Expr\n    rhs: Expr\n    def __init__(self, lhs, rhs):\n        self.lhs=lhs\n        self.rhs=rhs\n        # print(f'ctr: {self}')\n    def eval(self):\n        return self.lhs.eval()+self.rhs.eval()\n    def __str__(self):\n        return f\"({self.lhs}) + ({self.rhs})\"\nclass Mul(Expr):\n    lhs: Expr\n    rhs: Expr\n    def __init__(self, lhs, rhs):\n        self.lhs=lhs\n        self.rhs=rhs\n    def eval(self):\n        return self.lhs.eval()*self.rhs.eval()\n    def __str__(self):\n        return f\"({self.lhs}) * ({self.rhs})\"\n\nc1 = Const(5)\nc2 = Const(4)\nm = Add(c1, c2)\nc3 = Const(2)\na : Expr = Mul(m, c3)\nprint(f'{a} = {a.eval()}')\n#: ((5) + (4)) * (2) = 18\n\nfrom random import random, seed\nseed(137)\ndef random_expr(depth) -> Expr:\n    if depth<=0:\n        return Const(int(random()*42.0))\n    else:\n        lhs=random_expr(depth-1)\n        rhs=random_expr(depth-1)\n        ctorid = int(random()*3)\n        if ctorid==0:\n            return Mul(lhs,rhs)\n        else:\n            return Add(lhs,rhs)\nfor i in range(11):\n    print(random_expr(i).eval())\n#: 3\n#: 697\n#: 109\n#: 73568\n#: 1.5469e+06\n#: 2892\n#: 1.51958e+11\n#: 7.94951e+15\n#: 3.70513e+20\n#: 3.09768e+28\n#: 1.3109e+40\n\n#%% polymorphism_3\nimport operator\n\nclass Expr:\n    def eval(self):\n        return 0\n\nclass Const(Expr):\n    value: int\n\n    def __init__(self, value):\n        self.value = value\n\n    def eval(self):\n        return self.value\n\nclass BinOp(Expr):\n    lhs: Expr\n    rhs: Expr\n\n    def __init__(self, lhs, rhs):\n        self.lhs = lhs\n        self.rhs = rhs\n\n    def eval_from_fn(self, fn):\n        return fn(self.lhs.eval(), self.rhs.eval())\n\nclass Add(BinOp):\n    def eval(self):\n        return self.eval_from_fn(operator.add)\n\nclass Sub(BinOp):\n    def eval(self):\n        return self.eval_from_fn(operator.sub)\n\nclass Mul(BinOp):\n    def eval(self):\n        return self.eval_from_fn(operator.mul)\n\nclass Div(BinOp):\n    def eval(self):\n        return self.eval_from_fn(operator.floordiv)\n\n# TODO: remove Expr requirement\nexpr : Expr = Mul(Const(3), Add(Const(10), Const(5)))\nprint(expr.eval()) #: 45\n\n#%% polymorphism_4\nclass A(object):\n    a: int\n    def __init__(self, a: int):\n        self.a = a\n\n    def test_a(self, n: int):\n        print(\"test_a:A\", n)\n\n    def test(self, n: int):\n        print(\"test:A\", n)\n\n    def test2(self, n: int):\n        print(\"test2:A\", n)\n\nclass B(A):\n    b: int\n    def __init__(self, a: int, b: int):\n        super().__init__(a)\n        self.b = b\n\n    def test(self, n: int):\n        print(\"test:B\", n)\n\n    def test2(self, n: int):\n        print(\"test2:B\", n)\n\nclass C(B):\n    pass\n\nb = B(1, 2)\nb.test_a(1)\nb.test(1)\n#: test_a:A 1\n#: test:B 1\n\na: A = b\na.test(1)\na.test2(2)\n#: test:B 1\n#: test2:B 2\n\n\n\nclass AX(object):\n    value: u64\n\n    def __init__(self):\n        print('init/AX')\n        self.value = 15u64\n\n    def get_value(self) -> u64:\n        return self.value\n\nclass BX(object):\n    a: AX\n    def __init__(self):\n        print('init/BX')\n        self.a = AX()\n    def hai(self):\n        return f\"hai/BX: {self.a.value}\"\n    def inc(self, y):\n        self.a.value += u64(y)\n\nclass CX(BX):\n    def __init__(self):\n        print('init/CX')\n        super().__init__()\n    def getsuper(self):\n        return super()\n    def test(self):\n        print('test/CX:', self.a.value)\n        return self.a.get_value()\n    def hai(self):\n        return f\"hai/CX: {self.a.value}\"\n\ntable = CX()\n#: init/CX\n#: init/BX\n#: init/AX\nprint table.test()\n#: test/CX: 15\n#: 15\n\ns = table.getsuper()\nprint(s.hai())\n#: hai/BX: 15\ns.inc(1)\nprint(s.hai())\n#: hai/BX: 16\ntable.inc(1)\nprint(s.hai())\n#: hai/BX: 17\ntable.test()\n#: test/CX: 17\n\nc: List[BX] = [BX(), s, table]\n#: init/BX\n#: init/AX\nprint(c[0].hai())  #: hai/BX: 15\nprint(c[1].hai())  #: hai/BX: 17\nprint(c[2].hai())  #: hai/CX: 17\n\n\n#%% polymorphism_5_super\nclass B:\n    def __init__(self): pass\n    def foo(self): return \"B_\" + self.bar()\n    def bar(self): return \"bB\"\n\nclass X(B):\n    def __init__(self): super().__init__()\n    def foo(self): return \"X_\" + self.bar()\n    def bar(self): return \"bX\" + super().bar()\n\nclass Y(X):\n    def __init__(self): super().__init__()\n    def foo(self): return \"Y_\" + self.bar()\n    def bar(self): return \"bY\" + super().bar()\n    def s(self): return super()\n\nl: list[B] = [B(), Y(), X()]\nfor i in l:\n    print(type(i), i.foo(), i.bar())\n#: <class 'B'> B_bB bB\n#: <class 'B'> Y_bYbXbB bYbXbB\n#: <class 'B'> X_bXbB bXbB\n\no = Y()\nprint(o.foo())      #: Y_bYbXbB\nprint(o.s().foo())  #: X_bYbXbB\n\n\nclass Shape:\n    def area(self): return 0.0\n    def describe(self): return \"This is a shape.\"\n\nclass Circle(Shape):\n    radius: float\n\n    def __init__(self, radius): self.radius = radius\n    def area(self): return 3.1416 * self.radius**2\n    def describe(self): return f\"A circle with radius {self.radius}\"\n\nclass Rectangle(Shape):\n    width: float\n    height: float\n\n    def __init__(self, width, height):\n        self.width = width\n        self.height = height\n    def area(self): return self.width * self.height\n    def describe(self): return f\"A rectangle with width {self.width} and height {self.height}\"\n\nclass Square(Rectangle):\n    def __init__(self, width): super().__init__(width, width)\n    def describe(self): return super().describe().replace('rectangle', 'square')\n\ndef print_area(shape: Shape):\n    print(shape.describe())\n    print(shape.area())\n\nshapes: list[Shape] = []\nshapes.append(Circle(5))\nshapes.append(Rectangle(4, 6))\nshapes.append(Square(3))\n\nfor s in shapes:\n    print_area(s)\n#: A circle with radius 5\n#: 78.54\n#: A rectangle with width 4 and height 6\n#: 24\n#: A square with width 3 and height 3\n#: 9\n\n#%% side_effects,barebones\ndef foo(a, b, T: type, N: Literal[int]):\n    print(a, b, N, T.__class__.__name__)\ndef side(x):\n    print('side', x)\n    return x\ndef side_T(T: type) -> type:\n    print('side_T', T.__class__.__name__)\n    return List[T]\ndef side_N(N: Literal[int]) -> Literal[int]:\n    print('side_N', N)\n    return N*N\n\nfoo(a=side('a'), b=side('b'), T=int, N=0)\n#: side a\n#: side b\n#: a b 0 int\nfoo(b=side('b'), a=side('a'), T=side_T(int), N=0)\n#: side b\n#: side a\n#: side_T int\n#: a b 0 List[int]\nfoo(b=side('b'), T=side_T(float), a=side('a'), N=side_N(5))\n#: side b\n#: side_T float\n#: side a\n#: side_N 5\n#: a b 25 List[float]\nfoo(N=side_N(3), T=side_T(str), b=side('b'), a=side('a'))\n#: side_N 3\n#: side_T str\n#: side b\n#: side a\n#: a b 9 List[str]\n\ndef foo(a, b, *args, T: type, N: Literal[int], **kwargs):\n    print(a, b, args, kwargs, T, N)\n\nfoo(side('a'), side('b'), side('3'), side('4'), T=side_T(int), N=side_N(5))\n#: side a\n#: side b\n#: side 3\n#: side 4\n#: side_T int\n#: side_N 5\n#: a b ('3', '4') () <class 'List[int]'> 25\n\nfoo(z=side('z'), T=side_T(int), k=side('k'), b=side('b'), a=side('a'), N=side_N(5))\n#: side z\n#: side_T int\n#: side k\n#: side b\n#: side a\n#: side_N 5\n#: a b () (k: 'k', z: 'z') <class 'List[int]'> 25\n\nt = List[side_T(int)]\n#: side_T int\nprint(t, t().__class__.__name__)\n#: <class 'List[List[int]]'> List[List[int]]\n\na: Literal[int] = - side_N(5)\nprint(Int[a])\n#: side_N 5\n#: <class 'Int[-25]'>\na: Literal[int] = side_N(3) + 50 - side_N(5)\n# Important: should be again side_N(5) to test caching of literal fns\nprint(Int[a])\n#: side_N 3\n#: side_N 5\n#: <class 'Int[34]'>\n"
  },
  {
    "path": "test/parser/typecheck/test_class.codon",
    "content": "#%% class_err_1,barebones\n@extend\n@foo\nclass Foo:\n    pass\n#! cannot combine '@extend' with other attributes or decorators\n\n#%% class_extend_typedef,barebones\nsize_t = i32\n@extend\nclass size_t:\n    def foo(self):\n        return f'Int{N}.foo.{self}'\n\nprint size_t(1).foo()  #: Int32.foo.1\nprint Int[64](2).foo()  #: Int64.foo.2\n\n#%% class_err_2,barebones\ndef foo():\n    @extend\n    class Foo:\n        pass\nfoo()\n#! class extension must be a top-level statement\n#! during the realization of foo()\n\n#%% class_nested,barebones\nclass Foo:\n    foo: int\n    class Bar:\n        bar: int\n        b: Optional[Foo.Bar]  # TODO: allow this ONLY in type annotations\n        c: Optional[int]\n        class Moo:\n            # TODO: allow nested class reference to the upclass\n            # x: Foo.Bar\n            x: int\ny = Foo(1)\nz = Foo.Bar(2, None, 4)\nm = Foo.Bar.Moo(5)\nprint y.foo #: 1\nprint z.bar, z.b.__bool__(), z.c, m.x  #: 2 False 4 5\n\n#%% class_nested_2,barebones\n@tuple\nclass Foo:\n    @tuple\n    class Bar:\n        x: int\n    x: int\n    b: Bar\n    c: Foo.Bar\nf = Foo(5, Foo.Bar(6), Foo.Bar(7))\nprint(f) #: (x: 5, b: (x: 6), c: (x: 7))\n\n#%% class_nested_err,barebones\nclass Foo:\n    class Bar:\n        b: Ptr[Bar]\n#! name 'Bar' is not defined\n\n#%% class_err_4,barebones\n@extend\nclass Foo:\n    pass\n#! class name 'Foo' is not defined\n\n#%% class_err_5,barebones\nclass Foo[T, U]:\n    pass\n@extend\nclass Foo[T]:\n    pass\n#! class extensions cannot define data attributes and generics or inherit other classes\n\n#%% class_err_7,barebones\nclass Foo:\n    a: int\n    a: int\n#! duplicate data attribute 'a' in class definition\n\n#%% class_err_tuple_no_recursive,barebones\n@tuple\nclass Foo:\n    a: Foo\n#! name 'Foo' is not defined\n\n#%% class_err_8,barebones\nclass Foo:\n    while 0: pass\n#! unexpected expression in class definition\n\n#%% class_err_9,barebones\nclass F[T: Literal[float]]:\n    pass\n#! expected 'int', 'bool' or 'str'\n\n#%% class_err_11,barebones\ndef foo(x):\n    class A:\n        def bar():\n            print(x)\n    a = A()\n    return a\na = foo(1)\ntype(a).bar()\n#! name 'x' is not defined\n# TODO: store captures in class functions somehow or make them globally accessible!...\n\n#%% class_capture_outside,barebones\ndef foo(x):\n    T = type(x)\n    class A:\n        def bar():\n            print T()\n    A.bar()\nfoo(1)  #: 0\n# TODO: see above\n\n#%% recursive_class,barebones\nclass Node[T]:\n    data: T\n    children: List[Node[T]]\n    def __init__(self, data: T):\n        self.data = data\n        self.children = []\nprint Node(2).data #: 2\n\nclass Node2:\n    data: int\n    children: List[Node2]\n    def __init__(self, data: int):\n        self.data = data\n        self.children = []\nprint Node2(3).data #: 3\n\n#%% class_auto_init,barebones\nclass X[T]:\n    c: T\n    a: int = 4\n    b: int = 0\n    d: str = 'oops'\n    def __str__(self):\n        return f'X({self.a},{self.b},{self.c},{self.d})'\nx = X[float](c=0)\nprint x #: X(4,0,0,oops)\ny = X(c='darius',a=5)\nprint y #: X(5,0,darius,oops)\n\n#%% magic,barebones\n@tuple\nclass Foo:\n    x: int\n    y: int\na, b = Foo(1, 2), Foo(1, 3)\nprint a, b #: (x: 1, y: 2) (x: 1, y: 3)\nprint a.__len__() #: 2\nprint a.__hash__(), b.__hash__() #: 175247769363 175247769360\nprint a == a, a == b #: True False\nprint a != a, a != b #: False True\nprint a < a, a < b, b < a #: False True False\nprint a <= a, a <= b, b <= a #: True True False\nprint a > a, a > b, b > a #: False False True\nprint a >= a, a >= b, b >= a #: True False True\nprint a.__getitem__(1)  #: 2\nprint list(a.__iter__()) #: [1, 2]\n\n#%% magic_class,barebones\n@dataclass(eq=True, order=True)\nclass Foo:\n    x: int\n    y: int\n    def __str__(self): return f'{self.x}_{self.y}'\na, b = Foo(1, 2), Foo(1, 3)\nprint a, b #: 1_2 1_3\nprint a == a, a == b #: True False\nprint a != a, a != b #: False True\nprint a < a, a < b, b < a #: False True False\nprint a <= a, a <= b, b <= a #: True True False\nprint a > a, a > b, b > a #: False False True\nprint a >= a, a >= b, b >= a #: True False True\n\n# Right magic test\nclass X:\n    x: int\nx = X(1)\nprint(str(x)[:15])  #: <X object at 0x\n\nclass Y:\n    y: int\n    def __eq__(self, o: X): return self.y == o.x\n    def __ne__(self, o: X): return self.y != o.x\n    def __le__(self, o: X): return self.y <= o.x\n    def __lt__(self, o: X): return self.y <  o.x\n    def __ge__(self, o: X): return self.y >= o.x\n    def __gt__(self, o: X): return self.y >  o.x\n    def __add__(self, o: X):  return self.y + o.x + 1\n    def __radd__(self, o: X): return self.y + o.x + 2\nprint Y(1) == X(1), Y(1) != X(1)  #: True False\nprint X(1) == Y(1), X(1) != Y(1)  #: True False\nprint Y(1) <= X(2), Y(1) < X(2)  #: True True\nprint X(1) <= Y(2), X(1) < Y(2)  #: True True\nprint Y(1) >= X(2), Y(1) > X(2)  #: False False\nprint X(1) >= Y(2), X(1) > Y(2)  #: False False\nprint X(1) + Y(2)  #: 5\nprint Y(1) + X(2)  #: 4\n\n\nclass A:\n    def __radd__(self, n: int):\n        return 0\ndef f():\n    print('f')\n    return 1\ndef g():\n    print('g')\n    return A()\nf() + g()\n#: f\n#: g\n\n#%% magic_2,barebones\n@tuple\nclass Foo:\n    pass\na, b = Foo(), Foo()\nprint a, b #: () ()\nprint a.__len__() #: 0\nprint a.__hash__(), b.__hash__() #: 0 0\nprint a == a, a == b #: True True\nprint a != a, a != b #: False False\nprint a < a, a < b, b < a #: False False False\nprint a <= a, a <= b, b <= a #: True True True\nprint a > a, a > b, b > a #: False False False\nprint a >= a, a >= b, b >= a #: True True True\n\n# TODO: pickle / to_py / from_py\n\n#%% magic_contains,barebones\nsponge = (1, 'z', 1.55, 'q', 48556)\nprint 1.1 in sponge #: False\nprint 'q' in sponge #: True\nprint True in sponge #: False\n\nbob = (1, 2, 3)\nprint 1.1 in sponge #: False\nprint 1 in sponge #: True\nprint 0 in sponge #: False\n\n#%% magic_err_2,barebones\n@tuple\nclass Foo:\n    pass\ntry:\n    print Foo().__getitem__(1)\nexcept IndexError:\n    print 'error'  #: error\n\n#%% magic_empty_tuple,barebones\n@tuple\nclass Foo:\n    pass\nprint list(Foo().__iter__())  #: []\n\n#%% magic_err_4,barebones\n@tuple(eq=False)\nclass Foo:\n    x: int\nFoo(1).__eq__(Foo(1)) #! 'Foo' object has no attribute '__eq__'\n\n#%% magic_err_5,barebones\n@tuple(pickle=False)\nclass Foo:\n    x: int\np = Ptr[byte]()\nFoo(1).__pickle__(p) #! 'Foo' object has no attribute '__pickle__'\n\n#%% magic_err_6,barebones\n@tuple(container=False)\nclass Foo:\n    x: int\nFoo(1).__getitem__(0) #! 'Foo' object has no attribute '__getitem__'\n\n#%% magic_err_7,barebones\n@tuple(python=False)\nclass Foo:\n    x: int\np = Ptr[byte]()\nFoo(1).__to_py__(p) #! 'Foo' object has no attribute '__to_py__'\n\n#%% inherit_class_4,barebones\nclass defdict[K,V](Static[Dict[K,V]]):\n    fx: Function[[],V]\n    def __init__(self, d: Dict[K,V], fx: Function[[], V]):\n        self.__init__()\n        for k,v in d.items(): self[k] = v\n        self.fx = fx\n    def __getitem__(self, key: K) -> V:\n        if key in self:\n            return self.values[self.keys.index(key)]\n        else:\n            self[key] = self.fx()\n            return self[key]\nz = defdict({'ha':1}, lambda: -1)\nprint z\nprint z['he']\nprint z\n#: {'ha': 1}\n#: -1\n#: {'ha': 1, 'he': -1}\n\nclass Foo:\n    x: int = 0\n    def foo(self):\n        return f'foo {self.x}'\nclass Bar[T]:\n    y: Optional[T] = None\n    def bar(self):\n        return f'bar {self.y}/{self.y.__class__.__name__}'\nclass FooBarBaz[T](Static[Foo], Static[Bar[T]]):\n    def baz(self):\n        return f'baz! {self.foo()} {self.bar()}'\nprint FooBarBaz[str]().foo() #: foo 0\nprint FooBarBaz[float]().bar() #: bar None/Optional[float]\nprint FooBarBaz[str]().baz() #: baz! foo 0 bar None/Optional[str]\n\n#%% inherit_class_err_5,barebones\nclass defdict(Static[Dict[str,float]]):\n    def __init__(self, d: Dict[str, float]):\n        self.__init__(d.items())\nz = defdict()\nz[1.1] #! 'float' does not match expected type 'str'\n\n#%% inherit_tuple,barebones\nclass Foo:\n    a: int\n    b: str\n    def __init__(self, a: int):\n        self.a, self.b = a, 'yoo'\n@tuple\nclass FooTup(Static[Foo]): pass\n\nf = Foo(5)\nprint f.a, f.b #: 5 yoo\nfp = FooTup(6, 's')\nprint fp #: (a: 6, b: 's')\n\n#%% inherit_class_err_1,barebones\nclass defdict(Static[Array[int]]):\n    pass #! reference classes cannot inherit tuple classes\n\n#%% inherit_class_err_2,barebones\n@tuple\nclass defdict(Static[int]):\n    pass #! internal classes cannot inherit other classes\n\n#%% inherit_class_err_3,barebones\nclass defdict(Static[Dict[int, float, float]]):\n    pass #! Dict takes 2 generics (3 given)\n\n#%% inherit_class_err_4,barebones\nclass Foo:\n    x: int\nclass Bar:\n    x: float\nclass FooBar(Static[Foo], Static[Bar]):\n    pass\n# right now works as we rename other fields\n\n\n#%% class_deduce,barebones\nclass Foo:\n    def __init__(self, x):\n        self.x = [x]\n        self.y = 1, x\n\n    def foo(self, x, a=[]):\n        a.append(x)\n        print(a)\n        return a\n\n    def nloc(self, x):\n        a = x\n        def bar():\n            nonlocal a\n            a += 1\n            return a\n        a += 1\n        return bar\n\nf = Foo(1)\nprint(f.x, f.y, f.__class__.__name__) #: [1] (1, 1) Foo[List[int],Tuple[int,int]]\n\nf: Foo = Foo('s')\nprint(f.x, f.y, f.__class__.__name__) #: ['s'] (1, 's') Foo[List[str],Tuple[int,str]]\n\nf.foo(1.1)\n#: [1.1]\nl = f.foo(2)\n#: [1.1, 2]\nassert l == [1.1, 2]\n\nb = f.nloc(5)\nprint(b())  #: 7\nprint(b())  #: 8\nprint(b())  #: 9\n\nclass Fox:\n    def __init__(self):\n        self.x: List[int] = []\n\nf = Fox()\nprint(f.__class__.__name__)\n#: Fox[List[int]]\n\nclass Fox:\n    def __init__(self):\n        self.x = 5\n        self.y = []\nf = Fox()\nprint(f.__class__.__name__, f.x, f.y)\n#: Fox[int,List[NoneType]] 5 []\n\nclass Bar:\n    def __init__(self, y: float):\n        self.y = Foo(y)\n    def __init__(self, y: str):\n        self.x = Foo(y)\n\nb = Bar(3.1)\nprint(b.x.__class__.__name__, b.y.__class__.__name__, b.y.x, b.__class__.__name__)\n#: NoneType Foo[List[float],Tuple[int,float]] [3.1] Bar[NoneType,Foo[List[float],Tuple[int,float]]]\nb = Bar('3.1')\nprint(b.x.__class__.__name__, b.y.__class__.__name__, b.x.x, b.__class__.__name__)\n#: Foo[List[str],Tuple[int,str]] NoneType ['3.1'] Bar[Foo[List[str],Tuple[int,str]],NoneType]\n\n#%% class_var,barebones\nclass Foo:\n    cx = 15\n    x: int = 10\n    cy: ClassVar[str] = \"ho\"\n    class Bar:\n        bx = 1.1\nprint(Foo.cx)  #: 15\nf = Foo()\nprint(Foo.cy, f.cy)  #: ho ho\nprint(Foo.Bar.bx)  #: 1.1\n\nFoo.cx = 10\nprint(Foo.cx)  #: 10\n\ndef x():\n    class Foo:\n        i = 0\n        f = None\n        def __init__(self):\n            Foo.i += 1\n        def __repr__(self):\n            return 'heh-cls'\n    Foo.f = Foo()\n    Foo(), Foo(), Foo()\n    print Foo.f, Foo.i  #: heh-cls 4\n    return Foo()\nf = x()\nprint f.f, f.i  #: heh-cls 5\n\n@tuple\nclass Fot:\n    f = None\n    def __repr__(self):\n        return 'heh-tup'\nFot.f = Fot()\nprint Fot.f  #: heh-tup\n\n#%% extend,barebones\n@extend\nclass int:\n    def run_lola_run(self):\n        while self > 0:\n            yield self\n            self -= 1\nprint list((5).run_lola_run())  #: [5, 4, 3, 2, 1]\n\n#%% staticmethod,barebones\nclass Foo:\n    def __repr__(self):\n        return 'Foo'\n    def m(self):\n        print 'm', self\n    @staticmethod\n    def sm(i):\n        print 'sm', i\nFoo.sm(1)  #: sm 1\nFoo().sm(2)  #: sm 2\nFoo().m()  #: m Foo\n\n#%% class_setter,barebones\nclass Foo:\n    _x: int\n\n    @property\n    def x(self):\n        print('getter')\n        return self._x\n\n    @x.setter\n    def x(self, v):\n        print('setter')\n        self._x = v\n\nf = Foo(1)\nprint(f.x)\n#: getter\n#: 1\n\nf.x = 99\nprint(f.x)\nprint(f._x)\n#: setter\n#: getter\n#: 99\n#: 99\n\n#%% inherit_surrounding,barebones\n# Fix 354\nclass A:\n    pass\nclass B:\n    class C(B): pass\n#! nested classes cannot inherit surrounding classes\n\n#%% inherit_no_member_middle,barebones\n# Fix #532\nclass A:\n    _map: Dict[str, str]\n    def __init__(self):\n        self._map = Dict[str, str]()\nclass B(A):\n     def __init__(self):\n        super().__init__()\nclass C(B):\n    placeholder: str\n    def __init__(self):\n        super().__init__()\ntest = C()\n\n#%% inherit_optional,barebones\n# Fix 554\nclass A:\n    pass\nclass B(A):\n    pass\ndef foo(val: Optional[A]):\n    if val:\n        print(\"A\")\n    else:\n        print(\"None[A]\")\nfoo(A())   #: A\nfoo(None)  #: None[A]\n\n\n#%% no_extend,barebones\n@extend\nclass Tuple:\n    @inline\n    def inc_ref(self):\n        pass\n\n    @inline\n    def dec_ref(self):\n        pass\n(\"abc\",).inc_ref()  # #683\n#! 'Tuple' cannot be extended\n\n\n#%% class_var_poly,barebones\nclass A:\n    apple = {\"a\": 1, \"b\": 2, \"c\": 3}\n    pear = 3\nclass B(A):\n    pass\nclass C(B):\n    pass\n\nprint(A.apple)\n#: {'a': 1, 'b': 2, 'c': 3}\nprint(A.pear)\n#: 3\nprint(B.apple)\n#: {'a': 1, 'b': 2, 'c': 3}\nprint(B.pear)\n#: 3\nprint(C.apple)\n#: {'a': 1, 'b': 2, 'c': 3}\nprint(C.pear)\n#: 3\nA.pear += 1\nprint(A.pear)\n#: 4\nprint(B.pear)\n#: 4\nprint(C.pear)\n#: 4\nB.apple[\"d\"] = 4\nprint(C.apple)\n#: {'a': 1, 'b': 2, 'c': 3, 'd': 4}\n\n"
  },
  {
    "path": "test/parser/typecheck/test_collections.codon",
    "content": "#%% list_unbound,barebones\na = []\nprint(a, a.__class__.__name__)\n#: [] List[NoneType]\n\nd = {}\nprint(d, d.__class__.__name__)\n#: {} Dict[NoneType,NoneType]\n\ns = set()\nprint(s, s.__class__.__name__)\n#: set() Set[NoneType]\n\n#%% star_err,barebones\na = (1, 2, 3)\nz = *a #! unexpected star expression\n\n#%% list,barebones\na = [4, 5, 6]\nprint a #: [4, 5, 6]\nb = [1, 2, 3, *a]\nprint b #: [1, 2, 3, 4, 5, 6]\n\n#%% set,barebones\ngs = {1.12}\nprint gs #: {1.12}\nfs = {1, 2, 3, 1, 2, 3}\ngs.add(1.12)\ngs.add(1.13)\nprint fs, gs #: {1, 2, 3} {1.12, 1.13}\nprint {*fs, 5, *fs} #: {1, 2, 3, 5}\n\n#%% dict,barebones\ngd = {1: 'jedan', 2: 'dva', 2: 'two', 3: 'tri'}\nfd = {}\nfd['jedan'] = 1\nfd['dva'] = 2\nprint gd, fd #: {1: 'jedan', 2: 'two', 3: 'tri'} {'jedan': 1, 'dva': 2}\n\n\n\n#%% comprehension,barebones\nl = [(i, j, f'i{i}/{j}')\n     for i in range(50) if i % 2 == 0 if i % 3 == 0\n     for j in range(2) if j == 1]\nprint l #: [(0, 1, 'i0/1'), (6, 1, 'i6/1'), (12, 1, 'i12/1'), (18, 1, 'i18/1'), (24, 1, 'i24/1'), (30, 1, 'i30/1'), (36, 1, 'i36/1'), (42, 1, 'i42/1'), (48, 1, 'i48/1')]\n\ns = {i%3 for i in range(20)}\nprint s #: {0, 1, 2}\n\nd = {i: j for i in range(10) if i < 1 for j in range(10)}\nprint d  #: {0: 9}\n\nt = 's'\nx = {t: lambda x: x * t for t in range(5)}\nprint(x[3](10))  #: 40\nprint(t)  #: s\n\n#%% comprehension_opt,barebones\n@extend\nclass List:\n    def __init__(self, cap: int):\n        print 'optimize', cap\n        self.arr = Array[T](cap)\n        self.len = 0\ndef foo():\n    yield 0\n    yield 1\n    yield 2\nprint [i for i in range(3)] #: optimize 3\n#: [0, 1, 2]\nprint [i for i in foo()] #: [0, 1, 2]\nprint [i for i in range(3) if i%2 == 0] #: [0, 2]\nprint [i + j for i in range(1) for j in range(1)] #: [0]\nprint {i for i in range(3)} #: {0, 1, 2}\n\n#%% generator,barebones\nz = 3\ng = (e for e in range(20) if e % z == 1)\nprint str(g)[:13] #: <generator at\nprint list(g) #: [1, 4, 7, 10, 13, 16, 19]\n\ng1 = (a for a in range(3))\nprint list(g1) #: [0, 1, 2]\ng2 = (a for a in range(z + 1))\nprint list(g2) #: [0, 1, 2, 3]\n\ndef nest(z):\n    g1 = (a for a in range(3))\n    print list(g1) #: [0, 1, 2]\n    g2 = (a for a in range(z + 1))\n    print list(g2) #: [0, 1, 2, 3, 4]\nnest(4)\n\n#%% tuple_generator,barebones\na = (1, 2)\nb = ('f', 'g')\nprint a, b #: (1, 2) ('f', 'g')\nc = (*a, True, *b)\nprint c #: (1, 2, True, 'f', 'g')\nprint a + b + c #: (1, 2, 'f', 'g', 1, 2, True, 'f', 'g')\nprint () + (1, ) + ('a', 'b') #: (1, 'a', 'b')\n\nt = tuple(i+1 for i in (1,2,3))\nprint t #: (2, 3, 4)\nprint tuple((j, i) for i, j in ((1, 'a'), (2, 'b'), (3, 'c')))\n#: (('a', 1), ('b', 2), ('c', 3))\n\n#%% empty_tuple,barebones\nT = type(())  # only errors with empty tuple type\np = Ptr[T](cobj())\nprint p.__class__.__name__  #: Ptr[Tuple]\n\nprint [a for a in ()]  #: []\n\ndef foo(*args):\n    return [a for a in args]\nargs, result = ((), [()])\nprint list(foo(*args))  #: []\nprint result  #: [()]\n\n#%% collection_common_type,barebones\nl = [1, 2, 3]\nprint(l, l.__class__.__name__)\n#: [1, 2, 3] List[int]\n\nl = [1.1, 2, 3]\nprint(l, l.__class__.__name__)\n#: [1.1, 2, 3] List[float]\n\nl = [1, 2, 3.3]\nprint(l, l.__class__.__name__)\n#: [1, 2, 3.3] List[float]\n\nl = [1, None]\nprint(l, l.__class__.__name__)\n#: [1, None] List[Optional[int]]\n\nl = [None, 2.2]\nprint(l, l.__class__.__name__)\n#: [None, 2.2] List[Optional[float]]\n\nclass A:\n    def __repr__(self): return 'A'\nclass B(A):\n    def __repr__(self): return 'B'\nclass C(B):\n    def __repr__(self): return 'C'\nclass D(A):\n    def __repr__(self): return 'D'\n\nl = [A(), B(), C(), D()]\nprint(l, l.__class__.__name__)\n#: [A, B, C, D] List[A]\n\nl = [D(), C(), B(), A()]\nprint(l, l.__class__.__name__)\n#: [D, C, B, A] List[A]\n\nl = [C(), B()]\nprint(l, l.__class__.__name__)\n#: [C, B] List[B]\n\nl = [C(), A(), B()]\nprint(l, l.__class__.__name__)\n#: [C, A, B] List[A]\n\nl = [None, *[1, 2], None]\nprint(l, l.__class__.__name__)\n#: [None, 1, 2, None] List[Optional[int]]\n\n# l = [C(), D(), B()] # does not work (correct behaviour)\n# print(l, l.__class__.__name__)\n\nd = {1: None, 2.2: 's'}\nprint(d, d.__class__.__name__)\n#: {1: None, 2.2: 's'} Dict[float,Optional[str]]\n\n#%% comprehension_opt_clone\nimport sys\nz = [i for i in sys.argv]\n\n"
  },
  {
    "path": "test/parser/typecheck/test_cond.codon",
    "content": "\n#%% cond,barebones\na = 5\nprint (1 <= a <= 10), (1 >= a >= -5) #: True False\n\n#%% if_expr,barebones\nc = 5\na = 1 if c < 5 else 2\nb = -(1 if c else 2)\nprint a, b #: 2 -1\n\n\n\n#%% range_err,barebones\n1 ... 3 #! unexpected range expression\n\n#%% match\ndef foo(x):\n    match x:\n        case 1:\n            print 'int'\n        case 2 ... 10:\n            print 'range'\n        case 'ACGT':\n            print 'string'\n        case (a, 1):\n            print 'tuple_wild', a\n        case []:\n            print 'list'\n        case [[]]:\n            print 'list list'\n        case [1, 2]:\n            print 'list 2'\n        case [1, z, ...] if z < 5:\n            print 'list 3', z\n        case [1, _, ..., zz] | (1, zz):\n            print 'list 4', zz\n        case (1 ... 10, s := ('ACGT', 1 ... 4)):\n            print 'complex', s\n        case _:\n            print 'else'\nfoo(1) #: int\nfoo(5) #: range\nfoo('ACGT') #: string\nfoo((9, 1)) #: tuple_wild 9\nfoo(List[int]()) #: list\nfoo([List[int]()]) #: list list\nfoo([1, 2]) #: list 2\nfoo([1, 3]) #: list 3 3\nfoo([1, 5]) #: else\nfoo([1, 5, 10]) #: list 4 10\nfoo((1, 33)) #: list 4 33\nfoo((9, ('ACGT', 3))) #: complex ('ACGT', 3)\nfoo(range(10)) #: else\n\nfor op in 'MI=DXSN':\n    match op:\n        case 'M' | '=' | 'X':\n            print('case 1')\n        case 'I' or 'S':\n            print('case 2')\n        case _:\n            print('case 3')\n#: case 1\n#: case 2\n#: case 1\n#: case 3\n#: case 1\n#: case 2\n#: case 3\n\n#%% match_err_1,barebones\nmatch [1, 2]:\n    case [1, ..., 2, ..., 3]: pass\n#! multiple ellipses in a pattern\n\n#%% if_expr_2,barebones\ny = 1 if True else 2\nprint y.__class__.__name__ #: int\n\na = None\nb = 5\nz = a if bool(True) else b # needs bool to prevent static evaluation\nprint z, z.__class__.__name__ #: None Optional[int]\n\nzz = 1.11 if True else None\nprint zz, zz.__class__.__name__ #: 1.11 float\n\n#%% if,barebones\nfor a, b in [(1, 2), (3, 3), (5, 4)]:\n    if a > b:\n        print '1',\n    elif a == b:\n        print '=',\n    else:\n        print '2',\nprint '_'  #: 2 = 1 _\n\nif 1:\n    print '1' #: 1\n\n#%% static_if,barebones\ndef foo(x, N: Literal[int]):\n    if isinstance(x, int):\n        return x + 1\n    elif isinstance(x, float):\n        return x.__pow__(.5)\n    elif isinstance(x, Tuple[int, str]):\n        return f'foo: {x[1]}'\n    elif isinstance(x, Tuple) and (N >= 3 or static.len(x) > 2):\n        return x[2:]\n    elif hasattr(x, '__len__'):\n        return 'len ' + str(x.__len__())\n    else:\n        compile_error('invalid type')\nprint foo(N=1, x=1) #: 2\nprint foo(N=1, x=2.0) #: 1.41421\nprint foo(N=1, x=(1, 'bar')) #: foo: bar\nprint foo(N=1, x=(1, 2)) #: len 2\nprint foo(N=3, x=(1, 2)) #: ()\nprint foo(N=1, x=(1, 2, 3)) #: (3,)\n\n"
  },
  {
    "path": "test/parser/typecheck/test_ctx.codon",
    "content": ""
  },
  {
    "path": "test/parser/typecheck/test_error.codon",
    "content": "#%% assert,barebones\nassert True\nassert True, \"blah\"\n\ntry:\n    assert False\nexcept AssertionError as e:\n    print e.message[:15], e.message[-19:] #: Assert failed ( test_error.codon:6)\n\ntry:\n    assert False, f\"hehe {1}\"\nexcept AssertionError as e:\n    print e.message[:23], e.message[-20:] #: Assert failed: hehe 1 ( test_error.codon:11)\n\n#%% try_throw,barebones\nclass MyError(Exception):\n    def __init__(self, message: str):\n        super().__init__(message)\ntry:\n    raise MyError(\"hello!\")\nexcept MyError as e:\n    print str(e)  #: hello!\ntry:\n    raise OSError(\"hello os!\")\n# TODO: except (MyError, OSError) as e:\n#     print str(e)\nexcept MyError:\n    print \"my\"\nexcept OSError as o:\n    print \"os\", o.typename, len(o.message), o.file[-16:], o.line\n    #: os OSError 9 test_error.codon 24\nfinally:\n    print \"whoa\"  #: whoa\n\n# Test function name\ndef foo():\n    raise MyError(\"foo!\")\ntry:\n    foo()\nexcept MyError as e:\n    print e.typename, e.message #: MyError foo!\ntry:\n    foo()\nexcept Exception as e:\n    print e.typename, e.message #: MyError foo!\n\n#%% throw_error,barebones\nraise 'hello'\n#! exceptions must derive from BaseException\n\n#%% raise_from,barebones\ndef foo(bar):\n    try:\n        bar()\n    except ValueError as e:\n        raise RuntimeError(\"oops\") from e\n    raise RuntimeError(\"oops\")\n\ndef bar1():\n    raise ValueError(\"bar1\")\ntry:\n    foo(bar1)\nexcept RuntimeError as e:\n    print(e.message, e.__cause__)  #: oops bar1\n\ndef bar2():\n    raise ValueError(\"bar2\")\ntry:\n    foo(bar2)\nexcept RuntimeError as e:\n    print(e.message, e.__cause__)  #: oops bar2\n\ndef bar3():\n    pass\ntry:\n    foo(bar3)\nexcept RuntimeError as e:\n    print(e.message, e.__cause__)  #: oops None\n\n#%% try_else,barebones\ndef div(x, y):\n    if y == 0: raise ZeroDivisionError(\"oops!\")\n    return x // y\ndef divide(x: int, y: int):\n    try:\n        result = div(x, y)\n    except ZeroDivisionError:\n        print(\"ZeroDivisionError\")\n    else:\n        print(result)\n    finally:\n        print('Done!')\n\n    try:\n        result = div(x, y)\n    except ZeroDivisionError:\n        print(\"ZeroDivisionError\")\n    else:\n        print(result)\n\ndivide(3, 2)\n#: 1\n#: Done!\n#: 1\ndivide(3, 0)\n#: ZeroDivisionError\n#: Done!\n#: ZeroDivisionError\n\ntry:\n    try:\n        div(5, 1)\n    except ZeroDivisionError:\n        print('Zero')\n    else:\n        print('else before')  #: else before\n        div(5, 0)\n        print('else after')\n    finally:\n        print(\"finally\")  #: finally\nexcept:\n    print('caught') #: caught\n\n"
  },
  {
    "path": "test/parser/typecheck/test_function.codon",
    "content": "\n#%% lambda,barebones\nl = lambda a, b: a + b\nprint l(1, 2) #: 3\n\ne = 5\nlp = lambda x: x + e\nprint lp(1) #: 6\n\ne = 7\nprint lp(2) #: 9\n\ndef foo[T](a: T, l: CallableTrait[[T], T]):\n    return l(a)\nprint foo(4, lp) #: 11\n\ndef foox(a, l):\n    return l(a)\nprint foox(4, lp) #: 11\n\n# Fix 216\ng = lambda a, L=List[int]() : (L.append(a), L)[1]\nprint(g(1))\n#: [1]\ng = lambda a, b=1, *s, **kw: ((a,b,*s),kw)\nprint(g('hey!', c=3))\n#: (('hey!', 1), (c: 3))\nprint(g('hey!', 2, 3, 4, zz=3))\n#: (('hey!', 2, 3, 4), (zz: 3))\n\n#%% nested_lambda,barebones\ndef foo():\n    print list(a*a for a in range(3))\nfoo()  #: [0, 1, 4]\n\n#%% yieldexpr,barebones\ndef mysum(start):\n    m = start\n    while True:\n        a = (yield)\n        print a.__class__.__name__ #: int\n        if a == -1:\n            break\n        m += a\n    yield m\niadder = mysum(0)\nnext(iadder)\nfor i in range(10):\n    iadder.send(i)\n#: int\n#: int\n#: int\n#: int\n#: int\n#: int\n#: int\n#: int\n#: int\n#: int\nprint iadder.send(-1)  #: 45\n\n#%% return,barebones\ndef foo():\n    return 1\nprint foo()  #: 1\n\ndef bar():\n    print 2\n    return\n    print 1\nbar()  #: 2\n\n#%% yield,barebones\ndef foo():\n    yield 1\nprint [i for i in foo()], str(foo())[:16]  #: [1] <generator at 0x\n\n#%% yield_void,barebones\ndef foo():\n    yield\n    print 1\ny = foo()\nprint y.done()  #: False\ny.__next__()  #: 1\n# TODO: next() should work here!\nprint y.done()  #: True\n\n#%% yield_return,barebones\ndef foo():\n    yield 1\n    return\n    yield 2\nprint list(foo())  #: [1]\n\ndef foo(x=0):\n    yield 1\n    if x:\n        return\n    yield 2\nprint list(foo())  #: [1, 2]\nprint list(foo(1))  #: [1]\n\ndef foo(x=0):\n    if x:\n        return\n    yield 1\n    yield 2\nprint list(foo())  #: [1, 2]\nprint list(foo(1))  #: []\n\n#%% global,barebones\na = 1\ndef foo():\n    global a\n    a += 1\nprint a,\nfoo()\nprint a  #: 1 2\n\n#%% global_err,barebones\na = 1\nglobal a #! 'global' outside function\n\n#%% global_err_2,barebones\ndef foo():\n    global b\nfoo()\n#! name 'b' is not defined\n#! during the realization of foo()\n\n#%% global_err_3,barebones\ndef foo():\n    b = 1\n    def bar():\n        global b\n    bar()\nfoo()\n#! no binding for global 'b' found\n#! during the realization of bar()\n#! during the realization of foo()\n\n#%% global_err_4,barebones\na = 1\ndef foo():\n    a += 1\nfoo()  #! local variable 'a' referenced before assignment\n\n#%% global_ref,barebones\na = [1]\ndef foo():\n    a.append(2)\nfoo()\nprint a #: [1, 2]\n\n#%% yield_from,barebones\ndef foo():\n    yield from range(3)\n    yield from range(10, 13)\n    yield -1\nprint list(foo())  #: [0, 1, 2, 10, 11, 12, -1]\n\n#%% with,barebones\nclass Foo:\n    i: int\n    def __enter__(self: Foo):\n        print '> foo! ' + str(self.i)\n    def __exit__(self: Foo):\n        print '< foo! ' + str(self.i)\n    def foo(self: Foo):\n        print 'woof'\nclass Bar:\n    s: str\n    def __enter__(self: Bar):\n        print '> bar! ' + self.s\n    def __exit__(self: Bar):\n        print '< bar! ' + self.s\n    def bar(self: Bar):\n        print 'meow'\nwith Foo(0) as f:\n#: > foo! 0\n    f.foo()  #: woof\n#: < foo! 0\nwith Foo(1) as f, Bar('s') as b:\n#: > foo! 1\n#: > bar! s\n    f.foo()  #: woof\n    b.bar()  #: meow\n#: < bar! s\n#: < foo! 1\nwith Foo(2), Bar('t') as q:\n#: > foo! 2\n#: > bar! t\n    print 'eeh'  #: eeh\n    q.bar()  #: meow\n#: < bar! t\n#: < foo! 2\n\n\n#%% function_err_0,barebones\ndef foo(a, b, a):\n    pass #! duplicate argument 'a' in function definition\n\n#%% function_err_0b,barebones\ndef foo(a, b=1, c):\n    pass #! non-default argument 'c' follows default argument\n\n#%% function_err_0b_ok,barebones\ndef foo(a, b=1, *c):\n    pass\n\n#%% function_err_0c,barebones\ndef foo(a, b=1, *c, *d):\n    pass #! multiple star arguments provided\n\n#%% function_err_0e,barebones\ndef foo(a, b=1, *c = 1):\n    pass #! star arguments cannot have default values\n\n#%% function_err_0f,barebones\ndef foo(a, b=1, **c, **kwargs):\n    pass #! kwargs must be the last argument\n\n#%% function_err_0h,barebones\ndef foo(a, b=1, **c = 1):\n    pass #! star arguments cannot have default values\n\n#%% function_err_0i,barebones\ndef foo(a, **c, d):\n    pass #! kwargs must be the last argument\n\n#%% function_err_1,barebones\ndef foo():\n    @__force__\n    def bar(): pass\nfoo()\n#! builtin function must be a top-level statement\n#! during the realization of foo()\n\n#%% function_err_2,barebones\ndef f[T: Literal[float]]():\n    pass\n#! expected 'int', 'bool' or 'str'\n\n#%% function_err_3,barebones\ndef f(a, b=a):\n    pass\n#! name 'a' is not defined\n\n#%% function_llvm_err_1,barebones\n@llvm\ndef foo():\n    blah\n#! return types required for LLVM and C functions\n\n#%% function_llvm_err_2,barebones\n@llvm\ndef foo() -> int:\n    a{={=}}\n#! invalid LLVM code\n\n#%% function_llvm_err_4,barebones\na = 5\n@llvm\ndef foo() -> int:\n    a{=a\n#! invalid LLVM code\n\n#%% function_self,barebones\nclass Foo:\n    def foo(self):\n        return 'F'\nf = Foo()\nprint f.foo() #: F\n\n#%% function_self_err,barebones\nclass Foo:\n    def foo(self):\n        return 'F'\nFoo.foo(1) #! 'int' does not match expected type 'Foo'\n\n#%% function_nested,barebones\ndef foo(v):\n    value = v\n    def bar():\n        return value\n    return bar\nbaz = foo(2)\nprint baz() #: 2\n\ndef f(x):\n    a=1\n    def g(y):\n        return a+y\n    return g(x)\nprint f(5) #: 6\n\n#%% nested_generic_static,barebones\ndef foo():\n    N: Literal[int] = 5\n    Z: Literal[int] = 15\n    T = Int[Z]\n    def bar():\n        x = __array__[T](N)\n        print(x.__class__.__name__)\n    return bar\nfoo()()  #: Array[Int[15]]\n\n#%% nested_generic_error,barebones\ndef f[T]():\n    def g():\n        return T()\n    return g()\nprint f(int)\n#! name 'T' cannot be captured\n#! during\n#! during\n\n#%% block_unroll,barebones\n# Ensure that block unrolling is done in RAII manner on error\ndef foo():\n    while True:\n        def magic(a: x):\n            return\n        print b\nfoo()\n#! name 'x' is not defined\n#! during the realization of foo()\n\n#%% capture_recursive,barebones\ndef f(x: int) -> int:\n    z = 2 * x\n    def g(y: int) -> int:\n        if y == 0:\n            return 1\n        else:\n            return g(y - 1) * z\n    return g(4)\nprint(f(3))  #: 1296\n\n#%% id_static,barebones\ndef foo[N: Literal[int]]():\n    print N\nfoo(5) #: 5\n\ndef fox(N: Literal[int]):\n    print N\nfox(6) #: 6\n\n#%% function_typecheck_level,barebones\ndef foo(x):\n    def bar(z):  # bar has a parent foo(), however its unbounds must not be generalized!\n        print z\n    bar(x)\n    bar('x')\nfoo(1)\n#: 1\n#: x\nfoo('s')\n#: s\n#: x\n\n#%% function_builtin_error,barebones\n@__force__\ndef foo(x):\n    pass\n#! builtin, exported and external functions cannot be generic\n\n#%% early_return,barebones\ndef foo(x):\n    print  x-1\n    return\n    print len(x)\nfoo(5) #: 4\n\ndef foo2(x):\n    if isinstance(x, int):\n        print  x+1\n        return\n    print len(x)\nfoo2(1) #: 2\nfoo2('s') #: 1\n\n#%% static_fn,barebones\nclass A[TA]:\n    a: TA\n    def dump(a, b, c):\n        print a, b, c\n    def m2():\n        A.dump(1, 2, 's')\n    def __str__(self):\n        return 'A'\nA.dump(1, 2, 3)  #: 1 2 3\nA[int].m2()  #: 1 2 s\nA.m2()  #: 1 2 s\nc = A[str]('s')\nc.dump('y', 1.1)  #: A y 1.1\n\n#%% static_fn_overload,barebones\ndef foo(x: Literal[int]):\n    print('int', x)\n\n@overload\ndef foo(x: Literal[str]):\n    print('str', x)\n\nfoo(10)\n#: int 10\nfoo('s')\n#: str s\n\n#%% instantiate_function_2,barebones\ndef fx[T](x: T) -> T:\n    def g[T](z):\n        return z(T())\n    return g(fx, T)\nprint fx(1.1).__class__.__name__, fx(1).__class__.__name__ #: float int\n\n#%% void,barebones\ndef foo():\n    print 'foo'\ndef bar(x):\n    print 'bar', x.__class__.__name__\na = foo()  #: foo\nbar(a)  #: bar NoneType\n\ndef x():\n  pass\nb = lambda: x()\nb()\nx() if True else x()\n\n#%% void_2,barebones\ndef foo():\n    i = 0\n    while i < 10:\n        print i  #: 0\n        yield\n        i += 10\na = list(foo())\nprint(a)  #: [None]\n\n#%% global_none,barebones\na, b = None, None\ndef foo():\n    global a, b\n    a = [1, 2]\n    b = 3\nprint a, b,\nfoo()\nprint a, b #: None None [1, 2] 3\n\n#%% return_fn,barebones\ndef retfn(a):\n    def inner(b, *args, **kwargs):\n        print a, b, args, kwargs\n    print inner.__class__.__name__ #: inner[T1,T2,T3,T4]\n    return inner(15, ...)\nf = retfn(1)\nprint f.__class__.__name__ #: inner(int,...,int,...)\nf(2,3,foo='bar') #: 1 15 (2, 3) (foo: 'bar')\n\n#%% decorator_manual,barebones\ndef foo(x, *args, **kwargs):\n    print x, args, kwargs\n    return 1\ndef dec(fn, a):\n    print 'decorating', fn.__class__.__name__ #: decorating foo(...,...,...)\n    def inner(*args, **kwargs):\n        print 'decorator', args, kwargs #: decorator (5.5, 's') (z: True)\n        return fn(a, *args, **kwargs)\n    return inner(...)\nff = dec(foo(...), 10)\nprint ff(5.5, 's', z=True)\n#: 10 (5.5, 's') (z: True)\n#: 1\n\n\n#%% decorator,barebones\ndef foo(x, *args, **kwargs):\n    print x, args, kwargs\n    return 1\ndef dec(a):\n    def f(fn):\n        print 'decorating', fn.__class__.__name__\n        def inner(*args, **kwargs):\n            print 'decorator', args, kwargs\n            return fn(a, *args, **kwargs)\n        return inner\n    return f\nff = dec(10)(foo)\nprint ff(5.5, 's', z=True)\n#: decorating foo(...,...,...)\n#: decorator (5.5, 's') (z: True)\n#: 10 (5.5, 's') (z: True)\n#: 1\n\n@dec(a=5)\ndef zoo(e, b, *args):\n    return f'zoo: {e}, {b}, {args}'\nprint zoo(2, 3)\nprint zoo('s', 3)\n#: decorating zoo(...,...,...)\n#: decorator (2, 3) ()\n#: zoo: 5, 2, (3,)\n#: decorator ('s', 3) ()\n#: zoo: 5, s, (3,)\n\ndef mydecorator(func):\n    def inner():\n        print(\"before\")\n        func()\n        print(\"after\")\n    return inner\n@mydecorator\ndef foo2():\n    print(\"foo\")\nfoo2()\n#: before\n#: foo\n#: after\n\ndef timeme(func):\n    def inner(*args, **kwargs):\n        begin = 1\n        end = func(*args, **kwargs) - begin\n        print('time needed for', func.__class__.__name__, 'is', end)\n    return inner\n@timeme\ndef factorial(num):\n    n = 1\n    for i in range(1,num + 1):\n        n *= i\n    print(n)\n    return n\nfactorial(10)\n#: 3628800\n#: time needed for factorial(...) is 3628799\n\ndef dx1(func):\n    def inner():\n        x = func()\n        return x * x\n    return inner\ndef dx2(func):\n    def inner():\n        x = func()\n        return 2 * x\n    return inner\n@dx1\n@dx2\ndef num():\n    return 10\nprint(num()) #: 400\n\ndef dy1(func):\n    def inner(*a, **kw):\n        x = func(*a, **kw)\n        return x * x\n    return inner\ndef dy2(func):\n    def inner(*a, **kw):\n        x = func(*a, **kw)\n        return 2 * x\n    return inner\n@dy1\n@dy2\ndef num2(a, b):\n    return a+b\nprint(num2(10, 20)) #: 3600\n\n#%% c_void_return,barebones\nfrom C import seq_print(str)\nx = seq_print(\"not \")\nprint x  #: not None\n\n#%% return_none_err_1,barebones\ndef foo(n: int):\n    if n > 0:\n        return\n    else:\n        return 1\nfoo(1)\n#! 'NoneType' does not match expected type 'int'\n#! during the realization of foo(n: int)\n\n#%% return_none_err_2,barebones\ndef foo(n: int):\n    if n > 0:\n        return 1\n    return\nfoo(1)\n#! 'int' does not match expected type 'NoneType'\n#! during the realization of foo(n: int)\n\n#%% return_fail,barebones\nreturn #! 'return' outside function\n\n#%% yield_fail,barebones\nyield 5 #! 'yield' outside function\n\n#%% yield_fail_2,barebones\n(yield) #! 'yield' outside function\n\n#%% real_callable,barebones\ndef foo(x: Callable[[int, int], str]):\n    return x(1, 2)\n\ndef f1(a, b):\n    return f'f1:{a}.{b}'\n# Case 1: normal functions\nprint foo(f1)\n#: f1:1.2\n\ndef f2(a, b):\n    return f'f2:{a}+{b}'\n# Case 2: function pointers\nf2p: Function[[int,int],str] = f2\nprint foo(f2p)\n#: f2:1+2\n\ndef f3(a, b, c):\n    return f'f3:<{a}+{b}+{c}>'\n# Case 3: Partials\npt = f3(c='hey!', ...)\nprint foo(pt)\n#: f3:<1+2+hey!>\nprint foo(f3(b='hey!', ...))\n#: f3:<1+hey!+2>\n\n# Case 4: expressions\ndef i2i_1(x: int) -> int:\n  return x + 1\ndef i2i_2(x: int) -> int:\n  return x + 2\n# TODO: auto-deduction!\nfn = Callable[[int], int](i2i_1) if int(1) else i2i_2\nprint(fn(1)) #: 2\nprint (Callable[[int], int](i2i_1) if int(0) else i2i_2)(1)  #: 3\n# TODO: auto-deduction!\nl = [Callable[[int, int],str](f1), f2p, pt]\nfor fn in l: print(fn(1, 2))\n#: f1:1.2\n#: f2:1+2\n#: f3:<1+2+hey!>\n\n#%% scoping_advance,barebones\ndef bar():\n    print(a)\n\na = 5\nbar()  #: 5\n\na = 's'\nbar()  #: s\n\na = 5\ndef foo():\n  print(a + a)  # captured a; does not even have to be defined\n\nfor a in range(5):\n  foo()\n#: 0\n#: 2\n#: 4\n#: 6\n#: 8\n\na = 2  # if this is not working, this line will make the previous outputs all zeros\n\n#%% decorator_self_reference\nstore = Dict[int,int]()   # need to manually configure cache for now.\ndef memoize(func):\n    def inner(val: int) -> int:\n        if val in store:\n            print(f\"<- cache[{val}]\")\n            return store[val]\n        else:\n            result = func(val)\n            store[val] = result\n            return result\n    return inner\n\n@memoize\ndef fib(n: int) -> int:\n    print(f\"<- fib[{n}]\")\n    if n < 2:\n        return n\n    else:\n        return fib(n - 1) + fib(n - 2)    ## << not accessing decorated function\n\nf4 = fib(4)\nprint(f\"{f4=} : {store=}\")\n#: <- fib[4]\n#: <- fib[3]\n#: <- fib[2]\n#: <- fib[1]\n#: <- fib[0]\n#: <- cache[1]\n#: <- cache[2]\n#: f4=3 : store={0: 0, 1: 1, 2: 1, 3: 2, 4: 3}\n\nf6 = fib(6)\nprint(f\"{f6=} : {store=}\")\n#: <- fib[6]\n#: <- fib[5]\n#: <- cache[4]\n#: <- cache[3]\n#: <- cache[4]\n#: f6=8 : store={0: 0, 1: 1, 2: 1, 3: 2, 4: 3, 5: 5, 6: 8}\n\nf6 = fib(6)\nprint(f\"{f6=} : {store=}\")\n#: <- cache[6]\n#: f6=8 : store={0: 0, 1: 1, 2: 1, 3: 2, 4: 3, 5: 5, 6: 8}\n\n\ndef memoize(func):\n    store = Dict[int,int]()   # need to manually configure cache for now.\n    def inner(val: int) -> int:\n        if val in store:\n            print(f\"<- cache[{val}]\")\n            return store[val]\n        else:\n            result = func(val)\n            store[val] = result\n            return result\n    return inner\n\n@memoize\ndef fib(n: int) -> int:\n    print(f\"<- fib[{n}]\")\n    if n < 2:\n        return n\n    else:\n        return fib(n - 1) + fib(n - 2)    ## << not accessing decorated function\n\nf4 = fib(4)\nprint(f\"{f4=}\")\n#: <- fib[4]\n#: <- fib[3]\n#: <- fib[2]\n#: <- fib[1]\n#: <- fib[0]\n#: <- cache[1]\n#: <- cache[2]\n#: f4=3\n\nf6 = fib(6)\nprint(f\"{f6=}\")\n#: <- fib[6]\n#: <- fib[5]\n#: <- cache[4]\n#: <- cache[3]\n#: <- cache[4]\n#: f6=8\n\nf6 = fib(6)\nprint(f\"{f6=}\")\n#: <- cache[6]\n#: f6=8\n\n@memoize\ndef fib2(n: int) -> int:\n    print(f\"<- fib2[{n}]\")\n    if n < 2:\n        return n\n    else:\n        return fib2(n - 1) + fib2(n - 2)    ## << not accessing decorated function\n\nf4 = fib2(4)\nprint(f\"{f4=}\")\n#: <- fib2[4]\n#: <- fib2[3]\n#: <- fib2[2]\n#: <- fib2[1]\n#: <- fib2[0]\n#: <- cache[1]\n#: <- cache[2]\n#: f4=3\n\nf6 = fib2(6)\nprint(f\"{f6=}\")\n#: <- fib2[6]\n#: <- fib2[5]\n#: <- cache[4]\n#: <- cache[3]\n#: <- cache[4]\n#: f6=8\n\nf6 = fib2(6)\nprint(f\"{f6=}\")\n#: <- cache[6]\n#: f6=8\n\n#%% fn_defaults,barebones\ndef foo(x, a = List[int](), b = List[str]()):\n    a.append(1)\n    b.append(x)\n    print('foo', a, b)\n\nfoo('a')\n#: foo [1] ['a']\nfoo('b')\n#: foo [1, 1] ['a', 'b']\n\ndef wrap(s, i):\n    def foo(x, a = List[int](), b = List[str]()):\n        a.append(i)\n        b.append(x)\n        print('wrap.foo', a, b)\n    for c in s: foo(c)\n    return foo\nf = wrap(['a', 'b'], 1)\n#: wrap.foo [1] ['a']\n#: wrap.foo [1, 1] ['a', 'b']\nf('a')\n#: wrap.foo [1, 1, 1] ['a', 'b', 'a']\nf('b')\n#: wrap.foo [1, 1, 1, 1] ['a', 'b', 'a', 'b']\nf = wrap(['x'], 2)\n#: wrap.foo [2] ['x']\nf('c')\nf('d')\n#: wrap.foo [2, 2] ['x', 'c']\n#: wrap.foo [2, 2, 2] ['x', 'c', 'd']\n\ndef foo(x, a=[]):\n    a.append(x)\n    print(a)\n    return a\nfoo(1.1)\n#: [1.1]\nfoo(2)\n#: [1.1, 2]\n\n\n#%% fn_defaults_err,barebones\ndef foo(x, a = List[int](), b = List[str]()):\n    a.append(1)\n    b.append(x)\n    print('foo', a, b)\n\nfoo('a')\nfoo(3)\n#! 'int' does not match expected type 'str'\n#! during the realization of foo\n"
  },
  {
    "path": "test/parser/typecheck/test_import.codon",
    "content": "#%% import_c,barebones\nfrom C import sqrt(float) -> float\nprint sqrt(4.0) #: 2\n\nfrom C import puts(cobj)\nputs(\"hello\".ptr) #: hello\n\nfrom C import atoi(cobj) -> int as s2i\nprint s2i(\"11\".ptr) #: 11\n\n@C\ndef log(x: float) -> float:\n    pass\nprint log(5.5)  #: 1.70475\n\nfrom C import seq_flags: Int[32] as e\n# debug | standalone == 5\nprint e  #: 5\n\n#%% import_c_shadow_error,barebones\n# Issue #45\nfrom C import sqrt(float) -> float as foo\nsqrt(100.0)  #! name 'sqrt' is not defined\n\n\n#%% import_c_dylib,barebones\nfrom internal.dlopen import dlext\nRT = \"./libcodonrt.\" + dlext()\nfrom C import RT.seq_str_int(int, str, Ptr[bool]) -> str as sp\np = False\nprint sp(65, \"\", __ptr__(p))  #: 65\n\n#%% import_c_dylib_error,barebones\nfrom C import \"\".seq_print(str) as sp\nsp(\"hi!\") #! syntax error, unexpected '\"'\n\n#%% import,barebones\nzoo, _zoo = 1, 1\nprint zoo, _zoo, __name__  #: 1 1 __main__\n\nimport a  #: a\na.foo() #: a.foo\n\nfrom a import foo, bar as b\nfoo() #: a.foo\nb() #: a.bar\n\nprint str(a)[:9], str(a)[-18:] #: <module ' a/__init__.codon'>\n\nimport a.b\nprint a.b.c #: a.b.c\na.b.har() #: a.b.har a.b.__init__ a.b.c\n\nprint a.b.A.B.b_foo().__add__(1) #: a.b.A.B.b_foo()\n#: 2\n\nprint str(a.b)[:9], str(a.b)[-20:] #: <module ' a/b/__init__.codon'>\nprint Int[a.b.stt].__class__.__name__  #: Int[5]\n\nfrom a.b import *\nhar() #: a.b.har a.b.__init__ a.b.c\na.b.har() #: a.b.har a.b.__init__ a.b.c\nfx() #: a.foo\nprint(stt, Int[stt].__class__.__name__)  #: 5 Int[5]\n\nfrom a import *\nprint zoo, _zoo, __name__  #: 5 1 __main__\n\nf = Foo(Ptr[B]())\nprint f.__class__.__name__, f.t.__class__.__name__  #: Foo Ptr[B]\n\na.ha()  #: B\n\nprint par  #: x\n\n#%% import_static_time,barebones\nfrom a.b import stt  #: a\nprint(stt, stt.__is_static__)  #: 5 True\n\n#%% import_order,barebones\ndef foo():\n    import a\n    a.foo()\ndef bar():\n    import a\n    a.bar()\n\nbar() #: a\n#: a.bar\nfoo() #: a.foo\n\n#%% import_class\nimport sys\nprint str(sys)[:20]  #: <module 'sys' from '\nprint sys.maxsize  #: 9223372036854775807\n\n#%% import_rec,barebones\nfrom a.b.rec1 import bar\n#: import rec1\n#: import rec2\n#: done rec2\n#: rec2.x\n#: done rec1\nbar()\n#: rec1.bar\n\n#%% import_rec_err,barebones\nfrom a.b.rec1_err import bar\n#! cannot import name 'bar' from 'a.b.rec1_err'\n#! during the realization of <import a.b.rec2_err>\n#! during the realization of <import a.b.rec1_err>\n\n#%% import_err_1,barebones\nclass Foo:\n    import bar #! unexpected expression in class definition\n\n#%% import_err_2,barebones\nimport \"\".a.b.c #! syntax error, unexpected '\"'\n\n#%% import_err_3,barebones\nfrom a.b import foo() #! function signatures only allowed when importing C or Python functions\n\n#%% import_err_4,barebones\nfrom a.b.c import hai.hey #! expected identifier\n\n#%% import_err_4_x,barebones\nimport whatever #! no module named 'whatever'\n\n#%% import_err_5,barebones\nimport a.b\nprint a.b.x #! cannot import name 'x' from 'a.b.__init__'\n\n#%% import_err_6,barebones\nfrom a.b import whatever #! cannot import name 'whatever' from 'a.b.__init__'\n\n#%% import_subimport,barebones\nimport a as xa  #: a\n\nxa.foo()  #: a.foo\n#: a.sub\nxa.sub.foo()  #: a.sub.foo\n"
  },
  {
    "path": "test/parser/typecheck/test_infer.codon",
    "content": "#%% late_unify,barebones\na = []\na.append(1)\nprint a  #: [1]\nprint [1]+[1]  #: [1, 1]\n\n#%% late_unify_2,barebones\nclass XX[T]:\n    y: T\n    def __init__(self): pass\na = XX()\ndef f(i: int) -> int:\n    return i\nprint a.y.__class__.__name__ #: int\nf(a.y)\nprint a.__class__.__name__ #: XX[int]\nprint XX[bool].__class__.__name__ #: XX[bool]\n\n#%% map_unify\ndef map[T,S](l: List[T], f: CallableTrait[[T], S]):\n    return [f(x) for x in l]\ne = 1\nprint map([1, 2, 3], lambda x: x+e)  #: [2, 3, 4]\n\ndef map2(l, f):\n    return [f(x) for x in l]\nprint map2([1, 2, 3], lambda x: x+e)  #: [2, 3, 4]\n\n#%% nested,barebones\ndef m4[TD](a: int, d: TD):\n    def m5[TD,TE](a: int, d: TD, e: TE):\n        print a, d, e\n    m5(a, d, 1.12)\nm4(1, 's')  #: 1 s 1.12\nm4(1, True)  #: 1 True 1.12\n\n#%% nested_class,barebones\nclass A[TA]:\n    a: TA\n    # lots of nesting:\n    def m4[TD](self: A[TA], d: TD):\n        def m5[TA,TD,TE](a: TA, d: TD, e: TE):\n            print a, d, e\n        m5(self.a, d, d)\nax = A(42)\nax.m4(1)  #: 42 1 1\n\n#%% realization_big\nclass A[TA,TB,TC]:\n    a: TA\n    b: TB\n    c: TC\n\n    def dump(a, b, c):\n        print a, b, c\n\n    # non-generic method:\n    def m0(self: A[TA,TB,TC], a: int):\n        print a\n\n    # basic generics:\n    def m1[X](self: A[TA,TB,TC], other: A[X,X,X]):\n        print other.a, other.b, other.c\n\n    # non-generic method referencing outer generics:\n    def m2(a: TA, b: TB, c: TC):\n        A.dump(a, b, c)\n\n    # generic args:\n    def m3(self, other):\n        return self.a\n\n    # lots of nesting:\n    def m4[TD](self: A[TA,TB,TC], d: TD):\n        def m5[TA,TB,TC,TD,TE](a: TA, b: TB, c: TC, d: TD, e: TE):\n            print a, b, c, d, e\n        m5(self.a, self.b, self.c, d, d)\n\n    # instantiating the type:\n    def m5(self):\n        x = A(self.a, self.b, self.c)\n        A.dump(x.a, x.b, x.c)\n\n    # deeply nested generic type:\n    def m6[T](v: array[array[array[T]]]):\n        return v[0][0][0]\na1 = A(42, 3.14, \"hello\")\na2 = A(1, 2, 3)\na1.m1(a2)                           #: 1 2 3\nA[int,float,str].m2(1, 1.0, \"one\")  #: 1 1 one\nA[int,int,int].m2(11, 22, 33)       #: 11 22 33\nprint a1.m3(a2)                     #: 42\nprint a1.m3(a2)                     #: 42\nprint a2.m3(a1)                     #: 1\na1.m4(True)                         #: 42 3.14 hello True True\na1.m4([1])                          #: 42 3.14 hello [1] [1]\na2.m4(\"x\")                          #: 1 2 3 x x\na1.m5()                             #: 42 3.14 hello\na2.m5()                             #: 1 2 3\n\nv1 = array[array[array[str]]](1)\nv2 = array[array[str]](1)\nv3 = array[str](1)\nv1[0] = v2\nv2[0] = v3\nv3[0] = \"world\"\nprint A.m6(v1)                      #: world\n\nf = a2.m0\nf(99)                               #: 99\n\n#%% realization_small,barebones\nclass B1[T]:\n    a: T\n    def foo[S](self: S) -> B1[int]:\n        return B1[int](111)\nb1 = B1[bool](True).foo()\nprint b1.foo().a                    #: 111\n\nclass B2[T]:\n    a: T\n    def foo[S](self: B2[S]):\n        return B2[int](222)\nb2 = B2[str](\"x\").foo()\nprint b2.foo().a                    #: 222\n\n# explicit realization:\ndef m7[T,S]():\n    print \"works\"\nm7(str,float)                       #: works\nm7(str,float)                       #: works\nm7(float,str)                       #: works\n\n#%% recursive,barebones\ndef foo(a):\n    if not a:\n        foo(True)\n    print a\nfoo(0)\n#: True\n#: 0\n\ndef bar(a):\n    def baz(x):\n        if not x:\n            bar(True)\n        print (x)\n    baz(a)\nbar(0)\n#: True\n#: 0\n\ndef rec2(x, y):\n    if x:\n        return rec2(y, x)\n    else:\n        return 1.0\nprint rec2(1, False).__class__.__name__ #: float\n\ndef pq(x):\n    return True\ndef rec3(x, y):\n    if pq(x):\n        return rec3(y, x)\n    else:\n        return y\nprint rec3('x', 's').__class__.__name__  #: str\n\n# Nested mutually recursive function\ndef f[T](x: T) -> T:\n    def g[T](z):\n        return z(T())\n    return g(f, T=T)\nprint f(1.2).__class__.__name__ #: float\nprint f('s').__class__.__name__ #: str\n\ndef f2[T](x: T):\n    return f2(x - 1, T) if x else 1\nprint f2(1) #: 1\nprint f2(1.1).__class__.__name__ #: int\n\n\n#%% recursive_error,barebones\ndef pq(x):\n    return True\ndef rec3(x, y): #- ('a, 'b) -> 'b\n    if pq(x):\n        return rec3(y, x)\n    else:\n        return y\nrec3(1, 's')\n#! 'int' does not match expected type 'str'\n#! during the realization of rec3(x: int, y: str)\n\n#%% optionals,barebones\ny = None\nprint y  #: None\ny = 5\nprint y  #: 5\n\ndef foo(x: optional[int], y: int):\n    print 'foo', x, y\nfoo(y, 6)  #: foo 5 6\nfoo(5, 6)  #: foo 5 6\nfoo(5, y)  #: foo 5 5\ny = None\ntry:\n    foo(5, y)\nexcept ValueError:\n    print 'unwrap failed'  #: unwrap failed\n\nclass Cls:\n    x: int\nc = None\nfor i in range(2):\n    if c: c.x += 1  # check for unwrap() dot access\n    c = Cls(1)\nprint(c.x)  #: 1\n\n#%% optional_methods,barebones\n@extend\nclass int:\n    def x(self):\n        print 'x()!', self\n\ny = None\nz = 1 if y else None\nprint z  #: None\n\ny = 6\nz = 1 + y if y else None\nprint z  #: 7\nz.x()  #: x()! 7\nif 1: # otherwise compiler won't compile z.x() later\n    z = None\ntry:\n    z.x()\nexcept ValueError:\n    print 'unwrap failed'  #: unwrap failed\n\nprint Optional(1) + Optional(2)  #: 3\nprint Optional(1) + 3  #: 4\nprint 1 + Optional(1)  #: 2\n\n#%% optional_tuple,barebones\na = None\nif True:\n    a = ('x', 's')\nprint(a)  #: ('x', 's')\nprint(*a, (1, *a))  #: x s (1, 'x', 's')\nx,y=a\nprint(x,y,[*a]) #: x s ['x', 's']\n\n#%% default_type_none\nclass Test:\n    value: int\n    def __init__(self, value: int):\n        self.value = value\n    def __repr__(self):\n        return str(self.value)\ndef key_func(k: Test):\n    return k.value\nprint sorted([Test(1), Test(3), Test(2)], key=key_func)  #: [1, 2, 3]\nprint sorted([Test(1), Test(3), Test(2)], key=lambda x: x.value)  #: [1, 2, 3]\nprint sorted([1, 3, 2])  #: [1, 2, 3]\n\n#%% nested_map\nprint list(map(lambda i: i-2, map(lambda i: i+1, range(5))))\n#: [-1, 0, 1, 2, 3]\n\ndef h(x: list[int]):\n    return x\nprint h(list(map(lambda i: i-1, map(lambda i: i+2, range(5)))))\n#: [1, 2, 3, 4, 5]\n\n#%% func_unify_error,barebones\ndef foo(x:int):\n    print x\nz = 1 & foo #! 'foo(...)' does not match expected type 'int'\n\n#%% tuple_type_late,barebones\ncoords = []\nfor i in range(2):\n    coords.append( ('c', i, []) )\ncoords[0][2].append((1, 's'))\nprint(coords)  #: [('c', 0, [(1, 's')]), ('c', 1, [])]\n\n#%% instantiate_swap,barebones\nclass Foo[T, U]:\n    t: T\n    u: U\n    def __init__(self):\n        self.t = T()\n        self.u = U()\n    def __str__(self):\n        return f'{self.t} {self.u}'\nprint Foo[int, bool](), Foo[bool, int]() #: 0 False False 0\n\n#%% static_fail,barebones\ndef test(i: Int[32]):\n    print int(i)\ntest(Int[5](1)) #! 'Int[5]' does not match expected type 'Int[32]'\n\n#%% static_fail_2,barebones\nzi = Int[32](6)\ndef test3[N](i: Int[N]):\n    print int(i)\ntest3(zi) #! expected type expression\n# TODO: nicer error message!\n\n#%% static_fail_3,barebones\nzi = Int[32](6)\ndef test3[N: Literal[int]](i: Int[N]):\n    print int(i)\ntest3(1, int) #! expected static expression\n# TODO: nicer error message!\n\n#%% nested_fn_generic,barebones\ndef f(x):\n    def g(y):\n        return y\n    return g(x)\nprint f(5), f('s') #: 5 s\n\ndef f2[U](x: U, y):\n    def g[T, U](x: T, y: U):\n        return (x, y)\n    return g(y, x)\nx, y = 1, 'haha'\nprint f2(x, y).__class__.__name__ #: Tuple[str,int]\nprint f2('aa', 1.1, U=str).__class__.__name__ #: Tuple[float,str]\n\n#%% nested_fn_generic_error,barebones\ndef f[U](x: U, y): # ('u, 'a) -> tuple['a, 'u]\n    def g[T, U](x: T, y: U): # ('t, 'u) -> tuple['t, 'u]\n        return (x, y)\n    return g(y, x)\nprint f(1.1, 1, int).__class__.__name__ #! 'float' does not match expected type 'int'\n\n#%% fn_realization,barebones\ndef ff[T](x: T, y: tuple[T]):\n      print ff(T=str,...).__fn_name__ #: ff[str;str,Tuple[str]]\n      return x\nx = ff(1, (1,))\nprint x, x.__class__.__name__ #: 1 int\n# print f.__class__.__name__  # TODO ERRORS\n\ndef fg[T](x:T):\n    def g[T](y):\n        z = T()\n        return z\n    print fg(T=str,...).__fn_name__  #: fg[str;str]\n    print g(1, T).__class__.__name__ #: int\nfg(1)\nprint fg(1).__class__.__name__ #: NoneType\n\ndef f[T](x: T):\n    print f(x, T).__class__.__name__  #: int\n    print f(x).__class__.__name__      #: int\n    print f(x, int).__class__.__name__ #: int\n    return x\nprint f(1), f(1).__class__.__name__ #: 1 int\nprint f(1, int).__class__.__name__ #: int\n\n#%% fn_realization_error,barebones\ndef f[T](x: T):\n    print f(x, int).__class__.__name__\n    return x\nf('s')\n#! 'str' does not match expected type 'int'\n#! during the realization of f(x: str, T: str)\n\n#%% func_arg_instantiate,barebones\nclass A[T]:\n    y: Optional[T] = None\n    def foo(self, y: T):\n        self.y = y\n        return y\n    def bar(self, y):\n        return y\na = A()\nprint a.__class__.__name__ #: A[int]\na.y = 5\nprint a.__class__.__name__ #: A[int]\n\nb = A()\nprint b.foo(5) #: 5\nprint b.__class__.__name__, b.y #: A[int] 5\nprint b.bar('s'), b.bar('s').__class__.__name__ #: s str\nprint b.bar(5), b.bar(5).__class__.__name__ #: 5 int\n\naa = A()\nprint aa.foo('s') #: s\nprint aa.__class__.__name__, aa.y, aa.bar(5.1).__class__.__name__ #: A[str] s float\n\n#%% no_func_arg_instantiate_err,barebones\nclass A[T]:\n    y: Optional[T] = None\n    def foo(self, y): self.y = y\na = A()\na.foo(1)\n#! 'Optional[int]' does not match expected type 'Optional[NoneType]'\n#! during the realization\n\n#%% return_deduction,barebones\ndef fun[T, R](x, y: T) -> R:\n   \tdef ffi[T, R, Z](x: T, y: R, z: Z):\n   \t\treturn (x, y, z)\n   \tyy = ffi(False, byte(2), 's', T=bool, Z=str, R=R)\n   \tyz = ffi(1, byte(2), 's', T=int, Z=str, R=R)\n   \treturn byte(1)\nprint fun(2, 1.1, float, byte).__class__.__name__ #: byte\n\n#%% return_auto_deduction_err,barebones\ndef fun[T, R](x, y: T) -> R:\n   \treturn byte(1)\nprint fun(2, 1.1).__class__.__name__ #! cannot typecheck\n\n#%% random\n# shuffle used to fail before for some reason (sth about unbound variables)...\ndef foo():\n    from random import shuffle\n    v = list(range(10))\n    shuffle(v)\n    print sorted(v) #: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\nfoo()\n\n#%% function_type,barebones\nclass F:\n    f: Function[[int], int]\n    g: function[[int], None]\n    x: int\ndef foo(x: int):\n    return x+1\ndef goo(x: int):\n    print x+2\nf = F(foo, goo, 2)\nprint f.f(f.x) #: 3\nf.g(f.x) #: 4\n\ndef hoo(z):\n    print z+3\nf.g = hoo\nf.g(f.x)  #: 5\n\ndef hai(x, y, z):\n    print f'hai({x},{y},{z})'\nfn = Function[[int, int, int], None](hai)\nfn(1, 2, 3) #: hai(1,2,3)\nprint str(fn)[:12] #: <function at\nz = fn(2, 3, ...)\nz(3) #: hai(2,3,3)\n\n#%% int_float,barebones\nl = [1., 2, 3, 4]\nprint l, l.__class__.__name__ #: [1, 2, 3, 4] List[float]\n\n#%% forward_fn,barebones\ndef test(name, sort, key):\n    v1 = [1, 2, 3, 4]\n    sp = sort(v1, key)\n    print name, sp\ndef foo(l, f):\n    return [f(i) for i in l]\ntest('hi', foo, lambda x: x+1) #: hi [2, 3, 4, 5]\n\ndef foof(l: List[int], x, f: CallableTrait[[int], int]):\n    return [f(i)+x for i in l]\ntest('qsort', foof(x=3, ...), lambda x: x+1) #: qsort [5, 6, 7, 8]\n\n#%% mutually_recursive_error,barebones\ndef bl(x):\n    return True\ndef frec(x, y):\n    def grec(x, y):\n        return frec(y, x)\n    return grec(x, y) if bl(y) else 2\nprint frec(1, 2).__class__.__name__, frec('s', 1).__class__.__name__\n#! 'NoneType' does not match expected type 'int'\n#! during the realization of frec(x: int, y: int)\n\n#%% type_error_reporting\n# TODO: improve this certainly\ndef tee(iterable, n=2):\n    from collections import deque\n    it = iter(iterable)\n    deques = [deque() for i in range(n)]\n    def gen(mydeque):\n        while True:\n            if not mydeque:             # when the local deque is empty\n                if it.done():\n                    return\n                newval = it.__next__()\n                for d in deques:        # load it to all the deques\n                    d.append(newval)\n            yield mydeque.popleft()\n    return list(gen(d) for d in deques)\nit = [1,2,3,4]\na, b = tee(it)\n#! cannot typecheck\n#! cannot typecheck\n#! during the realization of tee(iterable: List[int], n: int)\n\n#%% new_syntax,barebones\ndef foo[T,U](x: type, y, z: Literal[int] = 10):\n    print T.__class__.__name__, U.__class__.__name__, x.__class__.__name__, y.__class__.__name__, Int[z+1].__class__.__name__\n    return List[x]()\nprint foo(T=int,U=str,...).__fn_name__ #: foo[x,z,int,str;T1]\nprint foo(T=int,U=str,z=5,x=bool,...).__fn_name__ #: foo[bool,5,int,str;T1]\nprint foo(float,3,T=int,U=str,z=5).__class__.__name__ #: List[float]\nfoo(float,1,10,str,int) #: str int float int Int[11]\n\n\nclass Foo[T,U: Literal[int]]:\n    a: T\n    b: Literal[int]\n    c: Int[U]\n    d: type\n    e: List[d]\n    f: UInt[b]\nprint Foo[5,int,float,6].__class__.__name__ #: Foo[5,int,float,6]\nprint Foo(1.1, 10i32, [False], 10u66).__class__.__name__ #: Foo[66,bool,float,32]\n\n\ndef foo2[N: Literal[int]]():\n    print Int[N].__class__.__name__, N\nx: Literal[int] = 5\ny: Literal[int] = 105 - x * 2\nfoo2(y-x) #: Int[90] 90\n\nif 1.1+2.2 > 0:\n    z: Literal[int] = 88\n    print z #: 88\nprint x #: 5\nx : Literal[int] = 3\nprint x #: 3\n\ndef fox(N: Literal[int] = 4):\n    print Int[N].__class__.__name__, N\nfox(5) #: Int[5] 5\nfox() #: Int[4] 4\n\n#%% new_syntax_err,barebones\nclass Foo[T,U: Literal[int]]:\n    a: T\n    b: Literal[int]\n    c: Int[U]\n    d: type\n    e: List[d]\n    f: UInt[b]\nprint Foo[float,6].__class__.__name__ #! Foo takes 4 generics (2 given)\n\n#%% type_arg_transform,barebones\nprint list(map(str, range(5)))\n#: ['0', '1', '2', '3', '4']\n\n\n#%% traits,barebones\ndef t[T](x: T, key: Optional[CallableTrait[[T], S]] = None, S: type = NoneType):\n    if isinstance(S, NoneType):\n        return x\n    else:\n        return (key.__val__())(x)\nprint t(5) #: 5\nprint t(6, lambda x: f'#{x}') #: #6\n\nz: CallableTrait[[int],int] = lambda x: x+1\nprint z(5) #: 6\n\ndef foo[T](x: T, func: Optional[CallableTrait[[], T]] = None) -> T:\n    return x\nprint foo(1) #: 1\n\n#%% trait_callable\nfoo = [1,2,11]\nprint(sorted(foo, key=str))\n#: [1, 11, 2]\n\nfoo = {1: \"a\", 2: \"a\", 11: \"c\"}\nprint(sorted(foo.items(), key=str))\n#: [(1, 'a'), (11, 'c'), (2, 'a')]\n\ndef call(f: CallableTrait[[int,int], Tuple[str,int]]):\n    print(f(1, 2))\n\ndef foo(*x): return f\"{x}_{x.__class__.__name__}\",1\ncall(foo)\n#: ('(1, 2)_Tuple[int,int]', 1)\n\ndef foo(a:int, *b: int): return f\"f1_{a}_{b}\", a+b[0]\ncall(foo)\n#: ('f1_1_(2,)', 3)\ndef foo(a:int, *b: float): return f\"f2_{a}_{b}\", int(a+b[0])\ncall(foo)\n#: ('f2_1_(2,)', 3)\n\ndef call(f: CallableTrait[[int,int],str]):\n    print(f(1, 2))\ndef foo(a: int, *b: int, **kw): return f\"{a}_{b}_{kw}\"\ncall(foo(zzz=1.1, ...))\n#: 1_(2,)_(zzz: 1.1)\n\n#%% traits_error,barebones\ndef t[T](x: T, key: Optional[CallableTrait[[T], S]] = None, S: type = NoneType):\n    if isinstance(S, NoneType):\n        return x\n    else:\n        return (key.__val__())(x)\nprint t(6, Optional(1)) #! 'Optional[int]' does not match expected type 'Optional[CallableTrait[[int],S]]'\n\n#%% traits_error_2,barebones\nz: CallableTrait[[int],int] = 4 #! 'CallableTrait[[int],int]' does not match expected type 'int'\n\n#%% trait_defdict\nclass dd(Static[Dict[K,V]]):\n    fn: S\n    K: type\n    V: type\n    S: TypeTrait[CallableTrait[[], V]]\n\n    def __init__(self: dd[K, VV, Function[[], V]], VV: TypeTrait[V]):\n        self.fn = lambda: VV()\n\n    def __init__(self, f: S):\n        self.fn = f\n\n    def __getitem__(self, key: K) -> V:\n        if key not in self:\n            self.__setitem__(key, self.fn())\n        return super().__getitem__(key)\n\n\nx = dd(list)\nx[1] = [1, 2]\nprint(x[2])\n#: []\nprint(x)\n#: {1: [1, 2], 2: []}\n\nz = 5\ny = dd(lambda: z+1)\ny.update({'a': 5})\nprint(y['b'])\n#: 6\nz = 6\nprint(y['c'])\n#: 6\n# TODO: should be 7 once by-ref capture lands\nprint(y)\n#: {'a': 5, 'b': 6, 'c': 6}\n\nxx = dd(lambda: 'empty')\nxx.update({1: 's', 2: 'b'})\nprint(xx[1], xx[44])\n#: s empty\nprint(xx)\n#: {44: 'empty', 1: 's', 2: 'b'}\n\ns = 'mississippi'\nd = dd(int)\nfor k in s:\n    d[k] = d[\"x\" + k]\nprint(sorted(d.items()))\n#: [('i', 0), ('m', 0), ('p', 0), ('s', 0), ('xi', 0), ('xm', 0), ('xp', 0), ('xs', 0)]\n\n\n#%% kwargs_getattr,barebones\ndef foo(**kwargs):\n    print kwargs['foo'], kwargs['bar']\n\nfoo(foo=1, bar='s')\n#: 1 s\n\n\n\n#%% union_types,barebones\ndef foo_int(x: int):\n    print(f'{x} {x.__class__.__name__}')\ndef foo_str(x: str):\n    print(f'{x} {x.__class__.__name__}')\ndef foo(x):\n    print(f'{x} {int(Union._get_tag(x))} {x.__class__.__name__}')\n\na: Union[int, str] = 5\nfoo_int(a)  #: 5 int\nfoo(a)  #: 5 0 Union[int | str]\nprint(static.len(a))  #: 2\nprint(static.len(Union[int, int]), static.len(Tuple[int, float, int]))  #: 1 3\n\n@extend\nclass str:\n    def __add__(self, i: int):\n        return int(self) + i\n\na += 6  ## this is U.__new__(a.__getter__(__add__)(59))\nb = a + 59\nprint(a, b, a.__class__.__name__, b.__class__.__name__)  #: 11 70 Union[int | str] int\n\nif True:\n    a = 'hello'\n    foo_str(a)  #: hello str\n    foo(a)  #: hello 1 Union[int | str]\n    b = a[1:3]\n    print(b)  #: el\nprint(a)  #: hello\n\na: Union[Union[Union[str], int], Union[int, int, str]] = 9\nfoo(a)  #: 9 0 Union[int | str]\n\ndef ret(x):\n    z : Union = x\n    if x < 1: z = 1\n    elif x < 10: z = False\n    else: z = 'oops'\n    return z\nr = ret(2)\nprint(r, r.__class__.__name__)  #: False Union[bool | int | str]\nr = ret(33.3)\nprint(r, r.__class__.__name__)  #: oops Union[bool | float | int | str]\n\ndef ret2(x) -> Union:\n    if x < 1: return 1\n    elif x < 10: return 2.2\n    else: return ['oops']\nr = ret2(20)\nprint(r, r.__class__.__name__)  #: ['oops'] Union[List[str] | float | int]\n\nclass A:\n    x: int\n    def foo(self):\n        return f\"A: {self.x}\"\nclass B:\n    y: str\n    def foo(self):\n        return f\"B: {self.y}\"\nx : Union[A,B] = A(5)  # TODO: just Union does not work in test mode :/\nprint(x.foo())  #: A: 5\nprint(x.x)  #: 5\nif True:\n    x = B(\"bee\")\nprint(x.foo())  #: B: bee\nprint(x.y)  #: bee\ntry:\n    print(x.x)\nexcept TypeError as e:\n    print(e.message)  #: invalid union call 'x'\n\ndef do(x: A):\n    print('do', x.x)\ntry:\n    do(x)\nexcept TypeError:\n    print('error') #: error\n\ndef do2(x: B):\n    print('do2', x.y)\ndo2(x)  #: do2 bee\n\nz: Union[int, str] = 1\nprint isinstance(z, int), isinstance(z, str), isinstance(z, float), isinstance(z, Union[int, float]), isinstance(z, Union[int, str])\n#: True False False False True\n\nprint isinstance(z, Union[int]), isinstance(z, Union[int, float, str])\n#: False False\n\nif True:\n    z = 's'\nprint isinstance(z, int), isinstance(z, str), isinstance(z, float), isinstance(z, Union[int, float]), isinstance(z, Union[int, str])\n#: False True False False True\n\nclass A:\n    def foo(self): return 1\nclass B:\n    def foo(self): return 's'\nclass C:\n    def foo(self): return [True, False]\nx : Union[A,B,C] = A()\nprint x.foo(), x.foo().__class__.__name__\n#: 1 Union[List[bool] | int | str]\n\nxx = Union[int, str](0)\nprint(xx)  #: 0\n\n#%% union_error,barebones\na: Union[int, str] = 123\nprint(123 == a)  #: True\nprint(a == 123)  #: True\ntry:\n    a = \"foo\"\n    print(a == 123)\nexcept TypeError:\n    print(\"oops\", a)  #: oops foo\n\n\n#%% delayed_lambda_realization,barebones\nx = []\nfor i in range(2):\n    print(all(x[j] < 0 for j in range(i)))\n    x.append(i)\n#: True\n#: False\n\n#%% no_generic,barebones\ndef foo(a, b: Literal[int]):\n    pass\nfoo(5)  #! generic 'b' not provided\n\n\n#%% no_generic_2,barebones\ndef f(a, b, T: type):\n    print(a, b)\nf(1, 2)  #! generic 'T' not provided\n\n#%% variardic_tuples,barebones\nna: Tuple[5, str] = ('a', 'b', 'c', 'd', 'e')\nprint(na, na.__class__.__name__)\n#: ('a', 'b', 'c', 'd', 'e') Tuple[str,str,str,str,str]\n\nnb = Tuple[5, str]('a', 'b', 'c', 'd', 'e')\nprint(nb, nb.__class__.__name__)\n#: ('a', 'b', 'c', 'd', 'e') Tuple[str,str,str,str,str]\n\nclass Foo[N: Literal[int], T: type]:\n    x: Tuple[N, T]\n    def __init__(self, t: T):\n        self.x = (t, ) * N\n\nf = Foo[5, str]('hi')\nprint(f.__class__.__name__)\n#: Foo[5,str]\nprint(f.x.__class__.__name__)\n#: Tuple[str,str,str,str,str]\nprint(f.x)\n#: ('hi', 'hi', 'hi', 'hi', 'hi')\n\nf = Foo[2,int](1)\nprint(f.__class__.__name__)\n#: Foo[2,int]\nprint(f.x.__class__.__name__)\n#: Tuple[int,int]\nprint(f.x)\n#: (1, 1)\nf.x = (3, 4)\nprint(f.x)\n#: (3, 4)\n\nprint(Tuple[int, int].__class__.__name__)\n#: Tuple[int,int]\nprint(Tuple[3, int].__class__.__name__)\n#: Tuple[int,int,int]\nprint(Tuple[0].__class__.__name__)\n#: Tuple\nprint(Tuple[-5, int].__class__.__name__)\n#: Tuple\nprint(Tuple[5, int, str].__class__.__name__)\n#: Tuple[int,str,int,str,int,str,int,str,int,str]\n\ndef foo(t: Tuple[N, int], N: Literal[int]):\n    print(\"foo\", N, t)\nfoo((1, 2, 3))\n#: foo 3 (1, 2, 3)\nfoo((1, 2, 3, 4, 5))\n#: foo 5 (1, 2, 3, 4, 5)\n\n\n#%% union_hasattr,barebones\nclass A:\n    def foo(self):\n        print('foo')\n    def bar(self):\n        print('bar')\nclass B:\n    def foo(self):\n        print('foo')\n    def baz(self):\n        print('baz')\n\na = A()\nprint(hasattr(a, 'foo'), hasattr(a, 'bar'), hasattr(a, 'baz'))\n#: True True False\nb = B()\nprint(hasattr(b, 'foo'), hasattr(b, 'bar'), hasattr(b, 'baz'))\n#: True False True\n\nc: Union[A, B] = A()\nprint(hasattr(c, 'foo'), hasattr(c, 'bar'), hasattr(c, 'baz'))\n#: True True False\n\nc = B()\nprint(hasattr(c, 'foo'), hasattr(c, 'bar'), hasattr(c, 'baz'))\n#: True False True\n\n\n#%% delayed_dispatch\nimport math\ndef fox(a, b, key=None): # key=None delays it!\n    return a if a <= b else b\n\na = 1.0\nb = 2.0\nc = fox(a, b)\nprint(math.log(c) / 2) #: 0\n\n#%% repeated_lambda,barebones\ndef acc(i, func=lambda a, b: a + b):\n    return i + func(i, i)\nprint acc(1)  #: 3\nprint acc('i')  #: iii\n\nx = 1\ndef const(value):\n    return lambda: (value, x)\nprint const(5)() #: (5, 1)\nprint const('s')() #: ('s', 1)\nx = 's'\nprint const(5)() #: (5, 's')\nprint const('s')() #: ('s', 's')\n\n\n#%% type_variables_pass,barebones\ndef foo(a):\n    print(a.__class__.__name__, a)\n    print(a().__class__.__name__, a())\n\nfoo(float)\n#: float <class 'float'>\n#: float 0\nprint(float)\n#: <class 'float'>\nfoo(list[int])\n#: List[int] <class 'List[int]'>\n#: List[int] []\nprint(list[int])\n#: <class 'List[int]'>\nfoo(type(list[int]))\n#: List[int] <class 'List[int]'>\n#: List[int] []\n\n# TODO: print(list)\n\ndef typtest(a, b):\n    print isinstance(a, b)\n    print isinstance(a, int)\n    print(a)\n    print(b)\n    print(a.__repr__())\n\ntyptest(int, int)\n#: True\n#: True\n#: <class 'int'>\n#: <class 'int'>\n#: <class 'int'>\ntyptest(int, float)\n#: False\n#: True\n#: <class 'int'>\n#: <class 'float'>\n#: <class 'int'>\n\nprint(List[int])\nprint(List[int].__repr__())\n# print(int.__repr__())  # this catches int.__repr__ as it should...\nprint(type(int).__repr__())\n#: <class 'List[int]'>\n#: <class 'List[int]'>\n#: <class 'int'>\n\n\n#%% tuple_heterogenous_1,barebones\nt = (1, 3.14, 'x')\nfor i in range(len(t)):\n    t[i]\n#! expected iterable expression\n#! during the realization of __getitem__\n\n#%% tuple_heterogenous_2,barebones\n@tuple\nclass A:\n    n: int\n    x: float\nA(1, 1.1)[1]\n#! expected iterable expression\n#! during the realization of __getitem__\n"
  },
  {
    "path": "test/parser/typecheck/test_loops.codon",
    "content": "#%% while_else,barebones\na = 1\nwhile a:\n    print a #: 1\n    a -= 1\nelse:\n    print 'else' #: else\na = 1\nwhile a:\n    print a #: 1\n    a -= 1\nelse not break:\n    print 'else' #: else\nwhile True:\n    print 'infinite' #: infinite\n    break\nelse:\n    print 'nope'\n\n#%% for_assignment,barebones\nl = [[1, 2, 3, 4], [5, 6, 7, 8, 9], [10, 11]]\nfor a, *m, b in l:\n    print a + b, len(m)\n#: 5 2\n#: 14 3\n#: 21 0\n\n#%% for_else,barebones\nfor i in [1]:\n    print i #: 1\nelse:\n    print 'else' #: else\nfor i in [1]:\n    print i #: 1\nelse not break:\n    print 'else' #: else\nfor i in [1]:\n    print i #: 1\n    break\nelse:\n    print 'nope'\n\nbest = 4\nfor s in [3, 4, 5]:\n    for i in [s]:\n        if s >= best:\n            print('b:', best)\n            break\n    else:\n        print('s:', s)\n        best = s\n#: s: 3\n#: b: 3\n#: b: 3\n\n\n#%% loop_domination,barebones\nfor i in range(2):\n    try: dat = 1\n    except: pass\n    print(dat)\n#: 1\n#: 1\n\ndef comprehension_test(x):\n    for n in range(3):\n        print('>', n)\n    l = ['1', '2', str(x)]\n    x = [n for n in l]\n    print(x, n)\ncomprehension_test(5)\n#: > 0\n#: > 1\n#: > 2\n#: ['1', '2', '5'] 2\n\n#%% while,barebones\na = 3\nwhile a:\n    print a\n    a -= 1\n#: 3\n#: 2\n#: 1\n\n#%% for_break_continue,barebones\nfor i in range(10):\n    if i % 2 == 0:\n        continue\n    print i\n    if i >= 5:\n        break\n#: 1\n#: 3\n#: 5\n\n#%% for_error,barebones\nfor i in 1:\n    pass\n#! '1' object has no attribute '__iter__'\n\n#%% for_void,barebones\ndef foo(): yield\nfor i in foo():\n    print i.__class__.__name__  #: NoneType\n\n#%% hetero_iter,barebones\ne = (1, 2, 3, 'foo', 5, 'bar', 6)\nfor i in e:\n    if isinstance(i, int):\n        if i == 1: continue\n    if isinstance(i, str):\n        if i == 'bar': break\n    print i\n\n#%% static_for,barebones\ndef foo(i: Literal[int]):\n    print('static', i, Int[i].__class__.__name__)\n\nfor i in static.tuple(1, 2, 3, 4, 5):\n    foo(i)\n    if i == 3: break\n#: static 1 Int[1]\n#: static 2 Int[2]\n#: static 3 Int[3]\nfor i in static.range(9, 4, -2):\n    foo(i)\n    if i == 3:\n        break\n#: static 9 Int[9]\n#: static 7 Int[7]\n#: static 5 Int[5]\nfor i in static.tuple(\"x\", 1, 3.3, 2):\n    print(i)\n#: x\n#: 1\n#: 3.3\n#: 2\n\nprint tuple(Int[i+10](i) for i in static.tuple(1, 2, 3)).__class__.__name__\n#: Tuple[Int[11],Int[12],Int[13]]\n\nfor i in static.range(0, 10):\n    if i % 2 == 0: continue\n    if i > 8: break\n    print('xyz', Int[i].__class__.__name__)\nprint('whoa')\n#: xyz Int[1]\n#: xyz Int[3]\n#: xyz Int[5]\n#: xyz Int[7]\n#: whoa\n\nfor i in static.range(15):\n    if i % 2 == 0: continue\n    if i > 8: break\n    print('xyz', Int[i].__class__.__name__)\nprint('whoa')\n#: xyz Int[1]\n#: xyz Int[3]\n#: xyz Int[5]\n#: xyz Int[7]\n#: whoa\n\nprint tuple(Int[i-10](i) for i in static.range(30,33)).__class__.__name__\n#: Tuple[Int[20],Int[21],Int[22]]\n\nfor i in static.tuple(0, 2, 4, 7, 11, 12, 13):\n    if i % 2 == 0: continue\n    if i > 8: break\n    print('xyz', Int[i].__class__.__name__)\nprint('whoa')\n#: xyz Int[7]\n#: whoa\n\nfor i in static.range(10):  # TODO: large values are too slow!\n    pass\nprint('done')\n#: done\n\ntt = (5, 'x', 3.14, False, [1, 2])\nfor i, j in static.enumerate(tt):\n    print(foo(i * 2 + 1), j)\n#: static 1 Int[1]\n#: None 5\n#: static 3 Int[3]\n#: None x\n#: static 5 Int[5]\n#: None 3.14\n#: static 7 Int[7]\n#: None False\n#: static 9 Int[9]\n#: None [1, 2]\n\nprint tuple((Int[i+1](i), j) for i, j in static.enumerate(tt)).__class__.__name__\n#: Tuple[Tuple[Int[1],int],Tuple[Int[2],str],Tuple[Int[3],float],Tuple[Int[4],bool],Tuple[Int[5],List[int]]]\n\n#%% static_range_error,barebones\nfor i in static.range(1000, -2000, -2):\n    pass\n#! static.range too large (expected 0..1024, got instead 1500)\n\n#%% continue_error,barebones\ncontinue #! 'continue' outside loop\n\n#%% break_error,barebones\nbreak #! 'break' outside loop\n"
  },
  {
    "path": "test/parser/typecheck/test_op.codon",
    "content": "\n#%% unary,barebones\na, b = False, 1\nprint not a, not b, ~b, +b, -b, -(+(-b)) #: True False -2 1 -1 1\n\n#%% binary_simple,barebones\nx, y = 1, 0\nc = [1, 2, 3]\n\nprint x and y, x or y #: 0 1\nprint x in c, x not in c #: True False\nprint c is c, c is not c #: True False\n\nz: Optional[int] = None\nprint z is None, None is z, None is not z, None is None #: True True False True\n\n#%% binary_and_or,barebones\ns = 'hehe'\na = 'hehe' or 5.2\nprint(a, a.__class__.__name__)  #: hehe str\na = 'hehe' and 5.2\nprint(a, a.__class__.__name__)  #: 5.2 float\na = s and 5.2\nprint(a, a.__class__.__name__)  #: 5.2 Union[float | str]\n\ne = ''\na = '' or 5.1\nprint(a, a.__class__.__name__)  #: 5.1 float\na = e or 5.1\nprint(a, a.__class__.__name__)  #: 5.1 Union[float | str]\na = '' and 5.1\nprint(a, a.__class__.__name__)  #:  str\n\ns = 'hehe'\na = s or 'aaa'\nprint(a, a.__class__.__name__)  #: hehe str\na = s and 'aaa'\nprint(a, a.__class__.__name__)  #: aaa str\n\ns = ''\na = s or 'bbb'\nprint(a, a.__class__.__name__)  #: bbb str\na = s and 'bbb'\nprint(a, a.__class__.__name__)  #:  str\n\ns = 'hehe'\na = s or 1\nprint(a, a.__class__.__name__)  #: hehe Union[int | str]\na = s and 1\nprint(a, a.__class__.__name__)  #: 1 Union[int | str]\n\ns = ''\na = s or 1\nprint(a, a.__class__.__name__)  #: 1 Union[int | str]\na = s and 1\nprint(a, a.__class__.__name__)  #:  Union[int | str]\n\ndef foo():\n    print('foo')\n    return True\n\ndef bar():\n    print('bar')\n    return False\n\nprint(foo() or bar())\n#: foo\n#: True\nprint(bar() or foo())\n#: bar\n#: foo\n#: True\nprint(foo() and bar())\n#: foo\n#: bar\n#: False\nprint(bar() and foo())\n#: bar\n#: False\n\n#%% chain_binary,barebones\ndef foo():\n    print 'foo'\n    return 15\na = b = c = foo() #: foo\nprint a, b, c #: 15 15 15\n\nx = y = []\nx.append(1)\nprint x, y #: [1] [1]\n\nprint 1 <= foo() <= 10 #: foo\n#: False\nprint 15 >= foo()+1 < 30 > 20 > foo()\n#: foo\n#: False\nprint 15 >= foo()-1 < 30 > 20 > foo()\n#: foo\n#: foo\n#: True\n\nprint True == (b == 15) #: True\n\n#%% pipe_error,barebones\ndef b(a, b, c, d):\n    pass\n1 |> b(1, ..., 2, ...)  #! multiple ellipsis expressions\n\n#%% index_normal,barebones\nt: tuple[int, int] = (1, 2)\nprint t #: (1, 2)\n\ntt: Tuple[int] = (1, )\nprint tt #: (1,)\n\ndef foo(i: int) -> int:\n    return i + 1\nf: CallableTrait[[int], int] = foo\nprint f(1) #: 2\nfx: function[[int], int] = foo\nprint fx(2) #: 3\nfxx: Function[[int], int] = foo\nprint fxx(3) #: 4\n\n#%% index_special,barebones\nclass Foo:\n    def __getitem__(self, foo):\n        print foo\nf = Foo()\nf[0,0] #: (0, 0)\nf[0,:] #: (0, slice(None, None, None))\nf[:,:] #: (slice(None, None, None), slice(None, None, None))\nf[:,0] #: (slice(None, None, None), 0)\n\n#%% index_error,barebones\nPtr[9.99] #! expected type expression\n\n#%% index_error_b,barebones\nPtr['s'] #! expected type expression\n\n#%% index_error_static,barebones\nPtr[1] #! expected type expression\n\n#%% index_error_2,barebones\nPtr[int, 's'] #! Ptr takes 1 generics (2 given)\n\n#%% index_error_3,barebones\nPtr[1, 's'] #! Ptr takes 1 generics (2 given)\n\n#%% callable_error,barebones\ndef foo(x: CallableTrait[[]]): pass  #! CallableTrait takes 2 generics (1 given)\n\n#%% binary,barebones\n@extend\nclass float:\n    def __add__(self, i: int):\n        print 'add'; return 0\n    def __sub__(self, i: int):\n        print 'sub'; return 0\n    def __mul__(self, i: int):\n        print 'mul'; return 0\n    def __pow__(self, i: int):\n        print 'pow'; return 0\n    def __truediv__(self, i: int):\n        print 'truediv'; return 0\n    def __floordiv__(self, i: int):\n        print 'div'; return 0\n    def __matmul__(self, i: int):\n        print 'matmul'; return 0\n    def __mod__(self, i: int):\n        print 'mod'; return 0\n    def __lt__(self, i: int):\n        print 'lt'; return 0\n    def __le__(self, i: int):\n        print 'le'; return 0\n    def __gt__(self, i: int):\n        print 'gt'; return 0\n    def __ge__(self, i: int):\n        print 'ge'; return 0\n    def __eq__(self, i: int):\n        print 'eq'; return 0\n    def __ne__(self, i: int):\n        print 'ne'; return 0\n    def __lshift__(self, i: int):\n        print 'lshift'; return 0\n    def __rshift__(self, i: int):\n        print 'rshift'; return 0\n    def __and__(self, i: int):\n        print 'and'; return 0\n    def __or__(self, i: int):\n        print 'or'; return 0\n    def __xor__(self, i: int):\n        print 'xor'; return 0\n# double assignment to disable propagation\ndef f(x): return x\na = f(1.0)\na = f(5.0)\na + f(1) #: add\n# wrap in function to disable canonicalization\na - f(1) #: sub\na * f(2) #: mul\na ** f(2) #: pow\na // f(2) #: div\na / f(2) #: truediv\na @ f(1) #: matmul\na % f(1) #: mod\na < f(1) #: lt\na <= f(1) #: le\na > f(1) #: gt\na >= f(1) #: ge\na == f(1) #: eq\na != f(1) #: ne\na << f(1) #: lshift\na >> f(1) #: rshift\na & f(1) #: and\na | f(1) #: or\na ^ f(1) #: xor\n\n#%% binary_rmagic,barebones\nclass Foo[T]:\n    def __add__(self, other: T):\n        print 'add'\n        return self\n    def __radd__(self, other: T):\n        print 'radd'\n        return self\nfoo = Foo[int]()\nfoo + 1 #: add\n1 + foo #: radd\n\n#%% binary_short_circuit,barebones\ndef moo():\n    print 'moo'\n    return True\nprint True or moo() #: True\nprint moo() or True #: moo\n#: True\nprint False and moo() #: False\nprint moo() and False #: moo\n#: False\n\n#%% binary_is,barebones\nprint 5 is None #: False\nprint None is None #: True\nprint (None if bool(True) else 1) is None #: True\nprint (None if bool(False) else 1) is None #: False\n\nprint 5 is 5.0 #: False\nprint 5 is 6 #: False\nprint 5 is 5 #: True\nprint 5 is 1.12 #: False\nclass Foo:\n    a: int\nx = Foo(1)\ny = Foo(1)\nz = x\nprint x is x, x is y, x is z, z is x, z is y #: True False True True False\n\na, b, c, d = Optional(5), Optional[int](), Optional(5), Optional(4)\nprint a is a, a is b, b is b, a is c, a is d #: True False True True False\naa, bb, cc, dd = Optional(Foo(1)), Optional[Foo](), Optional(Foo(1)), Optional(Foo(2))\nprint aa is aa, aa is bb, bb is bb, aa is cc, aa is dd #: True False True False False\n\n\n#%% pipe,barebones\ndef foo(a, b):\n    return a+b\nbar = lambda c, d: c+d\ndef hai(e):\n    while e > 0:\n        yield e\n        e -= 2\ndef echo(s):\n    print s\nfoo(1,2) |> bar(4) |> echo  #: 7\nfoo(1,2) |> bar(4) |> hai |> echo\n#: 7\n#: 5\n#: 3\n#: 1\n\n#%% pipe_prepend,barebones\ndef foo(a: Optional[int]):\n    print a\n    return 1\n5 |> foo #: 5\nNone |> foo #: None\nprint (None |> foo).__class__.__name__ #: int\n\ndef foo2(a: int):\n    print a\n    return 1\nOptional(5) |> foo2 #: 5\ntry:\n    Optional[int]() |> foo2\nexcept ValueError as e:\n    print e.message #: optional unpack failed: expected int, got None\n\n#%% pipe_prepend_error,barebones\ndef foo2(a: int):\n    print a\n    return 1\ntry:\n    None |> foo2\nexcept ValueError:\n    print 'exception' #: exception\n# Explanation: None can also be Optional[Generator[int]]\n# We cannot decide if this is a generator to be unrolled in a pipe,\n# or just an argument to be passed to a function.\n# So this will default to NoneType at the end.\n\n#%% instantiate_err,barebones\ndef foo[N]():\n    return N()\nfoo(int, float)  #! foo() takes 1 arguments (2 given)\n\n#%% instantiate_err_2,barebones\ndef foo[N, T]():\n    return N()\nfoo(int)  #! generic 'T' not provided\n\n#%% instantiate_err_3,barebones\nPtr[int, float]()  #! Ptr takes 1 generics (2 given)\n\n#%% slice,barebones\nz = [1, 2, 3, 4, 5]\ny = (1, 'foo', True)\nprint z[2], y[1]  #: 3 foo\nprint z[:1], z[1:], z[1:3], z[:4:2], z[::-1]  #: [1] [2, 3, 4, 5] [2, 3] [1, 3] [5, 4, 3, 2, 1]\n\n#%% static_index,barebones\na = (1, '2s', 3.3)\nprint a[1] #: 2s\nprint a[0:2], a[:2], a[1:] #: (1, '2s') (1, '2s') ('2s', 3.3)\nprint a[0:3:2], a[-1:] #: (1, 3.3) (3.3,)\n\n#%% static_index_side,barebones\ndef foo(a):\n    print(a)\n    return a\n\nprint (foo(2), foo(1))[::-1]\n#: 2\n#: 1\n#: (1, 2)\nprint (foo(1), foo(2), foo(3), foo(4))[2]\n#: 1\n#: 2\n#: 3\n#: 4\n#: 3\n\n#%% static_index_lenient,barebones\na = (1, 2)\nprint a[3:5] #: ()\n\n#%% static_index_err,barebones\na = (1, 2)\na[5] #! tuple index out of range (expected 0..1, got instead 5)\n\n#%% static_index_err_2,barebones\na = (1, 2)\na[-3] #! tuple index out of range (expected 0..1, got instead -1)\n\n#%% index_func_instantiate,barebones\nclass X:\n    def foo[T](self, x: T):\n        print x.__class__.__name__, x\nx = X()\nx.foo(5, int) #: int 5\n\n#%% index,barebones\nl = [1, 2, 3]\nprint l[2] #: 3\n\n#%% index_two_rounds,barebones\nl = []\nprint l[::-1] #: []\nl.append(('str', 1, True, 5.15))\nprint l, l.__class__.__name__ #: [('str', 1, True, 5.15)] List[Tuple[str,int,bool,float]]\n\n#%% nested_generic,barebones\nx = Array[Array[int]](0)\nf = Optional[Optional[Optional[int]]](Optional[Optional[int]](Optional[int](5)))\nprint x.len, f  #: 0 5\n\n#%% static,barebones\nclass Num[N_: Literal[int]]:\n    def __str__(self):\n        return f'[{N_}]'\n    def __init__(self):\n        pass\ndef foo[N: Literal[int]]():\n    print Num[N*2]()\nfoo(3) #: [6]\n\nclass XX[N_: Literal[int]]:\n    a: Num[N_*2]\n    def __init__(self):\n        self.a = Num()\ny = XX[5]()\nprint y.a, y.__class__.__name__, y.a.__class__.__name__ #: [10] XX[5] Num[10]\n\n@tuple\nclass FooBar[N: Literal[int]]:\n    x: Int[N]\nz = FooBar(i32(5))\nprint z, z.__class__.__name__, z.x.__class__.__name__ #: (x: Int[32](5)) FooBar[32] Int[32]\n\n@tuple\nclass Foo[N: Literal[int]]:\n    x: Int[2*N]\n    def ctr(x: Int[2*N]) -> Foo[N]:\n        return Foo[N](x)\nfoo = Foo[10].ctr(Int[20](0))\nprint foo.__class__.__name__, foo.x.__class__.__name__ #: Foo[10] Int[20]\n\n#%% static_2,barebones\nclass Num[N: Literal[int]]:\n    def __str__(self):\n        return f'~{N}'\n    def __init__(self):\n        pass\nclass Foo[T, A: Literal[int], B: Literal[int]]:\n    a: Num[A+B]\n    b: Num[A-B]\n    c: Num[A if A > 3 else B]\n    t: T\n    def __init__(self):\n        self.a = Num()\n        self.b = Num()\n        self.c = Num()\n        self.t = T()\n    def __str__(self):\n        return f'<{self.a} {self.b} {self.c} :: {self.t}>'\nprint Foo[int, 3, 4](), Foo[int, 5, 4]()\n#: <~7 ~-1 ~4 :: 0> <~9 ~1 ~5 :: 0>\n\n#%% static_int,barebones\ndef foo(n: Literal[int]):\n    print n\n@overload\ndef foo(n: Literal[bool]):\n    print n\n\na: Literal[int] = 5\nfoo(a < 1)   #: False\nfoo(a <= 1)  #: False\nfoo(a > 1)   #: True\nfoo(a >= 1)  #: True\nfoo(a == 1)  #: False\nfoo(a != 1)  #: True\nfoo(a and 1) #: 1\nfoo(a or 1)  #: 5\nfoo(a + 1)   #: 6\nfoo(a - 1)   #: 4\nfoo(a * 1)   #: 5\nfoo(a // 2)  #: 2\nfoo(a % 2)   #: 1\nfoo(a & 2)   #: 0\nfoo(a | 2)   #: 7\nfoo(a ^ 1)   #: 4\n\n#%% static_str,barebones\nclass X:\n    s: Literal[str]\n    i: Int[1 + (s == \"abc\")]\n    def __init__(self: X[s], s: Literal[str]):\n        i = Int[1+(s==\"abc\")]()\n        print s, self.s, self.i.__class__.__name__\ndef foo(x: Literal[str], y: Literal[str]):\n    print x+y\nz: Literal[str] = \"woo\"\nfoo(\"he\", z)  #: hewoo\nX(s='lolo') #: lolo lolo Int[1]\nX('abc') #: abc abc Int[2]\n\ndef foo2(x: Literal[str]):\n    print(x, x.__is_static__)\ns: Literal[str] = \"abcdefghijkl\"\nfoo2(s)  #: abcdefghijkl True\nfoo2(s[1])  #: b True\nfoo2(s[1:5])  #: bcde True\nfoo2(s[10:50])  #: kl True\nfoo2(s[1:30:3])  #: behk True\nfoo2(s[::-1])  #: lkjihgfedcba True\n\n#%% static_short_circuit,barebones\nx = 3.14\nif isinstance(x, List) and x.T is float:\n    print('is list')\nelse:\n    print('not list')  #: not list\n\n#%% partial_star_pipe_args,barebones\niter(['A', 'C']) |> print\n#: A\n#: C\niter(range(4)) |> print('x', ..., 1)\n#: x 0 1\n#: x 1 1\n#: x 2 1\n#: x 3 1\n\n#%% partial_static_keep,barebones\ndef foo(x: Literal[int]):\n    return lambda: str(x)\nf = foo(5)\nprint foo(5)() #: 5\nprint foo(8)() #: 8\n\ndef itemgetter(item: Literal[int]):\n    return lambda o: o[item]\nprint itemgetter(1)([1, 2, 3])  #: 2\nprint itemgetter(2)(\"abc\")  #: c\n"
  },
  {
    "path": "test/parser/typecheck/test_parser.codon",
    "content": "#%% keyword_prefix,barebones\ndef foo(return_, pass_, yield_, break_, continue_, print_, assert_):\n    return_.append(1)\n    pass_.append(2)\n    yield_.append(3)\n    break_.append(4)\n    continue_.append(5)\n    print_.append(6)\n    assert_.append(7)\n    return return_, pass_, yield_, break_, continue_, print_, assert_\nprint foo([1], [1], [1], [1], [1], [1], [1])\n#: ([1, 1], [1, 2], [1, 3], [1, 4], [1, 5], [1, 6], [1, 7])\n\n#%% spaces,barebones\ndef space_test():\n    x = 0.77\n    y = 0.86\n    z = x/(1if((1if(y==0)else(y))==0)else((1if(y==0)else(y))))\n    print(z)  #: 0.895349\n\n    h = \"hello\"\n    b = ((True)or(False))or(((\"sR2Kt7\"))==(h))\n    print(b)  #: True\n\n    h2: Optional[str] = \"hi\"\n    h3 = \"r\"\n    b2 = (((h2)==None)and(h3)==(\"r\"))\n    print(b2)  #: False\nspace_test()"
  },
  {
    "path": "test/parser/typecheck/test_python.codon",
    "content": "#%% python\nfrom python import os\nprint os.name  #: posix\n\nfrom python import datetime\nz = datetime.datetime.utcfromtimestamp(0)\nprint z  #: 1970-01-01 00:00:00\n\n#%% python_numpy\nfrom python import numpy as np\na = np.arange(9).reshape(3, 3)\nprint a\n#: [[0 1 2]\n#:  [3 4 5]\n#:  [6 7 8]]\nprint a.dtype.name  #: int64\nprint np.transpose(a)\n#: [[0 3 6]\n#:  [1 4 7]\n#:  [2 5 8]]\nn = np.array([[1, 2], [3, 4]])\nprint n[0], n[0][0] + 1 #: [1 2] 2\n\na = np.array([1,2,3])\nprint(a + 1) #: [2 3 4]\nprint(a - 1) #: [0 1 2]\nprint(1 - a) #: [ 0 -1 -2]\n\n#%% python_import_fn\nfrom python import re.split(str, str) -> List[str] as rs\nprint rs(r'\\W+', 'Words, words, words.')  #: ['Words', 'words', 'words', '']\n\n#%% python_import_fn_2\nfrom python import os.system(str) -> int\nsystem(\"echo 'hello!'\")  #: hello!\n\n#%% python_pydef\n@python\ndef test_pydef(n) -> str:\n    return ''.join(map(str,range(n)))\nprint test_pydef(5)  #: 01234\n\n#%% python_pydef_nested\ndef foo():\n    @python\n    def pyfoo():\n        return 1\n    print pyfoo() #: 1\n    if True:\n        @python\n        def pyfoo2():\n            return 2\n        print pyfoo2() #: 2\n    pass\n    @python\n    def pyfoo3():\n        if 1:\n            return 3\n    return str(pyfoo3())\nprint foo() #: 3\n\n#%% python_pyobj\n@python\ndef foofn() -> Dict[pyobj, pyobj]:\n    return {\"str\": \"hai\", \"int\": 1}\n\nfoo = foofn()\nprint(sorted(foo.items(), key=lambda x: str(x)), foo.__class__.__name__)\n#: [('int', 1), ('str', 'hai')] Dict[pyobj,pyobj]\nfoo[\"codon\"] = 5.15\nprint(sorted(foo.items(), key=lambda x: str(x)), foo[\"codon\"].__class__.__name__, foo.__class__.__name__)\n#: [('codon', 5.15), ('int', 1), ('str', 'hai')] pyobj Dict[pyobj,pyobj]\n\na = {1: \"s\", 2: \"t\"}\na[3] = foo[\"str\"]\nprint(sorted(a.items()))  #: [(1, 's'), (2, 't'), (3, 'hai')]\n\n\n#%% python_isinstance\nimport python\n\n@python\ndef foo():\n    return 1\n\nz = foo()\nprint(z.__class__.__name__)  #: pyobj\n\nprint isinstance(z, pyobj)  #: True\nprint isinstance(z, int)  #: False\nprint isinstance(z, python.int)  #: True\nprint isinstance(z, python.ValueError)  #: False\n\nprint isinstance(z, (int, str, python.int))  #: True\nprint isinstance(z, (int, str, python.AttributeError))  #: False\n\ntry:\n    foo().x\nexcept python.ValueError:\n    pass\nexcept python.AttributeError as e:\n    print('caught', e, e.__class__.__name__) #: caught 'int' object has no attribute 'x' pyobj\n\n\n#%% python_exceptions\nimport python\n\n@python\ndef foo():\n    return 1\n\ntry:\n    foo().x\nexcept python.AttributeError as f:\n    print 'py.Att', f  #: py.Att 'int' object has no attribute 'x'\nexcept ValueError:\n    print 'Val'\nexcept PyError as e:\n    print 'PyError', e\ntry:\n    foo().x\nexcept python.ValueError as f:\n    print 'py.Att', f\nexcept ValueError:\n    print 'Val'\nexcept PyError as e:\n    print 'PyError', e  #: PyError 'int' object has no attribute 'x'\ntry:\n    raise ValueError(\"ho\")\nexcept python.ValueError as f:\n    print 'py.Att', f\nexcept ValueError:\n    print 'Val'  #: Val\nexcept PyError as e:\n    print 'PyError', e\n\n#%% python_directive\n## codon: auto_python=1\nimport json\ns = json.dumps({'6': 7, '4': 5})\nprint(s.__class__.__name__, s)  #: pyobj {\"4\": 5, \"6\": 7}\n"
  },
  {
    "path": "test/parser/typecheck/test_typecheck.codon",
    "content": "#%% pass,barebones\npass\n\n#%% print,barebones\nprint 1,\nprint 1, 2  #: 1 1 2\n\nprint 1, 2  #: 1 2\nprint(3, \"4\", sep=\"-\", end=\" !\\n\") #: 3-4 !\n\nprint(1, 2) #: 1 2\nprint (1, 2) #: (1, 2)\n\ndef foo(i, j):\n    return i + j\nprint 3 |> foo(1)  #: 4\n\n#%% typeof_definitions,barebones\na = 10\nb: type(a) = 1\nprint(b.__class__.__name__) #: int\n\nl = []\nc: type(l) = []\nl.append(1)\nprint(l.__class__.__name__, c.__class__.__name__) #: List[int] List[int]\n\n#%% typeof_definitions_error,barebones\na = 10\ndef foo(a)->type(a): return a\n#! cannot use calls in type signatures\n\n#%% typeof_definitions_error_2,barebones\ndef foo() -> type:\n    return int\nclass Foo:\n    a: foo()\n#! cannot use calls in type signatures\n\n\n#%% multi_error,barebones\na = 55\nprint z  #! name 'z' is not defined\n# TODO in new parser!\nprint(a, q, w)  # name 'q' is not defined\nprint quit  # name 'quit' is not defined\n\n#%% static_unify,barebones\ndef foo(x: CallableTrait[[1,2], 3]): pass  #! CallableTrait cannot take static types\n\n#%% static_unify_2,barebones\ndef foo(x: List[1]): pass  #! expected type expression\n\n#%% expr,barebones\na = 5; b = 3\nprint a, b  #: 5 3\n\n#%% delayed_instantiation_correct_context,barebones\n# Test timing of the statements; ensure that delayed blocks still\n# use correct names.\ndef foo():\n    l = []\n\n    s = 1  # CH1\n    if isinstance(l, List[int]):  # delay typechecking this block\n        print(s)  #: 1\n        # if this is done badly, this print will print 's'\n        # or result in assertion error\n    print(s)  #: 1\n\n    s = 's'  # CH2\n    print(s)  #: s\n\n    # instantiate l so that the block above\n    # is typechecked in the next iteration\n    l.append(1)\nfoo()\n\n# check that this does not mess up comprehensions\n# (where variable names are used BEFORE their declaration)\nslice_prefixes = [(start, end)\n                    for start, end in [(1, 2), (3, 4)]]\nprint(slice_prefixes)  #: [(1, 2), (3, 4)]\n\ndef foo():\n    # fn itself must be delayed and unbound for this to reproduce\n    fn = (lambda _: lambda x: x)(None)\n\n    zizzer = 1\n    y = fn(zizzer)\n    print(y)  #: 1\n\n    zizzer = 's'\n    y = fn(zizzer)\n    print(y)  #: s\nfoo()\n\n#%% do_not_resolve_default_generics_on_partial,barebones\ndef coerce():\n    def foo(): pass\n    def bar(T1: type, I1: type = T1):\n        print(T1 is I1)  #: False\n        foo()\n    bar(int, I1=Int[64]) # creates bar=bar(foo,...) first\ncoerce()\n\n#%% compile_error_realization,barebones\ndef ctx():\n    def foo(): compile_error(\"bah!\")\n    def bar(err: Literal[bool]):\n        if err: foo()\n        else: print(\"ok\")\n    bar(False)\nctx()  #: ok\n\n#%% ctx_time_resolver,barebones\ndef bar(j):\n    # captures stdlib range, not foo's range\n    for i in range(*j): print(i)\ndef foo(range):\n    bar(range)\nfoo((1, 2))  #: 1\n\n# Test whether for loop variables respect ctx->add() time\nslopes = [1.0,2,3] if len(\"abc\") <= 5 else None\nif slopes is not None:\n    for ixx in range(3):\n        slopes[ixx] = ixx\nfor ixx in range(5):\n    ixx\n\n#%% capture_function_partial_proper_realize,barebones\ndef concatenate(arrays, axis = 0, out = None, dtype: type = NoneType):\n    def concat_inner(arrays, axis, out, dtype: type):\n        return 1\n\n    def concat_tuple(arrays, axis = 0, out = None, dtype: type = NoneType):\n        return concat_inner(arrays, axis, out=None, dtype=dtype)\n\n    return 1\n\nprint concatenate((1, 2))  #: 1\n\n#%% first_order_type,barebones\na = 1\nl: List[type(a)] = []\nprint(l, l.__class__.__name__)  #: [] List[int]\n\ndef foo(t: type) -> type:\n  return List[t]\nl = foo(int)()\nprint(l, l.__class__.__name__)  #: [] List[int]\nl = foo(str)()\nprint(l, l.__class__.__name__)  #: [] List[str]\n\ndef bar() -> type:\n  print(\"side effect!\")\n  class Foo:\n     a: int\n     def __init__(self, a=1):\n       print(f\"Foo! {a}\")\n       self.a = a\n     def bar(self, x: Foo):\n       return f'{self.a}-{x.a}'\n  return Foo\nf = bar()()\n#: side effect!\n#: Foo! 1\nprint f.bar(bar()(2))\n#: side effect!\n#: Foo! 2\n#: 1-2\n"
  },
  {
    "path": "test/parser/typecheck_expr.codon",
    "content": ""
  },
  {
    "path": "test/parser/typecheck_stmt.codon",
    "content": ""
  },
  {
    "path": "test/parser/types.codon",
    "content": ""
  },
  {
    "path": "test/python/__init__.py",
    "content": ""
  },
  {
    "path": "test/python/cython_jit.py",
    "content": "from typing import Dict, List, Tuple\nimport numpy as np\nimport codon\n\n@codon.convert\nclass Foo:\n    __slots__ = 'a', 'b', 'c'\n\n    def __init__(self, n):\n        self.a = n\n        self.b = n**2\n        self.c = n**3\n\n    def __eq__(self, other):\n        return (self.a == other.a and\n                self.b == other.b and\n                self.c == other.c)\n\n    def __hash__(self):\n        return hash((self.a, self.b, self.c))\n\n    @codon.jit\n    def total(self):\n        return self.a + self.b + self.c\n\ndef test_convertible():\n    assert Foo(10).total() == 1110\n\ndef test_many():\n    @codon.jit\n    def is_prime(n):\n        if n <= 1:\n            return False\n        for i in range(2, n):\n            if n % i == 0:\n                return False\n        return True\n\n    assert sum(1 for i in range(100000, 200000) if is_prime(i)) == 8392\n\ndef test_roundtrip():\n    @codon.jit\n    def roundtrip(x):\n        return x\n\n    for _ in range(5):\n        assert roundtrip(None) == None\n        assert roundtrip(42) == 42\n        assert roundtrip(3.14) == 3.14\n        assert roundtrip(False) == False\n        assert roundtrip(True) == True\n        assert roundtrip('hello') == 'hello'\n        assert roundtrip('') == ''\n        assert roundtrip(2+3j) == 2+3j\n        assert roundtrip(slice(1,2,3)) == slice(1,2,3)\n        assert roundtrip([11,22,33]) == [11,22,33]\n        assert roundtrip([[[42]]]) == [[[42]]]\n        assert roundtrip({11,22,33}) == {11,22,33}\n        assert roundtrip({11: 'one', 22: 'two', 33: 'three'}) == {11: 'one', 22: 'two', 33: 'three'}\n        assert roundtrip((11,22,33)) == (11,22,33)\n        assert Foo(roundtrip(Foo(123))[0]) == Foo(123)\n        assert roundtrip(roundtrip) is roundtrip\n\ndef test_return_type():\n    @codon.jit\n    def run() -> Tuple[int, str, float, List[int], Dict[str, int]]:\n        return (1, \"str\", 2.45, [1, 2, 3], {\"a\": 1, \"b\": 2})\n\n    r = run()\n    assert type(r) == tuple\n    assert type(r[0]) == int\n    assert type(r[1]) == str\n    assert type(r[2]) == float\n    assert type(r[3]) == list\n    assert len(r[3]) == 3\n    assert type(r[3][0]) == int\n    assert type(r[4]) == dict\n    assert len(r[4].items()) == 2\n    assert type(next(iter(r[4].keys()))) == str\n    assert type(next(iter(r[4].values()))) == int\n\ndef test_param_types():\n    @codon.jit\n    def run(a: int, b: Tuple[int, int], c: List[int], d: Dict[str, int]) -> int:\n        s = 0\n        for v in [a, *b, *c, *d.values()]:\n            s += v\n        return s\n\n    r = run(1, (2, 3), [4, 5, 6], dict(a=7, b=8, c=9))\n    assert type(r) == int\n    assert r == 45\n\ndef test_error_handling():\n    @codon.jit\n    def type_error():\n        return 1 + '1'\n\n    try:\n        type_error()\n    except codon.JITError:\n        pass\n    except:\n        assert False\n    else:\n        assert False\n\ntest_convertible()\ntest_many()\ntest_roundtrip()\ntest_return_type()\ntest_param_types()\ntest_error_handling()\n\n\n@codon.jit\ndef foo(y):\n    return f\"{y.__class__.__name__}; {y}\"\n\n\n@codon.jit(debug=True)\ndef foo2(y):\n    return f\"{y.__class__.__name__}; {y}\"\n\nclass Foo:\n    def __init__(self):\n        self.x = 1\n\n@codon.jit\ndef a(x):\n    return x+1\n\ndef b(x, z):\n    y = a(x)\n    return y * z\n\n@codon.jit(pyvars=['b'])\ndef c(x, y):\n    n = b(x,y) ** a(1)\n    return n\n\ndef test_cross_calls():\n    assert foo([None, 1]) == \"List[Optional[int]]; [None, 1]\"\n    assert foo([1, None, 1]) == \"List[Optional[int]]; [1, None, 1]\"\n    assert foo([1, None, 1.2]) == \"List[pyobj]; [1, None, 1.2]\"\n    assert foo({None: 1}) == \"Dict[pyobj,int]; {None: 1}\"\n    assert foo2([None, Foo()]).startswith(\"List[pyobj]; [None, <__main__.Foo object at\")\n\n    assert a(3) == 4\n    assert b(3, 4) == 16\n    assert round(c(5, 6.1), 2) == 1339.56\n\ntest_cross_calls()\n\n@codon.jit\ndef d(x, y):\n    z = x**2 + y\n    return z\n\ndef test_ndarray():\n    x = np.array([1, 2, 3, 4])\n    y = np.array([10., 100., 1000., 10000.])\n    z = d(x, y)\n    assert np.array_equal(z, [11., 104., 1009., 10016.])\n    assert z.dtype == np.float64\n\n    x = np.array([1j])\n    y = np.array([2j], np.complex64)\n    z = d(x, y)\n    assert np.array_equal(z, [-1+2j])\n    assert z.dtype == np.complex128\n\n    x = np.array([3, 4, 5], 'timedelta64[2s]')\n    y = a(x)\n    assert np.array_equal(y, np.array([4, 5, 6], 'timedelta64[2s]'))\n    assert np.datetime_data(y.dtype) == ('s', 2)\n\n    x = np.array([3, 4, 5], 'datetime64[2s]')\n    y = a(x)\n    assert np.array_equal(y, np.array([4, 5, 6], 'datetime64[2s]'))\n    assert np.datetime_data(y.dtype) == ('s', 2)\n\ntest_ndarray()\n\n@codon.jit\ndef e(x=2, y=99):\n    return 2*x + y\n\ndef test_arg_order():\n    assert e(1, 2) == 4\n    assert e(1) == 101\n    assert e(y=10, x=1) == 12\n    assert e(x=1) == 101\n    assert e() == 103\n\ntest_arg_order()\n"
  },
  {
    "path": "test/python/find-python-library.py",
    "content": "import sys, sysconfig, os, distutils.sysconfig as du_sysconfig, itertools\n\ndef get_python_library(python_version):\n    \"\"\"Get path to the python library associated with the current python\n    interpreter. Adapted from https://github.com/scikit-build/scikit-build/blob/master/skbuild/cmaker.py#L272\"\"\"\n    # determine direct path to libpython\n    python_library = sysconfig.get_config_var('LIBRARY')\n\n    # if static (or nonexistent), try to find a suitable dynamic libpython\n    if (not python_library or os.path.splitext(python_library)[1][-2:] == '.a'):\n\n        candidate_lib_prefixes = ['', 'lib']\n\n        candidate_implementations = ['python']\n        if hasattr(sys, \"pypy_version_info\"):\n            candidate_implementations = ['pypy-c', 'pypy3-c']\n\n        candidate_extensions = ['.lib', '.so', '.a']\n        if sysconfig.get_config_var('WITH_DYLD'):\n            candidate_extensions.insert(0, '.dylib')\n\n        candidate_versions = [python_version]\n        if python_version:\n            candidate_versions.append('')\n            candidate_versions.insert(\n                0, \"\".join(python_version.split(\".\")[:2]))\n\n        abiflags = getattr(sys, 'abiflags', '')\n        candidate_abiflags = [abiflags]\n        if abiflags:\n            candidate_abiflags.append('')\n\n        # Ensure the value injected by virtualenv is\n        # returned on windows.\n        # Because calling `sysconfig.get_config_var('multiarchsubdir')`\n        # returns an empty string on Linux, `du_sysconfig` is only used to\n        # get the value of `LIBDIR`.\n        libdir = du_sysconfig.get_config_var('LIBDIR')\n        if sysconfig.get_config_var('MULTIARCH'):\n            masd = sysconfig.get_config_var('multiarchsubdir')\n            if masd:\n                if masd.startswith(os.sep):\n                    masd = masd[len(os.sep):]\n                libdir = os.path.join(libdir, masd)\n\n        if libdir is None:\n            libdir = os.path.abspath(os.path.join(\n                sysconfig.get_config_var('LIBDEST'), \"..\", \"libs\"))\n\n        candidates = [\n            os.path.join (sysconfig.get_config_var('LIBPL'), sysconfig.get_config_var('LDLIBRARY'))\n        ] + [\n            os.path.join(\n                libdir,\n                ''.join((pre, impl, ver, abi, ext))\n            )\n            for (pre, impl, ext, ver, abi) in itertools.product(\n                candidate_lib_prefixes,\n                candidate_implementations,\n                candidate_extensions,\n                candidate_versions,\n                candidate_abiflags\n            )\n        ]\n\n        for candidate in candidates:\n            if os.path.exists(candidate):\n                # we found a (likely alternate) libpython\n                python_library = candidate\n                break\n\n    # TODO(opadron): what happens if we don't find a libpython?\n\n    return python_library\n\nprint(get_python_library('{}.{}'.format(sys.version_info[0], sys.version_info[1])))\n"
  },
  {
    "path": "test/python/myextension.codon",
    "content": "print('Hello from Codon')\n\ndef f1(a: float = 1.11, b: float = 2.22):\n    return (a, b)\n\ndef f2():\n    return ({1: 'one'}, {2}, [3])\n\ndef f3(x):\n    return x * 2\n\ndef f4(x):\n    return x\n\n@overload\ndef f4(a: float = 1.11, b: float = 2.22):\n    return f1(a=a, b=b)\n\n@overload\ndef f4():\n    return ['f4()']\n\n@overload\ndef f4(x: str):\n    return x, x\n\ndef f5():\n    pass\n\n@dataclass(python=True)\nclass Vec:\n    a: float\n    b: float\n    tag: str\n    n: ClassVar[int] = 0\n    d: ClassVar[int] = 0\n\n    def __init__(self, a: float, b: float, tag: str):\n        self.a = a\n        self.b = b\n        self.tag = tag\n\n    def __init__(self, a: float = 0.0, b: float = 0.0):\n        self.__init__(a, b, 'v' + str(Vec.n))\n        Vec.n += 1\n\n    def foo(self, a: float = 1.11, b: float = 2.22):\n        return (self.a, a, b)\n\n    def bar(self):\n        return self\n\n    def baz(a: float = 1.11, b: float = 2.22):\n        return (a, b)\n\n    def nop():\n        return 'nop'\n\n    @property\n    def c(self):\n        return self.a + self.b\n\n    def __str__(self):\n        return f'{self.tag}: <{self.a}, {self.b}>'\n\n    def __repr__(self):\n        return f'Vec({self.a}, {self.b}, {repr(self.tag)})'\n\n    def __add__(self, other: Vec):\n        return Vec(self.a + other.a, self.b + other.b, f'({self.tag}+{other.tag})')\n\n    def __iadd__(self, other: Vec):\n        self.a += other.a\n        self.b += other.b\n        self.tag = f'({self.tag}+={other.tag})'\n        return self\n\n    def __add__(self, x: float):\n        return Vec(self.a + x, self.b + x, f'({self.tag}+{x})')\n\n    def __iadd__(self, x: float):\n        self.a += x\n        self.b += x\n        self.tag = f'({self.tag}+={x})'\n        return self\n\n    def __add__(self, x: int):\n        return Vec(self.a + x, self.b + x, f'({self.tag}++{x})')\n\n    def __sub__(self, other: Vec):\n        return Vec(self.a - other.a, self.b - other.b, f'({self.tag}-{other.tag})')\n\n    def __isub__(self, other: Vec):\n        self.a -= other.a\n        self.b -= other.b\n        self.tag = f'({self.tag}-={other.tag})'\n        return self\n\n    def __mul__(self, x: float):\n        return Vec(self.a * x, self.b * x, f'({self.tag}*{x})')\n\n    def __imul__(self, x: float):\n        self.a *= x\n        self.b *= x\n        self.tag = f'({self.tag}*={x})'\n        return self\n\n    def __mod__(self, x: float):\n        return Vec(self.a % x, self.b % x, f'({self.tag}%{x})')\n\n    def __imod__(self, x: float):\n        self.a %= x\n        self.b %= x\n        self.tag = f'({self.tag}%={x})'\n        return self\n\n    def __divmod__(self, x: float):\n        raise ArithmeticError('no divmod')\n        # return self.a / x, self.a % x\n\n    def __pow__(self, x: float):\n        return Vec(self.a ** x, self.b ** x, f'({self.tag}**{x})')\n\n    def __ipow__(self, x: float):\n        self.a **= x\n        self.b **= x\n        self.tag = f'({self.tag}**={x})'\n        return self\n\n    def __neg__(self):\n        return Vec(-self.a, -self.b, f'(-{self.tag})')\n\n    def __pos__(self):\n        return Vec(self.a, self.b, f'(+{self.tag})')\n\n    def __abs__(self):\n        import math\n        return math.hypot(self.a, self.b)\n\n    def __bool__(self):\n        return bool(self.a) and bool(self.b)\n\n    def __invert__(self):\n        return Vec(-self.a - 1, -self.b - 1, f'(~{self.tag})')\n\n    def __lshift__(self, x: int):\n        y = 1 << x\n        return Vec(self.a * y, self.b * y, f'({self.tag}<<{x})')\n\n    def __ilshift__(self, x: int):\n        y = 1 << x\n        self.a *= y\n        self.b *= y\n        self.tag = f'({self.tag}<<={x})'\n        return self\n\n    def __rshift__(self, x: int):\n        y = 1 << x\n        return Vec(self.a / y, self.b / y, f'({self.tag}>>{x})')\n\n    def __irshift__(self, x: int):\n        y = 1 << x\n        self.a /= y\n        self.b /= y\n        self.tag = f'({self.tag}>>={x})'\n        return self\n\n    def __and__(self, x: int):\n        return Vec(int(self.a) & x, int(self.b) & x, f'({self.tag}&{x})')\n\n    def __iand__(self, x: int):\n        self.a = int(self.a) & x\n        self.b = int(self.b) & x\n        self.tag = f'({self.tag}&={x})'\n        return self\n\n    def __or__(self, x: int):\n        return Vec(int(self.a) | x, int(self.b) | x, f'({self.tag}|{x})')\n\n    def __ior__(self, x: int):\n        self.a = int(self.a) | x\n        self.b = int(self.b) | x\n        self.tag = f'({self.tag}|={x})'\n        return self\n\n    def __xor__(self, x: int):\n        return Vec(int(self.a) ^ x, int(self.b) ^ x, f'({self.tag}^{x})')\n\n    def __ixor__(self, x: int):\n        self.a = int(self.a) ^ x\n        self.b = int(self.b) ^ x\n        self.tag = f'({self.tag}^={x})'\n        return self\n\n    #def __int__(self):\n    #    return int(self.b)\n\n    #def __float__(self):\n    #    return self.b\n\n    #def __index__(self):\n    #    return int(self.a)\n\n    def __floordiv__(self, x: float):\n        return Vec(self.a // x, self.b // x, f'({self.tag}//{x})')\n\n    def __ifloordiv__(self, x: float):\n        self.a //= x\n        self.b //= x\n        self.tag = f'({self.tag}//={x})'\n        return self\n\n    def __truediv__(self, x: float):\n        return Vec(self.a / x, self.b / x, f'({self.tag}/{x})')\n\n    def __itruediv__(self, x: float):\n        self.a /= x\n        self.b /= x\n        self.tag = f'({self.tag}/={x})'\n        return self\n\n    def __matmul__(self, other: Vec):\n        return (self.a * other.a) + (self.b * other.b)\n\n    def __imatmul__(self, x: float):\n        self.a *= x\n        self.b *= x\n        self.tag = f'({self.tag}@={x})'\n        return self\n\n    def __len__(self):\n        return len(self.tag)\n\n    def __getitem__(self, idx: int):\n        if idx == 0:\n            return self.a\n        elif idx == 1:\n            return self.b\n        elif idx == 11:\n            return self.a + self.b\n        else:\n            raise KeyError('bad vec key ' + str(idx))\n\n    def __setitem__(self, idx: int, val: float):\n        if idx == 0:\n            self.a = val\n        elif idx == 1:\n            self.b = val\n        elif idx == 11:\n            self.a = val\n            self.b = val\n        else:\n            raise KeyError('bad vec key ' + str(idx) + ' with val ' + str(val))\n\n    def __delitem__(self, idx: int):\n        self[idx] = 0.0\n\n    def __contains__(self, val: float):\n        return self.a == val or self.b == val\n\n    def __contains__(self, val: str):\n        return self.tag == val\n\n    def __hash__(self):\n        return int(self.a + self.b)\n\n    def __call__(self, a: float = 1.11, b: float = 2.22):\n        return self.foo(a=a, b=b)\n\n    def __call__(self, x: str):\n        return (self.a, self.b, x)\n\n    def __call__(self):\n        return Vec(self.a, self.b, f'({self.tag}())')\n\n    def __iter__(self):\n        for c in self.tag:\n            yield c\n\n    def __eq__(self, other: Vec):\n        return self.a == other.a and self.b == other.b\n\n    def __eq__(self, x: float):\n        return self.a == x and self.b == x\n\n    def __ne__(self, other: Vec):\n        return self.a != other.a or self.b != other.b\n\n    def __lt__(self, other: Vec):\n        return abs(self) < abs(other)\n\n    def __le__(self, other: Vec):\n        return abs(self) <= abs(other)\n\n    def __gt__(self, other: Vec):\n        return abs(self) > abs(other)\n\n    def __ge__(self, other: Vec):\n        return abs(self) >= abs(other)\n\n    def __del__(self):\n        Vec.d += 1\n\n    def nd():\n        return Vec.d\n\ndef f6(x: float, t: str):\n    return Vec(x, x, t)\n\ndef reset():\n    Vec.n = 0\n\ndef par_sum(n: int):\n    m = 0\n    @par(num_threads=4)\n    for i in range(n):\n        m += 3*i + 7\n    return m\n\n@tuple\nclass Foo:\n    a: List[str]\n    x: Dict[str, int]\n\n    def __new__(a: List[str]) -> Foo:\n        return Foo(a, {s: i for i, s in enumerate(a)})\n\n    def __iter__(self):\n        return iter(self.a)\n\n    def __repr__(self):\n        return f'Foo({self.a}, {self.x})'\n\n    def hello(self):\n        return 'x'\n\n    def __int__(self):\n        return 42\n\n    def __float__(self):\n        return 3.14\n\n    def __index__(self):\n        return 99\n\nimport numpy as np\nimport numpy.pybridge\n\ndef numpy_test():\n    result = np.empty(2)\n    result[1:] = np.arange(1, 2)\n    return result\n"
  },
  {
    "path": "test/python/myextension2.codon",
    "content": "print('Hello from Codon 2')\n\n@tuple\nclass Vec:\n    a: float\n    b: float\n    tag: str\n    n: ClassVar[int] = 0\n    d: ClassVar[int] = 0\n\n    def __new__(a: float = 0.0, b: float = 0.0):\n        v = Vec(a, b, 'v' + str(Vec.n))\n        Vec.n += 1\n        return v\n\n    def foo(self, a: float = 1.11, b: float = 2.22):\n        return (self.a, a, b)\n\n    def bar(self):\n        return self\n\n    def baz(a: float = 1.11, b: float = 2.22):\n        return (a, b)\n\n    def nop():\n        return 'nop'\n\n    @property\n    def c(self):\n        return self.a + self.b\n\n    def __str__(self):\n        return f'{self.tag}: <{self.a}, {self.b}>'\n\n    def __repr__(self):\n        return f'Vec({self.a}, {self.b}, {repr(self.tag)})'\n\n    def __add__(self, other: Vec):\n        return Vec(self.a + other.a, self.b + other.b, f'({self.tag}+{other.tag})')\n\n    def __iadd__(self, other: Vec):\n        a, b = self.a, self.b\n        a += other.a\n        b += other.b\n        tag = f'({self.tag}+={other.tag})'\n        return Vec(a, b, tag)\n\n    def __add__(self, x: float):\n        return Vec(self.a + x, self.b + x, f'({self.tag}+{x})')\n\n    def __iadd__(self, x: float):\n        a, b = self.a, self.b\n        a += x\n        b += x\n        tag = f'({self.tag}+={x})'\n        return Vec(a, b, tag)\n\n    def __add__(self, x: int):\n        return Vec(self.a + x, self.b + x, f'({self.tag}++{x})')\n\n    def __sub__(self, other: Vec):\n        return Vec(self.a - other.a, self.b - other.b, f'({self.tag}-{other.tag})')\n\n    def __isub__(self, other: Vec):\n        a, b = self.a, self.b\n        a -= other.a\n        b -= other.b\n        tag = f'({self.tag}-={other.tag})'\n        return Vec(a, b, tag)\n\n    def __mul__(self, x: float):\n        return Vec(self.a * x, self.b * x, f'({self.tag}*{x})')\n\n    def __imul__(self, x: float):\n        a, b = self.a, self.b\n        a *= x\n        b *= x\n        tag = f'({self.tag}*={x})'\n        return Vec(a, b, tag)\n\n    def __mod__(self, x: float):\n        return Vec(self.a % x, self.b % x, f'({self.tag}%{x})')\n\n    def __imod__(self, x: float):\n        a, b = self.a, self.b\n        a %= x\n        b %= x\n        tag = f'({self.tag}%={x})'\n        return Vec(a, b, tag)\n\n    def __divmod__(self, x: float):\n        raise ArithmeticError('no divmod')\n        # return self.a / x, self.a % x\n\n    def __pow__(self, x: float):\n        return Vec(self.a ** x, self.b ** x, f'({self.tag}**{x})')\n\n    def __ipow__(self, x: float):\n        a, b = self.a, self.b\n        a **= x\n        b **= x\n        tag = f'({self.tag}**={x})'\n        return Vec(a, b, tag)\n\n    def __neg__(self):\n        return Vec(-self.a, -self.b, f'(-{self.tag})')\n\n    def __pos__(self):\n        return Vec(self.a, self.b, f'(+{self.tag})')\n\n    def __abs__(self):\n        import math\n        return math.hypot(self.a, self.b)\n\n    def __bool__(self):\n        return bool(self.a) and bool(self.b)\n\n    def __invert__(self):\n        return Vec(-self.a - 1, -self.b - 1, f'(~{self.tag})')\n\n    def __lshift__(self, x: int):\n        y = 1 << x\n        return Vec(self.a * y, self.b * y, f'({self.tag}<<{x})')\n\n    def __ilshift__(self, x: int):\n        a, b = self.a, self.b\n        y = 1 << x\n        a *= y\n        b *= y\n        tag = f'({self.tag}<<={x})'\n        return Vec(a, b, tag)\n\n    def __rshift__(self, x: int):\n        y = 1 << x\n        return Vec(self.a / y, self.b / y, f'({self.tag}>>{x})')\n\n    def __irshift__(self, x: int):\n        a, b = self.a, self.b\n        y = 1 << x\n        a /= y\n        b /= y\n        tag = f'({self.tag}>>={x})'\n        return Vec(a, b, tag)\n\n    def __and__(self, x: int):\n        return Vec(int(self.a) & x, int(self.b) & x, f'({self.tag}&{x})')\n\n    def __iand__(self, x: int):\n        a, b = self.a, self.b\n        a = int(self.a) & x\n        b = int(self.b) & x\n        tag = f'({self.tag}&={x})'\n        return Vec(a, b, tag)\n\n    def __or__(self, x: int):\n        return Vec(int(self.a) | x, int(self.b) | x, f'({self.tag}|{x})')\n\n    def __ior__(self, x: int):\n        a, b = self.a, self.b\n        a = int(self.a) | x\n        b = int(self.b) | x\n        tag = f'({self.tag}|={x})'\n        return Vec(a, b, tag)\n\n    def __xor__(self, x: int):\n        return Vec(int(self.a) ^ x, int(self.b) ^ x, f'({self.tag}^{x})')\n\n    def __ixor__(self, x: int):\n        a, b = self.a, self.b\n        a = int(self.a) ^ x\n        b = int(self.b) ^ x\n        tag = f'({self.tag}^={x})'\n        return Vec(a, b, tag)\n\n    #def __int__(self):\n    #    return int(self.b)\n\n    #def __float__(self):\n    #    return self.b\n\n    #def __index__(self):\n    #    return int(self.a)\n\n    def __floordiv__(self, x: float):\n        return Vec(self.a // x, self.b // x, f'({self.tag}//{x})')\n\n    def __ifloordiv__(self, x: float):\n        a, b = self.a, self.b\n        a //= x\n        b //= x\n        tag = f'({self.tag}//={x})'\n        return Vec(a, b, tag)\n\n    def __truediv__(self, x: float):\n        return Vec(self.a / x, self.b / x, f'({self.tag}/{x})')\n\n    def __itruediv__(self, x: float):\n        a, b = self.a, self.b\n        a /= x\n        b /= x\n        tag = f'({self.tag}/={x})'\n        return Vec(a, b, tag)\n\n    def __matmul__(self, other: Vec):\n        return (self.a * other.a) + (self.b * other.b)\n\n    def __imatmul__(self, x: float):\n        a, b = self.a, self.b\n        a *= x\n        b *= x\n        tag = f'({self.tag}@={x})'\n        return Vec(a, b, tag)\n\n    def __len__(self):\n        return len(self.tag)\n\n    def __getitem__(self, idx: int):\n        if idx == 0:\n            return self.a\n        elif idx == 1:\n            return self.b\n        elif idx == 11:\n            return self.a + self.b\n        else:\n            raise KeyError('bad vec key ' + str(idx))\n\n    def __contains__(self, val: float):\n        return self.a == val or self.b == val\n\n    def __contains__(self, val: str):\n        return self.tag == val\n\n    def __hash__(self):\n        return int(self.a + self.b)\n\n    def __call__(self, a: float = 1.11, b: float = 2.22):\n        return self.foo(a=a, b=b)\n\n    def __call__(self, x: str):\n        return (self.a, self.b, x)\n\n    def __call__(self):\n        return Vec(self.a, self.b, f'({self.tag}())')\n\n    def __iter__(self):\n        for c in self.tag:\n            yield c\n\n    def __eq__(self, other: Vec):\n        return self.a == other.a and self.b == other.b\n\n    def __eq__(self, x: float):\n        return self.a == x and self.b == x\n\n    def __ne__(self, other: Vec):\n        return self.a != other.a or self.b != other.b\n\n    def __lt__(self, other: Vec):\n        return abs(self) < abs(other)\n\n    def __le__(self, other: Vec):\n        return abs(self) <= abs(other)\n\n    def __gt__(self, other: Vec):\n        return abs(self) > abs(other)\n\n    def __ge__(self, other: Vec):\n        return abs(self) >= abs(other)\n\n    def __del__(self):\n        Vec.d += 1\n\n    def nd():\n        return Vec.d\n\ndef reset():\n    Vec.n = 0\n\ndef par_sum(n: int):\n    m = 0\n    @par(num_threads=4)\n    for i in range(n):\n        m += 3*i + 7\n    return m\n"
  },
  {
    "path": "test/python/mymodule.py",
    "content": "def multiply(a,b):\n    return a*b\n\ndef print_args(a,b,c,d,e):\n    t = (a,b,c,d,e)\n    if t != ((4, 5), {'a': 3.14, 'b': 2.123}, True, {'ACGT'}, [['abc'], ['1.1', '2.2'], []]):\n        raise ValueError('TEST FAILED!')\n    return ({'a': 3.14, 'b': 2.123}, (222, 3.14))\n\ndef print_args_var(a,b,c=1,*args,**kwargs):\n    return 'a={}, b={}, c={}, args={}, kwargs={}'.format(a, b, c, args, kwargs)\n\ndef throw_exc():\n    raise ValueError('foo')\n    return 0\n\ndef test_call_no_args():\n    return 42\n\ndef test_call_one_arg(x):\n    return x**2\n"
  },
  {
    "path": "test/python/pybridge.codon",
    "content": "import python\n\n@test\ndef test_basic():\n    from python import mymodule\n    assert str(mymodule.multiply(3, 4)) == '12'\ntest_basic()\n\n@test\ndef test_pybridge():\n    @python\n    def test_pydef(n: int) -> str:\n        return ''.join(map(str, range(n)))\n    assert test_pydef(5) == '01234'\ntest_pybridge()\n\nfrom python import mymodule\n@test\ndef test_conversions():\n    T = tuple[dict[str,float],tuple[int,float]]\n    t = mymodule.print_args(\n        (4,5),\n        {'a': 3.14, 'b': 2.123},\n        True,\n        {'ACGT'},\n        [['abc'], ['1.1', '2.2'], list[str]()]\n    )\n    assert T.__from_py__(t.p) == ({'a': 3.14, 'b': 2.123}, (222, 3.14))\ntest_conversions()\n\n@test\ndef test_pythrow():\n    from python import mymodule.throw_exc() -> None as te\n    try:\n        te()\n    except PyError as e:\n        assert python.type(e.pytype)._getattr('__name__') + \":\" + e.message == \"ValueError:foo\"\n        return\n    assert False\ntest_pythrow()\n\n@test\ndef test_pyargs():\n    from python import mymodule\n    assert str(mymodule.print_args_var(1, 2, 3)) == \"a=1, b=2, c=3, args=(), kwargs={}\"\n    assert str(mymodule.print_args_var(1, z=5, b=2)) == \"a=1, b=2, c=1, args=(), kwargs={'z': 5}\"\n    assert str(mymodule.print_args_var(1, *(1,2,3,4,5), z=5)) == \"a=1, b=1, c=2, args=(3, 4, 5), kwargs={'z': 5}\"\ntest_pyargs()\n\n@test\ndef test_roundtrip(x: T, T: type):\n    assert T.__from_py__(x.__to_py__()) == x\n\ntest_roundtrip(42)\ntest_roundtrip(3.14)\ntest_roundtrip(True)\ntest_roundtrip(False)\ntest_roundtrip(byte(99))\ntest_roundtrip('hello world')\ntest_roundtrip('')\ntest_roundtrip(List[int]())\ntest_roundtrip([11, 22, 33])\ntest_roundtrip(Set[int]())\ntest_roundtrip({11, 22, 33})\ntest_roundtrip(Dict[str,int]())\ntest_roundtrip({'aa': 11, 'bb': 22, 'cc': 33})\ntest_roundtrip((11, 1.1, '11', [1, 1], {1}, {1: 1}))\ntest_roundtrip(())\ntest_roundtrip(DynamicTuple((111, 222, 333, 444)))\ntest_roundtrip(DynamicTuple('hello world'))\ntest_roundtrip(DynamicTuple[int]())\ntest_roundtrip(DynamicTuple[str]())\ntest_roundtrip(Optional(0))\ntest_roundtrip(Optional(111))\ntest_roundtrip(Optional[int]())\ntest_roundtrip(None)\ntest_roundtrip(...)\n\n@test\ndef test_ops():\n    from python import numpy as np\n    a = np.array([1, 3])\n    assert str(a + 1) == '[2 4]'\n    assert str(a - 1) == '[0 2]'\n    assert str(a * 2) == '[2 6]'\n    assert str(a @ a) == '10'\n    assert str(a // 2) == '[0 1]'\n    assert str(a / 2) == '[0.5 1.5]'\n    assert str(a % 2) == '[1 1]'\n    assert str(a ** 2) == '[1 9]'\n    assert str(-a) == '[-1 -3]'\n    assert str(+a) == '[1 3]'\n    assert str(~a) == '[-2 -4]'\n    assert str(a << 1) == '[2 6]'\n    assert str(a >> 1) == '[0 1]'\n    assert str(a & 2) == '[0 2]'\n    assert str(a ^ 2) == '[3 1]'\n    assert str(a | 2) == '[3 3]'\n\n    assert str(a < 3) == '[ True False]'\n    assert str(a <= 3) == '[ True  True]'\n    assert str(a == 3) == '[False  True]'\n    assert str(a != 3) == '[ True False]'\n    assert str(a > 3) == '[False False]'\n    assert str(a >= 3) == '[False  True]'\n\n    assert str(1 + a) == '[2 4]'\n    assert str(1 - a) == '[ 0 -2]'\n    assert str(2 * a) == '[2 6]'\n    assert str(1 // a) == '[1 0]'\n    assert str(2 & a) == '[0 2]'\n    assert str(2 ^ a) == '[3 1]'\n    assert str(2 | a) == '[3 3]'\n    n = a[0]\n    assert str(10 // n) == '10'\n    assert str(10 / n) == '10.0'\n    assert str(10 % n) == '0'\n    assert str(10 ** n) == '10'\n    assert str(4 << n) == '8'\n    assert str(4 >> n) == '2'\n\n    assert str(3 < a) == '[False False]'\n    assert str(3 <= a) == '[False  True]'\n    assert str(3 == a) == '[False  True]'\n    assert str(3 != a) == '[ True False]'\n    assert str(3 > a) == '[ True False]'\n    assert str(3 >= a) == '[ True  True]'\n\n    a = np.array([1, 3])\n    b = a\n    a += 1\n    assert str(b) == '[2 4]'\n\n    a = np.array([1, 3])\n    b = a\n    a -= 1\n    assert str(b) == '[0 2]'\n\n    a = np.array([1, 3])\n    b = a\n    a //= 2\n    assert str(b) == '[0 1]'\n\n    a = np.array([1.0, 3.0])\n    b = a\n    a /= 2\n    assert str(b) == '[0.5 1.5]'\n\n    a = np.array([1, 3])\n    b = a\n    a %= 2\n    assert str(b) == '[1 1]'\n\n    a = np.array([1, 3])\n    b = a\n    a **= 2\n    assert str(b) == '[1 9]'\n\n    a = np.array([1, 3])\n    b = a\n    a <<= 1\n    assert str(b) == '[2 6]'\n\n    a = np.array([1, 3])\n    b = a\n    a >>= 1\n    assert str(b) == '[0 1]'\n\n    a = np.array([1, 3])\n    b = a\n    a &= 2\n    assert str(b) == '[0 2]'\n\n    a = np.array([1, 3])\n    b = a\n    a ^= 2\n    assert str(b) == '[3 1]'\n\n    a = np.array([1, 3])\n    b = a\n    a |= 2\n    assert str(b) == '[3 3]'\n\n    a = np.array([1, 3])\n    n = a[-1]\n    assert str(n) == '3'\n    assert int(n) == 3\n    assert float(n) == 3.0\n    from operator import index\n    assert index(n) == 3\n    assert hash(n) == 3\n    assert len(a) == 2\n    v = list(iter(a))\n    assert len(v) == 2 and v[0] == 1 and v[1] == 3\n    a[-1] = 99\n    assert repr(a) == 'array([ 1, 99])'\n    assert bool(a[0]) == True\n    assert bool(a[0] - 1) == False\n\ntest_ops()\n\n@test\ndef test_calls():\n    from python import mymodule\n    assert mymodule.test_call_no_args() == 42\n    assert mymodule.test_call_one_arg(-2) == 4\n\ntest_calls()\n"
  },
  {
    "path": "test/python/pyext.py",
    "content": "import myext as m\nimport myext2 as m2\n\ndef equal(v, a, b, tag):\n    ok = (v.a == a and v.b == b and v.tag == tag)\n    if not ok:\n        print('GOT:', v.a, v.b, v.tag)\n        print('EXP:', a, b, tag)\n    return ok\n\nsaw_fun = False\nsaw_set = False\nsaw_foo = False\n\ndef test_codon_extensions(m):\n    m.reset()\n\n    # functions #\n    #############\n\n    global saw_fun\n\n    if hasattr(m, 'f1'):\n        assert m.f1(2.2, 3.3) == (2.2, 3.3)\n        assert m.f1(2.2, 3.3) == (2.2, 3.3)\n        assert m.f1(3.3) == (3.3, 2.22)\n        assert m.f1() == (1.11, 2.22)\n        assert m.f1(a=2.2, b=3.3) == (2.2, 3.3)\n        assert m.f1(2.2, b=3.3) == (2.2, 3.3)\n        assert m.f1(b=3.3, a=2.2) == (2.2, 3.3)\n        assert m.f1(a=2.2) == (2.2, 2.22)\n        assert m.f1(b=3.3) == (1.11, 3.3)\n\n        try:\n            m.f1(1.1, 2.2, 3.3)\n        except:\n            pass\n        else:\n            assert False\n\n        try:\n            m.f1(z=1)\n        except:\n            pass\n        else:\n            assert False\n\n        assert m.f2() == ({1: 'one'}, {2}, [3])\n\n        try:\n            m.f2(1)\n        except:\n            pass\n        else:\n            assert False\n\n        try:\n            m.f2(z=1, y=5)\n        except:\n            pass\n        else:\n            assert False\n\n        assert m.f3(42) == 84\n        assert m.f3(1.5) == 3.0\n        assert m.f3('x') == 'xx'\n\n        try:\n            m.f3(1, 2)\n        except:\n            pass\n        else:\n            assert False\n\n        try:\n            m.f3(a=1, b=2)\n        except:\n            pass\n        else:\n            assert False\n\n        assert m.f4() == ['f4()']\n        assert m.f4(2.2, 3.3) == (2.2, 3.3)\n        assert m.f4(3.3) == (3.3, 2.22)\n        assert m.f4(a=2.2, b=3.3) == (2.2, 3.3)\n        assert m.f4(2.2, b=3.3) == (2.2, 3.3)\n        assert m.f4(b=3.3, a=2.2) == (2.2, 3.3)\n        assert m.f4(a=2.2) == (2.2, 2.22)\n        assert m.f4(b=3.3) == (1.11, 3.3)\n        assert m.f4('foo') == ('foo', 'foo')\n        assert m.f4(b'foo') == ('foo', 'foo')\n        assert m.f4({1}) == {1}\n        assert m.f5() is None\n        assert equal(m.f6(1.9, 't'), 1.9, 1.9, 't')\n\n        saw_fun = True\n\n    # constructors #\n    ################\n\n    x = m.Vec(3.14, 4.2, 'x')\n    y = m.Vec(100, 1000, tag='y')\n    z = m.Vec(b=2.2, a=1.1)\n    s = m.Vec(10)\n    t = m.Vec(b=11)\n    r = m.Vec(3, 4)\n\n    assert equal(x, 3.14, 4.2, 'x')\n    assert equal(y, 100, 1000, 'y')\n    assert equal(z, 1.1, 2.2, 'v0')\n    assert equal(s, 10, 0.0, 'v1')\n    assert equal(t, 0.0, 11, 'v2')\n\n    try:\n        m.Vec(tag=10, a=1, b=2)\n    except:\n        pass\n    else:\n        assert False\n\n    # to-str #\n    ##########\n\n    assert str(x) == 'x: <3.14, 4.2>'\n    assert repr(x) == \"Vec(3.14, 4.2, 'x')\"\n\n    # methods #\n    ###########\n\n    assert x.foo(2.2, 3.3) == (3.14, 2.2, 3.3)\n    assert y.foo(3.3) == (100, 3.3, 2.22)\n    assert z.foo() == (1.1, 1.11, 2.22)\n    assert x.foo(a=2.2, b=3.3) == (3.14, 2.2, 3.3)\n    assert x.foo(2.2, b=3.3) == (3.14, 2.2, 3.3)\n    assert x.foo(b=3.3, a=2.2) == (3.14, 2.2, 3.3)\n    assert x.foo(a=2.2) == (3.14, 2.2, 2.22)\n    assert x.foo(b=3.3) == (3.14, 1.11, 3.3)\n\n    try:\n        x.foo(1, a=1)\n    except:\n        pass\n    else:\n        assert False\n\n    try:\n        x.foo(1, 2, b=2)\n    except:\n        pass\n    else:\n        assert False\n\n    try:\n        x.foo(1, z=2)\n    except:\n        pass\n    else:\n        assert False\n\n    assert equal(x.bar(), 3.14, 4.2, 'x')\n    assert equal(y.bar(), 100, 1000, 'y')\n    assert equal(z.bar(), 1.1, 2.2, 'v0')\n    assert equal(s.bar(), 10, 0.0, 'v1')\n    assert equal(t.bar(), 0.0, 11, 'v2')\n\n    try:\n        x.bar(1)\n    except:\n        pass\n    else:\n        assert False\n\n    try:\n        x.bar(z=1)\n    except:\n        pass\n    else:\n        assert False\n\n    assert m.Vec.baz(2.2, 3.3) == (2.2, 3.3)\n    assert x.baz(2.2, 3.3) == (2.2, 3.3)\n    assert m.Vec.baz(3.3) == (3.3, 2.22)\n    assert m.Vec.baz() == (1.11, 2.22)\n    assert m.Vec.baz(a=2.2, b=3.3) == (2.2, 3.3)\n    assert m.Vec.baz(2.2, b=3.3) == (2.2, 3.3)\n    assert m.Vec.baz(b=3.3, a=2.2) == (2.2, 3.3)\n    assert m.Vec.baz(a=2.2) == (2.2, 2.22)\n    assert m.Vec.baz(b=3.3) == (1.11, 3.3)\n\n    try:\n        m.Vec.baz(1, a=1)\n    except:\n        pass\n    else:\n        assert False\n\n    try:\n        m.Vec.baz(1, 2, b=2)\n    except:\n        pass\n    else:\n        assert False\n\n    assert m.Vec.nop() == 'nop'\n    assert x.nop() == 'nop'\n    assert y.c == 1100\n\n    # fields #\n    ##########\n\n    if hasattr(t, '__setitem__'):\n        t.a = 99\n        assert equal(t, 99, 11, 'v2')\n        t.tag = 't'\n        assert equal(t, 99, 11, 't')\n\n    # magics #\n    ##########\n\n    assert equal(+y, 100, 1000, '(+y)')\n    assert equal(-y, -100, -1000, '(-y)')\n    assert equal(~y, -101, -1001, '(~y)')\n    assert abs(r) == 5.0\n    assert bool(y)\n    assert not bool(m.Vec())\n    assert len(x) == 1\n    assert len(x + y) == 5\n    assert hash(y) == 1100\n\n    assert equal(x + y, 103.14, 1004.2, '(x+y)')\n    try:\n        x + 'x'\n    except:\n        pass\n    else:\n        assert False\n    assert equal(x + y + y, 203.14, 2004.2, '((x+y)+y)')\n    assert equal(y + 50.5, 150.5, 1050.5, '(y+50.5)')\n    assert equal(y + 50, 150, 1050, '(y++50)')\n    # assert equal(50.5 + y, 150.5, 1050.5, '(y+50.5)')  # support for r-magics?\n    assert equal(y - x, 96.86, 995.8, '(y-x)')\n    assert equal(y * 3.5, 350.0, 3500.0, '(y*3.5)')\n    assert equal(y // 3, 33, 333, '(y//3)')\n    assert equal(y / 2.5, 40.0, 400.0, '(y/2.5)')\n    try:\n        divmod(y, 1)\n    except ArithmeticError as e:\n        assert str(e) == 'no divmod'\n    else:\n        assert False\n    assert equal(y % 7, 2, 6, '(y%7)')\n    assert equal(y ** 2, 10000, 1000000, '(y**2)')\n    assert equal(y << 1, 200, 2000, '(y<<1)')\n    assert equal(y >> 2, 25, 250, '(y>>2)')\n    assert equal(y & 77, 68, 72, '(y&77)')\n    assert equal(y | 77, 109, 1005, '(y|77)')\n    assert equal(y ^ 77, 41, 933, '(y^77)')\n    assert y @ r == 4300\n\n    def dup(v):\n        return m.Vec(v.a, v.b, v.tag + '1')\n\n    y1 = dup(y)\n    y1 += x\n    assert equal(y1, 103.14, 1004.2, '(y1+=x)')\n\n    y1 = dup(y)\n    y1 += 1.5\n    assert equal(y1, 101.5, 1001.5, '(y1+=1.5)')\n\n    y1 = dup(y)\n    y1 -= x\n    assert equal(y1, 96.86, 995.8, '(y1-=x)')\n\n    y1 = dup(y)\n    y1 *= 3.5\n    assert equal(y1, 350.0, 3500.0, '(y1*=3.5)')\n\n    y1 = dup(y)\n    y1 //= 3\n    assert equal(y1, 33, 333, '(y1//=3)')\n\n    y1 = dup(y)\n    y1 /= 2.5\n    assert equal(y1, 40.0, 400.0, '(y1/=2.5)')\n\n    y1 = dup(y)\n    y1 %= 7\n    assert equal(y1, 2, 6, '(y1%=7)')\n\n    y1 = dup(y)\n    y1 **= 2\n    assert equal(y1, 10000, 1000000, '(y1**=2)')\n\n    y1 = dup(y)\n    y1 <<= 1\n    assert equal(y1, 200, 2000, '(y1<<=1)')\n\n    y1 = dup(y)\n    y1 >>= 2\n    assert equal(y1, 25, 250, '(y1>>=2)')\n\n    y1 = dup(y)\n    y1 &= 77\n    assert equal(y1, 68, 72, '(y1&=77)')\n\n    y1 = dup(y)\n    y1 |= 77\n    assert equal(y1, 109, 1005, '(y1|=77)')\n\n    y1 = dup(y)\n    y1 ^= 77\n    assert equal(y1, 41, 933, '(y1^=77)')\n\n    y1 = dup(y)\n    y1 @= 3.5\n    assert equal(y1, 350.0, 3500.0, '(y1@=3.5)')\n\n    assert equal(y(), 100, 1000, '(y())')\n    assert x(2.2, 3.3) == (3.14, 2.2, 3.3)\n    assert y(3.3) == (100, 3.3, 2.22)\n    assert x(a=2.2, b=3.3) == (3.14, 2.2, 3.3)\n    assert x(2.2, b=3.3) == (3.14, 2.2, 3.3)\n    assert x(b=3.3, a=2.2) == (3.14, 2.2, 3.3)\n    assert x(a=2.2) == (3.14, 2.2, 2.22)\n    assert x(b=3.3) == (3.14, 1.11, 3.3)\n    assert y('foo') == (100.0, 1000.0, 'foo')\n\n    assert x == x\n    assert x != y\n    assert r == m.Vec(3, 4, '?')\n    assert x < y\n    assert y > x\n    assert x <= y\n    assert y >= x\n    assert y <= y\n    assert x >= x\n\n    assert list(iter(x)) == ['x']\n    assert list(iter(x+y+y)) == list('((x+y)+y)')\n\n    assert 100 in y\n    assert 1000 in y\n    assert 100.5 not in y\n    assert 'y' in y\n    assert 'x' not in y\n\n    assert y[0] == 100\n    assert y[1] == 1000\n    assert y[11] == 1100\n    try:\n        y[-1]\n    except KeyError as e:\n        assert str(e) == \"'bad vec key -1'\"\n    else:\n        assert False\n\n    global saw_set\n    if hasattr(y, '__setitem__'):\n        y[0] = 99.9\n        assert equal(y, 99.9, 1000, 'y')\n        y[1] = -42.6\n        assert equal(y, 99.9, -42.6, 'y')\n        y[11] = 7.7\n        assert equal(y, 7.7, 7.7, 'y')\n        try:\n            y[2] = 1.2\n        except KeyError as e:\n            assert str(e) == \"'bad vec key 2 with val 1.2'\"\n        else:\n            assert False\n\n        del y[1]\n        assert equal(y, 7.7, 0.0, 'y')\n\n        saw_set = True\n\n    assert m.Vec.nd() > 0\n\n    # tuple classes #\n    #################\n    global saw_foo\n    if hasattr(m, 'Foo'):\n        x = m.Foo(list('hello'))\n        assert x.a == list('hello')\n        assert x.x == {s: i for i, s in enumerate('hello')}\n        assert x.hello() == 'x'\n\n        try:\n            x.a = ['bye']\n        except AttributeError:\n            pass\n        else:\n            assert False\n\n        assert int(x) == 42\n        assert float(x) == 3.14\n        assert x.__index__() == 99\n        saw_foo = True\n\n    # Codon-specific #\n    ##################\n    def par_sum_check(n):\n        m = 0\n        for i in range(n):\n            m += 3*i + 7\n        return m\n\n    for n in (0, 1, 10, 33, 999, 1237):\n        assert m.par_sum(n) == par_sum_check(n)\n\nfor _ in range(3000):\n    test_codon_extensions(m)\n    test_codon_extensions(m2)\n\nassert saw_fun\nassert saw_set\nassert saw_foo\n\nassert str(m.numpy_test().round()) == \"[0. 1.]\"\n"
  },
  {
    "path": "test/python/setup.py",
    "content": "import os\nimport sys\nimport shutil\n\nfrom pathlib import Path\nfrom setuptools import setup, Extension\nfrom setuptools.command.build_ext import build_ext\n\n\ncodon_path = os.environ.get(\"CODON_DIR\")\nif not codon_path:\n    c = shutil.which(\"codon\")\n    if c:\n        codon_path = Path(c).parent / \"..\"\nelse:\n    codon_path = Path(codon_path)\nfor path in [\n    os.path.expanduser(\"~\") + \"/.codon\",\n    os.getcwd() + \"/..\",\n]:\n    path = Path(path)\n    if not codon_path and path.exists():\n        codon_path = path\n        break\n\nif (\n    not codon_path\n    or not (codon_path / \"include\" / \"codon\").exists()\n    or not (codon_path / \"lib\" / \"codon\").exists()\n):\n    print(\n        \"Cannot find Codon.\",\n        'Please either install Codon (/bin/bash -c \"$(curl -fsSL https://exaloop.io/install.sh)\"),',\n        \"or set CODON_DIR if Codon is not in PATH or installed in ~/.codon\",\n        file=sys.stderr,\n    )\n    sys.exit(1)\ncodon_path = codon_path.resolve()\nprint(\"Codon: \" + str(codon_path))\n\n\nclass CodonExtension(Extension):\n    def __init__(self, name, source):\n        self.source = source\n        super().__init__(name, sources=[], language='c')\n\nclass BuildCodonExt(build_ext):\n    def build_extensions(self):\n        pass\n\n    def run(self):\n        inplace, self.inplace = self.inplace, False\n        super().run()\n        for ext in self.extensions:\n            self.build_codon(ext)\n        if inplace:\n            self.copy_extensions_to_source()\n\n    def build_codon(self, ext):\n        extension_path = Path(self.get_ext_fullpath(ext.name))\n        build_dir = Path(self.build_temp)\n        os.makedirs(build_dir, exist_ok=True)\n        os.makedirs(extension_path.parent.absolute(), exist_ok=True)\n\n        optimization = '-debug' if self.debug else '-release'\n        self.spawn([\n            str(codon_path / \"bin\" / \"codon\"), 'build', optimization, \"--relocation-model=pic\",\n            '-pyext', '-o', str(extension_path) + \".o\", '-module', ext.name, ext.source])\n\n        print('-->', extension_path)\n        ext.runtime_library_dirs = [str(codon_path / \"lib\" / \"codon\")]\n        self.compiler.link_shared_object(\n            [str(extension_path) + \".o\"],\n            str(extension_path),\n            libraries=[\"codonrt\"],\n            library_dirs=ext.runtime_library_dirs,\n            runtime_library_dirs=ext.runtime_library_dirs,\n            extra_preargs=['-Wl,-rpath,@loader_path'],\n            # export_symbols=self.get_export_symbols(ext),\n            debug=self.debug,\n            build_temp=self.build_temp,\n        )\n        self.distribution.codon_lib = extension_path\n\nsetup(\n    name='myext',\n    version='0.1',\n    packages=['myext'],\n    ext_modules=[\n        CodonExtension('myext', 'myextension.codon'),\n    ],\n    cmdclass={'build_ext': BuildCodonExt}\n)\n\nsetup(\n    name='myext2',\n    version='0.1',\n    packages=['myext2'],\n    ext_modules=[\n        CodonExtension('myext2', 'myextension2.codon'),\n    ],\n    cmdclass={'build_ext': BuildCodonExt}\n)\n"
  },
  {
    "path": "test/stdlib/asyncio_test.codon",
    "content": "from time import time, sleep\nimport threading\nimport asyncio\n\nget_running_loop = asyncio.get_running_loop\nsleep_async = asyncio.sleep\ngather = asyncio.gather\nensure_future = asyncio.ensure_future\nCancelledError = asyncio.CancelledError\nInvalidStateError = asyncio.InvalidStateError\n\ndef new_loop():\n    return asyncio.new_event_loop()\n\nclass Box[T]:\n    v: T\n\n    def __init__(self):\n        self.v = T()\n\n@test\ndef test_call_soon_fifo_order():\n    loop = new_loop()\n    log: List[int] = []\n\n    def cb(i: int):\n        log.append(i)\n        if i == 4:\n            loop.stop()\n\n    for i in range(5):\n        loop.call_soon(cb, i)\n\n    loop.run_forever()\n    assert log == [0, 1, 2, 3, 4]\n\n@test\ndef test_loop_stop_from_callback():\n    loop = new_loop()\n    ran = Box[int]()\n\n    def cb():\n        ran.v += 1\n        loop.stop()\n\n    loop.call_soon(cb)\n    loop.run_forever()\n    assert ran.v == 1\n\n@test\ndef test_callback_exception_isolated():\n    loop = new_loop()\n    ran = Box[int]()\n\n    def bad(ran):\n        ran.v += 1\n        raise ValueError(\"boom\")\n\n    def good(ran, loop):\n        ran.v += 1\n        loop.stop()\n\n    loop.call_soon(bad, ran)\n    loop.call_soon(good, ran, loop)\n\n    # Expect loop to keep going and run good callback\n    loop.run_forever()\n    assert ran.v == 2\n\n@test\ndef test_call_later_orders_by_time():\n    loop = new_loop()\n    log = []\n\n    def cb(i: int):\n        log.append(i)\n        if len(log) == 3:\n            loop.stop()\n\n    loop.call_later(0.03, cb, 2)\n    loop.call_later(0.01, cb, 0)\n    loop.call_later(0.02, cb, 1)\n\n    loop.run_forever()\n    assert log == [0, 1, 2]\n\n@test\ndef test_call_later_zero_behaves_like_soon():\n    loop = new_loop()\n    log = []\n\n    def cb(i: int):\n        log.append(i)\n        if len(log) == 3:\n            loop.stop()\n\n    loop.call_later(0.0, cb, 1)\n    loop.call_soon(cb, 0)\n    loop.call_later(0.0, cb, 2)\n\n    loop.run_forever()\n    # Implementation-dependent tie-breaker, but should include all.\n    assert sorted(log) == [0, 1, 2]\n    assert len(log) == 3\n\n@test\ndef test_future_set_result_and_result():\n    loop = new_loop()\n    fut = asyncio.Future[int](loop=loop)\n\n    def cb():\n        fut.set_result(123)\n        loop.stop()\n\n    loop.call_soon(cb)\n    loop.run_forever()\n\n    assert fut.done()\n    assert fut.result() == 123\n\n@test\ndef test_future_result_pending_raises():\n    loop = new_loop()\n    fut = asyncio.Future[int](loop=loop)\n\n    ok = False\n    try:\n        fut.result()\n    except InvalidStateError:\n        ok = True\n\n    assert ok\n\n@test\ndef test_future_callbacks_run_once():\n    loop = new_loop()\n    fut = asyncio.Future[int](loop=loop)\n    count = Box[int]()\n\n    def on_done(fut):\n        count.v += 1\n\n    fut.add_done_callback(on_done)\n    fut.add_done_callback(on_done)\n\n    def setit():\n        fut.set_result(7)\n\n    loop.call_soon(setit)\n    loop.run_until_complete(fut)\n    assert count.v == 0\n\n@test\ndef test_future_set_result_twice_raises():\n    loop = new_loop()\n    fut = asyncio.Future[int](loop=loop)\n\n    fut.set_result(1)\n\n    ok = False\n    try:\n        fut.set_result(2)\n    except InvalidStateError:\n        ok = True\n\n    assert ok\n\n@test\ndef test_future_done_callback_added_after_done_runs_soon():\n    loop = new_loop()\n    fut = asyncio.Future[int](loop=loop)\n    seen = Box[bool](False)\n\n    fut.set_result(5)\n\n    def cb(fut):\n        seen.v = True\n        loop.stop()\n\n    fut.add_done_callback(cb)\n    loop.run_forever()\n    assert seen.v\n\n@test\ndef test_await_done_future_no_suspend_observable():\n    loop = new_loop()\n\n    @test\n    async def main():\n        fut = asyncio.Future[int](loop=loop)\n        fut.set_result(99)\n        # If await suspends unnecessarily, this order can change with call_soon\n        log = []\n        log.append(\"before\")\n        x = await fut\n        log.append(\"after\")\n        assert x == 99\n        assert log == [\"before\", \"after\"]\n        loop.stop()\n        return 0\n\n    t = loop.create_task(main())\n    loop.run_forever()\n    assert t.done()\n    assert t.result() == 0\n\n@test\ndef test_sleep_zero_yields_control():\n    loop = new_loop()\n    log = []\n\n    async def a():\n        log.append(\"a1\")\n        await sleep_async(0)\n        log.append(\"a2\")\n        return 0\n\n    async def b():\n        log.append(\"b1\")\n        await sleep_async(0)\n        log.append(\"b2\")\n        loop.stop()\n        return 0\n\n    loop.create_task(a())\n    loop.create_task(b())\n    loop.run_forever()\n\n    # Must include all markers, with interleaving possible\n    assert set(log) == {\"a1\", \"a2\", \"b1\", \"b2\"}\n    assert log[0] in [\"a1\", \"b1\"]\n    # Both a2 and b2 should occur after their respective a1/b1\n    assert log.index(\"a2\") > log.index(\"a1\")\n    assert log.index(\"b2\") > log.index(\"b1\")\n\n@test\ndef test_sleep_delay_approximately():\n    loop = new_loop()\n\n    @test\n    async def main():\n        t0 = time()\n        await sleep_async(0.05)\n        dt = time() - t0\n        # Don't be too strict; scheduler granularity differs\n        assert dt >= 0.03\n        loop.stop()\n        return 0\n\n    loop.create_task(main())\n    loop.run_forever()\n\n@test\ndef test_task_exception_propagates_to_await():\n    loop = new_loop()\n\n    async def child():\n        await sleep_async(0)\n        raise ValueError(\"boom\")\n\n    @test\n    async def parent():\n        try:\n            await child()\n            assert False\n        except ValueError as e:\n            assert \"boom\" in str(e)\n        loop.stop()\n\n    loop.create_task(parent())\n    loop.run_forever()\n\n@test\ndef test_cancellation_delivered_at_await_boundary_and_finally_runs():\n    loop = new_loop()\n    log: List[str] = []\n\n    async def child():\n        try:\n            log.append(\"child_enter\")\n            await sleep_async(10)  # will be cancelled\n            log.append(\"child_after\")  # should not happen\n        finally:\n            log.append(\"child_finally\")\n\n    async def parent():\n        try:\n            log.append(\"parent_enter\")\n            await child()\n            log.append(\"parent_after\")  # should not happen\n        finally:\n            log.append(\"parent_finally\")\n\n    t = loop.create_task(parent())\n\n    async def killer():\n        await sleep_async(0)\n        t.cancel(\"stop\")\n        await sleep_async(0)\n        loop.stop()\n        return 0\n\n    loop.create_task(killer())\n    loop.run_forever()\n\n    # child_finally and parent_finally must run\n    assert \"child_finally\" in log\n    assert \"parent_finally\" in log\n    assert \"child_after\" not in log\n    assert \"parent_after\" not in log\n\n@test\ndef test_cancel_then_await_never_completing_future_raises_immediately():\n    loop = new_loop()\n\n    @test\n    async def main():\n        fut = asyncio.Future[int](loop=loop)\n        # cancel current task then await fut that never completes\n        t = asyncio.current_task()\n        t.cancel(\"cancelled\")\n        try:\n            await fut\n            assert False\n        except CancelledError:\n            pass\n        loop.stop()\n        return 0\n\n    loop.create_task(main())\n    loop.run_forever()\n\n@test\ndef test_cancel_idempotent_and_returns_bool():\n    loop = new_loop()\n\n    async def main():\n        await sleep_async(10)\n        return 0\n\n    t = loop.create_task(main())\n\n    @test\n    async def killer():\n        await sleep_async(0)\n        r1 = t.cancel(\"x\")\n        r2 = t.cancel(\"y\")\n        assert r1 == True\n        loop.stop()\n        return 0\n\n    loop.create_task(killer())\n    loop.run_forever()\n\n@test\ndef test_structured_await_vs_spawn_order():\n    loop = new_loop()\n    log: List[str] = []\n\n    async def child(log):\n        log.append(\"child_start\")\n        await sleep_async(0)\n        log.append(\"child_end\")\n        return 7\n\n    async def structured(log):\n        log.append(\"structured_before\")\n        x = await child(log)\n        log.append(f\"structured_after_{x}\")\n\n    async def spawned(log):\n        log.append(\"spawned_before\")\n        t = loop.create_task(child(log))\n        log.append(\"spawned_after_create_task\")\n        x = await t\n        log.append(f\"spawned_after_{x}\")\n\n    async def main(log):\n        await structured(log)\n        await spawned(log)\n        loop.stop()\n\n    loop.create_task(main(log))\n    loop.run_forever()\n\n    # Structured must preserve child within await boundary\n    si = log.index(\"structured_before\")\n    cs = log.index(\"child_start\")\n    ce = log.index(\"child_end\")\n    sa = log.index(\"structured_after_7\")\n    assert si < cs < ce < sa\n\n    # Spawned has looser ordering: after_create_task may occur before child_start\n    assert \"spawned_after_create_task\" in log\n    assert \"spawned_after_7\" in log\n\n@test\ndef test_ensure_future_coro_runs():\n    loop = new_loop()\n    ran = Box[bool](False)\n\n    async def child():\n        ran.v = True\n        return 3\n\n    @test\n    async def main():\n        f = ensure_future(child(), loop=loop)\n        x = await f\n        assert x == 3\n        assert ran.v\n        loop.stop()\n        return 0\n\n    loop.create_task(main())\n    loop.run_forever()\n\n@test\ndef test_gather_success():\n    loop = new_loop()\n\n    async def a():\n        await sleep_async(0.01)\n        return 1\n\n    async def b():\n        await sleep_async(0.02)\n        return 2\n\n    @test\n    async def main():\n        r = await gather(a(), b())\n        assert r == (1, 2)\n        loop.stop()\n        return 0\n\n    loop.create_task(main())\n    loop.run_forever()\n\n@test\ndef test_gather_fail_fast_does_not_cancel_others():\n    loop = new_loop()\n    ran_late = Box[bool](False)\n\n    async def bad():\n        await sleep_async(0.01)\n        raise ValueError(\"boom\")\n\n    async def slow():\n        await sleep_async(0.05)\n        ran_late.v = True\n        return 123\n\n    @test\n    async def main():\n        try:\n            await gather(bad(), slow())\n            assert False\n        except ValueError:\n            pass\n        # Let loop run a bit more so slow can complete; it must NOT be cancelled.\n        await sleep_async(0.06)\n        assert ran_late.v\n        loop.stop()\n        return 0\n\n    loop.create_task(main())\n    loop.run_forever()\n\n@test\ndef test_gather_cancel_outer_cancels_children():\n    loop = new_loop()\n    child_cancelled = Box[int]()\n\n    async def child():\n        try:\n            await sleep_async(10)\n        finally:\n            child_cancelled.v += 1\n\n    @test\n    async def main():\n        g = ensure_future(gather(child(), child()), loop=loop)\n        await sleep_async(0)\n        g.cancel(\"cancel gather\")\n        try:\n            await g\n        except CancelledError:\n            pass\n        # give children a tick to run finally\n        await sleep_async(0)\n        assert child_cancelled.v == 2\n        loop.stop()\n        return 0\n\n    loop.create_task(main())\n    loop.run_forever()\n\n@test\ndef test_await_task_is_scheduled():\n    loop = new_loop()\n    ran = Box[bool](False)\n\n    async def child():\n        ran.v = True\n        return 9\n\n    @test\n    async def main():\n        t = loop.create_task(child())\n        x = await t\n        assert x == 9\n        assert ran.v\n        loop.stop()\n        return 0\n\n    loop.create_task(main())\n    loop.run_forever()\n\n@test\ndef test_await_future_does_not_schedule_producer():\n    loop = new_loop()\n    fut = asyncio.Future[int](loop=loop)\n\n    @test\n    async def main():\n        # Await will block until someone sets the result.\n        # We'll set it via call_later\n        def setit():\n            fut.set_result(77)\n            return 0\n        loop.call_later(0.1, setit)\n        x = await fut\n        assert x == 77\n        loop.stop()\n        return 0\n\n    loop.create_task(main())\n    loop.run_forever()\n\n@test\ndef test_no_missed_wakeup_when_future_completes_race():\n    loop = new_loop()\n\n    fut = asyncio.Future[int](loop=loop)\n\n    @test\n    async def waiter():\n        x = await fut\n        assert x == 1\n        loop.stop()\n        return 0\n\n    async def setter():\n        # Try to race by setting soon\n        await sleep_async(0)\n        fut.set_result(1)\n        return 0\n\n    loop.create_task(waiter())\n    loop.create_task(setter())\n    loop.run_forever()\n\n@test\ndef test_custom_await():\n\n    class MyFuture:\n        fut: asyncio.Future[int]\n        res: int\n\n        def __init__(self, res: int):\n            self.fut = asyncio.get_running_loop().create_future(int)\n            self.res = res\n\n        def __await__(self):\n            return self.fut.__await__()\n\n        def resolve(self):\n            self.fut.set_result(self.res)\n\n    class MyAwaitable:\n        def __await__(self):\n            yield\n            yield\n\n    @test\n    async def main():\n        fut = MyFuture(42)\n        loop = asyncio.get_running_loop()\n        loop.call_later(1, fut.resolve)\n        assert await fut == 42\n        await MyAwaitable()\n\n    asyncio.run(main())\n\n\ntest_call_soon_fifo_order()\ntest_loop_stop_from_callback()\ntest_callback_exception_isolated()\ntest_call_later_orders_by_time()\ntest_call_later_zero_behaves_like_soon()\ntest_future_set_result_and_result()\ntest_future_result_pending_raises()\ntest_future_callbacks_run_once()\ntest_future_set_result_twice_raises()\ntest_future_done_callback_added_after_done_runs_soon()\ntest_await_done_future_no_suspend_observable()\ntest_sleep_zero_yields_control()\ntest_sleep_delay_approximately()\ntest_task_exception_propagates_to_await()\ntest_cancellation_delivered_at_await_boundary_and_finally_runs()\ntest_cancel_then_await_never_completing_future_raises_immediately()\ntest_cancel_idempotent_and_returns_bool()\ntest_structured_await_vs_spawn_order()\ntest_ensure_future_coro_runs()\ntest_gather_success()\ntest_gather_fail_fast_does_not_cancel_others()\ntest_gather_cancel_outer_cancels_children()\ntest_await_task_is_scheduled()\ntest_await_future_does_not_schedule_producer()\ntest_no_missed_wakeup_when_future_completes_race()\ntest_custom_await()\n"
  },
  {
    "path": "test/stdlib/bisect_test.codon",
    "content": "import bisect\nimport sys\n\nli = [1, 3, 4, 4, 4, 6, 7]\nlst = [10, 20, 30, 40, 50]\n\nstr = [\"a\", \"b\", \"b\", \"c\", \"d\"]\n\n\n@test\ndef bisect_left():\n    assert bisect.bisect_left(li, 4, 0, len(li)) == 2\n    assert bisect.bisect_left(li, 4) == 2\n    assert bisect.bisect_left(str, \"b\", 0, len(str)) == 1\n    assert bisect.bisect_left(lst, 25, 1, 3) == 2\n\n    # precomputed cases\n    assert bisect.bisect_left(List[int](), 1, 0, 0) == 0\n    assert bisect.bisect_left([1], 0, 0, 1) == 0\n    assert bisect.bisect_left([1], 1, 0, 1) == 0\n    assert bisect.bisect_left([1], 2, 0, 1) == 1\n    assert bisect.bisect_left([1, 1], 0, 0, 2) == 0\n    assert bisect.bisect_left([1, 1], 1, 0, 2) == 0\n    assert bisect.bisect_left([1, 1], 2, 0, 2) == 2\n    assert bisect.bisect_left([1, 1, 1], 0, 0, 3) == 0\n    assert bisect.bisect_left([1, 1, 1], 1, 0, 3) == 0\n    assert bisect.bisect_left([1, 1, 1], 2, 0, 3) == 3\n    assert bisect.bisect_left([1, 1, 1, 1], 0, 0, 4) == 0\n    assert bisect.bisect_left([1, 1, 1, 1], 1, 0, 4) == 0\n    assert bisect.bisect_left([1, 1, 1, 1], 2, 0, 4) == 4\n    assert bisect.bisect_left([1, 2], 0, 0, 2) == 0\n    assert bisect.bisect_left([1, 2], 1, 0, 2) == 0\n    assert bisect.bisect_left([1, 2], 1.5, 0, 2) == 1\n    assert bisect.bisect_left([1, 2], 2, 0, 2) == 1\n    assert bisect.bisect_left([1, 2], 3, 0, 2) == 2\n    assert bisect.bisect_left([1, 1, 2, 2], 0, 0, 4) == 0\n    assert bisect.bisect_left([1, 1, 2, 2], 1, 0, 4) == 0\n    assert bisect.bisect_left([1, 1, 2, 2], 1.5, 0, 4) == 2\n    assert bisect.bisect_left([1, 1, 2, 2], 2, 0, 4) == 2\n    assert bisect.bisect_left([1, 1, 2, 2], 3, 0, 4) == 4\n    assert bisect.bisect_left([1, 2, 3], 0, 0, 3) == 0\n    assert bisect.bisect_left([1, 2, 3], 1, 0, 3) == 0\n    assert bisect.bisect_left([1, 2, 3], 1.5, 0, 3) == 1\n    assert bisect.bisect_left([1, 2, 3], 2, 0, 3) == 1\n    assert bisect.bisect_left([1, 2, 3], 2.5, 0, 3) == 2\n    assert bisect.bisect_left([1, 2, 3], 3, 0, 3) == 2\n    assert bisect.bisect_left([1, 2, 3], 4, 0, 3) == 3\n    assert bisect.bisect_left([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 0, 0, 10) == 0\n    assert bisect.bisect_left([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1, 0, 10) == 0\n    assert bisect.bisect_left([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1.5, 0, 10) == 1\n    assert bisect.bisect_left([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2, 0, 10) == 1\n    assert bisect.bisect_left([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2.5, 0, 10) == 3\n    assert bisect.bisect_left([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3, 0, 10) == 3\n    assert bisect.bisect_left([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3.5, 0, 10) == 6\n    assert bisect.bisect_left([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 4, 0, 10) == 6\n    assert bisect.bisect_left([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 5, 0, 10) == 10\n\n\n@test\ndef bisect_right():\n    assert bisect.bisect_right(li, 4, 0, len(li)) == 5\n    assert bisect.bisect_right(li, 4) == 5\n    assert bisect.bisect_right(str, \"b\", 0, len(str)) == 3\n    assert bisect.bisect_right(lst, 25, 1, 3) == 2\n\n    # precomputed casesrightrt bisect.bisect_right(List[int](), 1, 0, 0) == 0\n    assert bisect.bisect_right([1], 0, 0, 1) == 0\n    assert bisect.bisect_right([1], 1, 0, 1) == 1\n    assert bisect.bisect_right([1], 2, 0, 1) == 1\n    assert bisect.bisect_right([1, 1], 0, 0, 2) == 0\n    assert bisect.bisect_right([1, 1], 1, 0, 2) == 2\n    assert bisect.bisect_right([1, 1], 2, 0, 2) == 2\n    assert bisect.bisect_right([1, 1, 1], 0, 0, 3) == 0\n    assert bisect.bisect_right([1, 1, 1], 1, 0, 3) == 3\n    assert bisect.bisect_right([1, 1, 1], 2, 0, 3) == 3\n    assert bisect.bisect_right([1, 1, 1, 1], 0, 0, 4) == 0\n    assert bisect.bisect_right([1, 1, 1, 1], 1, 0, 4) == 4\n    assert bisect.bisect_right([1, 1, 1, 1], 2, 0, 4) == 4\n    assert bisect.bisect_right([1, 2], 0, 0, 2) == 0\n    assert bisect.bisect_right([1, 2], 1, 0, 2) == 1\n    assert bisect.bisect_right([1, 2], 1.5, 0, 2) == 1\n    assert bisect.bisect_right([1, 2], 2, 0, 2) == 2\n    assert bisect.bisect_right([1, 2], 3, 0, 2) == 2\n    assert bisect.bisect_right([1, 1, 2, 2], 0, 0, 4) == 0\n    assert bisect.bisect_right([1, 1, 2, 2], 1, 0, 4) == 2\n    assert bisect.bisect_right([1, 1, 2, 2], 1.5, 0, 4) == 2\n    assert bisect.bisect_right([1, 1, 2, 2], 2, 0, 4) == 4\n    assert bisect.bisect_right([1, 1, 2, 2], 3, 0, 4) == 4\n    assert bisect.bisect_right([1, 2, 3], 0, 0, 3) == 0\n    assert bisect.bisect_right([1, 2, 3], 1, 0, 3) == 1\n    assert bisect.bisect_right([1, 2, 3], 1.5, 0, 3) == 1\n    assert bisect.bisect_right([1, 2, 3], 2, 0, 3) == 2\n    assert bisect.bisect_right([1, 2, 3], 2.5, 0, 3) == 2\n    assert bisect.bisect_right([1, 2, 3], 3, 0, 3) == 3\n    assert bisect.bisect_right([1, 2, 3], 4, 0, 3) == 3\n    assert bisect.bisect_right([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 0, 0, 10) == 0\n    assert bisect.bisect_right([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1, 0, 10) == 1\n    assert bisect.bisect_right([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1.5, 0, 10) == 1\n    assert bisect.bisect_right([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2, 0, 10) == 3\n    assert bisect.bisect_right([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2.5, 0, 10) == 3\n    assert bisect.bisect_right([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3, 0, 10) == 6\n    assert bisect.bisect_right([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3.5, 0, 10) == 6\n    assert bisect.bisect_right([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 4, 0, 10) == 10\n    assert bisect.bisect_right([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 5, 0, 10) == 10\n\n\n@test\ndef bisect1():\n    assert bisect.bisect(li, 4, 0, len(li)) == 5\n    assert bisect.bisect(str, \"b\", 0, len(str)) == 3\n    assert bisect.bisect(lst, 25, 1, 3) == 2\n\n    # precomputed casesrightrt bisect.bisect_right(List[int](), 1, 0, 0) == 0\n    assert bisect.bisect([1], 0, 0, 1) == 0\n    assert bisect.bisect([1], 1, 0, 1) == 1\n    assert bisect.bisect([1], 2, 0, 1) == 1\n    assert bisect.bisect([1, 1], 0, 0, 2) == 0\n    assert bisect.bisect([1, 1], 1, 0, 2) == 2\n    assert bisect.bisect([1, 1], 2, 0, 2) == 2\n    assert bisect.bisect([1, 1, 1], 0, 0, 3) == 0\n    assert bisect.bisect([1, 1, 1], 1, 0, 3) == 3\n    assert bisect.bisect([1, 1, 1], 2, 0, 3) == 3\n    assert bisect.bisect([1, 1, 1, 1], 0, 0, 4) == 0\n    assert bisect.bisect([1, 1, 1, 1], 1, 0, 4) == 4\n    assert bisect.bisect([1, 1, 1, 1], 2, 0, 4) == 4\n    assert bisect.bisect([1, 2], 0, 0, 2) == 0\n    assert bisect.bisect([1, 2], 1, 0, 2) == 1\n    assert bisect.bisect([1, 2], 1.5, 0, 2) == 1\n    assert bisect.bisect([1, 2], 2, 0, 2) == 2\n    assert bisect.bisect([1, 2], 3, 0, 2) == 2\n    assert bisect.bisect([1, 1, 2, 2], 0, 0, 4) == 0\n    assert bisect.bisect([1, 1, 2, 2], 1, 0, 4) == 2\n    assert bisect.bisect([1, 1, 2, 2], 1.5, 0, 4) == 2\n    assert bisect.bisect([1, 1, 2, 2], 2, 0, 4) == 4\n    assert bisect.bisect([1, 1, 2, 2], 3, 0, 4) == 4\n    assert bisect.bisect([1, 2, 3], 0, 0, 3) == 0\n    assert bisect.bisect([1, 2, 3], 1, 0, 3) == 1\n    assert bisect.bisect([1, 2, 3], 1.5, 0, 3) == 1\n    assert bisect.bisect([1, 2, 3], 2, 0, 3) == 2\n    assert bisect.bisect([1, 2, 3], 2.5, 0, 3) == 2\n    assert bisect.bisect([1, 2, 3], 3, 0, 3) == 3\n    assert bisect.bisect([1, 2, 3], 4, 0, 3) == 3\n    assert bisect.bisect([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 0, 0, 10) == 0\n    assert bisect.bisect([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1, 0, 10) == 1\n    assert bisect.bisect([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 1.5, 0, 10) == 1\n    assert bisect.bisect([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2, 0, 10) == 3\n    assert bisect.bisect([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 2.5, 0, 10) == 3\n    assert bisect.bisect([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3, 0, 10) == 6\n    assert bisect.bisect([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 3.5, 0, 10) == 6\n    assert bisect.bisect([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 4, 0, 10) == 10\n    assert bisect.bisect([1, 2, 2, 3, 3, 3, 4, 4, 4, 4], 5, 0, 10) == 10\n\n\n@test\ndef insort_left():\n    bisect.insort_left(li, 5)\n    assert li == [1, 3, 4, 4, 4, 5, 6, 7]\n\n\n@test\ndef insort_right():\n    li = [1, 3, 4, 4, 4, 6, 7]\n    bisect.insort_right(li, 0, 0, len(li))\n    assert li == [0, 1, 3, 4, 4, 4, 6, 7]\n    bisect.insort_right(li, 10)\n    assert li == [0, 1, 3, 4, 4, 4, 6, 7, 10]\n\n\n@test\ndef insort():\n    li = [1, 3, 4, 4, 4, 6, 7]\n    bisect.insort(li, 0, 0, len(li))\n    assert li == [0, 1, 3, 4, 4, 4, 6, 7]\n    bisect.insort(li, 10)\n    assert li == [0, 1, 3, 4, 4, 4, 6, 7, 10]\n\n\nbisect_left()\nbisect_right()\nbisect1()\ninsort_left()\ninsort_right()\ninsort()\n"
  },
  {
    "path": "test/stdlib/cmath_test.codon",
    "content": "import math\nimport cmath\n\nINF = float(\"inf\")\nNAN = float(\"nan\")\n\n\ndef float_identical(x, y):\n    if math.isnan(x) or math.isnan(y):\n        if math.isnan(x) and math.isnan(y):\n            return True\n    elif x == y:\n        if x != 0.0:\n            return True\n        # both zero; check that signs match\n        elif math.copysign(1.0, x) == math.copysign(1.0, y):\n            return True\n        else:\n            return False\n    return False\n\n\ndef complex_identical(x, y):\n    return float_identical(x.real, y.real) and float_identical(x.imag, y.imag)\n\n\n###########\n# complex #\n###########\n\nZERO_DIVISION = (\n    (1 + 1j, 0 + 0j),\n    (1 + 1j, 0.0 + 0j),\n    (1 + 1j, 0 + 0j),\n    (1.0 + 0j, 0 + 0j),\n    (1 + 0j, 0 + 0j),\n)\n\n\ndef close_abs(x, y, eps=1e-9):\n    \"\"\"Return true iff floats x and y \"are close\".\"\"\"\n    # put the one with larger magnitude second\n    if abs(x) > abs(y):\n        x, y = y, x\n    if y == 0:\n        return abs(x) < eps\n    if x == 0:\n        return abs(y) < eps\n    # check that relative difference < eps\n    return abs((x - y) / y) < eps\n\n\ndef close_complex(x, y, eps=1e-9):\n    a = complex(x)\n    b = complex(y)\n    return close_abs(a.real, b.real, eps) and close_abs(a.imag, b.imag, eps)\n\n\ndef check_div(x, y):\n    \"\"\"Compute complex z=x*y, and check that z/x==y and z/y==x.\"\"\"\n    z = x * y\n    if x != 0:\n        q = z / x\n        if not close_complex(q, y):\n            return False\n        q = z.__truediv__(x)\n        if not close_complex(q, y):\n            return False\n    if y != 0:\n        q = z / y\n        if not close_complex(q, x):\n            return False\n        q = z.__truediv__(y)\n        if not close_complex(q, x):\n            return False\n    return True\n\n\n@test\ndef test_truediv():\n    from random import random\n\n    simple_real = [float(i) for i in range(-5, 6)]\n    simple_complex = [complex(x, y) for x in simple_real for y in simple_real]\n    for x in simple_complex:\n        for y in simple_complex:\n            assert check_div(x, y)\n\n    # A naive complex division algorithm (such as in 2.0) is very prone to\n    # nonsense errors for these (overflows and underflows).\n    assert check_div(complex(1e200, 1e200), 1 + 0j)\n    assert check_div(complex(1e-200, 1e-200), 1 + 0j)\n\n    # Just for fun.\n    for i in range(100):\n        check_div(complex(random(), random()), complex(random(), random()))\n\n    assert close_complex(complex.__truediv__(2 + 0j, 1 + 1j), 1 - 1j)\n\n    for denom_real, denom_imag in [(0.0, NAN), (NAN, 0.0), (NAN, NAN)]:\n        z = complex(0, 0) / complex(denom_real, denom_imag)\n        assert math.isnan(z.real)\n        assert math.isnan(z.imag)\n\n\ntest_truediv()\n\n\n@test\ndef test_richcompare():\n    assert not complex.__eq__(1 + 1j, 1 << 10000)\n    assert complex.__eq__(1 + 1j, 1 + 1j)\n    assert not complex.__eq__(1 + 1j, 2 + 2j)\n    assert not complex.__ne__(1 + 1j, 1 + 1j)\n    assert complex.__ne__(1 + 1j, 2 + 2j), True\n    for i in range(1, 100):\n        f = i / 100.0\n        assert complex.__eq__(f + 0j, f)\n        assert not complex.__ne__(f + 0j, f)\n        assert not complex.__eq__(complex(f, f), f)\n        assert complex.__ne__(complex(f, f), f)\n\n    import operator\n\n    assert operator.eq(1 + 1j, 1 + 1j) == True\n    assert operator.eq(1 + 1j, 2 + 2j) == False\n    assert operator.ne(1 + 1j, 1 + 1j) == False\n    assert operator.ne(1 + 1j, 2 + 2j) == True\n\n\ntest_richcompare()\n\n\n@test\ndef test_pow():\n    def pow(a, b):\n        return a ** b\n\n    assert close_complex(pow(1 + 1j, 0 + 0j), 1.0)\n    assert close_complex(pow(0 + 0j, 2 + 0j), 0.0)\n    assert close_complex(pow(1j, -1), 1 / (1j))\n    assert close_complex(pow(1j, 200), 1)\n\n    a = 3.33 + 4.43j\n    assert a ** (0j) == 1\n    assert a ** (0.0 + 0.0j) == 1\n\n    assert (3j) ** (0j) == 1\n    assert (3j) ** 0 == 1\n\n    # The following is used to exercise certain code paths\n    assert a ** 105 == a ** 105\n    assert a ** -105 == a ** -105\n    assert a ** -30 == a ** -30\n\n    assert (0.0j) ** 0 == 1\n\n\ntest_pow()\n\n\n@test\ndef test_conjugate():\n    assert close_complex(complex(5.3, 9.8).conjugate(), 5.3 - 9.8j)\n\n\ntest_conjugate()\n\n\n@test\ndef test_cabs():\n    nums = [complex(x / 3.0, y / 7.0) for x in range(-9, 9) for y in range(-9, 9)]\n    for num in nums:\n        assert close_complex((num.real ** 2 + num.imag ** 2) ** 0.5, abs(num))\n\n\ntest_cabs()\n\n\n@test\ndef test_negative_zero_repr_str():\n    def test(v, expected):\n        return str(v) == expected\n\n    assert test(complex(0.0, 1.0), \"1j\")\n    assert test(complex(-0.0, 1.0), \"(-0+1j)\")\n    assert test(complex(0.0, -1.0), \"-1j\")\n    assert test(complex(-0.0, -1.0), \"(-0-1j)\")\n\n    assert test(complex(0.0, 0.0), \"0j\")\n    assert test(complex(0.0, -0.0), \"-0j\")\n    assert test(complex(-0.0, 0.0), \"(-0+0j)\")\n    assert test(complex(-0.0, -0.0), \"(-0-0j)\")\n\n\ntest_negative_zero_repr_str()\n\n#########\n# cmath #\n#########\n\ncomplex_zeros = [complex(x, y) for x in [0.0, -0.0] for y in [0.0, -0.0]]\ncomplex_infinities = [\n    complex(x, y)\n    for x, y in [\n        (INF, 0.0),  # 1st quadrant\n        (INF, 2.3),\n        (INF, INF),\n        (2.3, INF),\n        (0.0, INF),\n        (-0.0, INF),  # 2nd quadrant\n        (-2.3, INF),\n        (-INF, INF),\n        (-INF, 2.3),\n        (-INF, 0.0),\n        (-INF, -0.0),  # 3rd quadrant\n        (-INF, -2.3),\n        (-INF, -INF),\n        (-2.3, -INF),\n        (-0.0, -INF),\n        (0.0, -INF),  # 4th quadrant\n        (2.3, -INF),\n        (INF, -INF),\n        (INF, -2.3),\n        (INF, -0.0),\n    ]\n]\ncomplex_nans = [\n    complex(x, y)\n    for x, y in [\n        (NAN, -INF),\n        (NAN, -2.3),\n        (NAN, -0.0),\n        (NAN, 0.0),\n        (NAN, 2.3),\n        (NAN, INF),\n        (-INF, NAN),\n        (-2.3, NAN),\n        (-0.0, NAN),\n        (0.0, NAN),\n        (2.3, NAN),\n        (INF, NAN),\n    ]\n]\n\n\n@llvm\n@pure\ndef small() -> float:\n    ret double 4.940660e-323\n\n\ndef almost_equal(a, b, rel_err=2e-15, abs_err=small()):\n    if math.isnan(a):\n        if math.isnan(b):\n            return True\n        return False\n\n    if math.isinf(a):\n        if a == b:\n            return True\n        return False\n\n    if not a and not b:\n        if math.copysign(1.0, a) != math.copysign(1.0, b):\n            return False\n\n    absolute_error = abs(b - a)\n    if absolute_error <= max(abs_err, rel_err * abs(a)):\n        return True\n    return False\n\n\n@test\ndef test_constants():\n    e_expected = 2.71828182845904523536\n    pi_expected = 3.14159265358979323846\n    assert math.isclose(cmath.pi, pi_expected)\n    assert math.isclose(cmath.e, e_expected)\n\n\ntest_constants()\n\n\n@test\ndef test_infinity_and_nan_constants():\n    assert cmath.inf.real == math.inf\n    assert cmath.inf.imag == 0.0\n    assert cmath.infj.real == 0.0\n    assert cmath.infj.imag == math.inf\n\n    assert math.isnan(cmath.nan.real)\n    assert cmath.nan.imag == 0.0\n    assert cmath.nanj.real == 0.0\n    assert math.isnan(cmath.nanj.imag)\n\n    assert str(cmath.inf) == \"inf\"\n    assert str(cmath.infj) == \"infj\"\n    assert str(cmath.nan) == \"nan\"\n    assert str(cmath.nanj) == \"nanj\"\n\n\ntest_infinity_and_nan_constants()\n\n\n@test\ndef test_user_object():\n    class MyComplexOS:\n        value: T\n        T: type\n\n        def __init__(self, value: T):\n            self.value = value\n\n        def __complex__(self):\n            return self.value\n\n    x = MyComplexOS(4.2)\n    assert cmath.acos(x) == cmath.acos(x.value)\n    assert cmath.acosh(x) == cmath.acosh(x.value)\n    assert cmath.asin(x) == cmath.asin(x.value)\n    assert cmath.asinh(x) == cmath.asinh(x.value)\n    assert cmath.atan(x) == cmath.atan(x.value)\n    assert cmath.atanh(x) == cmath.atanh(x.value)\n    assert cmath.cos(x) == cmath.cos(x.value)\n    assert cmath.cosh(x) == cmath.cosh(x.value)\n    assert cmath.exp(x) == cmath.exp(x.value)\n    assert cmath.log(x) == cmath.log(x.value)\n    assert cmath.log10(x) == cmath.log10(x.value)\n    assert cmath.sin(x) == cmath.sin(x.value)\n    assert cmath.sinh(x) == cmath.sinh(x.value)\n    assert cmath.sqrt(x) == cmath.sqrt(x.value)\n    assert cmath.tan(x) == cmath.tan(x.value)\n    assert cmath.tanh(x) == cmath.tanh(x.value)\n\n\ntest_user_object()\n\n\n@test\ndef test_input_type():\n    x = 42\n    y = float(x)\n    assert cmath.acos(x) == cmath.acos(y)\n    assert cmath.acosh(x) == cmath.acosh(y)\n    assert cmath.asin(x) == cmath.asin(y)\n    assert cmath.asinh(x) == cmath.asinh(y)\n    assert cmath.atan(x) == cmath.atan(y)\n    assert cmath.atanh(x) == cmath.atanh(y)\n    assert cmath.cos(x) == cmath.cos(y)\n    assert cmath.cosh(x) == cmath.cosh(y)\n    assert cmath.exp(x) == cmath.exp(y)\n    assert cmath.log(x) == cmath.log(y)\n    assert cmath.log10(x) == cmath.log10(y)\n    assert cmath.sin(x) == cmath.sin(y)\n    assert cmath.sinh(x) == cmath.sinh(y)\n    assert cmath.sqrt(x) == cmath.sqrt(y)\n    assert cmath.tan(x) == cmath.tan(y)\n    assert cmath.tanh(x) == cmath.tanh(y)\n\n\ntest_input_type()\n\n\n@test\ndef test_cmath_matches_math():\n    test_values = [0.01, 0.1, 0.2, 0.5, 0.9, 0.99]\n    unit_interval = test_values + [-x for x in test_values] + [0.0, 1.0, -1.0]\n    positive = test_values + [1.0] + [1.0 / x for x in test_values]\n    nonnegative = [0.0] + positive\n    real_line = [0.0] + positive + [-x for x in positive]\n\n    test_functions = {\n        \"acos\": unit_interval,\n        \"asin\": unit_interval,\n        \"atan\": real_line,\n        \"cos\": real_line,\n        \"cosh\": real_line,\n        \"exp\": real_line,\n        \"log\": positive,\n        \"log10\": positive,\n        \"sin\": real_line,\n        \"sinh\": real_line,\n        \"sqrt\": nonnegative,\n        \"tan\": real_line,\n        \"tanh\": real_line,\n    }\n\n    for v in test_functions[\"acos\"]:\n        z = cmath.acos(v)\n        assert almost_equal(z.real, math.acos(v))\n        assert z.imag == 0.0\n\n    for v in test_functions[\"asin\"]:\n        z = cmath.asin(v)\n        assert almost_equal(z.real, math.asin(v))\n        assert z.imag == 0.0\n\n    for v in test_functions[\"atan\"]:\n        z = cmath.atan(v)\n        assert almost_equal(z.real, math.atan(v))\n        assert z.imag == 0.0\n\n    for v in test_functions[\"cos\"]:\n        z = cmath.cos(v)\n        assert almost_equal(z.real, math.cos(v))\n        assert z.imag == 0.0\n\n    for v in test_functions[\"cosh\"]:\n        z = cmath.cosh(v)\n        assert almost_equal(z.real, math.cosh(v))\n        assert z.imag == 0.0\n\n    for v in test_functions[\"exp\"]:\n        z = cmath.exp(v)\n        assert almost_equal(z.real, math.exp(v))\n        assert z.imag == 0.0\n\n    for v in test_functions[\"log\"]:\n        z = cmath.log(v)\n        assert almost_equal(z.real, math.log(v))\n        assert z.imag == 0.0\n\n    for v in test_functions[\"log10\"]:\n        z = cmath.log10(v)\n        assert almost_equal(z.real, math.log10(v))\n        assert z.imag == 0.0\n\n    for v in test_functions[\"sin\"]:\n        z = cmath.sin(v)\n        assert almost_equal(z.real, math.sin(v))\n        assert z.imag == 0.0\n\n    for v in test_functions[\"sinh\"]:\n        z = cmath.sinh(v)\n        assert almost_equal(z.real, math.sinh(v))\n        assert z.imag == 0.0\n\n    for v in test_functions[\"sqrt\"]:\n        z = cmath.sqrt(v)\n        assert almost_equal(z.real, math.sqrt(v))\n        assert z.imag == 0.0\n\n    for v in test_functions[\"tan\"]:\n        z = cmath.tan(v)\n        assert almost_equal(z.real, math.tan(v))\n        assert z.imag == 0.0\n\n    for v in test_functions[\"tanh\"]:\n        z = cmath.tanh(v)\n        assert almost_equal(z.real, math.tanh(v))\n        assert z.imag == 0.0\n\n    for base in [0.5, 2.0, 10.0]:\n        for v in positive:\n            z = cmath.log(v, base)\n            s = math.log(v, base)\n            # added 'or z.real == s' since Codon version gives -0 vs. +0 in one test\n            assert almost_equal(z.real, math.log(v, base)) or z.real == s\n            assert z.imag == 0.0\n\n\ntest_cmath_matches_math()\n\n\n@test\ndef test_polar():\n    def check(arg, expected):\n        got = cmath.polar(arg)\n        return all(almost_equal(e, g) for e, g in zip(expected, got))\n\n    pi = cmath.pi\n    assert check(0, (0.0, 0.0))\n    assert check(1, (1.0, 0.0))\n    assert check(-1, (1.0, pi))\n    assert check(1j, (1.0, pi / 2))\n    assert check(-3j, (3.0, -pi / 2))\n    inf = float(\"inf\")\n    assert check(complex(inf, 0), (inf, 0.0))\n    assert check(complex(-inf, 0), (inf, pi))\n    assert check(complex(3, inf), (inf, pi / 2))\n    assert check(complex(5, -inf), (inf, -pi / 2))\n    assert check(complex(inf, inf), (inf, pi / 4))\n    assert check(complex(inf, -inf), (inf, -pi / 4))\n    assert check(complex(-inf, inf), (inf, 3 * pi / 4))\n    assert check(complex(-inf, -inf), (inf, -3 * pi / 4))\n    nan = float(\"nan\")\n    assert check(complex(nan, 0), (nan, nan))\n    assert check(complex(0, nan), (nan, nan))\n    assert check(complex(nan, nan), (nan, nan))\n    assert check(complex(inf, nan), (inf, nan))\n    assert check(complex(-inf, nan), (inf, nan))\n    assert check(complex(nan, inf), (inf, nan))\n    assert check(complex(nan, -inf), (inf, nan))\n\n\ntest_polar()\n\n\n@test\ndef test_phase():\n    from cmath import phase, pi\n\n    assert almost_equal(phase(0), 0.0)\n    assert almost_equal(phase(1.0), 0.0)\n    assert almost_equal(phase(-1.0), pi)\n    assert almost_equal(phase(-1.0 + 1e-300j), pi)\n    assert almost_equal(phase(-1.0 - 1e-300j), -pi)\n    assert almost_equal(phase(1j), pi / 2)\n    assert almost_equal(phase(-1j), -pi / 2)\n\n    # zeros\n    assert phase(complex(0.0, 0.0)) == 0.0\n    assert phase(complex(0.0, -0.0)) == -0.0\n    assert phase(complex(-0.0, 0.0)) == pi\n    assert phase(complex(-0.0, -0.0)) == -pi\n\n    # infinities\n    assert almost_equal(phase(complex(-INF, -0.0)), -pi)\n    assert almost_equal(phase(complex(-INF, -2.3)), -pi)\n    assert almost_equal(phase(complex(-INF, -INF)), -0.75 * pi)\n    assert almost_equal(phase(complex(-2.3, -INF)), -pi / 2)\n    assert almost_equal(phase(complex(-0.0, -INF)), -pi / 2)\n    assert almost_equal(phase(complex(0.0, -INF)), -pi / 2)\n    assert almost_equal(phase(complex(2.3, -INF)), -pi / 2)\n    assert almost_equal(phase(complex(INF, -INF)), -pi / 4)\n    assert phase(complex(INF, -2.3)) == -0.0\n    assert phase(complex(INF, -0.0)) == -0.0\n    assert phase(complex(INF, 0.0)) == 0.0\n    assert phase(complex(INF, 2.3)) == 0.0\n    assert almost_equal(phase(complex(INF, INF)), pi / 4)\n    assert almost_equal(phase(complex(2.3, INF)), pi / 2)\n    assert almost_equal(phase(complex(0.0, INF)), pi / 2)\n    assert almost_equal(phase(complex(-0.0, INF)), pi / 2)\n    assert almost_equal(phase(complex(-2.3, INF)), pi / 2)\n    assert almost_equal(phase(complex(-INF, INF)), 0.75 * pi)\n    assert almost_equal(phase(complex(-INF, 2.3)), pi)\n    assert almost_equal(phase(complex(-INF, 0.0)), pi)\n\n    # real or imaginary part NaN\n    for z in complex_nans:\n        assert math.isnan(phase(z))\n\n\ntest_phase()\n\n\n@test\ndef test_abs():\n    # zeros\n    for z in complex_zeros:\n        assert abs(z) == 0.0\n\n    # infinities\n    for z in complex_infinities:\n        assert abs(z) == INF\n\n    # real or imaginary part NaN\n    assert abs(complex(NAN, -INF)) == INF\n    assert math.isnan(abs(complex(NAN, -2.3)))\n    assert math.isnan(abs(complex(NAN, -0.0)))\n    assert math.isnan(abs(complex(NAN, 0.0)))\n    assert math.isnan(abs(complex(NAN, 2.3)))\n    assert abs(complex(NAN, INF)) == INF\n    assert abs(complex(-INF, NAN)) == INF\n    assert math.isnan(abs(complex(-2.3, NAN)))\n    assert math.isnan(abs(complex(-0.0, NAN)))\n    assert math.isnan(abs(complex(0.0, NAN)))\n    assert math.isnan(abs(complex(2.3, NAN)))\n    assert abs(complex(INF, NAN)) == INF\n    assert math.isnan(abs(complex(NAN, NAN)))\n\n\ntest_abs()\n\n\ndef c_equal(a, b):\n    eps = 1e-7\n    if abs(a.real - b[0]) > eps or abs(a.imag - b[1]) > eps:\n        return False\n    return True\n\n\n@test\ndef test_rect():\n    from cmath import rect, pi\n\n    assert c_equal(rect(0, 0), (0, 0))\n    assert c_equal(rect(1, 0), (1.0, 0))\n    assert c_equal(rect(1, -pi), (-1.0, 0))\n    assert c_equal(rect(1, pi / 2), (0, 1.0))\n    assert c_equal(rect(1, -pi / 2), (0, -1.0))\n\n\ntest_rect()\n\n\n@test\ndef test_isfinite():\n    real_vals = [float(\"-inf\"), -2.3, -0.0, 0.0, 2.3, float(\"inf\"), float(\"nan\")]\n    for x in real_vals:\n        for y in real_vals:\n            z = complex(x, y)\n            assert cmath.isfinite(z) == (math.isfinite(x) and math.isfinite(y))\n\n\ntest_isfinite()\n\n\n@test\ndef test_isnan():\n    assert not cmath.isnan(1)\n    assert not cmath.isnan(1j)\n    assert not cmath.isnan(INF)\n    assert cmath.isnan(NAN)\n    assert cmath.isnan(complex(NAN, 0))\n    assert cmath.isnan(complex(0, NAN))\n    assert cmath.isnan(complex(NAN, NAN))\n    assert cmath.isnan(complex(NAN, INF))\n    assert cmath.isnan(complex(INF, NAN))\n\n\ntest_isnan()\n\n\n@test\ndef test_isinf():\n    assert not cmath.isinf(1)\n    assert not cmath.isinf(1j)\n    assert not cmath.isinf(NAN)\n    assert cmath.isinf(INF)\n    assert cmath.isinf(complex(INF, 0))\n    assert cmath.isinf(complex(0, INF))\n    assert cmath.isinf(complex(INF, INF))\n    assert cmath.isinf(complex(NAN, INF))\n    assert cmath.isinf(complex(INF, NAN))\n\n\ntest_isinf()\n\n\n@test\ndef test_tanh_sign():\n    for z in complex_zeros:\n        assert complex_identical(cmath.tanh(z), z)\n\n\ntest_tanh_sign()\n\n\n@test\ndef test_atan_sign():\n    for z in complex_zeros:\n        assert complex_identical(cmath.atan(z), z)\n\n\ntest_atan_sign()\n\n\n@test\ndef test_atanh_sign():\n    for z in complex_zeros:\n        assert complex_identical(cmath.atanh(z), z)\n\n\ntest_atanh_sign()\n\n\n@test\ndef test_is_close():\n    # test complex values that are close to within 12 decimal places\n    complex_examples = [\n        (1.0 + 1.0j, 1.000000000001 + 1.0j),\n        (1.0 + 1.0j, 1.0 + 1.000000000001j),\n        (-1.0 + 1.0j, -1.000000000001 + 1.0j),\n        (1.0 - 1.0j, 1.0 - 0.999999999999j),\n    ]\n\n    for a, b in complex_examples:\n        assert cmath.isclose(a, b, rel_tol=1e-12)\n        assert not cmath.isclose(a, b, rel_tol=1e-13)\n\n    # test values near zero that are near to within three decimal places\n    near_zero_examples = [\n        (0.001j, 0),\n        (0.001 + 0j, 0),\n        (0.001 + 0.001j, 0),\n        (-0.001 + 0.001j, 0),\n        (0.001 - 0.001j, 0),\n        (-0.001 - 0.001j, 0),\n    ]\n\n    for a, b in near_zero_examples:\n        assert cmath.isclose(a, b, abs_tol=1.5e-03)\n        assert not cmath.isclose(a, b, abs_tol=0.5e-03)\n\n    assert cmath.isclose(0.001 - 0.001j, 0.001 + 0.001j, abs_tol=2e-03)\n    assert not cmath.isclose(0.001 - 0.001j, 0.001 + 0.001j, abs_tol=1e-03)\n\n\ntest_is_close()\n\n\n@test\ndef test_cmath_testcases():\n    def check(exp, got, flags):\n        def close(a, b):\n            if math.isnan(a):\n                return math.isnan(b)\n            elif math.isnan(b):\n                return math.isnan(a)\n            return math.isclose(a, b, rel_tol=1e-10, abs_tol=1e-15)\n\n        x1 = exp.real\n        y1 = exp.imag\n\n        x2 = got.real\n        y2 = got.imag\n\n        if \"ignore-real-sign\" in flags:\n            x1 = math.fabs(x1)\n            x2 = math.fabs(x2)\n\n        if \"ignore-imag-sign\" in flags:\n            y1 = math.fabs(y1)\n            y2 = math.fabs(y2)\n\n        return close(x1, x2) and close(y1, y2)\n\n    def run_test(test):\n        v = test.split()\n        if not v:\n            return True\n        name = v[0]\n        func = v[1]\n        inp = complex(float(v[2]), float(v[3]))\n        exp = complex(float(v[5]), float(v[6]))\n        flags = v[7:]\n\n        got = complex()\n        if func == \"rect\":\n            got = cmath.rect(inp.real, inp.imag)\n        elif func == \"polar\":\n            got = complex(*cmath.polar(inp))\n        elif func == \"exp\":\n            got = cmath.exp(inp)\n        elif func == \"log\":\n            got = cmath.log(inp)\n        elif func == \"log10\":\n            got = cmath.log10(inp)\n        elif func == \"sqrt\":\n            got = cmath.sqrt(inp)\n        elif func == \"acos\":\n            got = cmath.acos(inp)\n        elif func == \"asin\":\n            got = cmath.asin(inp)\n        elif func == \"atan\":\n            got = cmath.atan(inp)\n        elif func == \"cos\":\n            got = cmath.cos(inp)\n        elif func == \"sin\":\n            got = cmath.sin(inp)\n        elif func == \"tan\":\n            got = cmath.tan(inp)\n        elif func == \"acosh\":\n            got = cmath.acosh(inp)\n        elif func == \"asinh\":\n            got = cmath.asinh(inp)\n        elif func == \"atanh\":\n            got = cmath.atanh(inp)\n        elif func == \"cosh\":\n            got = cmath.cosh(inp)\n        elif func == \"sinh\":\n            got = cmath.sinh(inp)\n        elif func == \"tanh\":\n            got = cmath.tanh(inp)\n        else:\n            assert False, f\"ERROR: unknown function: {func}\"\n\n        if not check(exp, got, flags):\n            print(f\"{name} {func} {inp=} {got=} {exp=} {flags=}\")\n            return False\n        return True\n\n    tests = []\n    with open(\"test/stdlib/cmath_testcases.txt\") as f:\n        for line in f:\n            line = line.strip()\n            if not line.startswith(\"--\"):\n                tests.append(line)\n\n    for test in tests:\n        assert run_test(test)\n\n\ntest_cmath_testcases()\n\n\n@test\ndef test_complex_bool():\n    z = complex(0, 0)\n    assert not bool(z)\n    z = complex(1, 0)\n    assert bool(z)\n    z = complex(0, -1)\n    assert bool(z)\n    z = complex(1, -1)\n    assert bool(z)\n\n\ntest_complex_bool()\n\n\n@test\ndef test_complex64():\n    c64 = complex64\n    z = c64(.5 + .5j)\n    assert c64() == z * float32(0.)\n    assert z + float32(1) == c64(1.5, .5)\n    assert bool(z) == True\n    assert bool(0 * z) == False\n    assert +z == z\n    assert -z == c64(-.5 - .5j)\n    assert abs(z) == float32(0.7071067811865476)\n\n    assert z + c64(1) == c64(1.5 + .5j)\n    assert c64(1j) + z == c64(.5 + 1.5j)\n    assert z * c64(2) == c64(1 + 1j)\n    assert c64(2j) * z == c64(-1 + 1j)\n    assert z / c64(.5) == c64(1 + 1j)\n    assert c64(1j) / z == c64(1 + 1j)\n    assert z ** c64(2) == c64(.5j)\n\n    assert z + 1 == 1.5 + .5j\n    assert 1j + z == .5 + 1.5j\n    assert z * 2 == 1 + 1j\n    assert 2j * z == -1 + 1j\n    assert z / .5 == 1 + 1j\n    assert 1j / z == 1 + 1j\n    assert z ** 2 == .5j\n\n    y = c64(1j) ** z\n    assert math.isclose(float(y.real), 0.32239694194483454, rel_tol=1e-7)\n    assert math.isclose(float(y.imag), 0.32239694194483454, rel_tol=1e-7)\n    assert z != -z\n    assert z != 0\n    assert z.real == float32(.5)\n    assert (z + c64(1j)).imag == float32(1.5)\n    assert z.conjugate() == c64(.5 - .5j)\n    assert z.__copy__() == z\n    assert hash(z)\n    assert c64(complex(z)) == z\n\n    z = c64(1.5, 2.5)\n    assert z.real == f32(1.5)\n    assert z.imag == f32(2.5)\n    z = c64(3.5)\n    assert z.real == f32(3.5)\n    assert z.imag == f32(0.0)\n    z = c64(f32(4.5), f32(5.5))\n    assert z.real == f32(4.5)\n    assert z.imag == f32(5.5)\n    z = c64(f32(6.5))\n    assert z.real == f32(6.5)\n    assert z.imag == f32(0.0)\n\n    z = c64(0, 0)\n    assert not bool(z)\n    z = c64(1, 0)\n    assert bool(z)\n    z = c64(0, -1)\n    assert bool(z)\n    z = c64(1, -1)\n    assert bool(z)\n\n\ntest_complex64()\n\n\ndef test_complex_from_string():\n    # for tests when string is not zero-terminated\n    def f(s):\n        return complex(s[1:-1])\n\n    def g(s):\n        return complex(' ' * 50 + s[1:-1] + ' ' * 50)\n\n    assert complex(\"1\") == 1+0j\n    assert complex(\"1j\") == 1j\n    assert complex(\"-1\") == -1\n    assert complex(\"+1\") == +1\n    assert complex(\"(1+2j)\") == 1+2j\n    assert complex(\"(1.3+2.2j)\") == 1.3+2.2j\n    assert complex(\"3.14+1J\") == 3.14+1j\n    assert complex(\" ( +3.14-6J )\") == 3.14-6j\n    assert complex(\" ( +3.14-J )\") == 3.14-1j\n    assert complex(\" ( +3.14+j )\") == 3.14+1j\n    assert complex(\"J\") == 1j\n    assert complex(\"( j )\") == 1j\n    assert complex(\"+J\") == 1j\n    assert complex(\"( -j)\") == -1j\n    assert complex('1e-500') == 0.0 + 0.0j\n    assert complex('-1e-500j') == 0.0 - 0.0j\n    assert complex('-1e-500+1e-500j') == -0.0 + 0.0j\n    assert complex('1-1j') == 1.0 - 1j\n    assert complex('1J') == 1j\n\n    assert f(\"x1x\") == 1+0j\n    assert f(\"x1jx\") == 1j\n    assert f(\"x-1x\") == -1\n    assert f(\"x+1x\") == +1\n    assert f(\"x(1+2j)x\") == 1+2j\n    assert f(\"x(1.3+2.2j)x\") == 1.3+2.2j\n    assert f(\"x3.14+1Jx\") == 3.14+1j\n    assert f(\"x ( +3.14-6J )x\") == 3.14-6j\n    assert f(\"x ( +3.14-J )x\") == 3.14-1j\n    assert f(\"x ( +3.14+j )x\") == 3.14+1j\n    assert f(\"xJx\") == 1j\n    assert f(\"x( j )x\") == 1j\n    assert f(\"x+Jx\") == 1j\n    assert f(\"x( -j)x\") == -1j\n    assert f('x1e-500x') == 0.0 + 0.0j\n    assert f('x-1e-500jx') == 0.0 - 0.0j\n    assert f('x-1e-500+1e-500jx') == -0.0 + 0.0j\n    assert f('x1-1jx') == 1.0 - 1j\n    assert f('x1Jx') == 1j\n\n    assert g(\"x1x\") == 1+0j\n    assert g(\"x1jx\") == 1j\n    assert g(\"x-1x\") == -1\n    assert g(\"x+1x\") == +1\n    assert g(\"x(1+2j)x\") == 1+2j\n    assert g(\"x(1.3+2.2j)x\") == 1.3+2.2j\n    assert g(\"x3.14+1Jx\") == 3.14+1j\n    assert g(\"x ( +3.14-6J )x\") == 3.14-6j\n    assert g(\"x ( +3.14-J )x\") == 3.14-1j\n    assert g(\"x ( +3.14+j )x\") == 3.14+1j\n    assert g(\"xJx\") == 1j\n    assert g(\"x( j )x\") == 1j\n    assert g(\"x+Jx\") == 1j\n    assert g(\"x( -j)x\") == -1j\n    assert g('x1e-500x') == 0.0 + 0.0j\n    assert g('x-1e-500jx') == 0.0 - 0.0j\n    assert g('x-1e-500+1e-500jx') == -0.0 + 0.0j\n    assert g('x1-1jx') == 1.0 - 1j\n    assert g('x1Jx') == 1j\n\n    try:\n        complex(\"\\0\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        complex(\"3\\09\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        complex(\"1+\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        complex(\"1+1j+1j\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        complex(\"--\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        complex(\"(1+2j\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        complex(\"1+2j)\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        complex(\"1+(2j)\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        complex(\"(1+2j)123\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        complex(\"x\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        complex(\"1j+2\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        complex(\"1e1ej\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        complex(\"1e++1ej\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        complex(\")1+2j(\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        complex(\"\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        f(\" 1+2j\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        f(\"1..1j\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        f(\"1.11.1j\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        f(\"1e1.1j\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        f(\"   \")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        f(\"   J\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        g(\" 1+2j\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        g(\"1..1j\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        g(\"1.11.1j\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        g(\"1e1.1j\")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        g(\"   \")\n        assert False\n    except ValueError:\n        pass\n\n    try:\n        g(\"   J\")\n        assert False\n    except ValueError:\n        pass\n\ntest_complex_from_string()\n"
  },
  {
    "path": "test/stdlib/cmath_testcases.txt",
    "content": "-- Testcases for functions in cmath.\n--\n-- Each line takes the form:\n--\n-- <testid> <function> <input_value> -> <output_value> <flags>\n--\n-- where:\n--\n--   <testid> is a short name identifying the test,\n--\n--   <function> is the function to be tested (exp, cos, asinh, ...),\n--\n--   <input_value> is a pair of floats separated by whitespace\n--     representing real and imaginary parts of a complex number, and\n--\n--   <output_value> is the expected (ideal) output value, again\n--     represented as a pair of floats.\n--\n--   <flags> is a list of the floating-point flags required by C99\n--\n-- The possible flags are:\n--\n--   divide-by-zero : raised when a finite input gives a\n--     mathematically infinite result.\n--\n--   overflow : raised when a finite input gives a finite result whose\n--     real or imaginary part is too large to fit in the usual range\n--     of an IEEE 754 double.\n--\n--   invalid : raised for invalid inputs.\n--\n--   ignore-real-sign : indicates that the sign of the real part of\n--     the result is unspecified; if the real part of the result is\n--     given as inf, then both -inf and inf should be accepted as\n--     correct.\n--\n--   ignore-imag-sign : indicates that the sign of the imaginary part\n--     of the result is unspecified.\n--\n-- Flags may appear in any order.\n--\n-- Lines beginning with '--' (like this one) start a comment, and are\n-- ignored.  Blank lines, or lines containing only whitespace, are also\n-- ignored.\n\n-- The majority of the values below were computed with the help of\n-- version 2.3 of the MPFR library for multiple-precision\n-- floating-point computations with correct rounding.  All output\n-- values in this file are (modulo yet-to-be-discovered bugs)\n-- correctly rounded, provided that each input and output decimal\n-- floating-point value below is interpreted as a representation of\n-- the corresponding nearest IEEE 754 double-precision value.  See the\n-- MPFR homepage at http://www.mpfr.org for more information about the\n-- MPFR project.\n\n-- A minority of the test cases were generated with the help of\n-- mpmath 0.19 at 100 bit accuracy (http://mpmath.org) to improve\n-- coverage of real functions with real-valued arguments. These are\n-- used in test.test_math.MathTests.test_testfile, as well as in\n-- test_cmath.\n\n\n--------------------------\n-- acos: Inverse cosine --\n--------------------------\n\n-- zeros\nacos0000 acos 0.0 0.0 -> 1.5707963267948966 -0.0\nacos0001 acos 0.0 -0.0 -> 1.5707963267948966 0.0\nacos0002 acos -0.0 0.0 -> 1.5707963267948966 -0.0\nacos0003 acos -0.0 -0.0 -> 1.5707963267948966 0.0\n\n-- branch points: +/-1\nacos0010 acos 1.0 0.0 -> 0.0 -0.0\nacos0011 acos 1.0 -0.0 -> 0.0 0.0\nacos0012 acos -1.0 0.0 -> 3.1415926535897931 -0.0\nacos0013 acos -1.0 -0.0 -> 3.1415926535897931 0.0\n\n-- values along both sides of real axis\nacos0020 acos -9.8813129168249309e-324 0.0 -> 1.5707963267948966 -0.0\nacos0021 acos -9.8813129168249309e-324 -0.0 -> 1.5707963267948966 0.0\nacos0022 acos -1e-305 0.0 -> 1.5707963267948966 -0.0\nacos0023 acos -1e-305 -0.0 -> 1.5707963267948966 0.0\nacos0024 acos -1e-150 0.0 -> 1.5707963267948966 -0.0\nacos0025 acos -1e-150 -0.0 -> 1.5707963267948966 0.0\nacos0026 acos -9.9999999999999998e-17 0.0 -> 1.5707963267948968 -0.0\nacos0027 acos -9.9999999999999998e-17 -0.0 -> 1.5707963267948968 0.0\nacos0028 acos -0.001 0.0 -> 1.5717963269615634 -0.0\nacos0029 acos -0.001 -0.0 -> 1.5717963269615634 0.0\nacos0030 acos -0.57899999999999996 0.0 -> 2.1882979816120667 -0.0\nacos0031 acos -0.57899999999999996 -0.0 -> 2.1882979816120667 0.0\nacos0032 acos -0.99999999999999989 0.0 -> 3.1415926386886319 -0.0\nacos0033 acos -0.99999999999999989 -0.0 -> 3.1415926386886319 0.0\nacos0034 acos -1.0000000000000002 0.0 -> 3.1415926535897931 -2.1073424255447014e-08\nacos0035 acos -1.0000000000000002 -0.0 -> 3.1415926535897931 2.1073424255447014e-08\nacos0036 acos -1.0009999999999999 0.0 -> 3.1415926535897931 -0.044717633608306849\nacos0037 acos -1.0009999999999999 -0.0 -> 3.1415926535897931 0.044717633608306849\nacos0038 acos -2.0 0.0 -> 3.1415926535897931 -1.3169578969248168\nacos0039 acos -2.0 -0.0 -> 3.1415926535897931 1.3169578969248168\nacos0040 acos -23.0 0.0 -> 3.1415926535897931 -3.8281684713331012\nacos0041 acos -23.0 -0.0 -> 3.1415926535897931 3.8281684713331012\nacos0042 acos -10000000000000000.0 0.0 -> 3.1415926535897931 -37.534508668464674\nacos0043 acos -10000000000000000.0 -0.0 -> 3.1415926535897931 37.534508668464674\nacos0044 acos -9.9999999999999998e+149 0.0 -> 3.1415926535897931 -346.08091112966679\nacos0045 acos -9.9999999999999998e+149 -0.0 -> 3.1415926535897931 346.08091112966679\nacos0046 acos -1.0000000000000001e+299 0.0 -> 3.1415926535897931 -689.16608998577965\nacos0047 acos -1.0000000000000001e+299 -0.0 -> 3.1415926535897931 689.16608998577965\nacos0048 acos 9.8813129168249309e-324 0.0 -> 1.5707963267948966 -0.0\nacos0049 acos 9.8813129168249309e-324 -0.0 -> 1.5707963267948966 0.0\nacos0050 acos 1e-305 0.0 -> 1.5707963267948966 -0.0\nacos0051 acos 1e-305 -0.0 -> 1.5707963267948966 0.0\nacos0052 acos 1e-150 0.0 -> 1.5707963267948966 -0.0\nacos0053 acos 1e-150 -0.0 -> 1.5707963267948966 0.0\nacos0054 acos 9.9999999999999998e-17 0.0 -> 1.5707963267948966 -0.0\nacos0055 acos 9.9999999999999998e-17 -0.0 -> 1.5707963267948966 0.0\nacos0056 acos 0.001 0.0 -> 1.56979632662823 -0.0\nacos0057 acos 0.001 -0.0 -> 1.56979632662823 0.0\nacos0058 acos 0.57899999999999996 0.0 -> 0.95329467197772655 -0.0\nacos0059 acos 0.57899999999999996 -0.0 -> 0.95329467197772655 0.0\nacos0060 acos 0.99999999999999989 0.0 -> 1.4901161193847656e-08 -0.0\nacos0061 acos 0.99999999999999989 -0.0 -> 1.4901161193847656e-08 0.0\nacos0062 acos 1.0000000000000002 0.0 -> 0.0 -2.1073424255447014e-08\nacos0063 acos 1.0000000000000002 -0.0 -> 0.0 2.1073424255447014e-08\nacos0064 acos 1.0009999999999999 0.0 -> 0.0 -0.044717633608306849\nacos0065 acos 1.0009999999999999 -0.0 -> 0.0 0.044717633608306849\nacos0066 acos 2.0 0.0 -> 0.0 -1.3169578969248168\nacos0067 acos 2.0 -0.0 -> 0.0 1.3169578969248168\nacos0068 acos 23.0 0.0 -> 0.0 -3.8281684713331012\nacos0069 acos 23.0 -0.0 -> 0.0 3.8281684713331012\nacos0070 acos 10000000000000000.0 0.0 -> 0.0 -37.534508668464674\nacos0071 acos 10000000000000000.0 -0.0 -> 0.0 37.534508668464674\nacos0072 acos 9.9999999999999998e+149 0.0 -> 0.0 -346.08091112966679\nacos0073 acos 9.9999999999999998e+149 -0.0 -> 0.0 346.08091112966679\nacos0074 acos 1.0000000000000001e+299 0.0 -> 0.0 -689.16608998577965\nacos0075 acos 1.0000000000000001e+299 -0.0 -> 0.0 689.16608998577965\n\n-- random inputs\nacos0100 acos -3.3307113324596682 -10.732007530863266 -> 1.8706085694482339 3.113986806554613\nacos0101 acos -2863.952991743291 -2681013315.2571239 -> 1.5707973950301699 22.402607843274758\nacos0102 acos -0.33072639793220088 -0.85055464658253055 -> 1.8219426895922601 0.79250166729311966\nacos0103 acos -2.5722325842097802 -12.703940809821574 -> 1.7699942413107408 3.2565170156527325\nacos0104 acos -42.495233785459583 -0.54039320751337161 -> 3.1288732573153304 4.4424815519735601\nacos0105 acos -1.1363818625856401 9641.1325498630376 -> 1.5709141948820049 -9.8669410553254284\nacos0106 acos -2.4398426824157866e-11 0.33002051890266165 -> 1.570796326818066 -0.32430578041578667\nacos0107 acos -1.3521340428186552 2.9369737912076772 -> 1.9849059192339338 -1.8822893674117942\nacos0108 acos -1.827364706477915 1.0355459232147557 -> 2.5732246307960032 -1.4090688267854969\nacos0109 acos -0.25978373706403546 10.09712669185833 -> 1.5963940386378306 -3.0081673050196063\nacos0110 acos 0.33561778471072551 -4587350.6823999118 -> 1.5707962536333251 16.031960402579539\nacos0111 acos 0.49133444610998445 -0.8071422362990015 -> 1.1908761712801788 0.78573345813187867\nacos0112 acos 0.42196734507823974 -2.4812965431745115 -> 1.414091186100692 1.651707260988172\nacos0113 acos 2.961426210100655 -219.03295695248664 -> 1.5572768319822778 6.0824659885827304\nacos0114 acos 2.886209063652641 -20.38011207220606 -> 1.4302765252297889 3.718201853147642\nacos0115 acos 0.4180568075276509 1.4833433990823484 -> 1.3393834558303042 -1.2079847758301576\nacos0116 acos 52.376111405924718 0.013930429001941001 -> 0.00026601761804024188 -4.6515066691204714\nacos0117 acos 41637948387.625969 1.563418292894041 -> 3.7547918507883548e-11 -25.145424989809381\nacos0118 acos 0.061226659122249526 0.8447234394615154 -> 1.5240280306367315 -0.76791798971140812\nacos0119 acos 2.4480466420442959e+26 0.18002339201384662 -> 7.353756620564798e-28 -61.455650015996376\n\n-- values near infinity\nacos0200 acos 1.6206860518683021e+308 1.0308426226285283e+308 -> 0.56650826093826223 -710.54206874241561\nacos0201 acos 1.2067735875070062e+308 -1.3429173724390276e+308 -> 0.83874369390864889 710.48017794027498\nacos0202 acos -7.4130145132549047e+307 1.1759130543927645e+308 -> 2.1332729346478536 -710.21871115698752\nacos0203 acos -8.6329426442257249e+307 -1.2316282952184133e+308 -> 2.1821511032444838 710.29752145697148\nacos0204 acos 0.0 1.4289713855849746e+308 -> 1.5707963267948966 -710.24631069738996\nacos0205 acos -0.0 1.3153524545987432e+308 -> 1.5707963267948966 -710.1634604787539\nacos0206 acos 0.0 -9.6229037669269321e+307 -> 1.5707963267948966 709.85091679573691\nacos0207 acos -0.0 -4.9783616421107088e+307 -> 1.5707963267948966 709.19187157911233\nacos0208 acos 1.3937541925739389e+308 0.0 -> 0.0 -710.22135678707264\nacos0209 acos 9.1362388967371536e+307 -0.0 -> 0.0 709.79901953124613\nacos0210 acos -1.3457361220697436e+308 0.0 -> 3.1415926535897931 -710.18629698871848\nacos0211 acos -5.4699090056144284e+307 -0.0 -> 3.1415926535897931 709.28603271085649\nacos0212 acos 1.5880716932358901e+308 5.5638401252339929 -> 3.503519487773873e-308 -710.35187633140583\nacos0213 acos 1.2497211663463164e+308 -3.0456477717911024 -> 2.4370618453197486e-308 710.11227628223412\nacos0214 acos -9.9016224006029528e+307 4.9570427340789056 -> 3.1415926535897931 -709.87946935229468\nacos0215 acos -1.5854071066874139e+308 -4.4233577741497783 -> 3.1415926535897931 710.35019704672004\nacos0216 acos 9.3674623083647628 1.5209559051877979e+308 -> 1.5707963267948966 -710.30869484491086\nacos0217 acos 8.1773832021784383 -6.6093445795000056e+307 -> 1.5707963267948966 709.4752552227792\nacos0218 acos -3.1845935000665104 1.5768856396650893e+308 -> 1.5707963267948966 -710.34480761042687\nacos0219 acos -1.0577303880953903 -6.4574626815735613e+307 -> 1.5707963267948966 709.45200719662046\n\n-- values near 0\nacos0220 acos 1.8566986970714045e-320 3.1867234156760402e-321 -> 1.5707963267948966 -3.1867234156760402e-321\nacos0221 acos 7.9050503334599447e-323 -8.8931816251424378e-323 -> 1.5707963267948966 8.8931816251424378e-323\nacos0222 acos -4.4465908125712189e-323 2.4654065097222727e-311 -> 1.5707963267948966 -2.4654065097222727e-311\nacos0223 acos -6.1016916408192619e-311 -2.4703282292062327e-323 -> 1.5707963267948966 2.4703282292062327e-323\nacos0224 acos 0.0 3.4305783621842729e-311 -> 1.5707963267948966 -3.4305783621842729e-311\nacos0225 acos -0.0 1.6117409498633145e-319 -> 1.5707963267948966 -1.6117409498633145e-319\nacos0226 acos 0.0 -4.9900630229965901e-322 -> 1.5707963267948966 4.9900630229965901e-322\nacos0227 acos -0.0 -4.4889279210592818e-311 -> 1.5707963267948966 4.4889279210592818e-311\nacos0228 acos 5.3297678681477214e-312 0.0 -> 1.5707963267948966 -0.0\nacos0229 acos 6.2073425897211614e-313 -0.0 -> 1.5707963267948966 0.0\nacos0230 acos -4.9406564584124654e-324 0.0 -> 1.5707963267948966 -0.0\nacos0231 acos -1.7107517052899003e-318 -0.0 -> 1.5707963267948966 0.0\n\n-- special values\nacos1000 acos 0.0 0.0 -> 1.5707963267948966 -0.0\nacos1001 acos 0.0 -0.0 -> 1.5707963267948966 0.0\nacos1002 acos -0.0 0.0 -> 1.5707963267948966 -0.0\nacos1003 acos -0.0 -0.0 -> 1.5707963267948966 0.0\nacos1004 acos 0.0 nan -> 1.5707963267948966 nan\nacos1005 acos -0.0 nan -> 1.5707963267948966 nan\nacos1006 acos -2.3 inf -> 1.5707963267948966 -inf\nacos1007 acos -0.0 inf -> 1.5707963267948966 -inf\nacos1008 acos 0.0 inf -> 1.5707963267948966 -inf\nacos1009 acos 2.3 inf -> 1.5707963267948966 -inf\nacos1010 acos -2.3 nan -> nan nan\nacos1011 acos 2.3 nan -> nan nan\nacos1012 acos -inf 2.3 -> 3.1415926535897931 -inf\nacos1013 acos -inf 0.0 -> 3.1415926535897931 -inf\nacos1014 acos inf 2.3 -> 0.0 -inf\nacos1015 acos inf 0.0 -> 0.0 -inf\nacos1016 acos -inf inf -> 2.3561944901923448 -inf\nacos1017 acos inf inf -> 0.78539816339744828 -inf\nacos1018 acos inf nan -> nan inf                        ignore-imag-sign\nacos1019 acos -inf nan -> nan inf                       ignore-imag-sign\nacos1020 acos nan 0.0 -> nan nan\nacos1021 acos nan 2.3 -> nan nan\nacos1022 acos nan inf -> nan -inf\nacos1023 acos nan nan -> nan nan\nacos1024 acos -2.3 -inf -> 1.5707963267948966 inf\nacos1025 acos -0.0 -inf -> 1.5707963267948966 inf\nacos1026 acos 0.0 -inf -> 1.5707963267948966 inf\nacos1027 acos 2.3 -inf -> 1.5707963267948966 inf\nacos1028 acos -inf -2.3 -> 3.1415926535897931 inf\nacos1029 acos -inf -0.0 -> 3.1415926535897931 inf\nacos1030 acos inf -2.3 -> 0.0 inf\nacos1031 acos inf -0.0 -> 0.0 inf\nacos1032 acos -inf -inf -> 2.3561944901923448 inf\nacos1033 acos inf -inf -> 0.78539816339744828 inf\nacos1034 acos nan -0.0 -> nan nan\nacos1035 acos nan -2.3 -> nan nan\nacos1036 acos nan -inf -> nan inf\n\n\n--------------------------------------\n-- acosh: Inverse hyperbolic cosine --\n--------------------------------------\n\n-- zeros\nacosh0000 acosh 0.0 0.0 -> 0.0 1.5707963267948966\nacosh0001 acosh 0.0 -0.0 -> 0.0 -1.5707963267948966\nacosh0002 acosh -0.0 0.0 -> 0.0 1.5707963267948966\nacosh0003 acosh -0.0 -0.0 -> 0.0 -1.5707963267948966\n\n-- branch points: +/-1\nacosh0010 acosh 1.0 0.0 -> 0.0 0.0\nacosh0011 acosh 1.0 -0.0 -> 0.0 -0.0\nacosh0012 acosh -1.0 0.0 -> 0.0 3.1415926535897931\nacosh0013 acosh -1.0 -0.0 -> 0.0 -3.1415926535897931\n\n-- values along both sides of real axis\nacosh0020 acosh -9.8813129168249309e-324 0.0 -> 0.0 1.5707963267948966\nacosh0021 acosh -9.8813129168249309e-324 -0.0 -> 0.0 -1.5707963267948966\nacosh0022 acosh -1e-305 0.0 -> 0.0 1.5707963267948966\nacosh0023 acosh -1e-305 -0.0 -> 0.0 -1.5707963267948966\nacosh0024 acosh -1e-150 0.0 -> 0.0 1.5707963267948966\nacosh0025 acosh -1e-150 -0.0 -> 0.0 -1.5707963267948966\nacosh0026 acosh -9.9999999999999998e-17 0.0 -> 0.0 1.5707963267948968\nacosh0027 acosh -9.9999999999999998e-17 -0.0 -> 0.0 -1.5707963267948968\nacosh0028 acosh -0.001 0.0 -> 0.0 1.5717963269615634\nacosh0029 acosh -0.001 -0.0 -> 0.0 -1.5717963269615634\nacosh0030 acosh -0.57899999999999996 0.0 -> 0.0 2.1882979816120667\nacosh0031 acosh -0.57899999999999996 -0.0 -> 0.0 -2.1882979816120667\nacosh0032 acosh -0.99999999999999989 0.0 -> 0.0 3.1415926386886319\nacosh0033 acosh -0.99999999999999989 -0.0 -> 0.0 -3.1415926386886319\nacosh0034 acosh -1.0000000000000002 0.0 -> 2.1073424255447014e-08 3.1415926535897931\nacosh0035 acosh -1.0000000000000002 -0.0 -> 2.1073424255447014e-08 -3.1415926535897931\nacosh0036 acosh -1.0009999999999999 0.0 -> 0.044717633608306849 3.1415926535897931\nacosh0037 acosh -1.0009999999999999 -0.0 -> 0.044717633608306849 -3.1415926535897931\nacosh0038 acosh -2.0 0.0 -> 1.3169578969248168 3.1415926535897931\nacosh0039 acosh -2.0 -0.0 -> 1.3169578969248168 -3.1415926535897931\nacosh0040 acosh -23.0 0.0 -> 3.8281684713331012 3.1415926535897931\nacosh0041 acosh -23.0 -0.0 -> 3.8281684713331012 -3.1415926535897931\nacosh0042 acosh -10000000000000000.0 0.0 -> 37.534508668464674 3.1415926535897931\nacosh0043 acosh -10000000000000000.0 -0.0 -> 37.534508668464674 -3.1415926535897931\nacosh0044 acosh -9.9999999999999998e+149 0.0 -> 346.08091112966679 3.1415926535897931\nacosh0045 acosh -9.9999999999999998e+149 -0.0 -> 346.08091112966679 -3.1415926535897931\nacosh0046 acosh -1.0000000000000001e+299 0.0 -> 689.16608998577965 3.1415926535897931\nacosh0047 acosh -1.0000000000000001e+299 -0.0 -> 689.16608998577965 -3.1415926535897931\nacosh0048 acosh 9.8813129168249309e-324 0.0 -> 0.0 1.5707963267948966\nacosh0049 acosh 9.8813129168249309e-324 -0.0 -> 0.0 -1.5707963267948966\nacosh0050 acosh 1e-305 0.0 -> 0.0 1.5707963267948966\nacosh0051 acosh 1e-305 -0.0 -> 0.0 -1.5707963267948966\nacosh0052 acosh 1e-150 0.0 -> 0.0 1.5707963267948966\nacosh0053 acosh 1e-150 -0.0 -> 0.0 -1.5707963267948966\nacosh0054 acosh 9.9999999999999998e-17 0.0 -> 0.0 1.5707963267948966\nacosh0055 acosh 9.9999999999999998e-17 -0.0 -> 0.0 -1.5707963267948966\nacosh0056 acosh 0.001 0.0 -> 0.0 1.56979632662823\nacosh0057 acosh 0.001 -0.0 -> 0.0 -1.56979632662823\nacosh0058 acosh 0.57899999999999996 0.0 -> 0.0 0.95329467197772655\nacosh0059 acosh 0.57899999999999996 -0.0 -> 0.0 -0.95329467197772655\nacosh0060 acosh 0.99999999999999989 0.0 -> 0.0 1.4901161193847656e-08\nacosh0061 acosh 0.99999999999999989 -0.0 -> 0.0 -1.4901161193847656e-08\nacosh0062 acosh 1.0000000000000002 0.0 -> 2.1073424255447014e-08 0.0\nacosh0063 acosh 1.0000000000000002 -0.0 -> 2.1073424255447014e-08 -0.0\nacosh0064 acosh 1.0009999999999999 0.0 -> 0.044717633608306849 0.0\nacosh0065 acosh 1.0009999999999999 -0.0 -> 0.044717633608306849 -0.0\nacosh0066 acosh 2.0 0.0 -> 1.3169578969248168 0.0\nacosh0067 acosh 2.0 -0.0 -> 1.3169578969248168 -0.0\nacosh0068 acosh 23.0 0.0 -> 3.8281684713331012 0.0\nacosh0069 acosh 23.0 -0.0 -> 3.8281684713331012 -0.0\nacosh0070 acosh 10000000000000000.0 0.0 -> 37.534508668464674 0.0\nacosh0071 acosh 10000000000000000.0 -0.0 -> 37.534508668464674 -0.0\nacosh0072 acosh 9.9999999999999998e+149 0.0 -> 346.08091112966679 0.0\nacosh0073 acosh 9.9999999999999998e+149 -0.0 -> 346.08091112966679 -0.0\nacosh0074 acosh 1.0000000000000001e+299 0.0 -> 689.16608998577965 0.0\nacosh0075 acosh 1.0000000000000001e+299 -0.0 -> 689.16608998577965 -0.0\n\n-- random inputs\nacosh0100 acosh -1.4328589581250843 -1.8370347775558309 -> 1.5526962646549587 -2.190250168435786\nacosh0101 acosh -0.31075819156220957 -1.0772555786839297 -> 0.95139168286193709 -1.7812228089636479\nacosh0102 acosh -1.9044776578070453 -20.485370158932124 -> 3.7177411088932359 -1.6633888745861227\nacosh0103 acosh -0.075642506000858742 -21965976320.873051 -> 24.505907742881991 -1.5707963267983402\nacosh0104 acosh -1.6162271181056307 -3.0369343458696099 -> 1.9407057262861227 -2.0429549461750209\nacosh0105 acosh -0.3103780280298063 0.00018054880018078987 -> 0.00018992877058761416 1.886386995096728\nacosh0106 acosh -9159468751.5897655 5.8014747664273649 -> 23.631201197959193 3.1415926529564078\nacosh0107 acosh -0.037739157550933884 0.21841357493510705 -> 0.21685844960602488 1.6076735133449402\nacosh0108 acosh -8225991.0508394297 0.28318543008913644 -> 16.615956520420287 3.1415926191641019\nacosh0109 acosh -35.620070502302639 0.31303237005015 -> 4.2658980006943965 3.1328013255541873\nacosh0110 acosh 96.729939906820917 -0.029345228372365334 -> 5.2650434775863548 -0.00030338895866972843\nacosh0111 acosh 0.59656024007966491 -2.0412294654163978 -> 1.4923002024287835 -1.312568421900338\nacosh0112 acosh 109.29384112677828 -0.00015454863061533812 -> 5.3871662961545477 -1.4141245154061214e-06\nacosh0113 acosh 8.6705651969361597 -3.6723631649787465 -> 2.9336180958363545 -0.40267362031872861\nacosh0114 acosh 1.8101646445052686 -0.012345132721855478 -> 1.1997148566285769 -0.0081813912760150265\nacosh0115 acosh 52.56897195025288 0.001113916065985443 -> 4.6551827622264135 2.1193445872040307e-05\nacosh0116 acosh 0.28336786164214739 355643992457.40485 -> 27.290343226816528 1.5707963267940999\nacosh0117 acosh 0.73876621291911437 2.8828594541104322e-20 -> 4.2774820978159067e-20 0.73955845836827927\nacosh0118 acosh 0.025865471781718878 37125746064318.492 -> 31.938478989418012 1.5707963267948959\nacosh0119 acosh 2.2047353511780132 0.074712248143489271 -> 1.4286403248698021 0.037997904971626598\n\n-- values near infinity\nacosh0200 acosh 8.1548592876467785e+307 9.0943779335951128e+307 -> 710.08944620800605 0.83981165425478954\nacosh0201 acosh 1.4237229680972531e+308 -1.0336966617874858e+308 -> 710.4543331094759 -0.6279972876348755\nacosh0202 acosh -1.5014526899738939e+308 1.5670700378448792e+308 -> 710.66420706795464 2.3348137299106697\nacosh0203 acosh -1.0939040375213928e+308 -1.0416960351127978e+308 -> 710.30182863115886 -2.380636147787027\nacosh0204 acosh 0.0 1.476062433559588e+308 -> 710.27873384716929 1.5707963267948966\nacosh0205 acosh -0.0 6.2077210326221094e+307 -> 709.41256457484769 1.5707963267948966\nacosh0206 acosh 0.0 -1.5621899909968308e+308 -> 710.33544449990734 -1.5707963267948966\nacosh0207 acosh -0.0 -8.3556624833839122e+307 -> 709.70971018048317 -1.5707963267948966\nacosh0208 acosh 1.3067079752499342e+308 0.0 -> 710.15686680107228 0.0\nacosh0209 acosh 1.5653640340214026e+308 -0.0 -> 710.33747422926706 -0.0\nacosh0210 acosh -6.9011375992290636e+307 0.0 -> 709.51845699719922 3.1415926535897931\nacosh0211 acosh -9.9539576809926973e+307 -0.0 -> 709.88474095870185 -3.1415926535897931\nacosh0212 acosh 7.6449598518914925e+307 9.5706540768268358 -> 709.62081731754802 1.2518906916769345e-307\nacosh0213 acosh 5.4325410972602197e+307 -7.8064807816522706 -> 709.279177727925 -1.4369851312471974e-307\nacosh0214 acosh -1.1523626112360465e+308 7.0617510038869336 -> 710.03117010216909 3.1415926535897931\nacosh0215 acosh -1.1685027786862599e+308 -5.1568558357925625 -> 710.04507907571417 -3.1415926535897931\nacosh0216 acosh 3.0236370339788721 1.7503248720096417e+308 -> 710.44915723458064 1.5707963267948966\nacosh0217 acosh 6.6108007926031149 -9.1469968225806149e+307 -> 709.80019633903328 -1.5707963267948966\nacosh0218 acosh -5.1096262905623959 6.4484926785412395e+307 -> 709.45061713997973 1.5707963267948966\nacosh0219 acosh -2.8080920608735846 -1.7716118836519368e+308 -> 710.46124562363445 -1.5707963267948966\n\n-- values near 0\nacosh0220 acosh 4.5560530326699304e-317 7.3048989121436657e-318 -> 7.3048989121436657e-318 1.5707963267948966\nacosh0221 acosh 4.8754274133585331e-314 -9.8469794897684199e-315 -> 9.8469794897684199e-315 -1.5707963267948966\nacosh0222 acosh -4.6748876009960097e-312 9.7900342887557606e-318 -> 9.7900342887557606e-318 1.5707963267948966\nacosh0223 acosh -4.3136871538399236e-320 -4.9406564584124654e-323 -> 4.9406564584124654e-323 -1.5707963267948966\nacosh0224 acosh 0.0 4.3431013866496774e-314 -> 4.3431013866496774e-314 1.5707963267948966\nacosh0225 acosh -0.0 6.0147334335829184e-317 -> 6.0147334335829184e-317 1.5707963267948966\nacosh0226 acosh 0.0 -1.2880291387081297e-320 -> 1.2880291387081297e-320 -1.5707963267948966\nacosh0227 acosh -0.0 -1.4401563976534621e-317 -> 1.4401563976534621e-317 -1.5707963267948966\nacosh0228 acosh 1.3689680570863091e-313 0.0 -> 0.0 1.5707963267948966\nacosh0229 acosh 1.5304346893494371e-312 -0.0 -> 0.0 -1.5707963267948966\nacosh0230 acosh -3.7450175954766488e-320 0.0 -> 0.0 1.5707963267948966\nacosh0231 acosh -8.4250563080885801e-311 -0.0 -> 0.0 -1.5707963267948966\n\n-- special values\nacosh1000 acosh 0.0 0.0 -> 0.0 1.5707963267948966\nacosh1001 acosh -0.0 0.0 -> 0.0 1.5707963267948966\nacosh1002 acosh 0.0 inf -> inf 1.5707963267948966\nacosh1003 acosh 2.3 inf -> inf 1.5707963267948966\nacosh1004 acosh -0.0 inf -> inf 1.5707963267948966\nacosh1005 acosh -2.3 inf -> inf 1.5707963267948966\nacosh1006 acosh 0.0 nan -> nan nan\nacosh1007 acosh 2.3 nan -> nan nan\nacosh1008 acosh -0.0 nan -> nan nan\nacosh1009 acosh -2.3 nan -> nan nan\nacosh1010 acosh -inf 0.0 -> inf 3.1415926535897931\nacosh1011 acosh -inf 2.3 -> inf 3.1415926535897931\nacosh1012 acosh inf 0.0 -> inf 0.0\nacosh1013 acosh inf 2.3 -> inf 0.0\nacosh1014 acosh -inf inf -> inf 2.3561944901923448\nacosh1015 acosh inf inf -> inf 0.78539816339744828\nacosh1016 acosh inf nan -> inf nan\nacosh1017 acosh -inf nan -> inf nan\nacosh1018 acosh nan 0.0 -> nan nan\nacosh1019 acosh nan 2.3 -> nan nan\nacosh1020 acosh nan inf -> inf nan\nacosh1021 acosh nan nan -> nan nan\nacosh1022 acosh 0.0 -0.0 -> 0.0 -1.5707963267948966\nacosh1023 acosh -0.0 -0.0 -> 0.0 -1.5707963267948966\nacosh1024 acosh 0.0 -inf -> inf -1.5707963267948966\nacosh1025 acosh 2.3 -inf -> inf -1.5707963267948966\nacosh1026 acosh -0.0 -inf -> inf -1.5707963267948966\nacosh1027 acosh -2.3 -inf -> inf -1.5707963267948966\nacosh1028 acosh -inf -0.0 -> inf -3.1415926535897931\nacosh1029 acosh -inf -2.3 -> inf -3.1415926535897931\nacosh1030 acosh inf -0.0 -> inf -0.0\nacosh1031 acosh inf -2.3 -> inf -0.0\nacosh1032 acosh -inf -inf -> inf -2.3561944901923448\nacosh1033 acosh inf -inf -> inf -0.78539816339744828\nacosh1034 acosh nan -0.0 -> nan nan\nacosh1035 acosh nan -2.3 -> nan nan\nacosh1036 acosh nan -inf -> inf nan\n\n\n------------------------\n-- asin: Inverse sine --\n------------------------\n\n-- zeros\nasin0000 asin 0.0 0.0 -> 0.0 0.0\nasin0001 asin 0.0 -0.0 -> 0.0 -0.0\nasin0002 asin -0.0 0.0 -> -0.0 0.0\nasin0003 asin -0.0 -0.0 -> -0.0 -0.0\n\n-- branch points: +/-1\nasin0010 asin 1.0 0.0 -> 1.5707963267948966 0.0\nasin0011 asin 1.0 -0.0 -> 1.5707963267948966 -0.0\nasin0012 asin -1.0 0.0 -> -1.5707963267948966 0.0\nasin0013 asin -1.0 -0.0 -> -1.5707963267948966 -0.0\n\n-- values along both sides of real axis\nasin0020 asin -9.8813129168249309e-324 0.0 -> -9.8813129168249309e-324 0.0\nasin0021 asin -9.8813129168249309e-324 -0.0 -> -9.8813129168249309e-324 -0.0\nasin0022 asin -1e-305 0.0 -> -1e-305 0.0\nasin0023 asin -1e-305 -0.0 -> -1e-305 -0.0\nasin0024 asin -1e-150 0.0 -> -1e-150 0.0\nasin0025 asin -1e-150 -0.0 -> -1e-150 -0.0\nasin0026 asin -9.9999999999999998e-17 0.0 -> -9.9999999999999998e-17 0.0\nasin0027 asin -9.9999999999999998e-17 -0.0 -> -9.9999999999999998e-17 -0.0\nasin0028 asin -0.001 0.0 -> -0.0010000001666667416 0.0\nasin0029 asin -0.001 -0.0 -> -0.0010000001666667416 -0.0\nasin0030 asin -0.57899999999999996 0.0 -> -0.61750165481717001 0.0\nasin0031 asin -0.57899999999999996 -0.0 -> -0.61750165481717001 -0.0\nasin0032 asin -0.99999999999999989 0.0 -> -1.5707963118937354 0.0\nasin0033 asin -0.99999999999999989 -0.0 -> -1.5707963118937354 -0.0\nasin0034 asin -1.0000000000000002 0.0 -> -1.5707963267948966 2.1073424255447014e-08\nasin0035 asin -1.0000000000000002 -0.0 -> -1.5707963267948966 -2.1073424255447014e-08\nasin0036 asin -1.0009999999999999 0.0 -> -1.5707963267948966 0.044717633608306849\nasin0037 asin -1.0009999999999999 -0.0 -> -1.5707963267948966 -0.044717633608306849\nasin0038 asin -2.0 0.0 -> -1.5707963267948966 1.3169578969248168\nasin0039 asin -2.0 -0.0 -> -1.5707963267948966 -1.3169578969248168\nasin0040 asin -23.0 0.0 -> -1.5707963267948966 3.8281684713331012\nasin0041 asin -23.0 -0.0 -> -1.5707963267948966 -3.8281684713331012\nasin0042 asin -10000000000000000.0 0.0 -> -1.5707963267948966 37.534508668464674\nasin0043 asin -10000000000000000.0 -0.0 -> -1.5707963267948966 -37.534508668464674\nasin0044 asin -9.9999999999999998e+149 0.0 -> -1.5707963267948966 346.08091112966679\nasin0045 asin -9.9999999999999998e+149 -0.0 -> -1.5707963267948966 -346.08091112966679\nasin0046 asin -1.0000000000000001e+299 0.0 -> -1.5707963267948966 689.16608998577965\nasin0047 asin -1.0000000000000001e+299 -0.0 -> -1.5707963267948966 -689.16608998577965\nasin0048 asin 9.8813129168249309e-324 0.0 -> 9.8813129168249309e-324 0.0\nasin0049 asin 9.8813129168249309e-324 -0.0 -> 9.8813129168249309e-324 -0.0\nasin0050 asin 1e-305 0.0 -> 1e-305 0.0\nasin0051 asin 1e-305 -0.0 -> 1e-305 -0.0\nasin0052 asin 1e-150 0.0 -> 1e-150 0.0\nasin0053 asin 1e-150 -0.0 -> 1e-150 -0.0\nasin0054 asin 9.9999999999999998e-17 0.0 -> 9.9999999999999998e-17 0.0\nasin0055 asin 9.9999999999999998e-17 -0.0 -> 9.9999999999999998e-17 -0.0\nasin0056 asin 0.001 0.0 -> 0.0010000001666667416 0.0\nasin0057 asin 0.001 -0.0 -> 0.0010000001666667416 -0.0\nasin0058 asin 0.57899999999999996 0.0 -> 0.61750165481717001 0.0\nasin0059 asin 0.57899999999999996 -0.0 -> 0.61750165481717001 -0.0\nasin0060 asin 0.99999999999999989 0.0 -> 1.5707963118937354 0.0\nasin0061 asin 0.99999999999999989 -0.0 -> 1.5707963118937354 -0.0\nasin0062 asin 1.0000000000000002 0.0 -> 1.5707963267948966 2.1073424255447014e-08\nasin0063 asin 1.0000000000000002 -0.0 -> 1.5707963267948966 -2.1073424255447014e-08\nasin0064 asin 1.0009999999999999 0.0 -> 1.5707963267948966 0.044717633608306849\nasin0065 asin 1.0009999999999999 -0.0 -> 1.5707963267948966 -0.044717633608306849\nasin0066 asin 2.0 0.0 -> 1.5707963267948966 1.3169578969248168\nasin0067 asin 2.0 -0.0 -> 1.5707963267948966 -1.3169578969248168\nasin0068 asin 23.0 0.0 -> 1.5707963267948966 3.8281684713331012\nasin0069 asin 23.0 -0.0 -> 1.5707963267948966 -3.8281684713331012\nasin0070 asin 10000000000000000.0 0.0 -> 1.5707963267948966 37.534508668464674\nasin0071 asin 10000000000000000.0 -0.0 -> 1.5707963267948966 -37.534508668464674\nasin0072 asin 9.9999999999999998e+149 0.0 -> 1.5707963267948966 346.08091112966679\nasin0073 asin 9.9999999999999998e+149 -0.0 -> 1.5707963267948966 -346.08091112966679\nasin0074 asin 1.0000000000000001e+299 0.0 -> 1.5707963267948966 689.16608998577965\nasin0075 asin 1.0000000000000001e+299 -0.0 -> 1.5707963267948966 -689.16608998577965\n\n-- random inputs\nasin0100 asin -1.5979555835086083 -0.15003009814595247 -> -1.4515369557405788 -1.0544476399790823\nasin0101 asin -0.57488225895317679 -9.6080397838952743e-13 -> -0.61246024460412851 -1.174238005400403e-12\nasin0102 asin -3.6508087930516249 -0.36027527093220152 -> -1.4685890605305874 -1.9742273007152038\nasin0103 asin -1.5238659792326819 -1.1360813516996364 -> -0.86080051691147275 -1.3223742205689195\nasin0104 asin -1592.0639045555306 -0.72362427935018236 -> -1.5703418071175179 -8.0659336918729228\nasin0105 asin -0.19835471371312019 4.2131508416697709 -> -0.045777831019935149 2.1461732751933171\nasin0106 asin -1.918471054430213 0.40603305079779234 -> -1.3301396585791556 1.30263642314981\nasin0107 asin -254495.01623373642 0.71084414434470822 -> -1.5707935336394359 13.140183712762321\nasin0108 asin -0.31315882715691157 3.9647994288429866 -> -0.076450403840916004 2.0889762138713457\nasin0109 asin -0.90017064284720816 1.2530659485907105 -> -0.53466509741943447 1.1702811557577\nasin0110 asin 2.1615181696571075 -0.14058647488229523 -> 1.4976166323896871 -1.4085811039334604\nasin0111 asin 1.2104749210707795 -0.85732484485298999 -> 0.83913071588343924 -1.0681719250525901\nasin0112 asin 1.7059733185128891 -0.84032966373156581 -> 1.0510900815816229 -1.2967979791361652\nasin0113 asin 9.9137085017290687 -1.4608383970250893 -> 1.4237704820128891 -2.995414677560686\nasin0114 asin 117.12344751041495 -5453908091.5334015 -> 2.1475141411392012e-08 -23.112745450217066\nasin0115 asin 0.081041187798029227 0.067054349860173196 -> 0.080946786856771813 0.067223991060639698\nasin0116 asin 46.635472322049949 2.3835190718056678 -> 1.5197194940010779 4.5366989600972083\nasin0117 asin 3907.0687961127105 19.144021886390181 -> 1.5658965233083235 8.9637018715924217\nasin0118 asin 1.0889312322308273 509.01577883554768 -> 0.0021392803817829316 6.9256294494524706\nasin0119 asin 0.10851518277509224 1.5612510908217476 -> 0.058491014243902621 1.2297075725621327\n\n-- values near infinity\nasin0200 asin 1.5230241998821499e+308 5.5707228994084525e+307 -> 1.2201446370892068 710.37283486535966\nasin0201 asin 8.1334317698672204e+307 -9.2249425197872451e+307 -> 0.72259991284020042 -710.0962453049026\nasin0202 asin -9.9138506659241768e+307 6.701544526434995e+307 -> -0.97637511742194594 710.06887486671371\nasin0203 asin -1.4141298868173842e+308 -5.401505134514191e+307 -> -1.2059319055160587 -710.30396478954628\nasin0204 asin 0.0 9.1618092977897431e+307 -> 0.0 709.80181441050593\nasin0205 asin -0.0 6.8064342551939755e+307 -> -0.0 709.50463910853489\nasin0206 asin 0.0 -6.4997516454798215e+307 -> 0.0 -709.45853469751592\nasin0207 asin -0.0 -1.6767449053345242e+308 -> -0.0 -710.4062101803022\nasin0208 asin 5.4242749957378916e+307 0.0 -> 1.5707963267948966 709.27765497888902\nasin0209 asin 9.5342145121164749e+307 -0.0 -> 1.5707963267948966 -709.84165758595907\nasin0210 asin -7.0445698006201847e+307 0.0 -> -1.5707963267948966 709.53902780872136\nasin0211 asin -1.0016025569769706e+308 -0.0 -> -1.5707963267948966 -709.89095709697881\nasin0212 asin 1.6552203778877204e+308 0.48761543336249491 -> 1.5707963267948966 710.39328998153474\nasin0213 asin 1.2485712830384869e+308 -4.3489311161278899 -> 1.5707963267948966 -710.1113557467786\nasin0214 asin -1.5117842813353125e+308 5.123452666102434 -> -1.5707963267948966 710.30264641923031\nasin0215 asin -1.3167634313008016e+308 -0.52939679793528982 -> -1.5707963267948966 -710.16453260239768\nasin0216 asin 0.80843929176985907 1.0150851827767876e+308 -> 7.9642507396113875e-309 709.90432835561637\nasin0217 asin 8.2544809829680901 -1.7423548140539474e+308 -> 4.7375430746865733e-308 -710.44459336242164\nasin0218 asin -5.2499000118824295 4.6655578977512214e+307 -> -1.1252459249113292e-307 709.1269781491103\nasin0219 asin -5.9904782760833433 -4.7315689314781163e+307 -> -1.2660659419394637e-307 -709.14102757522312\n\n-- special values\nasin1000 asin -0.0 0.0 -> -0.0 0.0\nasin1001 asin 0.0 0.0 -> 0.0 0.0\nasin1002 asin -0.0 -0.0 -> -0.0 -0.0\nasin1003 asin 0.0 -0.0 -> 0.0 -0.0\nasin1004 asin -inf 0.0 -> -1.5707963267948966 inf\nasin1005 asin -inf 2.2999999999999998 -> -1.5707963267948966 inf\nasin1006 asin nan 0.0 -> nan nan\nasin1007 asin nan 2.2999999999999998 -> nan nan\nasin1008 asin -0.0 inf -> -0.0 inf\nasin1009 asin -2.2999999999999998 inf -> -0.0 inf\nasin1010 asin -inf inf -> -0.78539816339744828 inf\nasin1011 asin nan inf -> nan inf\nasin1012 asin -0.0 nan -> -0.0 nan\nasin1013 asin -2.2999999999999998 nan -> nan nan\nasin1014 asin -inf nan -> nan inf ignore-imag-sign\nasin1015 asin nan nan -> nan nan\nasin1016 asin inf 0.0 -> 1.5707963267948966 inf\nasin1017 asin inf 2.2999999999999998 -> 1.5707963267948966 inf\nasin1018 asin 0.0 inf -> 0.0 inf\nasin1019 asin 2.2999999999999998 inf -> 0.0 inf\nasin1020 asin inf inf -> 0.78539816339744828 inf\nasin1021 asin 0.0 nan -> 0.0 nan\nasin1022 asin 2.2999999999999998 nan -> nan nan\nasin1023 asin inf nan -> nan inf ignore-imag-sign\nasin1024 asin inf -0.0 -> 1.5707963267948966 -inf\nasin1025 asin inf -2.2999999999999998 -> 1.5707963267948966 -inf\nasin1026 asin nan -0.0 -> nan nan\nasin1027 asin nan -2.2999999999999998 -> nan nan\nasin1028 asin 0.0 -inf -> 0.0 -inf\nasin1029 asin 2.2999999999999998 -inf -> 0.0 -inf\nasin1030 asin inf -inf -> 0.78539816339744828 -inf\nasin1031 asin nan -inf -> nan -inf\nasin1032 asin -inf -0.0 -> -1.5707963267948966 -inf\nasin1033 asin -inf -2.2999999999999998 -> -1.5707963267948966 -inf\nasin1034 asin -0.0 -inf -> -0.0 -inf\nasin1035 asin -2.2999999999999998 -inf -> -0.0 -inf\nasin1036 asin -inf -inf -> -0.78539816339744828 -inf\n\n\n------------------------------------\n-- asinh: Inverse hyperbolic sine --\n------------------------------------\n\n-- zeros\nasinh0000 asinh 0.0 0.0 -> 0.0 0.0\nasinh0001 asinh 0.0 -0.0 -> 0.0 -0.0\nasinh0002 asinh -0.0 0.0 -> -0.0 0.0\nasinh0003 asinh -0.0 -0.0 -> -0.0 -0.0\n\n-- branch points: +/-i\nasinh0010 asinh 0.0 1.0 -> 0.0 1.5707963267948966\nasinh0011 asinh 0.0 -1.0 -> 0.0 -1.5707963267948966\nasinh0012 asinh -0.0 1.0 -> -0.0 1.5707963267948966\nasinh0013 asinh -0.0 -1.0 -> -0.0 -1.5707963267948966\n\n-- values along both sides of imaginary axis\nasinh0020 asinh 0.0 -9.8813129168249309e-324 -> 0.0 -9.8813129168249309e-324\nasinh0021 asinh -0.0 -9.8813129168249309e-324 -> -0.0 -9.8813129168249309e-324\nasinh0022 asinh 0.0 -1e-305 -> 0.0 -1e-305\nasinh0023 asinh -0.0 -1e-305 -> -0.0 -1e-305\nasinh0024 asinh 0.0 -1e-150 -> 0.0 -1e-150\nasinh0025 asinh -0.0 -1e-150 -> -0.0 -1e-150\nasinh0026 asinh 0.0 -9.9999999999999998e-17 -> 0.0 -9.9999999999999998e-17\nasinh0027 asinh -0.0 -9.9999999999999998e-17 -> -0.0 -9.9999999999999998e-17\nasinh0028 asinh 0.0 -0.001 -> 0.0 -0.0010000001666667416\nasinh0029 asinh -0.0 -0.001 -> -0.0 -0.0010000001666667416\nasinh0030 asinh 0.0 -0.57899999999999996 -> 0.0 -0.61750165481717001\nasinh0031 asinh -0.0 -0.57899999999999996 -> -0.0 -0.61750165481717001\nasinh0032 asinh 0.0 -0.99999999999999989 -> 0.0 -1.5707963118937354\nasinh0033 asinh -0.0 -0.99999999999999989 -> -0.0 -1.5707963118937354\nasinh0034 asinh 0.0 -1.0000000000000002 -> 2.1073424255447014e-08 -1.5707963267948966\nasinh0035 asinh -0.0 -1.0000000000000002 -> -2.1073424255447014e-08 -1.5707963267948966\nasinh0036 asinh 0.0 -1.0009999999999999 -> 0.044717633608306849 -1.5707963267948966\nasinh0037 asinh -0.0 -1.0009999999999999 -> -0.044717633608306849 -1.5707963267948966\nasinh0038 asinh 0.0 -2.0 -> 1.3169578969248168 -1.5707963267948966\nasinh0039 asinh -0.0 -2.0 -> -1.3169578969248168 -1.5707963267948966\nasinh0040 asinh 0.0 -20.0 -> 3.6882538673612966 -1.5707963267948966\nasinh0041 asinh -0.0 -20.0 -> -3.6882538673612966 -1.5707963267948966\nasinh0042 asinh 0.0 -10000000000000000.0 -> 37.534508668464674 -1.5707963267948966\nasinh0043 asinh -0.0 -10000000000000000.0 -> -37.534508668464674 -1.5707963267948966\nasinh0044 asinh 0.0 -9.9999999999999998e+149 -> 346.08091112966679 -1.5707963267948966\nasinh0045 asinh -0.0 -9.9999999999999998e+149 -> -346.08091112966679 -1.5707963267948966\nasinh0046 asinh 0.0 -1.0000000000000001e+299 -> 689.16608998577965 -1.5707963267948966\nasinh0047 asinh -0.0 -1.0000000000000001e+299 -> -689.16608998577965 -1.5707963267948966\nasinh0048 asinh 0.0 9.8813129168249309e-324 -> 0.0 9.8813129168249309e-324\nasinh0049 asinh -0.0 9.8813129168249309e-324 -> -0.0 9.8813129168249309e-324\nasinh0050 asinh 0.0 1e-305 -> 0.0 1e-305\nasinh0051 asinh -0.0 1e-305 -> -0.0 1e-305\nasinh0052 asinh 0.0 1e-150 -> 0.0 1e-150\nasinh0053 asinh -0.0 1e-150 -> -0.0 1e-150\nasinh0054 asinh 0.0 9.9999999999999998e-17 -> 0.0 9.9999999999999998e-17\nasinh0055 asinh -0.0 9.9999999999999998e-17 -> -0.0 9.9999999999999998e-17\nasinh0056 asinh 0.0 0.001 -> 0.0 0.0010000001666667416\nasinh0057 asinh -0.0 0.001 -> -0.0 0.0010000001666667416\nasinh0058 asinh 0.0 0.57899999999999996 -> 0.0 0.61750165481717001\nasinh0059 asinh -0.0 0.57899999999999996 -> -0.0 0.61750165481717001\nasinh0060 asinh 0.0 0.99999999999999989 -> 0.0 1.5707963118937354\nasinh0061 asinh -0.0 0.99999999999999989 -> -0.0 1.5707963118937354\nasinh0062 asinh 0.0 1.0000000000000002 -> 2.1073424255447014e-08 1.5707963267948966\nasinh0063 asinh -0.0 1.0000000000000002 -> -2.1073424255447014e-08 1.5707963267948966\nasinh0064 asinh 0.0 1.0009999999999999 -> 0.044717633608306849 1.5707963267948966\nasinh0065 asinh -0.0 1.0009999999999999 -> -0.044717633608306849 1.5707963267948966\nasinh0066 asinh 0.0 2.0 -> 1.3169578969248168 1.5707963267948966\nasinh0067 asinh -0.0 2.0 -> -1.3169578969248168 1.5707963267948966\nasinh0068 asinh 0.0 20.0 -> 3.6882538673612966 1.5707963267948966\nasinh0069 asinh -0.0 20.0 -> -3.6882538673612966 1.5707963267948966\nasinh0070 asinh 0.0 10000000000000000.0 -> 37.534508668464674 1.5707963267948966\nasinh0071 asinh -0.0 10000000000000000.0 -> -37.534508668464674 1.5707963267948966\nasinh0072 asinh 0.0 9.9999999999999998e+149 -> 346.08091112966679 1.5707963267948966\nasinh0073 asinh -0.0 9.9999999999999998e+149 -> -346.08091112966679 1.5707963267948966\nasinh0074 asinh 0.0 1.0000000000000001e+299 -> 689.16608998577965 1.5707963267948966\nasinh0075 asinh -0.0 1.0000000000000001e+299 -> -689.16608998577965 1.5707963267948966\n\n-- random inputs\nasinh0100 asinh -0.5946402853710423 -0.044506548910000145 -> -0.56459775392653022 -0.038256221441536356\nasinh0101 asinh -0.19353958046180916 -0.017489624793193454 -> -0.19237926804196651 -0.017171741895336792\nasinh0102 asinh -0.033117585138955893 -8.5256414015933757 -> -2.8327758348650969 -1.5668848791092411\nasinh0103 asinh -1.5184043184035716 -0.73491245339073275 -> -1.2715891419764005 -0.39204624408542355\nasinh0104 asinh -0.60716120271208818 -0.28900743958436542 -> -0.59119299421187232 -0.24745931678118135\nasinh0105 asinh -0.0237177865112429 2.8832601052166313 -> -1.7205820772413236 1.5620261702963094\nasinh0106 asinh -2.3906812342743979 2.6349216848574013 -> -1.9609636249445124 0.8142142660574706\nasinh0107 asinh -0.0027605019787620517 183.85588476550555 -> -5.9072920005445066 1.5707813120847871\nasinh0108 asinh -0.99083661164404713 0.028006797051617648 -> -0.8750185251283995 0.019894099615994653\nasinh0109 asinh -3.0362951937986393 0.86377266758504867 -> -1.8636030714685221 0.26475058859950168\nasinh0110 asinh 0.34438464536152769 -0.71603790174885029 -> 0.43985415690734164 -0.71015037409294324\nasinh0111 asinh 4.4925124413876256 -60604595352.871613 -> 25.520783738612078 -1.5707963267207683\nasinh0112 asinh 2.3213991428170337 -7.5459667007307258 -> 2.7560464993451643 -1.270073210856117\nasinh0113 asinh 0.21291939741682028 -1.2720428814784408 -> 0.77275088137338266 -1.3182099250896895\nasinh0114 asinh 6.6447359379455957 -0.97196191666946996 -> 2.602830695139672 -0.14368247412319965\nasinh0115 asinh 7.1326256655083746 2.1516360452706857 -> 2.7051146374367212 0.29051701669727581\nasinh0116 asinh 0.18846550905063442 3.4705348585339832 -> 1.917697875799296 1.514155593347924\nasinh0117 asinh 0.19065075303281598 0.26216814548222012 -> 0.19603050785932474 0.26013422809614117\nasinh0118 asinh 2.0242004665739719 0.70510281647495787 -> 1.4970366212896002 0.30526007200481453\nasinh0119 asinh 37.336596461576057 717.29157391678234 -> 7.269981997945294 1.5187910219576033\n\n-- values near infinity\nasinh0200 asinh 1.0760517500874541e+308 1.1497786241240167e+308 -> 710.34346055651815 0.81850936961793475\nasinh0201 asinh 1.1784839328845529e+308 -1.6478429586716638e+308 -> 710.59536255783678 -0.94996311735607697\nasinh0202 asinh -4.8777682248909193e+307 1.4103736217538474e+308 -> -710.28970147376992 1.2378239519096443\nasinh0203 asinh -1.2832478903233108e+308 -1.5732392613155698e+308 -> -710.59750164290745 -0.88657181439322452\nasinh0204 asinh 0.0 6.8431383856345372e+307 -> 709.51001718444604 1.5707963267948966\nasinh0205 asinh -0.0 8.601822432238051e+307 -> -709.73874482126689 1.5707963267948966\nasinh0206 asinh 0.0 -5.5698396067303782e+307 -> 709.30413698733742 -1.5707963267948966\nasinh0207 asinh -0.0 -7.1507777734621804e+307 -> -709.55399186002705 -1.5707963267948966\nasinh0208 asinh 1.6025136110019349e+308 0.0 -> 710.3609292261076 0.0\nasinh0209 asinh 1.3927819858239114e+308 -0.0 -> 710.22065899832899 -0.0\nasinh0210 asinh -6.0442994056210995e+307 0.0 -> -709.38588631057621 0.0\nasinh0211 asinh -1.2775271979042634e+308 -0.0 -> -710.13428215553972 -0.0\nasinh0212 asinh 1.0687496260268489e+308 1.0255615699476961 -> 709.95584521407841 9.5959010882679093e-309\nasinh0213 asinh 1.0050967333370962e+308 -0.87668970117333433 -> 709.89443961168183 -8.7224410556242882e-309\nasinh0214 asinh -5.7161452814862392e+307 8.2377808413450122 -> -709.33006540611166 1.4411426644501116e-307\nasinh0215 asinh -8.2009040727653315e+307 -6.407409526654976 -> -709.69101513070109 -7.8130526461510088e-308\nasinh0216 asinh 6.4239368496483982 1.6365990821551427e+308 -> 710.38197618101287 1.5707963267948966\nasinh0217 asinh 5.4729111423315882 -1.1227237438144211e+308 -> 710.00511346983546 -1.5707963267948966\nasinh0218 asinh -8.3455818297412723 1.443172020182019e+308 -> -710.25619930551818 1.5707963267948966\nasinh0219 asinh -2.6049726230372441 -1.7952291144022702e+308 -> -710.47448847685644 -1.5707963267948966\n\n-- values near 0\nasinh0220 asinh 1.2940113339664088e-314 6.9169190417774516e-323 -> 1.2940113339664088e-314 6.9169190417774516e-323\nasinh0221 asinh 2.3848478863874649e-315 -3.1907655025717717e-310 -> 2.3848478863874649e-315 -3.1907655025717717e-310\nasinh0222 asinh -3.0097643679641622e-316 4.6936236354918422e-322 -> -3.0097643679641622e-316 4.6936236354918422e-322\nasinh0223 asinh -1.787997087755751e-308 -8.5619622834902341e-310 -> -1.787997087755751e-308 -8.5619622834902341e-310\nasinh0224 asinh 0.0 1.2491433448427325e-314 -> 0.0 1.2491433448427325e-314\nasinh0225 asinh -0.0 2.5024072154538062e-308 -> -0.0 2.5024072154538062e-308\nasinh0226 asinh 0.0 -2.9643938750474793e-323 -> 0.0 -2.9643938750474793e-323\nasinh0227 asinh -0.0 -2.9396905927554169e-320 -> -0.0 -2.9396905927554169e-320\nasinh0228 asinh 5.64042930029359e-317 0.0 -> 5.64042930029359e-317 0.0\nasinh0229 asinh 3.3833911866596068e-318 -0.0 -> 3.3833911866596068e-318 -0.0\nasinh0230 asinh -4.9406564584124654e-324 0.0 -> -4.9406564584124654e-324 0.0\nasinh0231 asinh -2.2211379227994845e-308 -0.0 -> -2.2211379227994845e-308 -0.0\n\n-- special values\nasinh1000 asinh 0.0 0.0 -> 0.0 0.0\nasinh1001 asinh 0.0 -0.0 -> 0.0 -0.0\nasinh1002 asinh -0.0 0.0 -> -0.0 0.0\nasinh1003 asinh -0.0 -0.0 -> -0.0 -0.0\nasinh1004 asinh 0.0 inf -> inf 1.5707963267948966\nasinh1005 asinh 2.3 inf -> inf 1.5707963267948966\nasinh1006 asinh 0.0 nan -> nan nan\nasinh1007 asinh 2.3 nan -> nan nan\nasinh1008 asinh inf 0.0 -> inf 0.0\nasinh1009 asinh inf 2.3 -> inf 0.0\nasinh1010 asinh inf inf -> inf 0.78539816339744828\nasinh1011 asinh inf nan -> inf nan\nasinh1012 asinh nan 0.0 -> nan 0.0\nasinh1013 asinh nan 2.3 -> nan nan\nasinh1014 asinh nan inf -> inf nan                      ignore-real-sign\nasinh1015 asinh nan nan -> nan nan\nasinh1016 asinh 0.0 -inf -> inf -1.5707963267948966\nasinh1017 asinh 2.3 -inf -> inf -1.5707963267948966\nasinh1018 asinh inf -0.0 -> inf -0.0\nasinh1019 asinh inf -2.3 -> inf -0.0\nasinh1020 asinh inf -inf -> inf -0.78539816339744828\nasinh1021 asinh nan -0.0 -> nan -0.0\nasinh1022 asinh nan -2.3 -> nan nan\nasinh1023 asinh nan -inf -> inf nan                     ignore-real-sign\nasinh1024 asinh -0.0 -inf -> -inf -1.5707963267948966\nasinh1025 asinh -2.3 -inf -> -inf -1.5707963267948966\nasinh1026 asinh -0.0 nan -> nan nan\nasinh1027 asinh -2.3 nan -> nan nan\nasinh1028 asinh -inf -0.0 -> -inf -0.0\nasinh1029 asinh -inf -2.3 -> -inf -0.0\nasinh1030 asinh -inf -inf -> -inf -0.78539816339744828\nasinh1031 asinh -inf nan -> -inf nan\nasinh1032 asinh -0.0 inf -> -inf 1.5707963267948966\nasinh1033 asinh -2.3 inf -> -inf 1.5707963267948966\nasinh1034 asinh -inf 0.0 -> -inf 0.0\nasinh1035 asinh -inf 2.3 -> -inf 0.0\nasinh1036 asinh -inf inf -> -inf 0.78539816339744828\n\n\n---------------------------\n-- atan: Inverse tangent --\n---------------------------\n\n-- zeros\n-- These are tested in testAtanSign in test_cmath.py\n-- atan0000 atan 0.0 0.0 -> 0.0 0.0\n-- atan0001 atan 0.0 -0.0 -> 0.0 -0.0\n-- atan0002 atan -0.0 0.0 -> -0.0 0.0\n-- atan0003 atan -0.0 -0.0 -> -0.0 -0.0\n\n-- values along both sides of imaginary axis\natan0010 atan 0.0 -9.8813129168249309e-324 -> 0.0 -9.8813129168249309e-324\natan0011 atan -0.0 -9.8813129168249309e-324 -> -0.0 -9.8813129168249309e-324\natan0012 atan 0.0 -1e-305 -> 0.0 -1e-305\natan0013 atan -0.0 -1e-305 -> -0.0 -1e-305\natan0014 atan 0.0 -1e-150 -> 0.0 -1e-150\natan0015 atan -0.0 -1e-150 -> -0.0 -1e-150\natan0016 atan 0.0 -9.9999999999999998e-17 -> 0.0 -9.9999999999999998e-17\natan0017 atan -0.0 -9.9999999999999998e-17 -> -0.0 -9.9999999999999998e-17\natan0018 atan 0.0 -0.001 -> 0.0 -0.0010000003333335333\natan0019 atan -0.0 -0.001 -> -0.0 -0.0010000003333335333\natan0020 atan 0.0 -0.57899999999999996 -> 0.0 -0.6609570902866303\natan0021 atan -0.0 -0.57899999999999996 -> -0.0 -0.6609570902866303\natan0022 atan 0.0 -0.99999999999999989 -> 0.0 -18.714973875118524\natan0023 atan -0.0 -0.99999999999999989 -> -0.0 -18.714973875118524\natan0024 atan 0.0 -1.0000000000000002 -> 1.5707963267948966 -18.36840028483855\natan0025 atan -0.0 -1.0000000000000002 -> -1.5707963267948966 -18.36840028483855\natan0026 atan 0.0 -1.0009999999999999 -> 1.5707963267948966 -3.8007011672919218\natan0027 atan -0.0 -1.0009999999999999 -> -1.5707963267948966 -3.8007011672919218\natan0028 atan 0.0 -2.0 -> 1.5707963267948966 -0.54930614433405489\natan0029 atan -0.0 -2.0 -> -1.5707963267948966 -0.54930614433405489\natan0030 atan 0.0 -20.0 -> 1.5707963267948966 -0.050041729278491265\natan0031 atan -0.0 -20.0 -> -1.5707963267948966 -0.050041729278491265\natan0032 atan 0.0 -10000000000000000.0 -> 1.5707963267948966 -9.9999999999999998e-17\natan0033 atan -0.0 -10000000000000000.0 -> -1.5707963267948966 -9.9999999999999998e-17\natan0034 atan 0.0 -9.9999999999999998e+149 -> 1.5707963267948966 -1e-150\natan0035 atan -0.0 -9.9999999999999998e+149 -> -1.5707963267948966 -1e-150\natan0036 atan 0.0 -1.0000000000000001e+299 -> 1.5707963267948966 -9.9999999999999999e-300\natan0037 atan -0.0 -1.0000000000000001e+299 -> -1.5707963267948966 -9.9999999999999999e-300\natan0038 atan 0.0 9.8813129168249309e-324 -> 0.0 9.8813129168249309e-324\natan0039 atan -0.0 9.8813129168249309e-324 -> -0.0 9.8813129168249309e-324\natan0040 atan 0.0 1e-305 -> 0.0 1e-305\natan0041 atan -0.0 1e-305 -> -0.0 1e-305\natan0042 atan 0.0 1e-150 -> 0.0 1e-150\natan0043 atan -0.0 1e-150 -> -0.0 1e-150\natan0044 atan 0.0 9.9999999999999998e-17 -> 0.0 9.9999999999999998e-17\natan0045 atan -0.0 9.9999999999999998e-17 -> -0.0 9.9999999999999998e-17\natan0046 atan 0.0 0.001 -> 0.0 0.0010000003333335333\natan0047 atan -0.0 0.001 -> -0.0 0.0010000003333335333\natan0048 atan 0.0 0.57899999999999996 -> 0.0 0.6609570902866303\natan0049 atan -0.0 0.57899999999999996 -> -0.0 0.6609570902866303\natan0050 atan 0.0 0.99999999999999989 -> 0.0 18.714973875118524\natan0051 atan -0.0 0.99999999999999989 -> -0.0 18.714973875118524\natan0052 atan 0.0 1.0000000000000002 -> 1.5707963267948966 18.36840028483855\natan0053 atan -0.0 1.0000000000000002 -> -1.5707963267948966 18.36840028483855\natan0054 atan 0.0 1.0009999999999999 -> 1.5707963267948966 3.8007011672919218\natan0055 atan -0.0 1.0009999999999999 -> -1.5707963267948966 3.8007011672919218\natan0056 atan 0.0 2.0 -> 1.5707963267948966 0.54930614433405489\natan0057 atan -0.0 2.0 -> -1.5707963267948966 0.54930614433405489\natan0058 atan 0.0 20.0 -> 1.5707963267948966 0.050041729278491265\natan0059 atan -0.0 20.0 -> -1.5707963267948966 0.050041729278491265\natan0060 atan 0.0 10000000000000000.0 -> 1.5707963267948966 9.9999999999999998e-17\natan0061 atan -0.0 10000000000000000.0 -> -1.5707963267948966 9.9999999999999998e-17\natan0062 atan 0.0 9.9999999999999998e+149 -> 1.5707963267948966 1e-150\natan0063 atan -0.0 9.9999999999999998e+149 -> -1.5707963267948966 1e-150\natan0064 atan 0.0 1.0000000000000001e+299 -> 1.5707963267948966 9.9999999999999999e-300\natan0065 atan -0.0 1.0000000000000001e+299 -> -1.5707963267948966 9.9999999999999999e-300\n\n-- random inputs\natan0100 atan -0.32538873661060214 -1.5530461550412578 -> -1.3682728427554227 -0.69451401598762041\natan0101 atan -0.45863393495197929 -4799.1747094903594 -> -1.5707963068820623 -0.00020836916050636145\natan0102 atan -8.3006999685976162 -2.6788890251790938 -> -1.4619862771810199 -0.034811669653327826\natan0103 atan -1.8836307682985314 -1.1441976638861771 -> -1.1839984370871612 -0.20630956157312796\natan0104 atan -0.00063230482407491669 -4.9312520961829485 -> -1.5707692093223147 -0.20563867743008304\natan0105 atan -0.84278137150065946 179012.37493146997 -> -1.5707963267685969 5.5862059836425272e-06\natan0106 atan -0.95487853984049287 14.311334539886177 -> -1.5661322859434561 0.069676024526232005\natan0107 atan -1.3513252539663239 6.0500727021632198e-08 -> -0.93371676315220975 2.140800269742656e-08\natan0108 atan -0.20566254458595795 0.11933771944159823 -> -0.20556463711174916 0.11493405387141732\natan0109 atan -0.58563718795408559 0.64438965423212868 -> -0.68361089300233124 0.46759762751800249\natan0110 atan 48.479267751948292 -78.386382460112543 -> 1.5650888770910523 -0.0092276811373297584\natan0111 atan 1.0575373914056061 -0.75988012377296987 -> 0.94430886722043594 -0.31915698126703118\natan0112 atan 4444810.4314677203 -0.56553404593942558 -> 1.5707961018134231 -2.8625446437701909e-14\natan0113 atan 0.010101405082520009 -0.032932668550282478 -> 0.01011202676646334 -0.032941214776834996\natan0114 atan 1.5353585300154911 -2.1947099346796519 -> 1.3400310739206394 -0.29996003607449045\natan0115 atan 0.21869457055670882 9.9915684254007093 -> 1.5685846078876444 0.1003716881759439\natan0116 atan 0.17783290150246836 0.064334689863650957 -> 0.17668728064286277 0.062435808728873846\natan0117 atan 15.757474087615918 383.57262142534 -> 1.5706894060369621 0.0026026817278826603\natan0118 atan 10.587017408533317 0.21720238081843438 -> 1.4766594681336236 0.0019199097383010061\natan0119 atan 0.86026078678781204 0.1230148609359502 -> 0.7147259322534929 0.070551221954286605\n\n-- values near infinity\natan0200 atan 7.8764397011195798e+307 8.1647921137746308e+307 -> 1.5707963267948966 6.3439446939604493e-309\natan0201 atan 1.5873698696131487e+308 -1.0780367422960641e+308 -> 1.5707963267948966 -2.9279309368530781e-309\natan0202 atan -1.5844551864825834e+308 1.0290657809098675e+308 -> -1.5707963267948966 2.8829614736961417e-309\natan0203 atan -1.3168792562524032e+308 -9.088432341614825e+307 -> -1.5707963267948966 -3.5499373057390056e-309\natan0204 atan 0.0 1.0360465742258337e+308 -> 1.5707963267948966 9.6520757355646018e-309\natan0205 atan -0.0 1.0045063210373196e+308 -> -1.5707963267948966 9.955138947929503e-309\natan0206 atan 0.0 -9.5155296715763696e+307 -> 1.5707963267948966 -1.050913648020118e-308\natan0207 atan -0.0 -1.5565700490496501e+308 -> -1.5707963267948966 -6.4243816114189071e-309\natan0208 atan 1.2956339389525244e+308 0.0 -> 1.5707963267948966 0.0\natan0209 atan 1.4408126243772151e+308 -0.0 -> 1.5707963267948966 -0.0\natan0210 atan -1.0631786461936417e+308 0.0 -> -1.5707963267948966 0.0\natan0211 atan -1.0516056964171069e+308 -0.0 -> -1.5707963267948966 -0.0\natan0212 atan 1.236162319603838e+308 4.6827953496242936 -> 1.5707963267948966 0.0\natan0213 atan 7.000516472897218e+307 -5.8631608017844163 -> 1.5707963267948966 -0.0\natan0214 atan -1.5053444003338508e+308 5.1199197268420313 -> -1.5707963267948966 0.0\natan0215 atan -1.399172518147259e+308 -3.5687766472913673 -> -1.5707963267948966 -0.0\natan0216 atan 8.1252833070803021 6.2782953917343822e+307 -> 1.5707963267948966 1.5927890256908564e-308\natan0217 atan 2.8034285947515167 -1.3378049775753878e+308 -> 1.5707963267948966 -7.4749310756219562e-309\natan0218 atan -1.4073509988974953 1.6776381785968355e+308 -> -1.5707963267948966 5.9607608646364569e-309\natan0219 atan -2.7135551527592119 -1.281567445525738e+308 -> -1.5707963267948966 -7.8029447727565326e-309\n\n-- imaginary part = +/-1, real part tiny\natan0300 atan -1e-150 -1.0 -> -0.78539816339744828 -173.04045556483339\natan0301 atan 1e-155 1.0 -> 0.78539816339744828 178.79691829731851\natan0302 atan 9.9999999999999999e-161 -1.0 -> 0.78539816339744828 -184.55338102980363\natan0303 atan -1e-165 1.0 -> -0.78539816339744828 190.30984376228875\natan0304 atan -9.9998886718268301e-321 -1.0 -> -0.78539816339744828 -368.76019403576692\n\n-- Additional real values (mpmath)\natan0400 atan 1.7976931348623157e+308 0.0 -> 1.5707963267948966192 0.0\natan0401 atan -1.7976931348623157e+308 0.0 -> -1.5707963267948966192 0.0\natan0402 atan 1e-17 0.0 -> 1.0000000000000000715e-17 0.0\natan0403 atan -1e-17 0.0 -> -1.0000000000000000715e-17 0.0\natan0404 atan 0.0001 0.0 -> 0.000099999999666666673459 0.0\natan0405 atan -0.0001 0.0 -> -0.000099999999666666673459 0.0\natan0406 atan 0.999999999999999 0.0 -> 0.78539816339744781002 0.0\natan0407 atan 1.000000000000001 0.0 -> 0.78539816339744886473 0.0\natan0408 atan 14.101419947171719 0.0 -> 1.4999999999999999969 0.0\natan0409 atan 1255.7655915007897 0.0 -> 1.5700000000000000622 0.0\n\n-- special values\natan1000 atan -0.0 0.0 -> -0.0 0.0\natan1001 atan nan 0.0 -> nan 0.0\natan1002 atan -0.0 1.0 -> -0.0 inf divide-by-zero\natan1003 atan -inf 0.0 -> -1.5707963267948966 0.0\natan1004 atan -inf 2.2999999999999998 -> -1.5707963267948966 0.0\natan1005 atan nan 2.2999999999999998 -> nan nan\natan1006 atan -0.0 inf -> -1.5707963267948966 0.0\natan1007 atan -2.2999999999999998 inf -> -1.5707963267948966 0.0\natan1008 atan -inf inf -> -1.5707963267948966 0.0\natan1009 atan nan inf -> nan 0.0\natan1010 atan -0.0 nan -> nan nan\natan1011 atan -2.2999999999999998 nan -> nan nan\natan1012 atan -inf nan -> -1.5707963267948966 0.0 ignore-imag-sign\natan1013 atan nan nan -> nan nan\natan1014 atan 0.0 0.0 -> 0.0 0.0\natan1015 atan 0.0 1.0 -> 0.0 inf divide-by-zero\natan1016 atan inf 0.0 -> 1.5707963267948966 0.0\natan1017 atan inf 2.2999999999999998 -> 1.5707963267948966 0.0\natan1018 atan 0.0 inf -> 1.5707963267948966 0.0\natan1019 atan 2.2999999999999998 inf -> 1.5707963267948966 0.0\natan1020 atan inf inf -> 1.5707963267948966 0.0\natan1021 atan 0.0 nan -> nan nan\natan1022 atan 2.2999999999999998 nan -> nan nan\natan1023 atan inf nan -> 1.5707963267948966 0.0 ignore-imag-sign\natan1024 atan 0.0 -0.0 -> 0.0 -0.0\natan1025 atan nan -0.0 -> nan -0.0\natan1026 atan 0.0 -1.0 -> 0.0 -inf divide-by-zero\natan1027 atan inf -0.0 -> 1.5707963267948966 -0.0\natan1028 atan inf -2.2999999999999998 -> 1.5707963267948966 -0.0\natan1029 atan nan -2.2999999999999998 -> nan nan\natan1030 atan 0.0 -inf -> 1.5707963267948966 -0.0\natan1031 atan 2.2999999999999998 -inf -> 1.5707963267948966 -0.0\natan1032 atan inf -inf -> 1.5707963267948966 -0.0\natan1033 atan nan -inf -> nan -0.0\natan1034 atan -0.0 -0.0 -> -0.0 -0.0\natan1035 atan -0.0 -1.0 -> -0.0 -inf divide-by-zero\natan1036 atan -inf -0.0 -> -1.5707963267948966 -0.0\natan1037 atan -inf -2.2999999999999998 -> -1.5707963267948966 -0.0\natan1038 atan -0.0 -inf -> -1.5707963267948966 -0.0\natan1039 atan -2.2999999999999998 -inf -> -1.5707963267948966 -0.0\natan1040 atan -inf -inf -> -1.5707963267948966 -0.0\n\n\n---------------------------------------\n-- atanh: Inverse hyperbolic tangent --\n---------------------------------------\n\n-- zeros\n-- These are tested in testAtanhSign in test_cmath.py\n-- atanh0000 atanh 0.0 0.0 -> 0.0 0.0\n-- atanh0001 atanh 0.0 -0.0 -> 0.0 -0.0\n-- atanh0002 atanh -0.0 0.0 -> -0.0 0.0\n-- atanh0003 atanh -0.0 -0.0 -> -0.0 -0.0\n\n-- values along both sides of real axis\natanh0010 atanh -9.8813129168249309e-324 0.0 -> -9.8813129168249309e-324 0.0\natanh0011 atanh -9.8813129168249309e-324 -0.0 -> -9.8813129168249309e-324 -0.0\natanh0012 atanh -1e-305 0.0 -> -1e-305 0.0\natanh0013 atanh -1e-305 -0.0 -> -1e-305 -0.0\natanh0014 atanh -1e-150 0.0 -> -1e-150 0.0\natanh0015 atanh -1e-150 -0.0 -> -1e-150 -0.0\natanh0016 atanh -9.9999999999999998e-17 0.0 -> -9.9999999999999998e-17 0.0\natanh0017 atanh -9.9999999999999998e-17 -0.0 -> -9.9999999999999998e-17 -0.0\natanh0018 atanh -0.001 0.0 -> -0.0010000003333335333 0.0\natanh0019 atanh -0.001 -0.0 -> -0.0010000003333335333 -0.0\natanh0020 atanh -0.57899999999999996 0.0 -> -0.6609570902866303 0.0\natanh0021 atanh -0.57899999999999996 -0.0 -> -0.6609570902866303 -0.0\natanh0022 atanh -0.99999999999999989 0.0 -> -18.714973875118524 0.0\natanh0023 atanh -0.99999999999999989 -0.0 -> -18.714973875118524 -0.0\natanh0024 atanh -1.0000000000000002 0.0 -> -18.36840028483855 1.5707963267948966\natanh0025 atanh -1.0000000000000002 -0.0 -> -18.36840028483855 -1.5707963267948966\natanh0026 atanh -1.0009999999999999 0.0 -> -3.8007011672919218 1.5707963267948966\natanh0027 atanh -1.0009999999999999 -0.0 -> -3.8007011672919218 -1.5707963267948966\natanh0028 atanh -2.0 0.0 -> -0.54930614433405489 1.5707963267948966\natanh0029 atanh -2.0 -0.0 -> -0.54930614433405489 -1.5707963267948966\natanh0030 atanh -23.0 0.0 -> -0.043505688494814884 1.5707963267948966\natanh0031 atanh -23.0 -0.0 -> -0.043505688494814884 -1.5707963267948966\natanh0032 atanh -10000000000000000.0 0.0 -> -9.9999999999999998e-17 1.5707963267948966\natanh0033 atanh -10000000000000000.0 -0.0 -> -9.9999999999999998e-17 -1.5707963267948966\natanh0034 atanh -9.9999999999999998e+149 0.0 -> -1e-150 1.5707963267948966\natanh0035 atanh -9.9999999999999998e+149 -0.0 -> -1e-150 -1.5707963267948966\natanh0036 atanh -1.0000000000000001e+299 0.0 -> -9.9999999999999999e-300 1.5707963267948966\natanh0037 atanh -1.0000000000000001e+299 -0.0 -> -9.9999999999999999e-300 -1.5707963267948966\natanh0038 atanh 9.8813129168249309e-324 0.0 -> 9.8813129168249309e-324 0.0\natanh0039 atanh 9.8813129168249309e-324 -0.0 -> 9.8813129168249309e-324 -0.0\natanh0040 atanh 1e-305 0.0 -> 1e-305 0.0\natanh0041 atanh 1e-305 -0.0 -> 1e-305 -0.0\natanh0042 atanh 1e-150 0.0 -> 1e-150 0.0\natanh0043 atanh 1e-150 -0.0 -> 1e-150 -0.0\natanh0044 atanh 9.9999999999999998e-17 0.0 -> 9.9999999999999998e-17 0.0\natanh0045 atanh 9.9999999999999998e-17 -0.0 -> 9.9999999999999998e-17 -0.0\natanh0046 atanh 0.001 0.0 -> 0.0010000003333335333 0.0\natanh0047 atanh 0.001 -0.0 -> 0.0010000003333335333 -0.0\natanh0048 atanh 0.57899999999999996 0.0 -> 0.6609570902866303 0.0\natanh0049 atanh 0.57899999999999996 -0.0 -> 0.6609570902866303 -0.0\natanh0050 atanh 0.99999999999999989 0.0 -> 18.714973875118524 0.0\natanh0051 atanh 0.99999999999999989 -0.0 -> 18.714973875118524 -0.0\natanh0052 atanh 1.0000000000000002 0.0 -> 18.36840028483855 1.5707963267948966\natanh0053 atanh 1.0000000000000002 -0.0 -> 18.36840028483855 -1.5707963267948966\natanh0054 atanh 1.0009999999999999 0.0 -> 3.8007011672919218 1.5707963267948966\natanh0055 atanh 1.0009999999999999 -0.0 -> 3.8007011672919218 -1.5707963267948966\natanh0056 atanh 2.0 0.0 -> 0.54930614433405489 1.5707963267948966\natanh0057 atanh 2.0 -0.0 -> 0.54930614433405489 -1.5707963267948966\natanh0058 atanh 23.0 0.0 -> 0.043505688494814884 1.5707963267948966\natanh0059 atanh 23.0 -0.0 -> 0.043505688494814884 -1.5707963267948966\natanh0060 atanh 10000000000000000.0 0.0 -> 9.9999999999999998e-17 1.5707963267948966\natanh0061 atanh 10000000000000000.0 -0.0 -> 9.9999999999999998e-17 -1.5707963267948966\natanh0062 atanh 9.9999999999999998e+149 0.0 -> 1e-150 1.5707963267948966\natanh0063 atanh 9.9999999999999998e+149 -0.0 -> 1e-150 -1.5707963267948966\natanh0064 atanh 1.0000000000000001e+299 0.0 -> 9.9999999999999999e-300 1.5707963267948966\natanh0065 atanh 1.0000000000000001e+299 -0.0 -> 9.9999999999999999e-300 -1.5707963267948966\n\n-- random inputs\natanh0100 atanh -0.54460925980633501 -0.54038050126721027 -> -0.41984265808446974 -0.60354153938352828\natanh0101 atanh -1.6934614269829051 -0.48807386108113621 -> -0.58592769102243281 -1.3537837470975898\natanh0102 atanh -1.3467293985501207 -0.47868354895395876 -> -0.69961624370709985 -1.1994450156570076\natanh0103 atanh -5.6142232418984888 -544551613.39307702 -> -1.8932657550925744e-17 -1.5707963249585235\natanh0104 atanh -0.011841460381263651 -3.259978899823385 -> -0.0010183936547405188 -1.2731614020743838\natanh0105 atanh -0.0073345736950029532 0.35821949670922248 -> -0.0065004869024682466 0.34399359971920895\natanh0106 atanh -13.866782244320014 0.9541129545860273 -> -0.071896852055058899 1.5658322704631409\natanh0107 atanh -708.59964982780775 21.984802159266675 -> -0.0014098779074189741 1.5707525842838959\natanh0108 atanh -30.916832076030602 1.3691897138829843 -> -0.032292682045743676 1.5693652094847115\natanh0109 atanh -0.57461806339861754 0.29534797443913063 -> -0.56467464472482765 0.39615612824172625\natanh0110 atanh 0.40089246737415685 -1.632285984300659 -> 0.1063832707890608 -1.0402821335326482\natanh0111 atanh 2119.6167688262176 -1.5383653437377242e+17 -> 8.9565008518382049e-32 -1.5707963267948966\natanh0112 atanh 756.86017850941641 -6.6064087133223817 -> 0.0013211481136820046 -1.5707847948702234\natanh0113 atanh 4.0490617718041602 -2.5784456791040652e-12 -> 0.25218425538553618 -1.5707963267947291\natanh0114 atanh 10.589254957173523 -0.13956391149624509 -> 0.094700890282197664 -1.5695407140217623\natanh0115 atanh 1.0171187553160499 0.70766113465354019 -> 0.55260251975367791 0.96619711116641682\natanh0116 atanh 0.031645502527750849 0.067319983726544394 -> 0.031513018344086742 0.067285437670549036\natanh0117 atanh 0.13670177624994517 0.43240089361857947 -> 0.11538933151017253 0.41392008145336212\natanh0118 atanh 0.64173899243596688 2.9008577686695256 -> 0.065680142424134405 1.2518535724053921\natanh0119 atanh 0.19313813528025942 38.799619150741869 -> 0.00012820765917366644 1.5450292202823612\n\n-- values near infinity\natanh0200 atanh 5.3242646831347954e+307 1.3740396080084153e+308 -> 2.4519253616695576e-309 1.5707963267948966\natanh0201 atanh 1.158701641241358e+308 -6.5579268873375853e+307 -> 6.5365375267795098e-309 -1.5707963267948966\natanh0202 atanh -1.3435325735762247e+308 9.8947369259601547e+307 -> -4.8256680906589956e-309 1.5707963267948966\natanh0203 atanh -1.4359857522598942e+308 -9.4701204702391004e+307 -> -4.8531282262872645e-309 -1.5707963267948966\natanh0204 atanh 0.0 5.6614181068098497e+307 -> 0.0 1.5707963267948966\natanh0205 atanh -0.0 6.9813212721450139e+307 -> -0.0 1.5707963267948966\natanh0206 atanh 0.0 -7.4970613060311453e+307 -> 0.0 -1.5707963267948966\natanh0207 atanh -0.0 -1.5280601880314068e+308 -> -0.0 -1.5707963267948966\natanh0208 atanh 8.2219472336000745e+307 0.0 -> 1.2162568933954813e-308 1.5707963267948966\natanh0209 atanh 1.4811519617280899e+308 -0.0 -> 6.7515017083951325e-309 -1.5707963267948966\natanh0210 atanh -1.2282016263598785e+308 0.0 -> -8.1419856360537615e-309 1.5707963267948966\natanh0211 atanh -1.0616427760154426e+308 -0.0 -> -9.4193642399489563e-309 -1.5707963267948966\natanh0212 atanh 1.2971536510180682e+308 5.2847948452333293 -> 7.7091869510998328e-309 1.5707963267948966\natanh0213 atanh 1.1849860977411851e+308 -7.9781906447459949 -> 8.4389175696339014e-309 -1.5707963267948966\natanh0214 atanh -1.4029969422586635e+308 0.93891986543663375 -> -7.127599283218073e-309 1.5707963267948966\natanh0215 atanh -4.7508098912248211e+307 -8.2702421247039908 -> -2.1049042645278043e-308 -1.5707963267948966\natanh0216 atanh 8.2680742115769998 8.1153898410918065e+307 -> 0.0 1.5707963267948966\natanh0217 atanh 1.2575325146218885 -1.4746679147661649e+308 -> 0.0 -1.5707963267948966\natanh0218 atanh -2.4618803682310899 1.3781522717005568e+308 -> -0.0 1.5707963267948966\natanh0219 atanh -4.0952386694788112 -1.231083376353703e+308 -> -0.0 -1.5707963267948966\n\n-- values near 0\natanh0220 atanh 3.8017563659811628e-314 2.6635484239074319e-312 -> 3.8017563659811628e-314 2.6635484239074319e-312\natanh0221 atanh 1.7391110733611878e-321 -4.3547800672541419e-313 -> 1.7391110733611878e-321 -4.3547800672541419e-313\natanh0222 atanh -5.9656816081325078e-317 9.9692253555416263e-313 -> -5.9656816081325078e-317 9.9692253555416263e-313\natanh0223 atanh -6.5606671178400239e-313 -2.1680936406357335e-309 -> -6.5606671178400239e-313 -2.1680936406357335e-309\natanh0224 atanh 0.0 2.5230944401820779e-319 -> 0.0 2.5230944401820779e-319\natanh0225 atanh -0.0 5.6066569490064658e-320 -> -0.0 5.6066569490064658e-320\natanh0226 atanh 0.0 -2.4222487249468377e-317 -> 0.0 -2.4222487249468377e-317\natanh0227 atanh -0.0 -3.0861101089206037e-316 -> -0.0 -3.0861101089206037e-316\natanh0228 atanh 3.1219222884393986e-310 0.0 -> 3.1219222884393986e-310 0.0\natanh0229 atanh 9.8926337564976196e-309 -0.0 -> 9.8926337564976196e-309 -0.0\natanh0230 atanh -1.5462535092918154e-312 0.0 -> -1.5462535092918154e-312 0.0\natanh0231 atanh -9.8813129168249309e-324 -0.0 -> -9.8813129168249309e-324 -0.0\n\n-- real part = +/-1, imaginary part tiny\natanh0300 atanh 1.0 1e-153 -> 176.49433320432448 0.78539816339744828\natanh0301 atanh 1.0 9.9999999999999997e-155 -> 177.64562575082149 0.78539816339744828\natanh0302 atanh -1.0 1e-161 -> -185.70467357630065 0.78539816339744828\natanh0303 atanh 1.0 -1e-165 -> 190.30984376228875 -0.78539816339744828\natanh0304 atanh -1.0 -9.8813129168249309e-324 -> -372.22003596069061 -0.78539816339744828\n\n-- special values\natanh1000 atanh 0.0 0.0 -> 0.0 0.0\natanh1001 atanh 0.0 nan -> 0.0 nan\natanh1002 atanh 1.0 0.0 -> inf 0.0                      divide-by-zero\natanh1003 atanh 0.0 inf -> 0.0 1.5707963267948966\natanh1004 atanh 2.3 inf -> 0.0 1.5707963267948966\natanh1005 atanh 2.3 nan -> nan nan\natanh1006 atanh inf 0.0 -> 0.0 1.5707963267948966\natanh1007 atanh inf 2.3 -> 0.0 1.5707963267948966\natanh1008 atanh inf inf -> 0.0 1.5707963267948966\natanh1009 atanh inf nan -> 0.0 nan\natanh1010 atanh nan 0.0 -> nan nan\natanh1011 atanh nan 2.3 -> nan nan\natanh1012 atanh nan inf -> 0.0 1.5707963267948966       ignore-real-sign\natanh1013 atanh nan nan -> nan nan\natanh1014 atanh 0.0 -0.0 -> 0.0 -0.0\natanh1015 atanh 1.0 -0.0 -> inf -0.0                    divide-by-zero\natanh1016 atanh 0.0 -inf -> 0.0 -1.5707963267948966\natanh1017 atanh 2.3 -inf -> 0.0 -1.5707963267948966\natanh1018 atanh inf -0.0 -> 0.0 -1.5707963267948966\natanh1019 atanh inf -2.3 -> 0.0 -1.5707963267948966\natanh1020 atanh inf -inf -> 0.0 -1.5707963267948966\natanh1021 atanh nan -0.0 -> nan nan\natanh1022 atanh nan -2.3 -> nan nan\natanh1023 atanh nan -inf -> 0.0 -1.5707963267948966     ignore-real-sign\natanh1024 atanh -0.0 -0.0 -> -0.0 -0.0\natanh1025 atanh -0.0 nan -> -0.0 nan\natanh1026 atanh -1.0 -0.0 -> -inf -0.0                  divide-by-zero\natanh1027 atanh -0.0 -inf -> -0.0 -1.5707963267948966\natanh1028 atanh -2.3 -inf -> -0.0 -1.5707963267948966\natanh1029 atanh -2.3 nan -> nan nan\natanh1030 atanh -inf -0.0 -> -0.0 -1.5707963267948966\natanh1031 atanh -inf -2.3 -> -0.0 -1.5707963267948966\natanh1032 atanh -inf -inf -> -0.0 -1.5707963267948966\natanh1033 atanh -inf nan -> -0.0 nan\natanh1034 atanh -0.0 0.0 -> -0.0 0.0\natanh1035 atanh -1.0 0.0 -> -inf 0.0                    divide-by-zero\natanh1036 atanh -0.0 inf -> -0.0 1.5707963267948966\natanh1037 atanh -2.3 inf -> -0.0 1.5707963267948966\natanh1038 atanh -inf 0.0 -> -0.0 1.5707963267948966\natanh1039 atanh -inf 2.3 -> -0.0 1.5707963267948966\natanh1040 atanh -inf inf -> -0.0 1.5707963267948966\n\n\n----------------------------\n-- log: Natural logarithm --\n----------------------------\n\nlog0000 log 1.0 0.0 -> 0.0 0.0\nlog0001 log 1.0 -0.0 -> 0.0 -0.0\nlog0002 log -1.0 0.0 -> 0.0 3.1415926535897931\nlog0003 log -1.0 -0.0 -> 0.0 -3.1415926535897931\n-- values along both sides of real axis\nlog0010 log -9.8813129168249309e-324 0.0 -> -743.74692474082133 3.1415926535897931\nlog0011 log -9.8813129168249309e-324 -0.0 -> -743.74692474082133 -3.1415926535897931\nlog0012 log -1e-305 0.0 -> -702.28845336318398 3.1415926535897931\nlog0013 log -1e-305 -0.0 -> -702.28845336318398 -3.1415926535897931\nlog0014 log -1e-150 0.0 -> -345.38776394910684 3.1415926535897931\nlog0015 log -1e-150 -0.0 -> -345.38776394910684 -3.1415926535897931\nlog0016 log -9.9999999999999998e-17 0.0 -> -36.841361487904734 3.1415926535897931\nlog0017 log -9.9999999999999998e-17 -0.0 -> -36.841361487904734 -3.1415926535897931\nlog0018 log -0.001 0.0 -> -6.9077552789821368 3.1415926535897931\nlog0019 log -0.001 -0.0 -> -6.9077552789821368 -3.1415926535897931\nlog0020 log -0.57899999999999996 0.0 -> -0.54645280140914188 3.1415926535897931\nlog0021 log -0.57899999999999996 -0.0 -> -0.54645280140914188 -3.1415926535897931\nlog0022 log -0.99999999999999989 0.0 -> -1.1102230246251565e-16 3.1415926535897931\nlog0023 log -0.99999999999999989 -0.0 -> -1.1102230246251565e-16 -3.1415926535897931\nlog0024 log -1.0000000000000002 0.0 -> 2.2204460492503128e-16 3.1415926535897931\nlog0025 log -1.0000000000000002 -0.0 -> 2.2204460492503128e-16 -3.1415926535897931\nlog0026 log -1.0009999999999999 0.0 -> 0.00099950033308342321 3.1415926535897931\nlog0027 log -1.0009999999999999 -0.0 -> 0.00099950033308342321 -3.1415926535897931\nlog0028 log -2.0 0.0 -> 0.69314718055994529 3.1415926535897931\nlog0029 log -2.0 -0.0 -> 0.69314718055994529 -3.1415926535897931\nlog0030 log -23.0 0.0 -> 3.1354942159291497 3.1415926535897931\nlog0031 log -23.0 -0.0 -> 3.1354942159291497 -3.1415926535897931\nlog0032 log -10000000000000000.0 0.0 -> 36.841361487904734 3.1415926535897931\nlog0033 log -10000000000000000.0 -0.0 -> 36.841361487904734 -3.1415926535897931\nlog0034 log -9.9999999999999998e+149 0.0 -> 345.38776394910684 3.1415926535897931\nlog0035 log -9.9999999999999998e+149 -0.0 -> 345.38776394910684 -3.1415926535897931\nlog0036 log -1.0000000000000001e+299 0.0 -> 688.47294280521965 3.1415926535897931\nlog0037 log -1.0000000000000001e+299 -0.0 -> 688.47294280521965 -3.1415926535897931\nlog0038 log 9.8813129168249309e-324 0.0 -> -743.74692474082133 0.0\nlog0039 log 9.8813129168249309e-324 -0.0 -> -743.74692474082133 -0.0\nlog0040 log 1e-305 0.0 -> -702.28845336318398 0.0\nlog0041 log 1e-305 -0.0 -> -702.28845336318398 -0.0\nlog0042 log 1e-150 0.0 -> -345.38776394910684 0.0\nlog0043 log 1e-150 -0.0 -> -345.38776394910684 -0.0\nlog0044 log 9.9999999999999998e-17 0.0 -> -36.841361487904734 0.0\nlog0045 log 9.9999999999999998e-17 -0.0 -> -36.841361487904734 -0.0\nlog0046 log 0.001 0.0 -> -6.9077552789821368 0.0\nlog0047 log 0.001 -0.0 -> -6.9077552789821368 -0.0\nlog0048 log 0.57899999999999996 0.0 -> -0.54645280140914188 0.0\nlog0049 log 0.57899999999999996 -0.0 -> -0.54645280140914188 -0.0\nlog0050 log 0.99999999999999989 0.0 -> -1.1102230246251565e-16 0.0\nlog0051 log 0.99999999999999989 -0.0 -> -1.1102230246251565e-16 -0.0\nlog0052 log 1.0000000000000002 0.0 -> 2.2204460492503128e-16 0.0\nlog0053 log 1.0000000000000002 -0.0 -> 2.2204460492503128e-16 -0.0\nlog0054 log 1.0009999999999999 0.0 -> 0.00099950033308342321 0.0\nlog0055 log 1.0009999999999999 -0.0 -> 0.00099950033308342321 -0.0\nlog0056 log 2.0 0.0 -> 0.69314718055994529 0.0\nlog0057 log 2.0 -0.0 -> 0.69314718055994529 -0.0\nlog0058 log 23.0 0.0 -> 3.1354942159291497 0.0\nlog0059 log 23.0 -0.0 -> 3.1354942159291497 -0.0\nlog0060 log 10000000000000000.0 0.0 -> 36.841361487904734 0.0\nlog0061 log 10000000000000000.0 -0.0 -> 36.841361487904734 -0.0\nlog0062 log 9.9999999999999998e+149 0.0 -> 345.38776394910684 0.0\nlog0063 log 9.9999999999999998e+149 -0.0 -> 345.38776394910684 -0.0\nlog0064 log 1.0000000000000001e+299 0.0 -> 688.47294280521965 0.0\nlog0065 log 1.0000000000000001e+299 -0.0 -> 688.47294280521965 -0.0\n\n-- random inputs\nlog0066 log -1.9830454945186191e-16 -2.0334448025673346 -> 0.70973130194329803 -1.5707963267948968\nlog0067 log -0.96745853024741857 -0.84995816228299692 -> 0.25292811398722387 -2.4207570438536905\nlog0068 log -0.1603644313948418 -0.2929942111041835 -> -1.0965857872427374 -2.0715870859971419\nlog0069 log -0.15917913168438699 -0.25238799251132177 -> -1.2093477313249901 -2.1334784232033863\nlog0070 log -0.68907818535078802 -3.0693105853476346 -> 1.1460398629184565 -1.7916403813913211\nlog0071 log -17.268133447565589 6.8165120014604756 -> 2.9212694465974836 2.7656245081603164\nlog0072 log -1.7153894479690328 26.434055372802636 -> 3.2767542953718003 1.6355986276341734\nlog0073 log -8.0456794648936578e-06 0.19722758057570208 -> -1.6233969848296075 1.5708371206810101\nlog0074 log -2.4306442691323173 0.6846919750700996 -> 0.92633592001969589 2.8670160576718331\nlog0075 log -3.5488049250888194 0.45324040643185254 -> 1.2747008374256426 3.0145640007885111\nlog0076 log 0.18418516851510189 -0.26062518836212617 -> -1.1421287121940344 -0.95558440841183434\nlog0077 log 2.7124837795638399 -13.148769067133387 -> 2.5971659975706802 -1.3673583045209439\nlog0078 log 3.6521275476169149e-13 -3.7820543023170673e-05 -> -10.182658136741569 -1.5707963171384316\nlog0079 log 5.0877545813862239 -1.2834978326786852 -> 1.6576856213076328 -0.24711583497738485\nlog0080 log 0.26477986808461512 -0.67659001194187429 -> -0.31944085207999973 -1.197773671987121\nlog0081 log 0.0014754261398071962 5.3514691608205442 -> 1.6773711707153829 1.5705206219261802\nlog0082 log 0.29667334462157885 0.00020056045042584795 -> -1.2151233667079588 0.00067603114168689204\nlog0083 log 0.82104233671099425 3.9005387130133102 -> 1.3827918965299593 1.3633304701848363\nlog0084 log 0.27268135358180667 124.42088110945804 -> 4.8236724223559229 1.5686047258789015\nlog0085 log 0.0026286959168267485 0.47795808180573013 -> -0.73821712137809126 1.5652965360960087\n\n-- values near infinity\nlog0100 log 1.0512025744003172e+308 7.2621669750664611e+307 -> 709.44123967814494 0.60455434048332968\nlog0101 log 5.5344249034372126e+307 -1.2155859158431275e+308 -> 709.48562300345679 -1.143553056717973\nlog0102 log -1.3155575403469408e+308 1.1610793541663864e+308 -> 709.75847809546428 2.41848796504974\nlog0103 log -1.632366720973235e+308 -1.54299446211448e+308 -> 710.00545236515586 -2.3843326028455087\nlog0104 log 0.0 5.9449276692327712e+307 -> 708.67616191258526 1.5707963267948966\nlog0105 log -0.0 1.1201850459025692e+308 -> 709.30970253338171 1.5707963267948966\nlog0106 log 0.0 -1.6214225933466528e+308 -> 709.6795125501086 -1.5707963267948966\nlog0107 log -0.0 -1.7453269791591058e+308 -> 709.75315056087379 -1.5707963267948966\nlog0108 log 1.440860577601428e+308 0.0 -> 709.56144920058262 0.0\nlog0109 log 1.391515176148282e+308 -0.0 -> 709.52660185041327 -0.0\nlog0110 log -1.201354401295296e+308 0.0 -> 709.37965823023956 3.1415926535897931\nlog0111 log -1.6704337825976804e+308 -0.0 -> 709.70929198492399 -3.1415926535897931\nlog0112 log 7.2276974655190223e+307 7.94879711369164 -> 708.87154406512104 1.0997689307850458e-307\nlog0113 log 1.1207859593716076e+308 -6.1956200868221147 -> 709.31023883080104 -5.5279244310803286e-308\nlog0114 log -4.6678933874471045e+307 9.947107893220382 -> 708.43433142431388 3.1415926535897931\nlog0115 log -1.5108012453950142e+308 -5.3117197179375619 -> 709.60884877835008 -3.1415926535897931\nlog0116 log 7.4903750871504435 1.5320703776626352e+308 -> 709.62282865085137 1.5707963267948966\nlog0117 log 5.9760325525654778 -8.0149473997349123e+307 -> 708.97493177248396 -1.5707963267948966\nlog0118 log -7.880194206386629 1.7861845814767441e+308 -> 709.77629046837137 1.5707963267948966\nlog0119 log -9.886438993852865 -6.19235781080747e+307 -> 708.71693946977302 -1.5707963267948966\n\n-- values near 0\nlog0120 log 2.2996867579227779e-308 6.7861840770939125e-312 -> -708.36343567717392 0.00029509166223339815\nlog0121 log 6.9169190417774516e-323 -9.0414013188948118e-322 -> -739.22766796468386 -1.4944423210001669\nlog0122 log -1.5378064962914011e-316 1.8243628389354635e-310 -> -713.20014803142965 1.5707971697228842\nlog0123 log -2.3319898483706837e-321 -2.2358763941866371e-313 -> -719.9045008332522 -1.570796337224766\nlog0124 log 0.0 3.872770101081121e-315 -> -723.96033425374401 1.5707963267948966\nlog0125 log -0.0 9.6342800939043076e-322 -> -739.16707236281752 1.5707963267948966\nlog0126 log 0.0 -2.266099393427834e-308 -> -708.37814861757965 -1.5707963267948966\nlog0127 log -0.0 -2.1184695673766626e-315 -> -724.56361036731812 -1.5707963267948966\nlog0128 log 1.1363509854348671e-322 0.0 -> -741.30457770545206 0.0\nlog0129 log 3.5572726500569751e-322 -0.0 -> -740.16340580236522 -0.0\nlog0130 log -2.3696071074040593e-310 0.0 -> -712.93865466421641 3.1415926535897931\nlog0131 log -2.813283897266934e-317 -0.0 -> -728.88512203138862 -3.1415926535897931\n\n-- values near the unit circle\nlog0200 log -0.59999999999999998 0.80000000000000004 -> 2.2204460492503132e-17 2.2142974355881808\nlog0201 log 0.79999999999999993 0.60000000000000009 -> 6.1629758220391547e-33 0.64350110879328448\n\n-- special values\nlog1000 log -0.0 0.0 -> -inf 3.1415926535897931         divide-by-zero\nlog1001 log 0.0 0.0 -> -inf 0.0                         divide-by-zero\nlog1002 log 0.0 inf -> inf 1.5707963267948966\nlog1003 log 2.3 inf -> inf 1.5707963267948966\nlog1004 log -0.0 inf -> inf 1.5707963267948966\nlog1005 log -2.3 inf -> inf 1.5707963267948966\nlog1006 log 0.0 nan -> nan nan\nlog1007 log 2.3 nan -> nan nan\nlog1008 log -0.0 nan -> nan nan\nlog1009 log -2.3 nan -> nan nan\nlog1010 log -inf 0.0 -> inf 3.1415926535897931\nlog1011 log -inf 2.3 -> inf 3.1415926535897931\nlog1012 log inf 0.0 -> inf 0.0\nlog1013 log inf 2.3 -> inf 0.0\nlog1014 log -inf inf -> inf 2.3561944901923448\nlog1015 log inf inf -> inf 0.78539816339744828\nlog1016 log inf nan -> inf nan\nlog1017 log -inf nan -> inf nan\nlog1018 log nan 0.0 -> nan nan\nlog1019 log nan 2.3 -> nan nan\nlog1020 log nan inf -> inf nan\nlog1021 log nan nan -> nan nan\nlog1022 log -0.0 -0.0 -> -inf -3.1415926535897931       divide-by-zero\nlog1023 log 0.0 -0.0 -> -inf -0.0                       divide-by-zero\nlog1024 log 0.0 -inf -> inf -1.5707963267948966\nlog1025 log 2.3 -inf -> inf -1.5707963267948966\nlog1026 log -0.0 -inf -> inf -1.5707963267948966\nlog1027 log -2.3 -inf -> inf -1.5707963267948966\nlog1028 log -inf -0.0 -> inf -3.1415926535897931\nlog1029 log -inf -2.3 -> inf -3.1415926535897931\nlog1030 log inf -0.0 -> inf -0.0\nlog1031 log inf -2.3 -> inf -0.0\nlog1032 log -inf -inf -> inf -2.3561944901923448\nlog1033 log inf -inf -> inf -0.78539816339744828\nlog1034 log nan -0.0 -> nan nan\nlog1035 log nan -2.3 -> nan nan\nlog1036 log nan -inf -> inf nan\n\n\n------------------------------\n-- log10: Logarithm base 10 --\n------------------------------\n\nlogt0000 log10 1.0 0.0 -> 0.0 0.0\nlogt0001 log10 1.0 -0.0 -> 0.0 -0.0\nlogt0002 log10 -1.0 0.0 -> 0.0 1.3643763538418414\nlogt0003 log10 -1.0 -0.0 -> 0.0 -1.3643763538418414\n-- values along both sides of real axis\nlogt0010 log10 -9.8813129168249309e-324 0.0 -> -323.0051853474518 1.3643763538418414\nlogt0011 log10 -9.8813129168249309e-324 -0.0 -> -323.0051853474518 -1.3643763538418414\nlogt0012 log10 -1e-305 0.0 -> -305.0 1.3643763538418414\nlogt0013 log10 -1e-305 -0.0 -> -305.0 -1.3643763538418414\nlogt0014 log10 -1e-150 0.0 -> -150.0 1.3643763538418414\nlogt0015 log10 -1e-150 -0.0 -> -150.0 -1.3643763538418414\nlogt0016 log10 -9.9999999999999998e-17 0.0 -> -16.0 1.3643763538418414\nlogt0017 log10 -9.9999999999999998e-17 -0.0 -> -16.0 -1.3643763538418414\nlogt0018 log10 -0.001 0.0 -> -3.0 1.3643763538418414\nlogt0019 log10 -0.001 -0.0 -> -3.0 -1.3643763538418414\nlogt0020 log10 -0.57899999999999996 0.0 -> -0.23732143627256383 1.3643763538418414\nlogt0021 log10 -0.57899999999999996 -0.0 -> -0.23732143627256383 -1.3643763538418414\nlogt0022 log10 -0.99999999999999989 0.0 -> -4.821637332766436e-17 1.3643763538418414\nlogt0023 log10 -0.99999999999999989 -0.0 -> -4.821637332766436e-17 -1.3643763538418414\nlogt0024 log10 -1.0000000000000002 0.0 -> 9.6432746655328696e-17 1.3643763538418414\nlogt0025 log10 -1.0000000000000002 -0.0 -> 9.6432746655328696e-17 -1.3643763538418414\nlogt0026 log10 -1.0009999999999999 0.0 -> 0.0004340774793185929 1.3643763538418414\nlogt0027 log10 -1.0009999999999999 -0.0 -> 0.0004340774793185929 -1.3643763538418414\nlogt0028 log10 -2.0 0.0 -> 0.3010299956639812 1.3643763538418414\nlogt0029 log10 -2.0 -0.0 -> 0.3010299956639812 -1.3643763538418414\nlogt0030 log10 -23.0 0.0 -> 1.3617278360175928 1.3643763538418414\nlogt0031 log10 -23.0 -0.0 -> 1.3617278360175928 -1.3643763538418414\nlogt0032 log10 -10000000000000000.0 0.0 -> 16.0 1.3643763538418414\nlogt0033 log10 -10000000000000000.0 -0.0 -> 16.0 -1.3643763538418414\nlogt0034 log10 -9.9999999999999998e+149 0.0 -> 150.0 1.3643763538418414\nlogt0035 log10 -9.9999999999999998e+149 -0.0 -> 150.0 -1.3643763538418414\nlogt0036 log10 -1.0000000000000001e+299 0.0 -> 299.0 1.3643763538418414\nlogt0037 log10 -1.0000000000000001e+299 -0.0 -> 299.0 -1.3643763538418414\nlogt0038 log10 9.8813129168249309e-324 0.0 -> -323.0051853474518 0.0\nlogt0039 log10 9.8813129168249309e-324 -0.0 -> -323.0051853474518 -0.0\nlogt0040 log10 1e-305 0.0 -> -305.0 0.0\nlogt0041 log10 1e-305 -0.0 -> -305.0 -0.0\nlogt0042 log10 1e-150 0.0 -> -150.0 0.0\nlogt0043 log10 1e-150 -0.0 -> -150.0 -0.0\nlogt0044 log10 9.9999999999999998e-17 0.0 -> -16.0 0.0\nlogt0045 log10 9.9999999999999998e-17 -0.0 -> -16.0 -0.0\nlogt0046 log10 0.001 0.0 -> -3.0 0.0\nlogt0047 log10 0.001 -0.0 -> -3.0 -0.0\nlogt0048 log10 0.57899999999999996 0.0 -> -0.23732143627256383 0.0\nlogt0049 log10 0.57899999999999996 -0.0 -> -0.23732143627256383 -0.0\nlogt0050 log10 0.99999999999999989 0.0 -> -4.821637332766436e-17 0.0\nlogt0051 log10 0.99999999999999989 -0.0 -> -4.821637332766436e-17 -0.0\nlogt0052 log10 1.0000000000000002 0.0 -> 9.6432746655328696e-17 0.0\nlogt0053 log10 1.0000000000000002 -0.0 -> 9.6432746655328696e-17 -0.0\nlogt0054 log10 1.0009999999999999 0.0 -> 0.0004340774793185929 0.0\nlogt0055 log10 1.0009999999999999 -0.0 -> 0.0004340774793185929 -0.0\nlogt0056 log10 2.0 0.0 -> 0.3010299956639812 0.0\nlogt0057 log10 2.0 -0.0 -> 0.3010299956639812 -0.0\nlogt0058 log10 23.0 0.0 -> 1.3617278360175928 0.0\nlogt0059 log10 23.0 -0.0 -> 1.3617278360175928 -0.0\nlogt0060 log10 10000000000000000.0 0.0 -> 16.0 0.0\nlogt0061 log10 10000000000000000.0 -0.0 -> 16.0 -0.0\nlogt0062 log10 9.9999999999999998e+149 0.0 -> 150.0 0.0\nlogt0063 log10 9.9999999999999998e+149 -0.0 -> 150.0 -0.0\nlogt0064 log10 1.0000000000000001e+299 0.0 -> 299.0 0.0\nlogt0065 log10 1.0000000000000001e+299 -0.0 -> 299.0 -0.0\n\n-- random inputs\nlogt0066 log10 -1.9830454945186191e-16 -2.0334448025673346 -> 0.30823238806798503 -0.68218817692092071\nlogt0067 log10 -0.96745853024741857 -0.84995816228299692 -> 0.10984528422284802 -1.051321426174086\nlogt0068 log10 -0.1603644313948418 -0.2929942111041835 -> -0.47624115633305419 -0.89967884023059597\nlogt0069 log10 -0.15917913168438699 -0.25238799251132177 -> -0.52521304641665956 -0.92655790645688119\nlogt0070 log10 -0.68907818535078802 -3.0693105853476346 -> 0.4977187885066448 -0.77809953119328823\nlogt0071 log10 -17.268133447565589 6.8165120014604756 -> 1.2686912008098534 1.2010954629104202\nlogt0072 log10 -1.7153894479690328 26.434055372802636 -> 1.423076309032751 0.71033145859005309\nlogt0073 log10 -8.0456794648936578e-06 0.19722758057570208 -> -0.70503235244987561 0.68220589348055516\nlogt0074 log10 -2.4306442691323173 0.6846919750700996 -> 0.40230257845332595 1.2451292533748923\nlogt0075 log10 -3.5488049250888194 0.45324040643185254 -> 0.55359553977141063 1.3092085108866405\nlogt0076 log10 0.18418516851510189 -0.26062518836212617 -> -0.49602019732913638 -0.41500503556604301\nlogt0077 log10 2.7124837795638399 -13.148769067133387 -> 1.1279348613317008 -0.59383616643803216\nlogt0078 log10 3.6521275476169149e-13 -3.7820543023170673e-05 -> -4.4222722398941112 -0.68218817272717114\nlogt0079 log10 5.0877545813862239 -1.2834978326786852 -> 0.71992371806426847 -0.10732104352159283\nlogt0080 log10 0.26477986808461512 -0.67659001194187429 -> -0.13873139935281681 -0.52018649631300229\nlogt0081 log10 0.0014754261398071962 5.3514691608205442 -> 0.72847304354528819 0.6820684398178033\nlogt0082 log10 0.29667334462157885 0.00020056045042584795 -> -0.52772137299296806 0.00029359659442937261\nlogt0083 log10 0.82104233671099425 3.9005387130133102 -> 0.60053889028349361 0.59208690021184018\nlogt0084 log10 0.27268135358180667 124.42088110945804 -> 2.094894315538069 0.68123637673656989\nlogt0085 log10 0.0026286959168267485 0.47795808180573013 -> -0.32060362226100814 0.67979964816877081\n\n-- values near infinity\nlogt0100 log10 1.0512025744003172e+308 7.2621669750664611e+307 -> 308.10641562682065 0.26255461408256975\nlogt0101 log10 5.5344249034372126e+307 -1.2155859158431275e+308 -> 308.12569106009209 -0.496638782296212\nlogt0102 log10 -1.3155575403469408e+308 1.1610793541663864e+308 -> 308.24419052091019 1.0503359777705266\nlogt0103 log10 -1.632366720973235e+308 -1.54299446211448e+308 -> 308.3514500834093 -1.0355024924378222\nlogt0104 log10 0.0 5.9449276692327712e+307 -> 307.77414657501117 0.68218817692092071\nlogt0105 log10 -0.0 1.1201850459025692e+308 -> 308.04928977068465 0.68218817692092071\nlogt0106 log10 0.0 -1.6214225933466528e+308 -> 308.20989622030174 -0.68218817692092071\nlogt0107 log10 -0.0 -1.7453269791591058e+308 -> 308.24187680203539 -0.68218817692092071\nlogt0108 log10 1.440860577601428e+308 0.0 -> 308.15862195908755 0.0\nlogt0109 log10 1.391515176148282e+308 -0.0 -> 308.14348794720007 -0.0\nlogt0110 log10 -1.201354401295296e+308 0.0 -> 308.07967114380773 1.3643763538418414\nlogt0111 log10 -1.6704337825976804e+308 -0.0 -> 308.22282926451624 -1.3643763538418414\nlogt0112 log10 7.2276974655190223e+307 7.94879711369164 -> 307.85899996571993 4.7762357800858463e-308\nlogt0113 log10 1.1207859593716076e+308 -6.1956200868221147 -> 308.04952268169455 -2.4007470767963597e-308\nlogt0114 log10 -4.6678933874471045e+307 9.947107893220382 -> 307.66912092839902 1.3643763538418414\nlogt0115 log10 -1.5108012453950142e+308 -5.3117197179375619 -> 308.1792073341565 -1.3643763538418414\nlogt0116 log10 7.4903750871504435 1.5320703776626352e+308 -> 308.18527871564157 0.68218817692092071\nlogt0117 log10 5.9760325525654778 -8.0149473997349123e+307 -> 307.90390067652424 -0.68218817692092071\nlogt0118 log10 -7.880194206386629 1.7861845814767441e+308 -> 308.25192633617331 0.68218817692092071\nlogt0119 log10 -9.886438993852865 -6.19235781080747e+307 -> 307.79185604308338 -0.68218817692092071\n\n-- values near 0\nlogt0120 log10 2.2996867579227779e-308 6.7861840770939125e-312 -> -307.63833129662572 0.00012815668056362305\nlogt0121 log10 6.9169190417774516e-323 -9.0414013188948118e-322 -> -321.04249706727148 -0.64902805353306059\nlogt0122 log10 -1.5378064962914011e-316 1.8243628389354635e-310 -> -309.73888878263222 0.68218854299989429\nlogt0123 log10 -2.3319898483706837e-321 -2.2358763941866371e-313 -> -312.65055220919641 -0.68218818145055538\nlogt0124 log10 0.0 3.872770101081121e-315 -> -314.41197828323476 0.68218817692092071\nlogt0125 log10 -0.0 9.6342800939043076e-322 -> -321.01618073175331 0.68218817692092071\nlogt0126 log10 0.0 -2.266099393427834e-308 -> -307.64472104545649 -0.68218817692092071\nlogt0127 log10 -0.0 -2.1184695673766626e-315 -> -314.67397777042407 -0.68218817692092071\nlogt0128 log10 1.1363509854348671e-322 0.0 -> -321.94448750709819 0.0\nlogt0129 log10 3.5572726500569751e-322 -0.0 -> -321.44888284668451 -0.0\nlogt0130 log10 -2.3696071074040593e-310 0.0 -> -309.62532365619722 1.3643763538418414\nlogt0131 log10 -2.813283897266934e-317 -0.0 -> -316.55078643961042 -1.3643763538418414\n\n-- values near the unit circle\nlogt0200 log10 -0.59999999999999998 0.80000000000000004 -> 9.6432746655328709e-18 0.96165715756846815\nlogt0201 log10 0.79999999999999993 0.60000000000000009 -> 2.6765463916147622e-33 0.2794689806475476\n\n-- special values\nlogt1000 log10 -0.0 0.0 -> -inf 1.3643763538418414         divide-by-zero\nlogt1001 log10 0.0 0.0 -> -inf 0.0                         divide-by-zero\nlogt1002 log10 0.0 inf -> inf 0.68218817692092071\nlogt1003 log10 2.3 inf -> inf 0.68218817692092071\nlogt1004 log10 -0.0 inf -> inf 0.68218817692092071\nlogt1005 log10 -2.3 inf -> inf 0.68218817692092071\nlogt1006 log10 0.0 nan -> nan nan\nlogt1007 log10 2.3 nan -> nan nan\nlogt1008 log10 -0.0 nan -> nan nan\nlogt1009 log10 -2.3 nan -> nan nan\nlogt1010 log10 -inf 0.0 -> inf 1.3643763538418414\nlogt1011 log10 -inf 2.3 -> inf 1.3643763538418414\nlogt1012 log10 inf 0.0 -> inf 0.0\nlogt1013 log10 inf 2.3 -> inf 0.0\nlogt1014 log10 -inf inf -> inf 1.0232822653813811\nlogt1015 log10 inf inf -> inf 0.34109408846046035\nlogt1016 log10 inf nan -> inf nan\nlogt1017 log10 -inf nan -> inf nan\nlogt1018 log10 nan 0.0 -> nan nan\nlogt1019 log10 nan 2.3 -> nan nan\nlogt1020 log10 nan inf -> inf nan\nlogt1021 log10 nan nan -> nan nan\nlogt1022 log10 -0.0 -0.0 -> -inf -1.3643763538418414       divide-by-zero\nlogt1023 log10 0.0 -0.0 -> -inf -0.0                       divide-by-zero\nlogt1024 log10 0.0 -inf -> inf -0.68218817692092071\nlogt1025 log10 2.3 -inf -> inf -0.68218817692092071\nlogt1026 log10 -0.0 -inf -> inf -0.68218817692092071\nlogt1027 log10 -2.3 -inf -> inf -0.68218817692092071\nlogt1028 log10 -inf -0.0 -> inf -1.3643763538418414\nlogt1029 log10 -inf -2.3 -> inf -1.3643763538418414\nlogt1030 log10 inf -0.0 -> inf -0.0\nlogt1031 log10 inf -2.3 -> inf -0.0\nlogt1032 log10 -inf -inf -> inf -1.0232822653813811\nlogt1033 log10 inf -inf -> inf -0.34109408846046035\nlogt1034 log10 nan -0.0 -> nan nan\nlogt1035 log10 nan -2.3 -> nan nan\nlogt1036 log10 nan -inf -> inf nan\n\n\n-----------------------\n-- sqrt: Square root --\n-----------------------\n\n-- zeros\nsqrt0000 sqrt 0.0 0.0 -> 0.0 0.0\nsqrt0001 sqrt 0.0 -0.0 -> 0.0 -0.0\nsqrt0002 sqrt -0.0 0.0 -> 0.0 0.0\nsqrt0003 sqrt -0.0 -0.0 -> 0.0 -0.0\n\n-- values along both sides of real axis\nsqrt0010 sqrt -9.8813129168249309e-324 0.0 -> 0.0 3.1434555694052576e-162\nsqrt0011 sqrt -9.8813129168249309e-324 -0.0 -> 0.0 -3.1434555694052576e-162\nsqrt0012 sqrt -1e-305 0.0 -> 0.0 3.1622776601683791e-153\nsqrt0013 sqrt -1e-305 -0.0 -> 0.0 -3.1622776601683791e-153\nsqrt0014 sqrt -1e-150 0.0 -> 0.0 9.9999999999999996e-76\nsqrt0015 sqrt -1e-150 -0.0 -> 0.0 -9.9999999999999996e-76\nsqrt0016 sqrt -9.9999999999999998e-17 0.0 -> 0.0 1e-08\nsqrt0017 sqrt -9.9999999999999998e-17 -0.0 -> 0.0 -1e-08\nsqrt0018 sqrt -0.001 0.0 -> 0.0 0.031622776601683791\nsqrt0019 sqrt -0.001 -0.0 -> 0.0 -0.031622776601683791\nsqrt0020 sqrt -0.57899999999999996 0.0 -> 0.0 0.76092049518987193\nsqrt0021 sqrt -0.57899999999999996 -0.0 -> 0.0 -0.76092049518987193\nsqrt0022 sqrt -0.99999999999999989 0.0 -> 0.0 0.99999999999999989\nsqrt0023 sqrt -0.99999999999999989 -0.0 -> 0.0 -0.99999999999999989\nsqrt0024 sqrt -1.0000000000000002 0.0 -> 0.0 1.0\nsqrt0025 sqrt -1.0000000000000002 -0.0 -> 0.0 -1.0\nsqrt0026 sqrt -1.0009999999999999 0.0 -> 0.0 1.000499875062461\nsqrt0027 sqrt -1.0009999999999999 -0.0 -> 0.0 -1.000499875062461\nsqrt0028 sqrt -2.0 0.0 -> 0.0 1.4142135623730951\nsqrt0029 sqrt -2.0 -0.0 -> 0.0 -1.4142135623730951\nsqrt0030 sqrt -23.0 0.0 -> 0.0 4.7958315233127191\nsqrt0031 sqrt -23.0 -0.0 -> 0.0 -4.7958315233127191\nsqrt0032 sqrt -10000000000000000.0 0.0 -> 0.0 100000000.0\nsqrt0033 sqrt -10000000000000000.0 -0.0 -> 0.0 -100000000.0\nsqrt0034 sqrt -9.9999999999999998e+149 0.0 -> 0.0 9.9999999999999993e+74\nsqrt0035 sqrt -9.9999999999999998e+149 -0.0 -> 0.0 -9.9999999999999993e+74\nsqrt0036 sqrt -1.0000000000000001e+299 0.0 -> 0.0 3.1622776601683796e+149\nsqrt0037 sqrt -1.0000000000000001e+299 -0.0 -> 0.0 -3.1622776601683796e+149\nsqrt0038 sqrt 9.8813129168249309e-324 0.0 -> 3.1434555694052576e-162 0.0\nsqrt0039 sqrt 9.8813129168249309e-324 -0.0 -> 3.1434555694052576e-162 -0.0\nsqrt0040 sqrt 1e-305 0.0 -> 3.1622776601683791e-153 0.0\nsqrt0041 sqrt 1e-305 -0.0 -> 3.1622776601683791e-153 -0.0\nsqrt0042 sqrt 1e-150 0.0 -> 9.9999999999999996e-76 0.0\nsqrt0043 sqrt 1e-150 -0.0 -> 9.9999999999999996e-76 -0.0\nsqrt0044 sqrt 9.9999999999999998e-17 0.0 -> 1e-08 0.0\nsqrt0045 sqrt 9.9999999999999998e-17 -0.0 -> 1e-08 -0.0\nsqrt0046 sqrt 0.001 0.0 -> 0.031622776601683791 0.0\nsqrt0047 sqrt 0.001 -0.0 -> 0.031622776601683791 -0.0\nsqrt0048 sqrt 0.57899999999999996 0.0 -> 0.76092049518987193 0.0\nsqrt0049 sqrt 0.57899999999999996 -0.0 -> 0.76092049518987193 -0.0\nsqrt0050 sqrt 0.99999999999999989 0.0 -> 0.99999999999999989 0.0\nsqrt0051 sqrt 0.99999999999999989 -0.0 -> 0.99999999999999989 -0.0\nsqrt0052 sqrt 1.0000000000000002 0.0 -> 1.0 0.0\nsqrt0053 sqrt 1.0000000000000002 -0.0 -> 1.0 -0.0\nsqrt0054 sqrt 1.0009999999999999 0.0 -> 1.000499875062461 0.0\nsqrt0055 sqrt 1.0009999999999999 -0.0 -> 1.000499875062461 -0.0\nsqrt0056 sqrt 2.0 0.0 -> 1.4142135623730951 0.0\nsqrt0057 sqrt 2.0 -0.0 -> 1.4142135623730951 -0.0\nsqrt0058 sqrt 23.0 0.0 -> 4.7958315233127191 0.0\nsqrt0059 sqrt 23.0 -0.0 -> 4.7958315233127191 -0.0\nsqrt0060 sqrt 10000000000000000.0 0.0 -> 100000000.0 0.0\nsqrt0061 sqrt 10000000000000000.0 -0.0 -> 100000000.0 -0.0\nsqrt0062 sqrt 9.9999999999999998e+149 0.0 -> 9.9999999999999993e+74 0.0\nsqrt0063 sqrt 9.9999999999999998e+149 -0.0 -> 9.9999999999999993e+74 -0.0\nsqrt0064 sqrt 1.0000000000000001e+299 0.0 -> 3.1622776601683796e+149 0.0\nsqrt0065 sqrt 1.0000000000000001e+299 -0.0 -> 3.1622776601683796e+149 -0.0\n\n-- random inputs\nsqrt0100 sqrt -0.34252542541549913 -223039880.15076211 -> 10560.300180587592 -10560.300196805192\nsqrt0101 sqrt -0.88790791393018909 -5.3307751730827402 -> 1.5027154613689004 -1.7737140896343291\nsqrt0102 sqrt -113916.89291310767 -0.018143374626153858 -> 2.6877817875351178e-05 -337.51576691038952\nsqrt0103 sqrt -0.63187172386197121 -0.26293913366617694 -> 0.16205707495266153 -0.81125471918761971\nsqrt0104 sqrt -0.058185169308906215 -2.3548312990430991 -> 1.0717660342420072 -1.0985752598086966\nsqrt0105 sqrt -1.0580584765935896 0.14400319259151736 -> 0.069837489270111242 1.030987755262468\nsqrt0106 sqrt -1.1667595947504932 0.11159711473953678 -> 0.051598531319315251 1.0813981705111229\nsqrt0107 sqrt -0.5123728411449906 0.026175433648339085 -> 0.018278026262418718 0.71603556293597614\nsqrt0108 sqrt -3.7453400060067228 1.0946500314809635 -> 0.27990088541692498 1.9554243814742367\nsqrt0109 sqrt -0.0027736121575097673 1.0367943000839817 -> 0.71903560338719175 0.72096172651250545\nsqrt0110 sqrt 1501.2559699453188 -1.1997325207283589 -> 38.746047664730959 -0.015481998720355024\nsqrt0111 sqrt 1.4830075326850578 -0.64100878436755349 -> 1.244712815741096 -0.25749264258434584\nsqrt0112 sqrt 0.095395618499734602 -0.48226565701639595 -> 0.54175904053472879 -0.44509239434231551\nsqrt0113 sqrt 0.50109185681863277 -0.54054037379892561 -> 0.7868179858332387 -0.34349772344520979\nsqrt0114 sqrt 0.98779807595367897 -0.00019848758437225191 -> 0.99388031770665153 -9.9854872279921968e-05\nsqrt0115 sqrt 11.845472380792259 0.0010051104581506761 -> 3.4417252072345397 0.00014601840612346451\nsqrt0116 sqrt 2.3558249686735975 0.25605157371744403 -> 1.5371278477386647 0.083288964575761404\nsqrt0117 sqrt 0.77584894123159098 1.0496420627016076 -> 1.0200744386390885 0.51449287568756552\nsqrt0118 sqrt 1.8961715669604893 0.34940793467158854 -> 1.3827991781411615 0.12634080935066902\nsqrt0119 sqrt 0.96025378316565801 0.69573224860140515 -> 1.0358710342209998 0.33581991658093457\n\n-- values near 0\nsqrt0120 sqrt 7.3577938365086866e-313 8.1181408465112743e-319 -> 8.5777583531543516e-157 4.732087634251168e-163\nsqrt0121 sqrt 1.2406883874892108e-310 -5.1210133324269776e-312 -> 1.1140990057468052e-155 -2.2982756945349973e-157\nsqrt0122 sqrt -7.1145453001139502e-322 2.9561379244703735e-314 -> 1.2157585807480286e-157 1.2157586100077242e-157\nsqrt0123 sqrt -4.9963244206801218e-314 -8.4718424423690227e-319 -> 1.8950582312540437e-162 -2.2352459419578971e-157\nsqrt0124 sqrt 0.0 7.699553609385195e-318 -> 1.9620848107797476e-159 1.9620848107797476e-159\nsqrt0125 sqrt -0.0 3.3900826606499415e-309 -> 4.1170879639922327e-155 4.1170879639922327e-155\nsqrt0126 sqrt 0.0 -9.8907989772250828e-319 -> 7.032353438652342e-160 -7.032353438652342e-160\nsqrt0127 sqrt -0.0 -1.3722939367590908e-315 -> 2.6194407196566702e-158 -2.6194407196566702e-158\nsqrt0128 sqrt 7.9050503334599447e-323 0.0 -> 8.8910349979403099e-162 0.0\nsqrt0129 sqrt 1.8623241768349486e-309 -0.0 -> 4.3154654173506579e-155 -0.0\nsqrt0130 sqrt -2.665971134499887e-308 0.0 -> 0.0 1.6327801856036491e-154\nsqrt0131 sqrt -1.5477066694467245e-310 -0.0 -> 0.0 -1.2440685951533077e-155\n\n-- inputs whose absolute value overflows\nsqrt0140 sqrt 1.6999999999999999e+308 -1.6999999999999999e+308 -> 1.4325088230154573e+154 -5.9336458271212207e+153\nsqrt0141 sqrt -1.797e+308 -9.9999999999999999e+306 -> 3.7284476432057307e+152 -1.3410406899802901e+154\n\n-- Additional real values (mpmath)\nsqrt0150 sqrt 1.7976931348623157e+308 0.0 -> 1.3407807929942596355e+154 0.0\nsqrt0151 sqrt 2.2250738585072014e-308 0.0 -> 1.4916681462400413487e-154 0.0\nsqrt0152 sqrt 5e-324 0.0 -> 2.2227587494850774834e-162 0.0\n\n-- special values\nsqrt1000 sqrt 0.0 0.0 -> 0.0 0.0\nsqrt1001 sqrt -0.0 0.0 -> 0.0 0.0\nsqrt1002 sqrt 0.0 inf -> inf inf\nsqrt1003 sqrt 2.3 inf -> inf inf\nsqrt1004 sqrt inf inf -> inf inf\nsqrt1005 sqrt -0.0 inf -> inf inf\nsqrt1006 sqrt -2.3 inf -> inf inf\nsqrt1007 sqrt -inf inf -> inf inf\nsqrt1008 sqrt nan inf -> inf inf\nsqrt1009 sqrt 0.0 nan -> nan nan\nsqrt1010 sqrt 2.3 nan -> nan nan\nsqrt1011 sqrt -0.0 nan -> nan nan\nsqrt1012 sqrt -2.3 nan -> nan nan\nsqrt1013 sqrt -inf 0.0 -> 0.0 inf\nsqrt1014 sqrt -inf 2.3 -> 0.0 inf\nsqrt1015 sqrt inf 0.0 -> inf 0.0\nsqrt1016 sqrt inf 2.3 -> inf 0.0\nsqrt1017 sqrt -inf nan -> nan inf       ignore-imag-sign\nsqrt1018 sqrt inf nan -> inf nan\nsqrt1019 sqrt nan 0.0 -> nan nan\nsqrt1020 sqrt nan 2.3 -> nan nan\nsqrt1021 sqrt nan nan -> nan nan\nsqrt1022 sqrt 0.0 -0.0 -> 0.0 -0.0\nsqrt1023 sqrt -0.0 -0.0 -> 0.0 -0.0\nsqrt1024 sqrt 0.0 -inf -> inf -inf\nsqrt1025 sqrt 2.3 -inf -> inf -inf\nsqrt1026 sqrt inf -inf -> inf -inf\nsqrt1027 sqrt -0.0 -inf -> inf -inf\nsqrt1028 sqrt -2.3 -inf -> inf -inf\nsqrt1029 sqrt -inf -inf -> inf -inf\nsqrt1030 sqrt nan -inf -> inf -inf\nsqrt1031 sqrt -inf -0.0 -> 0.0 -inf\nsqrt1032 sqrt -inf -2.3 -> 0.0 -inf\nsqrt1033 sqrt inf -0.0 -> inf -0.0\nsqrt1034 sqrt inf -2.3 -> inf -0.0\nsqrt1035 sqrt nan -0.0 -> nan nan\nsqrt1036 sqrt nan -2.3 -> nan nan\n\n\n-- For exp, cosh, sinh, tanh we limit tests to arguments whose\n-- imaginary part is less than 10 in absolute value:  most math\n-- libraries have poor accuracy for (real) sine and cosine for\n-- large arguments, and the accuracy of these complex functions\n-- suffer correspondingly.\n--\n-- Similarly, for cos, sin and tan we limit tests to arguments\n-- with relatively small real part.\n\n\n-------------------------------\n-- exp: Exponential function --\n-------------------------------\n\n-- zeros\nexp0000 exp 0.0 0.0 -> 1.0 0.0\nexp0001 exp 0.0 -0.0 -> 1.0 -0.0\nexp0002 exp -0.0 0.0 -> 1.0 0.0\nexp0003 exp -0.0 -0.0 -> 1.0 -0.0\n\n-- random inputs\nexp0004 exp -17.957359009564684 -1.108613895795274 -> 7.0869292576226611e-09 -1.4225929202377833e-08\nexp0005 exp -1.4456149663368642e-15 -0.75359817331772239 -> 0.72923148323917997 -0.68426708517419033\nexp0006 exp -0.76008654883512661 -0.46657235480105019 -> 0.41764393109928666 -0.21035108396792854\nexp0007 exp -5.7071614697735731 -2.3744161818115816e-11 -> 0.0033220890242068356 -7.8880219364953578e-14\nexp0008 exp -0.4653981327927097 -5.2236706667445587e-21 -> 0.62788507378216663 -3.2798648420026468e-21\nexp0009 exp -3.2444565242295518 1.1535625304243959 -> 0.015799936931457641 0.035644950380024749\nexp0010 exp -3.0651456337977727 0.87765086532391878 -> 0.029805595629855953 0.035882775180855669\nexp0011 exp -0.11080823753233926 0.96486386300873106 -> 0.50979112534376314 0.73575512419561562\nexp0012 exp -2.5629722598928648 0.019636235754708079 -> 0.077060452853917397 0.0015133717341137684\nexp0013 exp -3.3201709957983357e-10 1.2684017344487268 -> 0.29780699855434889 0.95462610007689186\nexp0014 exp 0.88767276057993272 -0.18953422986895557 -> 2.3859624049858095 -0.45771559132044426\nexp0015 exp 1.5738333486794742 -2.2576803075544328e-11 -> 4.8251091132458654 -1.0893553826776623e-10\nexp0016 exp 1.6408702341813795 -1.438879484380837 -> 0.6786733590689048 -5.1148284173168825\nexp0017 exp 1.820279424202033 -0.020812040370785722 -> 6.1722462896420902 -0.1284755888435051\nexp0018 exp 1.7273965735945873 -0.61140621328954947 -> 4.6067931898799976 -3.2294267694441308\nexp0019 exp 2.5606034306862995 0.098153136008435504 -> 12.881325889966629 1.2684184812864494\nexp0020 exp 10.280368619483029 3.4564622559748535 -> -27721.283321551502 -9028.9663215568835\nexp0021 exp 1.104007405129741e-155 0.21258803067317278 -> 0.97748813933531764 0.21099037290544478\nexp0022 exp 0.027364777809295172 0.00059226603500623363 -> 1.0277424518451876 0.0006086970181346579\nexp0023 exp 0.94356313429255245 3.418530463518592 -> -2.4712285695346194 -0.70242654900218349\n\n-- cases where exp(z) representable, exp(z.real) not\nexp0030 exp 710.0 0.78500000000000003 -> 1.5803016909637158e+308 1.5790437551806911e+308\nexp0031 exp 710.0 -0.78500000000000003 -> 1.5803016909637158e+308 -1.5790437551806911e+308\n\n-- values for which exp(x) is subnormal, or underflows to 0\nexp0040 exp -735.0 0.78500000000000003 -> 4.3976783136329355e-320 4.3942198541120468e-320\nexp0041 exp -735.0 -2.3559999999999999 -> -4.3952079854037293e-320 -4.396690182341253e-320\nexp0042 exp -745.0 0.0 -> 4.9406564584124654e-324 0.0\nexp0043 exp -745.0 0.7 -> 0.0 0.0\nexp0044 exp -745.0 2.1 -> -0.0 0.0\nexp0045 exp -745.0 3.7 -> -0.0 -0.0\nexp0046 exp -745.0 5.3 -> 0.0 -0.0\n\n-- values for which exp(z) overflows\nexp0050 exp 710.0 0.0 -> inf 0.0                        overflow\nexp0051 exp 711.0 0.7 -> inf inf                        overflow\nexp0052 exp 710.0 1.5 -> 1.5802653829857376e+307 inf    overflow\nexp0053 exp 710.0 1.6 -> -6.5231579995501372e+306 inf   overflow\nexp0054 exp 710.0 2.8 -> -inf 7.4836177417448528e+307   overflow\n\n-- Additional real values (mpmath)\nexp0070 exp 1e-08 0.0 -> 1.00000001000000005 0.0\nexp0071 exp 0.0003 0.0 -> 1.0003000450045003375 0.0\nexp0072 exp 0.2 0.0 -> 1.2214027581601698475 0.0\nexp0073 exp 1.0 0.0 -> 2.7182818284590452354 0.0\nexp0074 exp -1e-08 0.0 -> 0.99999999000000005 0.0\nexp0075 exp -0.0003 0.0 -> 0.99970004499550033751 0.0\nexp0076 exp -1.0 0.0 -> 0.3678794411714423216 0.0\nexp0077 exp 2.220446049250313e-16 0.0 -> 1.000000000000000222 0.0\nexp0078 exp -1.1102230246251565e-16 0.0 -> 0.99999999999999988898 0.0\nexp0079 exp 2.302585092994046 0.0 -> 10.000000000000002171 0.0\nexp0080 exp -2.302585092994046 0.0 -> 0.099999999999999978292 0.0\nexp0081 exp 709.7827 0.0 -> 1.7976699566638014654e+308 0.0\n\n-- special values\nexp1000 exp 0.0 0.0 -> 1.0 0.0\nexp1001 exp -0.0 0.0 -> 1.0 0.0\nexp1002 exp 0.0 inf -> nan nan          invalid\nexp1003 exp 2.3 inf -> nan nan          invalid\nexp1004 exp -0.0 inf -> nan nan         invalid\nexp1005 exp -2.3 inf -> nan nan         invalid\nexp1006 exp 0.0 nan -> nan nan\nexp1007 exp 2.3 nan -> nan nan\nexp1008 exp -0.0 nan -> nan nan\nexp1009 exp -2.3 nan -> nan nan\nexp1010 exp -inf 0.0 -> 0.0 0.0\nexp1011 exp -inf 1.4 -> 0.0 0.0\nexp1012 exp -inf 2.8 -> -0.0 0.0\nexp1013 exp -inf 4.2 -> -0.0 -0.0\nexp1014 exp -inf 5.6 -> 0.0 -0.0\nexp1015 exp -inf 7.0 -> 0.0 0.0\nexp1016 exp inf 0.0 -> inf 0.0\nexp1017 exp inf 1.4 -> inf inf\nexp1018 exp inf 2.8 -> -inf inf\nexp1019 exp inf 4.2 -> -inf -inf\nexp1020 exp inf 5.6 -> inf -inf\nexp1021 exp inf 7.0 -> inf inf\nexp1022 exp -inf inf -> 0.0 0.0         ignore-real-sign ignore-imag-sign\nexp1023 exp inf inf -> inf nan          invalid ignore-real-sign\nexp1024 exp -inf nan -> 0.0 0.0         ignore-real-sign ignore-imag-sign\nexp1025 exp inf nan -> inf nan          ignore-real-sign\nexp1026 exp nan 0.0 -> nan 0.0\nexp1027 exp nan 2.3 -> nan nan\nexp1028 exp nan inf -> nan nan\nexp1029 exp nan nan -> nan nan\nexp1030 exp 0.0 -0.0 -> 1.0 -0.0\nexp1031 exp -0.0 -0.0 -> 1.0 -0.0\nexp1032 exp 0.0 -inf -> nan nan         invalid\nexp1033 exp 2.3 -inf -> nan nan         invalid\nexp1034 exp -0.0 -inf -> nan nan        invalid\nexp1035 exp -2.3 -inf -> nan nan        invalid\nexp1036 exp -inf -0.0 -> 0.0 -0.0\nexp1037 exp -inf -1.4 -> 0.0 -0.0\nexp1038 exp -inf -2.8 -> -0.0 -0.0\nexp1039 exp -inf -4.2 -> -0.0 0.0\nexp1040 exp -inf -5.6 -> 0.0 0.0\nexp1041 exp -inf -7.0 -> 0.0 -0.0\nexp1042 exp inf -0.0 -> inf -0.0\nexp1043 exp inf -1.4 -> inf -inf\nexp1044 exp inf -2.8 -> -inf -inf\nexp1045 exp inf -4.2 -> -inf inf\nexp1046 exp inf -5.6 -> inf inf\nexp1047 exp inf -7.0 -> inf -inf\nexp1048 exp -inf -inf -> 0.0 0.0        ignore-real-sign ignore-imag-sign\nexp1049 exp inf -inf -> inf nan         invalid ignore-real-sign\nexp1050 exp nan -0.0 -> nan -0.0\nexp1051 exp nan -2.3 -> nan nan\nexp1052 exp nan -inf -> nan nan\n\n\n-----------------------------\n-- cosh: Hyperbolic Cosine --\n-----------------------------\n\n-- zeros\ncosh0000 cosh 0.0 0.0 -> 1.0 0.0\ncosh0001 cosh 0.0 -0.0 -> 1.0 -0.0\ncosh0002 cosh -0.0 0.0 -> 1.0 -0.0\ncosh0003 cosh -0.0 -0.0 -> 1.0 0.0\n\n-- random inputs\ncosh0004 cosh -0.85395264297414253 -8.8553756148671958 -> -1.1684340348021185 0.51842195359787435\ncosh0005 cosh -19.584904237211223 -0.066582627994906177 -> 159816812.23336992 10656776.050406246\ncosh0006 cosh -0.11072618401130772 -1.484820215073247 -> 0.086397164744949503 0.11054275637717284\ncosh0007 cosh -3.4764840250681752 -0.48440348288275276 -> 14.325931955190844 7.5242053548737955\ncosh0008 cosh -0.52047063604524602 -0.3603805382775585 -> 1.0653940354683802 0.19193293606252473\ncosh0009 cosh -1.39518962975995 0.0074738604700702906 -> 2.1417031027235969 -0.01415518712296308\ncosh0010 cosh -0.37107064757653541 0.14728085307856609 -> 1.0580601496776991 -0.055712531964568587\ncosh0011 cosh -5.8470200958739653 4.0021722388336292 -> -112.86220667618285 131.24734033545013\ncosh0012 cosh -0.1700261444851883 0.97167540135354513 -> 0.57208748253577946 -0.1410904820240203\ncosh0013 cosh -0.44042397902648783 1.0904791964139742 -> 0.50760322393058133 -0.40333966652010816\ncosh0014 cosh 0.052267552491867299 -3.8889011430644174 -> -0.73452303414639297 0.035540704833537134\ncosh0015 cosh 0.98000764177127453 -1.2548829247784097 -> 0.47220747341416142 -1.0879421432180316\ncosh0016 cosh 0.083594701222644008 -0.88847899930181284 -> 0.63279782419312613 -0.064954566816002285\ncosh0017 cosh 1.38173531783776 -0.43185040816732229 -> 1.9221663374671647 -0.78073830858849347\ncosh0018 cosh 0.57315681120148465 -0.22255760951027942 -> 1.1399733125173004 -0.1335512343605956\ncosh0019 cosh 1.8882512333062347 4.5024932182383797 -> -0.7041602065362691 -3.1573822131964615\ncosh0020 cosh 0.5618219206858317 0.92620452129575348 -> 0.69822380405378381 0.47309067471054522\ncosh0021 cosh 0.54361442847062591 0.64176483583018462 -> 0.92234462074193491 0.34167906495845501\ncosh0022 cosh 0.0014777403107920331 1.3682028122677661 -> 0.2012106963899549 0.001447518137863219\ncosh0023 cosh 2.218885944363501 2.0015727395883687 -> -1.94294321081968 4.1290269176083196\n\n-- large real part\ncosh0030 cosh 710.5 2.3519999999999999 -> -1.2967465239355998e+308 1.3076707908857333e+308\ncosh0031 cosh -710.5 0.69999999999999996 -> 1.4085466381392499e+308 -1.1864024666450239e+308\n\n-- Additional real values (mpmath)\ncosh0050 cosh 1e-150 0.0 -> 1.0 0.0\ncosh0051 cosh 1e-18 0.0 -> 1.0 0.0\ncosh0052 cosh 1e-09 0.0 -> 1.0000000000000000005 0.0\ncosh0053 cosh 0.0003 0.0 -> 1.0000000450000003375 0.0\ncosh0054 cosh 0.2 0.0 -> 1.0200667556190758485 0.0\ncosh0055 cosh 1.0 0.0 -> 1.5430806348152437785 0.0\ncosh0056 cosh -1e-18 0.0 -> 1.0 -0.0\ncosh0057 cosh -0.0003 0.0 -> 1.0000000450000003375 -0.0\ncosh0058 cosh -1.0 0.0 -> 1.5430806348152437785 -0.0\ncosh0059 cosh 1.3169578969248168 0.0 -> 2.0000000000000001504 0.0\ncosh0060 cosh -1.3169578969248168 0.0 -> 2.0000000000000001504 -0.0\ncosh0061 cosh 17.328679513998633 0.0 -> 16777216.000000021938 0.0\ncosh0062 cosh 18.714973875118524 0.0 -> 67108864.000000043662 0.0\ncosh0063 cosh 709.7827 0.0 -> 8.9883497833190073272e+307 0.0\ncosh0064 cosh -709.7827 0.0 -> 8.9883497833190073272e+307 -0.0\n\n-- special values\ncosh1000 cosh 0.0 0.0 -> 1.0 0.0\ncosh1001 cosh 0.0 inf -> nan 0.0        invalid ignore-imag-sign\ncosh1002 cosh 0.0 nan -> nan 0.0        ignore-imag-sign\ncosh1003 cosh 2.3 inf -> nan nan        invalid\ncosh1004 cosh 2.3 nan -> nan nan\ncosh1005 cosh inf 0.0 -> inf 0.0\ncosh1006 cosh inf 1.4 -> inf inf\ncosh1007 cosh inf 2.8 -> -inf inf\ncosh1008 cosh inf 4.2 -> -inf -inf\ncosh1009 cosh inf 5.6 -> inf -inf\ncosh1010 cosh inf 7.0 -> inf inf\ncosh1011 cosh inf inf -> inf nan        invalid ignore-real-sign\ncosh1012 cosh inf nan -> inf nan\ncosh1013 cosh nan 0.0 -> nan 0.0        ignore-imag-sign\ncosh1014 cosh nan 2.3 -> nan nan\ncosh1015 cosh nan inf -> nan nan\ncosh1016 cosh nan nan -> nan nan\ncosh1017 cosh 0.0 -0.0 -> 1.0 -0.0\ncosh1018 cosh 0.0 -inf -> nan 0.0       invalid ignore-imag-sign\ncosh1019 cosh 2.3 -inf -> nan nan       invalid\ncosh1020 cosh inf -0.0 -> inf -0.0\ncosh1021 cosh inf -1.4 -> inf -inf\ncosh1022 cosh inf -2.8 -> -inf -inf\ncosh1023 cosh inf -4.2 -> -inf inf\ncosh1024 cosh inf -5.6 -> inf inf\ncosh1025 cosh inf -7.0 -> inf -inf\ncosh1026 cosh inf -inf -> inf nan       invalid ignore-real-sign\ncosh1027 cosh nan -0.0 -> nan 0.0       ignore-imag-sign\ncosh1028 cosh nan -2.3 -> nan nan\ncosh1029 cosh nan -inf -> nan nan\ncosh1030 cosh -0.0 -0.0 -> 1.0 0.0\ncosh1031 cosh -0.0 -inf -> nan 0.0      invalid ignore-imag-sign\ncosh1032 cosh -0.0 nan -> nan 0.0       ignore-imag-sign\ncosh1033 cosh -2.3 -inf -> nan nan      invalid\ncosh1034 cosh -2.3 nan -> nan nan\ncosh1035 cosh -inf -0.0 -> inf 0.0\ncosh1036 cosh -inf -1.4 -> inf inf\ncosh1037 cosh -inf -2.8 -> -inf inf\ncosh1038 cosh -inf -4.2 -> -inf -inf\ncosh1039 cosh -inf -5.6 -> inf -inf\ncosh1040 cosh -inf -7.0 -> inf inf\ncosh1041 cosh -inf -inf -> inf nan      invalid ignore-real-sign\ncosh1042 cosh -inf nan -> inf nan\ncosh1043 cosh -0.0 0.0 -> 1.0 -0.0\ncosh1044 cosh -0.0 inf -> nan 0.0       invalid ignore-imag-sign\ncosh1045 cosh -2.3 inf -> nan nan       invalid\ncosh1046 cosh -inf 0.0 -> inf -0.0\ncosh1047 cosh -inf 1.4 -> inf -inf\ncosh1048 cosh -inf 2.8 -> -inf -inf\ncosh1049 cosh -inf 4.2 -> -inf inf\ncosh1050 cosh -inf 5.6 -> inf inf\ncosh1051 cosh -inf 7.0 -> inf -inf\ncosh1052 cosh -inf inf -> inf nan       invalid ignore-real-sign\n\n\n---------------------------\n-- sinh: Hyperbolic Sine --\n---------------------------\n\n-- zeros\nsinh0000 sinh 0.0 0.0 -> 0.0 0.0\nsinh0001 sinh 0.0 -0.0 -> 0.0 -0.0\nsinh0002 sinh -0.0 0.0 -> -0.0 0.0\nsinh0003 sinh -0.0 -0.0 -> -0.0 -0.0\n\n-- random inputs\nsinh0004 sinh -17.282588091462742 -0.38187948694103546 -> -14867386.857248396 -5970648.6553516639\nsinh0005 sinh -343.91971203143208 -5.0172868877771525e-22 -> -1.1518691776521735e+149 -5.7792581214689021e+127\nsinh0006 sinh -14.178122253300922 -1.9387157579351293 -> 258440.37909034826 -670452.58500946441\nsinh0007 sinh -1.0343810581686239 -1.0970235266369905 -> -0.56070858278092739 -1.4098883258046697\nsinh0008 sinh -0.066126561416368204 -0.070461584169961872 -> -0.066010558700938124 -0.070557276738637542\nsinh0009 sinh -0.37630149150308484 3.3621734692162173 -> 0.37591118119332617 -0.23447115926369383\nsinh0010 sinh -0.049941960978670055 0.40323767020414625 -> -0.045955482136329009 0.3928878494430646\nsinh0011 sinh -16.647852603903715 0.0026852219129082098 -> -8492566.5739382561 22804.480671133562\nsinh0012 sinh -1.476625314303694 0.89473773116683386 -> -1.2982943334382224 1.7966593367791204\nsinh0013 sinh -422.36429577556913 0.10366634502307912 -> -1.3400321008920044e+183 1.3941600948045599e+182\nsinh0014 sinh 0.09108340745641981 -0.40408227416070353 -> 0.083863724802237902 -0.39480716553935602\nsinh0015 sinh 2.036064132067386 -2.6831729961386239 -> -3.37621124363175 -1.723868330002817\nsinh0016 sinh 2.5616717223063317 -0.0078978498622717767 -> 6.4399415853815869 -0.051472264400722133\nsinh0017 sinh 0.336804011985188 -6.5654622971649337 -> 0.32962499307574578 -0.29449170159995197\nsinh0018 sinh 0.23774603755649693 -0.92467195799232049 -> 0.14449839490603389 -0.82109449053556793\nsinh0019 sinh 0.0011388273541465494 1.9676196882949855 -> -0.00044014605389634999 0.92229398407098806\nsinh0020 sinh 3.2443870105663759 0.8054287559616895 -> 8.8702890778527426 9.2610748597042196\nsinh0021 sinh 0.040628908857054738 0.098206391190944958 -> 0.04044426841671233 0.098129544739707392\nsinh0022 sinh 4.7252283918217696e-30 9.1198155642656697 -> -4.5071980561644404e-30 0.30025730701661713\nsinh0023 sinh 0.043713693678420068 0.22512549887532657 -> 0.042624198673416713 0.22344201231217961\n\n-- large real part\nsinh0030 sinh 710.5 -2.3999999999999999 -> -1.3579970564885919e+308 -1.24394470907798e+308\nsinh0031 sinh -710.5 0.80000000000000004 -> -1.2830671601735164e+308 1.3210954193997678e+308\n\n-- Additional real values (mpmath)\nsinh0050 sinh 1e-100 0.0 -> 1.00000000000000002e-100 0.0\nsinh0051 sinh 5e-17 0.0 -> 4.9999999999999998955e-17 0.0\nsinh0052 sinh 1e-16 0.0 -> 9.999999999999999791e-17 0.0\nsinh0053 sinh 3.7e-08 0.0 -> 3.7000000000000008885e-8 0.0\nsinh0054 sinh 0.001 0.0 -> 0.0010000001666666750208 0.0\nsinh0055 sinh 0.2 0.0 -> 0.20133600254109399895 0.0\nsinh0056 sinh 1.0 0.0 -> 1.1752011936438014569 0.0\nsinh0057 sinh -3.7e-08 0.0 -> -3.7000000000000008885e-8 0.0\nsinh0058 sinh -0.001 0.0 -> -0.0010000001666666750208 0.0\nsinh0059 sinh -1.0 0.0 -> -1.1752011936438014569 0.0\nsinh0060 sinh 1.4436354751788103 0.0 -> 1.9999999999999999078 0.0\nsinh0061 sinh -1.4436354751788103 0.0 -> -1.9999999999999999078 0.0\nsinh0062 sinh 17.328679513998633 0.0 -> 16777215.999999992136 0.0\nsinh0063 sinh 18.714973875118524 0.0 -> 67108864.000000036211 0.0\nsinh0064 sinh 709.7827 0.0 -> 8.9883497833190073272e+307 0.0\nsinh0065 sinh -709.7827 0.0 -> -8.9883497833190073272e+307 0.0\n\n-- special values\nsinh1000 sinh 0.0 0.0 -> 0.0 0.0\nsinh1001 sinh 0.0 inf -> 0.0 nan        invalid ignore-real-sign\nsinh1002 sinh 0.0 nan -> 0.0 nan        ignore-real-sign\nsinh1003 sinh 2.3 inf -> nan nan        invalid\nsinh1004 sinh 2.3 nan -> nan nan\nsinh1005 sinh inf 0.0 -> inf 0.0\nsinh1006 sinh inf 1.4 -> inf inf\nsinh1007 sinh inf 2.8 -> -inf inf\nsinh1008 sinh inf 4.2 -> -inf -inf\nsinh1009 sinh inf 5.6 -> inf -inf\nsinh1010 sinh inf 7.0 -> inf inf\nsinh1011 sinh inf inf -> inf nan        invalid ignore-real-sign\nsinh1012 sinh inf nan -> inf nan        ignore-real-sign\nsinh1013 sinh nan 0.0 -> nan 0.0\nsinh1014 sinh nan 2.3 -> nan nan\nsinh1015 sinh nan inf -> nan nan\nsinh1016 sinh nan nan -> nan nan\nsinh1017 sinh 0.0 -0.0 -> 0.0 -0.0\nsinh1018 sinh 0.0 -inf -> 0.0 nan       invalid ignore-real-sign\nsinh1019 sinh 2.3 -inf -> nan nan       invalid\nsinh1020 sinh inf -0.0 -> inf -0.0\nsinh1021 sinh inf -1.4 -> inf -inf\nsinh1022 sinh inf -2.8 -> -inf -inf\nsinh1023 sinh inf -4.2 -> -inf inf\nsinh1024 sinh inf -5.6 -> inf inf\nsinh1025 sinh inf -7.0 -> inf -inf\nsinh1026 sinh inf -inf -> inf nan       invalid ignore-real-sign\nsinh1027 sinh nan -0.0 -> nan -0.0\nsinh1028 sinh nan -2.3 -> nan nan\nsinh1029 sinh nan -inf -> nan nan\nsinh1030 sinh -0.0 -0.0 -> -0.0 -0.0\nsinh1031 sinh -0.0 -inf -> 0.0 nan      invalid ignore-real-sign\nsinh1032 sinh -0.0 nan -> 0.0 nan       ignore-real-sign\nsinh1033 sinh -2.3 -inf -> nan nan      invalid\nsinh1034 sinh -2.3 nan -> nan nan\nsinh1035 sinh -inf -0.0 -> -inf -0.0\nsinh1036 sinh -inf -1.4 -> -inf -inf\nsinh1037 sinh -inf -2.8 -> inf -inf\nsinh1038 sinh -inf -4.2 -> inf inf\nsinh1039 sinh -inf -5.6 -> -inf inf\nsinh1040 sinh -inf -7.0 -> -inf -inf\nsinh1041 sinh -inf -inf -> inf nan      invalid ignore-real-sign\nsinh1042 sinh -inf nan -> inf nan       ignore-real-sign\nsinh1043 sinh -0.0 0.0 -> -0.0 0.0\nsinh1044 sinh -0.0 inf -> 0.0 nan       invalid ignore-real-sign\nsinh1045 sinh -2.3 inf -> nan nan       invalid\nsinh1046 sinh -inf 0.0 -> -inf 0.0\nsinh1047 sinh -inf 1.4 -> -inf inf\nsinh1048 sinh -inf 2.8 -> inf inf\nsinh1049 sinh -inf 4.2 -> inf -inf\nsinh1050 sinh -inf 5.6 -> -inf -inf\nsinh1051 sinh -inf 7.0 -> -inf inf\nsinh1052 sinh -inf inf -> inf nan       invalid ignore-real-sign\n\n\n------------------------------\n-- tanh: Hyperbolic Tangent --\n------------------------------\n\n-- Disabled test: replaced by test_math.testTanhSign()\n-- and test_cmath.testTanhSign()\n\n-- -- zeros\n-- tanh0000 tanh 0.0 0.0 -> 0.0 0.0\n-- tanh0001 tanh 0.0 -0.0 -> 0.0 -0.0\n-- tanh0002 tanh -0.0 0.0 -> -0.0 0.0\n-- tanh0003 tanh -0.0 -0.0 -> -0.0 -0.0\n\n-- random inputs\ntanh0004 tanh -21.200500450664993 -1.6970729480342996 -> -1.0 1.9241352344849399e-19\ntanh0005 tanh -0.34158771504251928 -8.0848504951747131 -> -2.123711225855613 1.2827526782026006\ntanh0006 tanh -15.454144725193689 -0.23619582288265617 -> -0.99999999999993283 -3.4336684248260036e-14\ntanh0007 tanh -7.6103163119661952 -0.7802748320307008 -> -0.99999999497219438 -4.9064845343755437e-07\ntanh0008 tanh -0.15374717235792129 -0.6351086327306138 -> -0.23246081703561869 -0.71083467433910219\ntanh0009 tanh -0.49101115474392465 0.09723001264886301 -> -0.45844445715492133 0.077191158541805888\ntanh0010 tanh -0.10690612157664491 2.861612800856395 -> -0.11519761626257358 -0.28400488355647507\ntanh0011 tanh -0.91505774192066702 1.5431174597727007 -> -1.381109893068114 0.025160819663709356\ntanh0012 tanh -0.057433367093792223 0.35491159541246459 -> -0.065220499046696953 0.36921788332369498\ntanh0013 tanh -1.3540418621233514 0.18969415642242535 -> -0.88235642861151387 0.043764069984411721\ntanh0014 tanh 0.94864783961003529 -0.11333689578867717 -> 0.74348401861861368 -0.051271042543855221\ntanh0015 tanh 1.9591698133845488 -0.0029654444904578339 -> 0.9610270776968135 -0.00022664240049212933\ntanh0016 tanh 1.0949715796669197 -0.24706642853984456 -> 0.81636574501369386 -0.087767436914149954\ntanh0017 tanh 5770428.2113731047 -3.7160580339833165 -> 1.0 -0.0\ntanh0018 tanh 1.5576782321399629 -1.0357943787966468 -> 1.0403002384895388 -0.081126347894671463\ntanh0019 tanh 0.62378536230552961 2.3471393579560216 -> 0.85582499238960363 -0.53569473646842869\ntanh0020 tanh 17.400628602508025 9.3987059533841979 -> 0.99999999999999845 -8.0175867720530832e-17\ntanh0021 tanh 0.15026177509871896 0.50630349159505472 -> 0.19367536571827768 0.53849847858853661\ntanh0022 tanh 0.57433977530711167 1.0071604546265627 -> 1.0857848159262844 0.69139213955872214\ntanh0023 tanh 0.16291181500449456 0.006972810241567544 -> 0.16149335907551157 0.0067910772903467817\n\n-- large real part\ntanh0030 tanh 710 0.13 -> 1.0 0.0\ntanh0031 tanh -711 7.4000000000000004 -> -1.0 0.0\ntanh0032 tanh 1000 -2.3199999999999998 -> 1.0 0.0\ntanh0033 tanh -1.0000000000000001e+300 -9.6699999999999999 -> -1.0 -0.0\n\n-- Additional real values (mpmath)\ntanh0050 tanh 1e-100 0.0 -> 1.00000000000000002e-100 0.0\ntanh0051 tanh 5e-17 0.0 -> 4.9999999999999998955e-17 0.0\ntanh0052 tanh 1e-16 0.0 -> 9.999999999999999791e-17 0.0\ntanh0053 tanh 3.7e-08 0.0 -> 3.6999999999999983559e-8 0.0\ntanh0054 tanh 0.001 0.0 -> 0.00099999966666680002076 0.0\ntanh0055 tanh 0.2 0.0 -> 0.19737532022490401141 0.0\ntanh0056 tanh 1.0 0.0 -> 0.76159415595576488812 0.0\ntanh0057 tanh -3.7e-08 0.0 -> -3.6999999999999983559e-8 0.0\ntanh0058 tanh -0.001 0.0 -> -0.00099999966666680002076 0.0\ntanh0059 tanh -1.0 0.0 -> -0.76159415595576488812 0.0\ntanh0060 tanh 0.5493061443340549 0.0 -> 0.50000000000000003402 0.0\ntanh0061 tanh -0.5493061443340549 0.0 -> -0.50000000000000003402 0.0\ntanh0062 tanh 17.328679513998633 0.0 -> 0.99999999999999822364 0.0\ntanh0063 tanh 18.714973875118524 0.0 -> 0.99999999999999988898 0.0\ntanh0064 tanh 711 0.0 -> 1.0 0.0\ntanh0065 tanh 1.797e+308 0.0 -> 1.0 0.0\n\n--special values\ntanh1000 tanh 0.0 0.0 -> 0.0 0.0\ntanh1001 tanh 0.0 inf -> nan nan        invalid\ntanh1002 tanh 2.3 inf -> nan nan        invalid\ntanh1003 tanh 0.0 nan -> nan nan\ntanh1004 tanh 2.3 nan -> nan nan\ntanh1005 tanh inf 0.0 -> 1.0 0.0\ntanh1006 tanh inf 0.7 -> 1.0 0.0\ntanh1007 tanh inf 1.4 -> 1.0 0.0\ntanh1008 tanh inf 2.1 -> 1.0 -0.0\ntanh1009 tanh inf 2.8 -> 1.0 -0.0\ntanh1010 tanh inf 3.5 -> 1.0 0.0\ntanh1011 tanh inf inf -> 1.0 0.0        ignore-imag-sign\ntanh1012 tanh inf nan -> 1.0 0.0        ignore-imag-sign\ntanh1013 tanh nan 0.0 -> nan 0.0\ntanh1014 tanh nan 2.3 -> nan nan\ntanh1015 tanh nan inf -> nan nan\ntanh1016 tanh nan nan -> nan nan\ntanh1017 tanh 0.0 -0.0 -> 0.0 -0.0\ntanh1018 tanh 0.0 -inf -> nan nan       invalid\ntanh1019 tanh 2.3 -inf -> nan nan       invalid\ntanh1020 tanh inf -0.0 -> 1.0 -0.0\ntanh1021 tanh inf -0.7 -> 1.0 -0.0\ntanh1022 tanh inf -1.4 -> 1.0 -0.0\ntanh1023 tanh inf -2.1 -> 1.0 0.0\ntanh1024 tanh inf -2.8 -> 1.0 0.0\ntanh1025 tanh inf -3.5 -> 1.0 -0.0\ntanh1026 tanh inf -inf -> 1.0 0.0       ignore-imag-sign\ntanh1027 tanh nan -0.0 -> nan -0.0\ntanh1028 tanh nan -2.3 -> nan nan\ntanh1029 tanh nan -inf -> nan nan\ntanh1030 tanh -0.0 -0.0 -> -0.0 -0.0\ntanh1031 tanh -0.0 -inf -> nan nan      invalid\ntanh1032 tanh -2.3 -inf -> nan nan      invalid\ntanh1033 tanh -0.0 nan -> nan nan\ntanh1034 tanh -2.3 nan -> nan nan\ntanh1035 tanh -inf -0.0 -> -1.0 -0.0\ntanh1036 tanh -inf -0.7 -> -1.0 -0.0\ntanh1037 tanh -inf -1.4 -> -1.0 -0.0\ntanh1038 tanh -inf -2.1 -> -1.0 0.0\ntanh1039 tanh -inf -2.8 -> -1.0 0.0\ntanh1040 tanh -inf -3.5 -> -1.0 -0.0\ntanh1041 tanh -inf -inf -> -1.0 0.0     ignore-imag-sign\ntanh1042 tanh -inf nan -> -1.0 0.0      ignore-imag-sign\ntanh1043 tanh -0.0 0.0 -> -0.0 0.0\ntanh1044 tanh -0.0 inf -> nan nan       invalid\ntanh1045 tanh -2.3 inf -> nan nan       invalid\ntanh1046 tanh -inf 0.0 -> -1.0 0.0\ntanh1047 tanh -inf 0.7 -> -1.0 0.0\ntanh1048 tanh -inf 1.4 -> -1.0 0.0\ntanh1049 tanh -inf 2.1 -> -1.0 -0.0\ntanh1050 tanh -inf 2.8 -> -1.0 -0.0\ntanh1051 tanh -inf 3.5 -> -1.0 0.0\ntanh1052 tanh -inf inf -> -1.0 0.0      ignore-imag-sign\n\n\n-----------------\n-- cos: Cosine --\n-----------------\n\n-- zeros\ncos0000 cos 0.0 0.0 -> 1.0 -0.0\ncos0001 cos 0.0 -0.0 -> 1.0 0.0\ncos0002 cos -0.0 0.0 -> 1.0 0.0\ncos0003 cos -0.0 -0.0 -> 1.0 -0.0\n\n-- random inputs\ncos0004 cos -2.0689194692073034 -0.0016802181751734313 -> -0.47777827208561469 -0.0014760401501695971\ncos0005 cos -0.4209627318177977 -1.8238516774258027 -> 2.9010402201444108 -1.2329207042329617\ncos0006 cos -1.9402181630694557 -2.9751857392891217 -> -3.5465459297970985 -9.1119163586282248\ncos0007 cos -3.3118320290191616 -0.87871302909286142 -> -1.3911528636565498 0.16878141517391701\ncos0008 cos -4.9540404623376872 -0.57949232239026827 -> 0.28062445586552065 0.59467861308508008\ncos0009 cos -0.45374584316245026 1.3950283448373935 -> 1.9247665574290578 0.83004572204761107\ncos0010 cos -0.42578172040176843 1.2715881615413049 -> 1.7517161459489148 0.67863902697363332\ncos0011 cos -0.13862985354300136 0.43587635877670328 -> 1.0859880290361912 0.062157548146672272\ncos0012 cos -0.11073221308966584 9.9384082307326475e-15 -> 0.99387545040722947 1.0982543264065479e-15\ncos0013 cos -1.5027633662054623e-07 0.0069668060249955498 -> 1.0000242682912412 1.0469545565660995e-09\ncos0014 cos 4.9728645490503052 -0.00027479808860952822 -> 0.25754011731975501 -0.00026552849549083186\ncos0015 cos 7.81969303486719 -0.79621523445878783 -> 0.045734882501585063 0.88253139933082991\ncos0016 cos 0.13272421880766716 -0.74668445308718201 -> 1.2806012244432847 0.10825373267437005\ncos0017 cos 4.2396521985973274 -2.2178848380884881 -> -2.1165117057056855 -4.0416492444641401\ncos0018 cos 1.1622206624927296 -0.50400115461197081 -> 0.44884072613370379 0.4823469915034318\ncos0019 cos 1.628772864620884e-08 0.58205705428979282 -> 1.1742319995791435 -1.0024839481956604e-08\ncos0020 cos 2.6385212606111241 2.9886107100937296 -> -8.7209475927161417 -4.7748352107199796\ncos0021 cos 4.8048375263775256 0.0062248852898515658 -> 0.092318702015846243 0.0061983430422306142\ncos0022 cos 7.9914515433858515 0.71659966615501436 -> -0.17375439906936566 -0.77217043527294582\ncos0023 cos 0.45124351152540226 1.6992693993812158 -> 2.543477948972237 -1.1528193694875477\n\n-- Additional real values (mpmath)\ncos0050 cos 1e-150 0.0 -> 1.0 -0.0\ncos0051 cos 1e-18 0.0 -> 1.0 -0.0\ncos0052 cos 1e-09 0.0 -> 0.9999999999999999995 -0.0\ncos0053 cos 0.0003 0.0 -> 0.9999999550000003375 -0.0\ncos0054 cos 0.2 0.0 -> 0.98006657784124162892 -0.0\ncos0055 cos 1.0 0.0 -> 0.5403023058681397174 -0.0\ncos0056 cos -1e-18 0.0 -> 1.0 0.0\ncos0057 cos -0.0003 0.0 -> 0.9999999550000003375 0.0\ncos0058 cos -1.0 0.0 -> 0.5403023058681397174 0.0\ncos0059 cos 1.0471975511965976 0.0 -> 0.50000000000000009945 -0.0\ncos0060 cos 2.5707963267948966 0.0 -> -0.84147098480789647357 -0.0\ncos0061 cos -2.5707963267948966 0.0 -> -0.84147098480789647357 0.0\ncos0062 cos 7.225663103256523 0.0 -> 0.58778525229247407559 -0.0\ncos0063 cos -8.79645943005142 0.0 -> -0.80901699437494722255 0.0\n\n-- special values\ncos1000 cos -0.0 0.0 -> 1.0 0.0\ncos1001 cos -inf 0.0 -> nan 0.0 invalid ignore-imag-sign\ncos1002 cos nan 0.0 -> nan 0.0 ignore-imag-sign\ncos1003 cos -inf 2.2999999999999998 -> nan nan invalid\ncos1004 cos nan 2.2999999999999998 -> nan nan\ncos1005 cos -0.0 inf -> inf 0.0\ncos1006 cos -1.3999999999999999 inf -> inf inf\ncos1007 cos -2.7999999999999998 inf -> -inf inf\ncos1008 cos -4.2000000000000002 inf -> -inf -inf\ncos1009 cos -5.5999999999999996 inf -> inf -inf\ncos1010 cos -7.0 inf -> inf inf\ncos1011 cos -inf inf -> inf nan invalid ignore-real-sign\ncos1012 cos nan inf -> inf nan\ncos1013 cos -0.0 nan -> nan 0.0 ignore-imag-sign\ncos1014 cos -2.2999999999999998 nan -> nan nan\ncos1015 cos -inf nan -> nan nan\ncos1016 cos nan nan -> nan nan\ncos1017 cos 0.0 0.0 -> 1.0 -0.0\ncos1018 cos inf 0.0 -> nan 0.0 invalid ignore-imag-sign\ncos1019 cos inf 2.2999999999999998 -> nan nan invalid\ncos1020 cos 0.0 inf -> inf -0.0\ncos1021 cos 1.3999999999999999 inf -> inf -inf\ncos1022 cos 2.7999999999999998 inf -> -inf -inf\ncos1023 cos 4.2000000000000002 inf -> -inf inf\ncos1024 cos 5.5999999999999996 inf -> inf inf\ncos1025 cos 7.0 inf -> inf -inf\ncos1026 cos inf inf -> inf nan invalid ignore-real-sign\ncos1027 cos 0.0 nan -> nan 0.0 ignore-imag-sign\ncos1028 cos 2.2999999999999998 nan -> nan nan\ncos1029 cos inf nan -> nan nan\ncos1030 cos 0.0 -0.0 -> 1.0 0.0\ncos1031 cos inf -0.0 -> nan 0.0 invalid ignore-imag-sign\ncos1032 cos nan -0.0 -> nan 0.0 ignore-imag-sign\ncos1033 cos inf -2.2999999999999998 -> nan nan invalid\ncos1034 cos nan -2.2999999999999998 -> nan nan\ncos1035 cos 0.0 -inf -> inf 0.0\ncos1036 cos 1.3999999999999999 -inf -> inf inf\ncos1037 cos 2.7999999999999998 -inf -> -inf inf\ncos1038 cos 4.2000000000000002 -inf -> -inf -inf\ncos1039 cos 5.5999999999999996 -inf -> inf -inf\ncos1040 cos 7.0 -inf -> inf inf\ncos1041 cos inf -inf -> inf nan invalid ignore-real-sign\ncos1042 cos nan -inf -> inf nan\ncos1043 cos -0.0 -0.0 -> 1.0 -0.0\ncos1044 cos -inf -0.0 -> nan 0.0 invalid ignore-imag-sign\ncos1045 cos -inf -2.2999999999999998 -> nan nan invalid\ncos1046 cos -0.0 -inf -> inf -0.0\ncos1047 cos -1.3999999999999999 -inf -> inf -inf\ncos1048 cos -2.7999999999999998 -inf -> -inf -inf\ncos1049 cos -4.2000000000000002 -inf -> -inf inf\ncos1050 cos -5.5999999999999996 -inf -> inf inf\ncos1051 cos -7.0 -inf -> inf -inf\ncos1052 cos -inf -inf -> inf nan invalid ignore-real-sign\n\n\n---------------\n-- sin: Sine --\n---------------\n\n-- zeros\nsin0000 sin 0.0 0.0 -> 0.0 0.0\nsin0001 sin 0.0 -0.0 -> 0.0 -0.0\nsin0002 sin -0.0 0.0 -> -0.0 0.0\nsin0003 sin -0.0 -0.0 -> -0.0 -0.0\n\n-- random inputs\nsin0004 sin -0.18691829163163759 -0.74388741985507034 -> -0.2396636733773444 -0.80023231101856751\nsin0005 sin -0.45127453702459158 -461.81339920716164 -> -7.9722299331077877e+199 -1.6450205811004628e+200\nsin0006 sin -0.47669228345768921 -2.7369936564987514 -> -3.557238022267124 -6.8308030771226615\nsin0007 sin -0.31024285525950857 -1.4869219939188296 -> -0.70972676047175209 -1.9985029635426839\nsin0008 sin -4.4194573407025608 -1.405999210989288 -> 2.0702480800802685 0.55362250792180601\nsin0009 sin -1.7810832046434898e-05 0.0016439555384379083 -> -1.7810856113185261e-05 0.0016439562786668375\nsin0010 sin -0.8200017874897666 0.61724876887771929 -> -0.8749078195948865 0.44835295550987758\nsin0011 sin -1.4536502806107114 0.63998575534150415 -> -1.2035709929437679 0.080012187489163708\nsin0012 sin -2.2653412155506079 0.13172760685583729 -> -0.77502093809190431 -0.084554426868229532\nsin0013 sin -0.02613983069491858 0.18404766597776073 -> -0.026580778863127943 0.18502525396735642\nsin0014 sin 1.5743065001054617 -0.53125574272642029 -> 1.1444596332092725 0.0019537598099352077\nsin0015 sin 7.3833101791283289e-20 -0.16453221324236217 -> 7.4834720674379429e-20 -0.16527555646466915\nsin0016 sin 0.34763834641254038 -2.8377416421089565 -> 2.918883541504663 -8.0002718053250224\nsin0017 sin 0.077105785180421563 -0.090056027316200674 -> 0.077341973814471304 -0.089909869380524587\nsin0018 sin 3.9063227798142329e-17 -0.05954098654295524 -> 3.9132490348956512e-17 -0.059576172859837351\nsin0019 sin 0.57333917932544598 8.7785221430594696e-06 -> 0.54244029338302935 7.3747869125301368e-06\nsin0020 sin 0.024861722816513169 0.33044620756118515 -> 0.026228801369651 0.3363889671570689\nsin0021 sin 1.4342727387492671 0.81361889790284347 -> 1.3370960060947923 0.12336137961387163\nsin0022 sin 1.1518087354403725 4.8597235966150558 -> 58.919141989603041 26.237003403758852\nsin0023 sin 0.00087773078406649192 34.792379211312095 -> 565548145569.38245 644329685822700.62\n\n-- Additional real values (mpmath)\nsin0050 sin 1e-100 0.0 -> 1.00000000000000002e-100 0.0\nsin0051 sin 3.7e-08 0.0 -> 3.6999999999999992001e-8 0.0\nsin0052 sin 0.001 0.0 -> 0.00099999983333334168748 0.0\nsin0053 sin 0.2 0.0 -> 0.19866933079506122634 0.0\nsin0054 sin 1.0 0.0 -> 0.84147098480789650665 0.0\nsin0055 sin -3.7e-08 0.0 -> -3.6999999999999992001e-8 0.0\nsin0056 sin -0.001 0.0 -> -0.00099999983333334168748 0.0\nsin0057 sin -1.0 0.0 -> -0.84147098480789650665 0.0\nsin0058 sin 0.5235987755982989 0.0 -> 0.50000000000000004642 0.0\nsin0059 sin -0.5235987755982989 0.0 -> -0.50000000000000004642 0.0\nsin0060 sin 2.6179938779914944 0.0 -> 0.49999999999999996018 -0.0\nsin0061 sin -2.6179938779914944 0.0 -> -0.49999999999999996018 -0.0\nsin0062 sin 7.225663103256523 0.0 -> 0.80901699437494673648 0.0\nsin0063 sin -8.79645943005142 0.0 -> -0.58778525229247340658 -0.0\n\n-- special values\nsin1000 sin -0.0 0.0 -> -0.0 0.0\nsin1001 sin -inf 0.0 -> nan 0.0 invalid ignore-imag-sign\nsin1002 sin nan 0.0 -> nan 0.0 ignore-imag-sign\nsin1003 sin -inf 2.2999999999999998 -> nan nan invalid\nsin1004 sin nan 2.2999999999999998 -> nan nan\nsin1005 sin -0.0 inf -> -0.0 inf\nsin1006 sin -1.3999999999999999 inf -> -inf inf\nsin1007 sin -2.7999999999999998 inf -> -inf -inf\nsin1008 sin -4.2000000000000002 inf -> inf -inf\nsin1009 sin -5.5999999999999996 inf -> inf inf\nsin1010 sin -7.0 inf -> -inf inf\nsin1011 sin -inf inf -> nan inf invalid ignore-imag-sign\nsin1012 sin nan inf -> nan inf ignore-imag-sign\nsin1013 sin -0.0 nan -> -0.0 nan\nsin1014 sin -2.2999999999999998 nan -> nan nan\nsin1015 sin -inf nan -> nan nan\nsin1016 sin nan nan -> nan nan\nsin1017 sin 0.0 0.0 -> 0.0 0.0\nsin1018 sin inf 0.0 -> nan 0.0 invalid ignore-imag-sign\nsin1019 sin inf 2.2999999999999998 -> nan nan invalid\nsin1020 sin 0.0 inf -> 0.0 inf\nsin1021 sin 1.3999999999999999 inf -> inf inf\nsin1022 sin 2.7999999999999998 inf -> inf -inf\nsin1023 sin 4.2000000000000002 inf -> -inf -inf\nsin1024 sin 5.5999999999999996 inf -> -inf inf\nsin1025 sin 7.0 inf -> inf inf\nsin1026 sin inf inf -> nan inf invalid ignore-imag-sign\nsin1027 sin 0.0 nan -> 0.0 nan\nsin1028 sin 2.2999999999999998 nan -> nan nan\nsin1029 sin inf nan -> nan nan\nsin1030 sin 0.0 -0.0 -> 0.0 -0.0\nsin1031 sin inf -0.0 -> nan 0.0 invalid ignore-imag-sign\nsin1032 sin nan -0.0 -> nan 0.0 ignore-imag-sign\nsin1033 sin inf -2.2999999999999998 -> nan nan invalid\nsin1034 sin nan -2.2999999999999998 -> nan nan\nsin1035 sin 0.0 -inf -> 0.0 -inf\nsin1036 sin 1.3999999999999999 -inf -> inf -inf\nsin1037 sin 2.7999999999999998 -inf -> inf inf\nsin1038 sin 4.2000000000000002 -inf -> -inf inf\nsin1039 sin 5.5999999999999996 -inf -> -inf -inf\nsin1040 sin 7.0 -inf -> inf -inf\nsin1041 sin inf -inf -> nan inf invalid ignore-imag-sign\nsin1042 sin nan -inf -> nan inf ignore-imag-sign\nsin1043 sin -0.0 -0.0 -> -0.0 -0.0\nsin1044 sin -inf -0.0 -> nan 0.0 invalid ignore-imag-sign\nsin1045 sin -inf -2.2999999999999998 -> nan nan invalid\nsin1046 sin -0.0 -inf -> -0.0 -inf\nsin1047 sin -1.3999999999999999 -inf -> -inf -inf\nsin1048 sin -2.7999999999999998 -inf -> -inf inf\nsin1049 sin -4.2000000000000002 -inf -> inf inf\nsin1050 sin -5.5999999999999996 -inf -> inf -inf\nsin1051 sin -7.0 -inf -> -inf -inf\nsin1052 sin -inf -inf -> nan inf invalid ignore-imag-sign\n\n\n------------------\n-- tan: Tangent --\n------------------\n\n-- zeros\ntan0000 tan 0.0 0.0 -> 0.0 0.0\ntan0001 tan 0.0 -0.0 -> 0.0 -0.0\ntan0002 tan -0.0 0.0 -> -0.0 0.0\ntan0003 tan -0.0 -0.0 -> -0.0 -0.0\n\n-- random inputs\ntan0004 tan -0.56378561833861074 -1.7110276237187664e+73 -> -0.0 -1.0\ntan0005 tan -3.5451633993471915e-12 -2.855471863564059 -> -4.6622441304889575e-14 -0.99340273843093951\ntan0006 tan -2.502442719638696 -0.26742234390504221 -> 0.66735215252994995 -0.39078997935420956\ntan0007 tan -0.87639597720371365 -55.586225523280206 -> -1.0285264565948176e-48 -1.0\ntan0008 tan -0.015783869596427243 -520.05944436039272 -> -0.0 -1.0\ntan0009 tan -0.84643549990725164 2.0749097935396343 -> -0.031412661676959573 1.0033548479526764\ntan0010 tan -0.43613792248559646 8.1082741629458059 -> -1.3879848444644593e-07 0.99999988344224011\ntan0011 tan -1.0820906367833114 0.28571868992480248 -> -1.3622485737936536 0.99089269377971245\ntan0012 tan -1.1477859580220084 1.9021637002708041 -> -0.034348450042071196 1.0293954097901687\ntan0013 tan -0.12465543176953409 3.0606851016344815e-05 -> -0.12530514290387343 3.1087420769945479e-05\ntan0014 tan 3.7582848717525343 -692787020.44038939 -> 0.0 -1.0\ntan0015 tan 2.2321967655142176e-06 -10.090069423008169 -> 1.5369846120622643e-14 -0.99999999655723759\ntan0016 tan 0.88371172390245012 -1.1635053630132823 -> 0.19705017118625889 -1.0196452280843129\ntan0017 tan 2.1347414231849267 -1.9311339960416831 -> -0.038663576915982524 -1.0174399993980778\ntan0018 tan 5.9027945255899974 -2.1574195684607135e-183 -> -0.39986591539281496 -2.5023753167976915e-183\ntan0019 tan 0.44811489490805362 683216075670.07556 -> 0.0 1.0\ntan0020 tan 4.1459766396068325 12.523017205605756 -> 2.4022514758988068e-11 1.0000000000112499\ntan0021 tan 1.7809617968443272 1.5052381702853379 -> -0.044066222118946903 1.0932684517702778\ntan0022 tan 1.1615313900880577 1.7956298728647107 -> 0.041793186826390362 1.0375339546034792\ntan0023 tan 0.067014779477908945 5.8517361577457097 -> 2.2088639754800034e-06 0.9999836182420061\n\n-- Additional real values (mpmath)\ntan0050 tan 1e-100 0.0 -> 1.00000000000000002e-100 0.0\ntan0051 tan 3.7e-08 0.0 -> 3.7000000000000017328e-8 0.0\ntan0052 tan 0.001 0.0 -> 0.0010000003333334666875 0.0\ntan0053 tan 0.2 0.0 -> 0.20271003550867249488 0.0\ntan0054 tan 1.0 0.0 -> 1.5574077246549022305 0.0\ntan0055 tan -3.7e-08 0.0 -> -3.7000000000000017328e-8 0.0\ntan0056 tan -0.001 0.0 -> -0.0010000003333334666875 0.0\ntan0057 tan -1.0 0.0 -> -1.5574077246549022305 0.0\ntan0058 tan 0.4636476090008061 0.0 -> 0.49999999999999997163 0.0\ntan0059 tan -0.4636476090008061 0.0 -> -0.49999999999999997163 0.0\ntan0060 tan 1.1071487177940904 0.0 -> 1.9999999999999995298 0.0\ntan0061 tan -1.1071487177940904 0.0 -> -1.9999999999999995298 0.0\ntan0062 tan 1.5 0.0 -> 14.101419947171719388 0.0\ntan0063 tan 1.57 0.0 -> 1255.7655915007896475 0.0\ntan0064 tan 1.5707963267948961 0.0 -> 1978937966095219.0538 0.0\ntan0065 tan 7.225663103256523 0.0 -> 1.3763819204711701522 0.0\ntan0066 tan -8.79645943005142 0.0 -> 0.7265425280053614098 0.0\n\n-- special values\ntan1000 tan -0.0 0.0 -> -0.0 0.0\ntan1001 tan -inf 0.0 -> nan nan invalid\ntan1002 tan -inf 2.2999999999999998 -> nan nan invalid\ntan1003 tan nan 0.0 -> nan nan\ntan1004 tan nan 2.2999999999999998 -> nan nan\ntan1005 tan -0.0 inf -> -0.0 1.0\ntan1006 tan -0.69999999999999996 inf -> -0.0 1.0\ntan1007 tan -1.3999999999999999 inf -> -0.0 1.0\ntan1008 tan -2.1000000000000001 inf -> 0.0 1.0\ntan1009 tan -2.7999999999999998 inf -> 0.0 1.0\ntan1010 tan -3.5 inf -> -0.0 1.0\ntan1011 tan -inf inf -> -0.0 1.0 ignore-real-sign\ntan1012 tan nan inf -> -0.0 1.0 ignore-real-sign\ntan1013 tan -0.0 nan -> -0.0 nan\ntan1014 tan -2.2999999999999998 nan -> nan nan\ntan1015 tan -inf nan -> nan nan\ntan1016 tan nan nan -> nan nan\ntan1017 tan 0.0 0.0 -> 0.0 0.0\ntan1018 tan inf 0.0 -> nan nan invalid\ntan1019 tan inf 2.2999999999999998 -> nan nan invalid\ntan1020 tan 0.0 inf -> 0.0 1.0\ntan1021 tan 0.69999999999999996 inf -> 0.0 1.0\ntan1022 tan 1.3999999999999999 inf -> 0.0 1.0\ntan1023 tan 2.1000000000000001 inf -> -0.0 1.0\ntan1024 tan 2.7999999999999998 inf -> -0.0 1.0\ntan1025 tan 3.5 inf -> 0.0 1.0\ntan1026 tan inf inf -> -0.0 1.0 ignore-real-sign\ntan1027 tan 0.0 nan -> 0.0 nan\ntan1028 tan 2.2999999999999998 nan -> nan nan\ntan1029 tan inf nan -> nan nan\ntan1030 tan 0.0 -0.0 -> 0.0 -0.0\ntan1031 tan inf -0.0 -> nan nan invalid\ntan1032 tan inf -2.2999999999999998 -> nan nan invalid\ntan1033 tan nan -0.0 -> nan nan\ntan1034 tan nan -2.2999999999999998 -> nan nan\ntan1035 tan 0.0 -inf -> 0.0 -1.0\ntan1036 tan 0.69999999999999996 -inf -> 0.0 -1.0\ntan1037 tan 1.3999999999999999 -inf -> 0.0 -1.0\ntan1038 tan 2.1000000000000001 -inf -> -0.0 -1.0\ntan1039 tan 2.7999999999999998 -inf -> -0.0 -1.0\ntan1040 tan 3.5 -inf -> 0.0 -1.0\ntan1041 tan inf -inf -> -0.0 -1.0 ignore-real-sign\ntan1042 tan nan -inf -> -0.0 -1.0 ignore-real-sign\ntan1043 tan -0.0 -0.0 -> -0.0 -0.0\ntan1044 tan -inf -0.0 -> nan nan invalid\ntan1045 tan -inf -2.2999999999999998 -> nan nan invalid\ntan1046 tan -0.0 -inf -> -0.0 -1.0\ntan1047 tan -0.69999999999999996 -inf -> -0.0 -1.0\ntan1048 tan -1.3999999999999999 -inf -> -0.0 -1.0\ntan1049 tan -2.1000000000000001 -inf -> 0.0 -1.0\ntan1050 tan -2.7999999999999998 -inf -> 0.0 -1.0\ntan1051 tan -3.5 -inf -> -0.0 -1.0\ntan1052 tan -inf -inf -> -0.0 -1.0 ignore-real-sign\n\n\n------------------------------------------------------------------------\n-- rect: Conversion from polar coordinates to rectangular coordinates --\n------------------------------------------------------------------------\n--\n-- For cmath.rect, we can use the same testcase syntax as for the\n-- complex -> complex functions above, but here the input arguments\n-- should be interpreted as a pair of floating-point numbers rather\n-- than the real and imaginary parts of a complex number.\n--\n-- Here are the 'spirit of C99' rules for rect.  First, the short\n-- version:\n--\n--    rect(x, t) = exp(log(x)+it) for positive-signed x\n--    rect(x, t) = -exp(log(-x)+it) for negative-signed x\n--    rect(nan, t) = exp(nan + it), except that in rect(nan, +-0) the\n--      sign of the imaginary part is unspecified.\n--\n-- and now the long version:\n--\n--   rect(x, -t) = conj(rect(x, t)) for all x and t\n--   rect(-x, t) = -rect(x, t) for all x and t\n--   rect(+0, +0) returns +0 + i0\n--   rect(+0, inf) returns +- 0 +- i0, where the signs of the real and\n--     imaginary parts are unspecified.\n--   rect(x, inf) returns NaN + i NaN and raises the \"invalid\"\n--     floating-point exception, for finite nonzero x.\n--   rect(inf, inf) returns +-inf + i NaN and raises the \"invalid\"\n--     floating-point exception (where the sign of the real part of the\n--     result is unspecified).\n--   rect(inf, +0) returns inf+i0\n--   rect(inf, x) returns inf*cis(x), for finite nonzero x\n--   rect(inf, NaN) returns +-inf+i NaN, where the sign of the real part\n--     of the result is unspecified.\n--   rect(NaN, x) returns NaN + i NaN for all nonzero numbers (including\n--     infinities) x\n--   rect(NaN, 0) returns NaN +- i0, where the sign of the imaginary\n--     part is unspecified\n--   rect(NaN, NaN) returns NaN + i NaN\n--   rect(x, NaN) returns NaN + i NaN for finite nonzero x\n--   rect(+0, NaN) return +-0 +- i0, where the signs of the real and\n--     imaginary parts are unspecified.\n\n-- special values\nrect1000 rect 0.0 0.0 -> 0.0 0.0\nrect1001 rect 0.0 inf -> 0.0 0.0        ignore-real-sign ignore-imag-sign\nrect1002 rect 2.3 inf -> nan nan        invalid\nrect1003 rect inf inf -> inf nan        invalid ignore-real-sign\nrect1004 rect inf 0.0 -> inf 0.0\nrect1005 rect inf 1.4 -> inf inf\nrect1006 rect inf 2.8 -> -inf inf\nrect1007 rect inf 4.2 -> -inf -inf\nrect1008 rect inf 5.6 -> inf -inf\nrect1009 rect inf 7.0 -> inf inf\nrect1010 rect nan 0.0 -> nan 0.0        ignore-imag-sign\nrect1011 rect nan 2.3 -> nan nan\nrect1012 rect nan inf -> nan nan\nrect1013 rect nan nan -> nan nan\nrect1014 rect inf nan -> inf nan        ignore-real-sign\nrect1015 rect 2.3 nan -> nan nan\nrect1016 rect 0.0 nan -> 0.0 0.0        ignore-real-sign ignore-imag-sign\nrect1017 rect 0.0 -0.0 -> 0.0 -0.0\nrect1018 rect 0.0 -inf -> 0.0 0.0       ignore-real-sign ignore-imag-sign\nrect1019 rect 2.3 -inf -> nan nan       invalid\nrect1020 rect inf -inf -> inf nan       invalid ignore-real-sign\nrect1021 rect inf -0.0 -> inf -0.0\nrect1022 rect inf -1.4 -> inf -inf\nrect1023 rect inf -2.8 -> -inf -inf\nrect1024 rect inf -4.2 -> -inf inf\nrect1025 rect inf -5.6 -> inf inf\nrect1026 rect inf -7.0 -> inf -inf\nrect1027 rect nan -0.0 -> nan 0.0       ignore-imag-sign\nrect1028 rect nan -2.3 -> nan nan\nrect1029 rect nan -inf -> nan nan\nrect1030 rect -0.0 0.0 -> -0.0 -0.0\nrect1031 rect -0.0 inf -> 0.0 0.0       ignore-real-sign ignore-imag-sign\nrect1032 rect -2.3 inf -> nan nan       invalid\nrect1033 rect -inf inf -> -inf nan      invalid ignore-real-sign\nrect1034 rect -inf 0.0 -> -inf -0.0\nrect1035 rect -inf 1.4 -> -inf -inf\nrect1036 rect -inf 2.8 -> inf -inf\nrect1037 rect -inf 4.2 -> inf inf\nrect1038 rect -inf 5.6 -> -inf inf\nrect1039 rect -inf 7.0 -> -inf -inf\nrect1040 rect -inf nan -> inf nan       ignore-real-sign\nrect1041 rect -2.3 nan -> nan nan\nrect1042 rect -0.0 nan -> 0.0 0.0       ignore-real-sign ignore-imag-sign\nrect1043 rect -0.0 -0.0 -> -0.0 0.0\nrect1044 rect -0.0 -inf -> 0.0 0.0      ignore-real-sign ignore-imag-sign\nrect1045 rect -2.3 -inf -> nan nan      invalid\nrect1046 rect -inf -inf -> -inf nan     invalid ignore-real-sign\nrect1047 rect -inf -0.0 -> -inf 0.0\nrect1048 rect -inf -1.4 -> -inf inf\nrect1049 rect -inf -2.8 -> inf inf\nrect1050 rect -inf -4.2 -> inf -inf\nrect1051 rect -inf -5.6 -> -inf -inf\nrect1052 rect -inf -7.0 -> -inf inf\n\n-------------------------------------------------------------------------\n-- polar: Conversion from rectangular coordinates to polar coordinates --\n-------------------------------------------------------------------------\n--\n-- For cmath.polar, we can use the same testcase syntax as for the\n-- complex -> complex functions above, but here the output arguments\n-- should be interpreted as a pair of floating-point numbers rather\n-- than the real and imaginary parts of a complex number.\n--\n-- Annex G of the C99 standard describes fully both the real and\n-- imaginary parts of polar (as cabs and carg, respectively, which in turn\n-- are defined in terms of the functions hypot and atan2).\n\n-- overflow\npolar0100 polar 1.4e308 1.4e308 -> inf 0.78539816339744828      overflow\n\n-- special values\npolar1000 polar 0.0 0.0 -> 0.0 0.0\npolar1001 polar 0.0 -0.0 -> 0.0 -0.0\npolar1002 polar -0.0 0.0 -> 0.0 3.1415926535897931\npolar1003 polar -0.0 -0.0 -> 0.0 -3.1415926535897931\npolar1004 polar inf 0.0 -> inf 0.0\npolar1005 polar inf 2.3 -> inf 0.0\npolar1006 polar inf inf -> inf 0.78539816339744828\npolar1007 polar 2.3 inf -> inf 1.5707963267948966\npolar1008 polar 0.0 inf -> inf 1.5707963267948966\npolar1009 polar -0.0 inf -> inf 1.5707963267948966\npolar1010 polar -2.3 inf -> inf 1.5707963267948966\npolar1011 polar -inf inf -> inf 2.3561944901923448\npolar1012 polar -inf 2.3 -> inf 3.1415926535897931\npolar1013 polar -inf 0.0 -> inf 3.1415926535897931\npolar1014 polar -inf -0.0 -> inf -3.1415926535897931\npolar1015 polar -inf -2.3 -> inf -3.1415926535897931\npolar1016 polar -inf -inf -> inf -2.3561944901923448\npolar1017 polar -2.3 -inf -> inf -1.5707963267948966\npolar1018 polar -0.0 -inf -> inf -1.5707963267948966\npolar1019 polar 0.0 -inf -> inf -1.5707963267948966\npolar1020 polar 2.3 -inf -> inf -1.5707963267948966\npolar1021 polar inf -inf -> inf -0.78539816339744828\npolar1022 polar inf -2.3 -> inf -0.0\npolar1023 polar inf -0.0 -> inf -0.0\npolar1024 polar nan -inf -> inf nan\npolar1025 polar nan -2.3 -> nan nan\npolar1026 polar nan -0.0 -> nan nan\npolar1027 polar nan 0.0 -> nan nan\npolar1028 polar nan 2.3 -> nan nan\npolar1029 polar nan inf -> inf nan\npolar1030 polar nan nan -> nan nan\npolar1031 polar inf nan -> inf nan\npolar1032 polar 2.3 nan -> nan nan\npolar1033 polar 0.0 nan -> nan nan\npolar1034 polar -0.0 nan -> nan nan\npolar1035 polar -2.3 nan -> nan nan\npolar1036 polar -inf nan -> inf nan\n"
  },
  {
    "path": "test/stdlib/datetime_test.codon",
    "content": "from datetime import *\nfrom unittest import TestCase\nfrom operator import lt, le, gt, ge, eq, ne, truediv, floordiv, mod\n\n\nclass TestTimeDelta(Static[TestCase]):\n    def test_constructor(self):\n        eq = self.assertEqual\n        td = timedelta\n\n        # Check keyword args to constructor\n        eq(\n            td(),\n            td(\n                weeks=0,\n                days=0,\n                hours=0,\n                minutes=0,\n                seconds=0,\n                milliseconds=0,\n                microseconds=0,\n            ),\n        )\n        eq(td(1), td(days=1))\n        eq(td(0, 1), td(seconds=1))\n        eq(td(0, 0, 1), td(microseconds=1))\n        eq(td(weeks=1), td(days=7))\n        eq(td(days=1), td(hours=24))\n        eq(td(hours=1), td(minutes=60))\n        eq(td(minutes=1), td(seconds=60))\n        eq(td(seconds=1), td(milliseconds=1000))\n        eq(td(milliseconds=1), td(microseconds=1000))\n\n        # Check float args to constructor\n        eq(td(weeks=1.0 / 7), td(days=1))\n        eq(td(days=1.0 / 24), td(hours=1))\n        eq(td(hours=1.0 / 60), td(minutes=1))\n        eq(td(minutes=1.0 / 60), td(seconds=1))\n        eq(td(seconds=0.001), td(milliseconds=1))\n        eq(td(milliseconds=0.001), td(microseconds=1))\n\n    def test_computations(self):\n        eq = self.assertEqual\n        td = timedelta\n\n        a = td(7)  # One week\n        b = td(0, 60)  # One minute\n        c = td(0, 0, 1000)  # One millisecond\n        eq(a + b + c, td(7, 60, 1000))\n        eq(a - b, td(6, 24 * 3600 - 60))\n        # eq(b.__rsub__(a), td(6, 24*3600 - 60))\n        eq(-a, td(-7))\n        eq(+a, td(7))\n        eq(-b, td(-1, 24 * 3600 - 60))\n        eq(-c, td(-1, 24 * 3600 - 1, 999000))\n        eq(abs(a), a)\n        eq(abs(-a), a)\n        eq(td(6, 24 * 3600), a)\n        eq(td(0, 0, 60 * 1000000), b)\n        eq(a * 10, td(70))\n        eq(a * 10, 10 * a)\n        eq(a * 10, 10 * a)\n        eq(b * 10, td(0, 600))\n        eq(10 * b, td(0, 600))\n        eq(b * 10, td(0, 600))\n        eq(c * 10, td(0, 0, 10000))\n        eq(10 * c, td(0, 0, 10000))\n        eq(c * 10, td(0, 0, 10000))\n        eq(a * -1, -a)\n        eq(b * -2, -b - b)\n        eq(c * -2, -c + -c)\n        eq(b * (60 * 24), (b * 60) * 24)\n        eq(b * (60 * 24), (60 * b) * 24)\n        eq(c * 1000, td(0, 1))\n        eq(1000 * c, td(0, 1))\n        eq(a // 7, td(1))\n        eq(b // 10, td(0, 6))\n        eq(c // 1000, td(0, 0, 1))\n        eq(a // 10, td(0, 7 * 24 * 360))\n        eq(a // 3600000, td(0, 0, 7 * 24 * 1000))\n        eq(a / 0.5, td(14))\n        eq(b / 0.5, td(0, 120))\n        eq(a / 7, td(1))\n        eq(b / 10, td(0, 6))\n        eq(c / 1000, td(0, 0, 1))\n        eq(a / 10, td(0, 7 * 24 * 360))\n        eq(a / 3600000, td(0, 0, 7 * 24 * 1000))\n\n        # Multiplication by float\n        us = td(microseconds=1)\n        eq((3 * us) * 0.5, 2 * us)\n        eq((5 * us) * 0.5, 2 * us)\n        eq(0.5 * (3 * us), 2 * us)\n        eq(0.5 * (5 * us), 2 * us)\n        eq((-3 * us) * 0.5, -2 * us)\n        eq((-5 * us) * 0.5, -2 * us)\n\n        # TODO: check Python's Issue #23521 and possibly\n        # incorporate the same fix here. For now a couple\n        # tests are disabled.\n\n        # Issue #23521\n        eq(td(seconds=1) * 0.123456, td(microseconds=123456))\n        # eq(td(seconds=1) * 0.6112295, td(microseconds=611229))\n\n        # Division by int and float\n        eq((3 * us) / 2, 2 * us)\n        eq((5 * us) / 2, 2 * us)\n        eq((-3 * us) / 2.0, -2 * us)\n        eq((-5 * us) / 2.0, -2 * us)\n        eq((3 * us) / -2, -2 * us)\n        eq((5 * us) / -2, -2 * us)\n        eq((3 * us) / -2.0, -2 * us)\n        eq((5 * us) / -2.0, -2 * us)\n        for i in range(-10, 10):\n            eq((i * us / 3) // us, round(i / 3))\n        for i in range(-10, 10):\n            eq((i * us / -3) // us, round(i / -3))\n\n        # Issue #23521\n        # eq(td(seconds=1) / (1 / 0.6112295), td(microseconds=611229))\n\n        # Issue #11576\n        eq(td(999999999, 86399, 999999) - td(999999999, 86399, 999998), td(0, 0, 1))\n        eq(td(999999999, 1, 1) - td(999999999, 1, 0), td(0, 0, 1))\n\n    \"\"\"\n    def test_disallowed_special(self):\n        a = timedelta(42)\n        NAN = 0. / 0.\n        self.assertRaises(ValueError, a.__mul__, NAN)\n        self.assertRaises(ValueError, a.__truediv__, NAN)\n    \"\"\"\n\n    def test_basic_attributes(self):\n        days, seconds, us = 1, 7, 31\n        td = timedelta(days, seconds, us)\n        self.assertEqual(td.days, days)\n        self.assertEqual(td.seconds, seconds)\n        self.assertEqual(td.microseconds, us)\n\n    def test_total_seconds(self):\n        td = timedelta(days=365)\n        self.assertEqual(td.total_seconds(), 31536000.0)\n        for total_seconds in [123456.789012, -123456.789012, 0.123456, 0, 1e6]:\n            td = timedelta(seconds=total_seconds)\n            self.assertEqual(td.total_seconds(), total_seconds)\n        # Issue8644: Test that td.total_seconds() has the same\n        # accuracy as td / timedelta(seconds=1).\n        for ms in [-1, -2, -123]:\n            td = timedelta(microseconds=ms)\n            self.assertEqual(td.total_seconds(), td / timedelta(seconds=1))\n\n    def test_carries(self):\n        t1 = timedelta(\n            days=100,\n            weeks=-7,\n            hours=-24 * (100 - 49),\n            minutes=-3,\n            seconds=12,\n            microseconds=(3 * 60 - 12) * 1e6 + 1,\n        )\n        t2 = timedelta(microseconds=1)\n        self.assertEqual(t1, t2)\n\n    def test_hash_equality(self):\n        t1 = timedelta(\n            days=100,\n            weeks=-7,\n            hours=-24 * (100 - 49),\n            minutes=-3,\n            seconds=12,\n            microseconds=(3 * 60 - 12) * 1000000,\n        )\n        t2 = timedelta()\n        self.assertEqual(hash(t1), hash(t2))\n\n        t1 += timedelta(weeks=7)\n        t2 += timedelta(days=7 * 7)\n        self.assertEqual(t1, t2)\n        self.assertEqual(hash(t1), hash(t2))\n\n        d = {t1: 1}\n        d[t2] = 2\n        self.assertEqual(len(d), 1)\n        self.assertEqual(d[t1], 2)\n\n    def test_compare(self):\n        t1 = timedelta(2, 3, 4)\n        t2 = timedelta(2, 3, 4)\n        self.assertEqual(t1, t2)\n        self.assertTrue(t1 <= t2)\n        self.assertTrue(t1 >= t2)\n        self.assertFalse(t1 != t2)\n        self.assertFalse(t1 < t2)\n        self.assertFalse(t1 > t2)\n\n        for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):\n            t2 = timedelta(*args)  # this is larger than t1\n            self.assertTrue(t1 < t2)\n            self.assertTrue(t2 > t1)\n            self.assertTrue(t1 <= t2)\n            self.assertTrue(t2 >= t1)\n            self.assertTrue(t1 != t2)\n            self.assertTrue(t2 != t1)\n            self.assertFalse(t1 == t2)\n            self.assertFalse(t2 == t1)\n            self.assertFalse(t1 > t2)\n            self.assertFalse(t2 < t1)\n            self.assertFalse(t1 >= t2)\n            self.assertFalse(t2 <= t1)\n\n    def test_str(self):\n        td = timedelta\n        eq = self.assertEqual\n\n        eq(str(td(1)), \"1 day, 0:00:00\")\n        eq(str(td(-1)), \"-1 day, 0:00:00\")\n        eq(str(td(2)), \"2 days, 0:00:00\")\n        eq(str(td(-2)), \"-2 days, 0:00:00\")\n\n        eq(str(td(hours=12, minutes=58, seconds=59)), \"12:58:59\")\n        eq(str(td(hours=2, minutes=3, seconds=4)), \"2:03:04\")\n        eq(str(td(weeks=-30, hours=23, minutes=12, seconds=34)), \"-210 days, 23:12:34\")\n\n        eq(str(td(milliseconds=1)), \"0:00:00.001000\")\n        eq(str(td(microseconds=3)), \"0:00:00.000003\")\n\n        # Codon's timedelta has a smaller range than Python's\n        # since it uses a pure microseconds representation, so\n        # below case is not supported.\n        \"\"\"\n        eq(str(td(days=999999999, hours=23, minutes=59, seconds=59,\n                   microseconds=999999)),\n           \"999999999 days, 23:59:59.999999\")\n        \"\"\"\n\n    def test_repr(self):\n        td = timedelta\n        self.assertEqual(repr(td(1)), \"timedelta(days=1)\")\n        self.assertEqual(repr(td(10, 2)), \"timedelta(days=10, seconds=2)\")\n        self.assertEqual(\n            repr(td(-10, 2, 400000)),\n            \"timedelta(days=-10, seconds=2, microseconds=400000)\",\n        )\n        self.assertEqual(repr(td(seconds=60)), \"timedelta(seconds=60)\")\n        self.assertEqual(repr(td()), \"timedelta(0)\")\n        self.assertEqual(repr(td(microseconds=100)), \"timedelta(microseconds=100)\")\n        self.assertEqual(\n            repr(td(days=1, microseconds=100)), \"timedelta(days=1, microseconds=100)\"\n        )\n        self.assertEqual(\n            repr(td(seconds=1, microseconds=100)),\n            \"timedelta(seconds=1, microseconds=100)\",\n        )\n\n    def test_resolution_info(self):\n        self.assertTrue(timedelta.max > timedelta.min)\n        #self.assertEqual(timedelta.min, timedelta(-999999999))\n        #self.assertEqual(timedelta.max, timedelta(999999999, 24*3600-1, 1e6-1))\n        self.assertEqual(timedelta.resolution, timedelta(0, 0, 1))\n\n    \"\"\"\n    def test_overflow(self):\n        tiny = timedelta(microseconds=1)  # timedelta.resolution\n\n        td = timedelta.min + tiny\n        td -= tiny  # no problem\n        self.assertRaises(OverflowError, td.__sub__, tiny)\n        self.assertRaises(OverflowError, td.__add__, -tiny)\n\n        td = timedelta.max - tiny\n        td += tiny  # no problem\n        self.assertRaises(OverflowError, td.__add__, tiny)\n        self.assertRaises(OverflowError, td.__sub__, -tiny)\n\n        self.assertRaises(OverflowError, lambda: -timedelta.max)\n\n        day = timedelta(1)\n        self.assertRaises(OverflowError, day.__mul__, 10**9)\n        self.assertRaises(OverflowError, day.__mul__, 1e9)\n        self.assertRaises(OverflowError, day.__truediv__, 1e-20)\n        self.assertRaises(OverflowError, day.__truediv__, 1e-10)\n        self.assertRaises(OverflowError, day.__truediv__, 9e-10)\n\n    def _test_overflow_special(self):\n        day = timedelta(1)\n        INF = 1. / 0.\n        self.assertRaises(OverflowError, day.__mul__, INF)\n        self.assertRaises(OverflowError, day.__mul__, -INF)\n    \"\"\"\n\n    def test_microsecond_rounding(self):\n        td = timedelta\n        eq = self.assertEqual\n\n        # Single-field rounding.\n        eq(td(milliseconds=0.4 / 1000), td(0))  # rounds to 0\n        eq(td(milliseconds=-0.4 / 1000), td(0))  # rounds to 0\n        eq(td(milliseconds=0.5 / 1000), td(microseconds=0))\n        eq(td(milliseconds=-0.5 / 1000), td(microseconds=-0))\n        eq(td(milliseconds=0.6 / 1000), td(microseconds=1))\n        eq(td(milliseconds=-0.6 / 1000), td(microseconds=-1))\n        eq(td(milliseconds=1.5 / 1000), td(microseconds=2))\n        eq(td(milliseconds=-1.5 / 1000), td(microseconds=-2))\n        eq(td(seconds=0.5 / 10 ** 6), td(microseconds=0))\n        eq(td(seconds=-0.5 / 10 ** 6), td(microseconds=-0))\n        eq(td(seconds=1 / 2 ** 7), td(microseconds=7812))\n        eq(td(seconds=-1 / 2 ** 7), td(microseconds=-7812))\n\n        # Rounding due to contributions from more than one field.\n        us_per_hour = 3600e6\n        us_per_day = us_per_hour * 24\n        eq(td(days=0.4 / us_per_day), td(0))\n        eq(td(hours=0.2 / us_per_hour), td(0))\n        eq(td(days=0.4 / us_per_day, hours=0.2 / us_per_hour), td(microseconds=1))\n\n        eq(td(days=-0.4 / us_per_day), td(0))\n        eq(td(hours=-0.2 / us_per_hour), td(0))\n        eq(td(days=-0.4 / us_per_day, hours=-0.2 / us_per_hour), td(microseconds=-1))\n\n        # Test for a patch in Issue 8860\n        eq(td(microseconds=0.5), 0.5 * td(microseconds=1.0))\n        resolution = td(microseconds=1)  # td.resolution\n        eq(td(microseconds=0.5) // resolution, 0.5 * resolution // resolution)\n\n    def test_massive_normalization(self):\n        td = timedelta(microseconds=-1)\n        self.assertEqual(\n            (td.days, td.seconds, td.microseconds), (-1, 24 * 3600 - 1, 999999)\n        )\n\n    def test_bool(self):\n        self.assertTrue(timedelta(1))\n        self.assertTrue(timedelta(0, 1))\n        self.assertTrue(timedelta(0, 0, 1))\n        self.assertTrue(timedelta(microseconds=1))\n        self.assertFalse(timedelta(0))\n\n    def test_division(self):\n        t = timedelta(hours=1, minutes=24, seconds=19)\n        second = timedelta(seconds=1)\n        self.assertEqual(t / second, 5059.0)\n        self.assertEqual(t // second, 5059)\n\n        t = timedelta(minutes=2, seconds=30)\n        minute = timedelta(minutes=1)\n        self.assertEqual(t / minute, 2.5)\n        self.assertEqual(t // minute, 2)\n\n        zerotd = timedelta(0)\n        # self.assertRaises(ZeroDivisionError, truediv, t, zerotd)\n        # self.assertRaises(ZeroDivisionError, floordiv, t, zerotd)\n\n    def test_remainder(self):\n        t = timedelta(minutes=2, seconds=30)\n        minute = timedelta(minutes=1)\n        r = t % minute\n        self.assertEqual(r, timedelta(seconds=30))\n\n        t = timedelta(minutes=-2, seconds=30)\n        r = t % minute\n        self.assertEqual(r, timedelta(seconds=30))\n\n        zerotd = timedelta(0)\n        # self.assertRaises(ZeroDivisionError, mod, t, zerotd)\n\n    def test_divmod(self):\n        t = timedelta(minutes=2, seconds=30)\n        minute = timedelta(minutes=1)\n        q, r = divmod(t, minute)\n        self.assertEqual(q, 2)\n        self.assertEqual(r, timedelta(seconds=30))\n\n        t = timedelta(minutes=-2, seconds=30)\n        q, r = divmod(t, minute)\n        self.assertEqual(q, -2)\n        self.assertEqual(r, timedelta(seconds=30))\n\n        zerotd = timedelta(0)\n        # self.assertRaises(ZeroDivisionError, divmod, t, zerotd)\n\n\nclass TestDateOnly(Static[TestCase]):\n    def test_delta_non_days_ignored(self):\n        dt = date(2000, 1, 2)\n        delta = timedelta(days=1, hours=2, minutes=3, seconds=4, microseconds=5)\n        days = timedelta(delta.days)\n        self.assertEqual(days, timedelta(1))\n\n        dt2 = dt + delta\n        self.assertEqual(dt2, dt + days)\n\n        dt2 = delta + dt\n        self.assertEqual(dt2, dt + days)\n\n        dt2 = dt - delta\n        self.assertEqual(dt2, dt - days)\n\n        delta = -delta\n        days = timedelta(delta.days)\n        self.assertEqual(days, timedelta(-2))\n\n        dt2 = dt + delta\n        self.assertEqual(dt2, dt + days)\n\n        dt2 = delta + dt\n        self.assertEqual(dt2, dt + days)\n\n        dt2 = dt - delta\n        self.assertEqual(dt2, dt - days)\n\n\nclass TestDate(Static[TestCase]):\n    theclass: type\n\n    def test_basic_attributes(self):\n        dt = self.theclass(2002, 3, 1)\n        self.assertEqual(dt.year, 2002)\n        self.assertEqual(dt.month, 3)\n        self.assertEqual(dt.day, 1)\n\n    def test_ordinal_conversions(self):\n        # Check some fixed values.\n        for y, m, d, n in [\n            (1, 1, 1, 1),  # calendar origin\n            (1, 12, 31, 365),\n            (2, 1, 1, 366),\n            # first example from \"Calendrical Calculations\"\n            (1945, 11, 12, 710347),\n        ]:\n            dt = self.theclass(y, m, d)\n            self.assertEqual(n, dt.toordinal())\n            fromord = self.theclass.fromordinal(n)\n            self.assertEqual(dt, fromord)\n            if hasattr(fromord, \"hour\"):\n                # if we're checking something fancier than a date, verify\n                # the extra fields have been zeroed out\n                self.assertEqual(fromord.hour, 0)\n                self.assertEqual(fromord.minute, 0)\n                self.assertEqual(fromord.second, 0)\n                self.assertEqual(fromord.microsecond, 0)\n\n        # Check first and last days of year spottily across the whole\n        # range of years supported.\n        for year in range(MINYEAR, MAXYEAR + 1, 7):\n            # Verify (year, 1, 1) -> ordinal -> y, m, d is identity.\n            d = self.theclass(year, 1, 1)\n            n = d.toordinal()\n            d2 = self.theclass.fromordinal(n)\n            self.assertEqual(d, d2)\n            # Verify that moving back a day gets to the end of year-1.\n            if year > 1:\n                d = self.theclass.fromordinal(n - 1)\n                d2 = self.theclass(year - 1, 12, 31)\n                self.assertEqual(d, d2)\n                self.assertEqual(d2.toordinal(), n - 1)\n\n        # Test every day in a leap-year and a non-leap year.\n        dim = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]\n        for year, isleap in (2000, True), (2002, False):\n            n = self.theclass(year, 1, 1).toordinal()\n            for month, maxday in zip(range(1, 13), dim):\n                if month == 2 and isleap:\n                    maxday += 1\n                for day in range(1, maxday + 1):\n                    d = self.theclass(year, month, day)\n                    self.assertEqual(d.toordinal(), n)\n                    self.assertEqual(d, self.theclass.fromordinal(n))\n                    n += 1\n\n    \"\"\"\n    def test_extreme_ordinals(self):\n        a = self.theclass.min\n        a = self.theclass(a.year, a.month, a.day)  # get rid of time parts\n        aord = a.toordinal()\n        b = a.fromordinal(aord)\n        self.assertEqual(a, b)\n\n        self.assertRaises(ValueError, lambda: a.fromordinal(aord - 1))\n\n        b = a + timedelta(days=1)\n        self.assertEqual(b.toordinal(), aord + 1)\n        self.assertEqual(b, self.theclass.fromordinal(aord + 1))\n\n        a = self.theclass.max\n        a = self.theclass(a.year, a.month, a.day)  # get rid of time parts\n        aord = a.toordinal()\n        b = a.fromordinal(aord)\n        self.assertEqual(a, b)\n\n        self.assertRaises(ValueError, lambda: a.fromordinal(aord + 1))\n\n        b = a - timedelta(days=1)\n        self.assertEqual(b.toordinal(), aord - 1)\n        self.assertEqual(b, self.theclass.fromordinal(aord - 1))\n    \"\"\"\n\n    def test_bad_constructor_arguments(self):\n        # bad years\n        self.theclass(MINYEAR, 1, 1)  # no exception\n        self.theclass(MAXYEAR, 1, 1)  # no exception\n\n        def make(theclass, a, b, c):\n            return self.theclass(a, b, c)\n\n        self.assertRaises(ValueError, make(self.theclass, ...), MINYEAR - 1, 1, 1)\n        self.assertRaises(ValueError, make(self.theclass, ...), MAXYEAR + 1, 1, 1)\n        # bad months\n        self.theclass(2000, 1, 1)  # no exception\n        self.theclass(2000, 12, 1)  # no exception\n        self.assertRaises(ValueError, make(self.theclass, ...), 2000, 0, 1)\n        self.assertRaises(ValueError, make(self.theclass, ...), 2000, 13, 1)\n        # bad days\n        self.theclass(2000, 2, 29)  # no exception\n        self.theclass(2004, 2, 29)  # no exception\n        self.theclass(2400, 2, 29)  # no exception\n        self.assertRaises(ValueError, make(self.theclass, ...), 2000, 2, 30)\n        self.assertRaises(ValueError, make(self.theclass, ...), 2001, 2, 29)\n        self.assertRaises(ValueError, make(self.theclass, ...), 2100, 2, 29)\n        self.assertRaises(ValueError, make(self.theclass, ...), 1900, 2, 29)\n        self.assertRaises(ValueError, make(self.theclass, ...), 2000, 1, 0)\n        self.assertRaises(ValueError, make(self.theclass, ...), 2000, 1, 32)\n\n    def test_hash_equality(self):\n        d = self.theclass(2000, 12, 31)\n        # same thing\n        e = self.theclass(2000, 12, 31)\n        self.assertEqual(d, e)\n        self.assertEqual(hash(d), hash(e))\n\n        dic = {d: 1}\n        dic[e] = 2\n        self.assertEqual(len(dic), 1)\n        self.assertEqual(dic[d], 2)\n        self.assertEqual(dic[e], 2)\n\n        d = self.theclass(2001, 1, 1)\n        # same thing\n        e = self.theclass(2001, 1, 1)\n        self.assertEqual(d, e)\n        self.assertEqual(hash(d), hash(e))\n\n        dic = {d: 1}\n        dic[e] = 2\n        self.assertEqual(len(dic), 1)\n        self.assertEqual(dic[d], 2)\n        self.assertEqual(dic[e], 2)\n\n    def test_computations(self):\n        a = self.theclass(2002, 1, 31)\n        b = self.theclass(1956, 1, 31)\n        c = self.theclass(2001, 2, 1)\n\n        diff = a - b\n        self.assertEqual(diff.days, 46 * 365 + len(range(1956, 2002, 4)))\n        self.assertEqual(diff.seconds, 0)\n        self.assertEqual(diff.microseconds, 0)\n\n        day = timedelta(1)\n        week = timedelta(7)\n        a = self.theclass(2002, 3, 2)\n        self.assertEqual(a + day, self.theclass(2002, 3, 3))\n        self.assertEqual(day + a, self.theclass(2002, 3, 3))\n        self.assertEqual(a - day, self.theclass(2002, 3, 1))\n        self.assertEqual(-day + a, self.theclass(2002, 3, 1))\n        self.assertEqual(a + week, self.theclass(2002, 3, 9))\n        self.assertEqual(a - week, self.theclass(2002, 2, 23))\n        self.assertEqual(a + 52 * week, self.theclass(2003, 3, 1))\n        self.assertEqual(a - 52 * week, self.theclass(2001, 3, 3))\n        self.assertEqual((a + week) - a, week)\n        self.assertEqual((a + day) - a, day)\n        self.assertEqual((a - week) - a, -week)\n        self.assertEqual((a - day) - a, -day)\n        self.assertEqual(a - (a + week), -week)\n        self.assertEqual(a - (a + day), -day)\n        self.assertEqual(a - (a - week), week)\n        self.assertEqual(a - (a - day), day)\n        self.assertEqual(c - (c - day), day)\n\n        \"\"\"\n        # Add/sub ints or floats should be illegal\n        for i in 1, 1.0:\n            self.assertRaises(TypeError, lambda: a+i)\n            self.assertRaises(TypeError, lambda: a-i)\n            self.assertRaises(TypeError, lambda: i+a)\n            self.assertRaises(TypeError, lambda: i-a)\n\n        # delta - date is senseless.\n        self.assertRaises(TypeError, lambda: day - a)\n        # mixing date and (delta or date) via * or // is senseless\n        self.assertRaises(TypeError, lambda: day * a)\n        self.assertRaises(TypeError, lambda: a * day)\n        self.assertRaises(TypeError, lambda: day // a)\n        self.assertRaises(TypeError, lambda: a // day)\n        self.assertRaises(TypeError, lambda: a * a)\n        self.assertRaises(TypeError, lambda: a // a)\n        # date + date is senseless\n        self.assertRaises(TypeError, lambda: a + a)\n        \"\"\"\n\n    \"\"\"\n    def test_overflow(self):\n        tiny = self.theclass.resolution\n\n        for delta in [tiny, timedelta(1), timedelta(2)]:\n            dt = self.theclass.min + delta\n            dt -= delta  # no problem\n            self.assertRaises(OverflowError, dt.__sub__, delta)\n            self.assertRaises(OverflowError, dt.__add__, -delta)\n\n            dt = self.theclass.max - delta\n            dt += delta  # no problem\n            self.assertRaises(OverflowError, dt.__add__, delta)\n            self.assertRaises(OverflowError, dt.__sub__, -delta)\n    \"\"\"\n\n    def test_fromtimestamp(self):\n        import time\n\n        # Try an arbitrary fixed value.\n        year, month, day = 1999, 9, 19\n        ts = time.mktime((year, month, day, 0, 0, 0, 0, 0, -1))\n        d = self.theclass.fromtimestamp(ts)\n        self.assertEqual(d.year, year)\n        self.assertEqual(d.month, month)\n        self.assertEqual(d.day, day)\n\n    def test_today(self):\n        import time\n\n        # We claim that today() is like fromtimestamp(time.time()), so\n        # prove it.\n        today = self.theclass.today()\n        todayagain = today\n\n        for dummy in range(3):\n            today = self.theclass.today()\n            ts = time.time()\n            todayagain = self.theclass.fromtimestamp(ts)\n            if today == todayagain:\n                break\n            # There are several legit reasons that could fail:\n            # 1. It recently became midnight, between the today() and the\n            #    time() calls.\n            # 2. The platform time() has such fine resolution that we'll\n            #    never get the same value twice.\n            # 3. The platform time() has poor resolution, and we just\n            #    happened to call today() right before a resolution quantum\n            #    boundary.\n            # 4. The system clock got fiddled between calls.\n            # In any case, wait a little while and try again.\n            time.sleep(0.1)\n\n        # It worked or it didn't.  If it didn't, assume it's reason #2, and\n        # let the test pass if they're within half a second of each other.\n        if today != todayagain:\n            self.assertAlmostEqual(todayagain, today, delta=timedelta(seconds=0.5))\n\n    def test_weekday(self):\n        for i in range(7):\n            # March 4, 2002 is a Monday\n            self.assertEqual(self.theclass(2002, 3, 4 + i).weekday(), i)\n            self.assertEqual(self.theclass(2002, 3, 4 + i).isoweekday(), i + 1)\n            # January 2, 1956 is a Monday\n            self.assertEqual(self.theclass(1956, 1, 2 + i).weekday(), i)\n            self.assertEqual(self.theclass(1956, 1, 2 + i).isoweekday(), i + 1)\n\n    def test_isocalendar(self):\n        # Check examples from\n        # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n        week_mondays = [\n            ((2003, 12, 22), (2003, 52, 1)),\n            ((2003, 12, 29), (2004, 1, 1)),\n            ((2004, 1, 5), (2004, 2, 1)),\n            ((2009, 12, 21), (2009, 52, 1)),\n            ((2009, 12, 28), (2009, 53, 1)),\n            ((2010, 1, 4), (2010, 1, 1)),\n        ]\n\n        test_cases = []\n        for cal_date, iso_date in week_mondays:\n            base_date = self.theclass(*cal_date)\n            # Adds one test case for every day of the specified weeks\n            for i in range(7):\n                new_date = base_date + timedelta(i)\n                new_iso = iso_date[0:2] + (iso_date[2] + i,)\n                test_cases.append((new_date, new_iso))\n\n        for d, exp_iso in test_cases:\n            self.assertEqual(tuple(d.isocalendar()), exp_iso)\n\n            # Check that the tuple contents are accessible by field name\n            t = d.isocalendar()\n            self.assertEqual((t.year, t.week, t.weekday), exp_iso)\n\n    def test_iso_long_years(self):\n        # Calculate long ISO years and compare to table from\n        # http://www.phys.uu.nl/~vgent/calendar/isocalendar.htm\n        ISO_LONG_YEARS_TABLE = \"\"\"\n              4   32   60   88\n              9   37   65   93\n             15   43   71   99\n             20   48   76\n             26   54   82\n            105  133  161  189\n            111  139  167  195\n            116  144  172\n            122  150  178\n            128  156  184\n            201  229  257  285\n            207  235  263  291\n            212  240  268  296\n            218  246  274\n            224  252  280\n            303  331  359  387\n            308  336  364  392\n            314  342  370  398\n            320  348  376\n            325  353  381\n        \"\"\"\n        iso_long_years = sorted(map(int, ISO_LONG_YEARS_TABLE.split()))\n        L = []\n        for i in range(400):\n            d = self.theclass(2000 + i, 12, 31).isocalendar()\n            d1 = self.theclass(1600 + i, 12, 31).isocalendar()\n            self.assertEqual((d.week, d.weekday), (d1.week, d1.weekday))\n            if d.week == 53:\n                L.append(i)\n        self.assertEqual(L, iso_long_years)\n\n    def test_isoformat(self):\n        t = self.theclass(2, 3, 2)\n        self.assertEqual(t.isoformat(), \"0002-03-02\")\n\n    def test_ctime(self):\n        t = self.theclass(2002, 3, 2)\n        self.assertEqual(t.ctime(), \"Sat Mar  2 00:00:00 2002\")\n\n    def test_resolution_info(self):\n        self.assertTrue(self.theclass.max > self.theclass.min)\n\n    \"\"\"\n    def test_extreme_timedelta(self):\n        big = self.theclass.max - self.theclass.min\n        # 3652058 days, 23 hours, 59 minutes, 59 seconds, 999999 microseconds\n        n = (big.days*24*3600 + big.seconds)*1000000 + big.microseconds\n        # n == 315537897599999999 ~= 2**58.13\n        justasbig = timedelta(0, 0, n)\n        self.assertEqual(big, justasbig)\n        self.assertEqual(self.theclass.min + big, self.theclass.max)\n        self.assertEqual(self.theclass.max - big, self.theclass.min)\n    \"\"\"\n\n    def test_timetuple(self):\n        from time import struct_time\n\n        for i in range(7):\n            # January 2, 1956 is a Monday (0)\n            d = self.theclass(1956, 1, 2 + i)\n            t = d.timetuple()\n            self.assertEqual(t, struct_time(1956, 1, 2 + i, 0, 0, 0, i, 2 + i, -1))\n            # February 1, 1956 is a Wednesday (2)\n            d = self.theclass(1956, 2, 1 + i)\n            t = d.timetuple()\n            self.assertEqual(\n                t, struct_time(1956, 2, 1 + i, 0, 0, 0, (2 + i) % 7, 32 + i, -1)\n            )\n            # March 1, 1956 is a Thursday (3), and is the 31+29+1 = 61st day\n            # of the year.\n            d = self.theclass(1956, 3, 1 + i)\n            t = d.timetuple()\n            self.assertEqual(\n                t, struct_time(1956, 3, 1 + i, 0, 0, 0, (3 + i) % 7, 61 + i, -1)\n            )\n            self.assertEqual(t.tm_year, 1956)\n            self.assertEqual(t.tm_mon, 3)\n            self.assertEqual(t.tm_mday, 1 + i)\n            self.assertEqual(t.tm_hour, 0)\n            self.assertEqual(t.tm_min, 0)\n            self.assertEqual(t.tm_sec, 0)\n            self.assertEqual(t.tm_wday, (3 + i) % 7)\n            self.assertEqual(t.tm_yday, 61 + i)\n            self.assertEqual(t.tm_isdst, -1)\n\n    def test_compare(self):\n        t1 = self.theclass(2, 3, 4)\n        t2 = self.theclass(2, 3, 4)\n        self.assertEqual(t1, t2)\n        self.assertTrue(t1 <= t2)\n        self.assertTrue(t1 >= t2)\n        self.assertFalse(t1 != t2)\n        self.assertFalse(t1 < t2)\n        self.assertFalse(t1 > t2)\n\n        for args in (3, 3, 3), (2, 4, 4), (2, 3, 5):\n            t2 = self.theclass(*args)  # this is larger than t1\n            self.assertTrue(t1 < t2)\n            self.assertTrue(t2 > t1)\n            self.assertTrue(t1 <= t2)\n            self.assertTrue(t2 >= t1)\n            self.assertTrue(t1 != t2)\n            self.assertTrue(t2 != t1)\n            self.assertFalse(t1 == t2)\n            self.assertFalse(t2 == t1)\n            self.assertFalse(t1 > t2)\n            self.assertFalse(t2 < t1)\n            self.assertFalse(t1 >= t2)\n            self.assertFalse(t2 <= t1)\n\n        \"\"\"\n        for badarg in OTHERSTUFF:\n            self.assertEqual(t1 == badarg, False)\n            self.assertEqual(t1 != badarg, True)\n            self.assertEqual(badarg == t1, False)\n            self.assertEqual(badarg != t1, True)\n\n            self.assertRaises(TypeError, lambda: t1 < badarg)\n            self.assertRaises(TypeError, lambda: t1 > badarg)\n            self.assertRaises(TypeError, lambda: t1 >= badarg)\n            self.assertRaises(TypeError, lambda: badarg <= t1)\n            self.assertRaises(TypeError, lambda: badarg < t1)\n            self.assertRaises(TypeError, lambda: badarg > t1)\n            self.assertRaises(TypeError, lambda: badarg >= t1)\n        \"\"\"\n\n    def test_bool(self):\n        # All dates are considered true.\n        # self.assertTrue(self.theclass.min)\n        # self.assertTrue(self.theclass.max)\n        self.assertTrue(self.theclass(1, 1, 1))\n\n    def test_replace(self):\n        cls = self.theclass\n        args = (1, 2, 3)\n        base = cls(*args)\n        self.assertEqual(base, base.replace())\n        self.assertEqual(base.replace(year=2), cls(2, 2, 3))\n        self.assertEqual(base.replace(month=3), cls(1, 3, 3))\n        self.assertEqual(base.replace(day=4), cls(1, 2, 4))\n\n        # Out of bounds.\n        base = cls(2000, 2, 29)\n        self.assertRaises(ValueError, base.replace, year=2001)\n\n    def test_fromisoformat(self):\n        # Test that isoformat() is reversible\n        base_dates = [\n            (1, 1, 1),\n            (1000, 2, 14),\n            (1900, 1, 1),\n            (2000, 2, 29),\n            (2004, 11, 12),\n            (2004, 4, 3),\n            (2017, 5, 30),\n        ]\n\n        for dt_tuple in base_dates:\n            dt = self.theclass(*dt_tuple)\n            dt_str = dt.isoformat()\n            dt_rt = self.theclass.fromisoformat(dt.isoformat())\n            self.assertEqual(dt, dt_rt)\n\n    def test_fromisoformat_fails(self):\n        # Test that fromisoformat() fails on invalid values\n        bad_strs = [\n            \"\",  # Empty string\n            \"\\ud800\",  # bpo-34454: Surrogate code point\n            \"009-03-04\",  # Not 10 characters\n            \"123456789\",  # Not a date\n            \"200a-12-04\",  # Invalid character in year\n            \"2009-1a-04\",  # Invalid character in month\n            \"2009-12-0a\",  # Invalid character in day\n            \"2009-01-32\",  # Invalid day\n            \"2009-02-29\",  # Invalid leap day\n            \"20090228\",  # Valid ISO8601 output not from isoformat()\n            \"2009\\ud80002\\ud80028\",  # Separators are surrogate codepoints\n        ]\n\n        for bad_str in bad_strs:\n            self.assertRaises(ValueError, self.theclass.fromisoformat, bad_str)\n\n    def test_fromisocalendar(self):\n        # For each test case, assert that fromisocalendar is the\n        # inverse of the isocalendar function\n        dates = [\n            (2016, 4, 3),\n            (2005, 1, 2),  # (2004, 53, 7)\n            (2008, 12, 30),  # (2009, 1, 2)\n            (2010, 1, 2),  # (2009, 53, 6)\n            (2009, 12, 31),  # (2009, 53, 4)\n            (1900, 1, 1),  # Unusual non-leap year (year % 100 == 0)\n            (1900, 12, 31),\n            (2000, 1, 1),  # Unusual leap year (year % 400 == 0)\n            (2000, 12, 31),\n            (2004, 1, 1),  # Leap year\n            (2004, 12, 31),\n            (1, 1, 1),\n            (9999, 12, 31),\n            (MINYEAR, 1, 1),\n            (MAXYEAR, 12, 31),\n        ]\n\n        for datecomps in dates:\n            dobj = self.theclass(*datecomps)\n            isocal = dobj.isocalendar()\n\n            d_roundtrip = self.theclass.fromisocalendar(*isocal)\n\n            self.assertEqual(dobj, d_roundtrip)\n\n    def test_fromisocalendar_value_errors(self):\n        isocals = [\n            (2019, 0, 1),\n            (2019, -1, 1),\n            (2019, 54, 1),\n            (2019, 1, 0),\n            (2019, 1, -1),\n            (2019, 1, 8),\n            (2019, 53, 1),\n            (10000, 1, 1),\n            (0, 1, 1),\n            (9999999, 1, 1),\n            (2 << 32, 1, 1),\n            (2019, 2 << 32, 1),\n            (2019, 1, 2 << 32),\n        ]\n\n        for isocal in isocals:\n            self.assertRaises(ValueError, self.theclass.fromisocalendar, *isocal)\n\n\nclass TestDateTime(Static[TestDate[theclass]]):\n    theclass: type\n\n    def test_basic_attributes(self):\n        dt = self.theclass(2002, 3, 1, 12, 0)\n        self.assertEqual(dt.year, 2002)\n        self.assertEqual(dt.month, 3)\n        self.assertEqual(dt.day, 1)\n        self.assertEqual(dt.hour, 12)\n        self.assertEqual(dt.minute, 0)\n        self.assertEqual(dt.second, 0)\n        self.assertEqual(dt.microsecond, 0)\n\n    def test_basic_attributes_nonzero(self):\n        # Make sure all attributes are non-zero so bugs in\n        # bit-shifting access show up.\n        dt = self.theclass(2002, 3, 1, 12, 59, 59, 8000)\n        self.assertEqual(dt.year, 2002)\n        self.assertEqual(dt.month, 3)\n        self.assertEqual(dt.day, 1)\n        self.assertEqual(dt.hour, 12)\n        self.assertEqual(dt.minute, 59)\n        self.assertEqual(dt.second, 59)\n        self.assertEqual(dt.microsecond, 8000)\n\n    def test_isoformat(self):\n        t = self.theclass(1, 2, 3, 4, 5, 1, 123)\n        self.assertEqual(t.isoformat(), \"0001-02-03T04:05:01.000123\")\n        self.assertEqual(t.isoformat(\"T\"), \"0001-02-03T04:05:01.000123\")\n        self.assertEqual(t.isoformat(\" \"), \"0001-02-03 04:05:01.000123\")\n        self.assertEqual(t.isoformat(\"\\x00\"), \"0001-02-03\\x0004:05:01.000123\")\n        # bpo-34482: Check that surrogates are handled properly.\n        self.assertEqual(t.isoformat(\"\\ud800\"), \"0001-02-03\\ud80004:05:01.000123\")\n        self.assertEqual(t.isoformat(timespec=\"hours\"), \"0001-02-03T04\")\n        self.assertEqual(t.isoformat(timespec=\"minutes\"), \"0001-02-03T04:05\")\n        self.assertEqual(t.isoformat(timespec=\"seconds\"), \"0001-02-03T04:05:01\")\n        self.assertEqual(\n            t.isoformat(timespec=\"milliseconds\"), \"0001-02-03T04:05:01.000\"\n        )\n        self.assertEqual(\n            t.isoformat(timespec=\"microseconds\"), \"0001-02-03T04:05:01.000123\"\n        )\n        self.assertEqual(t.isoformat(timespec=\"auto\"), \"0001-02-03T04:05:01.000123\")\n        self.assertEqual(t.isoformat(sep=\" \", timespec=\"minutes\"), \"0001-02-03 04:05\")\n        # str is ISO format with the separator forced to a blank.\n        self.assertEqual(str(t), \"0001-02-03 04:05:01.000123\")\n\n        # t = self.theclass(1, 2, 3, 4, 5, 1, 999500, tzinfo=timezone.utc)\n        # self.assertEqual(t.isoformat(timespec='milliseconds'), \"0001-02-03T04:05:01.999+00:00\")\n\n        t = self.theclass(1, 2, 3, 4, 5, 1, 999500)\n        self.assertEqual(\n            t.isoformat(timespec=\"milliseconds\"), \"0001-02-03T04:05:01.999\"\n        )\n\n        t = self.theclass(1, 2, 3, 4, 5, 1)\n        self.assertEqual(t.isoformat(timespec=\"auto\"), \"0001-02-03T04:05:01\")\n        self.assertEqual(\n            t.isoformat(timespec=\"milliseconds\"), \"0001-02-03T04:05:01.000\"\n        )\n        self.assertEqual(\n            t.isoformat(timespec=\"microseconds\"), \"0001-02-03T04:05:01.000000\"\n        )\n\n        t = self.theclass(2, 3, 2)\n        self.assertEqual(t.isoformat(), \"0002-03-02T00:00:00\")\n        self.assertEqual(t.isoformat(\"T\"), \"0002-03-02T00:00:00\")\n        self.assertEqual(t.isoformat(\" \"), \"0002-03-02 00:00:00\")\n        # str is ISO format with the separator forced to a blank.\n        self.assertEqual(str(t), \"0002-03-02 00:00:00\")\n        # ISO format with timezone\n        # tz = FixedOffset(timedelta(seconds=16), 'XXX')\n        # t = self.theclass(2, 3, 2, tzinfo=tz)\n        # self.assertEqual(t.isoformat(), \"0002-03-02T00:00:00+00:00:16\")\n\n    \"\"\"\n    def test_isoformat_timezone(self):\n        tzoffsets = [\n            ('05:00', timedelta(hours=5)),\n            ('02:00', timedelta(hours=2)),\n            ('06:27', timedelta(hours=6, minutes=27)),\n            ('12:32:30', timedelta(hours=12, minutes=32, seconds=30)),\n            ('02:04:09.123456', timedelta(hours=2, minutes=4, seconds=9, microseconds=123456))\n        ]\n\n        tzinfos = [\n            ('', None),\n            ('+00:00', timezone.utc),\n            ('+00:00', timezone(timedelta(0))),\n        ]\n\n        tzinfos += [\n            (prefix + expected, timezone(sign * td))\n            for expected, td in tzoffsets\n            for prefix, sign in [('-', -1), ('+', 1)]\n        ]\n\n        dt_base = self.theclass(2016, 4, 1, 12, 37, 9)\n        exp_base = '2016-04-01T12:37:09'\n\n        for exp_tz, tzi in tzinfos:\n            dt = dt_base.replace(tzinfo=tzi)\n            exp = exp_base + exp_tz\n            with self.subTest(tzi=tzi):\n                assert dt.isoformat() == exp\n    \"\"\"\n\n    \"\"\"\n    def test_more_ctime(self):\n        # Test fields that TestDate doesn't touch.\n        import time\n\n        t = self.theclass(2002, 3, 2, 18, 3, 5, 123)\n        self.assertEqual(t.ctime(), \"Sat Mar  2 18:03:05 2002\")\n        # Oops!  The next line fails on Win2K under MSVC 6, so it's commented\n        # out.  The difference is that t.ctime() produces \" 2\" for the day,\n        # but platform ctime() produces \"02\" for the day.  According to\n        # C99, t.ctime() is correct here.\n        # self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))\n\n        # So test a case where that difference doesn't matter.\n        t = self.theclass(2002, 3, 22, 18, 3, 5, 123)\n        self.assertEqual(t.ctime(), time.ctime(time.mktime(t.timetuple())))\n    \"\"\"\n\n    def test_tz_independent_comparing(self):\n        dt1 = self.theclass(2002, 3, 1, 9, 0, 0)\n        dt2 = self.theclass(2002, 3, 1, 10, 0, 0)\n        dt3 = self.theclass(2002, 3, 1, 9, 0, 0)\n        self.assertEqual(dt1, dt3)\n        self.assertTrue(dt2 > dt3)\n\n        # Make sure comparison doesn't forget microseconds, and isn't done\n        # via comparing a float timestamp (an IEEE double doesn't have enough\n        # precision to span microsecond resolution across years 1 through 9999,\n        # so comparing via timestamp necessarily calls some distinct values\n        # equal).\n        dt1 = self.theclass(MAXYEAR, 12, 31, 23, 59, 59, 999998)\n        us = timedelta(microseconds=1)\n        dt2 = dt1 + us\n        self.assertEqual(dt2 - dt1, us)\n        self.assertTrue(dt1 < dt2)\n\n    def test_bad_constructor_arguments(self):\n        # bad years\n        self.theclass(MINYEAR, 1, 1)  # no exception\n        self.theclass(MAXYEAR, 1, 1)  # no exception\n\n        make_dt1 = lambda a, b, c: self.theclass(a, b, c)\n        make_dt2 = lambda a, b, c, d: self.theclass(a, b, c, d)\n        make_dt3 = lambda a, b, c, d, e: self.theclass(a, b, c, d, e)\n        make_dt4 = lambda a, b, c, d, e, f: self.theclass(a, b, c, d, e, f)\n        make_dt5 = lambda a, b, c, d, e, f, g: self.theclass(a, b, c, d, e, f, g)\n\n        self.assertRaises(ValueError, make_dt1, MINYEAR - 1, 1, 1)\n        self.assertRaises(ValueError, make_dt1, MAXYEAR + 1, 1, 1)\n        # bad months\n        self.theclass(2000, 1, 1)  # no exception\n        self.theclass(2000, 12, 1)  # no exception\n        self.assertRaises(ValueError, make_dt1, 2000, 0, 1)\n        self.assertRaises(ValueError, make_dt1, 2000, 13, 1)\n        # bad days\n        self.theclass(2000, 2, 29)  # no exception\n        self.theclass(2004, 2, 29)  # no exception\n        self.theclass(2400, 2, 29)  # no exception\n        self.assertRaises(ValueError, make_dt1, 2000, 2, 30)\n        self.assertRaises(ValueError, make_dt1, 2001, 2, 29)\n        self.assertRaises(ValueError, make_dt1, 2100, 2, 29)\n        self.assertRaises(ValueError, make_dt1, 1900, 2, 29)\n        self.assertRaises(ValueError, make_dt1, 2000, 1, 0)\n        self.assertRaises(ValueError, make_dt1, 2000, 1, 32)\n        # bad hours\n        self.theclass(2000, 1, 31, 0)  # no exception\n        self.theclass(2000, 1, 31, 23)  # no exception\n        self.assertRaises(ValueError, make_dt2, 2000, 1, 31, -1)\n        self.assertRaises(ValueError, make_dt2, 2000, 1, 31, 24)\n        # bad minutes\n        self.theclass(2000, 1, 31, 23, 0)  # no exception\n        self.theclass(2000, 1, 31, 23, 59)  # no exception\n        self.assertRaises(ValueError, make_dt3, 2000, 1, 31, 23, -1)\n        self.assertRaises(ValueError, make_dt3, 2000, 1, 31, 23, 60)\n        # bad seconds\n        self.theclass(2000, 1, 31, 23, 59, 0)  # no exception\n        self.theclass(2000, 1, 31, 23, 59, 59)  # no exception\n        self.assertRaises(ValueError, make_dt4, 2000, 1, 31, 23, 59, -1)\n        self.assertRaises(ValueError, make_dt4, 2000, 1, 31, 23, 59, 60)\n        # bad microseconds\n        self.theclass(2000, 1, 31, 23, 59, 59, 0)  # no exception\n        self.theclass(2000, 1, 31, 23, 59, 59, 999999)  # no exception\n        self.assertRaises(ValueError, make_dt5, 2000, 1, 31, 23, 59, 59, -1)\n        self.assertRaises(ValueError, make_dt5, 2000, 1, 31, 23, 59, 59, 1000000)\n        \"\"\"\n        # bad fold\n        self.assertRaises(ValueError, self.theclass,\n                          2000, 1, 31, fold=-1)\n        self.assertRaises(ValueError, self.theclass,\n                          2000, 1, 31, fold=2)\n        # Positional fold:\n        self.assertRaises(TypeError, self.theclass,\n                          2000, 1, 31, 23, 59, 59, 0, None, 1)\n        \"\"\"\n\n    def test_hash_equality(self):\n        d = self.theclass(2000, 12, 31, 23, 30, 17)\n        e = self.theclass(2000, 12, 31, 23, 30, 17)\n        self.assertEqual(d, e)\n        self.assertEqual(hash(d), hash(e))\n\n        dic = {d: 1}\n        dic[e] = 2\n        self.assertEqual(len(dic), 1)\n        self.assertEqual(dic[d], 2)\n        self.assertEqual(dic[e], 2)\n\n        d = self.theclass(2001, 1, 1, 0, 5, 17)\n        e = self.theclass(2001, 1, 1, 0, 5, 17)\n        self.assertEqual(d, e)\n        self.assertEqual(hash(d), hash(e))\n\n        dic = {d: 1}\n        dic[e] = 2\n        self.assertEqual(len(dic), 1)\n        self.assertEqual(dic[d], 2)\n        self.assertEqual(dic[e], 2)\n\n    def test_computations(self):\n        a = self.theclass(2002, 1, 31)\n        b = self.theclass(1956, 1, 31)\n        diff = a - b\n        self.assertEqual(diff.days, 46 * 365 + len(range(1956, 2002, 4)))\n        self.assertEqual(diff.seconds, 0)\n        self.assertEqual(diff.microseconds, 0)\n        a = self.theclass(2002, 3, 2, 17, 6)\n        millisec = timedelta(0, 0, 1000)\n        hour = timedelta(0, 3600)\n        day = timedelta(1)\n        week = timedelta(7)\n        self.assertEqual(a + hour, self.theclass(2002, 3, 2, 18, 6))\n        self.assertEqual(hour + a, self.theclass(2002, 3, 2, 18, 6))\n        self.assertEqual(a + 10 * hour, self.theclass(2002, 3, 3, 3, 6))\n        self.assertEqual(a - hour, self.theclass(2002, 3, 2, 16, 6))\n        self.assertEqual(-hour + a, self.theclass(2002, 3, 2, 16, 6))\n        self.assertEqual(a - hour, a + -hour)\n        self.assertEqual(a - 20 * hour, self.theclass(2002, 3, 1, 21, 6))\n        self.assertEqual(a + day, self.theclass(2002, 3, 3, 17, 6))\n        self.assertEqual(a - day, self.theclass(2002, 3, 1, 17, 6))\n        self.assertEqual(a + week, self.theclass(2002, 3, 9, 17, 6))\n        self.assertEqual(a - week, self.theclass(2002, 2, 23, 17, 6))\n        self.assertEqual(a + 52 * week, self.theclass(2003, 3, 1, 17, 6))\n        self.assertEqual(a - 52 * week, self.theclass(2001, 3, 3, 17, 6))\n        self.assertEqual((a + week) - a, week)\n        self.assertEqual((a + day) - a, day)\n        self.assertEqual((a + hour) - a, hour)\n        self.assertEqual((a + millisec) - a, millisec)\n        self.assertEqual((a - week) - a, -week)\n        self.assertEqual((a - day) - a, -day)\n        self.assertEqual((a - hour) - a, -hour)\n        self.assertEqual((a - millisec) - a, -millisec)\n        self.assertEqual(a - (a + week), -week)\n        self.assertEqual(a - (a + day), -day)\n        self.assertEqual(a - (a + hour), -hour)\n        self.assertEqual(a - (a + millisec), -millisec)\n        self.assertEqual(a - (a - week), week)\n        self.assertEqual(a - (a - day), day)\n        self.assertEqual(a - (a - hour), hour)\n        self.assertEqual(a - (a - millisec), millisec)\n        self.assertEqual(\n            a + (week + day + hour + millisec),\n            self.theclass(2002, 3, 10, 18, 6, 0, 1000),\n        )\n        self.assertEqual(\n            a + (week + day + hour + millisec), (((a + week) + day) + hour) + millisec\n        )\n        self.assertEqual(\n            a - (week + day + hour + millisec),\n            self.theclass(2002, 2, 22, 16, 5, 59, 999000),\n        )\n        self.assertEqual(\n            a - (week + day + hour + millisec), (((a - week) - day) - hour) - millisec\n        )\n        \"\"\"\n        # Add/sub ints or floats should be illegal\n        for i in 1, 1.0:\n            self.assertRaises(TypeError, lambda: a+i)\n            self.assertRaises(TypeError, lambda: a-i)\n            self.assertRaises(TypeError, lambda: i+a)\n            self.assertRaises(TypeError, lambda: i-a)\n\n        # delta - datetime is senseless.\n        self.assertRaises(TypeError, lambda: day - a)\n        # mixing datetime and (delta or datetime) via * or // is senseless\n        self.assertRaises(TypeError, lambda: day * a)\n        self.assertRaises(TypeError, lambda: a * day)\n        self.assertRaises(TypeError, lambda: day // a)\n        self.assertRaises(TypeError, lambda: a // day)\n        self.assertRaises(TypeError, lambda: a * a)\n        self.assertRaises(TypeError, lambda: a // a)\n        # datetime + datetime is senseless\n        self.assertRaises(TypeError, lambda: a + a)\n        \"\"\"\n\n    def test_more_compare(self):\n        # The test_compare() inherited from TestDate covers the error cases.\n        # We just want to test lexicographic ordering on the members datetime\n        # has that date lacks.\n        args = (2000, 11, 29, 20, 58, 16, 999998)\n        newargsx = [\n            (2001, 11, 29, 20, 58, 16, 999998),\n            (2000, 12, 29, 20, 58, 16, 999998),\n            (2000, 11, 30, 20, 58, 16, 999998),\n            (2000, 11, 29, 21, 58, 16, 999998),\n            (2000, 11, 29, 20, 59, 16, 999998),\n            (2000, 11, 29, 20, 58, 17, 999998),\n            (2000, 11, 29, 20, 58, 16, 999999),\n        ]\n        t1 = self.theclass(*args)\n        t2 = self.theclass(*args)\n        self.assertEqual(t1, t2)\n        self.assertTrue(t1 <= t2)\n        self.assertTrue(t1 >= t2)\n        self.assertFalse(t1 != t2)\n        self.assertFalse(t1 < t2)\n        self.assertFalse(t1 > t2)\n\n        for i in range(len(args)):\n            newargs = newargsx[i]\n            t2 = self.theclass(*newargs)  # this is larger than t1\n            self.assertTrue(t1 < t2)\n            self.assertTrue(t2 > t1)\n            self.assertTrue(t1 <= t2)\n            self.assertTrue(t2 >= t1)\n            self.assertTrue(t1 != t2)\n            self.assertTrue(t2 != t1)\n            self.assertFalse(t1 == t2)\n            self.assertFalse(t2 == t1)\n            self.assertFalse(t1 > t2)\n            self.assertFalse(t2 < t1)\n            self.assertFalse(t1 >= t2)\n            self.assertFalse(t2 <= t1)\n\n    # A helper for timestamp constructor tests.\n    def verify_field_equality(self, expected, got):\n        self.assertEqual(expected.tm_year, got.year)\n        self.assertEqual(expected.tm_mon, got.month)\n        self.assertEqual(expected.tm_mday, got.day)\n        self.assertEqual(expected.tm_hour, got.hour)\n        self.assertEqual(expected.tm_min, got.minute)\n        self.assertEqual(expected.tm_sec, got.second)\n\n    def test_fromtimestamp(self):\n        import time\n\n        ts = time.time()\n        expected = time.localtime(int(ts))\n        got = self.theclass.fromtimestamp(ts)\n        self.verify_field_equality(expected, got)\n\n    \"\"\"\n    def test_utcfromtimestamp(self):\n        import time\n\n        ts = time.time()\n        expected = time.gmtime(int(ts))\n        got = self.theclass.utcfromtimestamp(ts)\n        self.verify_field_equality(expected, got)\n    \"\"\"\n\n    def test_timestamp_naive(self):\n        \"\"\"\n        t = self.theclass(1970, 1, 1)\n        self.assertEqual(t.timestamp(), 18000.0)\n        t = self.theclass(1970, 1, 1, 1, 2, 3, 4)\n        self.assertEqual(t.timestamp(),\n                         18000.0 + 3600 + 2*60 + 3 + 4*1e-6)\n        \"\"\"\n\n        t = self.theclass(1970, 1, 1)\n        self.assertEqual(t.timestamp(), 0)\n        t = self.theclass(1970, 1, 1, 1, 2, 3, 4)\n        self.assertEqual(t.timestamp(), 3600 + 2 * 60 + 3 + 4 * 1e-6)\n\n        \"\"\"\n        # Missing hour\n        t0 = self.theclass(2012, 3, 11, 2, 30)\n        t1 = t0.replace(fold=1)\n        self.assertEqual(self.theclass.fromtimestamp(t1.timestamp()),\n                         t0 - timedelta(hours=1))\n        self.assertEqual(self.theclass.fromtimestamp(t0.timestamp()),\n                         t1 + timedelta(hours=1))\n        # Ambiguous hour defaults to DST\n        t = self.theclass(2012, 11, 4, 1, 30)\n        self.assertEqual(self.theclass.fromtimestamp(t.timestamp()), t)\n\n        # Timestamp may raise an overflow error on some platforms\n        # XXX: Do we care to support the first and last year?\n        for t in [self.theclass(2,1,1), self.theclass(9998,12,12)]:\n            s = None\n            try:\n                s = t.timestamp()\n            except OverflowError:\n                continue\n\n            self.assertEqual(self.theclass.fromtimestamp(s), t)\n        \"\"\"\n\n    \"\"\"\n    def test_timestamp_aware(self):\n        t = self.theclass(1970, 1, 1, tzinfo=timezone.utc)\n        self.assertEqual(t.timestamp(), 0.0)\n        t = self.theclass(1970, 1, 1, 1, 2, 3, 4, tzinfo=timezone.utc)\n        self.assertEqual(t.timestamp(),\n                         3600 + 2*60 + 3 + 4*1e-6)\n        t = self.theclass(1970, 1, 1, 1, 2, 3, 4,\n                          tzinfo=timezone(timedelta(hours=-5), 'EST'))\n        self.assertEqual(t.timestamp(),\n                         18000 + 3600 + 2*60 + 3 + 4*1e-6)\n    \"\"\"\n    \"\"\"\n    def test_microsecond_rounding(self):\n        for fts in (self.theclass.fromtimestamp,\n                    self.theclass.utcfromtimestamp):\n            zero = fts(0)\n            self.assertEqual(zero.second, 0)\n            self.assertEqual(zero.microsecond, 0)\n            one = fts(1e-6)\n            minus_one = fts(-1e-6)\n\n            self.assertEqual(minus_one.second, 59)\n            self.assertEqual(minus_one.microsecond, 999999)\n\n            t = fts(-1e-8)\n            self.assertEqual(t, zero)\n            t = fts(-9e-7)\n            self.assertEqual(t, minus_one)\n            t = fts(-1e-7)\n            self.assertEqual(t, zero)\n            t = fts(-1/2**7)\n            self.assertEqual(t.second, 59)\n            self.assertEqual(t.microsecond, 992188)\n\n            t = fts(1e-7)\n            self.assertEqual(t, zero)\n            t = fts(9e-7)\n            self.assertEqual(t, one)\n            t = fts(0.99999949)\n            self.assertEqual(t.second, 0)\n            self.assertEqual(t.microsecond, 999999)\n            t = fts(0.9999999)\n            self.assertEqual(t.second, 1)\n            self.assertEqual(t.microsecond, 0)\n            t = fts(1/2**7)\n            self.assertEqual(t.second, 0)\n            self.assertEqual(t.microsecond, 7812)\n    \"\"\"\n    \"\"\"\n    def test_timestamp_limits(self):\n        # minimum timestamp\n        min_dt = self.theclass.min.replace(tzinfo=timezone.utc)\n        min_ts = min_dt.timestamp()\n        try:\n            # date 0001-01-01 00:00:00+00:00: timestamp=-62135596800\n            self.assertEqual(self.theclass.fromtimestamp(min_ts, tz=timezone.utc),\n                             min_dt)\n        except (OverflowError, OSError) as exc:\n            # the date 0001-01-01 doesn't fit into 32-bit time_t,\n            # or platform doesn't support such very old date\n            self.skipTest(str(exc))\n\n        # maximum timestamp: set seconds to zero to avoid rounding issues\n        max_dt = self.theclass.max.replace(tzinfo=timezone.utc,\n                                           second=0, microsecond=0)\n        max_ts = max_dt.timestamp()\n        # date 9999-12-31 23:59:00+00:00: timestamp 253402300740\n        self.assertEqual(self.theclass.fromtimestamp(max_ts, tz=timezone.utc),\n                         max_dt)\n\n        # number of seconds greater than 1 year: make sure that the new date\n        # is not valid in datetime.datetime limits\n        delta = 3600 * 24 * 400\n\n        # too small\n        ts = min_ts - delta\n        # converting a Python int to C time_t can raise a OverflowError,\n        # especially on 32-bit platforms.\n        self.assertRaises(ValueError, self.theclass.fromtimestamp, ts)\n        self.assertRaises(ValueError, self.theclass.utcfromtimestamp, ts)\n\n        # too big\n        ts = max_dt.timestamp() + delta\n        self.assertRaises(ValueError, self.theclass.fromtimestamp, ts)\n        self.assertRaises(ValueError, self.theclass.utcfromtimestamp, ts)\n    \"\"\"\n\n    def test_negative_float_fromtimestamp(self):\n        # The result is tz-dependent; at least test that this doesn't\n        # fail (like it did before bug 1646728 was fixed).\n        self.theclass.fromtimestamp(-1.05)\n\n    \"\"\"\n    def test_negative_float_utcfromtimestamp(self):\n        d = self.theclass.utcfromtimestamp(-1.05)\n        self.assertEqual(d, self.theclass(1969, 12, 31, 23, 59, 58, 950000))\n    \"\"\"\n\n    def test_utcnow(self):\n        import time\n\n        # Call it a success if utcnow() and utcfromtimestamp() are within\n        # a second of each other.\n        from_timestamp = None\n        from_now = None\n        tolerance = timedelta(seconds=1)\n        for dummy in range(3):\n            from_now = self.theclass.utcnow()\n            from_timestamp = self.theclass.utcfromtimestamp(time.time())\n            if abs(from_timestamp.__val__() - from_now.__val__()) <= tolerance:\n                break\n            # Else try again a few times.\n        self.assertLessEqual(abs(from_timestamp.__val__() - from_now.__val__()), tolerance)\n\n    def test_extract(self):\n        dt = self.theclass(2002, 3, 4, 18, 45, 3, 1234)\n        self.assertEqual(dt.date(), date(2002, 3, 4))\n        self.assertEqual(dt.time(), time(18, 45, 3, 1234))\n\n    def test_combine(self):\n        d = date(2002, 3, 4)\n        t = time(18, 45, 3, 1234)\n        expected = self.theclass(2002, 3, 4, 18, 45, 3, 1234)\n        combine = self.theclass.combine\n        dt = combine(d, t)\n        self.assertEqual(dt, expected)\n\n        dt = combine(time=t, date=d)\n        self.assertEqual(dt, expected)\n\n        self.assertEqual(d, dt.date())\n        self.assertEqual(t, dt.time())\n        self.assertEqual(dt, combine(dt.date(), dt.time()))\n\n        \"\"\"\n        self.assertRaises(TypeError, combine) # need an arg\n        self.assertRaises(TypeError, combine, d) # need two args\n        self.assertRaises(TypeError, combine, t, d) # args reversed\n        self.assertRaises(TypeError, combine, d, t, 1) # wrong tzinfo type\n        self.assertRaises(TypeError, combine, d, t, 1, 2)  # too many args\n        self.assertRaises(TypeError, combine, \"date\", \"time\") # wrong types\n        self.assertRaises(TypeError, combine, d, \"time\") # wrong type\n        self.assertRaises(TypeError, combine, \"date\", t) # wrong type\n\n        # tzinfo= argument\n        dt = combine(d, t, timezone.utc)\n        self.assertIs(dt.tzinfo, timezone.utc)\n        dt = combine(d, t, tzinfo=timezone.utc)\n        self.assertIs(dt.tzinfo, timezone.utc)\n        t = time()\n        dt = combine(dt, t)\n        self.assertEqual(dt.date(), d)\n        self.assertEqual(dt.time(), t)\n        \"\"\"\n\n    def test_replace(self):\n        cls = self.theclass\n        args = (1, 2, 3, 4, 5, 6, 7)\n        base = cls(*args)\n        self.assertEqual(base, base.replace())\n\n        self.assertEqual(base.replace(year=2), cls(2, 2, 3, 4, 5, 6, 7))\n        self.assertEqual(base.replace(month=3), cls(1, 3, 3, 4, 5, 6, 7))\n        self.assertEqual(base.replace(day=4), cls(1, 2, 4, 4, 5, 6, 7))\n        self.assertEqual(base.replace(hour=5), cls(1, 2, 3, 5, 5, 6, 7))\n        self.assertEqual(base.replace(minute=6), cls(1, 2, 3, 4, 6, 6, 7))\n        self.assertEqual(base.replace(second=7), cls(1, 2, 3, 4, 5, 7, 7))\n        self.assertEqual(base.replace(microsecond=8), cls(1, 2, 3, 4, 5, 6, 8))\n        # Out of bounds.\n        base = cls(2000, 2, 29)\n        self.assertRaises(ValueError, lambda: base.replace(year=2001))\n\n    \"\"\"\n    def test_astimezone(self):\n        dt = self.theclass.now()\n        f = FixedOffset(44, \"0044\")\n        dt_utc = dt.replace(tzinfo=timezone(timedelta(hours=-4), 'EDT'))\n        self.assertEqual(dt.astimezone(), dt_utc) # naive\n        self.assertRaises(TypeError, dt.astimezone, f, f) # too many args\n        self.assertRaises(TypeError, dt.astimezone, dt) # arg wrong type\n        dt_f = dt.replace(tzinfo=f) + timedelta(hours=4, minutes=44)\n        self.assertEqual(dt.astimezone(f), dt_f) # naive\n        self.assertEqual(dt.astimezone(tz=f), dt_f) # naive\n\n        class Bogus(tzinfo):\n            def utcoffset(self, dt): return None\n            def dst(self, dt): return timedelta(0)\n        bog = Bogus()\n        self.assertRaises(ValueError, dt.astimezone, bog)   # naive\n        self.assertEqual(dt.replace(tzinfo=bog).astimezone(f), dt_f)\n\n        class AlsoBogus(tzinfo):\n            def utcoffset(self, dt): return timedelta(0)\n            def dst(self, dt): return None\n        alsobog = AlsoBogus()\n        self.assertRaises(ValueError, dt.astimezone, alsobog) # also naive\n\n        class Broken(tzinfo):\n            def utcoffset(self, dt): return 1\n            def dst(self, dt): return 1\n        broken = Broken()\n        dt_broken = dt.replace(tzinfo=broken)\n        with self.assertRaises(TypeError):\n            dt_broken.astimezone()\n    \"\"\"\n\n    def test_fromisoformat_datetime(self):\n        # Test that isoformat() is reversible\n        base_dates = [(1, 1, 1), (1900, 1, 1), (2004, 11, 12), (2017, 5, 30)]\n\n        base_times = [\n            (0, 0, 0, 0),\n            (0, 0, 0, 241000),\n            (0, 0, 0, 234567),\n            (12, 30, 45, 234567),\n        ]\n\n        separators = [\" \", \"T\"]\n\n        dts = [\n            self.theclass(*date_tuple, *time_tuple)\n            for date_tuple in base_dates\n            for time_tuple in base_times\n        ]\n\n        for dt in dts:\n            for sep in separators:\n                dtstr = dt.isoformat(sep=sep)\n                dt_rt = self.theclass.fromisoformat(dtstr)\n                self.assertEqual(dt, dt_rt)\n\n    def test_fromisoformat_separators(self):\n        separators = [\" \", \"T\"]\n\n        for sep in separators:\n            dt = self.theclass(2018, 1, 31, 23, 59, 47, 124789)\n            dtstr = dt.isoformat(sep=sep)\n\n            dt_rt = self.theclass.fromisoformat(dtstr)\n            self.assertEqual(dt, dt_rt)\n\n    def test_fromisoformat_ambiguous(self):\n        # Test strings like 2018-01-31+12:15 (where +12:15 is not a time zone)\n        separators = [\"+\", \"-\"]\n        for sep in separators:\n            dt = self.theclass(2018, 1, 31, 12, 15)\n            dtstr = dt.isoformat(sep=sep)\n\n            dt_rt = self.theclass.fromisoformat(dtstr)\n            self.assertEqual(dt, dt_rt)\n\n    def test_fromisoformat_timespecs(self):\n        datetime_bases = [(2009, 12, 4, 8, 17, 45, 123456), (2009, 12, 4, 8, 17, 45, 0)]\n\n        timespecs = [\"hours\", \"minutes\", \"seconds\", \"milliseconds\", \"microseconds\"]\n\n        for dt_tuple in datetime_bases:\n            dt = self.theclass(*(dt_tuple[0:4]))\n            dtstr = dt.isoformat(timespec=\"hours\")\n            dt_rt = self.theclass.fromisoformat(dtstr)\n            self.assertEqual(dt, dt_rt)\n\n            dt = self.theclass(*(dt_tuple[0:5]))\n            dtstr = dt.isoformat(timespec=\"minutes\")\n            dt_rt = self.theclass.fromisoformat(dtstr)\n            self.assertEqual(dt, dt_rt)\n\n            dt = self.theclass(*(dt_tuple[0:6]))\n            dtstr = dt.isoformat(timespec=\"seconds\")\n            dt_rt = self.theclass.fromisoformat(dtstr)\n            self.assertEqual(dt, dt_rt)\n\n            new_microseconds = 1000 * (dt_tuple[6] // 1000)\n            dt_tuple2 = (*dt_tuple[0:6], new_microseconds)\n            dt = self.theclass(*(dt_tuple2[0:7]))\n            dtstr = dt.isoformat(timespec=\"milliseconds\")\n            dt_rt = self.theclass.fromisoformat(dtstr)\n            self.assertEqual(dt, dt_rt)\n\n            dt = self.theclass(*(dt_tuple[0:8]))\n            dtstr = dt.isoformat(timespec=\"microseconds\")\n            dt_rt = self.theclass.fromisoformat(dtstr)\n            self.assertEqual(dt, dt_rt)\n\n    def test_fromisoformat_fails_datetime(self):\n        # Test that fromisoformat() fails on invalid values\n        bad_strs = [\n            \"\",  # Empty string\n            \"\\ud800\",  # bpo-34454: Surrogate code point\n            \"2009.04-19T03\",  # Wrong first separator\n            \"2009-04.19T03\",  # Wrong second separator\n            \"2009-04-19T0a\",  # Invalid hours\n            \"2009-04-19T03:1a:45\",  # Invalid minutes\n            \"2009-04-19T03:15:4a\",  # Invalid seconds\n            \"2009-04-19T03;15:45\",  # Bad first time separator\n            \"2009-04-19T03:15;45\",  # Bad second time separator\n            \"2009-04-19T03:15:4500:00\",  # Bad time zone separator\n            \"2009-04-19T03:15:45.2345\",  # Too many digits for milliseconds\n            \"2009-04-19T03:15:45.1234567\",  # Too many digits for microseconds\n            \"2009-04-19T03:15:45.123456+24:30\",  # Invalid time zone offset\n            \"2009-04-19T03:15:45.123456-24:30\",  # Invalid negative offset\n            \"2009-04-10ᛇᛇᛇᛇᛇ12:15\",  # Too many unicode separators\n            \"2009-04\\ud80010T12:15\",  # Surrogate char in date\n            \"2009-04-10T12\\ud80015\",  # Surrogate char in time\n            \"2009-04-19T1\",  # Incomplete hours\n            \"2009-04-19T12:3\",  # Incomplete minutes\n            \"2009-04-19T12:30:4\",  # Incomplete seconds\n            \"2009-04-19T12:\",  # Ends with time separator\n            \"2009-04-19T12:30:\",  # Ends with time separator\n            \"2009-04-19T12:30:45.\",  # Ends with time separator\n            \"2009-04-19T12:30:45.123456+\",  # Ends with timzone separator\n            \"2009-04-19T12:30:45.123456-\",  # Ends with timzone separator\n            \"2009-04-19T12:30:45.123456-05:00a\",  # Extra text\n            \"2009-04-19T12:30:45.123-05:00a\",  # Extra text\n            \"2009-04-19T12:30:45-05:00a\",  # Extra text\n        ]\n\n        for bad_str in bad_strs:\n            self.assertRaises(ValueError, self.theclass.fromisoformat, bad_str)\n\n\nclass TestTime(Static[TestCase]):\n    theclass: type = time\n\n    def test_basic_attributes(self):\n        t = self.theclass(12, 0)\n        self.assertEqual(t.hour, 12)\n        self.assertEqual(t.minute, 0)\n        self.assertEqual(t.second, 0)\n        self.assertEqual(t.microsecond, 0)\n\n    def test_basic_attributes_nonzero(self):\n        # Make sure all attributes are non-zero so bugs in\n        # bit-shifting access show up.\n        t = self.theclass(12, 59, 59, 8000)\n        self.assertEqual(t.hour, 12)\n        self.assertEqual(t.minute, 59)\n        self.assertEqual(t.second, 59)\n        self.assertEqual(t.microsecond, 8000)\n\n    def test_comparing(self):\n        args = (1, 2, 3, 4)\n        newargsx = [(2, 2, 3, 4), (1, 3, 3, 4), (1, 2, 4, 4), (1, 2, 3, 5)]\n        t1 = self.theclass(*args)\n        t2 = self.theclass(*args)\n        self.assertEqual(t1, t2)\n        self.assertTrue(t1 <= t2)\n        self.assertTrue(t1 >= t2)\n        self.assertFalse(t1 != t2)\n        self.assertFalse(t1 < t2)\n        self.assertFalse(t1 > t2)\n\n        for i in range(len(args)):\n            newargs = newargsx[i]\n            t2 = self.theclass(*newargs)  # this is larger than t1\n            self.assertTrue(t1 < t2)\n            self.assertTrue(t2 > t1)\n            self.assertTrue(t1 <= t2)\n            self.assertTrue(t2 >= t1)\n            self.assertTrue(t1 != t2)\n            self.assertTrue(t2 != t1)\n            self.assertFalse(t1 == t2)\n            self.assertFalse(t2 == t1)\n            self.assertFalse(t1 > t2)\n            self.assertFalse(t2 < t1)\n            self.assertFalse(t1 >= t2)\n            self.assertFalse(t2 <= t1)\n\n        \"\"\"\n        for badarg in OTHERSTUFF:\n            self.assertEqual(t1 == badarg, False)\n            self.assertEqual(t1 != badarg, True)\n            self.assertEqual(badarg == t1, False)\n            self.assertEqual(badarg != t1, True)\n\n            self.assertRaises(TypeError, lambda: t1 <= badarg)\n            self.assertRaises(TypeError, lambda: t1 < badarg)\n            self.assertRaises(TypeError, lambda: t1 > badarg)\n            self.assertRaises(TypeError, lambda: t1 >= badarg)\n            self.assertRaises(TypeError, lambda: badarg <= t1)\n            self.assertRaises(TypeError, lambda: badarg < t1)\n            self.assertRaises(TypeError, lambda: badarg > t1)\n            self.assertRaises(TypeError, lambda: badarg >= t1)\n        \"\"\"\n\n    def test_bad_constructor_arguments(self):\n        # bad hours\n        self.theclass(0, 0)  # no exception\n        self.theclass(23, 0)  # no exception\n\n        make_time1 = lambda a, b: self.theclass(a, b)\n        make_time2 = lambda a, b, c: self.theclass(a, b, c)\n        make_time3 = lambda a, b, c, d: self.theclass(a, b, c, d)\n\n        self.assertRaises(ValueError, make_time1, -1, 0)\n        self.assertRaises(ValueError, make_time1, 24, 0)\n        # bad minutes\n        self.theclass(23, 0)  # no exception\n        self.theclass(23, 59)  # no exception\n        self.assertRaises(ValueError, make_time1, 23, -1)\n        self.assertRaises(ValueError, make_time1, 23, 60)\n        # bad seconds\n        self.theclass(23, 59, 0)  # no exception\n        self.theclass(23, 59, 59)  # no exception\n        self.assertRaises(ValueError, make_time2, 23, 59, -1)\n        self.assertRaises(ValueError, make_time2, 23, 59, 60)\n        # bad microseconds\n        self.theclass(23, 59, 59, 0)  # no exception\n        self.theclass(23, 59, 59, 999999)  # no exception\n        self.assertRaises(ValueError, make_time3, 23, 59, 59, -1)\n        self.assertRaises(ValueError, make_time3, 23, 59, 59, 1000000)\n\n    def test_hash_equality(self):\n        d = self.theclass(23, 30, 17)\n        e = self.theclass(23, 30, 17)\n        self.assertEqual(d, e)\n        self.assertEqual(hash(d), hash(e))\n\n        dic = {d: 1}\n        dic[e] = 2\n        self.assertEqual(len(dic), 1)\n        self.assertEqual(dic[d], 2)\n        self.assertEqual(dic[e], 2)\n\n        d = self.theclass(0, 5, 17)\n        e = self.theclass(0, 5, 17)\n        self.assertEqual(d, e)\n        self.assertEqual(hash(d), hash(e))\n\n        dic = {d: 1}\n        dic[e] = 2\n        self.assertEqual(len(dic), 1)\n        self.assertEqual(dic[d], 2)\n        self.assertEqual(dic[e], 2)\n\n    def test_isoformat(self):\n        t = self.theclass(4, 5, 1, 123)\n        self.assertEqual(t.isoformat(), \"04:05:01.000123\")\n        self.assertEqual(t.isoformat(), str(t))\n\n        t = self.theclass()\n        self.assertEqual(t.isoformat(), \"00:00:00\")\n        self.assertEqual(t.isoformat(), str(t))\n\n        t = self.theclass(microsecond=1)\n        self.assertEqual(t.isoformat(), \"00:00:00.000001\")\n        self.assertEqual(t.isoformat(), str(t))\n\n        t = self.theclass(microsecond=10)\n        self.assertEqual(t.isoformat(), \"00:00:00.000010\")\n        self.assertEqual(t.isoformat(), str(t))\n\n        t = self.theclass(microsecond=100)\n        self.assertEqual(t.isoformat(), \"00:00:00.000100\")\n        self.assertEqual(t.isoformat(), str(t))\n\n        t = self.theclass(microsecond=1000)\n        self.assertEqual(t.isoformat(), \"00:00:00.001000\")\n        self.assertEqual(t.isoformat(), str(t))\n\n        t = self.theclass(microsecond=10000)\n        self.assertEqual(t.isoformat(), \"00:00:00.010000\")\n        self.assertEqual(t.isoformat(), str(t))\n\n        t = self.theclass(microsecond=100000)\n        self.assertEqual(t.isoformat(), \"00:00:00.100000\")\n        self.assertEqual(t.isoformat(), str(t))\n\n        t = self.theclass(hour=12, minute=34, second=56, microsecond=123456)\n        self.assertEqual(t.isoformat(timespec=\"hours\"), \"12\")\n        self.assertEqual(t.isoformat(timespec=\"minutes\"), \"12:34\")\n        self.assertEqual(t.isoformat(timespec=\"seconds\"), \"12:34:56\")\n        self.assertEqual(t.isoformat(timespec=\"milliseconds\"), \"12:34:56.123\")\n        self.assertEqual(t.isoformat(timespec=\"microseconds\"), \"12:34:56.123456\")\n        self.assertEqual(t.isoformat(timespec=\"auto\"), \"12:34:56.123456\")\n\n        t = self.theclass(hour=12, minute=34, second=56, microsecond=999500)\n        self.assertEqual(t.isoformat(timespec=\"milliseconds\"), \"12:34:56.999\")\n\n        t = self.theclass(hour=12, minute=34, second=56, microsecond=0)\n        self.assertEqual(t.isoformat(timespec=\"milliseconds\"), \"12:34:56.000\")\n        self.assertEqual(t.isoformat(timespec=\"microseconds\"), \"12:34:56.000000\")\n        self.assertEqual(t.isoformat(timespec=\"auto\"), \"12:34:56\")\n\n    def test_str(self):\n        self.assertEqual(str(self.theclass(1, 2, 3, 4)), \"01:02:03.000004\")\n        self.assertEqual(str(self.theclass(10, 2, 3, 4000)), \"10:02:03.004000\")\n        self.assertEqual(str(self.theclass(0, 2, 3, 400000)), \"00:02:03.400000\")\n        self.assertEqual(str(self.theclass(12, 2, 3, 0)), \"12:02:03\")\n        self.assertEqual(str(self.theclass(23, 15, 0, 0)), \"23:15:00\")\n\n    def test_repr(self):\n        self.assertEqual(\n            repr(self.theclass(1, 2, 3, 4)),\n            \"time(hour=1, minute=2, second=3, microsecond=4)\",\n        )\n        self.assertEqual(\n            repr(self.theclass(10, 2, 3, 4000)),\n            \"time(hour=10, minute=2, second=3, microsecond=4000)\",\n        )\n        self.assertEqual(\n            repr(self.theclass(0, 2, 3, 400000)),\n            \"time(hour=0, minute=2, second=3, microsecond=400000)\",\n        )\n        self.assertEqual(\n            repr(self.theclass(12, 2, 3, 0)), \"time(hour=12, minute=2, second=3)\"\n        )\n        self.assertEqual(repr(self.theclass(23, 15, 0, 0)), \"time(hour=23, minute=15)\")\n\n    def test_resolution_info(self):\n        self.assertTrue(self.theclass.max > self.theclass.min)\n\n    def test_bool(self):\n        # time is always True.\n        cls = self.theclass\n        self.assertTrue(cls(1))\n        self.assertTrue(cls(0, 1))\n        self.assertTrue(cls(0, 0, 1))\n        self.assertTrue(cls(0, 0, 0, 1))\n        self.assertTrue(cls(0))\n        self.assertTrue(cls())\n\n    def test_replace(self):\n        cls = self.theclass\n        args = (1, 2, 3, 4)\n        base = cls(*args)\n        self.assertEqual(base, base.replace())\n\n        self.assertEqual(base.replace(hour=5), cls(5, 2, 3, 4))\n        self.assertEqual(base.replace(minute=6), cls(1, 6, 3, 4))\n        self.assertEqual(base.replace(second=7), cls(1, 2, 7, 4))\n        self.assertEqual(base.replace(microsecond=8), cls(1, 2, 3, 8))\n\n        # Out of bounds.\n        base = cls(1)\n        self.assertRaises(ValueError, lambda: base.replace(hour=24))\n        self.assertRaises(\n            ValueError, lambda: base.replace(minute=-2)\n        )  # minute=-1 indicates default; changed to -2 for test\n        self.assertRaises(ValueError, lambda: base.replace(second=100))\n        self.assertRaises(ValueError, lambda: base.replace(microsecond=1000000))\n\n\ncase_td = TestTimeDelta()\ncase_td.test_computations()\ncase_td.test_constructor()\ncase_td.test_resolution_info()\ncase_td.test_basic_attributes()\ncase_td.test_total_seconds()\ncase_td.test_carries()\ncase_td.test_hash_equality()\ncase_td.test_compare()\ncase_td.test_str()\ncase_td.test_repr()\ncase_td.test_microsecond_rounding()\ncase_td.test_massive_normalization()\ncase_td.test_bool()\ncase_td.test_division()\ncase_td.test_remainder()\ncase_td.test_divmod()\n\ncase_do = TestDateOnly()\ncase_do.test_delta_non_days_ignored()\n\ncase_dx = TestDate[date]()\ncase_dx.test_basic_attributes()\ncase_dx.test_ordinal_conversions()\n# case_dx.test_extreme_ordinals()\ncase_dx.test_bad_constructor_arguments()\ncase_dx.test_hash_equality()\ncase_dx.test_computations()\ncase_dx.test_fromtimestamp()\ncase_dx.test_today()\ncase_dx.test_weekday()\ncase_dx.test_isocalendar()\ncase_dx.test_iso_long_years()\ncase_dx.test_isoformat()\ncase_dx.test_ctime()\ncase_dx.test_timetuple()\ncase_dx.test_compare()\ncase_dx.test_replace()\ncase_dx.test_fromisoformat()\ncase_dx.test_fromisoformat_fails()\ncase_dx.test_fromisocalendar()\ncase_dx.test_fromisocalendar_value_errors()\n\ncase_dt = TestDateTime[datetime]()\ncase_dt.test_ordinal_conversions()\ncase_dt.test_today()\ncase_dt.test_weekday()\ncase_dt.test_isocalendar()\ncase_dt.test_iso_long_years()\ncase_dt.test_ctime()\ncase_dt.test_timetuple()\ncase_dt.test_compare()\ncase_dt.test_fromisoformat()\ncase_dt.test_fromisoformat_fails()\ncase_dt.test_fromisocalendar_value_errors()\n# ---\ncase_dt.test_basic_attributes()\ncase_dt.test_basic_attributes_nonzero()\ncase_dt.test_isoformat()\n# case_dt.test_more_ctime()\ncase_dt.test_tz_independent_comparing()\ncase_dt.test_bad_constructor_arguments()\ncase_dt.test_hash_equality()\ncase_dt.test_computations()\ncase_dt.test_more_compare()\ncase_dt.test_fromtimestamp()\n# case_dt.test_utcfromtimestamp()\ncase_dt.test_timestamp_naive()\n# case_dt.test_microsecond_rounding()\n# case_dt.test_timestamp_limits()\ncase_dt.test_negative_float_fromtimestamp()\n# case_dt.test_negative_float_utcfromtimestamp()\ncase_dt.test_utcnow()\ncase_dt.test_extract()\ncase_dt.test_combine()\ncase_dt.test_replace()\ncase_dt.test_fromisoformat_datetime()\ncase_dt.test_fromisoformat_separators()\ncase_dt.test_fromisoformat_ambiguous()\ncase_dt.test_fromisoformat_timespecs()\ncase_dt.test_fromisoformat_fails_datetime()\n\ncase_tx = TestTime()\ncase_tx.test_basic_attributes()\ncase_tx.test_basic_attributes_nonzero()\ncase_tx.test_comparing()\ncase_tx.test_bad_constructor_arguments()\ncase_tx.test_hash_equality()\ncase_tx.test_isoformat()\ncase_tx.test_str()\ncase_tx.test_repr()\ncase_tx.test_resolution_info()\ncase_tx.test_bool()\ncase_tx.test_replace()\n\n@test\ndef test_constants():\n    assert str(timedelta.min) == '-106751992 days, 19:59:05.224192'  # note: diff w/ Python\n    assert str(timedelta.max) == '106751991 days, 4:00:54.775807'    # note: diff w/ Python\n    assert str(timedelta.resolution) == '0:00:00.000001'\n\n    assert str(date.min) == '0001-01-01'\n    assert str(date.max) == '9999-12-31'\n    assert str(date.resolution) == '1 day, 0:00:00'\n\n    assert str(time.min) == '00:00:00'\n    assert str(time.max) == '23:59:59.999999'\n    assert str(time.resolution) == '0:00:00.000001'\n\n    assert str(datetime.min) == '0001-01-01 00:00:00'\n    assert str(datetime.max) == '9999-12-31 23:59:59.999999'\n    assert str(datetime.resolution) == '0:00:00.000001'\ntest_constants()\n"
  },
  {
    "path": "test/stdlib/heapq_test.codon",
    "content": "import heapq\nfrom random import random, randrange\n\n\n@test\ndef check_invariant(heap):\n    # Check the heap invariant.\n    for pos, item in enumerate(heap):\n        if pos:  # pos 0 has no parent\n            parentpos = (pos - 1) >> 1\n            assert heap[parentpos] <= item\n\n\n@test\ndef test_heapify():\n    for size in list(range(30)) + [20000]:\n        heap = [random() for dummy in range(size)]\n        heapq.heapify(heap)\n        check_invariant(heap)\n\n\n@test\ndef test_naive_nbest():\n    data = [randrange(2000) for i in range(1000)]\n    heap = []\n    for item in data:\n        heapq.heappush(heap, item)\n        if len(heap) > 10:\n            heapq.heappop(heap)\n    heap.sort()\n    assert heap == sorted(data)[-10:]\n\n\ndef heapiter(heap):\n    # An iterator returning a heap's elements, smallest-first.\n    while heap:\n        yield heapq.heappop(heap)\n\n\n@test\ndef test_nbest():\n    # Less-naive \"N-best\" algorithm, much faster (if len(data) is big\n    # enough <wink>) than sorting all of data.  However, if we had a max\n    # heap instead of a min heap, it could go faster still via\n    # heapify'ing all of data (linear time), then doing 10 heappops\n    # (10 log-time steps).\n    data = [randrange(2000) for i in range(1000)]\n    heap = data[:10]\n    heapq.heapify(heap)\n    for item in data[10:]:\n        if item > heap[0]:  # this gets rarer the longer we run\n            heapq.heapreplace(heap, item)\n    assert list(heapiter(heap)) == sorted(data)[-10:]\n\n\n@test\ndef test_nbest_with_pushpop():\n    data = [randrange(2000) for i in range(1000)]\n    heap = data[:10]\n    heapq.heapify(heap)\n    for item in data[10:]:\n        heapq.heappushpop(heap, item)\n    assert list(heapiter(heap)) == sorted(data)[-10:]\n    assert heapq.heappushpop(List[str](), \"x\") == \"x\"\n\n\n@test\ndef test_heappushpop():\n    h = List[int]()\n    x = heapq.heappushpop(h, 10)\n    assert (h, x) == (List[int](), 10)\n\n    h = [10]\n    x = heapq.heappushpop(h, 9)\n    assert (h, x) == ([10], 9)\n\n    h = [10]\n    x = heapq.heappushpop(h, 11)\n    assert (h, x) == ([11], 10)\n\n\n@test\ndef test_heappop_max():\n    # _heapop_max has an optimization for one-item lists which isn't\n    # covered in other tests, so test that case explicitly here\n    h = [3, 2]\n    assert heapq._heappop_max(h) == 3\n    assert heapq._heappop_max(h) == 2\n\n\n@test\ndef test_heapsort():\n    # Exercise everything with repeated heapsort checks\n    for trial in range(100):\n        size = randrange(50)\n        data = [randrange(25) for i in range(size)]\n        heap = None\n        if trial & 1:  # Half of the time, use heapify\n            heap = data[:]\n            heapq.heapify(heap)\n        else:  # The rest of the time, use heappush\n            heap = []\n            for item in data:\n                heapq.heappush(heap, item)\n        heap_sorted = [heapq.heappop(heap) for i in range(size)]\n        assert heap_sorted == sorted(data)\n\n\n\"\"\"\ndef test_merge(self):\n    inputs = []\n    for i in range(randrange(25)):\n        row = []\n        for j in range(randrange(100)):\n            tup = choice('ABC'), randrange(-500, 500)\n            row.append(tup)\n        inputs.append(row)\n\n    for key in [None, itemgetter(0), itemgetter(1), itemgetter(1, 0)]:\n        for reverse in [False, True]:\n            seqs = []\n            for seq in inputs:\n                seqs.append(sorted(seq, key=key, reverse=reverse))\n            self.assertEqual(sorted(chain(*inputs), key=key, reverse=reverse),\n                             list(heapq.merge(*seqs, key=key, reverse=reverse)))\n            self.assertEqual(list(heapq.merge()), [])\n\ndef test_empty_merges(self):\n    # Merging two empty lists (with or without a key) should produce\n    # another empty list.\n    self.assertEqual(list(heapq.merge([], [])), [])\n    self.assertEqual(list(heapq.merge([], [], key=lambda: 6)), [])\n\ndef test_merge_does_not_suppress_index_error(self):\n    # Issue 19018: Heapq.merge suppresses IndexError from user generator\n    def iterable():\n        s = list(range(10))\n        for i in range(20):\n            yield s[i]       # IndexError when i > 10\n    with self.assertRaises(IndexError):\n        list(heapq.merge(iterable(), iterable()))\n\ndef test_merge_stability(self):\n    class Int(int):\n        pass\n    inputs = [[], [], [], []]\n    for i in range(20000):\n        stream = randrange(4)\n        x = randrange(500)\n        obj = Int(x)\n        obj.pair = (x, stream)\n        inputs[stream].append(obj)\n    for stream in inputs:\n        stream.sort()\n    result = [i.pair for i in heapq.merge(*inputs)]\n    self.assertEqual(result, sorted(result))\n\"\"\"\n\n\ndef mykey(x: Tuple[int, int]):\n    return (x[0] * 547 % 2000, x[1] * 547 % 2000)\n\n\n@test\ndef test_nsmallest():\n    data = [(randrange(2000), i) for i in range(1000)]\n    for n in (10,):\n        assert list(heapq.nsmallest(n, data)) == sorted(data)[: min(n, len(data))]\n        assert (\n            list(heapq.nsmallest(n, data, key=mykey))\n            == sorted(data, key=mykey)[: min(n, len(data))]\n        )\n        assert list(heapq.nsmallest(n, data)) == sorted(data)[: min(n, len(data))]\n\n\n@test\ndef test_nlargest():\n    data = [(randrange(2000), i) for i in range(1000)]\n    for n in (0, 1, 2, 10, 100, 400, 999, 1000, 1100):\n        assert list(heapq.nlargest(n, data)) == sorted(data)[::-1][: min(n, len(data))]\n        assert (\n            list(heapq.nlargest(n, data, key=mykey))\n            == sorted(data, key=mykey)[::-1][: min(n, len(data))]\n        )\n        assert list(heapq.nlargest(n, data)) == sorted(data)[::-1][: min(n, len(data))]\n\n\n@test\ndef test_comparison_operator():\n    def hsort(data: List[float], T: type):\n        data2 = [T(x) for x in data]\n        heapq.heapify(data2)\n        return [heapq.heappop(data2).x for i in range(len(data))]\n\n    class LT:\n        x: float\n\n        def __init__(self, x: float):\n            self.x = x\n\n        def __lt__(self, other: LT):\n            return self.x > other.x\n\n    data = [random() for i in range(100)]\n    target = list(reversed(sorted(data)))\n    assert hsort(data, LT) == target\n\n\n@test\ndef test_merge():\n    assert [0, 1, 2, 3, 4, 5, 5, 7, 8, 10, 15, 20, 25] == list(heapq.merge([1, 3, 5, 7], [0, 2, 4, 8], [5, 10, 15, 20], List[int](), [25]))\n    assert ['dog', 'cat', 'fish', 'horse', 'kangaroo'] == list(heapq.merge(['dog', 'horse'], ['cat', 'fish', 'kangaroo'], key=len))\n\n    assert [25, 20, 15, 10, 8, 7, 5, 5, 4, 3, 2, 1, 0] == list(heapq.merge([7, 5, 3, 1], [8, 4, 2, 0], [20, 15, 10, 5], List[int](), [25], reverse=True))\n    assert ['kangaroo', 'horse', 'fish', 'dog', 'cat'] == list(heapq.merge(['horse', 'dog'], ['kangaroo', 'fish', 'cat'], key=len, reverse=True))\n\n\ntest_heapify()\ntest_naive_nbest()\ntest_nsmallest()\ntest_nlargest()\ntest_nbest()\ntest_nbest_with_pushpop()\ntest_heappushpop()\ntest_heappop_max()\ntest_heapsort()\n# test_comparison_operator()\ntest_merge()\n"
  },
  {
    "path": "test/stdlib/itertools_test.codon",
    "content": "import itertools\nfrom itertools import *\nimport math\nimport internal.static as static\n\n\ndef lzip(*args):\n    return list(zip(*args))\n\n\ndef onearg(x):\n    \"Test function of one argument\"\n    return 2 * x\n\n\ndef errfunc(*args):\n    \"Test function that raises an error\"\n    raise ValueError()\n\n\ndef gen3():\n    \"Non-restartable source sequence\"\n    for i in (0, 1, 2):\n        yield i\n\n\ndef isEven(x):\n    \"Test predicate\"\n    return x % 2 == 0\n\n\ndef isOdd(x):\n    \"Test predicate\"\n    return x % 2 == 1\n\n\ndef tupleize(*args):\n    return args\n\n\ndef irange(n):\n    for i in range(n):\n        yield i\n\n\ndef take(n, seq):\n    \"Convenience function for partially consuming a long of infinite iterable\"\n    return list(islice(seq, n))\n\n\ndef testR(r):\n    return r[0]\n\n\ndef testR2(r):\n    return r[2]\n\n\ndef underten(x):\n    return x < 10\n\n\n@test\ndef test_combinations():\n    f = lambda x: x  # hack to get non-static argument\n\n    assert list(itertools.combinations(\"ABCD\", f(2))) == [\n        [\"A\", \"B\"],\n        [\"A\", \"C\"],\n        [\"A\", \"D\"],\n        [\"B\", \"C\"],\n        [\"B\", \"D\"],\n        [\"C\", \"D\"],\n    ]\n    test_intermediate = itertools.combinations(\"ABCD\", f(2))\n    next(test_intermediate)\n    assert list(test_intermediate) == [\n        [\"A\", \"C\"],\n        [\"A\", \"D\"],\n        [\"B\", \"C\"],\n        [\"B\", \"D\"],\n        [\"C\", \"D\"],\n    ]\n    assert list(itertools.combinations(range(4), f(3))) == [\n        [0, 1, 2],\n        [0, 1, 3],\n        [0, 2, 3],\n        [1, 2, 3],\n    ]\n    test_intermediate = itertools.combinations(range(4), f(3))\n    next(test_intermediate)\n    assert list(test_intermediate) == [[0, 1, 3], [0, 2, 3], [1, 2, 3]]\n\n    assert list(itertools.combinations(\"ABCD\", 2)) == [\n        (\"A\", \"B\"),\n        (\"A\", \"C\"),\n        (\"A\", \"D\"),\n        (\"B\", \"C\"),\n        (\"B\", \"D\"),\n        (\"C\", \"D\"),\n    ]\n    test_intermediate = itertools.combinations(\"ABCD\", 2)\n    next(test_intermediate)\n    assert list(test_intermediate) == [\n        (\"A\", \"C\"),\n        (\"A\", \"D\"),\n        (\"B\", \"C\"),\n        (\"B\", \"D\"),\n        (\"C\", \"D\"),\n    ]\n    assert list(itertools.combinations(range(4), 3)) == [\n        (0, 1, 2),\n        (0, 1, 3),\n        (0, 2, 3),\n        (1, 2, 3),\n    ]\n    test_intermediate = itertools.combinations(range(4), 3)\n    next(test_intermediate)\n    assert list(test_intermediate) == [(0, 1, 3), (0, 2, 3), (1, 2, 3)]\n\n\n@test\ndef test_combinations_with_replacement():\n    f = lambda x: x  # hack to get non-static argument\n\n    assert list(itertools.combinations_with_replacement(range(3), f(3))) == [\n        [0, 0, 0],\n        [0, 0, 1],\n        [0, 0, 2],\n        [0, 1, 1],\n        [0, 1, 2],\n        [0, 2, 2],\n        [1, 1, 1],\n        [1, 1, 2],\n        [1, 2, 2],\n        [2, 2, 2],\n    ]\n    assert list(itertools.combinations_with_replacement(\"ABC\", f(2))) == [\n        [\"A\", \"A\"],\n        [\"A\", \"B\"],\n        [\"A\", \"C\"],\n        [\"B\", \"B\"],\n        [\"B\", \"C\"],\n        [\"C\", \"C\"],\n    ]\n    test_intermediate = itertools.combinations_with_replacement(\"ABC\", f(2))\n    next(test_intermediate)\n    assert list(test_intermediate) == [\n        [\"A\", \"B\"],\n        [\"A\", \"C\"],\n        [\"B\", \"B\"],\n        [\"B\", \"C\"],\n        [\"C\", \"C\"],\n    ]\n\n    assert list(itertools.combinations_with_replacement(range(3), 3)) == [\n        (0, 0, 0),\n        (0, 0, 1),\n        (0, 0, 2),\n        (0, 1, 1),\n        (0, 1, 2),\n        (0, 2, 2),\n        (1, 1, 1),\n        (1, 1, 2),\n        (1, 2, 2),\n        (2, 2, 2),\n    ]\n    assert list(itertools.combinations_with_replacement(\"ABC\", 2)) == [\n        (\"A\", \"A\"),\n        (\"A\", \"B\"),\n        (\"A\", \"C\"),\n        (\"B\", \"B\"),\n        (\"B\", \"C\"),\n        (\"C\", \"C\"),\n    ]\n    test_intermediate = itertools.combinations_with_replacement(\"ABC\", 2)\n    next(test_intermediate)\n    assert list(test_intermediate) == [\n        (\"A\", \"B\"),\n        (\"A\", \"C\"),\n        (\"B\", \"B\"),\n        (\"B\", \"C\"),\n        (\"C\", \"C\"),\n    ]\n\n@test\ndef test_islice():\n    ra100 = range(100)\n    ra = range(10)\n    assert list(itertools.islice(iter(\"ABCDEFG\"), 0, 2, 1)) == [\"A\", \"B\"]\n    assert list(itertools.islice(iter(ra100), 10, 20, 3)) == [10, 13, 16, 19]\n    assert list(itertools.islice(iter(ra100), 10, 3, 20)) == []\n    assert list(itertools.islice(iter(ra100), 10, 20, 1)) == [\n        10,\n        11,\n        12,\n        13,\n        14,\n        15,\n        16,\n        17,\n        18,\n        19,\n    ]\n    assert list(itertools.islice(iter(ra100), 10, 10, 1)) == []\n    assert list(itertools.islice(iter(ra100), 10, 3, 1)) == []\n    # stop=len(iterable)\n    assert list(itertools.islice(iter(ra), 0, 10, 1)) == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n    assert list(itertools.islice(iter(ra), 2, 10, 1)) == [2, 3, 4, 5, 6, 7, 8, 9]\n    assert list(itertools.islice(iter(ra), 1, 10, 2)) == [1, 3, 5, 7, 9]\n    try:\n        list(itertools.islice(iter(ra), -5, 10, 1))\n        assert False\n    except ValueError:\n        pass\n\n\n@test\ndef test_count():\n    # infinite loop here\n    assert take(3, itertools.count(3.25, 1.0)) == [3.25, 4.25, 5.25]\n    assert take(3, zip(\"abc\", itertools.count(3.25, 1.0))) == [\n        (\"a\", 3.25),\n        (\"b\", 4.25),\n        (\"c\", 5.25),\n    ]\n    assert take(2, zip(\"abc\", itertools.count(-1.0, 1.0))) == [(\"a\", -1.0), (\"b\", 0.0)]\n    assert take(2, zip(\"abc\", itertools.count(-3.0, 1.0))) == [(\"a\", -3.0), (\"b\", -2.0)]\n\n\n@test\ndef test_repeat():\n    assert list(itertools.repeat(\"a\", 3)) == [\"a\", \"a\", \"a\"]\n    assert list(itertools.repeat(1, 10)) == [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]\n    assert list(itertools.repeat(\"a\", -1)) == []\n    assert len(list(itertools.repeat(\"a\", 0))) == 0\n\n\n@test\ndef test_cycle():\n    assert take(10, zip(\"zzzzzzzzzzz\", itertools.cycle(iter(\"abc\")))) == [\n        (\"z\", \"a\"),\n        (\"z\", \"b\"),\n        (\"z\", \"c\"),\n        (\"z\", \"a\"),\n        (\"z\", \"b\"),\n        (\"z\", \"c\"),\n        (\"z\", \"a\"),\n        (\"z\", \"b\"),\n        (\"z\", \"c\"),\n        (\"z\", \"a\"),\n    ]\n    assert take(10, zip(\"zzzzzzzzzzz\", itertools.cycle([\"a\", \"b\"]))) == [\n        (\"z\", \"a\"),\n        (\"z\", \"b\"),\n        (\"z\", \"a\"),\n        (\"z\", \"b\"),\n        (\"z\", \"a\"),\n        (\"z\", \"b\"),\n        (\"z\", \"a\"),\n        (\"z\", \"b\"),\n        (\"z\", \"a\"),\n        (\"z\", \"b\"),\n    ]\n\n\n@test\ndef test_compress():\n    assert list(itertools.compress(\"ABCDEF\", [1, 0, 1, 0, 1, 1])) == [\n        \"A\",\n        \"C\",\n        \"E\",\n        \"F\",\n    ]\n    assert list(itertools.compress(\"ABCDEF\", [1, 1, 1, 1, 1, 1])) == [\n        \"A\",\n        \"B\",\n        \"C\",\n        \"D\",\n        \"E\",\n        \"F\",\n    ]\n    assert list(itertools.compress(\"ABCDEF\", [1, 0, 1])) == [\"A\", \"C\"]\n    assert list(itertools.compress(\"ABC\", [0, 1, 1, 1, 1, 1])) == [\"B\", \"C\"]\n\n\n@test\ndef test_dropwhile():\n    data = [1, 3, 5, 20, 2, 4, 6, 8]\n    assert list(itertools.dropwhile(underten, data)) == [20, 2, 4, 6, 8]\n\n\n@test\ndef test_takewhile():\n    data = [1, 3, 5, 20, 2, 4, 6, 8]\n    assert list(itertools.takewhile(underten, data)) == [1, 3, 5]\n\n\n@test\ndef test_filterfalse():\n    assert list(itertools.filterfalse(isEven, range(10))) == [1, 3, 5, 7, 9]\n    assert list(itertools.filterfalse(lambda x: bool(x), [0, 1, 0, 2, 0])) == [0, 0, 0]\n\n\n@test\ndef test_permutations():\n    f = lambda x: x  # hack to get non-static argument\n\n    assert list(itertools.permutations(range(3), f(2))) == [\n        [0, 1],\n        [0, 2],\n        [1, 0],\n        [1, 2],\n        [2, 0],\n        [2, 1],\n    ]\n\n    for n in range(3):\n        values = [5 * x - 12 for x in range(n)]\n        for r in range(n + 2):\n            result = list(itertools.permutations(values, f(r)))\n            if r > n:  # right number of perms\n                assert len(result) == 0\n            # factorial is not yet implemented in math\n            # else: fact(n) / fact(n - r)\n\n    assert list(itertools.permutations(range(3), 2)) == [\n        (0, 1),\n        (0, 2),\n        (1, 0),\n        (1, 2),\n        (2, 0),\n        (2, 1),\n    ]\n\n    for n in static.range(3):\n        values = [5 * x - 12 for x in range(n)]\n        for r in static.range(n + 2):\n            result = list(itertools.permutations(values, r))\n            if r > n:  # right number of perms\n                assert len(result) == 0\n            # factorial is not yet implemented in math\n            # else: fact(n) / fact(n - r)\n\n\n@test\ndef test_accumulate():\n    # addition\n    assert list(itertools.accumulate(range(10), int.__add__, initial=0)) == [\n        0,\n        0,\n        1,\n        3,\n        6,\n        10,\n        15,\n        21,\n        28,\n        36,\n        45,\n    ]\n    assert list(itertools.accumulate([7], int.__add__, initial=0)) == [\n        0,\n        7,\n    ]  # iterable of length 1\n    assert list(itertools.accumulate(range(10), int.__add__)) == [\n        0,\n        1,\n        3,\n        6,\n        10,\n        15,\n        21,\n        28,\n        36,\n        45,\n    ]\n    assert list(itertools.accumulate([7], int.__add__)) == [7]  # iterable of length 1\n    assert list(itertools.accumulate(\"abc\", str.__add__, initial=\"\")) == [\n        \"\",\n        \"a\",\n        \"ab\",\n        \"abc\",\n    ]\n    assert list(itertools.accumulate([\"\"], str.__add__, initial=str(0))) == [\"0\", \"0\"]\n    # multiply\n    assert list(itertools.accumulate(range(10), int.__mul__, initial=0)) == [\n        0,\n        0,\n        0,\n        0,\n        0,\n        0,\n        0,\n        0,\n        0,\n        0,\n        0,\n    ]\n    assert list(itertools.accumulate([1, 2, 3, 4, 5], int.__mul__, initial=1)) == [\n        1,\n        1,\n        2,\n        6,\n        24,\n        120,\n    ]\n    assert list(itertools.accumulate([7], int.__mul__)) == [7]\n    # pass\n\n\n@test\ndef test_chain():\n    assert list(itertools.chain(\"abc\", \"def\")) == [\"a\", \"b\", \"c\", \"d\", \"e\", \"f\"]\n    assert list(itertools.chain(\"abc\")) == [\"a\", \"b\", \"c\"]\n    assert list(itertools.chain(\"a\", \"b\", \"c\")) == [\"a\", \"b\", \"c\"]\n    assert list(itertools.chain([\"abc\", \"def\"])) == [\"abc\", \"def\"]\n    assert list(itertools.chain([\"abc\"])) == [\"abc\"]\n    assert list(itertools.chain([\"a\", \"b\", \"c\"])) == [\"a\", \"b\", \"c\"]\n\n\n@test\ndef test_starmap():\n    assert list(itertools.starmap(math.pow, [(2.0, 5.0), (3.0, 2.0), (10.0, 3.0)])) == [\n        32.0,\n        9.0,\n        1000.0,\n    ]\n    assert list(itertools.starmap(math.pow, [(0.0, 1.0), (1.0, 2.0), (2.0, 3.0)])) == [\n        0.0 ** 1.0,\n        1.0 ** 2.0,\n        2.0 ** 3.0,\n    ]\n\n\n@test\ndef test_groupby():\n    def key_str(s: str):\n        return s\n\n    assert list(itertools.groupby(iter(\"AAAABBBCCDAABBC\"), key_str)) == [\n        (\"A\", [\"A\", \"A\", \"A\", \"A\"]),\n        (\"B\", [\"B\", \"B\", \"B\"]),\n        (\"C\", [\"C\", \"C\"]),\n        (\"D\", [\"D\"]),\n        (\"A\", [\"A\", \"A\"]),\n        (\"B\", [\"B\", \"B\"]),\n        (\"C\", [\"C\"]),\n    ]\n\n\n@test\ndef test_zip_longest():\n    assert list(itertools.zip_longest(\"ABCDE\", \"12345\", fillvalue=\"-\")) == [\n        (\"A\", \"1\"),\n        (\"B\", \"2\"),\n        (\"C\", \"3\"),\n        (\"D\", \"4\"),\n        (\"E\", \"5\"),\n    ]\n    assert list(itertools.zip_longest(\"ABCDE\", \"123\", fillvalue=\"-\")) == [\n        (\"A\", \"1\"),\n        (\"B\", \"2\"),\n        (\"C\", \"3\"),\n        (\"D\", \"-\"),\n        (\"E\", \"-\"),\n    ]\n    assert list(itertools.zip_longest(\"123\", \"ABCDE\", fillvalue=\"-\")) == [\n        (\"1\", \"A\"),\n        (\"2\", \"B\"),\n        (\"3\", \"C\"),\n        (\"-\", \"D\"),\n        (\"-\", \"E\"),\n    ]\n    assert list(itertools.zip_longest(\"\", \"ABCDE\", fillvalue=\"-\")) == [\n        (\"-\", \"A\"),\n        (\"-\", \"B\"),\n        (\"-\", \"C\"),\n        (\"-\", \"D\"),\n        (\"-\", \"E\"),\n    ]\n    assert list(itertools.zip_longest(\"ABCDE\", \"\", fillvalue=\"-\")) == [\n        (\"A\", \"-\"),\n        (\"B\", \"-\"),\n        (\"C\", \"-\"),\n        (\"D\", \"-\"),\n        (\"E\", \"-\"),\n    ]\n    assert not list(itertools.zip_longest(\"\", \"\", fillvalue=\"-\"))\n\n\n@test\ndef test_zip_test():\n    assert list(zip()) == []\n    assert list(zip((1, 2))) == [(1,), (2,)]\n    assert list(zip([1, 2], [\"a\", \"b\"], (False, True))) == [\n        (1, \"a\", False),\n        (2, \"b\", True),\n    ]\n\n\ntest_combinations()\ntest_combinations_with_replacement()\ntest_islice()\ntest_count()\ntest_repeat()\ntest_cycle()\ntest_compress()\ntest_dropwhile()\ntest_takewhile()\ntest_filterfalse()\ntest_permutations()\ntest_accumulate()\ntest_chain()\ntest_starmap()\ntest_groupby()\ntest_zip_longest()\ntest_zip_test()\n\n\n# Updated tests lifted from CPython test suite\n\n\n@test\ndef test_accumulate_from_cpython():\n    assert list(accumulate(range(10))) == [0, 1, 3, 6, 10, 15, 21, 28, 36, 45]\n    assert list(accumulate(iterable=range(10))) == [0, 1, 3, 6, 10, 15, 21, 28, 36, 45]\n    assert list(accumulate(\"abc\")) == [\"a\", \"ab\", \"abc\"]\n    assert list(accumulate(List[float]())) == []\n    assert list(accumulate([7])) == [7]\n\n    s = [2, 8, 9, 5, 7, 0, 3, 4, 1, 6]\n    assert list(accumulate(s, min)) == [2, 2, 2, 2, 2, 0, 0, 0, 0, 0]\n    assert list(accumulate(s, max)) == [2, 8, 9, 9, 9, 9, 9, 9, 9, 9]\n    mul = lambda a, b: a * b\n    assert list(accumulate(s, mul)) == [2, 16, 144, 720, 5040, 0, 0, 0, 0, 0]\n\n    # assert list(accumulate([10, 5, 1], initial=None)) == [10, 15, 16]\n    assert list(accumulate([10, 5, 1], initial=100)) == [100, 110, 115, 116]\n    assert list(accumulate([10, 5, 1], initial=100.5)) == [100.5, 110.5, 115.5, 116.5]\n    assert list(accumulate(List[int](), initial=100)) == [100]\n\n\n@test\ndef test_chain_from_cpython():\n    assert list(chain(\"abc\", \"def\")) == list(\"abcdef\")\n    assert list(chain(\"abc\")) == list(\"abc\")\n    assert list(chain(\"\")) == []\n    assert list(take(4, chain(\"abc\", \"def\"))) == list(\"abcd\")\n\n\n@test\ndef test_chain_from_iterable_from_cpython():\n    assert list(chain.from_iterable([\"abc\", \"def\"])) == list(\"abcdef\")\n    assert list(chain.from_iterable([\"abc\"])) == list(\"abc\")\n    assert list(chain.from_iterable([\"\"])) == []\n    assert take(4, chain.from_iterable([\"abc\", \"def\"])) == list(\"abcd\")\n\n\n@test\ndef test_combinations_from_cpython():\n    f = lambda x: x  # hack to get non-static argument\n    from math import factorial as fact\n\n    err = False\n    try:\n        list(combinations(\"abc\", f(-2)))\n        assert False\n    except ValueError:\n        err = True\n    assert err\n\n    assert list(combinations(\"abc\", f(32))) == []  # r > n\n    assert list(combinations(\"ABCD\", f(2))) == [\n        [\"A\", \"B\"],\n        [\"A\", \"C\"],\n        [\"A\", \"D\"],\n        [\"B\", \"C\"],\n        [\"B\", \"D\"],\n        [\"C\", \"D\"],\n    ]\n    assert list(combinations(range(4), f(3))) == [\n        [0, 1, 2],\n        [0, 1, 3],\n        [0, 2, 3],\n        [1, 2, 3],\n    ]\n\n    for n in range(7):\n        values = [5 * x - 12 for x in range(n)]\n        for r in range(n + 2):\n            result = list(combinations(values, f(r)))\n\n            assert len(result) == (0 if r > n else fact(n) // fact(r) // fact(n - r))\n            assert len(result) == len(set(result))  # no repeats\n            # assert result == sorted(result)                     # lexicographic order\n            for c in result:\n                assert len(c) == r  # r-length combinations\n                assert len(set(c)) == r  # no duplicate elements\n                assert list(c) == sorted(c)  # keep original ordering\n                assert all(e in values for e in c)  # elements taken from input iterable\n                assert list(c) == [\n                    e for e in values if e in c\n                ]  # comb is a subsequence of the input iterable\n\n\n    assert list(combinations(\"abc\", 32)) == []  # r > n\n    assert list(combinations(\"ABCD\", 2)) == [\n        (\"A\", \"B\"),\n        (\"A\", \"C\"),\n        (\"A\", \"D\"),\n        (\"B\", \"C\"),\n        (\"B\", \"D\"),\n        (\"C\", \"D\"),\n    ]\n    assert list(combinations(range(4), 3)) == [\n        (0, 1, 2),\n        (0, 1, 3),\n        (0, 2, 3),\n        (1, 2, 3),\n    ]\n\n    for n in static.range(7):\n        values = [5 * x - 12 for x in range(n)]\n        for r in static.range(n + 2):\n            result = list(combinations(values, r))\n\n            assert len(result) == (0 if r > n else fact(n) // fact(r) // fact(n - r))\n            assert len(result) == len(set(result))  # no repeats\n            # assert result == sorted(result)                     # lexicographic order\n            for c in result:\n                assert len(c) == r  # r-length combinations\n                assert len(set(c)) == r  # no duplicate elements\n                assert list(c) == sorted(c)  # keep original ordering\n                assert all(e in values for e in c)  # elements taken from input iterable\n                assert list(c) == [\n                    e for e in values if e in c\n                ]  # comb is a subsequence of the input iterable\n\n\n\n@test\ndef test_combinations_with_replacement_from_cpython():\n    f = lambda x: x  # hack to get non-static argument\n    cwr = combinations_with_replacement\n    err = False\n    try:\n        list(combinations_with_replacement(\"abc\", f(-2)))\n        assert False\n    except ValueError:\n        err = True\n    assert err\n\n    assert list(combinations_with_replacement(\"ABC\", f(2))) == [\n        [\"A\", \"A\"],\n        [\"A\", \"B\"],\n        [\"A\", \"C\"],\n        [\"B\", \"B\"],\n        [\"B\", \"C\"],\n        [\"C\", \"C\"],\n    ]\n\n    def numcombs(n, r):\n        from math import factorial as fact\n\n        if not n:\n            return 0 if r else 1\n        return fact(n + r - 1) // fact(r) // fact(n - 1)\n\n    for n in range(7):\n        values = [5 * x - 12 for x in range(n)]\n        for r in range(n + 2):\n            result = list(combinations_with_replacement(values, r))\n            regular_combs = list(combinations(values, r))\n\n            assert len(result) == numcombs(n, r)\n            assert len(result) == len(set(result))  # no repeats\n            # assert result == sorted(result)                     # lexicographic order\n\n            if n == 0 or r <= 1:\n                assert result == regular_combs  # cases that should be identical\n            else:\n                assert set(result) >= set(regular_combs)\n\n            for c in result:\n                assert len(c) == r  # r-length combinations\n                noruns = [k for k, v in groupby(c)]  # combo without consecutive repeats\n                assert len(noruns) == len(\n                    set(noruns)\n                )  # no repeats other than consecutive\n                assert list(c) == sorted(c)  # keep original ordering\n                assert all(e in values for e in c)  # elements taken from input iterable\n                assert noruns == [\n                    e for e in values if e in c\n                ]  # comb is a subsequence of the input iterable\n\n\n    assert list(combinations_with_replacement(\"ABC\", 2)) == [\n        (\"A\", \"A\"),\n        (\"A\", \"B\"),\n        (\"A\", \"C\"),\n        (\"B\", \"B\"),\n        (\"B\", \"C\"),\n        (\"C\", \"C\"),\n    ]\n\n    for n in static.range(7):\n        values = [5 * x - 12 for x in range(n)]\n        for r in static.range(n + 2):\n            result = list(combinations_with_replacement(values, r))\n            regular_combs = list(combinations(values, r))\n\n            assert len(result) == numcombs(n, r)\n            assert len(result) == len(set(result))  # no repeats\n            # assert result == sorted(result)                     # lexicographic order\n\n            if n == 0 or r <= 1:\n                assert result == regular_combs  # cases that should be identical\n            else:\n                assert set(result) >= set(regular_combs)\n\n            for c in result:\n                assert len(c) == r  # r-length combinations\n                noruns = [k for k, v in groupby(c)]  # combo without consecutive repeats\n                assert len(noruns) == len(\n                    set(noruns)\n                )  # no repeats other than consecutive\n                assert list(c) == sorted(c)  # keep original ordering\n                assert all(e in values for e in c)  # elements taken from input iterable\n                assert noruns == [\n                    e for e in values if e in c\n                ]  # comb is a subsequence of the input iterable\n\n\n@test\ndef test_permutations_from_cpython():\n    f = lambda x: x  # hack to get non-static argument\n    from math import factorial as fact\n\n    err = False\n    try:\n        list(permutations(\"abc\", f(-2)))\n        assert False\n    except ValueError:\n        err = True\n    assert err\n\n    assert list(permutations(\"abc\", f(32))) == []\n    assert list(permutations(range(3), f(2))) == [\n        [0, 1],\n        [0, 2],\n        [1, 0],\n        [1, 2],\n        [2, 0],\n        [2, 1],\n    ]\n\n    for n in range(7):\n        values = [5 * x - 12 for x in range(n)]\n        for r in range(n + 2):\n            result = list(permutations(values, r))\n            assert len(result) == (\n                0 if r > n else fact(n) // fact(n - r)\n            )  # right number of perms\n            assert len(result) == len(set(result))  # no repeats\n            # assert result == sorted(result)                # lexicographic order\n            for p in result:\n                assert len(p) == r  # r-length permutations\n                assert len(set(p)) == r  # no duplicate elements\n                assert all(e in values for e in p)  # elements taken from input iterable\n\n            if r == n:\n                assert result == list(permutations(values, None))  # test r as None\n                assert result == list(permutations(values))  # test default r\n\n    assert list(permutations(\"abc\", 32)) == []\n    assert list(permutations(range(3), 2)) == [\n        (0, 1),\n        (0, 2),\n        (1, 0),\n        (1, 2),\n        (2, 0),\n        (2, 1),\n    ]\n\n    for n in static.range(7):\n        values = [5 * x - 12 for x in range(n)]\n        for r in static.range(n + 2):\n            result = list(permutations(values, r))\n            assert len(result) == (\n                0 if r > n else fact(n) // fact(n - r)\n            )  # right number of perms\n            assert len(result) == len(set(result))  # no repeats\n            # assert result == sorted(result)                # lexicographic order\n            for p in result:\n                assert len(p) == r  # r-length permutations\n                assert len(set(p)) == r  # no duplicate elements\n                assert all(e in values for e in p)  # elements taken from input iterable\n\n            if r == n:\n                assert result == list(permutations(values, r))\n\n\n@extend\nclass List:\n    def __lt__(self, other: List[T]):\n        if len(self) != len(other):\n            return len(self) < len(other)\n        for a, b in zip(self, other):\n            if a < b:\n                return True\n            if a > b:\n                return False\n        return False\n\n    def __le__(self, other: List[T]):\n        if len(self) != len(other):\n            return len(self) < len(other)\n        for a, b in zip(self, other):\n            if a < b:\n                return True\n            if a > b:\n                return False\n        return True\n\n    def __gt__(self, other: List[T]):\n        if len(self) != len(other):\n            return len(self) < len(other)\n        for a, b in zip(self, other):\n            if a > b:\n                return True\n            if a < b:\n                return False\n        return False\n\n    def __ge__(self, other: List[T]):\n        if len(self) != len(other):\n            return len(self) < len(other)\n        for a, b in zip(self, other):\n            if a > b:\n                return True\n            if a < b:\n                return False\n        return True\n\n\n@test\ndef test_combinatorics_from_cpython():\n    # Test relationships between product(), permutations(),\n    # combinations() and combinations_with_replacement().\n    from math import factorial as fact\n\n    for n in range(6):\n        s = \"ABCDEFG\"[:n]\n        for r in range(8):\n            prod = list(product(s, repeat=r))\n            cwr = list(combinations_with_replacement(s, r))\n            perm = list(permutations(s, r))\n            comb = list(combinations(s, r))\n\n            # Check size\n            assert len(prod) == n ** r\n            assert len(cwr) == (\n                (fact(n + r - 1) // fact(r) // fact(n - 1)) if n else (0 if r else 1)\n            )\n            assert len(perm) == (0 if r > n else fact(n) // fact(n - r))\n            assert len(comb) == (0 if r > n else fact(n) // fact(r) // fact(n - r))\n\n            # Check lexicographic order without repeated tuples\n            assert prod == sorted(set(prod))\n            assert cwr == sorted(set(cwr))\n            assert perm == sorted(set(perm))\n            assert comb == sorted(set(comb))\n\n            # Check interrelationships\n            assert cwr == [\n                t for t in prod if sorted(t) == list(t)\n            ]  # cwr: prods which are sorted\n            assert perm == [\n                t for t in prod if len(set(t)) == r\n            ]  # perm: prods with no dups\n            assert comb == [\n                t for t in perm if sorted(t) == list(t)\n            ]  # comb: perms that are sorted\n            assert comb == [\n                t for t in cwr if len(set(t)) == r\n            ]  # comb: cwrs without dups\n            assert comb == list(\n                filter(set(cwr).__contains__, perm)\n            )  # comb: perm that is a cwr\n            assert comb == list(\n                filter(set(perm).__contains__, cwr)\n            )  # comb: cwr that is a perm\n            assert comb == sorted(set(cwr) & set(perm))  # comb: both a cwr and a perm\n\n    for n in static.range(6):\n        s = \"ABCDEFG\"[:n]\n        for r in static.range(8):\n            prod = list(product(s, repeat=r))\n            cwr = list(combinations_with_replacement(s, r))\n            perm = list(permutations(s, r))\n            comb = list(combinations(s, r))\n\n            # Check size\n            assert len(prod) == n ** r\n            assert len(cwr) == (\n                (fact(n + r - 1) // fact(r) // fact(n - 1)) if n else (0 if r else 1)\n            )\n            assert len(perm) == (0 if r > n else fact(n) // fact(n - r))\n            assert len(comb) == (0 if r > n else fact(n) // fact(r) // fact(n - r))\n\n            # Check lexicographic order without repeated tuples\n            assert prod == sorted(set(prod))\n            assert cwr == sorted(set(cwr))\n            assert perm == sorted(set(perm))\n            assert comb == sorted(set(comb))\n\n            # Check interrelationships\n            assert cwr == [\n                t for t in prod if sorted(t) == list(t)\n            ]  # cwr: prods which are sorted\n            assert perm == [\n                t for t in prod if len(set(t)) == r\n            ]  # perm: prods with no dups\n            assert comb == [\n                t for t in perm if sorted(t) == list(t)\n            ]  # comb: perms that are sorted\n            assert comb == [\n                t for t in cwr if len(set(t)) == r\n            ]  # comb: cwrs without dups\n            assert comb == list(\n                filter(set(cwr).__contains__, perm)\n            )  # comb: perm that is a cwr\n            assert comb == list(\n                filter(set(perm).__contains__, cwr)\n            )  # comb: cwr that is a perm\n            assert comb == sorted(set(cwr) & set(perm))  # comb: both a cwr and a perm\n\n\n@test\ndef test_compress_from_cpython():\n    assert list(compress(data=\"ABCDEF\", selectors=[1, 0, 1, 0, 1, 1])) == list(\"ACEF\")\n    assert list(compress(\"ABCDEF\", [1, 0, 1, 0, 1, 1])) == list(\"ACEF\")\n    assert list(compress(\"ABCDEF\", [0, 0, 0, 0, 0, 0])) == list(\"\")\n    assert list(compress(\"ABCDEF\", [1, 1, 1, 1, 1, 1])) == list(\"ABCDEF\")\n    assert list(compress(\"ABCDEF\", [1, 0, 1])) == list(\"AC\")\n    assert list(compress(\"ABC\", [0, 1, 1, 1, 1, 1])) == list(\"BC\")\n    n = 10000\n    data = chain.from_iterable(repeat(range(6), n))\n    selectors = chain.from_iterable(repeat((0, 1)))\n    assert list(compress(data, selectors)) == [1, 3, 5] * n\n\n\n@test\ndef test_count_from_cpython():\n    assert lzip(\"abc\", count()) == [(\"a\", 0), (\"b\", 1), (\"c\", 2)]\n    assert lzip(\"abc\", count(3)) == [(\"a\", 3), (\"b\", 4), (\"c\", 5)]\n    assert take(2, lzip(\"abc\", count(3))) == [(\"a\", 3), (\"b\", 4)]\n    assert take(2, zip(\"abc\", count(-1))) == [(\"a\", -1), (\"b\", 0)]\n    assert take(2, zip(\"abc\", count(-3))) == [(\"a\", -3), (\"b\", -2)]\n    assert take(3, count(3.25)) == [3.25, 4.25, 5.25]\n\n\n@test\ndef test_count_with_stride_from_cpython():\n    assert lzip(\"abc\", count(2, 3)) == [(\"a\", 2), (\"b\", 5), (\"c\", 8)]\n    assert lzip(\"abc\", count(start=2, step=3)) == [(\"a\", 2), (\"b\", 5), (\"c\", 8)]\n    assert lzip(\"abc\", count(step=-1)) == [(\"a\", 0), (\"b\", -1), (\"c\", -2)]\n    assert lzip(\"abc\", count(2, 0)) == [(\"a\", 2), (\"b\", 2), (\"c\", 2)]\n    assert lzip(\"abc\", count(2, 1)) == [(\"a\", 2), (\"b\", 3), (\"c\", 4)]\n    assert lzip(\"abc\", count(2, 3)) == [(\"a\", 2), (\"b\", 5), (\"c\", 8)]\n    assert take(3, count(2.0, 1.25)) == [2.0, 3.25, 4.5]\n\n\n@test\ndef test_cycle_from_cpython():\n    assert take(10, cycle(\"abc\")) == list(\"abcabcabca\")\n    assert list(cycle(\"\")) == []\n    assert list(islice(cycle(gen3()), 10)) == [0, 1, 2, 0, 1, 2, 0, 1, 2, 0]\n\n\n@test\ndef test_groupby_from_cpython():\n    # Check whether it accepts arguments correctly\n    assert [] == list(groupby(List[int]()))\n    assert [] == list(groupby(List[int](), key=lambda a: a))\n    # Check normal input\n    if 1:\n        s = [\n            (0, 10, 20),\n            (0, 11, 21),\n            (0, 12, 21),\n            (1, 13, 21),\n            (1, 14, 22),\n            (2, 15, 22),\n            (3, 16, 23),\n            (3, 17, 23),\n        ]\n\n        if 1:\n            dup = []\n            for k, g in groupby(s, lambda r: r[0]):\n                for elem in g:\n                    assert k == elem[0]\n                    dup.append(elem)\n            assert s == dup\n\n        # Check nested case\n        if 1:\n            dup = []\n            for k, g in groupby(s, testR):\n                for ik, ig in groupby(g, testR2):\n                    for elem in ig:\n                        assert k == elem[0]\n                        assert ik == elem[2]\n                        dup.append(elem)\n            assert s == dup\n\n        # Check case where inner iterator is not used\n        keys = [k for k, g in groupby(s, testR)]\n        expectedkeys = set([r[0] for r in s])\n        assert set(keys) == expectedkeys\n        assert len(keys) == len(expectedkeys)\n\n    if 1:\n        # Exercise pipes and filters style\n        s = \"abracadabra\"\n        if 1:\n            # sort s | uniq\n            r = [k for k, g in groupby(sorted(s))]\n            assert r == [\"a\", \"b\", \"c\", \"d\", \"r\"]\n        if 1:\n            # sort s | uniq -d\n            r = [k for k, g in groupby(sorted(s)) if list(islice(g, 1, 2))]\n            assert r == [\"a\", \"b\", \"r\"]\n        if 1:\n            # sort s | uniq -c\n            r = [(len(list(g)), k) for k, g in groupby(sorted(s))]\n            assert r == [(5, \"a\"), (2, \"b\"), (1, \"c\"), (1, \"d\"), (2, \"r\")]\n        if 1:\n            # sort s | uniq -c | sort -rn | head -3\n            r = sorted(\n                [(len(list(g)), k) for k, g in groupby(sorted(s))], reverse=True\n            )[:3]\n            assert r == [(5, \"a\"), (2, \"r\"), (2, \"b\")]\n\n\n@test\ndef test_filter_from_cpython():\n    assert list(filter(isEven, range(6))) == [0, 2, 4]\n    # assert list(filter(None, [0,1,0,2,0])) == [1,2]  # TODO\n    assert list(filter(lambda x: bool(x), [0, 1, 0, 2, 0])) == [1, 2]\n    assert take(4, filter(isEven, count())) == [0, 2, 4, 6]\n\n\n@test\ndef test_filterfalse_from_cpython():\n    assert list(filterfalse(isEven, range(6))) == [1, 3, 5]\n    # assert list(filter(None, [0,1,0,2,0])) == [0,0,0]  # TODO\n    assert list(filterfalse(lambda x: bool(x), [0, 1, 0, 2, 0])) == [0, 0, 0]\n    assert take(4, filterfalse(isEven, count())) == [1, 3, 5, 7]\n\n\n@test\ndef test_zip_from_cpython():\n    ans = [(x, y) for x, y in zip(\"abc\", count())]\n    assert ans == [(\"a\", 0), (\"b\", 1), (\"c\", 2)]\n    assert list(zip(\"abc\", range(6))) == lzip(\"abc\", range(6))\n    assert list(zip(\"abcdef\", range(3))) == lzip(\"abcdef\", range(3))\n    assert take(3, zip(\"abcdef\", count())) == lzip(\"abcdef\", range(3))\n    assert list(zip(\"abcdef\")) == lzip(\"abcdef\")\n    assert list(zip()) == lzip()\n    assert [pair for pair in zip(\"abc\", \"def\")] == lzip(\"abc\", \"def\")\n\n\n@test\ndef test_ziplongest_from_cpython():\n    for args in (\n        (range(1000), range(2000, 2100), range(3000, 3050)),\n        (range(1000), range(0), range(3000, 3050), range(1200), range(1500)),\n        (range(1000), range(0), range(3000, 3050), range(1200), range(1500), range(0)),\n    ):\n        target = [\n            tuple(arg[i] if i < len(arg) else None for arg in args)\n            for i in range(max(map(len, args)))\n        ]\n        assert str(list(zip_longest(*args))) == str(target)\n        target2 = [\n            [(-999 if e is None else e.__val__()) for e in t] for t in target\n        ]  # Replace None fills with 'X'\n        assert list(zip_longest(*args, fillvalue=-999)) == target2\n\n    assert (\n        str(list(zip_longest(\"abc\", range(6))))\n        == \"[('a', 0), ('b', 1), ('c', 2), (None, 3), (None, 4), (None, 5)]\"\n    )\n    assert (\n        str(list(zip_longest(range(6), \"abc\")))\n        == \"[(0, 'a'), (1, 'b'), (2, 'c'), (3, None), (4, None), (5, None)]\"\n    )\n\n\n@test\ndef test_product_from_cpython():\n    for args, result in (\n        # ((), [()]),                     # zero iterables  # TODO\n        ((\"ab\",), [(\"a\",), (\"b\",)]),  # one iterable\n        (\n            (range(2), range(3)),\n            [(0, 0), (0, 1), (0, 2), (1, 0), (1, 1), (1, 2)],\n        ),  # two iterables\n        (\n            (range(0), range(2), range(3)),\n            List[Tuple[int, int, int]](),\n        ),  # first iterable with zero length\n        (\n            (range(2), range(0), range(3)),\n            List[Tuple[int, int, int]](),\n        ),  # middle iterable with zero length\n        (\n            (range(2), range(3), range(0)),\n            List[Tuple[int, int, int]](),\n        ),  # last iterable with zero length\n    ):\n        assert list(product(*args)) == result\n\n    assert (\n        len(list(product(range(7), range(7), range(7), range(7), range(7), range(7))))\n        == 7 ** 6\n    )\n\n\n@test\ndef test_repeat_from_cpython():\n    assert list(repeat(object=\"a\", times=3)) == [\"a\", \"a\", \"a\"]\n    assert lzip(range(3), repeat(\"a\")) == [(0, \"a\"), (1, \"a\"), (2, \"a\")]\n    assert list(repeat(\"a\", 3)) == [\"a\", \"a\", \"a\"]\n    assert take(3, repeat(\"a\")) == [\"a\", \"a\", \"a\"]\n    assert list(repeat(\"a\", 0)) == []\n    assert list(repeat(\"a\", -3)) == []\n\n\n@test\ndef test_map_from_cpython():\n    power = lambda a, b: a ** b\n    assert list(map(power, range(3), range(1, 7))) == [0 ** 1, 1 ** 2, 2 ** 3]\n    assert list(map(tupleize, \"abc\", range(5))) == [(\"a\", 0), (\"b\", 1), (\"c\", 2)]\n    assert list(map(tupleize, \"abc\", count())) == [(\"a\", 0), (\"b\", 1), (\"c\", 2)]\n    assert take(2, map(tupleize, \"abc\", count())) == [(\"a\", 0), (\"b\", 1)]\n    assert list(map(tupleize, List[int]())) == []\n\n\n@test\ndef test_starmap_from_cpython():\n    power = lambda a, b: a ** b\n    assert list(starmap(power, zip(range(3), range(1, 7)))) == [0 ** 1, 1 ** 2, 2 ** 3]\n    assert take(3, starmap(power, zip(count(), count(1)))) == [0 ** 1, 1 ** 2, 2 ** 3]\n    # assert list(starmap(tupleize, List[int]())) == []  # TODO\n    assert list(starmap(power, [(4, 5)])) == [4 ** 5]\n\n\n@test\ndef test_islice_from_cpython():\n    for args in (  # islice(args) should agree with range(args)\n        (10, 20, 3),\n        (10, 3, 20),\n        (10, 20),\n        (10, 10),\n        (10, 3),\n        (20,),\n    ):\n        assert list(islice(range(100), *args)) == list(range(*args))\n\n    for args, tgtargs in (  # Stop when seqn is exhausted\n        ((10, 110, 3), ((10, 100, 3))),\n        ((10, 110), ((10, 100))),\n        ((110,), (100,)),\n    ):\n        assert list(islice(range(100), *args)) == list(range(*tgtargs))\n\n    # Test stop=None\n    assert list(islice(range(10), None)) == list(range(10))\n    assert list(islice(range(10), None, None)) == list(range(10))\n    assert list(islice(range(10), None, None, None)) == list(range(10))\n    assert list(islice(range(10), 2, None)) == list(range(2, 10))\n    assert list(islice(range(10), 1, None, 2)) == list(range(1, 10, 2))\n\n\n@test\ndef test_takewhile_from_cpython():\n    data = [1, 3, 5, 20, 2, 4, 6, 8]\n    assert list(takewhile(underten, data)) == [1, 3, 5]\n    assert list(takewhile(underten, List[int]())) == []\n    t = takewhile(lambda x: bool(x), [1, 1, 1, 0, 0, 0])\n    assert list(t) == [1, 1, 1]\n\n\n@test\ndef test_dropwhile_from_cpython():\n    data = [1, 3, 5, 20, 2, 4, 6, 8]\n    assert list(dropwhile(underten, data)) == [20, 2, 4, 6, 8]\n    assert list(dropwhile(underten, List[int]())) == []\n\n\n@test\ndef test_tee_from_cpython():\n    import random\n\n    n = 200\n\n    a, b = tee(List[int]())  # test empty iterator\n    assert list(a) == []\n    assert list(b) == []\n\n    a, b = tee(irange(n))  # test 100% interleaved\n    assert lzip(a, b) == lzip(range(n), range(n))\n\n    a, b = tee(irange(n))  # test 0% interleaved\n    assert list(a) == list(range(n))\n    assert list(b) == list(range(n))\n\n    a, b = tee(irange(n))  # test dealloc of leading iterator\n    for i in range(100):\n        assert next(a) == i\n    assert list(b) == list(range(n))\n\n    a, b = tee(irange(n))  # test dealloc of trailing iterator\n    for i in range(100):\n        assert next(a) == i\n    assert list(a) == list(range(100, n))\n\n    for j in range(5):  # test randomly interleaved\n        order = [0] * n + [1] * n\n        random.shuffle(order)\n        lists = ([], [])\n        its = tee(irange(n))\n        for i in order:\n            value = next(its[i])\n            lists[i].append(value)\n        assert lists[0] == list(range(n))\n        assert lists[1] == list(range(n))\n\n    # test long-lagged and multi-way split\n    a, b, c = tee(range(2000), 3)\n    for i in range(100):\n        assert next(a) == i\n    assert list(b) == list(range(2000))\n    assert [next(c), next(c)] == list(range(2))\n    assert list(a) == list(range(100, 2000))\n    assert list(c) == list(range(2, 2000))\n\ntest_accumulate_from_cpython()\ntest_chain_from_cpython()\ntest_chain_from_iterable_from_cpython()\ntest_combinations_from_cpython()  # takes long time to typecheck\ntest_combinations_with_replacement_from_cpython()\ntest_permutations_from_cpython()\ntest_combinatorics_from_cpython()  # TODO: takes FOREVER to typecheck\ntest_compress_from_cpython()\ntest_count_from_cpython()\ntest_count_with_stride_from_cpython()\ntest_cycle_from_cpython()\ntest_groupby_from_cpython()\ntest_filter_from_cpython()\ntest_filterfalse_from_cpython()\ntest_zip_from_cpython()\ntest_ziplongest_from_cpython()\ntest_product_from_cpython()\ntest_repeat_from_cpython()\ntest_map_from_cpython()\ntest_starmap_from_cpython()\ntest_islice_from_cpython()\ntest_takewhile_from_cpython()\ntest_dropwhile_from_cpython()\ntest_tee_from_cpython()\n"
  },
  {
    "path": "test/stdlib/llvm_test.codon",
    "content": "#%% ptr,barebones\nimport internal.gc as gc\nprint gc.sizeof(Ptr[int]) #: 8\nprint gc.atomic(Ptr[int]) #: False\ny = Ptr[int](1)\ny[0] = 11\nprint y[0] #: 11\n_y = y.as_byte()\nprint int(_y[0]) #: 11\ny = Ptr[int](5)\ny[0] = 1; y[1] = 2; y[2] = 3; y[3] = 4; y[4] = 5\nz = Ptr[int](y)\nprint y[1], z[2] #: 2 3\nz = Ptr[int](y.as_byte())\nprint y[1], z[2] #: 2 3\nprint z.__bool__() #: True\nz.__int__() # big number...\nzz = z.__copy__() # this is not a deep copy!\nzz[2] = 10\nprint z[2], zz[2] #: 10 10\nprint y.__getitem__(1) #: 2\ny.__setitem__(1, 3)\nprint y[1] #: 3\nprint y.__add__(1)[0] #: 3\nprint (y + 3).__sub__(y + 1) #: 2\nprint y.__eq__(z) #: True\nprint y.__eq__(zz) #: True\nprint y.as_byte().__eq__('abc'.ptr) #: False\nprint y.__ne__(z) #: False\nprint y.__lt__(y+1) #: True\nprint y.__gt__(y+1) #: False\nprint (y+1).__le__(y) #: False\nprint y.__ge__(y) #: True\ny.__prefetch_r0__()\ny.__prefetch_r1__()\ny.__prefetch_r2__()\ny.__prefetch_r3__()\ny.__prefetch_w0__()\ny.__prefetch_w1__()\ny.__prefetch_w2__()\ny.__prefetch_w3__()\n\n#%% int,barebones\na = int()\nb = int(5)\nc = int(True)\nd = int(byte(1))\ne = int(1.1)\nprint a, b, c, d, e #: 0 5 1 1 1\nprint a.__repr__() #: 0\nprint b.__copy__() #: 5\nprint b.__hash__() #: 5\nprint a.__bool__(), b.__bool__() #: False True\nprint a.__pos__() #: 0\nprint b.__neg__() #: -5\nprint (-b).__abs__() #: 5\nprint c.__truediv__(5) #: 0.2\nprint b.__lshift__(1) #: 10\nprint b.__rshift__(1) #: 2\nprint b.__truediv__(5.15) #: 0.970874\nprint a.__add__(1) #: 1\nprint a.__add__(1.1) #: 1.1\nprint a.__sub__(1) #: -1\nprint a.__sub__(1.1) #: -1.1\nprint b.__mul__(1) #: 5\nprint b.__mul__(1.1) #: 5.5\nprint b.__floordiv__(2) #: 2\nprint b.__floordiv__(1.1) #: 4\nprint b.__mod__(2) #: 1\nprint b.__mod__(1.1) #: 0.6\nprint a.__eq__(1) #: False\nprint a.__eq__(1.1) #: False\nprint a.__ne__(1) #: True\nprint a.__ne__(1.1) #: True\nprint a.__lt__(1) #: True\nprint a.__lt__(1.1) #: True\nprint a.__le__(1) #: True\nprint a.__le__(1.1) #: True\nprint a.__gt__(1) #: False\nprint a.__gt__(1.1) #: False\nprint a.__ge__(1) #: False\nprint a.__ge__(1.1) #: False\n\n#%% uint,barebones\nau = Int[123](15)\na = UInt[123]()\nb = UInt[123](a)\na = UInt[123](15)\na = UInt[123](au)\nprint a.__copy__() #: 15\nprint a.__hash__() #: 15\nprint a.__bool__() #: True\nprint a.__pos__() #: 15\nprint a.__neg__() #: 10633823966279326983230456482242756593\nprint a.__invert__() #: 10633823966279326983230456482242756592\nm = UInt[123](4)\nprint a.__add__(m), a.__sub__(m), a.__mul__(m), a.__floordiv__(m), a.__truediv__(m) #: 19 11 60 3 3.75\nprint a.__mod__(m), a.__lshift__(m), a.__rshift__(m) #: 3 240 0\nprint a.__eq__(m), a.__ne__(m), a.__lt__(m), a.__gt__(m), a.__le__(m), a.__ge__(m) #: False True False True False True\nprint a.__and__(m), a.__or__(m), a.__xor__(m) #: 4 15 11\nprint a, a.popcnt() #: 15 4\nac = Int[128](5)\nbc = Int[32](5)\nprint ac, bc, int(ac), int(bc) #: 5 5 5 5\n\nprint int(Int[12](12)) #: 12\nprint int(Int[122](12)) #: 12\nprint int(Int[64](12)) #: 12\nprint int(UInt[12](12)) #: 12\nprint int(UInt[122](12)) #: 12\nprint int(UInt[64](12)) #: 12\n\nprint Int[32](212) #: 212\nprint Int[64](212) #: 212\nprint Int[66](212) #: 212\nprint UInt[32](112) #: 112\nprint UInt[64](112) #: 112\nprint UInt[66](112) #: 112\n\n\n#%% float,barebones\nz = float.__new__()\nz = 5.5\nprint z.__repr__() #: 5.5\nprint z.__copy__() #: 5.5\nprint z.__bool__(), z.__pos__(), z.__neg__() #: True 5.5 -5.5\nf = 1.3\nprint z.__floordiv__(f), z.__floordiv__(2) #: 4 2\nprint z.__truediv__(f), z.__truediv__(2) #: 4.23077 2.75\nprint z.__pow__(2.2), z.__pow__(2) #: 42.54 30.25\nprint z.__add__(2) #: 7.5\nprint z.__sub__(2) #: 3.5\nprint z.__mul__(2) #: 11\nprint z.__truediv__(2) #: 2.75\nprint z.__mod__(2) #: 1.5\nprint z.__eq__(2) #: False\nprint z.__ne__(2) #: True\nprint z.__lt__(2) #: False\nprint z.__gt__(2) #: True\nprint z.__le__(2) #: False\nprint z.__ge__(2) #: True\n\n#%% bool,barebones\nz = bool.__new__()\nprint z.__repr__() #: False\nprint z.__copy__() #: False\nprint z.__bool__(), z.__invert__() #: False True\nprint z.__eq__(True) #: False\nprint z.__ne__(True) #: True\nprint z.__lt__(True) #: True\nprint z.__gt__(True) #: False\nprint z.__le__(True) #: True\nprint z.__ge__(True) #: False\nprint z.__and__(True), z.__or__(True), z.__xor__(True) #: False True True\n\n#%% byte,barebones\nz = byte.__new__()\nz = byte(65)\nprint z.__repr__() #: byte('A')\nprint z.__bool__() #: True\nprint z.__eq__(byte(5)) #: False\nprint z.__ne__(byte(5)) #: True\nprint z.__lt__(byte(5)) #: False\nprint z.__gt__(byte(5)) #: True\nprint z.__le__(byte(5)) #: False\nprint z.__ge__(byte(5)) #: True\n\n#%% array,barebones\na = Array[float](5)\npa = Ptr[float](3)\nz = Array[float](pa, 3)\nz.__copy__()\nprint z.__len__() #: 3\nprint z.__bool__() #: True\nz.__setitem__(0, 1.0)\nz.__setitem__(1, 2.0)\nz.__setitem__(2, 3.0)\nprint z.__getitem__(1) #: 2\nprint z.slice(0, 2).len #: 2\n\n#%% optional,barebones\na = Optional[float]()\nprint bool(a) #: False\na = Optional[float](0.0)\nprint bool(a) #: False\na = Optional[float](5.5)\nprint a.__bool__(), a.__val__() #: True 5.5\n\n#%% generator,barebones\ndef foo():\n    yield 1\n    yield 2\n    yield 3\nz = foo()\ny = z.__iter__()\nprint str(y.__raw__())[:2] #: 0x\nprint y.__done__() #: False\nprint y.__promise__()[0] #: 0\ny.__resume__()\nprint y.__repr__()[:16] #: <generator at 0x\nprint y.__next__() #: 1\nprint y.done() #: False\ny.send(1)\ny.destroy()\nprint y.done() #: True\n"
  },
  {
    "path": "test/stdlib/math_test.codon",
    "content": "import math\n\nNAN = math.nan\nINF = math.inf\nNINF = -math.inf\n\n\ndef close(a: float, b: float, epsilon: float = 1e-7):\n    return abs(a - b) <= epsilon\n\n\n@test\ndef test_isnan():\n    assert math.isnan(float(\"nan\")) == True\n    assert math.isnan(4.0) == False\n\n\n@test\ndef test_isinf():\n    assert math.isinf(float(\"inf\")) == True\n    assert math.isinf(7.0) == False\n\n\n@test\ndef test_isfinite():\n    assert math.isfinite(1.4) == True\n    assert math.isfinite(0.0) == True\n    assert math.isfinite(NAN) == False\n    assert math.isfinite(INF) == False\n    assert math.isfinite(NINF) == False\n\n\n@test\ndef test_ceil():\n    assert isinstance(math.ceil(1.0), int)\n    assert math.ceil(3.3) == 4\n    assert math.ceil(0.5) == 1\n    assert math.ceil(1.0) == 1\n    assert math.ceil(1.5) == 2\n    assert math.ceil(-0.5) == 0\n    assert math.ceil(-1.0) == -1\n    assert math.ceil(-1.5) == -1\n\n\n@test\ndef test_floor():\n    assert isinstance(math.floor(1.0), int)\n    assert math.floor(3.3) == 3\n    assert math.floor(0.5) == 0\n    assert math.floor(1.0) == 1\n    assert math.floor(1.5) == 1\n    assert math.floor(-0.5) == -1\n    assert math.floor(-1.0) == -1\n    assert math.floor(-1.5) == -2\n\n\n@test\ndef test_fabs():\n    assert math.fabs(-1.0) == 1\n    assert math.fabs(0.0) == 0\n    assert math.fabs(1.0) == 1\n\n\n@test\ndef test_fmod():\n    assert math.fmod(10.0, 1.0) == 0.0\n    assert math.fmod(10.0, 0.5) == 0.0\n    assert math.fmod(10.0, 1.5) == 1.0\n    assert math.fmod(-10.0, 1.0) == -0.0\n    assert math.fmod(-10.0, 0.5) == -0.0\n    assert math.fmod(-10.0, 1.5) == -1.0\n\n\n@test\ndef test_exp():\n    assert math.exp(0.0) == 1\n    assert math.exp(-1.0) == 1 / math.e\n    assert math.exp(1.0) == math.e\n\n\n@test\ndef test_expm1():\n    assert math.expm1(0.0) == 0\n    assert close(math.expm1(1.0), 1.7182818284590453)\n    assert close(math.expm1(3.0), 19.085536923187668)\n    assert close(math.expm1(5.0), 147.4131591025766)\n    assert math.expm1(INF) == INF\n    assert math.expm1(NINF) == -1\n    assert math.isnan(math.expm1(NAN)) == True\n\n\n@test\ndef test_ldexp():\n    assert math.ldexp(0.0, 1) == 0.0\n    assert math.ldexp(1.0, 1) == 2.0\n    assert math.ldexp(1.0, -1) == 0.5\n    assert math.ldexp(-1.0, 1) == -2.0\n    assert math.ldexp(0.0, 1) == 0.0\n    assert math.ldexp(1.0, -1000000) == 0.0\n    assert math.ldexp(-1.0, -1000000) == -0.0\n    assert math.ldexp(INF, 30) == INF\n    assert math.ldexp(NINF, -213) == NINF\n    assert math.isnan(math.ldexp(NAN, 0)) == True\n\n\n@test\ndef test_log():\n    assert math.log(1.0 / math.e) == -1\n    assert math.log(1.0) == 0\n    assert math.log(math.e) == 1\n\n\n@test\ndef test_log2():\n    assert math.log2(1.0) == 0.0\n    assert math.log2(2.0) == 1.0\n    assert math.log2(4.0) == 2.0\n    assert math.log2(2.0 ** 1023) == 1023.0\n    assert math.isnan(math.log2(-1.5)) == True\n    assert math.isnan(math.log2(NINF)) == True\n    assert math.isnan(math.log2(NAN)) == True\n\n\n@test\ndef test_log10():\n    assert math.log10(0.1) == -1\n    assert math.log10(1.0) == 0\n    assert math.log10(10.0) == 1\n    assert math.log10(10000.0) == 4\n\n\n@test\ndef test_degrees():\n    assert math.degrees(math.pi) == 180.0\n    assert math.degrees(math.pi / 2) == 90.0\n    assert math.degrees(-math.pi / 4) == -45.0\n    assert math.degrees(0.0) == 0.0\n\n\n@test\ndef test_radians():\n    assert math.radians(180.0) == math.pi\n    assert math.radians(90.0) == math.pi / 2\n    assert math.radians(-45.0) == -math.pi / 4\n    assert math.radians(0.0) == 0.0\n\n\n@test\ndef test_sqrt():\n    assert math.sqrt(4.0) == 2\n    assert math.sqrt(0.0) == 0\n    assert math.sqrt(1.0) == 1\n    assert math.isnan(math.sqrt(-1.0)) == True\n\n\n@test\ndef test_pow():\n    assert math.pow(0.0, 1.0) == 0\n    assert math.pow(1.0, 0.0) == 1\n    assert math.pow(2.0, 1.0) == 2\n    assert math.pow(2.0, -1.0) == 0.5\n    assert math.pow(-0.0, 3.0) == -0.0\n    assert math.pow(-0.0, 2.3) == 0.0\n    assert math.pow(-0.0, 0.0) == 1\n    assert math.pow(-0.0, -0.0) == 1\n    assert math.pow(-2.0, 2.0) == 4.0\n    assert math.pow(-2.0, 3.0) == -8.0\n    assert math.pow(-2.0, -3.0) == -0.125\n    assert math.pow(INF, 1.0) == INF\n    assert math.pow(NINF, 1.0) == NINF\n    assert math.pow(1.0, INF) == 1\n    assert math.pow(1.0, NINF) == 1\n    assert math.isnan(math.pow(NAN, 1.0)) == True\n    assert math.isnan(math.pow(2.0, NAN)) == True\n    assert math.isnan(math.pow(0.0, NAN)) == True\n    assert math.pow(1.0, NAN) == 1\n\n\n@test\ndef test_acos():\n    assert math.acos(-1.0) == math.pi\n    assert math.acos(0.0) == math.pi / 2\n    assert math.acos(1.0) == 0\n    assert math.isnan(math.acos(NAN)) == True\n\n\n@test\ndef test_asin():\n    assert math.asin(-1.0) == -math.pi / 2\n    assert math.asin(0.0) == 0\n    assert math.asin(1.0) == math.pi / 2\n    assert math.isnan(math.asin(NAN)) == True\n\n\n@test\ndef test_atan():\n    assert math.atan(-1.0) == -math.pi / 4\n    assert math.atan(0.0) == 0\n    assert math.atan(1.0) == math.pi / 4\n    assert math.atan(INF) == math.pi / 2\n    assert math.atan(NINF) == -math.pi / 2\n    assert math.isnan(math.atan(NAN)) == True\n\n\n@test\ndef test_atan2():\n    assert math.atan2(-1.0, 0.0) == -math.pi / 2\n    assert math.atan2(-1.0, 1.0) == -math.pi / 4\n    assert math.atan2(0.0, 1.0) == 0\n    assert math.atan2(1.0, 1.0) == math.pi / 4\n    assert math.atan2(1.0, 0.0) == math.pi / 2\n    assert math.atan2(-0.0, 0.0) == -0\n    assert math.atan2(-0.0, 2.3) == -0\n    assert math.atan2(0.0, -2.3) == math.pi\n    assert math.atan2(INF, NINF) == math.pi * 3 / 4\n    assert math.atan2(INF, 2.3) == math.pi / 2\n    assert math.isnan(math.atan2(NAN, 0.0)) == True\n\n\n@test\ndef test_cos():\n    assert math.cos(0.0) == 1\n    assert close(math.cos(math.pi / 2), 6.123233995736766e-17)\n    assert close(math.cos(-math.pi / 2), 6.123233995736766e-17)\n    assert math.cos(math.pi) == -1\n    assert math.isnan(math.cos(INF)) == True\n    assert math.isnan(math.cos(NINF)) == True\n    assert math.isnan(math.cos(NAN)) == True\n\n\n@test\ndef test_sin():\n    assert math.sin(0.0) == 0\n    assert math.sin(math.pi / 2) == 1\n    assert math.sin(-math.pi / 2) == -1\n    assert math.isnan(math.sin(INF)) == True\n    assert math.isnan(math.sin(NINF)) == True\n    assert math.isnan(math.sin(NAN)) == True\n\n\n@test\ndef test_hypot():\n    assert math.hypot(12.0, 5.0) == 13\n    assert math.hypot(12.0 / 32.0, 5.0 / 32) == 13 / 32\n    assert math.hypot(0.0, 0.0) == 0\n    assert math.hypot(-3.0, 4.0) == 5\n    assert math.hypot(3.0, 4.0) == 5\n\n\n@test\ndef test_tan():\n    assert math.tan(0.0) == 0\n    assert close(math.tan(math.pi / 4), 0.9999999999999999)\n    assert close(math.tan(-math.pi / 4), -0.9999999999999999)\n    assert math.isnan(math.tan(INF)) == True\n    assert math.isnan(math.tan(NINF)) == True\n    assert math.isnan(math.tan(NAN)) == True\n\n\n@test\ndef test_cosh():\n    assert math.cosh(0.0) == 1\n    assert math.cosh(2.0) - 2 * math.cosh(1.0) ** 2 == -1\n    assert math.cosh(INF) == INF\n    assert math.cosh(NINF) == INF\n    assert math.isnan(math.cosh(NAN)) == True\n\n\n@test\ndef test_sinh():\n    assert math.sinh(0.0) == 0\n    assert math.sinh(1.0) + math.sinh(-1.0) == 0\n    assert math.sinh(INF) == INF\n    assert math.sinh(NINF) == NINF\n    assert math.isnan(math.sinh(NAN)) == True\n\n\n@test\ndef test_tanh():\n    assert math.tanh(0.0) == 0\n    assert math.tanh(1.0) + math.tanh(-1.0) == 0\n    assert math.tanh(INF) == 1\n    assert math.tanh(NINF) == -1\n    assert math.isnan(math.tanh(NAN)) == True\n\n\n@test\ndef test_acosh():\n    assert math.acosh(1.0) == 0\n    assert close(math.acosh(2.0), 1.3169578969248166)\n    assert math.acosh(INF) == INF\n    assert math.isnan(math.acosh(NAN)) == True\n    assert math.isnan(math.acosh(-1.0)) == True\n\n\n@test\ndef test_asinh():\n    assert math.asinh(0.0) == 0\n    assert close(math.asinh(1.0), 0.881373587019543)\n    assert close(math.asinh(-1.0), -0.881373587019543)\n    assert math.asinh(INF) == INF\n    assert math.isnan(math.asinh(NAN)) == True\n    assert math.asinh(NINF) == NINF\n\n\n@test\ndef test_atanh():\n    assert math.atanh(0.0) == 0\n    assert close(math.atanh(0.5), 0.5493061443340549)\n    assert close(math.atanh(-0.5), -0.5493061443340549)\n    assert math.isnan(math.atanh(INF)) == True\n    assert math.isnan(math.atanh(NAN)) == True\n    assert math.isnan(math.atanh(NINF)) == True\n\n\n@test\ndef test_copysign():\n    assert math.copysign(1.0, -0.0) == -1\n    assert math.copysign(1.0, 42.0) == 1\n    assert math.copysign(1.0, -42.0) == -1\n    assert math.copysign(3.0, 0.0) == 3\n    assert math.copysign(INF, 0.0) == INF\n    assert math.copysign(INF, -0.0) == NINF\n    assert math.copysign(NINF, 0.0) == INF\n    assert math.copysign(NINF, -0.0) == NINF\n    assert math.copysign(1.0, INF) == 1\n    assert math.copysign(1.0, NINF) == -1\n    assert math.copysign(INF, INF) == INF\n    assert math.copysign(INF, NINF) == NINF\n    assert math.copysign(NINF, INF) == INF\n    assert math.copysign(NINF, NINF) == NINF\n    assert math.isnan(math.copysign(NAN, 1.0)) == True\n    assert math.isnan(math.copysign(NAN, INF)) == True\n    assert math.isnan(math.copysign(NAN, NINF)) == True\n    assert math.isnan(math.copysign(NAN, NAN)) == True\n\n\n@test\ndef test_log1p():\n    assert close(math.log1p(2.0), 1.0986122886681098)\n    assert close(math.log1p(2.0 ** 90), 62.383246250395075)\n    assert close(math.log1p(2.0 ** 300), 207.94415416798358)\n    assert math.log1p(INF) == INF\n    assert math.log1p(-1.0) == NINF\n\n\n@test\ndef test_trunc():\n    assert isinstance(math.trunc(1.0), int)\n    assert math.trunc(1.0) == 1\n    assert math.trunc(-1.0) == -1\n    assert math.trunc(1.5) == 1\n    assert math.trunc(-1.5) == -1\n    assert math.trunc(1.99999999) == 1\n    assert math.trunc(-1.99999999) == -1\n    assert math.trunc(0.99999999) == 0\n    assert math.trunc(-100.999) == -100\n\n\n@test\ndef test_erf():\n    assert close(math.erf(1.0), 0.8427007929497148)\n    assert math.erf(0.0) == 0\n    assert close(math.erf(3.0), 0.9999779095030015)\n    assert math.erf(256.0) == 1.0\n    assert math.erf(INF) == 1.0\n    assert math.erf(NINF) == -1.0\n    assert math.isnan(math.erf(NAN)) == True\n\n\n@test\ndef test_erfc():\n    assert math.erfc(0.0) == 1.0\n    assert close(math.erfc(1.0), 0.15729920705028516)\n    assert close(math.erfc(2.0), 0.0046777349810472645)\n    assert close(math.erfc(-1.0), 1.8427007929497148)\n    assert math.erfc(INF) == 0.0\n    assert math.erfc(NINF) == 2.0\n    assert math.isnan(math.erfc(NAN)) == True\n\n\n@test\ndef test_gamma():\n    assert close(math.gamma(6.0), 120.0)\n    assert close(math.gamma(1.0), 1.0)\n    assert close(math.gamma(2.0), 1.0)\n    assert close(math.gamma(3.0), 2.0)\n    assert math.isnan(math.gamma(-1.0)) == True\n    assert math.gamma(INF) == INF\n    assert math.isnan(math.gamma(NINF)) == True\n    assert math.isnan(math.gamma(NAN)) == True\n\n\n@test\ndef test_lgamma():\n    assert math.lgamma(1.0) == 0.0\n    assert math.lgamma(2.0) == 0.0\n    assert math.lgamma(-1.0) == INF\n    assert math.lgamma(INF) == INF\n    assert math.lgamma(NINF) == INF\n    assert math.isnan(math.lgamma(NAN)) == True\n\n\n@test\ndef test_remainder():\n    assert math.remainder(2.0, 2.0) == 0.0\n    assert math.remainder(-4.0, 1.0) == -0.0\n    assert close(math.remainder(-3.8, 1.0), 0.20000000000000018)\n    assert close(math.remainder(3.8, 1.0), -0.20000000000000018)\n    assert math.isnan(math.remainder(INF, 1.0)) == True\n    assert math.isnan(math.remainder(NINF, 1.0)) == True\n    assert math.isnan(math.remainder(NAN, 1.0)) == True\n\n\n@test\ndef test_gcd():\n    assert math.gcd(0, 0) == 0\n    assert math.gcd(1, 0) == 1\n    assert math.gcd(-1, 0) == 1\n    assert math.gcd(0, 1) == 1\n    assert math.gcd(0, -1) == 1\n    assert math.gcd(7, 1) == 1\n    assert math.gcd(7, -1) == 1\n    assert math.gcd(-23, 15) == 1\n    assert math.gcd(120, 84) == 12\n    assert math.gcd(84, -120) == 12\n    assert math.gcd(Int[128]('1216342683557601535506311712'), Int[128]('436522681849110124616458784')) == Int[128](32)\n\n    x = 43461\n    y = 1064\n    for c in (652560, 576559230):\n        a = x * c\n        b = y * c\n        assert math.gcd(a, b) == c\n        assert math.gcd(b, a) == c\n        assert math.gcd(-a, b) == c\n        assert math.gcd(b, -a) == c\n        assert math.gcd(a, -b) == c\n        assert math.gcd(-b, a) == c\n        assert math.gcd(-a, -b) == c\n        assert math.gcd(-b, -a) == c\n\n    assert math.gcd() == 0\n    assert math.gcd(120) == 120\n    assert math.gcd(-120) == 120\n    assert math.gcd(120, 84, 102) == 6\n    assert math.gcd(120, 1, 84) == 1\n\n\n@test\ndef test_lcm():\n    assert math.lcm(0, 0) == 0\n    assert math.lcm(1, 0) == 0\n    assert math.lcm(-1, 0) == 0\n    assert math.lcm(0, 1) == 0\n    assert math.lcm(0, -1) == 0\n    assert math.lcm(7, 1) == 7\n    assert math.lcm(7, -1) == 7\n    assert math.lcm(-23, 15) == 345\n    assert math.lcm(120, 84) == 840\n    assert math.lcm(84, -120) == 840\n    #assert math.lcm(1216342683557601535506311712, 436522681849110124616458784) == 16592536571065866494401400422922201534178938447014944\n\n    x = 4346103\n    y = 1064501\n    for c in (8171, 58657):\n        a = x * c\n        b = y * c\n        d = x * y * c\n        assert math.lcm(a, b) == d\n        assert math.lcm(b, a) == d\n        assert math.lcm(-a, b) == d\n        assert math.lcm(b, -a) == d\n        assert math.lcm(a, -b) == d\n        assert math.lcm(-b, a) == d\n        assert math.lcm(-a, -b) == d\n        assert math.lcm(-b, -a) == d\n\n    assert math.lcm() == 1\n    assert math.lcm(120) == 120\n    assert math.lcm(-120) == 120\n    assert math.lcm(120, 84, 102) == 14280\n    assert math.lcm(120, 0, 84) == 0\n\n\n@test\ndef test_frexp():\n    assert math.frexp(-2.0) == (-0.5, 2)\n    assert math.frexp(-1.0) == (-0.5, 1)\n    assert math.frexp(0.0) == (0.0, 0)\n    assert math.frexp(1.0) == (0.5, 1)\n    assert math.frexp(2.0) == (0.5, 2)\n    assert math.frexp(INF)[0] == INF\n    assert math.frexp(NINF)[0] == NINF\n    assert math.isnan(math.frexp(NAN)[0]) == True\n\n\n@test\ndef test_modf():\n    assert math.modf(1.5) == (0.5, 1.0)\n    assert math.modf(-1.5) == (-0.5, -1.0)\n    assert math.modf(math.inf) == (0.0, INF)\n    assert math.modf(-math.inf) == (-0.0, NINF)\n    modf_nan = math.modf(NAN)\n    assert math.isnan(modf_nan[0]) == True\n    assert math.isnan(modf_nan[1]) == True\n\n\n@test\ndef test_isclose():\n    assert math.isclose(1.0 + 1.0, 1.000000000001 + 1.0) == True\n    assert math.isclose(2.90909324093284, 2.909093240932844234234234234) == True\n    assert math.isclose(2.90909324093284, 2.9) == False\n    assert math.isclose(2.90909324093284, 2.90909324) == True\n    assert math.isclose(2.90909324, 2.90909325) == False\n    assert math.isclose(NAN, 2.9) == False\n    assert math.isclose(2.9, NAN) == False\n    assert math.isclose(INF, INF) == True\n    assert math.isclose(NINF, NINF) == True\n    assert math.isclose(NINF, INF) == False\n    assert math.isclose(INF, NINF) == False\n\n\n@test\ndef test_fsum():\n    assert math.fsum((42,)) == 42.0\n    assert math.fsum((1,2,3)) == 6.0\n    assert math.fsum((1,2,-3)) == 0.0\n    assert math.fsum(()) == 0.0\n    assert math.fsum([.1] * 10) == 1.0\n\n    mant_dig = 53\n    etiny = -1074\n\n    def sum_exact(p):\n        n = len(p)\n        hi = 0.0\n        if n > 0:\n            hi = p[n-1]\n            n -= 1\n            while n > 0:\n                x = hi\n                y = p[n-1]\n                n -= 1\n                hi = x + y\n                yr = hi - x\n                lo = y - yr\n                if lo != 0.0:\n                    break\n\n            if n > 0 and ((lo < 0 and p[n-1] < 0) or (lo > 0 and p[n-1] > 0)):\n                y = lo * 2\n                x = hi + y\n                yr = x - hi\n                if y == yr:\n                    hi = x\n        return hi\n\n    def msum(iterable):\n        \"Full precision summation using multiple floats for intermediate values\"\n        # Rounded x+y stored in hi with the round-off stored in lo.  Together\n        # hi+lo are exactly equal to x+y.  The inner loop applies hi/lo summation\n        # to each partial so that the list of partial sums remains exact.\n        # Depends on IEEE-754 arithmetic guarantees.  See proof of correctness at:\n        # www-2.cs.cmu.edu/afs/cs/project/quake/public/papers/robust-arithmetic.ps\n\n        partials = []               # sorted, non-overlapping partial sums\n        for x in iterable:\n            i = 0\n            for y in partials:\n                if abs(x) < abs(y):\n                    x, y = y, x\n                hi = x + y\n                lo = y - (hi - x)\n                if lo:\n                    partials[i] = lo\n                    i += 1\n                x = hi\n            partials[i:] = [x]\n        return sum_exact(partials)\n\n    @pure\n    @llvm\n    def float1() -> float:\n        ret double 0x401DF11F45F4E61A\n\n    @pure\n    @llvm\n    def float2() -> float:\n        ret double 0xBFE62A2AF1BD3624\n\n    @pure\n    @llvm\n    def float3() -> float:\n        ret double 0x7C95555555555555\n\n    test_values = [\n        ([], 0.0),\n        ([0.0], 0.0),\n        ([1e100, 1.0, -1e100, 1e-100, 1e50, -1.0, -1e50], 1e-100),\n        ([2.0**53, -0.5, -2.0**-54], 2.0**53-1.0),\n        ([2.0**53, 1.0, 2.0**-100], 2.0**53+2.0),\n        ([2.0**53+10.0, 1.0, 2.0**-100], 2.0**53+12.0),\n        ([2.0**53-4.0, 0.5, 2.0**-54], 2.0**53-3.0),\n        ([1./n for n in range(1, 1001)], float1()),\n        ([(-1.)**n/n for n in range(1, 1001)], float2()),\n        ([1e16, 1., 1e-16], 10000000000000002.0),\n        ([1e16-2., 1.-2.**-53, -(1e16-2.), -(1.-2.**-53)], 0.0),\n        # exercise code for resizing partials array\n        ([2.**n - 2.**(n+50) + 2.**(n+52) for n in range(-1074, 972, 2)] +\n         [-2.**1022],\n         float3()),\n        ]\n\n    # Telescoping sum, with exact differences (due to Sterbenz)\n    terms = [1.7**i for i in range(1001)]\n    test_values.append((\n        [terms[i+1] - terms[i] for i in range(1000)] + [-terms[1000]],\n        -terms[0]\n    ))\n\n    for i, (vals, expected) in enumerate(test_values):\n        try:\n            actual = math.fsum(vals)\n        except OverflowError:\n            # self.fail(\"test %d failed: got OverflowError, expected %r \"\n            #           \"for math.fsum(%.100r)\" % (i, expected, vals))\n            assert False\n        except ValueError:\n            # self.fail(\"test %d failed: got ValueError, expected %r \"\n            #           \"for math.fsum(%.100r)\" % (i, expected, vals))\n            assert False\n\n        assert actual == expected\n\n    from random import random, gauss, shuffle\n    for j in range(10000):\n        vals = [7, 1e100, -7, -1e100, -9e-20, 8e-20] * 10\n        s = 0.\n        for i in range(200):\n            v = gauss(0, random()) ** 7 - s\n            s += v\n            vals.append(v)\n        shuffle(vals)\n        assert msum(vals) == math.fsum(vals)\n\n\n@test\ndef test_prod():\n    is_nan = lambda x: math.isnan(x)\n    prod = math.prod\n    assert prod(()) == 1\n    assert prod((), start=5) == 5\n    assert prod(list(range(2,8))) == 5040\n    assert prod(iter(list(range(2,8)))) == 5040\n    assert prod(range(1, 10), start=10) == 3628800\n\n    assert prod([1, 2, 3, 4, 5]) == 120\n    assert prod([1.0, 2.0, 3.0, 4.0, 5.0]) == 120.0\n    assert prod([1, 2, 3, 4.0, 5.0]) == 120.0\n    assert prod([1.0, 2.0, 3.0, 4, 5]) == 120.0\n\n    # Test overflow in fast-path for integers\n    assert prod([1, 1, 2**32, 1, 1]) == 2**32\n    # Test overflow in fast-path for floats\n    assert prod([1.0, 1.0, 2**32, 1, 1]) == float(2**32)\n\n    # Some odd cases\n    assert prod([2, 3], start='ab') == 'abababababab'\n    assert prod([2, 3], start=[1, 2]) == [1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2]\n    assert prod((), start={2: 3}) == {2:3}\n\n    #with self.assertRaises(TypeError):\n    #    prod([10, 20], 1)     # start is a keyword-only argument\n\n    assert prod([0, 1, 2, 3]) == 0\n    assert prod([1, 0, 2, 3]) == 0\n    assert prod([1, 2, 3, 0]) == 0\n\n    def _naive_prod(iterable, start=1):\n        for elem in iterable:\n            start *= elem\n        return start\n\n    iterable = range(1, 13)\n    assert prod(iterable) == _naive_prod(iterable)\n    iterable = range(-12, -1)\n    assert prod(iterable) == _naive_prod(iterable)\n    iterable = range(-11, 5)\n    assert prod(iterable) == 0\n\n    # Big floats\n\n    iterable = [float(x) for x in range(1, 123)]\n    assert prod(iterable) == _naive_prod(iterable, 1.0)\n    iterable = [float(x) for x in range(-123, -1)]\n    assert prod(iterable) == _naive_prod(iterable, 1.0)\n    iterable = [float(x) for x in range(-1000, 1000)]\n    assert is_nan(prod(iterable))\n\n    # Float tests\n\n    assert is_nan(prod([1, 2, 3, float(\"nan\"), 2, 3]))\n    assert is_nan(prod([1, 0, float(\"nan\"), 2, 3]))\n    assert is_nan(prod([1, float(\"nan\"), 0, 3]))\n    assert is_nan(prod([1, float(\"inf\"), float(\"nan\"),3]))\n    assert is_nan(prod([1, float(\"-inf\"), float(\"nan\"),3]))\n    assert is_nan(prod([1, float(\"nan\"), float(\"inf\"),3]))\n    assert is_nan(prod([1, float(\"nan\"), float(\"-inf\"),3]))\n\n    assert prod([1, 2, 3, float('inf'),-3,4]) == float('-inf')\n    assert prod([1, 2, 3, float('-inf'),-3,4]) == float('inf')\n\n    assert is_nan(prod([1,2,0,float('inf'), -3, 4]))\n    assert is_nan(prod([1,2,0,float('-inf'), -3, 4]))\n    assert is_nan(prod([1, 2, 3, float('inf'), -3, 0, 3]))\n    assert is_nan(prod([1, 2, 3, float('-inf'), -3, 0, 2]))\n\n    # Type preservation\n\n    assert type(prod([1, 2, 3, 4, 5, 6])) is int\n    assert type(prod([1, 2.0, 3, 4, 5, 6])) is float\n    assert type(prod(range(1, 10000))) is int\n    assert type(prod(range(1, 10000), start=1.0)) is float\n    #assert type(prod([1, decimal.Decimal(2.0), 3, 4, 5, 6])) is decimal.Decimal\n\n    # Tuples\n\n    assert prod((42,)) == 42\n    assert prod((-3.5, 4.0)) == -14.0\n    assert prod((3, 9, 3, 7, 1)) == 567\n    assert prod((1, 2.5, 3)) == 7.5\n    assert prod((2.5, 1, 3, 1.0, 1)) == 7.5\n    assert prod((2, 3), start='ab') == 'abababababab'\n\n\ntest_isnan()\ntest_isinf()\ntest_isfinite()\ntest_ceil()\ntest_floor()\ntest_fabs()\ntest_fmod()\ntest_exp()\ntest_expm1()\ntest_ldexp()\ntest_log()\ntest_log2()\ntest_log10()\ntest_degrees()\ntest_radians()\ntest_sqrt()\ntest_pow()\ntest_acos()\ntest_asin()\ntest_atan()\ntest_atan2()\ntest_cos()\ntest_sin()\ntest_hypot()\ntest_tan()\ntest_cosh()\ntest_sinh()\ntest_tanh()\ntest_acosh()\ntest_asinh()\ntest_atanh()\ntest_copysign()\ntest_log1p()\ntest_trunc()\ntest_erf()\ntest_erfc()\ntest_gamma()\ntest_lgamma()\ntest_remainder()\ntest_gcd()\ntest_lcm()\ntest_frexp()\ntest_modf()\ntest_isclose()\ntest_fsum()\ntest_prod()\n\n\n# 32-bit float ops\n\nNAN32 = math.nan32\nINF32 = math.inf32\nNINF32 = -math.inf32\n\n\ndef close32(a: float32, b: float32, epsilon: float32 = 1e-5f32):\n    return abs(a - b) <= epsilon\n\n\n@test\ndef test_float32_isnan():\n    assert math.isnan(float32(float(\"nan\"))) == True\n    assert math.isnan(4.0f32) == False\n\n\n@test\ndef test_float32_isinf():\n    assert math.isinf(float32(float(\"inf\"))) == True\n    assert math.isinf(7.0f32) == False\n\n\n@test\ndef test_float32_isfinite():\n    assert math.isfinite(1.4f32) == True\n    assert math.isfinite(0.0f32) == True\n    assert math.isfinite(NAN32) == False\n    assert math.isfinite(INF32) == False\n    assert math.isfinite(NINF32) == False\n\n\n@test\ndef test_float32_ceil():\n    assert isinstance(math.ceil(1.0f32), int)\n    assert math.ceil(3.3f32) == 4\n    assert math.ceil(0.5f32) == 1\n    assert math.ceil(1.0f32) == 1\n    assert math.ceil(1.5f32) == 2\n    assert math.ceil(-0.5f32) == 0\n    assert math.ceil(-1.0f32) == -1\n    assert math.ceil(-1.5f32) == -1\n\n\n@test\ndef test_float32_floor():\n    assert isinstance(math.floor(1.0f32), int)\n    assert math.floor(3.3f32) == 3\n    assert math.floor(0.5f32) == 0\n    assert math.floor(1.0f32) == 1\n    assert math.floor(1.5f32) == 1\n    assert math.floor(-0.5f32) == -1\n    assert math.floor(-1.0f32) == -1\n    assert math.floor(-1.5f32) == -2\n\n\n@test\ndef test_float32_fabs():\n    assert math.fabs(-1.0f32) == 1.0f32\n    assert math.fabs(0.0f32) == 0.0f32\n    assert math.fabs(1.0f32) == 1.0f32\n\n\n@test\ndef test_float32_fmod():\n    assert math.fmod(10.0f32, 1.0f32) == 0.0f32\n    assert math.fmod(10.0f32, 0.5f32) == 0.0f32\n    assert math.fmod(10.0f32, 1.5f32) == 1.0f32\n    assert math.fmod(-10.0f32, 1.0f32) == -0.0f32\n    assert math.fmod(-10.0f32, 0.5f32) == -0.0f32\n    assert math.fmod(-10.0f32, 1.5f32) == -1.0f32\n\n\n@test\ndef test_float32_exp():\n    assert math.exp(0.0f32) == 1.0f32\n    assert math.exp(-1.0f32) == 1.0f32 / math.e32\n    assert math.exp(1.0f32) == math.e32\n\n\n@test\ndef test_float32_expm1():\n    assert math.expm1(0.0f32) == 0.0f32\n    assert close32(math.expm1(1.0f32), 1.7182818284590453f32)\n    assert close32(math.expm1(3.0f32), 19.085536923187668f32)\n    assert close32(math.expm1(5.0f32), 147.4131591025766f32)\n    assert math.expm1(INF32) == INF32\n    assert math.expm1(NINF32) == -1.0f32\n    assert math.isnan(math.expm1(NAN32)) == True\n\n\n@test\ndef test_float32_ldexp():\n    assert math.ldexp(0.0f32, 1) == 0.0f32\n    assert math.ldexp(1.0f32, 1) == 2.0f32\n    assert math.ldexp(1.0f32, -1) == 0.5f32\n    assert math.ldexp(-1.0f32, 1) == -2.0f32\n    assert math.ldexp(0.0f32, 1) == 0.0f32\n    assert math.ldexp(1.0f32, -1000000) == 0.0f32\n    assert math.ldexp(-1.0f32, -1000000) == -0.0f32\n    assert math.ldexp(INF32, 30) == INF32\n    assert math.ldexp(NINF32, -213) == NINF32\n    assert math.isnan(math.ldexp(NAN32, 0)) == True\n\n\n@test\ndef test_float32_log():\n    assert math.log(1.0f32 / math.e32) == -1.0f32\n    assert math.log(1.0f32) == 0.0f32\n    assert close32(math.log(math.e32), 1.0f32)\n\n\n@test\ndef test_float32_log2():\n    assert math.log2(1.0f32) == 0.0f32\n    assert math.log2(2.0f32) == 1.0f32\n    assert math.log2(4.0f32) == 2.0f32\n    assert math.log2(2.0f32 ** 50.0f32) == 50.0f32\n    assert math.isnan(math.log2(-1.5f32)) == True\n    assert math.isnan(math.log2(NINF32)) == True\n    assert math.isnan(math.log2(NAN32)) == True\n\n\n@test\ndef test_float32_log10():\n    assert math.log10(0.1f32) == -1.0f32\n    assert math.log10(1.0f32) == 0.0f32\n    assert math.log10(10.0f32) == 1.0f32\n    assert math.log10(10000.0f32) == 4.0f32\n\n\n@test\ndef test_float32_degrees():\n    assert math.degrees(math.pi32) == 180.0f32\n    assert math.degrees(math.pi32 / 2.0f32) == 90.0f32\n    assert math.degrees(-math.pi32 / 4.0f32) == -45.0f32\n    assert math.degrees(0.0f32) == 0.0f32\n\n\n@test\ndef test_float32_radians():\n    assert math.radians(180.0f32) == math.pi32\n    assert math.radians(90.0f32) == math.pi32 / 2.0f32\n    assert math.radians(-45.0f32) == -math.pi32 / 4.0f32\n    assert math.radians(0.0f32) == 0.0f32\n\n\n@test\ndef test_float32_sqrt():\n    assert math.sqrt(4.0f32) == 2.0f32\n    assert math.sqrt(0.0f32) == 0.0f32\n    assert math.sqrt(1.0f32) == 1.0f32\n    assert math.isnan(math.sqrt(-1.0f32)) == True\n\n\n@test\ndef test_float32_pow():\n    assert math.pow(0.0f32, 1.0f32) == 0.0f32\n    assert math.pow(1.0f32, 0.0f32) == 1.0f32\n    assert math.pow(2.0f32, 1.0f32) == 2.0f32\n    assert math.pow(2.0f32, -1.0f32) == 0.5f32\n    assert math.pow(-0.0f32, 3.0f32) == -0.0f32\n    assert math.pow(-0.0f32, 2.3f32) == 0.0f32\n    assert math.pow(-0.0f32, 0.0f32) == 1.0f32\n    assert math.pow(-0.0f32, -0.0f32) == 1.0f32\n    assert math.pow(-2.0f32, 2.0f32) == 4.0f32\n    assert math.pow(-2.0f32, 3.0f32) == -8.0f32\n    assert math.pow(-2.0f32, -3.0f32) == -0.125f32\n    assert math.pow(INF32, 1.0f32) == INF32\n    assert math.pow(NINF32, 1.0f32) == NINF32\n    assert math.pow(1.0f32, INF32) == 1.0f32\n    assert math.pow(1.0f32, NINF32) == 1.0f32\n    assert math.isnan(math.pow(NAN32, 1.0f32)) == True\n    assert math.isnan(math.pow(2.0f32, NAN32)) == True\n    assert math.isnan(math.pow(0.0f32, NAN32)) == True\n    assert math.pow(1.0f32, NAN32) == 1.0f32\n\n\n@test\ndef test_float32_acos():\n    assert close32(math.acos(-1.0f32), math.pi32)\n    assert close32(math.acos(0.0f32), math.pi32 / 2.0f32)\n    assert math.acos(1.0f32) == 0.0f32\n    assert math.isnan(math.acos(NAN32)) == True\n\n\n@test\ndef test_float32_asin():\n    assert close32(math.asin(-1.0f32), -math.pi32 / 2.0f32)\n    assert math.asin(0.0f32) == 0.0f32\n    assert close32(math.asin(1.0f32), math.pi32 / 2.0f32)\n    assert math.isnan(math.asin(NAN32)) == True\n\n\n@test\ndef test_float32_atan():\n    assert math.atan(-1.0f32) == -math.pi32 / 4.0f32\n    assert math.atan(0.0f32) == 0.0f32\n    assert close32(math.atan(1.0f32), math.pi32 / 4.0f32)\n    assert close32(math.atan(INF32), math.pi32 / 2.0f32)\n    assert close32(math.atan(NINF32), -math.pi32 / 2.0f32)\n    assert math.isnan(math.atan(NAN32)) == True\n\n\n@test\ndef test_float32_atan2():\n    assert math.atan2(-1.0f32, 0.0f32) == -math.pi32 / 2.0f32\n    assert math.atan2(-1.0f32, 1.0f32) == -math.pi32 / 4.0f32\n    assert math.atan2(0.0f32, 1.0f32) == 0.0f32\n    assert math.atan2(1.0f32, 1.0f32) == math.pi32 / 4.0f32\n    assert math.atan2(1.0f32, 0.0f32) == math.pi32 / 2.0f32\n    assert math.atan2(-0.0f32, 0.0f32) == -0.0f32\n    assert math.atan2(-0.0f32, 2.3f32) == -0.0f32\n    assert close32(math.atan2(0.0f32, -2.3f32), math.pi32)\n    assert math.atan2(INF32, NINF32) == math.pi32 * 3.0f32 / 4.0f32\n    assert math.atan2(INF32, 2.3f32) == math.pi32 / 2.0f32\n    assert math.isnan(math.atan2(NAN32, 0.0f32)) == True\n\n\n@test\ndef test_float32_cos():\n    assert math.cos(0.0f32) == 1.0f32\n    assert close32(math.cos(math.pi32 / 2.0f32), 6.123233995736766e-17f32)\n    assert close32(math.cos(-math.pi32 / 2.0f32), 6.123233995736766e-17f32)\n    assert math.cos(math.pi32) == -1.0f32\n    assert math.isnan(math.cos(INF32)) == True\n    assert math.isnan(math.cos(NINF32)) == True\n    assert math.isnan(math.cos(NAN32)) == True\n\n\n@test\ndef test_float32_sin():\n    assert math.sin(0.0f32) == 0.0f32\n    assert math.sin(math.pi32 / 2.0f32) == 1.0f32\n    assert math.sin(-math.pi32 / 2.0f32) == -1.0f32\n    assert math.isnan(math.sin(INF32)) == True\n    assert math.isnan(math.sin(NINF32)) == True\n    assert math.isnan(math.sin(NAN32)) == True\n\n\n@test\ndef test_float32_hypot():\n    assert math.hypot(12.0f32, 5.0f32) == 13.0f32\n    assert math.hypot(12.0f32 / 32.0f32, 5.0f32 / 32.0f32) == 13.0f32 / 32.0f32\n    assert math.hypot(0.0f32, 0.0f32) == 0.0f32\n    assert math.hypot(-3.0f32, 4.0f32) == 5.0f32\n    assert math.hypot(3.0f32, 4.0f32) == 5.0f32\n\n\n@test\ndef test_float32_tan():\n    assert math.tan(0.0f32) == 0.0f32\n    assert close32(math.tan(math.pi32 / 4.0f32), 0.9999999999999999f32)\n    assert close32(math.tan(-math.pi32 / 4.0f32), -0.9999999999999999f32)\n    assert math.isnan(math.tan(INF32)) == True\n    assert math.isnan(math.tan(NINF32)) == True\n    assert math.isnan(math.tan(NAN32)) == True\n\n\n@test\ndef test_float32_cosh():\n    assert math.cosh(0.0f32) == 1.0f32\n    assert close32(math.cosh(2.0f32) - 2.0f32 * math.cosh(1.0f32) ** 2.0f32, -1.0f32)\n    assert math.cosh(INF32) == INF32\n    assert math.cosh(NINF32) == INF32\n    assert math.isnan(math.cosh(NAN32)) == True\n\n\n@test\ndef test_float32_sinh():\n    assert math.sinh(0.0f32) == 0.0f32\n    assert math.sinh(1.0f32) + math.sinh(-1.0f32) == 0.0f32\n    assert math.sinh(INF32) == INF32\n    assert math.sinh(NINF32) == NINF32\n    assert math.isnan(math.sinh(NAN32)) == True\n\n\n@test\ndef test_float32_tanh():\n    assert math.tanh(0.0f32) == 0.0f32\n    assert math.tanh(1.0f32) + math.tanh(-1.0f32) == 0.0f32\n    assert math.tanh(INF32) == 1.0f32\n    assert math.tanh(NINF32) == -1.0f32\n    assert math.isnan(math.tanh(NAN32)) == True\n\n\n@test\ndef test_float32_acosh():\n    assert math.acosh(1.0f32) == 0.0f32\n    assert close32(math.acosh(2.0f32), 1.3169578969248166f32)\n    assert math.acosh(INF32) == INF32\n    assert math.isnan(math.acosh(NAN32)) == True\n    assert math.isnan(math.acosh(-1.0f32)) == True\n\n\n@test\ndef test_float32_asinh():\n    assert math.asinh(0.0f32) == 0.0f32\n    assert close32(math.asinh(1.0f32), 0.881373587019543f32)\n    assert close32(math.asinh(-1.0f32), -0.881373587019543f32)\n    assert math.asinh(INF32) == INF32\n    assert math.isnan(math.asinh(NAN32)) == True\n    assert math.asinh(NINF32) == NINF32\n\n\n@test\ndef test_float32_atanh():\n    assert math.atanh(0.0f32) == 0.0f32\n    assert close32(math.atanh(0.5f32), 0.5493061443340549f32)\n    assert close32(math.atanh(-0.5f32), -0.5493061443340549f32)\n    assert math.isnan(math.atanh(INF32)) == True\n    assert math.isnan(math.atanh(NAN32)) == True\n    assert math.isnan(math.atanh(NINF32)) == True\n\n\n@test\ndef test_float32_copysign():\n    assert math.copysign(1.0f32, -0.0f32) == -1.0f32\n    assert math.copysign(1.0f32, 42.0f32) == 1.0f32\n    assert math.copysign(1.0f32, -42.0f32) == -1.0f32\n    assert math.copysign(3.0f32, 0.0f32) == 3.0f32\n    assert math.copysign(INF32, 0.0f32) == INF32\n    assert math.copysign(INF32, -0.0f32) == NINF32\n    assert math.copysign(NINF32, 0.0f32) == INF32\n    assert math.copysign(NINF32, -0.0f32) == NINF32\n    assert math.copysign(1.0f32, INF32) == 1.0f32\n    assert math.copysign(1.0f32, NINF32) == -1.0f32\n    assert math.copysign(INF32, INF32) == INF32\n    assert math.copysign(INF32, NINF32) == NINF32\n    assert math.copysign(NINF32, INF32) == INF32\n    assert math.copysign(NINF32, NINF32) == NINF32\n    assert math.isnan(math.copysign(NAN32, 1.0f32)) == True\n    assert math.isnan(math.copysign(NAN32, INF32)) == True\n    assert math.isnan(math.copysign(NAN32, NINF32)) == True\n    assert math.isnan(math.copysign(NAN32, NAN32)) == True\n\n\n@test\ndef test_float32_log1p():\n    assert close32(math.log1p(2.0f32), 1.0986122886681098f32)\n    assert close32(math.log1p(2.0f32 ** 90.0f32), 62.383246250395075f32)\n    assert math.log1p(INF32) == INF32\n    assert math.log1p(-1.0f32) == NINF32\n\n\n@test\ndef test_float32_trunc():\n    assert isinstance(math.trunc(1.0f32), int)\n    assert math.trunc(1.0f32) == 1\n    assert math.trunc(-1.0f32) == -1\n    assert math.trunc(1.5f32) == 1\n    assert math.trunc(-1.5f32) == -1\n    assert math.trunc(1.99999f32) == 1\n    assert math.trunc(-1.99999f32) == -1\n    assert math.trunc(0.99999f32) == 0\n    assert math.trunc(-100.999f32) == -100\n\n\n@test\ndef test_float32_erf():\n    assert close32(math.erf(1.0f32), 0.8427007929497148f32)\n    assert math.erf(0.0f32) == 0.0f32\n    assert close32(math.erf(3.0f32), 0.9999779095030015f32)\n    assert math.erf(256.0f32) == 1.0f32\n    assert math.erf(INF32) == 1.0f32\n    assert math.erf(NINF32) == -1.0f32\n    assert math.isnan(math.erf(NAN32)) == True\n\n\n@test\ndef test_float32_erfc():\n    assert math.erfc(0.0f32) == 1.0f32\n    assert close32(math.erfc(1.0f32), 0.15729920705028516f32)\n    assert close32(math.erfc(2.0f32), 0.0046777349810472645f32)\n    assert close32(math.erfc(-1.0f32), 1.8427007929497148f32)\n    assert math.erfc(INF32) == 0.0f32\n    assert math.erfc(NINF32) == 2.0f32\n    assert math.isnan(math.erfc(NAN32)) == True\n\n\n@test\ndef test_float32_gamma():\n    assert close32(math.gamma(6.0f32), 120.0f32)\n    assert close32(math.gamma(1.0f32), 1.0f32)\n    assert close32(math.gamma(2.0f32), 1.0f32)\n    assert close32(math.gamma(3.0f32), 2.0f32)\n    assert math.isnan(math.gamma(-1.0f32)) == True\n    assert math.gamma(INF32) == INF32\n    assert math.isnan(math.gamma(NINF32)) == True\n    assert math.isnan(math.gamma(NAN32)) == True\n\n\n@test\ndef test_float32_lgamma():\n    assert math.lgamma(1.0f32) == 0.0f32\n    assert math.lgamma(2.0f32) == 0.0f32\n    assert math.lgamma(-1.0f32) == INF32\n    assert math.lgamma(INF32) == INF32\n    assert math.lgamma(NINF32) == INF32\n    assert math.isnan(math.lgamma(NAN32)) == True\n\n\n@test\ndef test_float32_remainder():\n    assert math.remainder(2.0f32, 2.0f32) == 0.0f32\n    assert math.remainder(-4.0f32, 1.0f32) == -0.0f32\n    assert close32(math.remainder(-3.8f32, 1.0f32), 0.20000000000000018f32)\n    assert close32(math.remainder(3.8f32, 1.0f32), -0.20000000000000018f32)\n    assert math.isnan(math.remainder(INF32, 1.0f32)) == True\n    assert math.isnan(math.remainder(NINF32, 1.0f32)) == True\n    assert math.isnan(math.remainder(NAN32, 1.0f32)) == True\n\n\n@test\ndef test_float32_frexp():\n    assert math.frexp(-2.0f32) == (-0.5f32, 2)\n    assert math.frexp(-1.0f32) == (-0.5f32, 1)\n    assert math.frexp(0.0f32) == (0.0f32, 0)\n    assert math.frexp(1.0f32) == (0.5f32, 1)\n    assert math.frexp(2.0f32) == (0.5f32, 2)\n    assert math.frexp(INF32)[0] == INF32\n    assert math.frexp(NINF32)[0] == NINF32\n    assert math.isnan(math.frexp(NAN32)[0]) == True\n\n\n@test\ndef test_float32_modf():\n    assert math.modf(1.5f32) == (0.5f32, 1.0f32)\n    assert math.modf(-1.5f32) == (-0.5f32, -1.0f32)\n    assert math.modf(math.inf32) == (0.0f32, INF32)\n    assert math.modf(-math.inf32) == (-0.0f32, NINF32)\n    modf_nan = math.modf(NAN32)\n    assert math.isnan(modf_nan[0]) == True\n    assert math.isnan(modf_nan[1]) == True\n\n\n@test\ndef test_float32_isclose():\n    assert math.isclose(1.0f32 + 1.0f32, 1.000000000001f32 + 1.0f32) == True\n    assert math.isclose(2.90909324093284f32, 2.909093240932844234234234234f32) == True\n    assert math.isclose(2.90909324093284f32, 2.9f32) == False\n    assert math.isclose(2.90909324093284f32, 2.90909324f32) == True\n    assert math.isclose(2.909094f32, 2.909095f32) == False\n    assert math.isclose(NAN32, 2.9f32) == False\n    assert math.isclose(2.9f32, NAN32) == False\n    assert math.isclose(INF32, INF32) == True\n    assert math.isclose(NINF32, NINF32) == True\n    assert math.isclose(NINF32, INF32) == False\n    assert math.isclose(INF32, NINF32) == False\n\n\ntest_float32_isnan()\ntest_float32_isinf()\ntest_float32_isfinite()\ntest_float32_ceil()\ntest_float32_floor()\ntest_float32_fabs()\ntest_float32_fmod()\ntest_float32_exp()\ntest_float32_expm1()\ntest_float32_ldexp()\ntest_float32_log()\ntest_float32_log2()\ntest_float32_log10()\ntest_float32_degrees()\ntest_float32_radians()\ntest_float32_sqrt()\ntest_float32_pow()\ntest_float32_acos()\ntest_float32_asin()\ntest_float32_atan()\ntest_float32_atan2()\ntest_float32_cos()\ntest_float32_sin()\ntest_float32_hypot()\ntest_float32_tan()\ntest_float32_cosh()\ntest_float32_sinh()\ntest_float32_tanh()\ntest_float32_acosh()\ntest_float32_asinh()\ntest_float32_atanh()\ntest_float32_copysign()\ntest_float32_log1p()\ntest_float32_trunc()\ntest_float32_erf()\ntest_float32_erfc()\ntest_float32_gamma()\ntest_float32_lgamma()\ntest_float32_remainder()\ntest_float32_frexp()\ntest_float32_modf()\ntest_float32_isclose()\n"
  },
  {
    "path": "test/stdlib/operator_test.codon",
    "content": "from operator import *\nimport operator\n\n\ndef apply1(f, x, expected):\n    return f(x) == expected\n\n\ndef apply2(f, lhs, rhs, expected):\n    return f(lhs, rhs) == expected\n\n\nn_eq = 0\nn_ne = 0\nn_lt = 0\nn_le = 0\nn_gt = 0\nn_ge = 0\n\nn_len = 0\nn_bool = 0\n\nn_abs = 0\nn_pos = 0\nn_neg = 0\nn_inv = 0\nn_index = 0\n\nn_add = 0\nn_sub = 0\nn_mul = 0\nn_matmul = 0\nn_truediv = 0\nn_floordiv = 0\nn_mod = 0\nn_pow = 0\nn_and = 0\nn_or = 0\nn_xor = 0\nn_lshift = 0\nn_rshift = 0\n\nn_iadd = 0\nn_isub = 0\nn_imul = 0\nn_imatmul = 0\nn_itruediv = 0\nn_ifloordiv = 0\nn_imod = 0\nn_ipow = 0\nn_iand = 0\nn_ior = 0\nn_ixor = 0\nn_ilshift = 0\nn_irshift = 0\n\n\nclass C:\n    def __eq__(self, other: C):\n        global n_eq\n        n_eq += 1\n        return False\n\n    def __ne__(self, other: C):\n        global n_ne\n        n_ne += 1\n        return False\n\n    def __lt__(self, other: C):\n        global n_lt\n        n_lt += 1\n        return False\n\n    def __le__(self, other: C):\n        global n_le\n        n_le += 1\n        return False\n\n    def __gt__(self, other: C):\n        global n_gt\n        n_gt += 1\n        return False\n\n    def __ge__(self, other: C):\n        global n_ge\n        n_ge += 1\n        return False\n\n    def __len__(self):\n        global n_len\n        n_len += 1\n        return 42\n\n    def __bool__(self):\n        global n_bool\n        n_bool += 1\n        return True\n\n    def __abs__(self):\n        global n_abs\n        n_abs += 1\n        return \"abs\"\n\n    def __pos__(self):\n        global n_pos\n        n_pos += 1\n        return \"pos\"\n\n    def __neg__(self):\n        global n_neg\n        n_neg += 1\n        return \"neg\"\n\n    def __invert__(self):\n        global n_inv\n        n_inv += 1\n        return \"inv\"\n\n    def __index__(self):\n        global n_index\n        n_index += 1\n        return \"index\"\n\n    def __add__(self, other: C):\n        global n_add\n        n_add += 1\n        return \"add\"\n\n    def __sub__(self, other: C):\n        global n_sub\n        n_sub += 1\n        return \"sub\"\n\n    def __mul__(self, other: C):\n        global n_mul\n        n_mul += 1\n        return \"mul\"\n\n    def __matmul__(self, other: C):\n        global n_matmul\n        n_matmul += 1\n        return \"matmul\"\n\n    def __truediv__(self, other: C):\n        global n_truediv\n        n_truediv += 1\n        return \"truediv\"\n\n    def __floordiv__(self, other: C):\n        global n_floordiv\n        n_floordiv += 1\n        return \"floordiv\"\n\n    def __mod__(self, other: C):\n        global n_mod\n        n_mod += 1\n        return \"mod\"\n\n    def __pow__(self, other: C):\n        global n_pow\n        n_pow += 1\n        return \"pow\"\n\n    def __and__(self, other: C):\n        global n_and\n        n_and += 1\n        return \"and\"\n\n    def __or__(self, other: C):\n        global n_or\n        n_or += 1\n        return \"or\"\n\n    def __xor__(self, other: C):\n        global n_xor\n        n_xor += 1\n        return \"xor\"\n\n    def __lshift__(self, other: C):\n        global n_lshift\n        n_lshift += 1\n        return \"lshift\"\n\n    def __rshift__(self, other: C):\n        global n_rshift\n        n_rshift += 1\n        return \"rshift\"\n\n    def __iadd__(self, other: C):\n        global n_iadd\n        n_iadd += 1\n        return self\n\n    def __isub__(self, other: C):\n        global n_isub\n        n_isub += 1\n        return self\n\n    def __imul__(self, other: C):\n        global n_imul\n        n_imul += 1\n        return self\n\n    def __imatmul__(self, other: C):\n        global n_imatmul\n        n_imatmul += 1\n        return self\n\n    def __itruediv__(self, other: C):\n        global n_itruediv\n        n_itruediv += 1\n        return self\n\n    def __ifloordiv__(self, other: C):\n        global n_ifloordiv\n        n_ifloordiv += 1\n        return self\n\n    def __imod__(self, other: C):\n        global n_imod\n        n_imod += 1\n        return self\n\n    def __ipow__(self, other: C):\n        global n_ipow\n        n_ipow += 1\n        return self\n\n    def __iand__(self, other: C):\n        global n_iand\n        n_iand += 1\n        return self\n\n    def __ior__(self, other: C):\n        global n_ior\n        n_ior += 1\n        return self\n\n    def __ixor__(self, other: C):\n        global n_ixor\n        n_ixor += 1\n        return self\n\n    def __ilshift__(self, other: C):\n        global n_ilshift\n        n_ilshift += 1\n        return self\n\n    def __irshift__(self, other: C):\n        global n_irshift\n        n_irshift += 1\n        return \"rshift\"\n\n\n@test\ndef test_comparisons():\n    assert apply2(eq, 1, 2, False)\n    assert apply2(eq, 2, 1, False)\n    assert apply2(eq, 1, 1, True)\n    assert apply2(eq, 1, 1.1, False)\n\n    assert apply2(operator.__eq__, 1, 2, False)\n    assert apply2(operator.__eq__, 2, 1, False)\n    assert apply2(operator.__eq__, 1, 1, True)\n    assert apply2(operator.__eq__, 1, 1.1, False)\n\n    assert apply2(ne, 1, 2, True)\n    assert apply2(ne, 2, 1, True)\n    assert apply2(ne, 1, 1, False)\n    assert apply2(ne, 1, 1.1, True)\n\n    assert apply2(operator.__ne__, 1, 2, True)\n    assert apply2(operator.__ne__, 2, 1, True)\n    assert apply2(operator.__ne__, 1, 1, False)\n    assert apply2(operator.__ne__, 1, 1.1, True)\n\n    assert apply2(lt, 1, 2, True)\n    assert apply2(lt, 2, 1, False)\n    assert apply2(lt, 1, 1, False)\n    assert apply2(lt, 1, 1.1, True)\n\n    assert apply2(operator.__lt__, 1, 2, True)\n    assert apply2(operator.__lt__, 2, 1, False)\n    assert apply2(operator.__lt__, 1, 1, False)\n    assert apply2(operator.__lt__, 1, 1.1, True)\n\n    assert apply2(le, 1, 2, True)\n    assert apply2(le, 2, 1, False)\n    assert apply2(le, 1, 1, True)\n    assert apply2(le, 1, 1.1, True)\n\n    assert apply2(operator.__le__, 1, 2, True)\n    assert apply2(operator.__le__, 2, 1, False)\n    assert apply2(operator.__le__, 1, 1, True)\n    assert apply2(operator.__le__, 1, 1.1, True)\n\n    assert apply2(gt, 1, 2, False)\n    assert apply2(gt, 2, 1, True)\n    assert apply2(gt, 1, 1, False)\n    assert apply2(gt, 1, 1.1, False)\n\n    assert apply2(operator.__gt__, 1, 2, False)\n    assert apply2(operator.__gt__, 2, 1, True)\n    assert apply2(operator.__gt__, 1, 1, False)\n    assert apply2(operator.__gt__, 1, 1.1, False)\n\n    assert apply2(ge, 1, 2, False)\n    assert apply2(ge, 2, 1, True)\n    assert apply2(ge, 1, 1, True)\n    assert apply2(ge, 1, 1.1, False)\n\n    assert apply2(operator.__ge__, 1, 2, False)\n    assert apply2(operator.__ge__, 2, 1, True)\n    assert apply2(operator.__ge__, 1, 1, True)\n    assert apply2(operator.__ge__, 1, 1.1, False)\n\n    assert apply2(eq, C(), C(), False)\n    assert n_eq == 1\n    assert apply2(ne, C(), C(), False)\n    assert n_ne == 1\n    assert apply2(lt, C(), C(), False)\n    assert n_lt == 1\n    assert apply2(le, C(), C(), False)\n    assert n_le == 1\n    assert apply2(gt, C(), C(), False)\n    assert n_gt == 1\n    assert apply2(ge, C(), C(), False)\n    assert n_ge == 1\n\n    assert apply2(operator.__eq__, C(), C(), False)\n    assert n_eq == 2\n    assert apply2(operator.__ne__, C(), C(), False)\n    assert n_ne == 2\n    assert apply2(operator.__lt__, C(), C(), False)\n    assert n_lt == 2\n    assert apply2(operator.__le__, C(), C(), False)\n    assert n_le == 2\n    assert apply2(operator.__gt__, C(), C(), False)\n    assert n_gt == 2\n    assert apply2(operator.__ge__, C(), C(), False)\n    assert n_ge == 2\n\n\ntest_comparisons()\n\n\nclass A1:\n    def __bool__(self):\n        return True\n\n    def __len__(self):\n        return 0\n\n\nclass A2:\n    def __len__(self):\n        return 42\n\n\n@test\ndef test_truthiness():\n    assert truth(1)\n    assert not truth(0)\n    assert not_(False)\n    assert not not_(True)\n    assert not not_(A1())\n    assert not not_(A2())\n\n    a = A1()\n    b = A1()\n\n    assert is_(a, a)\n    assert not is_(a, b)\n    assert is_not(a, b)\n    assert not is_not(a, a)\n\n    assert truth(C())\n    assert n_bool == 1\n\n\ntest_truthiness()\n\n\n@test\ndef test_ops():\n    x = C()\n    y = C()\n\n    assert abs(x) == \"abs\"\n    assert n_abs == 1\n    assert operator.__abs__(x) == \"abs\"\n    assert n_abs == 2\n\n    assert pos(x) == \"pos\"\n    assert n_pos == 1\n    assert operator.__pos__(x) == \"pos\"\n    assert n_pos == 2\n\n    assert neg(x) == \"neg\"\n    assert n_neg == 1\n    assert operator.__neg__(x) == \"neg\"\n    assert n_neg == 2\n\n    assert inv(x) == \"inv\"\n    assert n_inv == 1\n    assert operator.__inv__(x) == \"inv\"\n    assert n_inv == 2\n    assert invert(x) == \"inv\"\n    assert n_inv == 3\n    assert operator.__invert__(x) == \"inv\"\n    assert n_inv == 4\n\n    assert index(x) == \"index\"\n    assert n_index == 1\n    assert operator.__index__(x) == \"index\"\n    assert n_index == 2\n\n    assert add(x, y) == \"add\"\n    assert n_add == 1\n    assert operator.__add__(x, y) == \"add\"\n    assert n_add == 2\n\n    assert iadd(x, y) is x\n    assert n_iadd == 1\n    assert operator.__iadd__(x, y) is x\n    assert n_iadd == 2\n\n    assert sub(x, y) == \"sub\"\n    assert n_sub == 1\n    assert operator.__sub__(x, y) == \"sub\"\n    assert n_sub == 2\n\n    assert isub(x, y) is x\n    assert n_isub == 1\n    assert operator.__isub__(x, y) is x\n    assert n_isub == 2\n\n    assert mul(x, y) == \"mul\"\n    assert n_mul == 1\n    assert operator.__mul__(x, y) == \"mul\"\n    assert n_mul == 2\n\n    assert imul(x, y) is x\n    assert n_imul == 1\n    assert operator.__imul__(x, y) is x\n    assert n_imul == 2\n\n    assert matmul(x, y) == \"matmul\"\n    assert n_matmul == 1\n    assert operator.__matmul__(x, y) == \"matmul\"\n    assert n_matmul == 2\n\n    assert imatmul(x, y) is x\n    assert n_imatmul == 1\n    assert operator.__imatmul__(x, y) is x\n    assert n_imatmul == 2\n\n    assert truediv(x, y) == \"truediv\"\n    assert n_truediv == 1\n    assert operator.__truediv__(x, y) == \"truediv\"\n    assert n_truediv == 2\n\n    assert itruediv(x, y) is x\n    assert n_itruediv == 1\n    assert operator.__itruediv__(x, y) is x\n    assert n_itruediv == 2\n\n    assert floordiv(x, y) == \"floordiv\"\n    assert n_floordiv == 1\n    assert operator.__floordiv__(x, y) == \"floordiv\"\n    assert n_floordiv == 2\n\n    assert ifloordiv(x, y) is x\n    assert n_ifloordiv == 1\n    assert operator.__ifloordiv__(x, y) is x\n    assert n_ifloordiv == 2\n\n    assert mod(x, y) == \"mod\"\n    assert n_mod == 1\n    assert operator.__mod__(x, y) == \"mod\"\n    assert n_mod == 2\n\n    assert imod(x, y) is x\n    assert n_imod == 1\n    assert operator.__imod__(x, y) is x\n    assert n_imod == 2\n\n    assert pow(x, y) == \"pow\"\n    assert n_pow == 1\n    assert operator.__pow__(x, y) == \"pow\"\n    assert n_pow == 2\n\n    assert ipow(x, y) is x\n    assert n_ipow == 1\n    assert operator.__ipow__(x, y) is x\n    assert n_ipow == 2\n\n    assert and_(x, y) == \"and\"\n    assert n_and == 1\n    assert operator.__and__(x, y) == \"and\"\n    assert n_and == 2\n\n    assert iand(x, y) is x\n    assert n_iand == 1\n    assert operator.__iand__(x, y) is x\n    assert n_iand == 2\n\n    assert or_(x, y) == \"or\"\n    assert n_or == 1\n    assert operator.__or__(x, y) == \"or\"\n    assert n_or == 2\n\n    assert ior(x, y) is x\n    assert n_ior == 1\n    assert operator.__ior__(x, y) is x\n    assert n_ior == 2\n\n    assert xor(x, y) == \"xor\"\n    assert n_xor == 1\n    assert operator.__xor__(x, y) == \"xor\"\n    assert n_xor == 2\n\n    assert ixor(x, y) is x\n    assert n_ixor == 1\n    assert operator.__ixor__(x, y) is x\n    assert n_ixor == 2\n\n    assert lshift(x, y) == \"lshift\"\n    assert n_lshift == 1\n    assert operator.__lshift__(x, y) == \"lshift\"\n    assert n_lshift == 2\n\n    assert ilshift(x, y) is x\n    assert n_ilshift == 1\n    assert operator.__ilshift__(x, y) is x\n    assert n_ilshift == 2\n\n    assert rshift(x, y) == \"rshift\"\n    assert n_rshift == 1\n    assert operator.__rshift__(x, y) == \"rshift\"\n    assert n_rshift == 2\n\n    assert irshift(x, y) is x\n    assert n_irshift == 1\n    assert operator.__irshift__(x, y) is x\n    assert n_irshift == 2\n\n\ntest_ops()\n\n\nclass B1:\n    def __length_hint__(self):\n        return 101\n\n\nclass B2:\n    def __length_hint__(self):\n        return 202\n\n    def __len__(self):\n        return 303\n\n\nclass B3:\n    pass\n\n\n@test\ndef test_sequence_ops():\n    assert concat([1, 2], [3, 4]) == [1, 2, 3, 4]\n    assert operator.__concat__([1, 2], [3, 4]) == [1, 2, 3, 4]\n    assert contains([1, 2, 3], 2)\n    assert not contains([1, 2, 3], 0)\n    assert operator.__contains__([1, 2, 3], 2)\n    assert not operator.__contains__([1, 2, 3], 0)\n    assert countOf([1, 2, 1, 1], 1) == 3\n\n    v = [1, 2, 3]\n    assert getitem(v, 1) == 2\n    delitem(v, 1)\n    assert v == [1, 3]\n    setitem(v, 1, 99)\n    assert v == [1, 99]\n\n    assert length_hint(B1()) == 101\n    assert length_hint(B2()) == 303\n    assert length_hint(B3()) == 0\n    assert length_hint(B3(), default=404) == 404\n\n    assert itemgetter(-1)([11, 22, 33]) == 33\n    assert itemgetter(-1, -2, -3)([11, 22, 33]) == (33, 22, 11)\n\n\ntest_sequence_ops()\n\n\nclass C2:\n    foo: int\n\n    def __init__(self, foo):\n        self.foo = foo\n\n    def bar(self, k, m, n):\n        return self.foo + k + m*100 + n*1000\n\n\n@test\ndef test_getter_ops():\n    v = ['a']\n    operator.attrgetter('append')(v)('hello')\n    operator.methodcaller('append', 'goodbye')(v)\n    assert v == ['a', 'hello', 'goodbye']\n\n    c = C2(10)\n    assert operator.attrgetter('foo')(c) == 10\n    assert operator.methodcaller('bar', 9, n=7, m=3)(c) == 7319\n\n\ntest_getter_ops()\n"
  },
  {
    "path": "test/stdlib/random_test.codon",
    "content": "import random as R\nimport time\nimport sys\nfrom copy import copy\n\nseed = int(time.time())\n# sys.stderr.write('seed: ' + str(seed) + '\\n')\nR.seed(seed)\n\n\n@test\ndef test_rnd_result(name, results, invariant):\n    print(name)\n    for a in results:\n        assert invariant(a)\n\n\ntest_rnd_result(\n    \"randrange\", [R.randrange(10) for _ in range(100)], range(10).__contains__\n)\ntest_rnd_result(\n    \"randrange\", [R.randrange(5, 20) for _ in range(100)], range(5, 20).__contains__\n)\ntest_rnd_result(\n    \"randrange\",\n    [R.randrange(9, 99, 3) for _ in range(100)],\n    range(9, 99, 3).__contains__,\n)\ntest_rnd_result(\n    \"randint\", [R.randint(5, 20) for _ in range(100)], range(5, 20 + 1).__contains__\n)\n\npopulation = list(\"ABCDEFGHIJKLMNOP\")\ntest_rnd_result(\n    \"choice\", [R.choice(population) for _ in range(100)], population.__contains__\n)\ntest_rnd_result(\n    \"choice\", [R.choice(population) for _ in range(100)], population.__contains__\n)\ntest_rnd_result(\"choices\", R.choices(population), population.__contains__)\ntest_rnd_result(\"choices\", R.choices(population, k=100), population.__contains__)\n\n\n@test\ndef test_shuffle(v):\n    s = copy(v)\n    R.shuffle(s)\n    assert sorted(v) == sorted(s)\n\n\ntest_shuffle(list(range(100)))\n\n\n@test\ndef test_sample(n: int, k: int):\n    s = R.sample(list(range(n)), k=k)\n    assert len(s) == k\n    assert len(set(s)) == len(s)\n    for a in s:\n        assert a in range(n)\n\n\ntest_sample(100, 5)\ntest_sample(100, 100)\ntest_sample(100, 0)\n\n\nfrom python import random as Rpy\n\n@test\ndef test_vs_python(*args, seed, method: Literal[str], T: type = float):\n    print(seed, method, args)\n    R1 = R.Random(seed)\n    R2 = Rpy.Random(seed)\n\n    N = 50\n    A1 = [T(getattr(R1, method)(*args)) for _ in range(N)]\n    A2 = [T.__from_py__(getattr(R2, method)(*args).p) for _ in range(N)]\n    assert A1 == A2\n\ntest_vs_python(-10, 10, seed=22, method='randrange', T=int)\ntest_vs_python(-10, 10, 3, seed=33, method='randrange', T=int)\ntest_vs_python(-10, 10, seed=44, method='randint', T=int)\ntest_vs_python(20, seed=55, method='getrandbits', T=int)\ntest_vs_python(32, seed=55, method='getrandbits', T=int)\ntest_vs_python(40, seed=55, method='getrandbits', T=int)\ntest_vs_python(63, seed=55, method='getrandbits', T=int)\ntest_vs_python(seed=0, method='random')\ntest_vs_python(-12.5, 101.2, seed=1, method='uniform')\ntest_vs_python(-13, 5.5, 0, seed=2, method='triangular')\n#test_vs_python(1.0, 2, seed=3, method='betavariate')  # different in older Python versions\ntest_vs_python(0.3, seed=4, method='expovariate')\n#test_vs_python(1.0, 2, seed=5, method='gammavariate')  # different in older Python versions\ntest_vs_python(1.0, 2.0, seed=-101, method='gauss')\ntest_vs_python(1.0, 2.0, seed=-102, method='lognormvariate')\ntest_vs_python(1.0, 2.0, seed=-103, method='normalvariate')\ntest_vs_python(1.0, 2.0, seed=0xffffffff, method='vonmisesvariate')\ntest_vs_python(1.0, seed=0xffffffff-1, method='paretovariate')\ntest_vs_python(1.0, 2.0, seed=0, method='weibullvariate')\n\n\n@test\ndef test_state():\n    r = R.Random(1234)\n    state = r.getstate()\n    N = 100\n    A1 = [r.random() for _ in range(N)]\n    B1 = [r.gauss() for _ in range(N)]\n    r.setstate(state)\n    A2 = [r.random() for _ in range(N)]\n    B2 = [r.gauss() for _ in range(N)]\n    r.seed(1234)\n    A3 = [r.random() for _ in range(N)]\n    B3 = [r.gauss() for _ in range(N)]\n\n    assert A1 == A2 == A3\n    assert B1 == B2 == B3\n\ntest_state()\n"
  },
  {
    "path": "test/stdlib/re_test.codon",
    "content": "import re\nimport string\nimport internal.static as static\n\n@test\ndef test_search_star_plus():\n    assert re.search('x*', 'axx').span(0) == (0, 0)\n    assert re.search('x*', 'axx').span() == (0, 0)\n    assert re.search('x+', 'axx').span(0) == (1, 3)\n    assert re.search('x+', 'axx').span() == (1, 3)\n    assert re.search('x', 'aaa') is None\n    assert re.match('a*', 'xxx').span(0) == (0, 0)\n    assert re.match('a*', 'xxx').span() == (0, 0)\n    assert re.match('x*', 'xxxa').span(0) == (0, 3)\n    assert re.match('x*', 'xxxa').span() == (0, 3)\n    assert re.match('a+', 'xxx') is None\ntest_search_star_plus()\n\n@test\ndef test_branching():\n    \"\"\"Test Branching\n    Test expressions using the OR ('|') operator.\"\"\"\n    assert re.match('(ab|ba)', 'ab').span() == (0, 2)\n    assert re.match('(ab|ba)', 'ba').span() == (0, 2)\n    assert re.match('(abc|bac|ca|cb)', 'abc').span() == (0, 3)\n    assert re.match('(abc|bac|ca|cb)', 'bac').span() == (0, 3)\n    assert re.match('(abc|bac|ca|cb)', 'ca').span() == (0, 2)\n    assert re.match('(abc|bac|ca|cb)', 'cb').span() == (0, 2)\n    assert re.match('((a)|(b)|(c))', 'a').span() == (0, 1)\n    assert re.match('((a)|(b)|(c))', 'b').span() == (0, 1)\n    assert re.match('((a)|(b)|(c))', 'c').span() == (0, 1)\ntest_branching()\n\n@test\ndef test_basic_re_sub():\n    def bump_num(matchobj):\n        int_value = int(matchobj.group(0))\n        return str(int_value + 1)\n\n    assert re.sub('y', 'a', 'xyz') == 'xaz'\n\n    assert re.sub(\"(?i)b+\", \"x\", \"bbbb BBBB\") == 'x x'\n    assert re.sub(r'\\d+', bump_num, '08.2 -2 23x99y') == '9.3 -3 24x100y'\n    assert re.sub(r'\\d+', bump_num, '08.2 -2 23x99y', 3) == '9.3 -3 23x99y'\n    assert re.sub(r'\\d+', bump_num, '08.2 -2 23x99y', count=3) == '9.3 -3 23x99y'\n\n    assert re.sub('.', lambda m: r\"\\n\", 'x') == '\\\\n'\n    assert re.sub('.', r\"\\n\", 'x') == '\\n'\n\n    s = r\"\\1\\1\"\n    assert re.sub('(.)', s, 'x') == 'xx'\n    assert re.sub('(.)', s.replace('\\\\', r'\\\\'), 'x') == s\n    assert re.sub('(.)', lambda m: s, 'x') == s\n\n    assert re.sub('(?P<a>x)', r'\\g<a>\\g<a>', 'xx') == 'xxxx'\n    assert re.sub('(?P<a>x)', r'\\g<a>\\g<1>', 'xx') == 'xxxx'\n    assert re.sub('(?P<unk>x)', r'\\g<unk>\\g<unk>', 'xx') == 'xxxx'\n    assert re.sub('(?P<unk>x)', r'\\g<1>\\g<1>', 'xx') == 'xxxx'\n    assert re.sub('()x', r'\\g<0>\\g<0>', 'xx') == 'xxxx'\n\n    assert re.sub('a', r'\\t\\n\\v\\r\\f\\a\\b', 'a') == '\\t\\n\\v\\r\\f\\a\\b'\n    assert re.sub('a', '\\t\\n\\v\\r\\f\\a\\b', 'a') == '\\t\\n\\v\\r\\f\\a\\b'\n    assert re.sub('a', '\\t\\n\\v\\r\\f\\a\\b', 'a') == (chr(9)+chr(10)+chr(11)+chr(13)+chr(12)+chr(7)+chr(8))\n    for c in 'cdehijklmopqsuwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ':\n        try:\n            re.sub('a', '\\\\' + c, 'a')\n            assert False\n        except re.error:\n            pass\n    assert re.sub(r'^\\s*', 'X', 'test') == 'Xtest'\ntest_basic_re_sub()\n\n@test\ndef test_bug_449964():\n    assert re.sub(r'(?P<unk>x)', r'\\g<1>\\g<1>\\b', 'xx') == 'xx\\bxx\\b'\ntest_bug_449964()\n\n@test\ndef test_bug_449000():\n    # Test for sub() on escaped characters\n    assert re.sub(r'\\r\\n', r'\\n', 'abc\\r\\ndef\\r\\n') == 'abc\\ndef\\n'\n    assert re.sub('\\r\\n', r'\\n', 'abc\\r\\ndef\\r\\n') =='abc\\ndef\\n'\n    assert re.sub(r'\\r\\n', '\\n', 'abc\\r\\ndef\\r\\n') == 'abc\\ndef\\n'\n    assert re.sub('\\r\\n', '\\n', 'abc\\r\\ndef\\r\\n') == 'abc\\ndef\\n'\ntest_bug_449000()\n\n@test\ndef test_sub_template_numeric_escape():\n    # bug 776311 and friends\n    assert re.sub('x', r'\\0', 'x') == '\\0'\n    assert re.sub('x', r'\\000', 'x') == '\\000'\n    assert re.sub('x', r'\\001', 'x') == '\\001'\n    assert re.sub('x', r'\\008', 'x') == '\\0' + '8'\n    assert re.sub('x', r'\\009', 'x') == '\\0' + '9'\n    assert re.sub('x', r'\\111', 'x') == '\\111'\n    assert re.sub('x', r'\\117', 'x') == '\\117'\n    assert re.sub('x', r'\\377', 'x') == '\\377'\n\n    assert re.sub('x', r'\\1111', 'x'), '\\1111'\n    assert re.sub('x', r'\\1111', 'x'), '\\111' + '1'\n\n    assert re.sub('x', r'\\00', 'x') == '\\x00'\n    assert re.sub('x', r'\\07', 'x') == '\\x07'\n    assert re.sub('x', r'\\08', 'x') == '\\0' + '8'\n    assert re.sub('x', r'\\09', 'x') == '\\0' + '9'\n    assert re.sub('x', r'\\0a', 'x') == '\\0' + 'a'\n\n    '''\n    self.checkTemplateError('x', r'\\400', 'x',\n                            r'octal escape value \\400 outside of '\n                            r'range 0-0o377', 0)\n    self.checkTemplateError('x', r'\\777', 'x',\n                            r'octal escape value \\777 outside of '\n                            r'range 0-0o377', 0)\n\n    self.checkTemplateError('x', r'\\1', 'x', 'invalid group reference 1', 1)\n    self.checkTemplateError('x', r'\\8', 'x', 'invalid group reference 8', 1)\n    self.checkTemplateError('x', r'\\9', 'x', 'invalid group reference 9', 1)\n    self.checkTemplateError('x', r'\\11', 'x', 'invalid group reference 11', 1)\n    self.checkTemplateError('x', r'\\18', 'x', 'invalid group reference 18', 1)\n    self.checkTemplateError('x', r'\\1a', 'x', 'invalid group reference 1', 1)\n    self.checkTemplateError('x', r'\\90', 'x', 'invalid group reference 90', 1)\n    self.checkTemplateError('x', r'\\99', 'x', 'invalid group reference 99', 1)\n    self.checkTemplateError('x', r'\\118', 'x', 'invalid group reference 11', 1)\n    self.checkTemplateError('x', r'\\11a', 'x', 'invalid group reference 11', 1)\n    self.checkTemplateError('x', r'\\181', 'x', 'invalid group reference 18', 1)\n    self.checkTemplateError('x', r'\\800', 'x', 'invalid group reference 80', 1)\n    self.checkTemplateError('x', r'\\8', '', 'invalid group reference 8', 1)\n    '''\n\n    assert re.sub('(((((((((((x)))))))))))', r'\\11', 'x') == 'x'\n    assert re.sub('((((((((((y))))))))))(.)', r'\\118', 'xyz') == 'xz8'\n    assert re.sub('((((((((((y))))))))))(.)', r'\\11a', 'xyz') == 'xza'\ntest_sub_template_numeric_escape()\n\n@test\ndef test_qualified_re_sub():\n    assert re.sub('a', 'b', 'aaaaa') == 'bbbbb'\n    assert re.sub('a', 'b', 'aaaaa', 1) == 'baaaa'\n    assert re.sub('a', 'b', 'aaaaa', count=1) == 'baaaa'\ntest_qualified_re_sub()\n\n@test\ndef test_bug_114660():\n    assert re.sub(r'(\\S)\\s+(\\S)', r'\\1 \\2', 'hello  there') == 'hello there'\ntest_bug_114660()\n\n@test\ndef test_symbolic_refs():\n    assert re.sub('(?P<a>x)|(?P<b>y)', r'\\g<b>', 'xx') == ''\n    assert re.sub('(?P<a>x)|(?P<b>y)', r'\\2', 'xx') == ''\n    # Support > 100 groups.\n    pat = '|'.join(f'x(?P<a{i}>{hex(i)[2:]})y' for i in range(1, 200 + 1))\n    assert re.sub(pat, r'\\g<200>', 'xc8yzxc8y') == 'c8zc8'\ntest_symbolic_refs()\n\n@test\ndef test_re_subn():\n    assert re.subn(\"(?i)b+\", \"x\", \"bbbb BBBB\") == ('x x', 2)\n    assert re.subn(\"b+\", \"x\", \"bbbb BBBB\") == ('x BBBB', 1)\n    assert re.subn(\"b+\", \"x\", \"xyz\") == ('xyz', 0)\n    assert re.subn(\"b*\", \"x\", \"xyz\") == ('xxxyxzx', 4)\n    assert re.subn(\"b*\", \"x\", \"xyz\", 2) == ('xxxyz', 2)\n    assert re.subn(\"b*\", \"x\", \"xyz\", count=2) == ('xxxyz', 2)\ntest_re_subn()\n\n# TODO: Current version does not allow None == None,\n#       so use this as a workaround.\ndef cmp_opt(x, y):\n    if x is None:\n        return y is None\n    elif y is None:\n        return x is None\n    else:\n        return unwrap(x) == unwrap(y)\n\ndef cmp_list(a, b):\n    if len(a) != len(b):\n        return False\n\n    for i in range(len(a)):\n        if not cmp_opt(a[i], b[i]):\n            return False\n\n    return True\n\ndef cmp_tuple(a, b):\n    if static.len(a) != static.len(b):\n        return False\n    elif static.len(a) == 0:\n        return True\n    else:\n        if not cmp_opt(a[0], b[0]):\n            return False\n        return cmp_tuple(a[1:], b[1:])\n\n@test\ndef test_re_split():\n    assert cmp_list(re.split(\":\", \":a:b::c\"), ['', 'a', 'b', '', 'c'])\n    assert cmp_list(re.split(\":+\", \":a:b::c\"), ['', 'a', 'b', 'c'])\n    assert cmp_list(re.split(\"(:+)\", \":a:b::c\"), ['', ':', 'a', ':', 'b', '::', 'c'])\n\n    assert cmp_list(re.split(\"(?::+)\", \":a:b::c\"), ['', 'a', 'b', 'c'])\n    assert cmp_list(re.split(\"(:)+\", \":a:b::c\"), ['', ':', 'a', ':', 'b', ':', 'c'])\n    assert cmp_list(re.split(\"([b:]+)\", \":a:b::c\"), ['', ':', 'a', ':b::', 'c'])\n    assert cmp_list(re.split(\"(b)|(:+)\", \":a:b::c\"), [None, '', None, ':', 'a', None, ':', '', 'b', None, '', None, '::', 'c'][1:])\n    assert cmp_list(re.split(\"(?:b)|(?::+)\", \":a:b::c\"), ['', 'a', '', '', 'c'])\n\n    for sep, expected in [\n        (':*', [None, '', '', 'a', '', 'b', '', 'c', ''][1:]),\n        ('(?::*)', [None, '', '', 'a', '', 'b', '', 'c', ''][1:]),\n        ('(:*)', [None, '', ':', '', '', 'a', ':', '', '', 'b', '::', '', '', 'c', '', ''][1:]),\n        ('(:)*', [None, '', ':', '', None, 'a', ':', '', None, 'b', ':', '', None, 'c', None, ''][1:]),\n    ]:\n        assert cmp_list(re.split(sep, ':a:b::c'), expected)\n\n    for sep, expected in [\n        ('', ['', ':', 'a', ':', 'b', ':', ':', 'c', '']),\n        # (r'\\b', [':', 'a', ':', 'b', '::', 'c', '']),  # TODO: this fails; re2 difference maybe?\n    ]:\n        assert cmp_list(re.split(sep, ':a:b::c'), expected)\ntest_re_split()\n\n@test\ndef test_qualified_re_split():\n    assert cmp_list(re.split(\":\", \":a:b::c\", 2), ['', 'a', 'b::c'])\n    assert cmp_list(re.split(\":\", \":a:b::c\", maxsplit=2), ['', 'a', 'b::c'])\n    assert cmp_list(re.split(':', 'a:b:c:d', maxsplit=2), ['a', 'b', 'c:d'])\n    assert cmp_list(re.split(\"(:)\", \":a:b::c\", maxsplit=2), ['', ':', 'a', ':', 'b::c'])\n    assert cmp_list(re.split(\"(:+)\", \":a:b::c\", maxsplit=2), ['', ':', 'a', ':', 'b::c'])\n    assert cmp_list(re.split(\"(:*)\", \":a:b::c\", maxsplit=2), ['', ':', '', '', 'a:b::c'])\ntest_qualified_re_split()\n\n@test\ndef test_re_findall():\n    assert re.findall(\":+\", \"abc\") == []\n    assert re.findall(\":+\", \"a:b::c:::d\") == [\":\", \"::\", \":::\"]\n    assert re.findall(\"(:+)\", \"a:b::c:::d\") == [\":\", \"::\", \":::\"]\n    # (!) Note: this is different in Codon, as we always return the full match even\n    #           if there are capturing groups.\n    assert re.findall(\"(:)(:*)\", \"a:b::c:::d\") == [\":\", \"::\", \":::\"]\n\n    x = \"\\xe0\"\n    xx = x * 2\n    xxx = x * 3\n    string = f\"a{x}b{xx}c{xxx}d\"\n    assert re.findall(f\"{x}+\", string) == [x, xx, xxx]\n    assert re.findall(f\"({x}+)\", string) == [x, xx, xxx]\n    # (!) Note: same as above.\n    assert re.findall(f\"({x})({x}*)\", string) == [x, xx, xxx]\ntest_re_findall()\n\n@test\ndef test_re_match():\n    assert cmp_list(re.match('a', 'a').groups(), List[Optional[str]]())\n    assert cmp_list(re.match('(a)', 'a').groups(), [None, 'a',][1:])\n    assert re.match('(a)', 'a').group(0).__val__() == 'a'\n    assert re.match('(a)', 'a').group(1).__val__() == 'a'\n    assert re.match('(a)', 'a').group(1, 1) == (Optional('a'), Optional('a'))\n    assert cmp_list(re.match('\\xe0', '\\xe0').groups(), List[Optional[str]]())\n    assert cmp_list(re.match('(\\xe0)', '\\xe0').groups(), ['\\xe0'])\n    assert unwrap(re.match('(\\xe0)', '\\xe0').group(0)) == '\\xe0'\n    assert unwrap(re.match('(\\xe0)', '\\xe0').group(1)) == '\\xe0'\n    assert re.match('(\\xe0)', '\\xe0').group(1, 1) == (Optional('\\xe0'), Optional('\\xe0'))\n\n    pat = re.compile('((a)|(b))(c)?')\n    assert cmp_list(pat.match('a').groups(), [None, 'a', 'a', None, None][1:])\n    assert cmp_list(pat.match('b').groups(), [None, 'b', None, 'b', None][1:])\n    assert cmp_list(pat.match('ac').groups(), [None, 'a', 'a', None, 'c'][1:])\n    assert cmp_list(pat.match('bc').groups(), [None, 'b', None, 'b', 'c'][1:])\n    assert cmp_list(pat.match('bc').groups(\"\"), [None, 'b', \"\", 'b', 'c'][1:])\n\n    pat = re.compile('(?:(?P<a1>a)|(?P<b2>b))(?P<c3>c)?')\n    assert cmp_tuple(pat.match('a').group(1, 2, 3), (Optional('a'), Optional[str](), Optional[str]()))\n    assert cmp_tuple(pat.match('b').group('a1', 'b2', 'c3'), (Optional[str](), Optional('b'), Optional[str]()))\n    assert cmp_tuple(pat.match('ac').group(1, 'b2', 3), (Optional('a'), Optional[str](), Optional('c')))\ntest_re_match()\n\ndef raises(exception: type, function, *args, **kwargs):\n    try:\n        function(*args, **kwargs)\n    except exception:\n        return True\n    except:\n        pass\n    return False\n\n@test\ndef test_group():\n    class Index:\n        value: int\n        def __init__(self, value):\n            self.value = value\n        def __index__(self):\n            return self.value\n    # A single group\n    m = re.match('(a)(b)', 'ab')\n    assert m.group() == 'ab'\n    assert unwrap(m.group(0)) == 'ab'\n    assert unwrap(m.group(1)) == 'a'\n    assert unwrap(m.group(Index(1))) == 'a'\n    assert raises(IndexError, m.group, -1)\n    assert raises(IndexError, m.group, 3)\n    assert raises(IndexError, m.group, 1<<1000)\n    assert raises(IndexError, m.group, Index(1<<1000))\n    assert raises(IndexError, m.group, 'x')\n    # Multiple groups\n    assert cmp_tuple(m.group(2, 1), ('b', 'a'))\n    assert cmp_tuple(m.group(Index(2), Index(1)), ('b', 'a'))\ntest_group()\n\n@test\ndef test_match_getitem():\n    pat = re.compile('(?:(?P<a1>a)|(?P<b2>b))(?P<c3>c)?')\n\n    m = pat.match('a')\n    assert unwrap(m['a1']) == 'a'\n    assert m['b2'] is None\n    assert m['c3'] is None\n    assert unwrap(m[0]) == 'a'\n    assert unwrap(m[1]) == 'a'\n    assert m[2] is None\n    assert m[3] is None\n\n    assert raises(IndexError, lambda i: m[i], 'X')\n    assert raises(IndexError, lambda i: m[i], -1)\n    assert raises(IndexError, lambda i: m[i], 4)\n\n    m = pat.match('ac')\n    assert unwrap(m['a1']) == 'a'\n    assert m['b2'] is None\n    assert unwrap(m['c3']) == 'c'\n    assert unwrap(m[0]) == 'ac'\n    assert unwrap(m[1]) == 'a'\n    assert m[2] is None\n    assert unwrap(m[3]) == 'c'\ntest_match_getitem()\n\n@test\ndef test_re_fullmatch():\n    assert re.fullmatch(r\"a\", \"a\").span() == (0, 1)\n    assert re.fullmatch(r\"a|ab\", \"ab\").span() == (0, 2)\n    assert re.fullmatch(\"\\xe0|\\xe0\\xdf\", \"\\xe0\\xdf\").span() == (0, 2)\n\n    assert re.fullmatch(r\".*?$\", \"abc\").span() == (0, 3)\n    assert re.fullmatch(r\".*?\", \"abc\").span() == (0, 3)\n    assert re.fullmatch(r\"a.*?b\", \"ab\").span() == (0, 2)\n    assert re.fullmatch(r\"a.*?b\", \"abb\").span() == (0, 3)\n    assert re.fullmatch(r\"a.*?b\", \"axxb\").span() == (0, 4)\n    assert re.fullmatch(r\"a+\", \"ab\") is None\n    assert re.fullmatch(r\"abc$\", \"abc\\n\") is None\n    assert re.fullmatch(r\"(?m)abc$\", \"abc\\n\") is None\n\n    assert re.compile(r\"bc\").fullmatch(\"abcd\", pos=1, endpos=3).span() == (1, 3)\n    # TODO: following fails; $ does not respect endpos in re2?\n    #assert re.compile(r\".*?$\").fullmatch(\"abcd\", pos=1, endpos=3).span() == (1, 3)\n    assert re.compile(r\".*?\").fullmatch(\"abcd\", pos=1, endpos=3).span() == (1, 3)\ntest_re_fullmatch()\n\n@test\ndef test_finditer():\n    iter = re.finditer(r\":+\", \"a:b::c:::d\")\n    assert cmp_list([item.group(0) for item in iter], [\":\", \"::\", \":::\"])\n\n    pat = re.compile(r\":+\")\n    iter = pat.finditer(\"a:b::c:::d\", 1, 10)\n    assert cmp_list([item.group(0) for item in iter], [\":\", \"::\", \":::\"])\n\n    pat = re.compile(r\":+\")\n    iter = pat.finditer(\"a:b::c:::d\", pos=1, endpos=10)\n    assert cmp_list([item.group(0) for item in iter], [\":\", \"::\", \":::\"])\n\n    pat = re.compile(r\":+\")\n    iter = pat.finditer(\"a:b::c:::d\", endpos=10, pos=1)\n    assert cmp_list([item.group(0) for item in iter], [\":\", \"::\", \":::\"])\n\n    pat = re.compile(r\":+\")\n    iter = pat.finditer(\"a:b::c:::d\", pos=3, endpos=8)\n    assert cmp_list([item.group(0) for item in iter], [\"::\", \"::\"])\ntest_finditer()\n\n@test\ndef test_constants():\n    assert re.I == re.IGNORECASE\n    assert re.L == re.LOCALE\n    assert re.M == re.MULTILINE\n    assert re.S == re.DOTALL\n    assert re.X == re.VERBOSE\ntest_constants()\n\n@test\ndef test_anyall():\n    assert unwrap(re.match(\"a.b\", \"a\\nb\", re.DOTALL).group(0)) == \"a\\nb\"\n    assert unwrap(re.match(\"a.*b\", \"a\\n\\nb\", re.DOTALL).group(0)) == \"a\\n\\nb\"\ntest_anyall()\n\n@test\ndef test_groupdict():\n    d = re.match('(?P<first>first) (?P<second>second)', 'first second').groupdict()\n    assert len(d) == 2 and unwrap(d['first']) == 'first' and unwrap(d['second']) == 'second'\ntest_groupdict()\n\n@test\ndef test_expand():\n    assert re.match(\"(?P<first>first) (?P<second>second)\", \"first second\").expand(r\"\\2 \\1 \\g<second> \\g<first>\") == \"second first second first\"\n    assert re.match(\"(?P<first>first)|(?P<second>second)\", \"first\").expand(r\"\\2 \\g<second>\") == \" \"\ntest_expand()\n\n@test\ndef test_getattr():\n    assert re.compile(\"(?i)(a)(b)\").pattern == \"(?i)(a)(b)\"\n    # TODO: Codon does not support flags like this\n    # self.assertEqual(re.compile(\"(?i)(a)(b)\").flags, re.I | re.U)\n    assert re.compile(\"(?i)(a)(b)\").groups == 2\n    assert re.compile(\"(?i)(a)(b)\").groupindex == {}\n    assert re.compile(\"(?i)(?P<first>a)(?P<other>b)\").groupindex == {'first': 1, 'other': 2}\n\n    assert re.match(\"(a)\", \"a\").pos == 0\n    assert re.match(\"(a)\", \"a\").endpos == 1\n    assert re.match(\"(a)\", \"a\").string == \"a\"\n    assert re.match(\"(a)\", \"a\").re\ntest_getattr()\n\ndef check_match(pattern, text, match: Optional[str] = None, span: Optional[re.Span] = None, matcher = re.fullmatch):\n    if match is None and span is None:\n        # the pattern matches the whole text\n        match = text\n        span = re.Span(0, len(text))\n    elif match is None or span is None:\n        raise ValueError('If match is not None, span should be specified '\n                         '(and vice versa).')\n    m = matcher(pattern, text)\n    return bool(m) and m.group() == match and m.span() == (*span,)\n\nLITERAL_CHARS = string.ascii_letters + string.digits\n\n@test\ndef test_re_escape():\n    p = ''.join(chr(i) for i in range(256))\n    for c in p:\n        assert check_match(re.escape(c), c)\n        assert check_match('[' + re.escape(c) + ']', c)\n    assert check_match(re.escape(p), p)\n    for c in '-.]{}':\n        assert re.escape(c)[:1] == '\\\\'\n    literal_chars = LITERAL_CHARS\n    assert re.escape(literal_chars) == literal_chars\ntest_re_escape()\n"
  },
  {
    "path": "test/stdlib/sort_test.codon",
    "content": "from algorithms.qsort import qsort_inplace\nfrom algorithms.heapsort import heap_sort_inplace\nfrom algorithms.pdqsort import pdq_sort_inplace\nfrom algorithms.timsort import tim_sort_inplace\nfrom time import time\n\n\ndef key(n: int):\n    return -n\n\n\ndef gen_list(n: int):\n    import random\n\n    v = List[int](n)\n    for _ in range(n):\n        v.append(random.randint(0, 10000))\n    return v\n\n\ndef copy_to(a, b):\n    b.clear()\n    for i in a:\n        b.append(i)\n\n\n@test\ndef ensure_sorted(v):\n    for i in range(len(v) - 1):\n        assert key(v[i]) <= key(v[i + 1])\n\n\nv0 = gen_list(100)\nv1 = List[int](len(v0))\n\n\ndef test_sort1(name, sort):\n    copy_to(v0, v1)\n    t0 = time()\n    sort(v1, key)\n    t1 = time()\n    print name, t1 - t0\n    ensure_sorted(v1)\n\n\ntest_sort1(\"qsort   :\", qsort_inplace)\ntest_sort1(\"heapsort:\", heap_sort_inplace)\ntest_sort1(\"pdqsort :\", pdq_sort_inplace)\ntest_sort1(\"timsort :\", tim_sort_inplace)\n\n\n@test\ndef test_sort2(name, sort):\n    from random import shuffle\n\n    fail = False\n    print name\n    for N in (0, 1, 10, 100, 1000, 10000):  # , 100000): # too slow; maybe add later?\n        print N\n        for i in range(1000):\n            v = list(range(N))\n            shuffle(v)\n            sort(v, key)\n            if v != list(reversed(range(N))):\n                fail = True\n    assert not fail\n\n\ntest_sort2(\"qsort   :\", qsort_inplace)\ntest_sort2(\"heapsort:\", heap_sort_inplace)\ntest_sort2(\"pdqsort :\", pdq_sort_inplace)\ntest_sort2(\"timsort :\", tim_sort_inplace)\n\n# test standard sort routines\n@test\ndef test_standard_sort():\n    copy_to(v0, v1)\n    v2 = sorted(v1)\n    for i in range(len(v2) - 1):\n        assert v2[i] <= v2[i + 1]\n\n    v2 = sorted(v1, key=key)\n    for i in range(len(v2) - 1):\n        assert key(v2[i]) <= key(v2[i + 1])\n\n    v2.sort()\n    for i in range(len(v2) - 1):\n        assert v2[i] <= v2[i + 1]\n\n    v2.sort(key=key)\n    for i in range(len(v2) - 1):\n        assert key(v2[i]) <= key(v2[i + 1])\n\n\ntest_standard_sort()\n"
  },
  {
    "path": "test/stdlib/statistics_test.codon",
    "content": "import statistics\nimport math\n\n\n@test\ndef med():\n    # Test median with even nuber of int data points.\n    data = [1, 2, 3, 4, 5, 6]\n    assert statistics.median(data) == 3.5\n\n    # Test median with an odd number of int data points.\n    data = [1, 2, 3, 4, 5, 6, 9]\n    assert statistics.median(data) == 4\n\n    # Test median works with an odd number of Fractions.\n    fdata = [1 / 7, 2 / 7, 3 / 7, 4 / 7, 5 / 7]\n    assert statistics.median(fdata) == 3 / 7\n\n    # Test median works with an even number of Fractions.\n    fdata = [1 / 7, 2 / 7, 3 / 7, 4 / 7, 5 / 7, 6 / 7]\n    assert statistics.median(fdata) == 1 / 2\n\n    # Test median works with an odd number of Decimals.\n    ddata = [2.5, 3.1, 4.2, 5.7, 5.8]\n    assert statistics.median(ddata) == 4.2\n\n\nmed()\n\n\n@test\ndef med_low():\n    # Test median_low with an even number of ints.\n    data = [1, 2, 3, 4, 5, 6]\n    assert statistics.median_low(data) == 3\n\n    # Test median_low works with an even number of Fractions.\n    fdata = [1 / 7, 2 / 7, 3 / 7, 4 / 7, 5 / 7, 6 / 7]\n    assert statistics.median_low(fdata) == 3 / 7\n\n    # Test median_low works with an even number of Decimals.\n    ddata = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6]\n    assert statistics.median_low(ddata) == 3.3\n\n\nmed_low()\n\n\n@test\ndef med_high():\n    # Test median_high with an even number of ints.\n    data = [1, 2, 3, 4, 5, 6]\n    assert statistics.median_high(data) == 4\n\n    # Test median_high works with an even number of Fractions.\n    fdata = [1 / 7, 2 / 7, 3 / 7, 4 / 7, 5 / 7, 6 / 7]\n    assert statistics.median_high(fdata) == 4 / 7\n\n    # Test median_high works with an even number of Decimals.\n    ddata = [1.1, 2.2, 3.3, 4.4, 5.5, 6.6]\n    assert statistics.median_high(ddata) == 4.4\n\n\nmed_high()\n\n\n@test\ndef med_grouped():\n    # Test median_grouped with repeated median values.\n    data = [12, 13, 14, 14, 14, 15, 15]\n    assert statistics.median_grouped(data) == 14\n\n    data = [12, 13, 14, 14, 14, 14, 15]\n    assert statistics.median_grouped(data) == 13.875\n\n    data = [5, 10, 10, 15, 20, 20, 20, 20, 25, 25, 30]\n    assert statistics.median_grouped(data, 5) == 19.375\n\n    # Test median_grouped with repeated median values.\n    data = [2, 3, 3, 4, 4, 4, 5, 5, 5, 5, 6, 6]\n    assert statistics.median_grouped(data) == 4.5\n\n    data = [3, 4, 4, 4, 5, 5, 5, 5, 6, 6]\n    assert statistics.median_grouped(data) == 4.75\n\n    # Test median_grouped with repeated single values.\n    ddata = [3.2]\n    assert statistics.median_grouped(ddata) == 3.2\n\n    # Test median_grouped works with an odd number of Fractions.\n    fdata = [5 / 4, 9 / 4, 13 / 4, 13 / 4, 17 / 4]\n    assert statistics.median_grouped(fdata) == 3.0\n\n    # Test median_grouped works with an even number of Fractions.\n    fdata = [5 / 4, 9 / 4, 13 / 4, 13 / 4, 17 / 4, 17 / 4]\n    assert statistics.median_grouped(fdata) == 3.25\n\n    # Test median_grouped works with an odd number of Decimals.\n    ddata = [5.5, 6.5, 6.5, 7.5, 8.5]\n    assert statistics.median_grouped(ddata) == 6.75\n\n    # Test median_grouped works with an even number of Decimals.\n    ddata = [5.5, 5.5, 6.5, 6.5, 7.5, 8.5]\n    assert statistics.median_grouped(ddata) == 6.5\n\n\nmed_grouped()\n\n\n@test\ndef test_mode():\n    data = [12, 13, 14, 14, 14, 15, 15]\n    assert statistics.mode(data) == 14\n\n    data = list(range(20, 50, 3))\n    assert statistics.mode(data) == 20\n\n    # Test mode with nominal data.\n    ndata = [\"a\", \"b\", \"c\", \"b\", \"d\", \"b\"]\n    assert statistics.mode(ndata) == \"b\"\n\n    ndata = [\"fe\", \"fi\", \"fo\", \"fum\", \"fi\", \"fi\"]\n    assert statistics.mode(ndata) == \"fi\"\n\n    # Test mode with bimodal data.\n    data = [1, 1, 2, 2, 2, 2, 3, 4, 5, 6, 6, 6, 6, 7, 8, 9, 9]\n    assert statistics.mode(data) == 2\n\n    # Test mode when data points are all unique.\n    data = list(range(10))\n    assert statistics.mode(data) == 0\n\n\ntest_mode()\n\n\n@test\ndef test_multimode():\n    data = [1, 1, 2, 2, 2, 2, 3, 4, 5, 6, 6, 6, 6, 7, 8, 9, 9]\n    assert statistics.multimode(data) == [2, 6]\n\n    ndata = [\"a\", \"a\", \"b\", \"b\", \"b\", \"b\", \"b\", \"b\", \"b\", \"b\", \"c\", \"c\"]\n    assert statistics.multimode(ndata) == [\"b\"]\n\n    ndata = [\n        \"a\",\n        \"a\",\n        \"b\",\n        \"b\",\n        \"b\",\n        \"b\",\n        \"c\",\n        \"c\",\n        \"d\",\n        \"d\",\n        \"d\",\n        \"d\",\n        \"e\",\n        \"e\",\n        \"f\",\n        \"f\",\n        \"f\",\n        \"f\",\n        \"g\",\n        \"g\",\n    ]\n    assert statistics.multimode(ndata) == [\"b\", \"d\", \"f\"]\n\n\ntest_multimode()\n\n\n@test\ndef test_quantiles():\n    for n in range(2, 10):\n        data = [10.0] * n\n        assert statistics.quantiles(data) == [10.0, 10.0, 10.0]\n        assert statistics.quantiles(data, method=\"inclusive\") == [10.0, 10.0, 10.0]\n\n    data = [100, 200, 400, 800]\n    for n, expected in [\n        (2, [300.0]),\n        (3, [200.0, 400.0]),\n        (4, [175.0, 300.0, 500.0]),\n        (5, [160.0, 240.0, 360.0, 560.0]),\n        (6, [150.0, 200.0, 300.0, 400.0, 600.0]),\n        (8, [137.5, 175.0, 225.0, 300.0, 375.0, 500.0, 650.0]),\n        (10, [130.0, 160.0, 190.0, 240.0, 300.0, 360.0, 440.0, 560.0, 680.0]),\n        (12, [125.0, 150.0, 175.0, 200.0, 250.0, 300.0, 350.0, 400.0, 500.0, 600.0, 700.0]),\n        (15, [120.0, 140.0, 160.0, 180.0, 200.0, 240.0, 280.0, 320.0, 360.0, 400.0, 480.0, 560.0, 640.0, 720.0])\n    ]:\n        assert statistics.quantiles(data, n=n, method=\"inclusive\") == expected\n\n\ntest_quantiles()\n\n\n@test\ndef test_mean():\n    data = [100.0, 200.0, 400.0, 800.0]\n    assert statistics.mean(data) == 375.0\n\n    data = [17.25, 19.75, 20.0, 21.5, 21.75, 23.25, 25.125, 27.5]\n    assert statistics.mean(data) == 22.015625\n\n    data = [\n        0.0,\n        1.0,\n        2.0,\n        3.0,\n        3.0,\n        3.0,\n        4.0,\n        5.0,\n        5.0,\n        6.0,\n        7.0,\n        7.0,\n        7.0,\n        7.0,\n        8.0,\n        9.0,\n    ]\n    assert statistics.mean(data) == 4.8125\n\n\ntest_mean()\n\n\n@test\ndef test_geometric_mean():\n    PRECISION = 1e-6\n\n    data = [54.0, 24.0, 36.0]\n    assert math.fabs(statistics.geometric_mean(data) - 36) < PRECISION\n\n    data = [4.0, 9.0]\n    assert math.fabs(statistics.geometric_mean(data) - 6) < PRECISION\n\n    data = [17.625]\n    assert math.fabs(statistics.geometric_mean(data) - 17.625) < PRECISION\n\n    data = [3.5, 4.0, 5.25]\n    assert math.fabs(statistics.geometric_mean(data) - 4.18886) < PRECISION\n\n\ntest_geometric_mean()\n\n\n@test\ndef test_harmonic_mean():\n    data = [1.0, 0.0, 2.0]\n    assert statistics.harmonic_mean(data) == 0\n\n    data = [2.0, 4.0, 4.0, 8.0, 16.0, 16.0]\n    assert statistics.harmonic_mean(data) == 6 * 4 / 5\n\n    data = [1 / 8, 1 / 4, 1 / 4, 1 / 2, 1 / 2]\n    assert statistics.harmonic_mean(data) == 1 / 4\n\n    for x in range(1, 101):\n        assert statistics.harmonic_mean([float(x)]) == float(x)\n\n\ntest_harmonic_mean()\n\n\n@test\ndef test_pvariance():\n    data = [float(i) for i in range(10000)]\n    assert statistics.pvariance(data) == (10000 ** 2 - 1) / 12\n\n    data = [4.0, 7.0, 13.0, 16.0]\n    assert statistics.pvariance(data) == 22.5\n\n    data = [1 / 4, 1 / 4, 3 / 4, 7 / 4]\n    assert statistics.pvariance(data) == 3 / 8\n\n\ntest_pvariance()\n\n\n@test\ndef test_pstdev():\n    data = [float(i) for i in range(10000)]\n    assert statistics.pstdev(data) == math.sqrt(statistics.pvariance(data))\n\n    data = [4.0, 7.0, 13.0, 16.0]\n    assert statistics.pstdev(data) == math.sqrt(statistics.pvariance(data))\n\n    data = [1 / 4, 1 / 4, 3 / 4, 7 / 4]\n    assert statistics.pstdev(data) == math.sqrt(statistics.pvariance(data))\n\n\ntest_pstdev()\n\n\n@test\ndef test_variance():\n    data = [4.0, 7.0, 13.0, 16.0]\n    assert statistics.variance(data) == 30.0\n\n    data = [1 / 4, 1 / 4, 3 / 4, 7 / 4]\n    assert statistics.variance(data) == 1 / 2\n\n\ntest_variance()\n\n\n@test\ndef test_stdev():\n    data = [4.0, 7.0, 13.0, 16.0]\n    assert statistics.stdev(data) == math.sqrt(statistics.variance(data))\n\n    data = [1 / 4, 1 / 4, 3 / 4, 7 / 4]\n    assert statistics.stdev(data) == math.sqrt(statistics.variance(data))\n\n\ntest_stdev()\n\n\n@test\ndef test_mean_NormalDist():\n    X = statistics.NormalDist(10000.0, 3.0)\n    assert X.mean == 10000.0\n\n\ntest_mean_NormalDist()\n\n\n@test\ndef test_stdev():\n    X = statistics.NormalDist(10000.0, 3.0)\n    assert X.stdev == 3.0\n\n\ntest_stdev()\n\n\n@test\ndef test_variance():\n    X = statistics.NormalDist(10000.0, 3.0)\n    assert X.variance == 9.0\n\n\ntest_variance()\n\n\n@test\ndef test_pdf():\n    PRECISION = 1e-6\n    X = statistics.NormalDist(100.0, 15.0)\n\n    # verify peak around center\n    assert X.pdf(99.0) < X.pdf(100.0)\n    assert X.pdf(101.0) < X.pdf(100.0)\n\n    for i in range(50):\n        assert (\n            math.fabs((X.pdf(100.0 - float(i)) - X.pdf(100.0 + float(i)))) < PRECISION\n        )\n\n\ntest_pdf()\n\n\n@test\ndef test_cdf():\n    X = statistics.NormalDist(100.0, 15.0)\n    # Verify center (should be exact)\n    assert X.cdf(100.0) == 0.50\n\n\ntest_cdf()\n\n\n@test\ndef test_inv_cdf():\n    PRECISION = 1e-6\n    iq = statistics.NormalDist(100.0, 15.0)\n    assert iq.inv_cdf(0.50) == iq.mean\n\n    # One hundred ever smaller probabilities to test tails out to\n    # extreme probabilities: 1 / 2**50 and (2**50-1) / 2 ** 50\n    for e in range(1, 51):\n        p = 2.0 ** (-e)\n        assert math.fabs(iq.cdf(iq.inv_cdf(p)) - p) < PRECISION\n        p = 1.0 - p\n        assert math.fabs(iq.cdf(iq.inv_cdf(p)) - p) < PRECISION\n\n\ntest_inv_cdf()\n\n\n@test\ndef test_ND_quartiles():\n    PRECISION = 1e-6\n    Z = statistics.NormalDist(0.0, 1.0)\n    for n, expected in [\n        (2, [0.0]),\n        (3, [-0.430727, 0.430727]),\n        (4, [-0.67449, 0.0, 0.67449]),\n    ]:\n        actual = Z.quantiles(n)\n        for i in range(len(expected)):\n            assert math.fabs(actual[i] - expected[i]) < PRECISION\n\n\ntest_ND_quartiles()\n\n\n@test\ndef test_overlap():\n    PRECISION = 1e-5\n    for X1, X2, published_result in [\n        (statistics.NormalDist(0.0, 2.0), statistics.NormalDist(1.0, 2.0), 0.80258),\n        (statistics.NormalDist(0.0, 1.0), statistics.NormalDist(1.0, 2.0), 0.60993),\n    ]:\n        assert math.fabs(X1.overlap(X2) - published_result) < PRECISION\n        assert math.fabs(X2.overlap(X1) - published_result) < PRECISION\n\n\ntest_overlap()\n\n\n@test\ndef test_samples():\n    mu, sigma = 10000.0, 3.0\n    X = statistics.NormalDist(mu, sigma)\n    n = 1000\n    data = X.samples(n)\n    assert len(data) == n\n\n\ntest_samples()\n\n\n@test\ndef test_from_samples():\n    data = [96.0, 107.0, 90.0, 92.0, 110.0]\n    ND = statistics.NormalDist.from_samples(data)\n    assert ND == statistics.NormalDist(99.0, 9.0)\n\n\ntest_from_samples()\n"
  },
  {
    "path": "test/stdlib/str_test.codon",
    "content": "@test\ndef test_isdigit():\n    assert \"0\".isdigit() == True\n    assert \"\".isdigit() == False\n    assert \"a\".isdigit() == False\n    assert \"2829357\".isdigit() == True\n    assert \"kshfkjhe\".isdigit() == False\n    assert \"9735g385497\".isdigit() == False\n\n\n@test\ndef test_islower():\n    assert \"\".islower() == False\n    assert \"a\".islower() == True\n    assert \"A\".islower() == False\n    assert \"5\".islower() == False\n    assert \"ahuiuej\".islower() == True\n    assert \"AhUiUeJ\".islower() == False\n    assert \"9735g385497\".islower() == True\n    assert \"9735G385497\".islower() == False\n\n\n@test\ndef test_isupper():\n    assert \"\".isupper() == False\n    assert \"a\".isupper() == False\n    assert \"A\".isupper() == True\n    assert \"5\".isupper() == False\n    assert \".J, U-I\".isupper() == True\n    assert \"AHUIUEJ\".isupper() == True\n    assert \"AhUiUeJ\".isupper() == False\n    assert \"9735g385497\".isupper() == False\n    assert \"9735G385497\".isupper() == True\n\n\n@test\ndef test_isalnum():\n    assert \"\".isalnum() == False\n    assert \"a\".isalnum() == True\n    assert \"5\".isalnum() == True\n    assert \",\".isalnum() == False\n    assert \"H6\".isalnum() == True\n    assert \".J, U-I\".isalnum() == False\n    assert \"A4kki83UE\".isalnum() == True\n    assert \"AhUiUeJ\".isalnum() == True\n    assert \"973 g38597\".isalnum() == False\n    assert \"9735G3-5497\".isalnum() == False\n\n\n@test\ndef test_isalpha():\n    assert \"\".isalpha() == False\n    assert \"a\".isalpha() == True\n    assert \"5\".isalpha() == False\n    assert \",\".isalpha() == False\n    assert \"Hh\".isalpha() == True\n    assert \".J, U-I\".isalpha() == False\n    assert \"A4kki83UE\".isalpha() == False\n    assert \"AhUiUeJ\".isalpha() == True\n    assert \"973 g38597\".isalpha() == False\n    assert \"9735G3-5497\".isalpha() == False\n\n\n@test\ndef test_isspace():\n    assert \"\".isspace() == False\n    assert \" \".isspace() == True\n    assert \"5 \".isspace() == False\n    assert \"\\t\\n\\r \".isspace() == True\n    assert \"\\t \".isspace() == True\n    assert \"\\t\\ngh\\r \".isspace() == False\n    assert \"A4kki 3UE\".isspace() == False\n\n\n@test\ndef test_istitle():\n    assert \"\".istitle() == False\n    assert \" \".istitle() == False\n    assert \"I \".istitle() == True\n    assert \"IH\".istitle() == False\n    assert \"Ih\".istitle() == True\n    assert \"Hter Hewri\".istitle() == True\n    assert \"Kweiur oiejf\".istitle() == False\n\n\n@test\ndef test_capitalize():\n    assert \" hello \".capitalize() == \" hello \"\n    assert \"Hello \".capitalize() == \"Hello \"\n    assert \"hello \".capitalize() == \"Hello \"\n    assert \"aaaa\".capitalize() == \"Aaaa\"\n    assert \"AaAa\".capitalize() == \"Aaaa\"\n\n\n@test\ndef test_isdecimal():\n    assert \"\".isdecimal() == False\n    assert \"a\".isdecimal() == False\n    assert \"0\".isdecimal() == True\n    assert \"\\xbc\".isdecimal() == False\n    assert \"0123456789\".isdecimal() == True\n    assert \"0123456789a\".isdecimal() == False\n\n\n@test\ndef test_lower():\n    assert \"HeLLo\".lower() == \"hello\"\n    assert \"hello\".lower() == \"hello\"\n    assert \"HELLO\".lower() == \"hello\"\n    assert \"HEL _ LO\".lower() == \"hel _ lo\"\n\n\n@test\ndef test_upper():\n    assert \"HeLLo\".upper() == \"HELLO\"\n    assert \"hello\".upper() == \"HELLO\"\n    assert \"HELLO\".upper() == \"HELLO\"\n    assert \"HEL _ LO\".upper() == \"HEL _ LO\"\n\n\n@test\ndef test_isascii():\n    assert \"\".isascii() == True\n    assert \"\\x00\".isascii() == True\n    assert \"\\x7f\".isascii() == True\n    assert \"\\x00\\x7f\".isascii() == True\n    assert \"\\x80\".isascii() == False\n    assert \"строка\".isascii() == False\n    assert \"\\xe9\".isascii() == False\n\n\n@test\ndef test_casefold():\n    assert \"\".casefold() == \"\"\n    assert \"HeLLo\".casefold() == \"hello\"\n    assert \"hello\".casefold() == \"hello\"\n    assert \"HELLO\".casefold() == \"hello\"\n    assert \"HEL _ LO\".casefold() == \"hel _ lo\"\n\n\n@test\ndef test_swapcase():\n    assert \"\".swapcase() == \"\"\n    assert \"HeLLo cOmpUteRs\".swapcase() == \"hEllO CoMPuTErS\"\n    assert \"H.e_L,L-o cOmpUteRs\".swapcase() == \"h.E_l,l-O CoMPuTErS\"\n\n\n@test\ndef test_title():\n    assert \"\".title() == \"\"\n    assert \" hello \".title() == \" Hello \"\n    assert \"hello \".title() == \"Hello \"\n    assert \"Hello \".title() == \"Hello \"\n    assert \"fOrMaT thIs aS titLe String\".title() == \"Format This As Title String\"\n    assert \"fOrMaT,thIs-aS*titLe;String\".title() == \"Format,This-As*Title;String\"\n    assert \"getInt\".title() == \"Getint\"\n\n\n@test\ndef test_isnumeric():\n    assert \"\".isdecimal() == False\n    assert \"a\".isdecimal() == False\n    assert \"0\".isdecimal() == True\n    assert \"\\xbc\".isdecimal() == False\n    assert \"0123456789\".isdecimal() == True\n    assert \"0123456789a\".isdecimal() == False\n\n\n@test\ndef test_ljust():\n    assert \"abc\".ljust(10, \" \") == \"abc       \"\n    assert \"abc\".ljust(6, \" \") == \"abc   \"\n    assert \"abc\".ljust(3, \" \") == \"abc\"\n    assert \"abc\".ljust(2, \" \") == \"abc\"\n    assert \"abc\".ljust(10, \"*\") == \"abc*******\"\n\n\n@test\ndef test_rjust():\n    assert \"abc\".rjust(10, \" \") == \"       abc\"\n    assert \"abc\".rjust(6, \" \") == \"   abc\"\n    assert \"abc\".rjust(3, \" \") == \"abc\"\n    assert \"abc\".rjust(2, \" \") == \"abc\"\n    assert \"abc\".rjust(10, \"*\") == \"*******abc\"\n\n\n@test\ndef test_center():\n    assert \"abc\".center(10, \" \") == \"   abc    \"\n    assert \"abc\".center(6, \" \") == \" abc  \"\n    assert \"abc\".center(3, \" \") == \"abc\"\n    assert \"abc\".center(2, \" \") == \"abc\"\n    assert \"abc\".center(10, \"*\") == \"***abc****\"\n\n\n@test\ndef test_zfill():\n    assert \"123\".zfill(2) == \"123\"\n    assert \"123\".zfill(3) == \"123\"\n    assert \"123\".zfill(4) == \"0123\"\n    assert \"+123\".zfill(3) == \"+123\"\n    assert \"+123\".zfill(4) == \"+123\"\n    assert \"+123\".zfill(5) == \"+0123\"\n    assert \"-123\".zfill(3) == \"-123\"\n    assert \"-123\".zfill(4) == \"-123\"\n    assert \"-123\".zfill(5) == \"-0123\"\n    assert \"\".zfill(3) == \"000\"\n    assert \"34\".zfill(1) == \"34\"\n    assert \"34\".zfill(4) == \"0034\"\n    assert \"1+2\".zfill(5) == \"001+2\"\n    assert \"+\".zfill(10) == \"+000000000\"\n    assert \"-\".zfill(10) == \"-000000000\"\n\n\n@test\ndef test_count():\n    assert \"aaa\".count(\"a\", 0, len(\"aaa\")) == 3\n    assert \"aaa\".count(\"b\", 0, len(\"aaa\")) == 0\n    assert \"aaa\".count(\"a\", 1, len(\"aaa\")) == 2\n    assert \"aaa\".count(\"a\", 10, len(\"aaa\")) == 0\n    assert \"aaa\".count(\"a\", -1, len(\"aaa\")) == 1\n    assert \"aaa\".count(\"a\", 0, 1) == 1\n    assert \"aaa\".count(\"a\", 0, 10) == 3\n    assert \"aaa\".count(\"a\", 0, -1) == 2\n    assert \"aaa\".count(\"aa\") == 1\n    assert \"ababa\".count(\"aba\") == 1\n    assert \"abababa\".count(\"aba\") == 2\n    assert \"abababa\".count(\"abab\") == 1\n\n\n@test\ndef test_find():\n    assert \"abcdefghiabc\".find(\"abc\", 0, len(\"abcdefghiabc\")) == 0\n    assert \"abcdefghiabc\".find(\"abc\") == 0\n    assert \"abcdefghiabc\".find(\"abc\", 1, len(\"abcdefghiabc\")) == 9\n    assert \"abcdefghiabc\".find(\"def\", 4, len(\"abcdefghiabc\")) == -1\n    assert \"abcdefghiabc\".find(\"abcdef\", 0, len(\"abcdefghiabc\")) == 0\n    assert \"abcdefghiabc\".find(\"abcdef\") == 0\n    assert \"abcdefghiabc\".find(\"hiabc\", 1, len(\"abcdefghiabc\")) == 7\n    assert \"abcdefghiabc\".find(\"defgh\", 4, len(\"abcdefghiabc\")) == -1\n    assert \"rrarrrrrrrrra\".find(\"a\", 0, len(\"rrarrrrrrrrra\")) == 2\n    assert \"rrarrrrrrrrra\".find(\"a\", 4, len(\"rrarrrrrrrrra\")) == 12\n    assert \"rrarrrrrrrrra\".find(\"a\", 4, 6) == -1\n    assert \"abc\".find(\"\", 0, len(\"abc\")) == 0\n    assert \"abc\".find(\"\", 3, len(\"abc\")) == 3\n    assert \"abc\".find(\"\", 4, len(\"abc\")) == -1\n\n\n@test\ndef test_rfind():\n    assert \"abcdefghiabc\".rfind(\"abc\", 0, len(\"abcdefghiabc\")) == 9\n    assert \"abcdefghiabc\".rfind(\"\", 0, len(\"abcdefghiabc\")) == 12\n    assert \"abcdefghiabc\".rfind(\"abcd\", 0, len(\"abcdefghiabc\")) == 0\n    assert \"abcdefghiabc\".rfind(\"abcz\", 0, len(\"abcdefghiabc\")) == -1\n    assert \"abcdefghiabc\".rfind(\"abc\") == 9\n    assert \"abcdefghiabc\".rfind(\"\") == 12\n    assert \"abcdefghiabc\".rfind(\"abcd\") == 0\n    assert \"abcdefghiabc\".rfind(\"abcz\") == -1\n    assert \"rrarrrrrrrrra\".rfind(\"a\", 0, len(\"rrarrrrrrrrra\")) == 12\n    assert \"rrarrrrrrrrra\".rfind(\"a\", 4, len(\"rrarrrrrrrrra\")) == 12\n    assert \"rrarrrrrrrrra\".rfind(\"a\", 4, 6) == -1\n    assert \"abc\".rfind(\"\", 0, len(\"abc\")) == 3\n    assert \"abc\".rfind(\"\", 3, len(\"abc\")) == 3\n    assert \"abc\".rfind(\"\", 4, len(\"abc\")) == -1\n\n\n@test\ndef test_isidentifier():\n    assert \"a\".isidentifier() == True\n    assert \"Z\".isidentifier() == True\n    assert \"_\".isidentifier() == True\n    assert \"b0\".isidentifier() == True\n    assert \"bc\".isidentifier() == True\n    assert \"b_\".isidentifier() == True\n    assert \" \".isidentifier() == False\n    assert \"3t\".isidentifier() == False\n    assert \"_gth_45\".isidentifier() == True\n\n\n@test\ndef test_isprintable():\n    assert \"\".isprintable() == True\n    assert '\"'.isprintable() == True\n    assert \"'\".isprintable() == True\n    assert \" \".isprintable() == True\n    assert \"abcdef\".isprintable() == True\n    assert \"0123456789\".isprintable() == True\n    assert \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\".isprintable() == True\n    assert \"abcdefghijklmnopqrstuvwxyz\".isprintable() == True\n    assert \"!#$%&()*+,-./:;?@[\\\\]^_`{|}~\".isprintable() == True\n    assert \"abcdef\\n\".isprintable() == False\n\n\n@test\ndef test_lstrip():\n    assert \"\".lstrip() == \"\"\n    assert \"   \".lstrip() == \"\"\n    assert \"   hello   \".lstrip(\"\") == \"hello   \"\n    assert \" \\t\\n\\rabc \\t\\n\\r\".lstrip(\"\") == \"abc \\t\\n\\r\"\n    assert \"xyzzyhelloxyzzy\".lstrip(\"xyz\") == \"helloxyzzy\"\n\n\n@test\ndef test_rstrip():\n    assert \"\".rstrip() == \"\"\n    assert \"   \".rstrip() == \"\"\n    assert \"   hello   \".rstrip(\"\") == \"   hello\"\n    assert \" \\t\\n\\rabc \\t\\n\\r\".rstrip(\"\") == \" \\t\\n\\rabc\"\n    assert \"xyzzyhelloxyzzy\".rstrip(\"xyz\") == \"xyzzyhello\"\n\n\n@test\ndef test_strip():\n    assert \"\".strip() == \"\"\n    assert \"   \".strip() == \"\"\n    assert \"   hello   \".strip(\"\") == \"hello\"\n    assert \"   hello   \".strip() == \"hello\"\n    assert \" \\t\\n\\rabc \\t\\n\\r\".strip() == \"abc\"\n    assert \"xyzzyhelloxyzzy\".strip(\"xyz\") == \"hello\"\n    assert \"hello\".strip(\"xyz\") == \"hello\"\n    assert \"mississippi\".strip(\"mississippi\") == \"\"\n    assert \"mississippi\".strip(\"i\") == \"mississipp\"\n\n\n@test\ndef test_partition():\n    assert \"hello\".partition(\"l\") == (\"he\", \"l\", \"lo\")\n    assert \"this is the partition method\".partition(\"ti\") == (\n        \"this is the par\",\n        \"ti\",\n        \"tion method\",\n    )\n    assert \"http://www.seq.org\".partition(\"://\") == (\"http\", \"://\", \"www.seq.org\")\n    assert \"http://www.seq.org\".partition(\"?\") == (\"http://www.seq.org\", \"\", \"\")\n    assert \"http://www.seq.org\".partition(\"http://\") == (\"\", \"http://\", \"www.seq.org\")\n    assert \"http://www.seq.org\".partition(\"org\") == (\"http://www.seq.\", \"org\", \"\")\n\n\n@test\ndef test_rpartition():\n    assert \"hello\".rpartition(\"l\") == (\"hel\", \"l\", \"o\")\n    assert \"this is the rpartition method\".rpartition(\"ti\") == (\n        \"this is the rparti\",\n        \"ti\",\n        \"on method\",\n    )\n    assert \"http://www.seq.org\".rpartition(\"://\") == (\"http\", \"://\", \"www.seq.org\")\n    assert \"http://www.seq.org\".rpartition(\"?\") == (\"\", \"\", \"http://www.seq.org\")\n    assert \"http://www.seq.org\".rpartition(\"http://\") == (\"\", \"http://\", \"www.seq.org\")\n    assert \"http://www.seq.org\".rpartition(\"org\") == (\"http://www.seq.\", \"org\", \"\")\n\n\n@test\ndef test_split():\n    assert \"  h    l \\t\\n l   o \".split() == [\"h\", \"l\", \"l\", \"o\"]\n    assert \"  h    l \\t\\n l   o \".split(None, 2) == [\"h\", \"l\", \"l   o \"]\n    assert \"  h    l \\t\\n l   o \".split(None, 0) == [\"h    l \\t\\n l   o \"]\n    assert not \"\".split()\n    assert not \"   \".split()\n    assert \"h l l o\".split(\" \", -1) == [\"h\", \"l\", \"l\", \"o\"]\n    assert \"a|b|c|d\".split(\"|\", -1) == [\"a\", \"b\", \"c\", \"d\"]\n    assert \"h l l o\".split(\" \") == [\"h\", \"l\", \"l\", \"o\"]\n    assert \"a|b|c|d\".split(\"|\") == [\"a\", \"b\", \"c\", \"d\"]\n    assert \"a|b|c|d\".split(\"|\", 0) == [\"a|b|c|d\"]\n    assert \"abcd\".split(\"|\", -1) == [\"abcd\"]\n    assert \"\".split(\"|\", -1) == [\"\"]\n    assert \"endcase |\".split(\"|\", -1) == [\"endcase \", \"\"]\n    assert \"| startcase\".split(\"|\", -1) == [\"\", \" startcase\"]\n    assert \"|bothcase|\".split(\"|\", -1) == [\"\", \"bothcase\", \"\"]\n    assert \"abbbc\".split(\"bb\", -1) == [\"a\", \"bc\"]\n    assert \"aaa\".split(\"aaa\", -1) == [\"\", \"\"]\n    assert \"aaa\".split(\"aaa\", 0) == [\"aaa\"]\n    assert \"abbaab\".split(\"ba\", -1) == [\"ab\", \"ab\"]\n    assert \"aa\".split(\"aaa\", -1) == [\"aa\"]\n    assert \"Abbobbbobb\".split(\"bbobb\", -1) == [\"A\", \"bobb\"]\n    assert \"AbbobbBbbobb\".split(\"bbobb\", -1) == [\"A\", \"B\", \"\"]\n    assert (\"a|\" * 20)[:-1].split(\"|\", -1) == [\"a\"] * 20\n    assert (\"a|\" * 20)[:-1].split(\"|\", 15) == [\"a\"] * 15 + [\"a|a|a|a|a\"]\n    assert \"a|b|c|d\".split(\"|\", 1) == [\"a\", \"b|c|d\"]\n    assert \"a|b|c|d\".split(\"|\", 2) == [\"a\", \"b\", \"c|d\"]\n    assert \"a|b|c|d\".split(\"|\", 3) == [\"a\", \"b\", \"c\", \"d\"]\n    assert \"a|b|c|d\".split(\"|\", 4) == [\"a\", \"b\", \"c\", \"d\"]\n    assert \"a||b||c||d\".split(\"|\", 2) == [\"a\", \"\", \"b||c||d\"]\n\n\n@test\ndef test_rsplit():\n    assert \"  h    l \\t\\n l   o \".rsplit() == [\"h\", \"l\", \"l\", \"o\"]\n    assert \"  h    l \\t\\n l   o \".rsplit(None, 2) == [\"  h    l\", \"l\", \"o\"]\n    assert \"  h    l \\t\\n l   o \".rsplit(None, 0) == [\"  h    l \\t\\n l   o\"]\n    assert not \"\".rsplit()\n    assert not \"   \".rsplit()\n    assert \"a|b|c|d\".rsplit(\"|\", -1) == [\"a\", \"b\", \"c\", \"d\"]\n    assert \"a|b|c|d\".rsplit(\"|\") == [\"a\", \"b\", \"c\", \"d\"]\n    assert \"a|b|c|d\".rsplit(\"|\", 1) == [\"a|b|c\", \"d\"]\n    assert \"a|b|c|d\".rsplit(\"|\", 2) == [\"a|b\", \"c\", \"d\"]\n    assert \"a|b|c|d\".rsplit(\"|\", 3) == [\"a\", \"b\", \"c\", \"d\"]\n    assert \"a|b|c|d\".rsplit(\"|\", 4) == [\"a\", \"b\", \"c\", \"d\"]\n    assert \"a|b|c|d\".rsplit(\"|\", 0) == [\"a|b|c|d\"]\n    assert \"a||b||c||d\".rsplit(\"|\", 2) == [\"a||b||c\", \"\", \"d\"]\n    assert \"abcd\".rsplit(\"|\", -1) == [\"abcd\"]\n    assert \"\".rsplit(\"|\", -1) == [\"\"]\n    assert \"endcase |\".rsplit(\"|\", -1) == [\"endcase \", \"\"]\n    assert \"| startcase\".rsplit(\"|\", -1) == [\"\", \" startcase\"]\n    assert \"|bothcase|\".rsplit(\"|\", -1) == [\"\", \"bothcase\", \"\"]\n    # assert 'a\\x00\\x00b\\x00c\\x00d'.rsplit('\\x00', -1)\n    assert \"abbbc\".rsplit(\"bb\", -1) == [\"ab\", \"c\"]\n    assert \"aaa\".rsplit(\"aaa\", -1) == [\"\", \"\"]\n    assert \"aaa\".rsplit(\"aaa\", 0) == [\"aaa\"]\n    assert \"abbaab\".rsplit(\"ba\", -1) == [\"ab\", \"ab\"]\n    assert \"aa\".rsplit(\"aaa\", -1) == [\"aa\"]\n    assert \"bbobbbobbA\".rsplit(\"bbobb\", -1) == [\"bbob\", \"A\"]\n    assert \"bbobbBbbobbA\".rsplit(\"bbobb\", -1) == [\"\", \"B\", \"A\"]\n    assert (\"aBLAH\" * 20)[:-4].rsplit(\"BLAH\", -1) == [\"a\"] * 20\n    assert (\"a|\" * 20)[:-1].rsplit(\"|\", 15) == [\"a|a|a|a|a\"] + [\"a\"] * 15\n    assert \"a||b||c||d\".rsplit(\"|\", 2) == [\"a||b||c\", \"\", \"d\"]\n\n\n@test\ndef test_splitlines():\n    assert \"\\n\\nasdf\\nsadf\\nsdf\\n\".splitlines(False) == [\"\", \"\", \"asdf\", \"sadf\", \"sdf\"]\n    assert \"\\n\\nasdf\\nsadf\\nsdf\\n\".splitlines() == [\"\", \"\", \"asdf\", \"sadf\", \"sdf\"]\n    assert \"abc\\ndef\\n\\rghi\".splitlines(False) == [\"abc\", \"def\", \"\", \"ghi\"]\n    assert \"abc\\ndef\\n\\r\\nghi\".splitlines(False) == [\"abc\", \"def\", \"\", \"ghi\"]\n    assert \"abc\\ndef\\r\\nghi\".splitlines(False) == [\"abc\", \"def\", \"ghi\"]\n    assert \"abc\\ndef\\r\\nghi\\n\".splitlines(False) == [\"abc\", \"def\", \"ghi\"]\n    assert \"abc\\ndef\\r\\nghi\\n\\r\".splitlines(False) == [\"abc\", \"def\", \"ghi\", \"\"]\n    assert \"\\nabc\\ndef\\r\\nghi\\n\\r\".splitlines(False) == [\"\", \"abc\", \"def\", \"ghi\", \"\"]\n    assert \"\\nabc\\ndef\\r\\nghi\\n\\r\".splitlines(True) == [\n        \"\\n\",\n        \"abc\\n\",\n        \"def\\r\\n\",\n        \"ghi\\n\",\n        \"\\r\",\n    ]\n    assert \"abc\\ndef\\r\\nghi\\n\".splitlines(True) == [\"abc\\n\", \"def\\r\\n\", \"ghi\\n\"]\n\n\n@test\ndef test_startswith():\n    assert \"hello\".startswith(\"he\", 0, len(\"hello\")) == True\n    assert \"hello\".startswith(\"hello\", 0, len(\"hello\")) == True\n    assert \"hello\".startswith(\"hello world\", 0, len(\"hello\")) == False\n    assert \"hello\".startswith(\"\", 0, len(\"hello\")) == True\n    assert \"hello\".startswith(\"ello\", 0, len(\"hello\")) == False\n    assert \"hello\".startswith(\"he\") == True\n    assert \"hello\".startswith(\"hello\") == True\n    assert \"hello\".startswith(\"hello world\") == False\n    assert \"hello\".startswith(\"\") == True\n    assert \"hello\".startswith(\"ello\") == False\n    assert \"hello\".startswith(\"ello\", 1, len(\"hello\")) == True\n    assert \"hello\".startswith(\"o\", 4, len(\"hello\")) == True\n    assert \"hello\".startswith(\"o\", 5, len(\"hello\")) == False\n    assert \"hello\".startswith(\"lo\", 3, len(\"hello\")) == True\n    assert \"hello\".startswith(\"\", 5, len(\"hello\")) == True\n    assert \"hello\".startswith(\"lo\", 6, len(\"hello\")) == False\n    assert \"helloworld\".startswith(\"lowo\", 3, len(\"helloworld\")) == True\n    assert \"helloworld\".startswith(\"lowo\", 3, 7) == True\n    assert \"helloworld\".startswith(\"lowo\", 3, 6) == False\n    assert \"\".startswith(\"\", 0, 1) == True\n    assert \"\".startswith(\"\", 0, 0) == True\n    assert \"\".startswith(\"\", 1, 0) == False\n    assert \"hello\".startswith(\"he\", 0, -1) == True\n    assert \"hello\".startswith(\"hello\", 0, -1) == False\n    assert \"hello\".startswith(\"he\", 0, -3) == True\n    assert \"hello\".startswith(\"ello\", -4, len(\"hello\")) == True\n    assert \"hello\".startswith(\"ello\", -5, len(\"hello\")) == False\n    assert \"hello\".startswith(\"\", -3, -3) == True\n    assert \"hello\".startswith(\"o\", -1, len(\"hello\")) == True\n\n\n@test\ndef test_endswith():\n    assert \"hello\".endswith(\"lo\", 0, len(\"hello\")) == True\n    assert \"hello\".endswith(\"he\", 0, len(\"hello\")) == False\n    assert \"hello\".endswith(\"\", 0, len(\"hello\")) == True\n    assert \"hello\".endswith(\"hello world\", 0, len(\"hello\")) == False\n    assert \"hello\".endswith(\"lo\") == True\n    assert \"hello\".endswith(\"he\") == False\n    assert \"hello\".endswith(\"\") == True\n    assert \"hello\".endswith(\"hello world\") == False\n    assert \"helloworld\".endswith(\"worl\", 0, len(\"hello\")) == False\n    assert \"helloworld\".endswith(\"worl\", 3, 9) == True\n    assert \"helloworld\".endswith(\"world\", 3, 12) == True\n    assert \"helloworld\".endswith(\"lowo\", 1, 7) == True\n    assert \"helloworld\".endswith(\"lowo\", 2, 7) == True\n    assert \"helloworld\".endswith(\"lowo\", 3, 7) == True\n    assert \"helloworld\".endswith(\"lowo\", 4, 7) == False\n    assert \"helloworld\".endswith(\"lowo\", 3, 8) == False\n    assert \"ab\".endswith(\"ab\", 0, 1) == False\n    assert \"ab\".endswith(\"ab\", 0, 0) == False\n    assert \"\".endswith(\"\", 0, 1) == True\n    assert \"\".endswith(\"\", 0, 0) == True\n    assert \"\".endswith(\"\", 1, 0) == False\n    assert \"hello\".endswith(\"lo\", -2, len(\"hello\")) == True\n    assert \"hello\".endswith(\"he\", -2, len(\"hello\")) == False\n    assert \"hello\".endswith(\"\", -3, -3) == True\n    assert \"helloworld\".endswith(\"worl\", -6, len(\"helloworld\")) == False\n    assert \"helloworld\".endswith(\"worl\", -5, -1) == True\n    assert \"helloworld\".endswith(\"worl\", -5, 9) == True\n    assert \"helloworld\".endswith(\"world\", -7, 12) == True\n    assert \"helloworld\".endswith(\"lowo\", -99, -3) == True\n    assert \"helloworld\".endswith(\"lowo\", -8, -3) == True\n    assert \"helloworld\".endswith(\"lowo\", -7, -3) == True\n    assert \"helloworld\".endswith(\"lowo\", 3, -4) == False\n    assert \"helloworld\".endswith(\"lowo\", -8, -2) == False\n\n\n@test\ndef test_index():\n    assert \"abcdefghiabc\".index(\"abc\", 0, len(\"abcdefghiabc\")) == 0\n    assert \"abcdefghiabc\".index(\"abc\") == 0\n    assert \"abcdefghiabc\".index(\"abc\", 1, len(\"abcdefghiabc\")) == 9\n    assert \"abc\".index(\"\", 0, len(\"abc\")) == 0\n    assert \"abc\".index(\"\", 3, len(\"abc\")) == 3\n    assert \"rrarrrrrrrrra\".index(\"a\", 0, len(\"rrarrrrrrrrra\")) == 2\n    assert \"rrarrrrrrrrra\".index(\"a\", 4, len(\"rrarrrrrrrrra\")) == 12\n    try:\n        \"abcdefghiabc\".index(\"def\", 4, len(\"abcdefghiabc\"))\n        assert False\n    except ValueError:\n        pass\n\n\n@test\ndef test_rindex():\n    assert \"abcdefghiabc\".rindex(\"\", 0, len(\"abcdefghiabc\")) == 12\n    assert \"abcdefghiabc\".rindex(\"\") == 12\n    assert \"abcdefghiabc\".rindex(\"def\", 0, len(\"abcdefghiabc\")) == 3\n    assert \"abcdefghiabc\".rindex(\"abc\", 0, len(\"abcdefghiabc\")) == 9\n    assert \"abcdefghiabc\".rindex(\"abc\", 0, -1) == 0\n    assert \"rrarrrrrrrrra\".rindex(\"a\", 0, len(\"rrarrrrrrrrra\")) == 12\n    assert \"rrarrrrrrrrra\".rindex(\"a\", 4, len(\"rrarrrrrrrrra\")) == 12\n    try:\n        \"rrarrrrrrrrra\".rindex(\"a\", 4, 6)\n        assert False\n    except ValueError:\n        pass\n\n\n@test\ndef test_replace():\n    # interleave-- default will be len(str) + 1\n    assert \"A\".replace(\"\", \"\", len(\"A\") + 1) == \"A\"\n    assert \"A\".replace(\"\", \"*\", len(\"A\") + 1) == \"*A*\"\n    assert \"A\".replace(\"\", \"*1\", len(\"A\") + 1) == \"*1A*1\"\n    assert \"A\".replace(\"\", \"*-#\", len(\"A\") + 1) == \"*-#A*-#\"\n    assert \"AA\".replace(\"\", \"*-\", len(\"AA\") + 1) == \"*-A*-A*-\"\n    assert \"AA\".replace(\"\", \"*-\", -1) == \"*-A*-A*-\"\n    assert \"AA\".replace(\"\", \"*-\") == \"*-A*-A*-\"\n    assert \"AA\".replace(\"\", \"*-\", 4) == \"*-A*-A*-\"\n    assert \"AA\".replace(\"\", \"*-\", 3) == \"*-A*-A*-\"\n    assert \"AA\".replace(\"\", \"*-\", 2) == \"*-A*-A\"\n    assert \"AA\".replace(\"\", \"*-\", 1) == \"*-AA\"\n    assert \"AA\".replace(\"\", \"*-\", 0) == \"AA\"\n\n    # substring deletion\n    assert \"A\".replace(\"A\", \"\", len(\"A\") + 1) == \"\"\n    assert \"AAA\".replace(\"A\", \"\", len(\"AAA\") + 1) == \"\"\n    assert \"AAA\".replace(\"A\", \"\", -1) == \"\"\n    assert \"AAA\".replace(\"A\", \"\") == \"\"\n    assert \"AAA\".replace(\"A\", \"\", 4) == \"\"\n    assert \"AAA\".replace(\"A\", \"\", 3) == \"\"\n    assert \"AAA\".replace(\"A\", \"\", 2) == \"A\"\n    assert \"AAA\".replace(\"A\", \"\", 1) == \"AA\"\n    assert \"AAA\".replace(\"A\", \"\", 0) == \"AAA\"\n    assert \"ABACADA\".replace(\"A\", \"\", len(\"ABACADA\") + 1) == \"BCD\"\n    assert \"ABACADA\".replace(\"A\", \"\", -1) == \"BCD\"\n    assert \"ABACADA\".replace(\"A\", \"\", 5) == \"BCD\"\n    assert \"ABACADA\".replace(\"A\", \"\", 4) == \"BCD\"\n    assert \"ABACADA\".replace(\"A\", \"\", 3) == \"BCDA\"\n    assert \"ABACADA\".replace(\"A\", \"\", 2) == \"BCADA\"\n    assert \"ABACADA\".replace(\"A\", \"\", 1) == \"BACADA\"\n    assert \"ABACADA\".replace(\"A\", \"\", 0) == \"ABACADA\"\n    assert \"ABCAD\".replace(\"A\", \"\", len(\"ABCAD\") + 1) == \"BCD\"\n    assert \"ABCADAA\".replace(\"A\", \"\", len(\"ABCADAA\") + 1) == \"BCD\"\n    assert \"BCD\".replace(\"A\", \"\", len(\"BCD\") + 1) == \"BCD\"\n    assert (\"^\" + (\"A\" * 1000) + \"^\").replace(\"A\", \"\", 999) == \"^A^\"\n    assert \"the\".replace(\"the\", \"\", len(\"the\") + 1) == \"\"\n    assert \"theater\".replace(\"the\", \"\", len(\"theater\") + 1) == \"ater\"\n    assert \"thethe\".replace(\"the\", \"\", len(\"thethe\") + 1) == \"\"\n    assert \"thethethethe\".replace(\"the\", \"\", len(\"thethethethe\") + 1) == \"\"\n    assert \"theatheatheathea\".replace(\"the\", \"\", len(\"theatheatheathea\") + 1) == \"aaaa\"\n    assert \"that\".replace(\"the\", \"\", len(\"that\") + 1) == \"that\"\n    assert (\n        \"here and there\".replace(\"the\", \"\", len(\"here and there\") + 1) == \"here and re\"\n    )\n    assert (\n        \"here and there and there\".replace(\n            \"the\", \"\", len(\"here and there and there\") + 1\n        )\n        == \"here and re and re\"\n    )\n    assert \"here and there and there\".replace(\"the\", \"\", -1) == \"here and re and re\"\n    assert \"here and there and there\".replace(\"the\", \"\", 3) == \"here and re and re\"\n    assert \"here and there and there\".replace(\"the\", \"\", 2) == \"here and re and re\"\n    assert \"here and there and there\".replace(\"the\", \"\", 1) == \"here and re and there\"\n    assert (\n        \"here and there and there\".replace(\"the\", \"\", 0) == \"here and there and there\"\n    )\n\n    # substring replace in place\n    assert (\n        \"Who goes there?\".replace(\"o\", \"o\", len(\"Who goes there?\") + 1)\n        == \"Who goes there?\"\n    )\n    assert (\n        \"Who goes there?\".replace(\"o\", \"O\", len(\"Who goes there?\") + 1)\n        == \"WhO gOes there?\"\n    )\n    assert \"Who goes there?\".replace(\"o\", \"O\", -1) == \"WhO gOes there?\"\n    assert \"Who goes there?\".replace(\"o\", \"O\", 3) == \"WhO gOes there?\"\n    assert \"Who goes there?\".replace(\"o\", \"O\", 2) == \"WhO gOes there?\"\n    assert \"Who goes there?\".replace(\"o\", \"O\", 1) == \"WhO goes there?\"\n    assert \"Who goes there?\".replace(\"o\", \"O\", 0) == \"Who goes there?\"\n    assert (\n        \"Who goes there?\".replace(\"a\", \"q\", len(\"Who goes there?\") + 1)\n        == \"Who goes there?\"\n    )\n    assert (\n        \"Who goes there?\".replace(\"W\", \"w\", len(\"Who goes there?\") + 1)\n        == \"who goes there?\"\n    )\n    assert (\n        \"WWho goes there?WW\".replace(\"W\", \"w\", len(\"WWho goes there?WW\") + 1)\n        == \"wwho goes there?ww\"\n    )\n    assert (\n        \"Who goes there?\".replace(\"?\", \"!\", len(\"Who goes there?\") + 1)\n        == \"Who goes there!\"\n    )\n    assert (\n        \"This is a tissue\".replace(\"is\", \"**\", len(\"This is a tissue\") + 1)\n        == \"Th** ** a t**sue\"\n    )\n    assert \"This is a tissue\".replace(\"is\", \"**\", -1) == \"Th** ** a t**sue\"\n    assert \"This is a tissue\".replace(\"is\", \"**\", 4) == \"Th** ** a t**sue\"\n    assert \"This is a tissue\".replace(\"is\", \"**\", 3) == \"Th** ** a t**sue\"\n    assert \"This is a tissue\".replace(\"is\", \"**\", 2) == \"Th** ** a tissue\"\n    assert \"This is a tissue\".replace(\"is\", \"**\", 1) == \"Th** is a tissue\"\n    assert \"This is a tissue\".replace(\"is\", \"**\", 0) == \"This is a tissue\"\n    assert \"Reykjavik\".replace(\"k\", \"KK\", len(\"Reykjavik\") + 1) == \"ReyKKjaviKK\"\n    assert \"Reykjavik\".replace(\"k\", \"KK\", -1) == \"ReyKKjaviKK\"\n    assert \"Reykjavik\".replace(\"k\", \"KK\", 2) == \"ReyKKjaviKK\"\n    assert \"Reykjavik\".replace(\"k\", \"KK\", 1) == \"ReyKKjavik\"\n    assert \"Reykjavik\".replace(\"k\", \"KK\", 0) == \"Reykjavik\"\n    assert \"A.B.C.\".replace(\".\", \"----\", len(\"A.B.C.\") + 1) == \"A----B----C----\"\n    assert (\n        \"spam, spam, eggs and spam\".replace(\n            \"spam\", \"ham\", len(\"spam, spam, eggs and spam\") + 1\n        )\n        == \"ham, ham, eggs and ham\"\n    )\n    assert (\n        \"spam, spam, eggs and spam\".replace(\"spam\", \"ham\", -1)\n        == \"ham, ham, eggs and ham\"\n    )\n    assert (\n        \"spam, spam, eggs and spam\".replace(\"spam\", \"ham\", 4)\n        == \"ham, ham, eggs and ham\"\n    )\n    assert (\n        \"spam, spam, eggs and spam\".replace(\"spam\", \"ham\", 3)\n        == \"ham, ham, eggs and ham\"\n    )\n    assert (\n        \"spam, spam, eggs and spam\".replace(\"spam\", \"ham\", 2)\n        == \"ham, ham, eggs and spam\"\n    )\n    assert (\n        \"spam, spam, eggs and spam\".replace(\"spam\", \"ham\", 1)\n        == \"ham, spam, eggs and spam\"\n    )\n    assert (\n        \"spam, spam, eggs and spam\".replace(\"spam\", \"ham\", 0)\n        == \"spam, spam, eggs and spam\"\n    )\n\n\n@test\ndef test_expandtabs():\n    assert \"abc\\rab\\tdef\\ng\\thi\".expandtabs(8) == \"abc\\rab      def\\ng       hi\"\n    assert \"abc\\rab\\tdef\\ng\\thi\".expandtabs(8) == \"abc\\rab      def\\ng       hi\"\n    assert \"abc\\rab\\tdef\\ng\\thi\".expandtabs(4) == \"abc\\rab  def\\ng   hi\"\n    assert \"abc\\r\\nab\\tdef\\ng\\thi\".expandtabs(8) == \"abc\\r\\nab      def\\ng       hi\"\n    assert \"abc\\r\\nab\\tdef\\ng\\thi\".expandtabs(4) == \"abc\\r\\nab  def\\ng   hi\"\n    assert \"abc\\r\\nab\\r\\ndef\\ng\\r\\nhi\".expandtabs(4) == \"abc\\r\\nab\\r\\ndef\\ng\\r\\nhi\"\n    assert \" \\ta\\n\\tb\".expandtabs(1) == \"  a\\n b\"\n    assert \"\\tdndhd\\ty\\ty\\tyu\\t\".expandtabs(3) == \"   dndhd y  y  yu \"\n\n\n@test\ndef test_translate():\n    assert \"I yor ge\".translate({ord(\"g\"): \"w\", ord(\"y\"): \"f\"}) == \"I for we\"\n    assert \"abababc\".translate({ord(\"a\"): \"\"}) == \"bbbc\"\n    assert \"abababc\".translate({ord(\"a\"): \"\", ord(\"b\"): \"i\"}) == \"iiic\"\n    assert \"abababc\".translate({ord(\"a\"): \"\", ord(\"b\"): \"i\", ord(\"c\"): \"x\"}) == \"iiix\"\n    assert \"abababc\".translate({ord(\"a\"): \"\", ord(\"b\"): \"\"}) == \"c\"\n    assert \"xzx\".translate({ord(\"z\"): \"yy\"}) == \"xyyx\"\n    assert \"aaabbbccc\".translate({ord(\"b\"): Optional(\"XY\"), ord(\"c\"): None, ord(\"a\"): Optional(\"\")}) == \"XYXYXY\"\n\n@test\ndef test_repr():\n    assert repr(\"\") == \"''\"\n    assert repr(\"hello\") == \"'hello'\"\n    assert repr(\"     \") == \"'     '\"\n    assert repr(\"\\r\\a\\n\\t\") == \"'\\\\r\\\\a\\\\n\\\\t'\"\n\n\n@test\ndef test_fstr():\n    assert f\"{2+2}\" == \"4\"\n    n = 42\n    assert f\"{n}{n}xx{n}\" == \"4242xx42\"\n    assert f\"{n=}\" == \"n=42\"\n    assert f\"hello {n=} world\" == \"hello n=42 world\"\n\n\n@test\ndef test_slice(\n    s=\"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789\",\n    indices=(0, 1, 3, 41, 0xFFFFFFFFFFF, -1, -2, -37),\n):\n    for start in indices:\n        for stop in indices:\n            for step in indices[1:]:\n                L = list(s)[start:stop:step]\n                assert s[start:stop:step] == \"\".join(L)\n\n\n@test\ndef test_join():\n    assert \"\".join(str(a) for a in range(0)) == \"\"\n    assert \"\".join(List[str]()) == \"\"\n    assert \"a\".join(str(a) for a in range(0)) == \"\"\n    assert \"a\".join(List[str]()) == \"\"\n    assert \"ab\".join(str(a) for a in range(999, 1000)) == \"999\"\n    assert \"ab\".join([\"999\"]) == \"999\"\n    assert \"xyz\".join(str(a) for a in range(5)) == \"0xyz1xyz2xyz3xyz4\"\n    assert \"xyz\".join([\"00\", \"1\", \"22\", \"3\", \"44\"]) == \"00xyz1xyz22xyz3xyz44\"\n    assert \"xyz\".join(iter([\"00\", \"1\", \"22\", \"3\", \"44\"])) == \"00xyz1xyz22xyz3xyz44\"\n    assert \"xyz\".join([\"00\", \"1\", \"22\", \"3\", \"44\"]) == \"00xyz1xyz22xyz3xyz44\"\n    assert \"xyz\".join(iter([\"00\", \"\", \"22\", \"3\", \"\"])) == \"00xyzxyz22xyz3xyz\"\n\n\n@test\ndef test_repr():\n    s = (\n        \"\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\"\n        \"\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f !\\\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefg\"\n        \"hijklmnopqrstuvwxyz{|}~\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\"\n        \"\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xff\"\n    )\n    assert repr(s) == (\n        \"'\\\\x00\\\\x01\\\\x02\\\\x03\\\\x04\\\\x05\\\\x06\\\\x07\\\\x08\\\\t\\\\n\\\\x0b\\\\x0c\\\\r\\\\x0e\\\\x0f\\\\x10\\\\x11\"\n        \"\\\\x12\\\\x13\\\\x14\\\\x15\\\\x16\\\\x17\\\\x18\\\\x19\\\\x1a\\\\x1b\\\\x1c\\\\x1d\\\\x1e\\\\x1f !\\\"#$%&\\\\'()*+,\"\n        \"-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\\\\"\n        \"x7f\\\\x80\\\\x81\\\\x82\\\\x83\\\\x84\\\\x85\\\\x86\\\\x87\\\\x88\\\\x89\\\\x8a\\\\x8b\\\\x8c\\\\x8d\\\\x8e\\\\x8f\\\\x90\"\n        \"\\\\x91\\\\x92\\\\x93\\\\x94\\\\x95\\\\x96\\\\x97\\\\x98\\\\x99\\\\x9a\\\\x9b\\\\x9c\\\\x9d\\\\x9e\\\\x9f\\\\xa0\\\\xff'\"\n    )\n    assert repr(\"\") == \"''\"\n    assert repr('\"') == \"'\\\"'\"\n    assert repr(\"'\") == '\"\\'\"'\n    assert repr(\"\\\"'\") == \"'\\\"\\\\''\"\n\n\ntest_isdigit()\ntest_islower()\ntest_isupper()\ntest_isalnum()\ntest_isalpha()\ntest_isspace()\ntest_istitle()\ntest_capitalize()\ntest_isdecimal()\ntest_lower()\ntest_upper()\ntest_isascii()\ntest_casefold()\ntest_swapcase()\ntest_title()\ntest_isnumeric()\ntest_ljust()\ntest_rjust()\ntest_center()\ntest_zfill()\ntest_count()\ntest_find()\ntest_rfind()\ntest_isidentifier()\ntest_isprintable()\ntest_lstrip()\ntest_rstrip()\ntest_strip()\ntest_partition()\ntest_rpartition()\ntest_split()\ntest_rsplit()\ntest_splitlines()\ntest_startswith()\ntest_endswith()\ntest_index()\ntest_rindex()\ntest_replace()\ntest_expandtabs()\ntest_translate()\ntest_repr()\ntest_fstr()\ntest_slice()\ntest_join()\ntest_repr()\n"
  },
  {
    "path": "test/transform/canonical.codon",
    "content": "@tuple\nclass Vec[T]:\n    x: T\n    y: T\n\n    @pure\n    def __abs__(self):\n        return ((self.x * self.x) + (self.y * self.y)) ** 0.5\n\n    @pure\n    @commutative\n    @associative\n    def __add__(self, other: Vec[T]):\n        print 'vec add', self, other\n        return Vec[T](self.x + other.x, self.y + other.y)\n\n    @pure\n    @commutative\n    @associative\n    def __add__(self, other: T):\n        print 'vec add', self, other\n        return Vec[T](self.x + other, self.y + other)\n\n    @pure\n    def __sub__(self, other: Vec[T]):\n        print 'vec sub', self, other\n        return Vec[T](self.x - other.x, self.y - other.y)\n\n    @pure\n    def __sub__(self, other: T):\n        print 'vec sub', self, other\n        return Vec[T](self.x - other, self.y - other)\n\n    @pure\n    @commutative\n    @associative\n    @distributive\n    def __mul__(self, other: Vec[T]):\n        print 'vec mul', self, other\n        return Vec[T](self.x * other.x, self.y * other.y)\n\n    @pure\n    @commutative\n    @associative\n    @distributive\n    def __mul__(self, other: T):\n        print 'vec mul', self, other\n        return Vec[T](self.x * other, self.y * other)\n\n    @pure\n    @commutative\n    def __eq__(self, other: Vec[T]):\n        print 'vec eq', self, other\n        return abs(self) == abs(other)\n\n    @pure\n    @commutative\n    def __ne__(self, other: Vec[T]):\n        print 'vec ne', self, other\n        return abs(self) != abs(other)\n\n    @pure\n    def __lt__(self, other: Vec[T]):\n        print 'vec lt', self, other\n        return abs(self) < abs(other)\n\n    @pure\n    def __le__(self, other: Vec[T]):\n        print 'vec le', self, other\n        return abs(self) <= abs(other)\n\n    @pure\n    def __gt__(self, other: Vec[T]):\n        print 'vec gt', self, other\n        return abs(self) > abs(other)\n\n    @pure\n    def __ge__(self, other: Vec[T]):\n        print 'vec ge', self, other\n        return abs(self) >= abs(other)\n\n@test\ndef test_op_chain_canon():\n    @pure\n    def f(a): return a\n\n    a = Vec(1, 2)\n    b = Vec(3, 4)\n    c = a + f(b)  # -> f(b) + a\n    assert (c.x, c.y) == (4, 6)\n    # EXPECT: vec add (x: 3, y: 4) (x: 1, y: 2)\n\n    a = Vec(1, 2)\n    b = Vec(3, 4)\n    c = Vec(5, 6)\n    d = f(a + f(b) + f(f(c)))  # -> f(f(f(c)) + f(b) + a)\n    assert (d.x, d.y) == (9, 12)\n    # EXPECT: vec add (x: 5, y: 6) (x: 3, y: 4)\n    # EXPECT: vec add (x: 8, y: 10) (x: 1, y: 2)\n\n    a = Vec(1, 2)\n    b = Vec(3, 4)\n    c = Vec(5, 6)\n    d = f(a + (f(b) + f(f(c))))  # -> f(f(f(c)) + f(b) + a)\n    assert (d.x, d.y) == (9, 12)\n    # EXPECT: vec add (x: 5, y: 6) (x: 3, y: 4)\n    # EXPECT: vec add (x: 8, y: 10) (x: 1, y: 2)\n\n    a = Vec(1, 2)\n    b = Vec(3, 4)\n    c = a - f(b)  # -> no change\n    assert (c.x, c.y) == (-2, -2)\n    # EXPECT: vec sub (x: 1, y: 2) (x: 3, y: 4)\n\n    # don't canon float ops\n    assert f(1e100) + f(f(-1e100)) + f(f(f(1.))) == 1.\ntest_op_chain_canon()\n\nclass C:\n    n: int\n\n    def __lt__(self: C, other: C):\n        return self.n < other.n\n\n@test\ndef test_inequality_canon():\n    @pure\n    def f(a): return a\n\n    a = Vec(1,1)\n    b = Vec(2,2)\n\n    assert not (f(a) == b)\n    assert f(a) != b\n    assert f(a) < b\n    assert f(a) <= b\n    assert not (f(a) > b)\n    assert not (f(a) >= b)\n    # EXPECT: vec eq (x: 1, y: 1) (x: 2, y: 2)\n    # EXPECT: vec ne (x: 1, y: 1) (x: 2, y: 2)\n    # EXPECT: vec lt (x: 1, y: 1) (x: 2, y: 2)\n    # EXPECT: vec le (x: 1, y: 1) (x: 2, y: 2)\n    # EXPECT: vec gt (x: 1, y: 1) (x: 2, y: 2)\n    # EXPECT: vec ge (x: 1, y: 1) (x: 2, y: 2)\n\n    assert not (a == f(b))\n    assert a != f(b)\n    assert a < f(b)\n    assert a <= f(b)\n    assert not (a > f(b))\n    assert not (a >= f(b))\n    # EXPECT: vec eq (x: 2, y: 2) (x: 1, y: 1)\n    # EXPECT: vec ne (x: 2, y: 2) (x: 1, y: 1)\n    # EXPECT: vec gt (x: 2, y: 2) (x: 1, y: 1)\n    # EXPECT: vec ge (x: 2, y: 2) (x: 1, y: 1)\n    # EXPECT: vec lt (x: 2, y: 2) (x: 1, y: 1)\n    # EXPECT: vec le (x: 2, y: 2) (x: 1, y: 1)\n\n    c1 = C(1)\n    c2 = C(2)\n    # ensure we don't use missing ops\n    assert c1 < f(c2)\ntest_inequality_canon()\n\n@test\ndef test_add_mul_canon():\n    @pure\n    def f(a): return a\n\n    a = Vec(1,1)\n    b = Vec(2,2)\n    c = Vec(3,3)\n    d = (a*f(b) + c*a)  # -> (f(b) + c) * a\n    assert (d.x, d.y) == (5, 5)\n    # EXPECT: vec add (x: 2, y: 2) (x: 3, y: 3)\n    # EXPECT: vec mul (x: 5, y: 5) (x: 1, y: 1)\n\n    d = (a + c*a)  # -> (c + 1) * a\n    assert (d.x, d.y) == (4, 4)\n    # EXPECT: vec add (x: 3, y: 3) 1\n    # EXPECT: vec mul (x: 4, y: 4) (x: 1, y: 1)\n\n    d = (c*a + a)  # -> (c + 1) * a\n    assert (d.x, d.y) == (4, 4)\n    # EXPECT: vec add (x: 3, y: 3) 1\n    # EXPECT: vec mul (x: 4, y: 4) (x: 1, y: 1)\n\n    a = Vec(1,1)\n    b = a + a + a + a + a\n    assert (b.x, b.y) == (5, 5)\n    # EXPECT: vec mul (x: 1, y: 1) 5\n\n    a = Vec(1,1)\n    b = a + a*2 + a*3 + a*4 + a*5\n    assert (b.x, b.y) == (15, 15)\n    # EXPECT: vec mul (x: 1, y: 1) 15\n\n    x = f(100.)  # don't distribute float ops\n    assert (x * 0.1) + (x * 0.2) == 30.\ntest_add_mul_canon()\n"
  },
  {
    "path": "test/transform/dict_opt.codon",
    "content": "class DummyDict[K, V]:\n    def __getitem__(self, k: K):\n        raise ValueError('failed')\n        return V()\n    def get(self, k: K, d: V):\n        raise ValueError('failed')\n        return V()\n    def __setitem__(self, k: K, v: V):\n        assert False\n    def __dict_do_op_throws__[F, Z](self, key: K, other: Z, op: F):\n        pass\n    def __dict_do_op__[F, Z](self, key: K, other: Z, dflt: V, op: F):\n        pass\n\nclass WrappedDict[K, V]:\n    d: Dict[K,V]\n    do_op_throws_count: int\n    do_op_count: int\n\n    def __init__(self):\n        self.d = {}\n        self.do_op_throws_count = 0\n        self.do_op_count = 0\n    def __getitem__(self, k: K):\n        return self.d.__getitem__(k)\n    def get(self, k: K, d: V):\n        return self.d.get(k, d)\n    def __setitem__(self, k: K, v: V):\n        self.d.__setitem__(k, v)\n    def setdefault(self, k: K, v: V):\n        return self.d.setdefault(k, v)\n\n    def __dict_do_op_throws__[F, Z](self, key: K, other: Z, op: F):\n        self.do_op_throws_count += 1\n        self.d.__dict_do_op_throws__(key, other, op)\n    def __dict_do_op__[F, Z](self, key: K, other: Z, dflt: V, op: F):\n        self.do_op_count += 1\n        self.d.__dict_do_op__(key, other, dflt, op)\n\n@test\ndef test_dict_op():\n    x = DummyDict[int, int]()\n    x[1] = x[1] + 1\n    x[1] = x.get(1, 1) + 1\ntest_dict_op()\n\n@test\ndef test_dict_do_not_op():\n    x = DummyDict[int, int]()\n    try:\n        x[1] = x[2] + 1\n    except ValueError:\n        return\n    assert False\ntest_dict_do_not_op()\n\n@test\ndef test_wrapped_dict():\n    def my_op(a, b):\n        return a * b\n    def my_op_throws(a, b):\n        raise ValueError('my_op_throws')\n        return a * b\n\n    x = WrappedDict[str, float]()\n    x['a'] = x.get('a', 0.0) + 1.0        # invokes opt (do_op_count)\n    x['a'] = x['a'] * 2                   # invokes opt (do_op_throws_count)\n    x['b'] = x.setdefault('b', 4.5) + 99  # no opt (no getitem/get)\n    x['a'] += x['b']                      # invokes opt (do_op_throws_count)\n    x['b'] = my_op(x['b'], 2.0)           # no opt (not a int/float method)\n    foo = x['a']\n    x['a'] = x['b'] + foo                 # no opt (different keys)\n\n    try:\n        x['c'] += 1.0                     # invokes opt (do_op_throws_count)\n        assert False\n    except KeyError:\n        pass\n\n    try:\n        x['d'] = my_op_throws(x['d'], 2.0)  # no opt (not a int/float method)\n        assert False\n    except KeyError:\n        pass\n\n    try:\n        x['d'] = my_op_throws(x.get('d', 111.0), 2.0)  # no opt (not a int/float method)\n        assert False\n    except ValueError:\n        pass\n\n    assert x.do_op_throws_count == 3\n    assert x.do_op_count == 1\n    assert x.d == {'a': 312.5, 'b': 207}\ntest_wrapped_dict()\n"
  },
  {
    "path": "test/transform/escapes.codon",
    "content": "# entry point for validator\n@nonpure\ndef expect_capture(return_captures: bool, extern_captures: bool, arg_captures):\n    return False\n\ng = [0]\nh = ''\n\n@nonpure\ndef make_sure_globals_arent_optd_out():\n    g.append(1)\n    print(h)\n\nmake_sure_globals_arent_optd_out()\n\n@test\ndef test_1(a):\n    global g\n    g = a\n    assert expect_capture(False, True, ())  # a\ntest_1([42])\n\n@test\ndef test_2(a, b, c):\n    x = c\n    b[0] = a\n    y = x\n    assert expect_capture(False, False, (1,))  # a\n    assert expect_capture(False, False, ())    # b\n    assert expect_capture(False, False, ())    # c\ntest_2([42], [[1]], ['x'])\n\n@test\ndef test_3(a):\n    global g\n    x = [1]\n    p = __ptr__(x)\n    p[0] = a\n    q = p\n    g = p[0]\n    assert expect_capture(False, True, ())  # a\ntest_3([42])\n\n@test\ndef test_4(a):\n    global g\n    v = [a]\n    g = v[0]\n    assert expect_capture(False, True, ())  # a\ntest_4([42])\n\n@test\ndef test_5(a):\n    global g\n    v = [a]\n    for i in v:\n        g = i\n    assert expect_capture(False, True, ())  # a\ntest_5([42])\n\n@test\ndef test_6(a, b, c):\n    a[0] = b\n    c[0] = a\n    assert expect_capture(False, False, (2,))    # a\n    assert expect_capture(False, False, (0, 2))  # b\n    assert expect_capture(False, False, ())      # c\ntest_6([[0]], [42], [[[0]]])\n\n@test\ndef test_7(a, b, c):\n    assert expect_capture(True, False, ())   # a\n    assert expect_capture(False, False, ())  # b\n    assert expect_capture(True, False, ())   # c\n    return a if b else c\ntest_7([11], g, [22])\n\nclass X:\n    v: List[List[int]]\n\n@test\ndef test_8(a):\n    x = X([])\n    x.v.append(a)\n    assert expect_capture(True, False, ())  # a\n    return x\ntest_8([42])\n\n@test\ndef test_9(a):\n    a = [0]\n    assert expect_capture(False, False, ())  # a\n    return a\ntest_9([42])\n\n@test\ndef test_10(a, b):\n    if b:\n        a = [0]\n    assert expect_capture(True, False, ())   # a\n    assert expect_capture(False, False, ())  # b\n    return a\ntest_10([42], [99])\n\n@test\ndef test_11(a):\n    global g\n    g = a\n    assert expect_capture(True, True, ())  # a\n    return g\ntest_11([42])\n\n@test\ndef test_12(a, b):\n    global g\n    b[0] = a\n    x = {2: b}\n    y = [x]\n    z = {'z': y}\n    g = z['z'][0][2][0]\n    assert expect_capture(True, True, (1,))  # a\n    assert expect_capture(True, True, (0,))  # b\n    return z\ntest_12([42], [[0]])\n\n@test\ndef test_13(a, n):\n    if n > 0:\n        test_13(a, n - 1)\n    assert expect_capture(True, True, ())    # a\n    assert expect_capture(False, False, ())  # b\n    return a\ntest_13([42], 3)\n\n@test\ndef test_14(a):\n    def assign_global(a):\n        global g\n        g = a\n\n    assign_global(a)\n    assert expect_capture(True, True, ())  # a\n    return g\ntest_14([42])\n\nclass OA:\n    a: Optional[OA]\n\n@test\ndef test_15(a, b):\n    a.a = b\n    b.a = a\n    assert expect_capture(False, False, (1,))  # a\n    assert expect_capture(False, False, (0,))  # b\ntest_15(OA(None), OA(None))\n\n@test\ndef test_16(a, b):\n    a.a = b\n    b.a = a\n    assert expect_capture(True, False, (1,))  # a\n    assert expect_capture(True, False, (0,))  # b\n    return a\ntest_16(OA(None), OA(None))\n\n@test\ndef test_17(a):\n    global h\n    h = a[1:-1]\n    assert expect_capture(True, True, ())  # a\n    return a[2:-2]\ntest_17('hello world')\n\n@test\ndef test_18(a, b):\n    if a:\n        x = (b, b)\n        raise ValueError(x[len(b)])\n    assert expect_capture(False, False, ())  # a\n    assert expect_capture(False, True, ())   # b\ntest_18([0 for _ in range(0)], 'b')\n\ndef assign1(x, y):\n    x[0] = y\n\n@test\ndef test_19(a, b, cond, elem):\n    assign1(a if cond else b, elem)\n    assert expect_capture(False, False, ())     # a\n    assert expect_capture(True, False, ())      # b\n    assert expect_capture(False, False, ())     # cond\n    assert expect_capture(True, False, (0, 1))  # elem\n    return b\ntest_19(['a'], ['b'], [True], 'x')\n\n@test\ndef test_20(x):\n    a = ['']\n    p = a\n    p[0] = x\n    assert expect_capture(True, False, ())  # x\n    return a\ntest_20('x')\n\n@test\ndef test_21(x):\n    a = ''\n    p = __ptr__(a)\n    p[0] = x\n    assert expect_capture(True, False, ())  # x\n    return a\ntest_21('x')\n\nclass A:\n    a: List[str]\n\n@test\ndef test_22(x):\n    a = ['']\n    p = A([])\n    p.a = a\n    p.a[0] = x\n    assert expect_capture(True, False, ())  # x\n    return a\ntest_22('x')\n\ndef assign(p, a):\n    p.a = a\n\ndef test_23(x):\n    a = ['']\n    p = A([])\n    assign(p, a)\n    p.a[0] = x\n    assert expect_capture(True, False, ())  # x\n    return a\ntest_23('x')\n\nclass S:\n    s: str\n\n@test\ndef test_24(a, b, cond):\n    q = S('')\n    if cond:\n        q = a\n    q.s = b\n    assert expect_capture(False, False, ())    # a\n    assert expect_capture(False, False, (0,))  # b\n    assert expect_capture(False, False, ())    # cond\ntest_24(S('s'), 'b', True)\n\n@test\ndef test_25(a, b, v):\n    q = S('')\n    for i in v:\n        if i:\n            q = a\n        else:\n            q = S('q')\n    q.s = b\n    assert expect_capture(False, False, ())    # a\n    assert expect_capture(False, False, (0,))  # b\n    assert expect_capture(False, False, ())    # v\ntest_25(S('s'), 'b', [0,1,0,1])\n\n@test\ndef test_26(a, b, v):\n    q = S('')\n    for i in v:\n        if i:\n            q = a\n        else:\n            q.s = b\n    return q\n    assert expect_capture(True, False, ())    # a\n    assert expect_capture(True, False, (0,))  # b\n    assert expect_capture(False, False, ())   # v\n    return q\ntest_26(S('s'), 'b', [0,1,0,1])\n\n@test\ndef test_27(x):\n    a = ''\n    p = __ptr__(a)\n    q = __ptr__(p)\n    q[0][0] = x\n    assert expect_capture(True, False, ())  # x\n    return a\ntest_27('x')\n"
  },
  {
    "path": "test/transform/folding.codon",
    "content": "OP_COUNT = 0\n\n@llvm\ndef inc(a: int) -> int:\n    %tmp = add i64 %a, 1\n    ret i64 %tmp\n\nclass I:\n    @llvm\n    def __float__(self: int) -> float:\n        %tmp = sitofp i64 %self to double\n        ret double %tmp\n\n    @llvm\n    def __bool__(self: int) -> bool:\n        %0 = icmp ne i64 %self, 0\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n    def __pos__(self: int) -> int:\n        return self\n\n    def __neg__(self: int) -> int:\n        return I.__sub__(0, self)\n\n    @llvm\n    def __abs__(self: int) -> int:\n        %0 = icmp sgt i64 %self, 0\n        %1 = sub i64 0, %self\n        %2 = select i1 %0, i64 %self, i64 %1\n        ret i64 %2\n\n    @llvm\n    def __lshift__(self: int, other: int) -> int:\n        %0 = shl i64 %self, %other\n        ret i64 %0\n\n    @llvm\n    def __rshift__(self: int, other: int) -> int:\n        %0 = ashr i64 %self, %other\n        ret i64 %0\n\n    @llvm\n    def __add__(self: int, other: float) -> float:\n        %0 = sitofp i64 %self to double\n        %1 = fadd double %0, %other\n        ret double %1\n\n    @llvm\n    def __add__(self: int, b: int) -> int:\n        %tmp = add i64 %self, %b\n        ret i64 %tmp\n\n    @llvm\n    def __sub__(self: int, other: float) -> float:\n        %0 = sitofp i64 %self to double\n        %1 = fsub double %0, %other\n        ret double %1\n\n    @llvm\n    def __sub__(self: int, b: int) -> int:\n        %tmp = sub i64 %self, %b\n        ret i64 %tmp\n\n    @llvm\n    def __mul__(self: int, other: float) -> float:\n        %0 = sitofp i64 %self to double\n        %1 = fmul double %0, %other\n        ret double %1\n\n    @llvm\n    def __mul__(self: int, b: int) -> int:\n        %tmp = mul i64 %self, %b\n        ret i64 %tmp\n\n    @llvm\n    def __floordiv__(self: int, other: float) -> float:\n        declare double @llvm.floor.f64(double)\n        %0 = sitofp i64 %self to double\n        %1 = fdiv double %0, %other\n        %2 = call double @llvm.floor.f64(double %1)\n        ret double %2\n\n    @llvm\n    def __floordiv__(self: int, b: int) -> int:\n        %tmp = sdiv i64 %self, %b\n        ret i64 %tmp\n\n    @llvm\n    def __truediv__(self: int, other: float) -> float:\n        %0 = sitofp i64 %self to double\n        %1 = fdiv double %0, %other\n        ret double %1\n\n    @llvm\n    def __truediv__(self: int, other: int) -> float:\n        %0 = sitofp i64 %self to double\n        %1 = sitofp i64 %other to double\n        %2 = fdiv double %0, %1\n        ret double %2\n\n    @llvm\n    def __mod__(self: int, other: float) -> float:\n        %0 = sitofp i64 %self to double\n        %1 = frem double %0, %other\n        ret double %1\n\n    @llvm\n    def __mod__(a: int, b: int) -> int:\n        %tmp = srem i64 %a, %b\n        ret i64 %tmp\n\n    @llvm\n    def __invert__(a: int) -> int:\n        %tmp = xor i64 %a, -1\n        ret i64 %tmp\n\n    @llvm\n    def __and__(a: int, b: int) -> int:\n        %tmp = and i64 %a, %b\n        ret i64 %tmp\n\n    @llvm\n    def __or__(a: int, b: int) -> int:\n        %tmp = or i64 %a, %b\n        ret i64 %tmp\n\n    @llvm\n    def __xor__(a: int, b: int) -> int:\n        %tmp = xor i64 %a, %b\n        ret i64 %tmp\n\n    @llvm\n    def __shr__(a: int, b: int) -> int:\n        %tmp = ashr i64 %a, %b\n        ret i64 %tmp\n\n    @llvm\n    def __shl__(a: int, b: int) -> int:\n        %tmp = shl i64 %a, %b\n        ret i64 %tmp\n\n    @llvm\n    def __bitreverse__(a: int) -> int:\n        declare i64 @llvm.bitreverse.i64(i64 %a)\n        %tmp = call i64 @llvm.bitreverse.i64(i64 %a)\n        ret i64 %tmp\n\n    @llvm\n    def __bswap__(a: int) -> int:\n        declare i64 @llvm.bswap.i64(i64 %a)\n        %tmp = call i64 @llvm.bswap.i64(i64 %a)\n        ret i64 %tmp\n\n    @llvm\n    def __ctpop__(a: int) -> int:\n        declare i64 @llvm.ctpop.i64(i64 %a)\n        %tmp = call i64 @llvm.ctpop.i64(i64 %a)\n        ret i64 %tmp\n\n    @llvm\n    def __ctlz__(a: int) -> int:\n        declare i64 @llvm.ctlz.i64(i64 %a, i1 %is_zero_undef)\n        %tmp = call i64 @llvm.ctlz.i64(i64 %a, i1 false)\n        ret i64 %tmp\n\n    @llvm\n    def __cttz__(a: int) -> int:\n        declare i64 @llvm.cttz.i64(i64 %a, i1 %is_zero_undef)\n        %tmp = call i64 @llvm.cttz.i64(i64 %a, i1 false)\n        ret i64 %tmp\n\n    @llvm\n    def __eq__(self: int, b: float) -> bool:\n        %0 = sitofp i64 %self to double\n        %1 = fcmp oeq double %0, %b\n        %2 = zext i1 %1 to i8\n        ret i8 %2\n\n    @llvm\n    def __eq__(a: int, b: int) -> bool:\n        %tmp = icmp eq i64 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @llvm\n    def __ne__(self: int, b: float) -> bool:\n        %0 = sitofp i64 %self to double\n        %1 = fcmp one double %0, %b\n        %2 = zext i1 %1 to i8\n        ret i8 %2\n\n    @llvm\n    def __ne__(a: int, b: int) -> bool:\n        %tmp = icmp ne i64 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @llvm\n    def __lt__(self: int, b: float) -> bool:\n        %0 = sitofp i64 %self to double\n        %1 = fcmp olt double %0, %b\n        %2 = zext i1 %1 to i8\n        ret i8 %2\n\n    @llvm\n    def __lt__(a: int, b: int) -> bool:\n        %tmp = icmp slt i64 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @llvm\n    def __gt__(self: int, b: float) -> bool:\n        %0 = sitofp i64 %self to double\n        %1 = fcmp ogt double %0, %b\n        %2 = zext i1 %1 to i8\n        ret i8 %2\n\n    @llvm\n    def __gt__(a: int, b: int) -> bool:\n        %tmp = icmp sgt i64 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @llvm\n    def __le__(self: int, b: float) -> bool:\n        %0 = sitofp i64 %self to double\n        %1 = fcmp ole double %0, %b\n        %2 = zext i1 %1 to i8\n        ret i8 %2\n\n    @llvm\n    def __le__(a: int, b: int) -> bool:\n        %tmp = icmp sle i64 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    @llvm\n    def __ge__(self: int, b: float) -> bool:\n        %0 = sitofp i64 %self to double\n        %1 = fcmp oge double %0, %b\n        %2 = zext i1 %1 to i8\n        ret i8 %2\n\n    @llvm\n    def __ge__(a: int, b: int) -> bool:\n        %tmp = icmp sge i64 %a, %b\n        %res = zext i1 %tmp to i8\n        ret i8 %res\n\n    def __pow__(self: int, exp: float):\n        return float(self) ** exp\n\n    def __pow__(self: int, exp: int):\n        if exp < 0:\n            return 0\n        result = 1\n        while True:\n            if exp & 1:\n                result *= self\n            exp >>= 1\n            if not exp:\n                break\n            self *= self\n        return result\n\n@extend\nclass int:\n    def __int__(self) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return self\n\n    def __float__(self) -> float:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__float__(self)\n\n    def __bool__(self) -> bool:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__bool__(self)\n\n    def __pos__(self) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return self\n\n    def __neg__(self) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__neg__(self)\n\n    def __lshift__(self, other: int) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__lshift__(self, other)\n\n    def __rshift__(self, other: int) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__rshift__(self, other)\n\n    def __add__(self, other: float) -> float:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__add__(self, other)\n\n    def __add__(self, b: int) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__add__(self, b)\n\n    def __sub__(self, other: float) -> float:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__sub__(self, other)\n\n    def __sub__(self, b: int) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__sub__(self, b)\n\n    def __mul__(self, other: float) -> float:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__mul__(self, other)\n\n    def __mul__(self, b: int) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__mul__(self, b)\n\n    def __floordiv__(self, other: float) -> float:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__floordiv__(self, other)\n\n    def __floordiv__(self, b: int) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__floordiv__(self, b)\n\n    def __truediv__(self, other: float) -> float:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__truediv__(self, other)\n\n    def __truediv__(self, other: int) -> float:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__truediv__(self, other)\n\n    def __mod__(self, other: float) -> float:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__mod__(self, other)\n\n    def __mod__(self, b: int) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__mod__(self, b)\n\n    def __invert__(self) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__invert__(self)\n\n    def __and__(self, b: int) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__and__(self, b)\n\n    def __or__(self, b: int) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__or__(self, b)\n\n    def __xor__(self, b: int) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__xor__(self, b)\n\n    def __eq__(self, b: float) -> bool:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__eq__(self, b)\n\n    def __eq__(self, b: int) -> bool:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__eq__(self, b)\n\n    def __ne__(self, b: float) -> bool:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__ne__(self, b)\n\n    def __ne__(self, b: int) -> bool:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__ne__(self, b)\n\n    def __lt__(self, b: float) -> bool:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__lt__(self, b)\n\n    def __lt__(self, b: int) -> bool:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__lt__(self, b)\n\n    def __gt__(self, b: float) -> bool:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__gt__(self, b)\n\n    def __gt__(self, b: int) -> bool:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__gt__(self, b)\n\n    def __le__(self, b: float) -> bool:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__le__(self, b)\n\n    def __le__(self, b: int) -> bool:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__le__(self, b)\n\n    def __ge__(self, b: float) -> bool:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__ge__(self, b)\n\n    def __ge__(self, b: int) -> bool:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__ge__(self, b)\n\n    def __pow__(self, exp: float):\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__pow__(self, exp)\n\n    def __pow__(self, exp: int):\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return I.__pow__(self, exp)\n\n\nclass F:\n    @llvm\n    def __int__(self: float) -> int:\n        %0 = fptosi double %self to i64\n        ret i64 %0\n\n    def __float__(self: float):\n        return self\n\n    @llvm\n    def __bool__(self: float) -> bool:\n        %0 = fcmp one double %self, 0.000000e+00\n        %1 = zext i1 %0 to i8\n        ret i8 %1\n\n@extend\nclass float:\n    def __int__(self) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return F.__int__(self)\n\n    def __float__(self) -> float:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return self\n\n    def __bool__(self) -> bool:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return F.__bool__(self)\n\n@extend\nclass bool:\n    def __int__(self) -> int:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return 1 if self else 0\n\n    def __float__(self) -> float:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return 1. if self else 0.\n\n    def __bool__(self) -> bool:\n        global OP_COUNT\n        OP_COUNT = inc(OP_COUNT)\n        return self\n\ndef eq(a: int, b: int) -> bool:\n    return I.__eq__(a, b)\n\n@noinline\ndef foo(x):\n  return x\n\n@test\ndef test_int_simple_fold():\n    op_count = OP_COUNT\n    x = 1\n    y = x + 2\n    z = x + 3\n    assert eq(foo(x + 1), 2)\n    assert eq(foo(y * 2), 6)\n    assert eq(foo(z // 3), 1)\n    assert eq(foo(x >> 2), 0)\n    assert eq(foo(x << y), 8)\n    assert eq(foo(x | y), 3)\n    assert eq(foo(z & z), 4)\n    assert not foo(x > y)\n    assert foo(y < z)\n    assert not foo(x >= z)\n    assert foo(x <= 2)\n    assert foo(x == 1)\n    assert foo(x != 2)\n    assert eq(foo(y ** 3), 27)\n    assert eq(foo(y ** 0), 1)\n    assert eq(foo(y ** -1), 0)\n    assert eq(op_count, OP_COUNT)\ntest_int_simple_fold()\n\n@test\ndef test_tuple_simple_fold():\n    op_count = OP_COUNT\n    x = (1,)\n    y = x[0] + 2\n    z = x[0] + 3\n    assert eq(foo(x[0] + 1), 2)\n    assert eq(foo(y * 2), 6)\n    assert eq(foo(z // 3), 1)\n    assert eq(foo(x[0] >> 2), 0)\n    assert eq(foo(x[0] << y), 8)\n    assert eq(foo(x[0] | y), 3)\n    assert eq(foo(z & z), 4)\n    assert not foo(x[0] > y)\n    assert foo(y < z)\n    assert not foo(x[0] >= z)\n    assert foo(x[0] <= 2)\n    assert foo(x[0] == 1)\n    assert foo(x[0] != 2)\n    assert eq(foo(y ** 3), 27)\n    assert eq(foo(y ** 0), 1)\n    assert eq(foo(y ** -1), 0)\n    assert eq(op_count, OP_COUNT)\ntest_tuple_simple_fold()\n\n@test\ndef test_ternary_fold():\n    op_count = OP_COUNT\n    x = 1\n    y = x * 2\n    assert (x + 1 if x != 0 else -1) > 0\n    assert (x + 1 if x == 0 else -1) < 0\n    assert eq(op_count, OP_COUNT)\ntest_ternary_fold()\n\n@test\ndef test_try_catch_fold():\n    op_count = OP_COUNT\n    x = 0\n    y = x + 1\n    try:\n        x = 1\n    finally:\n        x = 4\n    assert x == 4\n    assert eq(op_count, OP_COUNT)\ntest_try_catch_fold()\n\n@test\ndef test_while_fold():\n    op_count = OP_COUNT\n    x = 0\n    y = 1\n    while (x != 0):\n        y = 2\n    assert y + 1 == 2\n    assert eq(op_count, OP_COUNT)\ntest_while_fold()\n\n@test\ndef test_imperative_for_fold():\n    op_count = OP_COUNT\n    foo = 2\n    y = 1\n    for i in range(foo, 2):\n        y = foo - 1\n    assert y == 1\n    assert eq(op_count, OP_COUNT)\ntest_imperative_for_fold()\n\n@test\ndef test_long_fold():\n    op_count = OP_COUNT\n    x = 3\n    assert eq(foo(x + x + x + x + x + x + x + x + x + x + x + x), 36)\n    assert eq(op_count, OP_COUNT)\ntest_long_fold()\n\n@test\ndef test_conversions():\n    op_count = OP_COUNT\n    n = 42\n    b = True\n    x = 3.14\n\n    assert eq(foo(n.__int__()), n)\n    assert foo(n.__float__()) == 42.0\n    assert foo(n.__bool__())\n\n    assert eq(foo(b.__int__()), 1)\n    assert foo(b.__float__()) == 1.0\n    assert foo(b.__bool__())\n\n    assert eq(foo(x.__int__()), 3)\n    assert foo(x.__float__()) == x\n    assert foo(x.__bool__())\n    assert eq(op_count, OP_COUNT)\ntest_conversions()\n\n@test\ndef test_no_ops():\n    def v(): return 42\n    op_count = OP_COUNT\n    n = v()\n    assert eq(+n, n)\n    assert eq(-(-n), n)\n    assert eq(~(~n), n)\n    assert eq(op_count, OP_COUNT)\ntest_no_ops()\n\n@test\ndef test_algebraic_simplification():\n    def v(): return 42\n    op_count = OP_COUNT\n    n = v()\n    assert eq(0*n, 0)\n    assert eq(n*0, 0)\n    assert eq(n*1, n)\n    assert eq(1*n, n)\n    assert eq(n+0, n)\n    assert eq(0+n, n)\n    assert eq(n-0, n)\n    assert eq(0//n, 0)\n    assert eq(op_count, OP_COUNT)\ntest_algebraic_simplification()\n\nn = 42\n@test\ndef test_global_fold():\n    op_count = OP_COUNT\n    m = 1\n    assert eq(n + m, 43)\n    assert eq(op_count, OP_COUNT)\ntest_global_fold()\n\n# make sure globals fold properly with demotion\nop_count1 = OP_COUNT\na = foo(42)\na = 42\nb = a + 10\nc = b\nop_count2 = OP_COUNT\n\n@test\ndef test_global_fold_non_const():\n    op_count = OP_COUNT\n    assert c == 52\n    assert op_count1 == op_count2\ntest_global_fold_non_const()\n\nsome_global = 42\n@test\ndef test_side_effect_analysis():\n    @pure\n    @test\n    def foo():\n        assert False\n        return some_global\n\n    def bar():\n        a = 1\n        b = 2\n        c = 3\n        return foo()\n\n    def baz():\n        global some_global\n        some_global += 1\n        return some_global\n\n    def fab():\n        return baz()\n\n    foo()\n    x = foo()\n    # bar()  TODO: fix partials\n    # y = bar()\n    assert baz() == 43\n    baz()\n    assert some_global == 44\n    assert fab() == 45\n    fab()\n    assert some_global == 46\ntest_side_effect_analysis()\n"
  },
  {
    "path": "test/transform/for_lowering.codon",
    "content": "@extend\nclass range:\n    def __iter__(self):\n        yield 999\n\n@test\ndef test_for_lowering():\n    from random import randint\n    x = 10\n    v = []\n    for i in range(x):\n        v.append(i)\n    assert v == [0,1,2,3,4,5,6,7,8,9]\n\n    v = []\n    for i in range(x - 11, x//10):\n        v.append(i)\n    assert v == [-1, 0]\n\n    v = []\n    for i in range(x, -x):\n        v.append(i)\n    assert v == []\n\n    v = []\n    for i in range(x//3, -x//3, -2):\n        v.append(i)\n    assert v == [3, 1, -1]\n\n    v = []\n    for i in range(-1, 7, 3):\n        v.append(i)\n    assert v == [-1, 2, 5]\n\n    v = []\n    for i in range(0, 1, randint(1,1)):  # no lowering for non-const step\n        v.append(i)\n    assert v == [999]\n\n    v = []\n    try:\n        for i in range(0, 1, 0):  # no lowering for zero step\n            v.append(i)\n        v.append(-1)\n    except:\n        v.append(-2)\n    assert v == [-2]\n\n    v = []\n    for i in range(5):\n        if i == 1:\n            continue\n        if i == 3:\n            break\n        v.append(i)\n    assert v == [0, 2]\n\ntest_for_lowering()\n"
  },
  {
    "path": "test/transform/inlining.codon",
    "content": "v = 1\nv = 2\n\ndef inline_me_aggressive_simple():\n  return 1\n\ndef inline_me_aggressive_complex():\n  while True:\n    if v == 1:\n      return 1\n    return 2\n\ndef inline_me_aggressive_args(x):\n  return x + 1\n\ndef inline_me_simple():\n  return 1\n\ndef inline_me_complex():\n  while True:\n    if v == 1:\n      return 1\n    return 2\n\ndef inline_me_args(x):\n  return x + 1\n\n@test\ndef inlining_test():\n  assert inline_me_simple() == -1\n  assert inline_me_complex() == 2\n  assert inline_me_args(2) == -3\n  assert inline_me_aggressive_simple() == -1\n  assert inline_me_aggressive_complex() == -2\n  assert inline_me_aggressive_args(2) == -3\n\ninlining_test()\n\ndef inline_me_aggressive_nested_while_finally(n):\n    while n != 0:\n        try:\n            while n != 0:\n                if n == 42:\n                    n -= 1\n                    continue\n                try:\n                    if n > 0:\n                        continue\n                    else:\n                        break\n                finally:\n                    return -1\n            return -2\n        finally:\n            return n + 1\n\ndef inline_test_nested_while_finally():\n    a = 42\n    checkpoint1 = False\n    checkpoint2 = False\n    checkpoint3 = False\n    try:\n        while a != 4:\n            try:\n                a = inline_me_aggressive_nested_while_finally(a)\n                checkpoint1 = True\n                assert a == -42\n            finally:\n                a = 4\n            checkpoint2 = True\n            assert a == 4\n    finally:\n        checkpoint3 = True\n        assert a == 4\n        a = 5\n    assert a == 5\n    assert checkpoint1\n    assert checkpoint2\n    assert checkpoint3\n\ninline_test_nested_while_finally()\n"
  },
  {
    "path": "test/transform/io_opt.codon",
    "content": "class DummyFile:\n    write_count: int = 0\n    write_gen_count: int = 0\n\n    def write(self, s: str):\n        self.write_count += 1\n    def __file_write_gen__(self, g):\n        self.write_gen_count += 1\n\n@test\ndef test_file_io():\n    f = DummyFile()\n    f.write('')                                     # opt not applied\n    f.write('hello world')                          # opt not applied\n    f.write(str.cat(\"hello \", \"world\"))             # opt applied\n    a, b, c = 3.14, 'xyz', 42\n    f.write(f'hello {a} world {b=} abc {a+c} zzz')  # opt applied\n    f.write(f'hello world')                         # opt not applied\n    assert f.write_count == 3\n    assert f.write_gen_count == 2\ntest_file_io()\n\n@test\ndef test_print():\n    from sys import stdout\n    print('hello world')               # EXPECT: hello world\n    print(str.cat(\"hello \", \"world\"))  # EXPECT: hello world\n    a, b, c = 3.14, 'xyz', 42\n    print(f'hello {a} world {b=} abc {a+c} zzz', file=stdout, sep='x')  # EXPECT: hello 3.14 world b=xyz abc 45.14 zzz\n    print(f'hello', f'world', sep='x')  # EXPECT: helloxworld\ntest_print()\n"
  },
  {
    "path": "test/transform/kernels.codon",
    "content": "import gpu\nimport internal.static as static\n\n@test\ndef test_hello_world():\n    @gpu.kernel\n    def kernel(a, b, c):\n        i = gpu.thread.x\n        c[i] = a[i] + b[i]\n\n    a = [i for i in range(16)]\n    b = [2*i for i in range(16)]\n    c = [0 for _ in range(16)]\n    kernel(a, b, c, grid=1, block=16)\n\n    assert c == [3*i for i in range(16)]\n\n@test\ndef test_raw():\n    @gpu.kernel\n    def kernel(a, b, c):\n        i = gpu.thread.x\n        c[i] = a[i] + b[i]\n\n    a = [i for i in range(16)]\n    b = [2*i for i in range(16)]\n    c = [0 for _ in range(16)]\n    kernel(gpu.raw(a), gpu.raw(b), gpu.raw(c), grid=1, block=16)\n\n    assert c == [3*i for i in range(16)]\n\n@test\ndef test_conversions():\n    @gpu.kernel\n    def kernel(x, v):\n        v[0] = x\n\n    def empty_tuple(x):\n        if static.len(x) == 0:\n            return ()\n        else:\n            T = type(x[0])\n            return (T(),) + empty_tuple(x[1:])\n\n    def check(x):\n        if isinstance(x, Tuple):\n            e = empty_tuple(x)\n        else:\n            e = type(x)()\n        v = [e]\n        kernel(x, v, grid=1, block=1)\n        return v == [x]\n\n    assert check(None)\n    assert check(42)\n    assert check(3.14)\n    assert check(f32(2.718))\n    assert check(byte(99))\n    assert check(Int[128](123123))\n    assert check(UInt[128](321321))\n    assert check(Optional[int]())\n    assert check(Optional(111))\n    assert check((1, 2, 3))\n    assert check(([1], [2], [3]))\n    # assert check(())  # TODO: PTX can't handle this; why?\n    assert check(DynamicTuple((1, 2, 3)))\n    assert check(DynamicTuple(([1], [2], [3])))\n    assert check(DynamicTuple[int]())\n    assert check(DynamicTuple[List[List[List[str]]]]())\n    assert check('hello world')\n    assert check([1, 2, 3])\n    assert check([[1], [2], [3]])\n    assert check({1: [1.1], 2: [2.2]})\n    assert check({'a', 'b', 'c'})\n    assert check(Optional([1, 2, 3]))\n\n@test\ndef test_user_classes():\n    @dataclass(gpu=True, eq=True)\n    class A:\n       x: int\n       y: List[int]\n\n    @tuple\n    class B:\n        x: int\n        y: List[int]\n\n    @gpu.kernel\n    def kernel(a, b, c):\n        a.x += b.x + c[0]\n        c[1][0][0] = 9999\n        a.y[0] = c[0] + 1\n        b.y[0] = c[0] + 2\n\n    a = A(42, [-1])\n    b = B(100, [-2])\n    c = (1000, [[-1]])\n    kernel(a, b, c, grid=1, block=1)\n\n    assert a == A(1142, [1001])\n    assert b == B(100, [1002])\n    assert c == (1000, [[9999]])\n\n    @gpu.kernel\n    def kernel2(a, b, c):\n        a[0].x += b[0].x + c[0][0]\n        c[0][1][0][0] = 9999\n        a[0].y[0] = c[0][0] + 1\n        b[0].y[0] = c[0][0] + 2\n\n    a = [A(42, [-1])]\n    b = [B(100, [-2])]\n    c = [(1000, [[-1]])]\n    kernel2(a, b, c, grid=1, block=1)\n\n    assert a == [A(1142, [1001])]\n    assert b == [B(100, [1002])]\n    assert c == [(1000, [[9999]])]\n\n@test\ndef test_intrinsics():\n    @gpu.kernel\n    def kernel(v):\n        block_id = (gpu.block.x + gpu.block.y*gpu.grid.dim.x +\n                    gpu.block.z*gpu.grid.dim.x*gpu.grid.dim.y)\n        thread_id = (block_id*gpu.block.dim.x*gpu.block.dim.y*gpu.block.dim.z +\n                     gpu.thread.z*gpu.block.dim.x*gpu.block.dim.y +\n                     gpu.thread.y*gpu.block.dim.x +\n                     gpu.thread.x)\n        v[thread_id] = thread_id\n        gpu.syncthreads()\n\n    grid = gpu.Dim3(3, 4, 5)\n    block = gpu.Dim3(6, 7, 8)\n    N = grid.x * grid.y * grid.z * block.x * block.y * block.z\n    v = [0 for _ in range(N)]\n    kernel(v, grid=grid, block=block)\n    assert v == list(range(N))\n\n@test\ndef test_matmul():\n    A = [[12, 7, 3],\n         [4, 5, 6],\n         [7, 8, 9]]\n\n    B = [[5, 8, 1, 2],\n         [6, 7, 3, 0],\n         [4, 5, 9, 1]]\n\n    def mmz(A, B):\n        return [[0]*len(B[0]) for _ in range(len(A))]\n\n    def matmul(A, B):\n        result = mmz(A, B)\n        for i in range(len(A)):\n            for j in range(len(B[0])):\n                for k in range(len(B)):\n                    result[i][j] += A[i][k] * B[k][j]\n        return result\n\n    expected = matmul(A, B)\n\n    @gpu.kernel\n    def kernel(A, B, result):\n        i = gpu.thread.x\n        j = gpu.thread.y\n        result[i][j] = sum(A[i][k]*B[k][j] for k in range(len(A[0])))\n\n    result = mmz(A, B)\n    kernel(A, B, result, grid=1, block=(len(result), len(result[0])))\n    assert result == expected\n\nMAX    = 1000  # maximum Mandelbrot iterations\nN      = 256   # width and height of image\n\n@test\ndef test_mandelbrot():\n    pixels = [0 for _ in range(N * N)]\n\n    def scale(x, a, b):\n        return a + (x/N)*(b - a)\n\n    expected = [0 for _ in range(N * N)]\n    for i in range(N):\n        for j in range(N):\n            c = complex(scale(j, -2.00, 0.47), scale(i, -1.12, 1.12))\n            z = 0j\n            iteration = 0\n\n            while abs(z) <= 2 and iteration < MAX:\n                z = z**2 + c\n                iteration += 1\n\n            expected[N*i + j] = int(255 * iteration/MAX)\n\n    @gpu.kernel\n    def kernel(pixels):\n        idx = (gpu.block.x * gpu.block.dim.x) + gpu.thread.x\n        i, j = divmod(idx, N)\n        c = complex(scale(j, -2.00, 0.47), scale(i, -1.12, 1.12))\n        z = 0j\n        iteration = 0\n\n        while abs(z) <= 2 and iteration < MAX:\n            z = z**2 + c\n            iteration += 1\n\n        pixels[idx] = int(255 * iteration/MAX)\n\n    kernel(pixels, grid=(N*N)//1024, block=1024)\n    assert pixels == expected\n\n@test\ndef test_kitchen_sink():\n    @gpu.kernel\n    def kernel(x):\n        i = gpu.thread.x\n        d = {1: 2.1, 2: 3.5, 3: 4.2}\n        s = {4, 5, 6}\n        z = sum(\n            d.get(x[i], j) + (j if i in s else -j)\n            for j in range(i)\n        )\n        x[i] = int(z)\n\n    x = [i for i in range(16)]\n    kernel(x, grid=1, block=16)\n    assert x == [0, 2, 6, 9, 12, 20, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0]\n\n@test\ndef test_auto_par():\n    a = [i for i in range(16)]\n    b = [2*i for i in range(16)]\n    c = [0 for _ in range(16)]\n\n    @par(gpu=True)\n    for i in range(16):\n        c[i] = a[i] + b[i]\n\n    assert c == [3*i for i in range(16)]\n\n    @par(gpu=True)\n    for i in range(16):\n        c[i] += a[i] + b[i]\n\n    assert c == [6*i for i in range(16)]\n\n    N = 200\n    Z = 42\n    x = [0] * (N*N)\n    y = [0] * (N*N)\n\n    for i in range(2, N - 1, 3):\n        for j in range(3, N, 2):\n            x[i*N + j] = i + j + Z\n\n    @par(gpu=True, collapse=2)\n    for i in range(2, N - 1, 3):\n        for j in range(3, N, 2):\n            y[i*N + j] = i + j + Z\n\n    assert x == y\n\n    @par(gpu=True)\n    for i in range(1):\n        pass\n\ntest_hello_world()\ntest_raw()\ntest_conversions()\ntest_user_classes()\ntest_intrinsics()\ntest_matmul()\ntest_mandelbrot()\ntest_kitchen_sink()\ntest_auto_par()\n"
  },
  {
    "path": "test/transform/list_opt.codon",
    "content": "add_count = 0\n\n@extend\nclass List:\n    def __add__(self, other: List[T]) -> List[T]:\n        global add_count\n        add_count += 1\n        n = self.len + other.len\n        v = List[T](n)\n        v.len = n\n        p = v.arr.ptr\n        str.memcpy(p.as_byte(),\n                   self.arr.ptr.as_byte(),\n                   self.len * gc.sizeof(T))\n        str.memcpy((p + self.len).as_byte(),\n                   other.arr.ptr.as_byte(),\n                   other.len * gc.sizeof(T))\n        return v\n\n@test\ndef test_list_optimization():\n    add_count0 = add_count\n    A = list(range(3))\n    B = list(range(10))\n    assert [0] + [1] == [0, 1]\n    assert A + B == [0, 1, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9]\n    assert (A + B[:] + B[7:] + B[:3] + B[3:7] + B[7:3:-1] + A[::-1] + [11, 22, 33] ==\n            [0, 1, 2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 2, 1, 0, 11, 22, 33])\n\n    def f(a, tag, order):\n        order.append(tag)\n        return a\n\n    order = []\n    X = (f([1, 2], 'a', order) +\n         [f(3, 'b', order), f(4, 'c', order)] +\n         f(list(range(10)), 'd', order)[f(5, 'e', order):f(2, 'f', order):f(-1, 'g', order)])\n    assert X == [1, 2, 3, 4, 5, 4, 3]\n    assert order == ['a', 'b', 'c', 'd', 'e', 'f', 'g']\n    assert add_count == add_count0\n\ntest_list_optimization()\n"
  },
  {
    "path": "test/transform/omp.codon",
    "content": "import openmp as omp\nimport threading as thr\n\nlock = thr.Lock()\n\n@tuple\nclass A:\n    n: int\n\n    def __new__() -> A:\n        return A(0)\n\n    def __add__(self, other: A):\n        return A(self.n + other.n)\n\n    def __atomic_add__(a: Ptr[A], other: A):\n        with lock:\n            a[0] = A(a[0].n + other.n)\n\n@test\ndef test_omp_api():\n    thr.active_count()\n    thr.get_native_id()\n    omp.set_num_threads(4)\n    omp.get_num_threads()\n    omp.get_max_threads()\n    omp.get_thread_num()\n    omp.get_num_procs()\n    omp.in_parallel()\n    omp.set_dynamic(False)\n    omp.get_dynamic()\n    omp.get_cancellation()\n    omp.set_schedule('static', 10)\n    omp.get_schedule()\n    omp.get_thread_limit()\n    omp.set_max_active_levels(1)\n    omp.get_max_active_levels()\n    omp.get_level()\n    omp.get_ancestor_thread_num(0)\n    omp.get_team_size(0)\n    omp.get_active_level()\n    omp.in_final()\n    omp.get_proc_bind()\n    omp.set_default_device(0)\n    omp.get_default_device()\n    omp.get_num_devices()\n    omp.get_num_teams()\n    omp.get_team_num()\n    omp.is_initial_device()\n    omp.get_wtime()\n    omp.get_wtick()\n\n@test\ndef test_omp_schedules():\n    omp.set_num_threads(4)\n    N = 10001\n\n    x = list(range(N))\n    y = [0] * N\n    @par\n    for i in range(N):\n        y[i] = x[i] ** 2\n    assert all(y[i] == x[i]**2 for i in range(N))\n\n    x = list(range(N))\n    y = [0] * N\n    @par(schedule='static')\n    for i in range(N):\n        y[i] = x[i] ** 2\n    assert all(y[i] == x[i]**2 for i in range(N))\n\n    x = list(range(N))\n    y = [0] * N\n    @par(schedule='static', chunk_size=1)\n    for i in range(N):\n        y[i] = x[i] ** 2\n    assert all(y[i] == x[i]**2 for i in range(N))\n\n    x = list(range(N))\n    y = [0] * N\n    chunk = 13\n    @par(schedule='static', chunk_size=chunk)\n    for i in range(N):\n        y[i] = x[i] ** 2\n    assert all(y[i] == x[i]**2 for i in range(N))\n\n    x = list(range(N))\n    y = [0] * N\n    @par(schedule='static', chunk_size=N-1)\n    for i in range(N):\n        y[i] = x[i] ** 2\n    assert all(y[i] == x[i]**2 for i in range(N))\n\n    x = list(range(N))\n    y = [0] * N\n    @par(schedule='static', chunk_size=N)\n    for i in range(N):\n        y[i] = x[i] ** 2\n    assert all(y[i] == x[i]**2 for i in range(N))\n\n    x = list(range(N))\n    y = [0] * N\n    @par(schedule='static', chunk_size=N+1)\n    for i in range(N):\n        y[i] = x[i] ** 2\n    assert all(y[i] == x[i]**2 for i in range(N))\n\n    x = list(range(N))\n    y = [0] * N\n    @par(schedule='dynamic')\n    for i in range(N):\n        y[i] = x[i] ** 2\n    assert all(y[i] == x[i]**2 for i in range(N))\n\n    x = list(range(N))\n    y = [0] * N\n    @par(schedule='dynamic', chunk_size=1)\n    for i in range(N):\n        y[i] = x[i] ** 2\n    assert all(y[i] == x[i]**2 for i in range(N))\n\n    x = list(range(N))\n    y = [0] * N\n    chunk = 17\n    @par(schedule='dynamic', chunk_size=chunk)\n    for i in range(N):\n        y[i] = x[i] ** 2\n    assert all(y[i] == x[i]**2 for i in range(N))\n\n    x = list(range(N))\n    y = [0] * N\n    @par(schedule='dynamic', chunk_size=N-1)\n    for i in range(N):\n        y[i] = x[i] ** 2\n    assert all(y[i] == x[i]**2 for i in range(N))\n\n    x = list(range(N))\n    y = [0] * N\n    @par(schedule='dynamic', chunk_size=N)\n    for i in range(N):\n        y[i] = x[i] ** 2\n    assert all(y[i] == x[i]**2 for i in range(N))\n\n    x = list(range(N))\n    y = [0] * N\n    @par(schedule='dynamic', chunk_size=N+1)\n    for i in range(N):\n        y[i] = x[i] ** 2\n    assert all(y[i] == x[i]**2 for i in range(N))\n\n@test\ndef test_omp_ranges():\n    nt = 4\n    lock = thr.Lock()\n    seen = set()\n\n    @omp.critical\n    def add(seen, i):\n        seen.add(i)\n\n    @par\n    for i in range(3, 123, 7):\n        add(seen, i)\n    assert seen == set(range(3, 123, 7))\n    seen.clear()\n\n    @par\n    for i in range(-3, -123, 7):\n        with lock:\n            seen.add(i)\n    assert seen == set(range(-3, -123, 7))\n    seen.clear()\n\n    @par(num_threads=nt)\n    for i in range(-3, -123, -7):\n        add(seen, i)\n    assert seen == set(range(-3, -123, -7))\n    seen.clear()\n\n    @par(chunk_size=12)\n    for i in range(3, 123, 7):\n        with lock:\n            seen.add(i)\n    assert seen == set(range(3, 123, 7))\n    seen.clear()\n\n    @par(chunk_size=12)\n    for i in range(-3, -123, 7):\n        add(seen, i)\n    assert seen == set(range(-3, -123, 7))\n    seen.clear()\n\n    @par(chunk_size=12, num_threads=nt)\n    for i in range(-3, -123, -7):\n        with lock:\n            seen.add(i)\n    assert seen == set(range(-3, -123, -7))\n    seen.clear()\n\n    @par(chunk_size=10000)\n    for i in range(3, 123, 7):\n        add(seen, i)\n    assert seen == set(range(3, 123, 7))\n    seen.clear()\n\n    @par(chunk_size=10000)\n    for i in range(-3, -123, 7):\n        with lock:\n            seen.add(i)\n    assert seen == set(range(-3, -123, 7))\n    seen.clear()\n\n    @par(chunk_size=10000, num_threads=nt)\n    for i in range(-3, -123, -7):\n        add(seen, i)\n    assert seen == set(range(-3, -123, -7))\n    seen.clear()\n\n    @par(schedule='dynamic', num_threads=nt)\n    for i in range(-3, -123, -7):\n        with lock:\n            seen.add(i)\n    assert seen == set(range(-3, -123, -7))\n    seen.clear()\n\n    @par(schedule='dynamic', chunk_size=12)\n    for i in range(3, 123, 7):\n        add(seen, i)\n    assert seen == set(range(3, 123, 7))\n    seen.clear()\n\n    @par(schedule='dynamic', chunk_size=12)\n    for i in range(-3, -123, 7):\n        with lock:\n            seen.add(i)\n    assert seen == set(range(-3, -123, 7))\n    seen.clear()\n\n    @par(schedule='dynamic', chunk_size=12, num_threads=nt)\n    for i in range(-3, -123, -7):\n        add(seen, i)\n    assert seen == set(range(-3, -123, -7))\n    seen.clear()\n\nmy_global = 42\n\nclass Vector:\n    x: float\n    y: float\n\n    def __init__(self):\n        self.x = 0.0\n        self.y = 0.0\n\n    def __add__(self, other: Vector):\n        return Vector(self.x + other.x, self.y + other.y)\n\n    def __str__(self):\n        return f'<{self.x}, {self.y}>'\n\n@test\ndef test_omp_reductions():\n    def expected(N, a, op):\n        for i in range(N):\n            a = op(a, type(a)(i))\n        return a\n\n    from math import inf\n    omp.set_num_threads(4)\n    N = 10001\n    L = list(range(N))\n\n    # static\n    a = 0\n    @par\n    for i in L:\n        a += i\n    assert a == expected(N, 0, int.__add__)\n\n    a = 0\n    @par\n    for i in L:\n        a |= i\n    assert a == expected(N, 0, int.__or__)\n\n    a = 0\n    @par\n    for i in L:\n        a ^= i\n    assert a == expected(N, 0, int.__xor__)\n\n    a = 0xffffffff\n    @par\n    for i in L:\n        a &= i\n    assert a == expected(N, 0xffffffff, int.__and__)\n\n    a = 1\n    @par\n    for i in L:\n        a *= i\n    assert a == expected(N, 1, int.__mul__)\n\n    a = 0\n    @par\n    for i in L:\n        b = N+1 if i == N//2 else i\n        a = max(a, b)\n    assert a == N+1\n\n    a = 0\n    @par\n    for i in L:\n        b = -1 if i == N//2 else i\n        a = min(a, b)\n    assert a == -1\n\n    x = A(0)\n    @par\n    for i in L:\n        x += A(i)\n    assert x.n == expected(N, 0, int.__add__)\n\n    # static chunked\n    a = 0\n    @par(chunk_size=3)\n    for i in L:\n        a += i\n    assert a == expected(N, 0, int.__add__)\n\n    a = 0\n    @par(chunk_size=3)\n    for i in L:\n        a |= i\n    assert a == expected(N, 0, int.__or__)\n\n    a = 0\n    @par(chunk_size=3)\n    for i in L:\n        a ^= i\n    assert a == expected(N, 0, int.__xor__)\n\n    a = 0xffffffff\n    @par(chunk_size=3)\n    for i in L:\n        a &= i\n    assert a == expected(N, 0xffffffff, int.__and__)\n\n    a = 1\n    @par(chunk_size=3)\n    for i in L[1:10]:\n        a *= i\n    assert a == 1*2*3*4*5*6*7*8*9\n\n    a = 0\n    @par(chunk_size=3)\n    for i in L:\n        b = N+1 if i == N//2 else i\n        a = max(a, b)\n    assert a == N+1\n\n    a = 0\n    @par(chunk_size=3)\n    for i in L:\n        b = -1 if i == N//2 else i\n        a = min(a, b)\n    assert a == -1\n\n    x = A(0)\n    @par(chunk_size=3)\n    for i in L:\n        x += A(i)\n    assert x.n == expected(N, 0, int.__add__)\n\n    # dynamic\n    a = 0\n    @par(schedule='dynamic')\n    for i in L:\n        a += i\n    assert a == expected(N, 0, int.__add__)\n\n    a = 0\n    @par(schedule='dynamic')\n    for i in L:\n        a |= i\n    assert a == expected(N, 0, int.__or__)\n\n    a = 0\n    @par(schedule='dynamic')\n    for i in L:\n        a ^= i\n    assert a == expected(N, 0, int.__xor__)\n\n    a = 0xffffffff\n    @par(schedule='dynamic')\n    for i in L:\n        a &= i\n    assert a == expected(N, 0xffffffff, int.__and__)\n\n    a = 1\n    @par(schedule='dynamic')\n    for i in L[1:10]:\n        a *= i\n    assert a == 1*2*3*4*5*6*7*8*9\n\n    a = 0\n    @par(schedule='dynamic')\n    for i in L:\n        b = N+1 if i == N//2 else i\n        a = max(a, b)\n    assert a == N+1\n\n    a = 0\n    @par(schedule='dynamic')\n    for i in L:\n        b = -1 if i == N//2 else i\n        a = min(a, b)\n    assert a == -1\n\n    x = A(0)\n    @par(schedule='dynamic')\n    for i in L:\n        x += A(i)\n    assert x.n == expected(N, 0, int.__add__)\n\n    # floats\n    c = 0.\n    @par\n    for i in L:\n        c += float(i)\n    assert c == expected(N, 0., float.__add__)\n\n    c = 1.\n    @par\n    for i in L[1:10]:\n        c *= float(i)\n    assert c == float(1*2*3*4*5*6*7*8*9)\n\n    c = 0.\n    @par\n    for i in L:\n        b = float(N+1 if i == N//2 else i)\n        c = max(b, c)\n    assert c == float(N+1)\n\n    c = 0.\n    @par\n    for i in L:\n        b = float(-1 if i == N//2 else i)\n        c = min(b, c)\n    assert c == -1.\n\n    c = 0.\n    @par\n    for i in L:\n        c += i  # float-int op\n    assert c == expected(N, 0., float.__add__)\n\n    c = 0.\n    @par\n    for i in L:\n        c = i + c  # int-float op\n    assert c == expected(N, 0., float.__add__)\n\n    # float32s\n    c = f32(0.)\n    # this one can give different results due to\n    # non-commutativity of floats; so limit to 1001\n    @par\n    for i in L[1:1001]:\n        c += f32(i)\n    assert c == sum((f32(i) for i in range(1001)), f32(0))\n\n    c = f32(1.)\n    @par\n    for i in L[1:10]:\n        c *= f32(i)\n    assert c == f32(1*2*3*4*5*6*7*8*9)\n\n    c = f32(0.)\n    @par\n    for i in L:\n        b = f32(N+1 if i == N//2 else i)\n        c = max(b, c)\n    assert c == f32(N+1)\n\n    c = f32(0.)\n    @par\n    for i in L:\n        b = f32(-1 if i == N//2 else i)\n        c = min(b, c)\n    assert c == f32(-1.)\n\n    c = f32(0.)\n    @par\n    for i in L[:12]:\n        c += i  # float-int op\n    assert c == f32(1+2+3+4+5+6+7+8+9+10+11)\n\n    c = f32(0.)\n    @par\n    for i in L[:12]:\n        c = i + c  # int-float op\n    assert c == f32(1+2+3+4+5+6+7+8+9+10+11)\n\n    x_add = 10.\n    x_min = inf\n    x_max = -inf\n    @par\n    for i in L:\n        x_i = float(i)\n        x_add += x_i\n        x_min = min(x_min, x_i)\n        x_max = max(x_i, x_max)\n    assert x_add == expected(N, 10., float.__add__)\n    assert x_min == expected(N, inf, min)\n    assert x_max == expected(N, -inf, max)\n\n    x_mul = 2.\n    @par\n    for i in L[:10]:\n        x_i = float(i)\n        x_mul *= x_i\n    assert x_mul == expected(10, 2., float.__mul__)\n\n    # multiple reductions\n    global my_global\n    g = my_global\n    a = 0\n    b = 0\n    @par(schedule='dynamic', num_threads=3)\n    for i in L:\n        a += i\n        b ^= i\n        my_global += i\n    assert a == expected(N, 0, int.__add__)\n    assert b == expected(N, 0, int.__xor__)\n    assert my_global == g + expected(N, 0, int.__add__)\n\n    # custom reductions\n    vectors = [Vector(i, i) for i in range(10)]\n    v = Vector()\n    @par\n    for vv in vectors:\n        v += vv\n    assert v.x == 45.0\n    assert v.y == 45.0\n\nanother_global = 0\n@test\ndef test_omp_critical():\n    @omp.critical\n    def foo(i):\n        global another_global\n        another_global += i\n\n    @omp.critical\n    def bar(i):\n        global another_global\n        another_global += i\n\n    global another_global\n    for n in (99999, 100000, 100001):\n        another_global = 0\n        @par(schedule='dynamic')\n        for i in range(n):\n            foo(i)\n            bar(i)\n        assert another_global == 2*sum(range(n))\n\n@test\ndef test_omp_non_imperative():\n    def squares(N):\n        for i in range(N):\n            yield i*i\n\n    N = 10001\n    v = [0] * N\n\n    @par\n    for i,s in enumerate(squares(N)):\n        v[i] = s\n\n    assert all(s == i*i for i,s in enumerate(v))\n\ntest_generator_based_loops_global = 0.7\n\n@test\ndef test_omp_non_imperative_reductions():\n    def squares(n):\n        for i in range(n):\n            yield i*i\n\n    @omp.critical\n    def add(v, x):\n        v.add(x)\n\n    @nonpure\n    def foo(x):\n        return x\n\n    global test_generator_based_loops_global\n    N = 1001\n\n    # no reductions\n    v = set()\n    @par\n    for i in squares(N):\n        x = i - 1\n        add(v, x)\n    assert v == {i**2 - 1 for i in range(N)}\n\n    # one reduction\n    a = 0\n    @par\n    for i in squares(N):\n        a += i\n    assert a == 333833500\n\n    # kitchen sink\n    a = 7\n    b = 0\n    c = 0.5\n    d = foo(0)\n    e = foo(1)\n    f = foo(-1)\n    g = -1\n    h = Vector(1.5, 1.25)\n    @par\n    for i in squares(N):\n        x = foo(i) + d\n        y = foo(x) + e + f\n        a = x + a + d\n        b ^= y - d\n        c += x + d\n        test_generator_based_loops_global += y - d\n        f = foo(-1)\n        g = max(i, g)\n        h += Vector(i, i)\n    assert a == 333833507\n    assert b == 332752\n    assert c == 333833500.5\n    assert test_generator_based_loops_global == 333833500.7\n    assert g == (N - 1) ** 2\n    assert h.x == 333833501.5\n    assert h.y == 333833501.25\n\n@test\ndef test_omp_transform(a, b, c):\n    a0, b0, c0 = a, b, c\n    d = a + b + c\n    v = list(range(int(d*d)))\n    ids = set()\n\n    @par('schedule(static, 5) num_threads(3) ordered')\n    for i in v:\n        a += type(a)(i)\n        z = i * i\n        c = type(c)(z)\n        b += type(b)(z)\n        with lock:\n            ids.add(omp.get_thread_num())\n\n    for i in v:\n        a0 += type(a0)(i)\n        z = i * i\n        c0 = type(c0)(z)\n        b0 += type(b0)(z)\n\n    assert ids == {0, 1, 2}\n    assert int(a) == int(a0)\n    assert abs(b - b0) < b/1e6\n    assert c == v[-1] ** 2\n\n@test\ndef test_omp_nested():\n    def squares(n):\n        for i in range(n):\n            yield i*i\n\n    N = 100\n    v = []\n\n    v.clear()\n    @par\n    for i in range(N):\n        @par\n        for j in range(i):\n            with lock:\n                v.append(i + j)\n    assert set(v) == {i + j for i in range(N) for j in range(i)}\n\n    v.clear()\n    @par\n    for i in range(N):\n        @par\n        for j in squares(i):\n            with lock:\n                v.append(i + j)\n    assert set(v) == {i + j for i in range(N) for j in squares(i)}\n\n    v.clear()\n    @par\n    for i in squares(N):\n        @par\n        for j in range(i):\n            with lock:\n                v.append(i + j)\n    assert set(v) == {i + j for i in squares(N) for j in range(i)}\n\n    v.clear()\n    @par\n    for i in squares(N):\n        @par\n        for j in squares(i):\n            with lock:\n                v.append(i + j)\n    assert set(v) == {i + j for i in squares(N) for j in squares(i)}\n\n@test\ndef test_omp_corner_cases():\n    def squares(n):\n        for i in range(n):\n            yield i*i\n\n    @nonpure\n    def foo(x):\n        return x\n\n    v = list(range(10))\n\n    @par\n    for i in range(10):\n        pass\n\n    @par\n    for i in v:\n        pass\n\n    @par(num_threads=2)\n    for i in range(10):\n        pass\n\n    @par(schedule='dynamic')\n    for i in range(10):\n        pass\n\n    @par(num_threads=2, schedule='dynamic')\n    for i in range(10):\n        pass\n\n    @par\n    for i in squares(10):\n        pass\n\n    @par(num_threads=2)\n    for i in squares(10):\n        pass\n\n    @par\n    for i in range(10):\n        foo(i)\n\n    @par\n    for i in squares(10):\n        foo(i)\n\n    @par\n    for i in range(10):\n        a = foo(i)\n\n    @par\n    for i in squares(10):\n        a = foo(i)\n\n    @par\n    for i in range(10):\n        i += i\n\n    @par\n    for i in squares(10):\n        i += i\n\n@test\ndef test_omp_collapse():\n    # trivial\n    A0 = []\n    B0 = []\n\n    for i in range(10):\n        A0.append(i)\n\n    @par(num_threads=4, collapse=1)\n    for i in range(10):\n        with lock:\n            B0.append(i)\n\n    assert sorted(A0) == sorted(B0)\n\n    # basic\n    A1 = []\n    B1 = []\n\n    for i in range(10):\n        for j in range(10):\n            A1.append((i,j))\n\n    @par(num_threads=4, collapse=2)\n    for i in range(10):\n        for j in range(10):\n            with lock:\n                B1.append((i,j))\n\n    assert sorted(A1) == sorted(B1)\n\n    # deep\n    A2 = []\n    B2 = []\n\n    for a in range(3):\n        for b in range(4):\n            for c in range(5):\n                for d in range(6):\n                    A2.append((a,b,c,d))\n\n    @par(num_threads=4, collapse=4)\n    for a in range(3):\n        for b in range(4):\n            for c in range(5):\n                for d in range(6):\n                    with lock:\n                        B2.append((a,b,c,d))\n\n    assert sorted(A2) == sorted(B2)\n\n    # ranges 1\n    A3 = []\n    B3 = []\n\n    for a in range(-5,5,2):\n        for b in range(5,-7,-2):\n            for c in range(0,17,3):\n                for d in range(5):\n                    A3.append((a,b,c,d))\n\n    @par(num_threads=4, collapse=4)\n    for a in range(-5,5,2):\n        for b in range(5,-7,-2):\n            for c in range(0,17,3):\n                for d in range(5):\n                    with lock:\n                        B3.append((a,b,c,d))\n\n    assert sorted(A3) == sorted(B3)\n\n    # ranges 2\n    A4 = []\n    B4 = []\n\n    for i in range(10):\n        for j in range(7,-5,-2):\n            for k in range(-5,10,3):\n                A4.append((i,j,k))\n\n    @par(num_threads=4, collapse=3)\n    for i in range(10):\n        for j in range(7,-5,-2):\n            for k in range(-5,10,3):\n                with lock:\n                    B4.append((i,j,k))\n\n    assert sorted(A4) == sorted(B4)\n\n    # zero\n    B5 = []\n\n    @noinline\n    def zstart():\n        return 5\n\n    @noinline\n    def zstop():\n        return -5\n\n    start = zstart()\n    stop = zstop()\n\n    @par(num_threads=4, collapse=3)\n    for i in range(10):\n        for j in range(start, stop, 1):\n            for k in range(-5,10,3):\n                with lock:\n                    B5.append((i,j,k))\n\n    assert len(B5) == 0\n\n    # order\n    A6 = []\n    B6 = []\n\n    for a in range(-5,5,2):\n        for b in range(5,-7,-2):\n            for c in range(0,17,3):\n                for d in range(5):\n                    A6.append((a,b,c,d))\n\n    @par(num_threads=1, collapse=4)\n    for a in range(-5,5,2):\n        for b in range(5,-7,-2):\n            for c in range(0,17,3):\n                for d in range(5):\n                    B6.append((a,b,c,d))  # no lock since threads=1\n\n    assert A6 == B6\n\n@test\ndef test_omp_ordered(N: int = 1000):\n    @omp.ordered\n    def f(A, i):\n        A.append(i)\n\n    A = []\n\n    @par(schedule='dynamic', chunk_size=1, num_threads=2, ordered=True)\n    for i in range(N):\n        f(A, i)\n\n    assert A == list(range(N))\n\n@test\ndef test_threadlocal():\n    x: thr.ThreadLocal[int] = 42\n\n    @par\n    for i in range(omp.get_num_threads()):\n        x = i**2\n\n    @par\n    for i in range(omp.get_num_threads()):\n        assert x == i**2\n\ntest_omp_api()\ntest_omp_schedules()\ntest_omp_ranges()\ntest_omp_reductions()\ntest_omp_critical()\ntest_omp_non_imperative()\ntest_omp_non_imperative_reductions()\ntest_omp_transform(111, 222, 333)\ntest_omp_transform(111.1, 222.2, 333.3)\ntest_omp_nested()\ntest_omp_corner_cases()\ntest_omp_collapse()\ntest_omp_ordered()\ntest_threadlocal()\n"
  },
  {
    "path": "test/transform/outlining.codon",
    "content": "@nonpure\ndef __outline_begin__():\n    pass\n\n@nonpure\ndef __outline_end__():\n    pass\n\n@nonpure\ndef __outline_successes__():\n    return 0\n\n@nonpure\ndef __outline_failures__():\n    return 0\n\n# use this to avoid const-folding\ndef N(n):\n    return n\n\n@test\ndef test_basic_outline():\n    __outline_begin__()\n    print 'hello world'\n    __outline_end__()\ntest_basic_outline()\n# EXPECT: hello world\n\n@test\ndef test_private_vars_outline(x):\n    b = N(10)\n    __outline_begin__()\n    a = N(42)\n    print a + 1\n    a = N(99)\n    __outline_end__()\n    b = N(13)\ntest_private_vars_outline('x')\n# EXPECT: 43\n\n@test\ndef test_shared_vars_outline(x):\n    b = N(10)\n    __outline_begin__()\n    a = N(42)\n    print a + b + x\n    a = N(99)\n    y = N(-42)\n    __outline_end__()\n    b = N(13)\n    print a\n    print b\n    print y\ntest_shared_vars_outline(100)\n# EXPECT: 152\n# EXPECT: 99\n# EXPECT: 13\n# EXPECT: -42\n\nf = 'f'\n@test\ndef test_shared_vars_with_mod_outline(x):\n    global f\n    b = N(10)\n    f = 'o'\n    __outline_begin__()\n    for i in range(1):\n        a = N(42)\n        print a + b + x\n        a = N(99)\n        x = N(-5)\n        f = str(i)\n    __outline_end__()\n    b = N(13)\n    print b, x\ntest_shared_vars_with_mod_outline(100)\nprint f\n# EXPECT: 152\n# EXPECT: 13 -5\n# EXPECT: 0\n\ng = 'g'\n@test\ndef test_out_flow_return_outline(x):\n    global g\n    b = N(10)\n    g = 'o'\n    __outline_begin__()\n    for i in range(1):\n        a = N(42)\n        print a + b + x\n        a = N(99)\n        x = N(-5)\n        g = str(i)\n        return a\n        break\n    __outline_end__()\n    assert False\ntest_out_flow_return_outline(100)\nprint g\n# EXPECT: 152\n# EXPECT: 0\n\nh = 'h'\n@test\ndef test_out_flow_break_continue_outline(x):\n    global h\n    b = N(10)\n    h = 'o'\n    for i in range(3):\n        __outline_begin__()\n        if i == 0:\n            continue\n        if i == 2:\n            break\n        for j in range(2):\n            if j == 0:\n                continue\n\n            a = N(42)\n            print a + b + x + j\n            a = N(99)\n            x = N(-5)\n            h = str(i)\n            break\n        __outline_end__()\n    b = N(13)\n    print b, x\ntest_out_flow_break_continue_outline(100)\nprint h\n# EXPECT: 153\n# EXPECT: 13 -5\n# EXPECT: 1\n\n@test\ndef test_generator_return_outline(x):\n    for i in range(x):\n        yield i\n        __outline_begin__()\n        if i == 2:\n            continue\n        if i == 4:\n            return\n        __outline_end__()\n        yield i\nprint list(test_generator_return_outline(10))\n# EXPECT: [0, 0, 1, 1, 2, 3, 3, 4]\n\n@test\ndef test_normal_return_outline(x):\n    for i in range(x):\n        yield i\n        __outline_begin__()\n        if i == 2:\n            continue\n        if i == 4:\n            return\n        __outline_end__()\n        yield i\nprint list(test_normal_return_outline(1))\n# EXPECT: [0, 0]\n\n@test\ndef test_invalid_outline_yield(x):\n    b = N(10)\n    __outline_begin__()\n    a = N(42)\n    yield a + b + x\n    a = N(99)\n    __outline_end__()\n    b = N(13)\ntest_invalid_outline_yield(-123)\n\n@test\ndef test_invalid_outline_yield_in(x):\n    b = N(10)\n    __outline_begin__()\n    a = (yield) + b\n    yield b\n    __outline_end__()\n    b = N(13)\ntest_invalid_outline_yield_in(-123)\n\n@test\ndef test_invalid_outline_stack_alloc():\n    __outline_begin__()\n    a = __array__[int](1)\n    a[0] = 42\n    __outline_end__()\n    print a[0]\ntest_invalid_outline_stack_alloc()\n# EXPECT: 42\n\nprint __outline_successes__()\nprint __outline_failures__()\n# EXPECT: 8\n# EXPECT: 3\n"
  },
  {
    "path": "test/transform/str_opt.codon",
    "content": "import internal.static as static\n\ncat_count = 0\n\n# c/p from str.seq\ndef old_cat(*args):\n    total = 0\n    if static.len(args) == 1 and hasattr(args[0], \"__iter__\") and hasattr(args[0], \"__len__\"):\n        for s in args[0]:\n            if not isinstance(s, str):\n                compile_error(\"not a string\")\n            total += s.len\n        p = cobj(total)\n        n = 0\n        for s in args[0]:\n            str.memcpy(p + n, s.ptr, s.len)\n            n += s.len\n        return str(p, total)\n    elif static.len(args) == 1 and hasattr(args[0], \"__iter__\"):\n        sz = 10\n        p = cobj(sz)\n        n = 0\n        for s in args[0]:\n            if not isinstance(s, str):\n                compile_error(\"not a string\")\n            if n + s.len > sz:\n                sz = 1 + 3 * (n + s.len) // 2\n                pp = cobj(sz)\n                str.memcpy(pp, p, n)\n                p = pp\n            str.memcpy(p + n, s.ptr, s.len)\n            n += s.len\n        return str(p, n)\n    else:\n        total = 0\n        for i in args:\n            if not isinstance(i, str):\n                compile_error(\"not a string\")\n            total += i.len\n        p = cobj(total)\n        n = 0\n        for i in args:\n            str.memcpy(p + n, i.ptr, i.len)\n            n += i.len\n        return str(p, total)\n\n@extend\nclass str:\n    def cat(*args):\n        global cat_count\n        cat_count += 1\n        return old_cat(*args)\n\n@test\ndef test_str_optimization():\n    assert 'hello ' + 'world' == \"hello world\"  # no opt: just adding 2 strs\n    assert cat_count == 0\n    # assert 'a' + 'b' + 'c' == 'abc' # superseded by string statics\n    # assert cat_count == 1\n    assert 'a' * 2 == 'aa'  # no opt: mul instead of add\n    assert cat_count == 0\n    # assert 'a' + ('b' + 'c') == 'abc'\n    # assert cat_count == 2\n    # assert 'a' + ('b' + ('c' + 'd')) == 'abcd'\n    # assert cat_count == 3\n\n    a = 'a'\n    b = 'b'\n    c = 'c'\n    assert (a*2 + b*3 + c*4) == 'aabbbcccc'\n    assert cat_count == 1\ntest_str_optimization()\n"
  },
  {
    "path": "test/types.cpp",
    "content": "// Copyright (C) 2022-2026 Exaloop Inc. <https://exaloop.io>\n\n#include <algorithm>\n#include <dirent.h>\n#include <fcntl.h>\n#include <fstream>\n#include <gc.h>\n#include <iostream>\n#include <sstream>\n#include <string>\n#include <sys/types.h>\n#include <sys/wait.h>\n#include <tuple>\n#include <unistd.h>\n#include <vector>\n\n#include \"codon/parser/common.h\"\n#include \"codon/util/common.h\"\n#include \"gtest/gtest.h\"\n\nTEST(TypeCoreTest, TestName) { ; }\n"
  }
]